Compare commits

...

96 Commits
uat ... master

Author SHA1 Message Date
a9f5542e02 footer fix 2024-11-05 18:46:20 +06:00
Vlad
6ae37e3bc3 links 2024-10-28 15:09:27 +06:00
Vlad
8c5b82b943 links 2024-10-28 15:06:56 +06:00
1e276fe15b Merge pull request 'hide total amount' (#5) from Elaman/kgroad-frontend2:main into master
Reviewed-on: https://gitea.fishrungames.com/Transparency/kgroad-frontend2/pulls/5
2024-10-11 18:50:31 +00:00
eb84fa2db4 hide total amount 2024-10-11 17:41:15 +06:00
Vlad
a755c9125a language 2024-09-20 16:59:15 +06:00
Vlad
7daa197649 links 2024-09-20 15:05:42 +06:00
775e238e77 z-index for nav-language 2024-09-11 16:00:27 +06:00
5eff43ef61 Fix error in footer 2024-09-11 15:55:18 +06:00
Vladislav Khorev
3c810dad82 Remove netkg tracker for uat 2024-09-10 17:39:02 +00:00
Vladislav Khorev
b10ab93bf1 Merge 2024-09-10 17:37:17 +00:00
Vladislav Khorev
49cb64a368 fixing minor issues 2024-09-10 17:35:22 +00:00
Vladislav Khorev
cbe5c0656c Remove netkg tracker from uat 2024-09-10 16:25:58 +00:00
1a606f4ecd Fix news details 2024-09-09 22:23:59 +06:00
19397689b1 Fix news details 2024-09-09 21:39:48 +06:00
Vladislav Khorev
922d8386c6 Merge 2024-09-08 14:16:41 +00:00
Vladislav Khorev
dcac729914 merge 2024-09-08 14:15:44 +00:00
Vladislav Khorev
ecd3848ec3 package lock added 2024-09-08 14:14:22 +00:00
Vladislav Khorev
32761ce479 Merge 2024-09-08 14:14:05 +00:00
54ec09afe0 Fix description for news 2024-09-03 11:35:13 +06:00
ea58e4f2d1 fix dates of news 2024-09-03 10:58:38 +06:00
Vladislav Khorev
c3697c9e55 Update readme 2024-07-28 21:01:16 +00:00
b85d1e8f30 fixing navbar 2024-07-09 09:41:47 +06:00
Vladislav Khorev
1363519e75 fixing translations 2024-07-03 17:52:47 +00:00
bea7ec82c2 Merge pull request 'updated navbar, fixed map' (#3) from kg-road-fix into ali
Reviewed-on: Transparency/kgroad-frontend2#3
2024-06-13 11:41:41 +00:00
6508bc0258 updated navbar, fixed map 2024-06-13 17:36:48 +06:00
981c5863ee Merge pull request 'fix' (#1) from fix into ali
Reviewed-on: Transparency/kgroad-frontend2#1
2024-05-30 10:34:32 +00:00
5b5feaa43f added name surname to volunteers page 2024-05-29 15:51:41 +06:00
545285f1c3 added kg text, removed image from about, added links in descriptions of posts 2024-05-29 15:27:00 +06:00
Vlad
aa4bf468cd fix date 2024-05-24 17:11:43 +06:00
Vladislav Khorev
4f26c31c36 Working on translations 2024-05-24 08:47:59 +00:00
Vladislav Khorev
90acfefe80 Fixing profile url 2024-04-13 18:57:36 +00:00
Vladislav Khorev
e9509e9396 Fixing bug 2024-04-11 11:52:21 +00:00
Vladislav Khorev
6837cfa46a Added backend calls to support request 2024-04-11 11:41:33 +00:00
Vladislav Khorev
c7f7380821 moving profile photo to env and fix bug 2024-04-10 21:03:40 +00:00
Vladislav Khorev
57fab57b9d moving profile photo to env 2024-04-10 20:59:20 +00:00
Vladislav Khorev
ffe8c84d3d Working with links in footer 2024-04-10 20:37:16 +00:00
Vladislav Khorev
f38f7ad7bd MErge with ali 2024-04-10 20:31:01 +00:00
Vladislav Khorev
d9ac888d48 Merge branch 'ali' into release005 2024-04-10 20:27:46 +00:00
Vladislav Khorev
b885c9c9bc Merge branch 'ali' of gitea.fishrungames.com:Transparency/kgroad-frontend2 into ali 2024-04-10 20:26:17 +00:00
Vladislav Khorev
2228025e0f Added privacy and some support 2024-04-10 20:26:07 +00:00
Vladislav Khorev
046a17122d Fixing compilation erorr 2024-03-22 08:49:03 +00:00
Vladislav Khorev
5fb5487ea9 Merge branch 'ali' into release005 2024-03-22 08:40:14 +00:00
Vladislav Khorev
80a3f658b9 release004 2024-03-22 08:39:52 +00:00
Alibek
45650996c3 made language in menu 2024-03-20 17:18:10 +06:00
Alibek
36896b1fad fixed metadata in report page 2024-03-18 17:30:09 +06:00
Alibek
779f37a1a3 fixed message 2024-03-18 17:08:49 +06:00
Alibek
a520214eb9 made report message 2024-03-18 16:59:05 +06:00
Vladislav Khorev
a01b35343f release changes 2024-03-18 10:53:48 +00:00
Alibek
c54131197e fixed bug with latitude and longitude 2024-03-18 16:33:57 +06:00
Alibek
006a1a7b7b fixed redirect to profile 2024-03-16 19:04:37 +06:00
Vladislav Khorev
18f173820f Fixing bugs 2024-03-16 12:20:50 +00:00
Alibek
40a4116c03 mistake in sign-up form corrected 2024-03-16 02:21:47 +06:00
Alibek
0ba0f1df89 fixed sort in profile-reports table and in statistics table, improved error messages, improved metatags by fetching metadata from server 2024-03-16 02:19:33 +06:00
Alibek
0221a79015 made metatags 2024-03-15 15:34:11 +06:00
Alibek
ad86f2e79e improved seo, made not-found page, fixed bugs in news details and in report datails pages 2024-03-14 04:13:08 +06:00
Alibek
7d36510a9b fixed bug with sign-in 2024-03-13 16:48:13 +06:00
Alibek
7bc3e2ccb5 added policy checkbox in sign-up form, fixed height bug in sign-up page 2024-03-12 04:04:41 +06:00
Alibek
74b15060a1 fixed bugs with sort 2024-03-10 04:38:46 +06:00
Alibek
bcc7279d39 fixed few bugs 2024-03-02 02:07:30 +06:00
Alibek
5330f21a73 made breadcrumbs 2024-03-01 19:54:03 +06:00
Alibek
65a700dad7 added dynamic pagination in reviews-section 2024-03-01 14:14:32 +06:00
Alibek
0a7513dd89 added change-image feature in profile, sign-up validating password and shows correct error, if user already exists 2024-02-29 20:45:09 +06:00
Alibek
6650a98503 resoled conflicts 2024-02-29 14:22:42 +06:00
Alibek
e2db27b56f resolved 2024-02-29 14:14:16 +06:00
Alibek
6637ab4eae added next-intl, composed next-auth and next-intl in middleware, fixed some bugs, completed details page, added google auth 2024-02-29 14:09:37 +06:00
Vladislav Khorev
d1440f3907 yarn lock 2024-02-25 19:27:10 +00:00
Vladislav Khorev
773a33e21a uat changes 2024-02-25 19:23:13 +00:00
Vladislav Khorev
3c4661a9c8 Fixing google analytics, adding some changes from prod 2024-02-25 19:20:22 +00:00
Vladislav Khorev
82eb226810 Release version 2024-02-25 16:20:52 +00:00
Vladislav Khorev
f9754aca1a Merge 2024-02-25 16:08:56 +00:00
Vladislav Khorev
d933102065 Merge changes 2024-02-25 16:08:02 +00:00
Vladislav Khorev
5ae5ff9fd3 Merge branch 'ali' into release003 2024-02-25 16:03:48 +00:00
Vladislav Khorev
2e03fd9175 Added google analytics and net.kg analytics 2024-02-24 14:01:03 +00:00
Vladislav Khorev
c3a94c920f For prod 2024-02-24 13:22:15 +00:00
Vladislav Khorev
721e8506dd Merge branch 'release003' into ali 2024-02-24 13:02:06 +00:00
Vladislav Khorev
566c4d4473 Fixing bug with comments/reviews 2024-02-24 13:00:24 +00:00
Vladislav Khorev
67b935b47f Fixing things 2024-02-24 12:07:50 +00:00
Vladislav Khorev
deb08e290e Some changes for uat 2024-02-24 11:44:35 +00:00
Vladislav Khorev
e3a5ed72e0 Make release version prep 2024-02-24 11:38:42 +00:00
Vladislav Khorev
3b365d7742 Make release 003 2024-02-24 10:42:58 +00:00
Alibek
9cd7b2a67d made like 2024-02-20 00:33:33 +06:00
Alibek
d77846bbbb changed 2024-02-19 17:20:52 +06:00
Alibek
76b7a8e6b4 commited 2024-02-19 17:06:15 +06:00
Alibek
7a9ba30e8f made like 2024-02-19 16:32:34 +06:00
Alibek
16bb5a4a1a map 2024-02-19 15:18:03 +06:00
Alibek
b0870824ba fixed bugs 2024-02-19 15:14:19 +06:00
Alibek
d4c49a1061 made filters in rating section 2024-02-16 14:11:15 +06:00
Alibek
a12aaa1213 made pagination and fixed map 2024-02-15 22:45:27 +06:00
Alibek
ed72067108 Merge branch 'ali' of gitea.fishrungames.com:Transparency/kgroad-frontend2 into ali 2024-02-14 14:21:45 +06:00
Alibek
95ff16d48b added trycatch in news page, removed font Tilda-Sans 2024-02-14 14:21:20 +06:00
Vladislav Khorev
e0e2f5470d renamed folders 2024-02-14 08:04:02 +00:00
Alibek
d6c6504d1f made all pages 2024-02-13 19:18:07 +06:00
Alibek
f6ceb252b2 made news, stats, volunteers, auth 2024-02-09 19:42:22 +06:00
Alibek
45a9d698cd made fall auth 2024-02-09 03:53:32 +06:00
Alibek
1468fac1cc remade folder structure, fixed problem with build 2024-02-07 23:24:59 +06:00
330 changed files with 21945 additions and 1941 deletions

2
.env
View File

@ -1,2 +0,0 @@
NEXTAUTH_SECRET=";sadmfxflpdk"
NEXTAUTH_URL="http://localhost:3000"

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
/.pnp
.pnp.js
.yarn/install-state.gz
.env
# testing
/coverage

View File

@ -1,3 +1,12 @@
## TL;DR
```
git pull
yarn install
yarn build
yarn start
```
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started

View File

@ -1,7 +0,0 @@
import React from "react";
const page = () => {
return <div>page</div>;
};
export default page;

View File

@ -1,3 +0,0 @@
import RootLayout from "@/app/App";
export default RootLayout;

View File

@ -1,3 +0,0 @@
import Home from "@/pages/Home/Home";
export default Home;

View File

@ -1,7 +0,0 @@
import React from "react";
const page = () => {
return <div>page</div>;
};
export default page;

View File

@ -1,7 +0,0 @@
import React from "react";
const page = () => {
return <div>page</div>;
};
export default page;

9
lib/next-auth.d.ts vendored
View File

@ -4,6 +4,13 @@ declare module "next-auth" {
interface Session {
refresh_token: string;
access_token: string;
expires_in: Date;
}
interface User {
refresh_token: string;
access_token: string;
expires_in: Date;
}
}
@ -13,6 +20,6 @@ declare module "next-auth/jwt" {
interface JWT {
refresh_token: string;
access_token: string;
exp: number;
expires_in: Date;
}
}

221
messages/en.json Normal file
View File

@ -0,0 +1,221 @@
{
"general": {
"date": "Date",
"address": "Address",
"status": "Status",
"description": "Description",
"reviews": "Reviews",
"rating": "Rating",
"review": "Review",
"write_comment": "Write Comment",
"search": "Search",
"search_for": "Search For",
"city": "City",
"added_roads": "Added Roads",
"broken_roads": "Broken Roads",
"accident_hotspots": "Accident Hotspots",
"local_defects": "Local Defects",
"repair_plans": "Repair Plans",
"repaired": "Repaired",
"fixed_local_defects": "Fixed Local Defects",
"news": "News",
"details": "Details",
"navigation": "Navigation",
"contacts": "Contacts",
"download_our_app": "Download our app",
"socials": "Stay Connected",
"back": "Back",
"save": "Save",
"saving": "Saving",
"cancel": "Cancel",
"cancellation": "Cancellation",
"save_changes": "Save Changes",
"send": "Send",
"receive": "Receive",
"delete": "Delete",
"show_on_map": "Show on Map",
"author_of_appeal": "Author of Appeal",
"enter_city": "Enter City",
"page_not_found": "Page Not Found (404)",
"incorrect_address_or_nonexistent_page": "Incorrect Address or Nonexistent Page.",
"home": "Home",
"first_name": "First Name",
"last_name": "Last Name",
"email": "Email"
},
"navigation": {
"home": "Home",
"about_us": "About Us",
"privacy": "Privacy Policy",
"support": "Support",
"statistics": "Statistics",
"news": "News",
"volunteers": "Volunteers",
"profile": "Profile",
"login": "Login"
},
"home": {
"title": "Roads of Kyrgyzstan",
"subtitle": "Let's Make Roads Safe!",
"info": "Current information about the state of roads",
"report_broken_road": "Report Broken Road",
"road_map": "Road Map",
"latest_news": "Stay informed about the latest news on traffic, construction, and events!",
"enter_location": "Enter city, village, or region",
"broken_roads": "Broken road",
"accident_hotspots": "Accident hotspot",
"local_defects": "Local defect",
"repair_plans": "In repair plan",
"repaired": "Repaired",
"fixed_local_defects": "Fixed local defect",
"rating": "Rating",
"road_discussions": "Discussing roads: rating, experience, comfort on the way!",
"enter_address": "Enter address",
"read_more": "Read More"
},
"about_us": {
"name": "About us KGROAD",
"description1": "Transparency International is a global independent organization that studies and fights against corruption both internationally and within individual countries, one of the goals of which is to achieve greater transparency and accountability of government.",
"description2": "The team of Transparency International Kyrgyzstan has developed a platform for monitoring road construction and repair, containing an interactive map to provide access to information on roads and streets repaired in recent years, including cost of works, contractor details, warranty periods, links to procurement documents and other information on works, as well as generalized statistics on road works (e.g. road works performed by the same contractor). ",
"description3": "The platform will also provide users with the opportunity to promptly report defects on roads and the need to fix them, vote on the priority of road repairs and provide feedback in a convenient format that will allow citizens to independently analyze and monitor the use of execution of state road contracts. Information on road construction and repairs is collected from public data from the state procurement portal and the Ministry of Justice.",
"description4":"In addition to the web platform, there are two mobile applications for iOS and Android platforms with functions similar to the website."
},
"volunteers": {
"activists": "Activists",
"received_votes": "Received Votes",
"left_votes": "Left Votes",
"rating": "Rating"
},
"profile": {
"personal_cabinet": "Personal Cabinet",
"personal_data": "Personal Data",
"my_appeals": "My Appeals",
"logout": "Logout",
"write_appeal": "Write Appeal",
"profile_photo": "Profile Photo",
"others_identification": "With a profile photo, other people will recognize you, and it will be easier for you to determine which account you logged into.",
"add_profile_photo": "Add Profile Photo",
"profile_photo_updated": "Profile Photo Updated",
"delete": "Delete",
"change": "Change"
},
"authorization": {
"change_password": "Change Password",
"old_password": "Old Password",
"enter_old_password": "Enter Old Password",
"new_password": "New Password",
"enter_new_password": "Enter New Password",
"confirm_new_password": "Confirm New Password",
"confirm_new_password_prompt": "Please confirm the new password",
"password": "Password",
"forgot_password": "Forgot Password?",
"login": "Login",
"register": "Register",
"sign_in_account": "Sign in to Account",
"enter_credentials": "Please enter your credentials",
"login_via_google": "Login via Google",
"enter_password": "Enter Password",
"password_requirements": "Minimum 8 characters, 1 uppercase letter, and 1 digit",
"no_account_yet": "Don't have an account yet? Register",
"registration": "Registration",
"register_now": "Register Now",
"already_have_account": "Already have an account? Sign in",
"enter_email": "Enter Email",
"enter_email_for_code": "Enter email, and we will send a code to reset the password",
"send_code": "Send Code",
"confirm_code": "Confirm Code",
"enter_code": "Enter Code",
"enter_reset_code": "Enter code to reset and recover the password",
"reset_code": "Reset Code",
"reset_password": "Reset Password",
"check_email": "Check Your Email",
"code_sent_to": "We sent a code to the email name@gmail.com",
"confirmation_code": "Confirmation Code",
"confirm": "Confirm",
"resend_code_in": "Resend Code in",
"resend_code": "Resend Code"
},
"send_report": {
"how_to_mark_road_section": "How to mark a road section?",
"mark_road_instructions": "Place a pin and start drawing a road section (it can consist of any number of broken lines).",
"remove_segment_instruction": "To remove a segment, click on the points again.",
"add_problem_description": "Add a problem description",
"enter_description": "Enter description",
"add_photos": "Add Photos",
"upload_photos_instructions": "Upload up to 5 photos related to the road you want to mark. Photos will help better understand the problem.",
"attach_file": "Attach File (up to 5 MB)",
"submit_for_moderation": "Submit for Moderation",
"appeal_submitted": "Your appeal has been submitted",
"thanks_for_appeal": "Thank you for your appeal. It is currently under moderation.",
"view_my_appeals": "View My Appeals"
},
"months": {
"january": "January",
"february": "February",
"march": "March",
"april": "April",
"may": "May",
"june": "June",
"july": "July",
"august": "August",
"september": "September",
"october": "October",
"november": "November",
"december": "December"
},
"validation_errors": {
"invalid_email_format": "Invalid email format.",
"passwords_do_not_match": "Passwords do not match.",
"required_field_not_filled": "Required field not filled.",
"exceeded_maximum_length": "Exceeded maximum length of the field.",
"login_required_before_commenting": "Please log in or register before leaving a comment.",
"login_required_before_like": "Please log in or register before liking."
},
"server_errors": {
"invalid_email_or_password": "Invalid email or password.",
"server_error_auth_attempt": "Server error during authentication attempt.",
"login_failed": "Failed to log in. Something went wrong, please try again later.",
"account_already_exists": "An account with this email already exists.",
"account_not_found": "Account not found.",
"invalid_activation_code": "Invalid activation code.",
"invalid_activation_code_reset": "Invalid activation code for reset.",
"invalid_password_reset_code": "Invalid password reset code.",
"invalid_code": "Invalid code."
},
"disclaimer": {
"text": "This website is funded by the European Union. Its contents are the sole responsibility of Transparency International Kyrgyzstan and do not necessarily reflect the views of the European Union."
},
"rights": {
"text": "All rights reserved"
},
"PrivacyPolicy":{
"titel0":"Privacy policy",
"title1": "General provisions",
"title2": "Collection of personal data",
"title3": "Storage and use of personal data",
"title4": "Transfer of personal data",
"title5": "Destruction of personal data",
"title6": "Protection of personal data",
"title7": "User requests",
"p1": "These Privacy Policy Regulations define the procedure for processing and protecting information about individuals (hereinafter referred to as Users) using the mailing list, the feedback form of the site, and e-mail addresses located on the domain name ",
"p1o":" (hereinafter referred to as the Website). We have developed a Privacy Policy that describes how we process personal data — any actions (operations) or a set of actions (operations) performed using automation tools or without using such tools with personal data, including collection, recording, systematization, accumulation, storage, clarification (updating, modification), extraction, use, transfer (distribution, provision, access), depersonalization, blocking, deletion, destruction of personal data. The relations related to the processing of personal data and information about the users of the Website are governed by this Regulation. This Privacy Policy regulates any type of processing of personal data and personal information (any personally identifiable information and any other related information) about individuals who are users of the Website. This Policy applies to the processing of personal, personal data collected by any means, both active and passive, both via the Internet and without its use, from persons located anywhere in the world.",
"p2": "The purpose of processing personal data is to fulfill the Operator's obligations to Users regarding the use of the Website and its services. The processing of personal data of users is carried out with the consent of the personal data subject to the processing of his personal data. Personal data refers to any information relating directly or indirectly to a specific or identifiable individual (the subject of personal data) and which can be used to identify or communicate with a particular person. We may request your first name, last name, patronymic and e-mail address to include you in the mailing list with your consent and to respond to you if you have contacted us through the feedback form on the Website.",
"p3": "Users' personal data is stored exclusively on electronic media and processed using automated systems.",
"p4": "HOW WE USE YOUR PERSONAL INFORMATION",
"p5": "The personal data we collect allows us to send you e-mail notifications about news and various events in the field of anti-corruption. Your personal data may also be used when responding to your request sent via the feedback form or to our e-mail address located on the Website. If you do not wish to be included in our mailing list, you can unsubscribe at any time by informing us at the specified contacts for feedback.",
"p6":"COLLECTION AND USE OF NON-PERSONAL INFORMATION",
"p7":"We also collect personal data that is not personal data that does not allow us to directly associate it with any person. We may collect, use, transfer and disclose non-personal information for any purpose. The following are examples of non-personal information that we collect and how we can use it. We may collect personal data, such as: the source of the link to the Website, the characteristics of user behavior on the Website, the number of pages viewed on the Website. Such data is depersonalized and is not personal. This data is collected in order to better understand what information is of interest to Website visitors and improve the quality of the content.",
"p8": "Personal data of Users is not transferred to any third parties. Users who have submitted their personal data to us before the entry into force of these Rules automatically agree to the use of their personal data. At the same time, they can always withdraw their consent to the use of personal data through the feedback form. The Operator blocks personal data related to the relevant User from the moment of the request or request of the User or his legal representative, in case of identification of false personal data or illegal actions.",
"p9": "The user's personal data is destroyed when the personal data subject withdraws consent to the processing of personal data.",
"p10":"IDENTIFICATION FILES (COOKIES) AND OTHER TECHNOLOGIES",
"p11":"The Website, interactive services and applications, e-mail messages and any other communications on our behalf may use identification cookies and other technologies such as pixel tags, web beacons. Accordingly, when using the website and other services, a pop-up window may appear on your device notifying you about the use of cookies. These technologies help us better understand user behavior, tell us which sections of our site have been visited by users, and measure the effectiveness of advertising and online searches. We consider the information collected by cookies and other technologies as non-personal information. You can control cookies by following the instructions of your browser or device. As a rule, you can change your cookie settings in the 'Settings' or 'System Settings' section. As with most websites, we collect some information automatically and store it in statistics files. Such information includes the Internet Protocol address (IP address), browser type and language, information about the Internet service provider, sending and exiting pages, information about the operating system, date and time stamp, as well as information about visits. We use such information to understand and analyze trends, administer the website, study user behavior on it, and collect demographic information about our core user base. The organization may use such information for its own marketing purposes. In some of our e-mail messages, we use interactive links to information posted on the Website. When users follow such links, before they get to the destination page on our website, their requests go through a separate registration. We track such 'passing' data to help us identify interest in individual topics and measure the effectiveness of our communications with users. If you prefer that your requests are not tracked in this way, you should not follow text or image links in e-mail messages. Pixel labels allow us to send e-mail messages in a format that is readable by consumers and tell us whether such messages have been read. We may use such information to limit the number of messages sent to consumers or to stop sending them.",
"p12": "We take precautions to ensure the protection of your personal data in order to ensure the protection of the User's personal data from unauthorized or accidental access to it, destruction, modification, blocking, copying, distribution, as well as from other illegal actions of third parties.",
"p13":"INTEGRITY AND PRESERVATION OF PERSONAL INFORMATION",
"p14":"We will store your personal data and information for as long as necessary to fulfill the purposes described in this Privacy Policy. We do not collect personal data about minors. If we become aware that we have received personal data about a minor, we will take measures to delete such information as soon as possible. We strongly recommend that parents and other persons under whose supervision minors are (legal representatives — parents, adoptive parents or guardians) monitor the use of websites by minors. The Operator is not responsible for the actions of third parties who have gained access to User information as a result of using the Internet or the Website's Services and for the consequences of using data and information that, due to the nature of the Website, are available to any Internet user.",
"p15":"QUESTIONS ABOUT PRIVACY",
"p16":"If you have any questions regarding this Privacy Policy or data processing, you can contact us using the feedback contacts kyrgyzstan@transparency.org.",
"p17": "Users have the right to send their requests to the Operator, including requests regarding the use of their personal data, sending a withdrawal of consent to the processing of personal data in writing to the address specified in the General Provisions section of this regulation, or in the form of an electronic document sent via the feedback form. The Operator undertakes to review and send a response to the User's request within 30 days from the date of receipt of the request.",
"p18":"OTHER",
"p19":"A visitor to the Organization's website who provides his personal data and information thereby agrees to the provisions of this Privacy Policy. We reserve the right to make any changes to the Policy at any time at our discretion in order to further improve the system of protection against unauthorized access to personal data reported by Users without the User's consent. When we make significant changes to the Privacy Policy, a corresponding notification is posted on our website along with the updated version of the Privacy Policy.This Policy does not apply to the actions and Internet resources of third parties."
}
}

234
messages/kg.json Normal file
View File

@ -0,0 +1,234 @@
{
"general": {
"date": "Күн",
"address": "Дарек",
"status": "Статус",
"description": "Көйгөй",
"reviews": "Комментарийлер",
"rating": "Рейтинг",
"review": "Комментарий",
"write_comment": "Комментарий жазуу",
"search": "Издөө",
"search_for": "Издөө",
"city": "Шаар",
"added_roads": "Белгилөөлөр",
"broken_roads": "Бузулган жолдор",
"accident_hotspots": "Авариялык абалда",
"local_defects": "Жергиликтүү кемчиликтер",
"repair_plans": "Ремонттоо планында",
"repaired": "Ремонттолгон",
"fixed_local_defects": "Жергиликтүү оңдолгон жолдор",
"news": "Жаңылыктар",
"details": "Кененирээк маалымат",
"navigation": "Навигация",
"contacts": "Байланыш",
"download_our_app": "Биздин колдонмону жүктөңүз",
"socials": "Социалдык тармактар",
"back": "Артка",
"save": "Сактоо",
"saving": "Сакталат",
"cancel": "Жокко чыгаруу",
"cancellation": "Жокко чыгаруу",
"save_changes": "Өзгөртүүлөрдү сактоо",
"send": "Жиберүү",
"receive": "Алуу",
"delete": "Жок кылуу",
"show_on_map": "Картада көрсөтүү",
"author_of_appeal": "Өтүнүчтүн автору",
"enter_city": "Жерликти киргизиңиз",
"page_not_found": "Барак табылган жок (404)",
"incorrect_address_or_nonexistent_page": "Дарек туура эмес же баракча жок",
"home": "Башкы бет",
"first_name": "Аты-жөнү",
"last_name": "Фамилия",
"email": "Электрондук почта"
},
"navigation": {
"home": "Башкы бет",
"about_us": "Биз жөнүндө",
"privacy": "Купуялык саясаты",
"support": "Колдоо",
"statistics": "Статистика",
"news": "Жаңылыктар",
"volunteers": "Ыктыярчылар",
"profile": "Профиль",
"login": "Кирүү"
},
"about_us": {
"name": "Биз жөнүндө",
"description1": "Transparency International ̶ это глобальная независимая организация по изучению и борьбе с коррупцией как в международном масштабе, так и в масштабе отдельных стран, одна из целей которой добиться большей прозрачности и подотчетности власти.",
"description2": "Команда Трансперенси Интернешнл Кыргызстан разработала платформу для мониторинга строительства и ремонта дорог, содержащая интерактивную карту по обеспечению доступа к информации о дорогах и улицах, отремонтированных за последние годы, включая стоимость работ, данные организации-подрядчика, гарантийные сроки, ссылки на закупочную документацию и другую информация о работах, а также обобщенная статистика по дорожным работам (например, дорожные работы, выполненные одним и тем же подрядчиком). ",
"description3": "Платформа также предоставит пользователям возможность оперативно сообщать о дефектах на дорогах и необходимости их устранения, голосовать за приоритетность ремонта дорог и предоставлять обратную связь в удобном формате, которые позволят гражданам самостоятельно анализировать и контролировать использование исполнения государственных дорожных контрактов. Информация по дорожным строительствам и ремонтам собирается из открытых данных портала госзакупок и Минюста.",
"description4":"Помимо веб-платформы имеются два мобильных приложения для платформ iOS и Android с функциями, аналогичными веб-сайту."
},
"home": {
"title": "Кыргызстандын жолдору",
"subtitle": "Жолдорду коопсуз кылалы!",
"info": "Жолдун абалы боюнча акыркы маалымат",
"report_broken_road": "Бузулган жолду белгилөө",
"road_map": "Жол картасы",
"latest_news": "Жол кыймылы, жасалып жатканы ж.б. тууралуу акыркы жаңылыктардан кабардар болуңуз!",
"enter_location": "Шаар, айыл же аймакты киргизиңиз",
"broken_roads": "Бузулган жол",
"accident_hotspots": "Авариялык абалда",
"local_defects": "Жергиликтүү дефект",
"repair_plans": "Ремонттоо планында",
"repaired": "Оңдолгон",
"fixed_local_defects": "Оңдолгон жергиликтүү дефект",
"rating": "Рейтинг",
"road_discussions": "Жолдорду талкуулоо: рейтинг, жеке тажрыйба, ылайык айдоо!",
"enter_address": "Даректи киргизиңиз",
"read_more": "Көбүрөөк маалымат"
},
"transparency_international_kyrgyzstan": {
"name": "Транспаренси Интернешнл-Кыргызстан",
"description": "Transparency International эл аралык уюмунун Кыргыз Республикасындагы бөлүмү.",
"mission": "Коррупциянын алдын алуу жана өлкөдө демократияны чыңдоо үчүн эффективдүү мамлекеттик саясатты жана ак ниет башкарууну алдыга жылдыруу.",
"goals_and_priorities": {
"anti-corruption_education": "Кыргызстандын калкын коррупцияга каршы күрөштүн мааниси жана зарылчылыгы жөнүндө коомчулуктун маалымдуулугун жогорулатуу;",
"study_of_corruption_practices": "Кыргызстанда жана башка өлкөлөрдө коррупция менен күрөшүүнүн тажрыйбасы жана теориясын изилдөөнү уюштуруу жана ага жарандык коом структураларынын катышуусу;",
"supporting_citizens_and_organizations": "Жарандарга жана уюмдарга алардын конституциялык укуктарын жана эркиндиктерин ишке ашырууга жардам көрсөтүү;",
"international_experience": "Коррупцияны азайтуу боюнча эл аралык тажрыйбага, анын технологияларын, ресурстарын колдонууга, ошондой эле коррупцияга каршы эл аралык диалогго жарандык коомдун түзүмдөрүн кошууга басымдуулук кылуу."
}
},
"volunteers": {
"activists": "Активисттер",
"received_votes": "Алынган добуштар",
"left_votes": "Калтырылган добуштар",
"rating": "Рейтинг"
},
"profile": {
"personal_cabinet": "Жеке кабинет",
"personal_data": "Жеке маалымат",
"my_appeals": "Менин кайрылууларым",
"logout": "Чыгуу",
"write_appeal": "Кайрылуу",
"profile_photo": "Сүрөт",
"others_identification": "Профиль сүрөтү аркылуу башка адамдар сизди тааный алышат, жана сиз кайсы аккаунтка киргениңиз белгилүү болот.",
"add_profile_photo": "Профиль сүрөтү кошуу",
"profile_photo_updated": "Сүрөттү жаңырланытуу",
"delete": "Жок кылуу",
"change": "Өзгөртүү"
},
"authorization": {
"change_password": "Сыр сөздү өзгөртүү",
"old_password": "Эски сыр сөз",
"enter_old_password": "Эски сыр сөздү киргизиңиз",
"new_password": "Жаңы сыр сөз",
"enter_new_password": "Жаңы сыр сөздү киргизиңиз",
"confirm_new_password": "Жаңы сыр сөздү ырастоо",
"confirm_new_password_prompt": "Сураныч, жаңы сыр сөздү ырастоо зарыл",
"password": "Сыр сөз",
"forgot_password": "Сыр сөздү унуттуңузбу?",
"login": "Кириш",
"register": "Каттоо",
"sign_in_account": "Аккаунтка кириңиз",
"enter_credentials": "Сураныч, киргизиңиз",
"login_via_google": "Google аркылуу кирүү",
"enter_password": "Сыр сөздү киргизиңиз",
"password_requirements": "Минимум 8 белги, 1 башкы буюк тамга жана 1 сандар",
"no_account_yet": "Аккаунтуңуз жокпу? Катталыңыз",
"registration": "Каттоо",
"register_now": "Каттоо",
"already_have_account": "Аккаунтуңуз барбы? Кириңиз",
"enter_email": "Электрондук почтаны киргизиңиз",
"enter_email_for_code": "Электрондук почтаны киргизиңиз, биз сизге сыр сөздү жөнөтөбүз",
"send_code": "Код жөнөтүү",
"confirm_code": "Кодту ырастоо",
"enter_code": "Кодду киргизиңиз",
"enter_reset_code": "Сыр сөздү өзгөртүп жаңыртуу үчүн кодту киргизиңиз",
"reset_code": "Сыр сөздү өзгөртүү коду",
"reset_password": "Сыр сөздү өзгөртүү",
"check_email": "Почтаны текшериңиз",
"code_sent_to": "Биз кодду name@gmail.com почтасына жөнөттүк",
"confirmation_code": "Ырастоо коду",
"confirm": "Ырастоо",
"resend_code_in": "Кодду кайра жөнөтүү",
"resend_code": "Кодду кайра жөнөтүү"
},
"send_report": {
"how_to_mark_road_section": "Жол бөлүмүн белгилөө",
"mark_road_instructions": "Төйнөгүчтү коюп, жолдун бир бөлүгүн тарта баштаңыз (ал каалаган сандагы сынык сызыктардан турушу мүмкүн).",
"remove_segment_instruction": "Жол бөлүгүн алып таштоо үчүн коюлган төйнөгүчтү кайра басыңыз.",
"add_problem_description": "Көйгөйдү сүрөттөңүз",
"enter_description": "Сүрөттөмөнү киргизиңиз",
"add_photos": "Сүрөт салыңыз",
"upload_photos_instructions": "Көйгөйдү ырастоо үчүн жолго тиешелүү 5 сүрөткө чеийн киргизсеңиз болот.",
"attach_file": "Сүрөт тиркөө (5 МБга чейин)",
"submit_for_moderation": "Модерацияга жиберүү",
"appeal_submitted": "Сиздин кайрылууңуз жиберилди",
"thanks_for_appeal": "Кайрылууңуз үчүн рахмат. Азырынча ал модерацияда.",
"view_my_appeals": "Менин кайрылууларымды көрүү"
},
"months": {
"january": "Үчтүн айы",
"february": "Бирдин айы",
"march": "Жалган куран",
"april": "Чын куран",
"may": "Бугу",
"june": "Кулжа",
"july": "Теке",
"august": "Баш оона",
"september": "Аяк оона",
"october": "Тогуздун айы",
"november": "Жетинин айы",
"december": "Бештин айы"
},
"validation_errors": {
"invalid_email_format": "Электрондук почтанын форматы туура эмес.",
"passwords_do_not_match": "Сыр сөздөрү туура келбейт.",
"required_field_not_filled": "Талап кылынган жер толтурулган эмес.",
"exceeded_maximum_length": "Тамгалардын максималдуу узундугу ашып кетти.",
"login_required_before_commenting": "Комментарий калтырыш үчүн жеке кабинетиңизге кириңиз же каттоодон өтүңүз.",
"login_required_before_like": "Лайк коюш үчүн жеке кабинетиңизге кириңиз же каттоодон өтүңүз."
},
"server_errors": {
"invalid_email_or_password": "Почта же сыр сөз туура эмес.",
"server_error_auth_attempt": "Авторизация аракетинде сервердеги ката.",
"login_failed": "Кирүүгө мүмкүн эмес. Кийинчирээк кайталап көрүңүз.",
"account_already_exists": "Бул почта катталган.",
"account_not_found": "Аккаунт табылган жок.",
"invalid_activation_code": "Код жараксыз.",
"invalid_activation_code_reset": "Активация коду жараксыз.",
"invalid_password_reset_code": "Сыр сөздү өзгөртүү коду жараксыз.",
"invalid_code": "Туура эмес код."
},
"disclaimer": {
"text": "Бул веб-сайт Европа Биримдиги тарабынан каржыланат. Анын мазмуну үчүн Трансперенси Интернешнл Кыргызстан гана жоопкерчиликтүү жана ал Европа Биримдигинин көз карашын сөзсүз түрдө чагылдырбайт."
},
"rights": {
"text": "Бардык укуктар корголгон"
},
"PrivacyPolicy":{
"titel0":"Купуялык саясаты",
"title1": "Жалпы жоболор",
"title2": "Жеке маалыматтарды чогултуу",
"title3": "Жеке маалыматтарды сактоо жана пайдалануу",
"title4": "Жеке маалыматтарды өткөрүп берүү",
"title5": "Жеке маалыматтарды жок кылуу",
"title6": "Жеке маалыматтарды коргоо",
"title7": "Колдонуучунун кайрылуулары",
"p1":"Бул Купуялык саясатынын билдирүүсү почта тизмесин, сайттын пикир формасын жана домендик аталышта жайгашкан электрондук почта даректерин колдонгон жеке адамдар (мындан ары - Колдонуучулар) жөнүндө маалыматты иштеп чыгуу жана коргоо тартибин аныктайт.",
"p1o":" (мындан ары Сайт деп аталат) домендик аталышта жайгашкан почталык таркатма, сайттын пикир билдирүү формасын жана электрондук почта даректерин колдонгон жеке адамдар (мындан ары - Колдонуучулар) жөнүндө маалыматты колдонуу жана коргоо тартибин аныктайт. Биз жеке маалыматтарды кантип иштетээрибизди сүрөттөгөн Купуялык саясатын иштеп чыктык - автоматташтыруу куралдарын колдонуу менен же мындай куралдарды колдонбостон аткарылган ар кандай аракеттер (операциялар) же аракеттердин (операциялардын) жыйындысы, анын ичинде жеке маалыматтарды чогултуу, жазып алуу, системалаштыруу, топтоо, сактоо, тактоо (жаңыртуу, өзгөртүү),  чыгаруу, пайдалануу, берүү (тараттуу,  көрсөтүү, жетүү),  өздүксүздөндүрүү,  жаап коюу,  өчүрүү, жок кылуу. Жеке маалыматтарды жана Сайттын колдонуучулары жөнүндө маалыматтарды иштетүүгө байланышкан мамилелер ушул Жобо менен жөнгө салынат. Бул Купуялык саясаты Сайттын колдонуучулары болуп саналган адамдар жөнүндө персоналдык маалыматтарды жана жеке мүнөздөгү маалыматтарды ( өздүктү тактаганга мүмкүндүк берген каалаган маалымат жана ага байланыштуу каалаган башка маалымат) иштетүүнүн бардык түрүн жөнгө салат. Бул Саясат дүйнөнүн каалаган жеринде жайгашкан адамдардан Интернет аркылуу да, аны колдонбостон да активдүү жана пассивдүү ар кандай каражаттар менен чогултулган жеке, персоналдык маалыматтарды иштетүүгө карата колдонулат.",
"p2":"Жеке маалыматтарды  иштетүүнүн максаты Оператор тарабынан Сайтты жана анын кызматтарын пайдалануу боюнча Колдонуучулардын алдындагы милдеттерин аткаруу болуп саналат. Колдонуучунун жеке маалыматтарын иштетүү жеке маалыматтар субъектинин анын жеке маалыматтарын иштетүүгө макулдугу менен жүзөгө ашырылат. Жеке маалыматтар  деп, аныкталган же аныкталуучу адамга (жеке маалыматтар субъектисине) түздөн-түз же кыйыр түрдө тиешелүү жана аныкталган адамды идентификациялоо же аны менен байланыштыруу үчүн колдонулушу мүмкүн болгон  каалаган  маалымат. Сиздин макулдугуңуз менен сизди почталык таркатмага киргизүү, ошондой эле Сайттагы пикир билдирүү формасы аркылуу биз менен байланышсаңыз, сизге жооп берүү үчүн атыңызды, фамилияңызды, атаңыздын атыңыздын жана электрондук почта дарегиңизди сурашыбыз мүмкүн.",
"p3": "Колдонуучулардын жеке маалыматтары электрондук алып жүрүүчүлөрдө гана сакталат жана автоматташтырылган системалардын жардамы менен иштетилет.",
"p4": "БИЗ СИЗДИН ЖЕКЕ МААЛЫМАТТАРЫНЫЗДЫ КАНТИП КОЛДОНОБУЗ",
"p5":"Биз чогулткан жеке маалыматтар бизге коррупцияга каршы күрөшүү тармагындагы жаңылыктар жана ар кандай окуялар жөнүндө электрондук почта билдирүүлөрүн жөнөтүүгө мүмкүндүк берет. Ошондой эле, сиздин жеке маалыматтарыңыз пикир билдирүү формасы аркылуу же Сайтта жайгашкан биздин электрондук почта дарегибизге жөнөтүлгөн сурооңузга жооп берүү учурунда колдонулушу мүмкүн. Эгерде сиз биздин почта таркатма тизмебизге кирүүнү каалабасаңыз, анда көрсөтүлгөн пикир билдирүү байланыштар аркылуу бизге маалымдоо аркылуу жазылууну каалаган убакта баш тартсаңыз болот.",
"p6":"ЖЕКЕ ЭМЕС МААЛЫМАТТАРДЫ ЧОГУЛТУУ ЖАНА КОЛДОНУУ",
"p7":"Биз ошондой эле жеке эмес саналган персоналдык маалыматтарды чогултабыз - кандайдыр бир аныкталган адам менен түздөн-түз байланышта болбогон маалымат. Биз ар кандай максатта жеке эмес маалыматты чогултабыз, колдонобуз, өткөрүп бере алабыз жана ачып бере алабыз. Төмөндө биз чогулткан жеке эмес маалыматтын мисалдары жана биз аны кантип колдонорубуз көрсөтүлгөн. Биз Сайтка өтүүнүн булагы, Сайттагы колдонуучунун жүрүм-турум өзгөчүлүктөрү, Сайтта каралган барактардын саны сыяктуу персоналдык маалыматтарды чогултушубуз мүмкүн. Мындай маалыматтар анонимдүү жана жеке эмес. Бул маалыматтар Сайттын конокторун кандай маалымат кызыктырарын жакшыраак түшүнүү жана мазмундун сапатын жакшыртуу максатында чогултулат.",
"p8":"Колдонуучулардын жеке маалыматтары үчүнчү жактарга берилбейт. Ушул Эрежелер күчүнө киргенге чейин бизге өздөрүнүн жеке маалыматтарын берген колдонуучулар автоматтык түрдө өздөрүнүн жеке маалыматтарын пайдаланууга макул болушат. Ошол эле учурда алар ар дайым пикир билдирүү формасы аркылуу жеке маалыматтарды колдонууга макулдугун кайта чакырып ала алышат. Оператор  Колдонуучудан же анын мыйзамдуу өкүлүнөн арыз же суроо-талап түшкөн учурдан тартып, такталбаган жеке маалыматтар же мыйзамсыз аракеттер аныкталган учурда  тиешелүү Колдонуучуга таандык жеке маалыматтарды жаап коет.",
"p9": "Жеке маалыматтардын субъекти жеке маалыматтарды иштетүүгө макулдугун  кайта чакырып алса, колдонуучунун жеке маалыматтары жок кылынат.",
"p10":"ИДЕНФИКАЦИЯЛЫК ФАЙЛДАР (КУКИ) ЖАНА БАШКА ТЕХНОЛОГИЯЛАР",
"p11":"Вебсайт, интерактивдүү кызматтар жана тиркемелер, электрондук почта билдирүүлөрү жана биздин атыбыздан башка коммуникациялар идентификациялык куки файлдарын (cookies) жана башка пиксел энбелгилерин (pixel tags), веб-маяктар (web beacons) сыяктуу башка технологияларды колдонушу мүмкүн. Демек, веб-сайтты жана башка кызматтарды колдонууда, сиздин түзмөгүңүздө кукилерди колдонуу жөнүндө эскерткен калкыма терезе пайда болушу мүмкүн. Бул технологиялар бизге колдонуучунун жүрүм-турумун жакшыраак түшүнүүгө, биздин сайттын кайсы бөлүктөрүнө колдонуучулар киргенин айтып берүүгө жана жарнаманын жана онлайн издөөлөрдүн натыйжалуулугун өлчөөгө жардам берет. Биз кукилер жана башка технологиялар тарабынан чогултулган маалыматты жеке эмес маалымат деп эсептейбиз. Сиз браузериңиздеги же түзмөгүңүздөгү нускамаларды аткаруу менен cookie файлдарын көзөмөлдөй аласыз. Адатта, сиз куки жөндөөлөрүңүздү 'Жөндөөлөр' же 'Система жөндөөлөрү' бөлүмүнөн өзгөртө аласыз. Көпчүлүк веб-сайттардагыдай эле, биз кээ бир маалыматты автоматтык түрдө чогултуп, статистикалык файлдарда сактайбыз. Бул маалыматка Интернет Протоколунун (IP) дареги, браузердин түрү жана тили, Интернет-кызмат провайдери, шилтеме берүү жана чыгуу баракчалары, операциялык тутум маалыматы, дата жана убакыт маалыматы жана кирип чыгуу маалыматы кирет. Биз мындай маалыматты тенденцияларды түшүнүү жана талдоо, сайтты башкаруу, сайттагы колдонуучунун жүрүм-турумун изилдөө жана жалпы колдонуучуларыбыз жамааты жөнүндө демографиялык маалыматты чогултуу үчүн колдонобуз. Уюм мындай маалыматты өзүнүн маркетингдик максаттары үчүн колдоно алат. Кээ бир электрондук почта билдирүүлөрүндө биз Сайтта жайгаштырылган маалыматка интерактивдүү шилтемелерди колдонобуз. Колдонуучулар биздин веб-сайттагы көздөгөн бетке жеткенге чейин мындай шилтемелер боюнча өткөндө, алардын суроо-талаптары өзүнчө катталат. Биз белгилүү бир темаларга болгон кызыгууну аныктоого жана колдонуучулар менен болгон байланышыбыздын натыйжалуулугун өлчөөгө жардам берүү үчүн бул 'өтүү' маалыматына көз салабыз. Эгер сиз өзүңүздүн байланыштарыңызга ушундай жол менен көз салууну каалабасаңыз, электрондук почта билдирүүлөрүндөгү тексттик же сүрөт шилтемелерин боюнча өтпөшүңүз керек . Пиксел энбелгилери бизге электрондук почта билдирүүлөрүн керектөөчү окуй турган форматта жеткирүүгө жана мындай билдирүүлөр окулгандыгы жөнүндө маалымат берет. Биз мындай маалыматты керектөөчүлөргө жөнөткөн байланыштарды чектөө же токтотуу үчүн колдонушубуз мүмкүн.",
"p12":"Колдонуучунун жеке маалыматтарын уруксатсыз же кокустан  жетүүдөн, жок кылуудан, өзгөртүүдөн,  жаап коюудан, көчүрүүдөн, жайылтуудан, ошондой эле үчүнчү жактардын башка мыйзамсыз аракеттеринен коргоону камсыз кылуу үчүн биз сиздин жеке маалыматтарыңыздын корголушун камсыз кылуу боюнча чараларды көрөбүз.",
"p13":"ЖЕКЕ МААЛЫМАТТАРДЫН БҮТҮНДҮГҮ ЖАНА САКТАЛЫШЫ",
"p14":"Биз сиздин жеке маалыматтарыңызды ушул Купуялык саясатында сүрөттөлгөн максаттарды аткаруу үчүн зарыл болгон мезгил ичинде сактайбыз. Биз жашы жете электердин жеке маалыматын чогултпайбыз. Эгер биз жашы жете элек бала жөнүндө жеке маалыматты чогултканыбызды билсек, биз мындай маалыматты мүмкүн болушунча тезирээк жок кылуу үчүн чараларды көрөбүз. Биз ата-энелерге жана көзөмөлүндө жашы жете элек балдар бар башка адамдарга (мыйзамдуу өкүлдөр - ата-энелер, асырап алуучулар же камкорчулар) жашы жете электердин веб-сайттарды колдонуусун көзөмөлдөөнү туруктуу сунуштайбыз. Оператор Интернетти же Сайттын Кызматтарын колдонуунун натыйжасында Колдонуучу жөнүндө маалыматка жетүү мүмкүнчүлүгүнө ээ болгон үчүнчү жактардын аракеттери үчүн жана Сайттын мүнөзүнө байланыштуу каалаган интернет колдонуучуга жеткиликтүү маалыматтарды колдонуунун кесепеттери үчүн жоопкерчилик тартпайт.",
"p15":"КУПУЯЛЫК ЖӨНҮНДӨ СУРООЛОРУ",
"p16":"Бул Купуялык саясатына же маалыматтарды иштетүүгө байланыштуу суроолоруңуз болсо, kyrgyzstan@transparency.org байланыш дареги аркылуу биз менен байланышсаңыз болот.",
"p17":"Колдонуучулар Операторго өздөрүнүн суроо-талаптарын, анын ичинде алардын жеке маалыматтарын колдонууга, жеке маалыматтарды иштетүүгө макулдукту жокко чыгарууга байланыштуу суроо-талаптарды ушул жобонун Жалпы жоболор бөлүмүндө көрсөтүлгөн дарек боюнча жазуу жүзүндө же пикир билдирүү формасы аркылуу жөнөтүлгөн электрондук документ формасында жөнөтүүгө укуктуу. Оператор суроо-талапты алган күндөн тартып 30 күндүн ичинде Колдонуучунун суроо-талабын карап чыгууга жана ага жооп жөнөтүүгө милдеттенет.",
"p18":"БАШКАЛАР",
"p19":"Уюмдун веб-сайтына өзүнүн жеке маалыматтарын жана маалыматын берген конок ушул Купуялык саясатынын жоболоруна макул болот. Колдонуучунун макулдугусуз Колдонуучулар тарабынан билдирилген жеке маалыматтарга уруксатсыз кирүүдөн коргоо тутумун андан ары өркүндөтүү максатында, биз каалаган убакта ушул Саясатка өзгөртүүлөрдү киргизүү укугун сактайбыз. Купуялык саясатыбызга олуттуу өзгөртүүлөрдү киргизгенде, биз Купуялык саясатыбыздын жаңыртылган версиясы менен бирге веб-сайтыбызга билдирүү жарыялайбыз. Бул Саясат үчүнчү жактардын аракеттерине жана интернет-ресурстарына жайылтылбайт."
}
}

221
messages/ru.json Normal file
View File

@ -0,0 +1,221 @@
{
"general": {
"date": "Дата",
"address": "Адрес",
"status": "Статус",
"description": "Описание",
"reviews": "Комментарии",
"rating": "Рейтинг",
"review": "Комментарий",
"write_comment": "Написать комментарий",
"search": "Поиск",
"search_for": "Искать",
"city": "Город",
"added_roads": "Добавлено дорог",
"broken_roads": "Разбитых дорог",
"accident_hotspots": "Очагов аварийности",
"local_defects": "Локальных дефектов",
"repair_plans": "В планах ремонта",
"repaired": "Отремонтировано",
"fixed_local_defects": "Локальных дефектов исправлено",
"news": "Новости",
"details": "Подробнее",
"navigation": "Навигация",
"contacts": "Контакты",
"download_our_app": "Скачивай наше приложение",
"socials": "Социальные сети",
"back": "Назад",
"save": "Сохранить",
"saving": "Сохранение",
"cancel": "Отменить",
"cancellation": "Отмена",
"save_changes": "Сохранить изменения",
"send": "Отправить",
"receive": "Получить",
"delete": "Удалить",
"show_on_map": "Показать на карте",
"author_of_appeal": "Автор обращения",
"enter_city": "Введите населенный пункт",
"page_not_found": "Страница не найдена (404)",
"incorrect_address_or_nonexistent_page": "Неправильно набран адрес или такой страницы не существует.",
"home": "На главную",
"first_name": "Имя",
"last_name": "Фамилия",
"email": "Электронная почта"
},
"navigation": {
"home": "Главная",
"about_us": "О нас",
"privacy": "Политика конфиденциальности",
"support": "Поддержка",
"statistics": "Статистика",
"news": "Новости",
"volunteers": "Волонтеры",
"profile": "Профиль",
"login": "Войти"
},
"home": {
"title": "Дороги Кыргызстана",
"subtitle": "Сделаем дороги безопасными!",
"info": "Актуальная информация о состоянии дорог",
"report_broken_road": "Отметить разбитую дорогу",
"road_map": "Карта дорог",
"latest_news": "Будьте в курсе последних новостей о дорожном движении, строительствах и мероприятиях!",
"enter_location": "Введите город, село или регион",
"broken_roads": "Разбитая дорога",
"accident_hotspots": "Очаг аварийности",
"local_defects": "Локальный дефект",
"repair_plans": "В плане ремонта",
"repaired": "Отремонтировано",
"fixed_local_defects": "Локальный дефект исправлен",
"rating": "Рейтинг",
"road_discussions": "Обсуждаем дороги: рейтинг, опыт, комфорт в пути!",
"enter_address": "Введите адрес",
"read_more": "Читать"
},
"about_us": {
"name": "О нас",
"description1": "Transparency International ̶ это глобальная независимая организация по изучению и борьбе с коррупцией как в международном масштабе, так и в масштабе отдельных стран, одна из целей которой добиться большей прозрачности и подотчетности власти.",
"description2": "Команда Трансперенси Интернешнл Кыргызстан разработала платформу для мониторинга строительства и ремонта дорог, содержащая интерактивную карту по обеспечению доступа к информации о дорогах и улицах, отремонтированных за последние годы, включая стоимость работ, данные организации-подрядчика, гарантийные сроки, ссылки на закупочную документацию и другую информация о работах, а также обобщенная статистика по дорожным работам (например, дорожные работы, выполненные одним и тем же подрядчиком). ",
"description3": "Платформа также предоставит пользователям возможность оперативно сообщать о дефектах на дорогах и необходимости их устранения, голосовать за приоритетность ремонта дорог и предоставлять обратную связь в удобном формате, которые позволят гражданам самостоятельно анализировать и контролировать использование исполнения государственных дорожных контрактов. Информация по дорожным строительствам и ремонтам собирается из открытых данных портала госзакупок и Минюста.",
"description4":"Помимо веб-платформы имеются два мобильных приложения для платформ iOS и Android с функциями, аналогичными веб-сайту."
},
"volunteers": {
"activists": "Активисты",
"received_votes": "Получено голосов",
"left_votes": "Оставлено голосов",
"rating": "Рейтинг"
},
"profile": {
"personal_cabinet": "Личный кабинет",
"personal_data": "Личные данные",
"my_appeals": "Мои обращения",
"logout": "Выйти из аккаунта",
"write_appeal": "Написать обращение",
"profile_photo": "Фото профиля",
"others_identification": "По фото профиля другие люди смогут вас узнавать, а вам будет проще определять, в какой аккаунт вы вошли.",
"add_profile_photo": "Добавить фото профиля",
"profile_photo_updated": "Фото профиля обновлено",
"delete": "Удалить",
"change": "Сменить"
},
"authorization": {
"change_password": "Изменить пароль",
"old_password": "Старый пароль",
"enter_old_password": "Введите старый пароль",
"new_password": "Новый пароль",
"enter_new_password": "Введите новый пароль",
"confirm_new_password": "Подтвердить новый пароль",
"confirm_new_password_prompt": "Пожалуйста, подтвердите новый пароль",
"password": "Пароль",
"forgot_password": "Забыли пароль?",
"login": "Войти",
"register": "Зарегистрироваться",
"sign_in_account": "Войдите в аккаунт",
"enter_credentials": "Пожалуйста, введите свои данные",
"login_via_google": "Войти через Google",
"enter_password": "Введите пароль",
"password_requirements": "Минимум 8 символов, 1 заглавная буква и цифра",
"no_account_yet": "Еще нет аккаунта? Зарегистрируйтесь",
"registration": "Регистрация",
"register_now": "Зарегистрировать",
"already_have_account": "Уже есть аккаунт? Войти в аккаунт",
"enter_email": "Введите электронную почту",
"enter_email_for_code": "Введите электронную почту и мы отправим код для восстановления пароля",
"send_code": "Отправить код",
"confirm_code": "Потвердить код",
"enter_code": "Введите код",
"enter_reset_code": "Введите код для сброса и восстановления пароля",
"reset_code": "Код сброса пароля",
"reset_password": "Сбросить пароль",
"check_email": "Проверьте свою почту",
"code_sent_to": "Мы отправили код на почту name@gmail.com",
"confirmation_code": "Код подтверждения",
"confirm": "Подтвердить",
"resend_code_in": "Отправить код повторно через",
"resend_code": "Отправить код повторно"
},
"send_report": {
"how_to_mark_road_section": "Как отметить участок дороги?",
"mark_road_instructions": "Поставьте булавку и начните рисовать участок дороги (он может состоять из любого количества ломаных линий).",
"remove_segment_instruction": "Чтобы удалить отрезок, нажмите на точки повторно.",
"add_problem_description": "Добавьте описание проблемы",
"enter_description": "Введите описание",
"add_photos": "Добавьте фотографии",
"upload_photos_instructions": "Загрузите до 5 фотографий, связанных с дорогой, которую вы хотите отметить. Фотографии помогут лучше понять проблему.",
"attach_file": "Прикрепить файл (до 5 МБ)",
"submit_for_moderation": "Отправить на модерацию",
"appeal_submitted": "Ваше обращение отправлено",
"thanks_for_appeal": "Спасибо за ваше обращение. На данный момент оно в модерации.",
"view_my_appeals": "Смотреть мои обращения"
},
"months": {
"january": "Январь",
"february": "Февраль",
"march": "Март",
"april": "Апрель",
"may": "Май",
"june": "Июнь",
"july": "Июль",
"august": "Август",
"september": "Сентябрь",
"october": "Октябрь",
"november": "Ноябрь",
"december": "Декабрь"
},
"validation_errors": {
"invalid_email_format": "Неверный формат электронной почты.",
"passwords_do_not_match": "Пароли не совпадают.",
"required_field_not_filled": "Обязательное поле не заполнено.",
"exceeded_maximum_length": "Превышена максимальная длина поля.",
"login_required_before_commenting": "Перед тем как оставить комментарий, пожалуйста, войдите или зарегистрируйтесь.",
"login_required_before_like": "Перед тем как поставить лайк, пожалуйста, войдите или зарегистрируйтесь."
},
"server_errors": {
"invalid_email_or_password": "Неверная почта или пароль.",
"server_error_auth_attempt": "Серверная ошибка при попытке авторизации.",
"login_failed": "Не удалось войти в систему. Что-то пошло не так, попробуйте еще раз немного позже.",
"account_already_exists": "Такая учетная запись уже существует.",
"account_not_found": "Такой учетной записи не существует.",
"invalid_activation_code": "Код не действителен.",
"invalid_activation_code_reset": "Код активации не действителен.",
"invalid_password_reset_code": "Код сброса пароля не действителен.",
"invalid_code": "Неверный код."
},
"disclaimer": {
"text": "Этот веб-сайт финансируется Европейским Союзом. Ответственность за его содержание лежит исключительно на Трансперенси Интернешнл Кыргызстан и не обязательно отражает точку зрения Европейского Союза."
},
"rights": {
"text": "Все права защищены"
},
"PrivacyPolicy":{
"titel0":"Политика конфиденциальности",
"title1": "Общие положения",
"title2": "Сбор персональных данных",
"title3": "Хранение и использование персональных данных",
"title4": "Передача персональных данных",
"title5": "Уничтожение персональных данных",
"title6": "Защита персональных данных",
"title7": "Обращения пользователей ",
"p1":"Настоящее Положение о политике конфиденциальности определяет порядок обработки и защиты информации о физических лицах (далее — Пользователи), пользующихся почтовой рассылкой, формой обратной связи сайта, электронными почтовыми адресами, расположенными на доменном имени ",
"p1o":" (далее — Сайт). Мы разработали Политику Конфиденциальности, которая описывает, как мы осуществляем обработку персональных данных — любые действия (операции) или совокупность действий (операций), совершаемых с использованием средств автоматизации или без использования таких средств с персональными данными, включая сбор, запись, систематизацию, накопление, хранение, уточнение (обновление, изменение), извлечение, использование, передачу (распространение, предоставление, доступ), обезличивание, блокирование, удаление, уничтожение персональных данных. Отношения, связанные с обработкой персональных данных и информации о пользователях Сайта, регулируются настоящим Положением. Настоящая Политика Конфиденциальности регулирует любой вид обработки персональных данных и информации личного характера (любой информации, позволяющей установить личность, и любой иной информации, связанной с этим) о физических лицах, которые являются пользователями Сайта. Настоящая Политика распространяется на обработку личных, персональных данных, собранных любыми средствами, как активными, так и пассивными, как через Интернет, так и без его использования, от лиц, находящихся в любой точке мира.",
"p2": "Целью обработки персональных данных является выполнение обязательств Оператора перед Пользователями в отношении использования Сайта и его сервисов. Обработка персональных данных пользователей осуществляется с согласия субъекта персональных данных на обработку его персональных данных. Под персональными данными понимается любая информация, относящаяся к прямо или косвенно определенному или определяемому физическому лицу (субъекту персональных данных) и которая может быть использована для идентификации определенного лица либо связи с ним. Мы можем запросить у Вас имя, фамилию, отчество и адрес электронной почты для того, чтобы включить Вас в почтовую рассылку с Вашего согласия, а также для того, чтобы ответить Вам, если Вы обратились к нам через форму обратной связи на Сайте.",
"p3": "Персональные данные Пользователей хранятся исключительно на электронных носителях и обрабатываются с использованием автоматизированных систем. ",
"p4": "КАК МЫ ИСПОЛЬЗУЕМ ВАШУ ПЕРСОНАЛЬНУЮ ИНФОРМАЦИЮ",
"p5":"Собираемые нами персональные данные позволяют направлять Вам по электронной почте уведомления о новостях и различных событиях в сфере антикоррупции. Также ваши персональные данные могут быть использованы при ответе на ваше обращение, направленное через форму обратной связи или на наш адрес электронной почты, расположенные на Сайте. Если Вы не желаете быть включенным в наш список рассылки, Вы можете в любое время отказаться от рассылки путём информирования нас по указанным контактам для обратной связи.",
"p6":"СБОР И ИСПОЛЬЗОВАНИЕ ИНФОРМАЦИИ, НЕ ЯВЛЯЮЩЕЙСЯ ПЕРСОНАЛЬНОЙ",
"p7":"Мы также собираем персональные данные, не являющиеся персональными данные, не позволяющие прямо ассоциировать их с каким-либо определённым лицом. Мы можем собирать, использовать, передавать и раскрывать информацию, не являющуюся персональной, для любых целей. Ниже приведены примеры информации, не являющейся персональной, которую мы собираем, и как мы можем её использовать. Мы можем собирать персональные данные, такие как: источник перехода на Сайт, особенности поведения пользователя на Сайте, количество просмотренных на Сайте страниц. Такие данные обезличены и не являются персональными. Эти данные собираются для того, чтобы лучше понимать, какая информация интересна посетителям Сайта и улучшать качество контента.",
"p8":"Персональные данные Пользователей не передаются каким-либо третьим лицам. Пользователи, которые передали нам свои персональные данные до вступления в силу настоящих Правил автоматически соглашаются на использование своих персональных данных. В тоже время, они всегда могут отозвать свое согласие на использование персональных данных через форму обратной связи. Оператор осуществляет блокирование персональных данных, относящихся к соответствующему Пользователю, с момента обращения или запроса Пользователя или его законного представителя, в случае выявления недостоверных персональных данных или неправомерных действий.",
"p9":"Персональные данные пользователя уничтожаются при отзыве субъектом персональных данных согласия на обработку персональных данных.",
"p10":"ИДЕНТИФИКАЦИОННЫЕ ФАЙЛЫ (СOOKIES) И ИНЫЕ ТЕХНОЛОГИИ",
"p11":"Веб-сайт, интерактивные услуги и приложения, сообщения электронной почты и любые иные коммуникации от нашего лица могут использовать идентификационные файлы cookies и иные технологии, такие как пиксельные ярлыки (pixel tags), веб-маяки (web beacons). Соответственно при пользовании веб-сайтом и иными услугами на Вашем устройстве может возникнуть всплывающее окно с уведомлением об использовании cookies. Указанные технологии помогают нам лучше понимать поведение пользователей, сообщают нам, какие разделы нашего сайта были посещены пользователями, и измеряют эффективность рекламы и сетевых поисков. Мы рассматриваем информацию, собираемую файлами cookies и иными технологиями как информацию, не являющуюся персональной. Вы можете контролировать cookies следуя инструкциям Вашего браузера или устройства. Как правило, Вы можете изменить настройки cookies в разделе «Настройки» или «Системные настройки». Как и в случае большинства веб-сайтов, мы собираем некоторую информацию автоматически и храним её в файлах статистики. Такая информация включает в себя адрес Интернет-протокола (IP-адрес), тип и язык браузера, информацию о поставщике Интернет-услуг, страницы отсылки и выхода, сведения об операционной системе, отметку даты и времени, а также сведения о посещениях. Мы используем такую информацию для понимания и анализа тенденций, администрирования сайта, изучения поведения пользователей на сайте и сбора демографической информации о нашем основном контингенте пользователей в целом. Организация может использовать такую информацию в своих маркетинговых целях. В некоторых наших сообщениях электронной почты мы используем интерактивные ссылки на информацию, размещённую на Сайте. Когда пользователи проходят по таким ссылкам, прежде чем они попадают на страницу назначения на нашем веб-сайте, их запросы проходят отдельную регистрацию. Мы отслеживаем такие «проходные» данные, для того чтобы помочь нам определить интерес к отдельным темам и измерить эффективность наших коммуникаций с пользователями. Если Вы предпочитаете, чтобы Ваши обращения не отслеживались подобным образом, Вы не должны проходить по текстовым или графическим ссылкам в сообщениях электронной почты. Пиксельные ярлыки позволяют нам направлять сообщения электронной почты в формате, читаемом потребителями, и сообщают нам, были ли такие сообщения прочитаны. Мы можем использовать такую информацию для ограничения количества направляемых потребителям сообщений или прекращения их направления.",
"p12":"Мы предпринимает меры предосторожности для обеспечения защиты Ваших персональных данных в целях обеспечения защиты персональных данных Пользователя от неправомерного или случайного доступа к ним, уничтожения, изменения, блокирования, копирования, распространения, а также от иных неправомерных действий третьих лиц.",
"p13":"ЦЕЛОСТНОСТЬ И СОХРАНЕНИЕ ПЕРСОНАЛЬНОЙ ИНФОРМАЦИИ",
"p14":"Мы будем хранить Ваши персональные данные и информацию в течение срока, необходимого для выполнения целей, описываемых в настоящей Политике Конфиденциальности. Мы не собираем персональные данные о несовершеннолетних. Если нам станет известно о том, что мы получили персональные данные о несовершеннолетнем, мы предпримем меры для удаления такой информации в максимально короткий срок. Мы настоятельно рекомендуем родителям и иным лицам, под чьим присмотром находятся несовершеннолетние (законные представители — родители, усыновители или попечители), контролировать использование несовершеннолетними веб-сайтов. Оператор не несет ответственности за действия третьих лиц, получивших в результате использования Интернета или Услуг Сайта доступ к информации о Пользователе и за последствия использования данных и информации, которые, в силу природы Сайта, доступны любому пользователю сети Интернет.",
"p15":"ВОПРОСЫ ОТНОСИТЕЛЬНО КОНФИДЕНЦИАЛЬНОСТИ",
"p16":"Если у вас возникнут вопросы в отношении настоящей Политики Конфиденциальности или обработки данных, Вы можете связаться с нами по контактам обратной связи kyrgyzstan@transparency.org.",
"p17":"Пользователи вправе направлять Оператору свои запросы, в том числе запросы относительно использования их персональных данных, направления отзыва согласия на обработку персональных данных в письменной форме по адресу, указанному в разделе Общие положения настоящего положения, или в форме электронного документа, отправленного посредством формы обратной связи. Оператор обязуется рассмотреть и направить ответ на поступивший запрос Пользователя в течение 30 дней с момента поступления обращения.",
"p18":"ДРУГОЕ",
"p19": "Посетитель сайта Организации, предоставляющий свои персональные данные и информацию, тем самым соглашается с положениями данной Политики Конфиденциальности. Мы оставляем за собой право вносить любые изменения в Политику в любое время по своему усмотрению с целью дальнейшего совершенствования системы защиты от несанкционированного доступа к сообщаемым Пользователями персональным данным без согласия Пользователя. Когда мы вносим существенные изменения в Политику Конфиденциальности, на нашем сайте размещается соответствующее уведомление вместе с обновлённой версией Политики Конфиденциальности. Действие настоящей Политики не распространяется на действия и интернет-ресурсы третьих лиц."
}
}

View File

@ -1,5 +1,9 @@
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {
distDir: "build",
images: {
remotePatterns: [
{
@ -8,6 +12,10 @@ const nextConfig = {
},
],
},
env: {
CLIENT_ID: process.env.CLIENT_ID,
CLIENT_SECRET: process.env.CLIENT_SECRET,
},
};
export default nextConfig;
export default withNextIntl(nextConfig);

5900
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,24 +5,29 @@
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"start": "next start -p 8003",
"lint": "next lint"
},
"dependencies": {
"@next/third-parties": "^14.1.0",
"axios": "^1.6.5",
"dotenv": "^16.4.1",
"html-react-parser": "^5.1.15",
"leaflet": "^1.9.4",
"next": "14.1.0",
"next": "^14.1.0",
"next-auth": "^4.24.5",
"next-intl": "^3.9.0",
"react": "^18",
"react-dom": "^18",
"react-leaflet": "^4.2.1",
"sass": "^1.70.0",
"sharp": "^0.33.2",
"use-debounce": "^10.0.0",
"zustand": "^4.5.0"
},
"devDependencies": {
"@types/leaflet": "^1.9.8",
"@types/node": "^20",
"@types/node": "^20.11.16",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-leaflet": "^3.0.0",

3589
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
.app {
padding-top: 78px;
}
@media screen and (max-width: 768px) {
.app {
padding-top: 72px;
}
}

View File

@ -1,21 +0,0 @@
import "./globals.scss";
import "./fonts.scss";
import "./App.scss";
import Footer from "@/widgets/Footer/Footer";
import Navbar from "@/widgets/Navbar/Navbar";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<Navbar />
<div className="app">{children}</div>
<Footer />
</body>
</html>
);
}

View File

@ -1,9 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700;800;900&display=swap");
@import url("https://fonts.cdnfonts.com/css/arial");
@import url("../shared/fonts/TildaSans.css");
* {
font-family: Tilda Sans;
}

View File

@ -1,25 +0,0 @@
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
button,
a {
cursor: pointer;
color: black;
display: flex;
align-items: center;
justify-content: center;
border: none;
background-color: transparent;
text-decoration: none;
font-size: 16px;
}
ul,
ol,
li {
list-style-type: none;
list-style: none;
}

View File

@ -1,47 +0,0 @@
.section-header {
margin-bottom: 52px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
text-align: center;
h3 {
font-size: 42px;
font-weight: 500;
line-height: 50px;
}
p {
font-size: 24px;
font-weight: 300;
line-height: 29px;
}
}
@media screen and (max-width: 768px) {
.section-header {
margin-bottom: 42px;
h3 {
font-size: 36px;
line-height: 43px;
}
}
}
@media screen and (max-width: 550px) {
.section-header {
margin-bottom: 42px;
h3 {
font-size: 24px;
line-height: 29px;
}
p {
font-size: 18px;
line-height: 22px;
}
}
}

View File

@ -1,22 +0,0 @@
import "./SectionHeader.scss";
interface ISectionHeaderProps {
title: string;
description?: string;
style?: object;
}
const SectionHeader: React.FC<ISectionHeaderProps> = ({
title,
description,
style,
}: ISectionHeaderProps) => {
return (
<div style={style} className="section-header">
<h3>{title}</h3>
{description && <p>{description}</p>}
</div>
);
};
export default SectionHeader;

View File

@ -1,2 +0,0 @@
export const baseAPI =
"https://api.kgroaduat.fishrungames.com/api/v1";

View File

@ -1,5 +0,0 @@
export interface IList {
count: number | null;
previous: number | null;
next: number | null;
}

View File

@ -1,32 +0,0 @@
export interface IAuthor {
id: number;
first_name: string;
last_name: string;
govern_status: unknown;
}
export interface ILocation {
id: 4;
latitude: number;
longitude: number;
address: string;
}
export interface IReportImage {
id: number;
image: string;
}
export interface IReport {
results: any;
id: number;
image: IReportImage[];
description: string;
author: IAuthor;
created_at: string;
updated_at: string;
location: ILocation[];
category: number;
total_likes: number;
count_reviews: number;
}

View File

@ -1,7 +0,0 @@
export const LINKS = [
{ id: 1, pagename: "Главная", pathname: "/" },
{ id: 2, pagename: "О нас", pathname: "/about-us" },
{ id: 3, pagename: "Статистика", pathname: "/statistics" },
{ id: 4, pagename: "Новости", pathname: "/news" },
{ id: 5, pagename: "Волонтеры", pathname: "/volunteers" },
];

View File

@ -1,26 +0,0 @@
export const ROAD_TYPES_STATS: Record<number, string> = {
1: "Разбитых дорог",
2: "Очагов аварийности",
3: "Локальных дефектов",
4: "В планах ремонта",
5: "Отремонтировано",
6: "Локальных дефектов исправлено",
};
export const ROAD_TYPES: Record<number, string> = {
1: "Разбитая дорога",
2: "Очаг аварийности",
3: "Локальный дефект",
4: "В планах ремонта",
5: "Отремонтировано",
6: "Локальный дефект исправлен",
};
export const ROAD_TYPES_COLORS: Record<number, string> = {
1: "rgb(230, 68, 82)",
2: "rgb(194, 136, 226)",
3: "rgb(135, 40, 157)",
4: "rgb(255, 172, 51)",
5: "rgb(254, 211, 99)",
6: "rgb(143, 222, 106)",
};

View File

@ -1,60 +0,0 @@
import "./Footer.scss";
import logo from "../../shared/assets/logo.svg";
import Link from "next/link";
import Image from "next/image";
import { LINKS } from "@/shared/variables/links";
import youtube from "./icons/youtube.svg";
import facebook from "./icons/facebook.svg";
import instagram from "./icons/instagram.svg";
import app_store_btn from "./icons/app-store-btn.svg";
import play_market_btn from "./icons/play-market-btn.svg";
const Footer = () => {
return (
<footer className="footer">
<Link href="/">
<Image src={logo} alt="Logo" />
</Link>
<div className="footer__links">
<h4>Навигация</h4>
<ul>
{LINKS.map((link) => (
<li>
<Link href={link.pathname} key={link.id}>
{link.pagename}
</Link>
</li>
))}
</ul>
</div>
<div className="footer__contacts">
<h4>Контакты</h4>
<ul>
<li>namename@gmail.com</li>
<li>+09646895467</li>
<li>
{[youtube, facebook, instagram].map((net) => (
<Link href="#">
<Image src={net} alt="Net Icon" key={net} />
</Link>
))}
</li>
</ul>
</div>
<div className="footer__apps">
<h4>Скачивай наше приложение</h4>
<div className="footer__apps-btns">
{[app_store_btn, play_market_btn].map((app) => (
<Link href="#">
<Image src={app} alt="App Button" key={app} />
</Link>
))}
</div>
</div>
</footer>
);
};
export default Footer;

View File

@ -1,88 +0,0 @@
"use client";
import "./HomeMap.scss";
import "leaflet/dist/leaflet.css";
import {
MapContainer,
Marker,
Popup,
TileLayer,
} from "react-leaflet";
import geo_green_icon from "./icons/geo-green.svg";
import geo_orange_icon from "./icons/geo-orange.svg";
import geo_pink_icon from "./icons/geo-pink.svg";
import geo_purple_icon from "./icons/geo-purple.svg";
import geo_red_icon from "./icons/geo-red.svg";
import geo_yellow_icon from "./icons/geo-yellow.svg";
import { DivIcon, Icon, LatLngTuple } from "leaflet";
import { StaticImageData } from "next/image";
import Link from "next/link";
import { ILocation } from "@/shared/types/report-type";
interface IData {
id: number;
location: ILocation[];
category: number;
}
interface IHomeMapProps {
data: IData[] | undefined;
}
const HomeMap: React.FC<IHomeMapProps> = ({
data,
}: IHomeMapProps) => {
const createCustomIcon = (icon: StaticImageData) => {
const customIcon = new Icon({
iconUrl: icon.src,
iconSize: [32, 32],
});
return customIcon;
};
const icons: Record<string, DivIcon> = {
1: createCustomIcon(geo_red_icon),
2: createCustomIcon(geo_pink_icon),
3: createCustomIcon(geo_purple_icon),
4: createCustomIcon(geo_orange_icon),
5: createCustomIcon(geo_green_icon),
6: createCustomIcon(geo_yellow_icon),
};
const position = [42.8746, 74.606];
return (
<MapContainer
center={position as LatLngTuple}
zoom={14}
scrollWheelZoom={false}
className="home-map"
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{data?.map((marker) => (
<Marker
key={marker.id}
icon={icons[marker.category]}
position={
[
+marker.location[0].latitude,
+marker.location[0].longitude,
] as LatLngTuple
}
>
<Popup>
<Link href={`/report/${marker.location[0].id}`}>
{marker.location[0].address}
</Link>
</Popup>
</Marker>
))}
</MapContainer>
);
};
export default HomeMap;

View File

@ -1,123 +0,0 @@
"use client";
import "./MapSection.scss";
import SearchForm from "@/features/SearchBar/SearchForm";
import { useEffect, useState } from "react";
import SectionHeader from "@/entities/SectionHeader/SectionHeader";
import {
ROAD_TYPES,
ROAD_TYPES_COLORS,
} from "@/shared/variables/road-types";
import HomeMap from "./HomeMap/HomeMap";
import { useRouter } from "next/navigation";
import { useMapStore } from "./mapSectionStore";
import Switch from "./Switch/Switch";
import { useShallow } from "zustand/react/shallow";
interface IMapSectionProps {
[key: string]: string;
}
const MapSection: React.FC<IMapSectionProps> = ({
categories = "1,2,3,4,5,6",
queryMap,
queryRating,
}: IMapSectionProps) => {
const [mapSearch, setMapSearch] = useState<string>(queryMap || "");
const data = useMapStore(useShallow((state) => state.data));
const getReports = useMapStore(
useShallow((state) => state.getReports)
);
const router = useRouter();
useEffect(() => {
getReports(categories);
}, [categories]);
const handleSubmit: React.FormEventHandler<
HTMLFormElement
> = async (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
setMapSearch(formData.get("map-search") as string);
router.push(
`/?тип-дороги=${categories}${
mapSearch ? `&поиск-на-карте=${mapSearch}` : ""
}${queryRating ? `&поиск-рейтинг=${queryRating}` : ""}`,
{
scroll: false,
}
);
};
const setSearchParams = (category: string) => {
const availableCategories = ["1", "2", "3", "4", "5", "6"];
if (!categories || !availableCategories.includes(category))
return categories;
if (categories?.includes(category)) {
const updatedCategories = categories
?.replace(category + ",", "")
.replace("," + category, "")
.replace(category, "");
return updatedCategories;
} else {
const newValue = category + ",";
const updatedCategories = newValue + categories;
return updatedCategories;
}
};
return (
<section className="map-section">
<SectionHeader
title="Карта дорог"
description="Будьте в курсе последних новостей о дорожном движении, строительствах и мероприятиях!"
/>
<SearchForm
onChange={(e) => setMapSearch(e.target.value)}
name="map-search"
value={mapSearch}
placeholder="Введите город, село или регион"
handleSubmit={handleSubmit}
/>
<div className="map-section__categories">
<ul className="map-section__categories_left">
{[1, 2, 3].map((sw) => (
<li key={sw}>
<Switch
href={`/?тип-дороги=${setSearchParams(
sw.toString()
)}`}
color={ROAD_TYPES_COLORS[sw]}
defaultState={categories.includes(sw.toString())}
/>
<label>{ROAD_TYPES[sw]}</label>
</li>
))}
</ul>
<ul className="map-section__categories_right">
{[4, 5, 6].map((sw) => (
<li key={sw}>
<Switch
href={`/?тип-дороги=${setSearchParams(
sw.toString()
)}`}
color={ROAD_TYPES_COLORS[sw]}
defaultState={categories.includes(sw.toString())}
/>
<label>{ROAD_TYPES[sw]}</label>
</li>
))}
</ul>
</div>
<HomeMap data={data?.results} />
</section>
);
};
export default MapSection;

View File

@ -1,41 +0,0 @@
import { baseAPI } from "@/shared/api/baseAPI";
import { IFetch } from "@/shared/types/fetch-type";
import { IList } from "@/shared/types/list-type";
import { IReport } from "@/shared/types/report-type";
import axios from "axios";
import { create } from "zustand";
interface IFetchReports extends IList {
results: IReport[];
}
interface IMapStore extends IFetch {
data: IFetchReports;
getReports: (categories: string) => Promise<void>;
}
export const useMapStore = create<IMapStore>((set) => ({
data: {
count: 0,
previous: null,
next: null,
results: [],
},
isLoading: false,
error: "",
getReports: async (categories: string = "1,2,3,4,5,6") => {
try {
set({ isLoading: true });
const response = await axios.get<IFetchReports>(
`${baseAPI}/report/?category=${categories}`
);
set({ data: response.data });
} catch (error: any) {
set({ error: error.message });
} finally {
set({ isLoading: false });
}
},
}));

View File

@ -1,22 +0,0 @@
@import "../../../shared/ui/variables.scss";
.nav-auth-profile-lg,
.nav-auth-signin-lg {
width: fit-content;
padding: 8px 16px;
background-color: $light-blue;
border-radius: 5px;
font-weight: 900;
color: white;
}
.nav-auth-profile-sm,
.nav-auth-signin-sm {
width: fit-content;
}
.nav-auth-profile-sm_active,
.nav-auth-signin-sm_active {
width: fit-content;
color: $light-blue;
}

View File

@ -1,47 +0,0 @@
import "./NavLanguage.scss";
import Image from "next/image";
import { useState } from "react";
import globus from "./icons/globus.svg";
import chevron from "./icons/chevron-down.svg";
import check from "./icons/check.svg";
const NavLanguage = () => {
const [language, setLanguage] = useState<string>("ru");
const [openMenu, setOpenMenu] = useState<boolean>(false);
const LANGUAGES = [
{ id: 1, language: "Русский", index: "ru" },
{ id: 2, language: "Кыргызча", index: "kg" },
{ id: 3, language: "English", index: "en" },
];
return (
<div className="nav-language">
<button
onClick={() => setOpenMenu((prev) => !prev)}
className="nav-language__btn"
>
<Image src={globus} alt="globus icon" />
<Image src={chevron} alt="chevron icon" />
</button>
{openMenu && (
<div className="nav-language__select">
{LANGUAGES.map((lang) => (
<button
className={`nav-language__option${
language === lang.index ? "_active" : ""
}`}
onClick={() => setLanguage(lang.index)}
key={lang.id}
>
{lang.language}
{lang.index === language ? (
<Image src={check} alt="check icon" />
) : null}
</button>
))}
</div>
)}
</div>
);
};
export default NavLanguage;

View File

@ -1,25 +0,0 @@
@import "@/shared/ui/variables.scss";
.nav-menu {
padding: 48px 30px 30px 30px;
width: 100%;
height: 100vh;
min-height: 500px;
position: fixed;
top: 72px;
left: 0;
display: flex;
flex-direction: column;
gap: 26px;
background-color: white;
a {
justify-content: flex-start;
font-size: 24px;
font-weight: 500;
}
&__link_active {
color: $light-blue;
}
}

View File

@ -1,29 +0,0 @@
import { LINKS } from "@/shared/variables/links";
import "./NavMenu.scss";
import Link from "next/link";
import { usePathname } from "next/navigation";
import NavAuth from "../NavAuth/NavAuth";
const NavMenu = () => {
const auth = false;
const pathname = usePathname();
return (
<nav className="nav-menu">
{LINKS.map((link) => (
<Link
className={`nav-menu__link${
pathname === link.pathname ? "_active" : ""
}`}
href={link.pathname}
key={link.id}
>
{link.pagename}
</Link>
))}
<NavAuth responsible />
</nav>
);
};
export default NavMenu;

View File

@ -1,133 +0,0 @@
@import "../../shared/ui/variables.scss";
.rating-section {
padding: 0 90px;
display: flex;
flex-direction: column;
align-items: center;
&__table {
width: 100%;
padding: 15px;
background-color: #fff;
border: 1px solid $gray-300;
border-radius: 5px;
overflow: hidden;
overflow-x: auto;
table {
width: 100%;
border-collapse: collapse;
border-radius: 6px;
thead {
tr {
border-bottom: 1px solid #ddd;
th {
padding: 15px 0;
text-align: left;
color: $gray-500;
font-size: 16px;
font-weight: 500;
gap: 4px;
}
#report-header-date,
#report-header-comment,
#report-header-like {
cursor: pointer;
div {
display: flex;
align-items: center;
gap: 4px;
}
}
}
}
tbody {
tr {
border-bottom: 1px solid #ddd;
td {
padding: 15px 0;
a {
justify-content: flex-start;
}
}
#report-date {
min-width: 150px;
color: $gray-500;
font-weight: 500;
font-size: 16px;
}
#report-location {
min-width: 200px;
overflow: hidden;
a {
text-decoration: underline;
white-space: preserve nowrap;
color: $light-blue;
font-size: 16px;
font-weight: 500;
}
}
#report-type {
min-width: 210px;
}
#report-description {
min-width: 378px;
p {
width: 318px;
color: #32303a;
font-size: 18px;
font-weight: 500;
line-height: 20px;
letter-spacing: 0.1px;
text-align: justify;
}
}
#report-comment,
#report-like {
div {
display: flex;
align-items: center;
gap: 7px;
}
}
}
tr:last-child {
border-bottom: none;
}
}
}
}
&-error {
td {
text-align: center;
font-size: 42px;
}
}
}
@media screen and (max-width: 1024px) {
.rating-section {
padding: 0 30px;
}
}
@media screen and (max-width: 550px) {
.rating-section {
padding: 0 16px;
}
}

View File

@ -1,175 +0,0 @@
"use client";
import "./RatingSection.scss";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import SectionHeader from "@/entities/SectionHeader/SectionHeader";
import SearchForm from "@/features/SearchBar/SearchForm";
import arrow_down_icon from "./icons/arrow-down.svg";
import arrow_up_icon from "./icons/arrow-up.svg";
import like_icon from "./icons/like.svg";
import message_icon from "./icons/message.svg";
import { useRatingStore } from "./ratingSectionStore";
import { useShallow } from "zustand/react/shallow";
import Link from "next/link";
import {
ROAD_TYPES,
ROAD_TYPES_COLORS,
} from "@/shared/variables/road-types";
import RoadType from "@/entities/RoadType/RoadType";
interface IRatingSectionProps {
[key: string]: string;
}
const RatingSection: React.FC<IRatingSectionProps> = ({
categories = "1,2,3,4,5,6",
queryMap,
queryRating,
}: IRatingSectionProps) => {
const [ratingSearch, setRatingSearch] = useState<string>(
queryRating || ""
);
const router = useRouter();
const reports = useRatingStore(useShallow((state) => state.data));
const getReports = useRatingStore(
useShallow((state) => state.getReports)
);
useEffect(() => {
getReports(ratingSearch);
}, []);
const handleSubmit: React.FormEventHandler<
HTMLFormElement
> = async (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
setRatingSearch(formData.get("rating-search") as string);
router.push(
`/?тип-дороги=${categories}${
queryMap ? `&поиск-на-карте=${queryMap}` : ""
}${ratingSearch ? `&поиск-рейтинг=${ratingSearch}` : ""}`,
{
scroll: false,
}
);
getReports(ratingSearch);
};
const sliceDate = (date: string) => {
return `${date.slice(8, 10)}.${date.slice(5, 7)}.${date.slice(
0,
4
)}`;
};
const sliceLocation = (location: string) => {
if (location.length > 15) return `${location.slice(0, 15)}...`;
return location;
};
const sliceDescription = (description: string) => {
if (description.length > 49) {
return `${description.slice(0, 50)}...`;
}
return description;
};
return (
<div className="rating-section">
<SectionHeader
title="Рейтинг"
description="Обсуждаем дороги: рейтинг, опыт, комфорт в пути!"
/>
<SearchForm
onChange={(e) => setRatingSearch(e.target.value)}
name="rating-search"
placeholder="Введите адрес"
value={ratingSearch}
handleSubmit={handleSubmit}
/>
<div className="rating-section__table">
<table>
<thead>
<tr>
<th id="report-header-date" tabIndex={0}>
<div>
Дата
<Image src={arrow_down_icon} alt="Arrow Down" />
<Image src={arrow_up_icon} alt="Arrow Up" />
</div>
</th>
<th>Адрес</th>
<th>Статус</th>
<th>Описание</th>
<th id="report-header-comment" tabIndex={0}>
<div>
Комментарии
<Image src={arrow_down_icon} alt="Arrow Down" />
<Image src={arrow_up_icon} alt="Arrow Up" />
</div>
</th>
<th id="report-header-like" tabIndex={0}>
<div>
Рейтинг
<Image src={arrow_down_icon} alt="Arrow Down" />
<Image src={arrow_up_icon} alt="Arrow Up" />
</div>
</th>
</tr>
</thead>
<tbody>
{reports.results.length !== 0 ? (
reports.results.map((report) => (
<tr key={report.id}>
<td id="report-date">
{sliceDate(report.created_at)}
</td>
<td id="report-location">
<Link href={`/report/${report.location[0].id}`}>
{sliceLocation(report.location[0].address)}
</Link>
</td>
<td id="report-type">
<RoadType
color={ROAD_TYPES_COLORS[report.category]}
>
{ROAD_TYPES[report.category]}
</RoadType>
</td>
<td id="report-description">
<p>{sliceDescription(report.description)}</p>
</td>
<td id="report-comment">
<div>
<Image src={message_icon} alt="Message Icon" />
{report.count_reviews}
</div>
</td>
<td id="report-like">
<div>
<Image src={like_icon} alt="Like Icon" />
{report.total_likes}
</div>
</td>
</tr>
))
) : (
<tr className="rating-section-error">
<td colSpan={7}>Упс, адрес не найден</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
};
export default RatingSection;

View File

@ -1,14 +0,0 @@
<svg width="16.000000" height="16.000000" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip183_53161">
<rect id="arrow-down" width="16.000000" height="16.000000" transform="translate(16.000000 16.000000) rotate(180.000000)" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="arrow-down" width="16.000000" height="16.000000" transform="translate(16.000000 16.000000) rotate(180.000000)" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip183_53161)">
<path id="Icon" d="M8 12.667L8 3.33398M3.33301 8L8 3.33398L12.6665 8" stroke="#667085" stroke-opacity="1.000000" stroke-width="1.333333" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 783 B

View File

@ -1,14 +0,0 @@
<svg width="16.000000" height="16.000000" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip161_49481">
<rect id="arrow-down" width="16.000000" height="16.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="arrow-down" width="16.000000" height="16.000000" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip161_49481)">
<path id="Icon" d="M8 3.33301L8 12.666M12.667 8L8 12.666L3.3335 8" stroke="#667085" stroke-opacity="1.000000" stroke-width="1.333333" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 656 B

View File

@ -1,33 +0,0 @@
import { ROAD_TYPES_STATS } from "@/shared/variables/road-types";
import "./StatisticsSection.scss";
import { statiscsSectionStore } from "./statisticsSection.store";
const StatisticsSection = async () => {
// const { getStatsCount, getErrorMessage } = statiscsSectionStore();
// const statistics = await getStatsCount();
// const error = getErrorMessage();
const mockArray = [
{ category: 1, count: 132 },
{ category: 2, count: 12 },
{ category: 3, count: 1432 },
{ category: 4, count: 18 },
{ category: 5, count: 95 },
{ category: 6, count: 54 },
];
return (
<section className="statistics-section">
<ul className="statistics-section__list">
{mockArray.map((cat) => (
<li>
<p>{ROAD_TYPES_STATS[cat.category]}</p>
<span>{cat.count}</span>
</li>
))}
</ul>
</section>
);
};
export default StatisticsSection;

View File

@ -1,35 +0,0 @@
import { baseAPI } from "@/shared/api/baseAPI";
import axios, { AxiosError } from "axios";
interface IStatsCount {
category: number;
count: number;
}
export const statiscsSectionStore = () => {
let errorMessage = "";
async function getStatsCount() {
try {
const response = await axios.get<IStatsCount[]>(
`${baseAPI}/report/category_count/`
);
return response.data;
} catch (error: unknown) {
if (error instanceof AxiosError) {
errorMessage = error.message;
} else {
errorMessage = "An error ocured";
}
}
}
function getErrorMessage() {
return errorMessage;
}
return {
getStatsCount,
getErrorMessage,
};
};

View File

@ -0,0 +1,9 @@
// .app {
// padding-top: 78px;
// }
// @media screen and (max-width: 768px) {
// .app {
// padding-top: 72px;
// }
// }

View File

@ -0,0 +1,11 @@
"use client";
import { SessionProvider } from "next-auth/react";
export const Providers = ({
children,
}: {
children: React.ReactNode;
}) => {
return <SessionProvider>{children}</SessionProvider>;
};

View File

@ -0,0 +1,102 @@
.about-us {
display: flex;
flex-direction: column;
h2 {
margin-bottom: 40px;
width: fit-content;
}
&__image {
align-self: center;
margin-bottom: 65px;
border-radius: 12px;
max-width: 1072px;
width: 100%;
height: 598px;
object-fit: cover;
}
h3 {
margin-bottom: 10px;
font-size: 24px;
font-weight: 500;
line-height: 29px;
color: rgb(62, 50, 50);
}
&__descriptions {
display: flex;
flex-direction: column;
gap: 20px;
p {
font-size: 20px;
font-weight: 400;
line-height: 34px;
color: rgb(62, 50, 50);
}
ul {
display: flex;
flex-direction: column;
gap: 20px;
li {
font-size: 20px;
font-weight: 400;
line-height: 34px;
color: rgb(62, 50, 50);
}
}
}
&__author {
margin-top: 30px;
}
}
@media screen and (max-width: 768px) {
.about-us {
h2 {
margin-bottom: 30px;
}
img {
margin-bottom: 30px;
height: 392px;
}
h3 {
font-size: 20px;
line-height: 24px;
}
&__descriptions {
gap: 16px;
p {
font-size: 18px;
line-height: 34px;
}
}
}
}
@media screen and (max-width: 550px) {
.about-us {
h2 {
margin-bottom: 20px;
}
img {
height: 231px;
}
&__descriptions {
p {
font-size: 16px;
line-height: 140%;
}
}
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 332 KiB

View File

@ -0,0 +1,71 @@
import Typography from "@/shared/ui/components/Typography/Typography";
import "./AboutUs.scss";
import { Metadata } from "next";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import { apiInstance } from "@/shared/config/apiConfig";
import { IMetatag } from "@/shared/types/metatag-type";
import { useTranslations } from "next-intl";
export async function generateMetadata(): Promise<Metadata> {
const data = await apiInstance
.get<IMetatag[]>("/metatags/")
.then((res) => res.data)
.catch((e) => console.log(e));
if (!data)
return {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
openGraph: {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
},
};
const metadata = data.filter((tag) => tag.page === "about-us")[0];
if (!metadata) {
return {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
openGraph: {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
},
};
}
return {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
keywords: metadata.keywords.split(","),
openGraph: {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
type: "website",
},
};
}
const AboutUs = () => {
const t = useTranslations("about_us");
return (
<div className="about-us page-padding">
<BreadCrumbs homeRequired />
<Typography element="h2">{t("name")}</Typography>
<div className="about-us__descriptions">
<h3>
{t("description1")}
</h3>
<h3>{t("description2")}
{t("description3")}</h3>
<h3>{t("description4")}</h3>
</div>
</div>
);
};
export default AboutUs;

View File

@ -0,0 +1,20 @@
.create-report {
display: flex;
flex-direction: column;
gap: 40px;
h2 {
text-align: start;
}
}
@media screen and (max-width: 768px) {
.create-report {
gap: 30px;
}
}
@media screen and (max-width: 550px) {
.create-report {
gap: 20px;
}
}

View File

@ -0,0 +1,30 @@
import Typography from "@/shared/ui/components/Typography/Typography";
import "./CreateReport.scss";
import dynamic from "next/dynamic";
import { Metadata } from "next";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
export const metadata: Metadata = {
title: "KG ROAD | Написать обращение",
description: "Написать обращение KG ROAD",
};
const DynamicForm = dynamic(
() => import("@/widgets/forms/ReportForm/ReportForm"),
{
ssr: false,
}
);
const CreateReport = () => {
return (
<div className="create-report page-padding">
<BreadCrumbs homeRequired />
<Typography element="h2">Написать обращение</Typography>
<DynamicForm />
</div>
);
};
export default CreateReport;

View File

@ -0,0 +1,30 @@
import "../globals.scss";
import "./App.scss";
import "@/shared/fonts/fonts.scss";
import Navbar from "@/widgets/Navbar/Navbar";
import Footer from "@/widgets/Footer/Footer";
import { NextIntlClientProvider, useMessages } from "next-intl";
import { Providers } from "./Providers";
export default function LocaleLayout({
children,
params,
}: Readonly<{
children: React.ReactNode;
params: { locale: string };
}>) {
const messages = useMessages();
return (
<html lang={params.locale}>
<body>
<NextIntlClientProvider locale={params.locale} messages={messages}>
<Providers>
<Navbar />
<div className="app">{children}</div>
<Footer />
</Providers>
</NextIntlClientProvider>
</body>
</html>
);
}

View File

@ -0,0 +1,25 @@
.news {
display: flex;
flex-direction: column;
h2 {
width: fit-content;
margin-bottom: 40px;
}
}
@media screen and (max-width: 768px) {
.news {
h2 {
margin-bottom: 30px;
}
}
}
@media screen and (max-width: 550px) {
.news {
h2 {
margin-bottom: 20px;
}
}
}

View File

@ -0,0 +1,104 @@
.news-details {
display: flex;
flex-direction: column;
gap: 40px;
&__img {
align-self: center;
#news-img {
margin-bottom: 30px;
width: 100%;
max-width: 1072px;
height: 598px;
border-radius: 12px;
object-fit: cover;
}
}
&__date-and-reviews {
display: flex;
align-items: center;
gap: 80px;
}
&__date,
&__reviews {
display: flex;
align-items: center;
gap: 6px;
p {
font-size: 15px;
font-weight: 500;
line-height: 20px;
color: rgba(62, 50, 50, 0.75);
}
}
&__text {
width: 100%;
max-width: 1072px;
align-self: center;
display: flex;
flex-direction: column;
gap: 10px;
color: rgb(62, 50, 50);
h3 {
font-size: 24px;
font-weight: 500;
line-height: 29px;
}
p {
font-size: 20px;
font-weight: 400;
line-height: 34px;
}
}
}
@media screen and (max-width: 768px) {
.news-details {
gap: 30px;
&__img {
#news-img {
margin-bottom: 25px;
height: 392px;
}
}
&__text {
h3 {
font-size: 20px;
line-height: 24px;
}
p {
font-size: 18px;
line-height: 34px;
}
}
}
}
@media screen and (max-width: 550px) {
.news-details {
gap: 20px;
&__img {
#news-img {
margin-bottom: 20px;
height: 231px;
}
}
&__text {
p {
font-size: 16px;
line-height: 140%;
}
}
}
}

View File

@ -0,0 +1,9 @@
"use client";
import NotFound from "@/widgets/NotFound/NotFound";
const error = () => {
return <NotFound />;
};
export default error;

View File

@ -0,0 +1,17 @@
<svg width="16.000000" height="16.000000" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip2340_50999">
<rect id="calendar" width="16.000000" height="16.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="calendar" width="16.000000" height="16.000000" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip2340_50999)">
<path id="Vector" d="M12.666 2.66699C13.4023 2.66699 14 3.26367 14 4L14 13.334C14 14.0703 13.4023 14.667 12.666 14.667L3.33398 14.667C2.59766 14.667 2 14.0703 2 13.334L2 4C2 3.26367 2.59766 2.66699 3.33398 2.66699L12.666 2.66699Z" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round"/>
<path id="Vector" d="M10.666 1.33301L10.666 4" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round" stroke-linecap="round"/>
<path id="Vector" d="M5.33398 1.33301L5.33398 4" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round" stroke-linecap="round"/>
<path id="Vector" d="M2 6.66699L14 6.66699" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,14 @@
<svg width="16.000000" height="16.000000" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip2340_51004">
<rect id="message-circle" width="16.000000" height="16.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="message-circle" width="16.000000" height="16.000000" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip2340_51004)">
<path id="Vector" d="M13.4004 10.2002C12.9297 11.1416 12.207 11.9326 11.3125 12.4863C10.416 13.04 9.38477 13.333 8.33398 13.333C7.45312 13.3359 6.58594 13.1299 5.80078 12.7334L2 14L3.26758 10.2002C2.86914 9.41504 2.66406 8.54688 2.66602 7.66699C2.66797 6.61426 2.96094 5.58301 3.51367 4.68848C4.06641 3.79395 4.85938 3.07031 5.80078 2.59961C6.58594 2.20312 7.45312 1.99805 8.33398 2L8.66602 2C10.0566 2.07715 11.3691 2.66309 12.3535 3.64746C13.3359 4.63086 13.9238 5.94336 14 7.33301L14 7.66699C14.002 8.54688 13.7969 9.41504 13.4004 10.2002Z" stroke="#6E6565" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,109 @@
import Typography from "@/shared/ui/components/Typography/Typography";
import "./NewsDetails.scss";
import { apiInstance } from "@/shared/config/apiConfig";
import { INews } from "@/shared/types/news-type";
import Image from "next/image";
import message from "./icons/message.svg";
import calendar from "./icons/calendar.svg";
import ReviewSection from "@/widgets/ReviewSection/ReviewSection";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import { Metadata } from "next";
import parse from "html-react-parser";
export async function generateMetadata({
params,
}: {
params: { id: string };
}): Promise<Metadata> {
const response = await apiInstance.get<INews>(`/news/${params.id}/`);
return {
title: response.data.title,
description: response.data.description,
openGraph: {
images: [response.data.image],
type: "article",
publishedTime: response.data.created_at,
},
};
}
function convertLinksToHtml(text: string) {
const urlPattern =
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|$!:,.;]*[-A-Z0-9+&@#\/%=~_|$])[\s.,;]?/gi;
return text.replace(urlPattern, (url) => {
const href = url.trim();
return `<a href="${href}" target="_blank" rel="noopener noreferrer">${href}</a>`;
});
}
const formatDescription = (text: string) => {
return text.replace(/(\r\n|\n|\r)/g, "<br />");
};
const NewsDetails = async ({
params,
}: {
params: { id: string; новость: string };
}) => {
const getNewsById = async () => {
const response = await apiInstance.get<INews>(`/news/${params.id}/`);
return response.data;
};
const data = await getNewsById();
const months: Record<string, string> = {
"01": "Январь",
"02": "Февраль",
"03": "Март",
"04": "Апрель",
"05": "Май",
"06": "Июнь",
"07": "Июль",
"08": "Август",
"09": "Сентябрь",
"10": "Октябрь",
"11": "Ноябрь",
"12": "Декабрь",
};
const htmlContentWithLinks = convertLinksToHtml(data.description);
const formattedHtmlContent = formatDescription(htmlContentWithLinks);
return (
<div className="news-details page-padding">
<BreadCrumbs />
<Typography element="h2">{data.title}</Typography>
<div className="news-details__img">
<img id="news-img" src={data.image} alt="News Image" />
<div className="news-details__date-and-reviews">
<div className="news-details__date">
<Image src={calendar} alt="Calendar Icon" />
<p>
{months[data.created_at.slice(5, 7)]}{" "}
{data.created_at.slice(8, 10) === "0"
? data.created_at.slice(9, 10)
: data.created_at.slice(8, 10)}
, {data.created_at.slice(0, 4)}
</p>
</div>
<div className="news-details__reviews">
<Image src={message} alt="Message Icon" />
<p>Комментарии: {data.count_reviews}</p>
</div>
</div>
</div>
<div className="news-details__text">
<h3>{data.title}</h3>
<div className="news-details__description">
{parse(formattedHtmlContent)}
</div>
</div>
<ReviewSection endpoint="news" id={data.id} />
</div>
);
};
export default NewsDetails;

View File

@ -0,0 +1,59 @@
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import "./News.scss";
import Typography from "@/shared/ui/components/Typography/Typography";
import NewsList from "@/widgets/NewsList/NewsList";
import { Metadata } from "next";
import { apiInstance } from "@/shared/config/apiConfig";
import { IMetatag } from "@/shared/types/metatag-type";
export async function generateMetadata(): Promise<Metadata> {
const data = await apiInstance
.get<IMetatag[]>("/metatags/")
.then((res) => res.data)
.catch((e) => console.log(e));
if (!data)
return {
title: "KG ROAD | Новости",
description: "Страница новостей KG ROAD",
};
const metadata = data.filter((tag) => tag.page === "news")[0];
if (!metadata) {
return {
title: "KG ROAD | Новости",
description: "Страница новостей KG ROAD",
};
}
return {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
keywords: metadata.keywords.split(","),
openGraph: {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
type: "website",
},
};
}
const News = ({
searchParams,
}: {
searchParams: {
["страница-новостей"]: string;
};
}) => {
return (
<div className="news page-padding">
<BreadCrumbs homeRequired />
<Typography element="h2">Новости</Typography>
<NewsList searchParams={searchParams} />
</div>
);
};
export default News;

View File

@ -0,0 +1,9 @@
"use client";
import NotFound from "@/widgets/NotFound/NotFound";
import "../globals.scss";
import "@/shared/fonts/fonts.scss";
export default function NotFoundPage() {
return <NotFound />;
}

78
src/app/[locale]/page.tsx Normal file
View File

@ -0,0 +1,78 @@
import Header from "@/widgets/home/Header/Header";
import StatisticsSection from "@/widgets/home/StatisticsSection/StatisticsSection";
import RatingSection from "@/widgets/home/RatingSection/RatingSection";
import NewsSection from "@/widgets/home/NewsSection/NewsSection";
import MapSection from "@/widgets/home/MapSection/MapSection";
import { Metadata } from "next";
import { apiInstance } from "@/shared/config/apiConfig";
import { IMetatag } from "@/shared/types/metatag-type";
export async function generateMetadata(): Promise<Metadata> {
const data = await apiInstance
.get<IMetatag[]>("/metatags/")
.then((res) => res.data)
.catch((e) => console.log(e));
if (!data)
return {
title: "KG ROAD | Главная",
description:
"Главная страница KG ROAD | Сделаем дороги безопасными!",
openGraph: {
title: "KG ROAD | Главная",
description:
"Главная страница KG ROAD | Сделаем дороги безопасными!",
type: "website",
},
};
const metadata = data.filter((tag) => tag.page === "home")[0];
if (!metadata) {
return {
title: "KG ROAD | Главная",
description:
"Главная страница KG ROAD | Сделаем дороги безопасными!",
openGraph: {
title: "KG ROAD | Главная",
description:
"Главная страница KG ROAD | Сделаем дороги безопасными!",
type: "website",
},
};
}
return {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
keywords: metadata.keywords.split(","),
openGraph: {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
type: "website",
},
};
}
const Home = async ({
searchParams,
}: {
searchParams: {
["тип-дороги"]: string;
["поиск-на-карте"]: string;
["поиск-рейтинг"]: string;
["страница-рейтинга"]: string;
};
}) => {
return (
<div className="home">
<Header />
<StatisticsSection />
<MapSection searchParams={searchParams} />
<RatingSection searchParams={searchParams} />
<NewsSection />
</div>
);
};
export default Home;

View File

@ -0,0 +1,102 @@
.privacy {
display: flex;
flex-direction: column;
h2 {
margin-bottom: 40px;
width: fit-content;
}
&__image {
align-self: center;
margin-bottom: 65px;
border-radius: 12px;
max-width: 1072px;
width: 100%;
height: 598px;
object-fit: cover;
}
h3 {
margin-bottom: 10px;
font-size: 24px;
font-weight: 500;
line-height: 29px;
color: rgb(62, 50, 50);
}
&__descriptions {
display: flex;
flex-direction: column;
gap: 20px;
p {
font-size: 20px;
font-weight: 400;
line-height: 34px;
color: rgb(62, 50, 50);
}
ul {
display: flex;
flex-direction: column;
gap: 20px;
li {
font-size: 20px;
font-weight: 400;
line-height: 34px;
color: rgb(62, 50, 50);
}
}
}
&__author {
margin-top: 30px;
}
}
@media screen and (max-width: 768px) {
.privacy {
h2 {
margin-bottom: 30px;
}
img {
margin-bottom: 30px;
height: 392px;
}
h3 {
font-size: 20px;
line-height: 24px;
}
&__descriptions {
gap: 16px;
p {
font-size: 18px;
line-height: 34px;
}
}
}
}
@media screen and (max-width: 550px) {
.privacy {
h2 {
margin-bottom: 20px;
}
img {
height: 231px;
}
&__descriptions {
p {
font-size: 16px;
line-height: 140%;
}
}
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 332 KiB

View File

@ -0,0 +1,99 @@
import Typography from "@/shared/ui/components/Typography/Typography";
import "./Privacy.scss";
import Image from "next/image";
import header from "./assets/header.svg";
import { Metadata } from "next";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import { apiInstance } from "@/shared/config/apiConfig";
import { IMetatag } from "@/shared/types/metatag-type";
import { useTranslations } from "next-intl";
export async function generateMetadata(): Promise<Metadata> {
const data = await apiInstance
.get<IMetatag[]>("/metatags/")
.then((res) => res.data)
.catch((e) => console.log(e));
if (!data)
return {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
openGraph: {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
},
};
const metadata = data.filter((tag) => tag.page === "privacy")[0];
if (!metadata) {
return {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
openGraph: {
title: "KG ROAD | О нас",
description:
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
},
};
}
return {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
keywords: metadata.keywords.split(","),
openGraph: {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
type: "website",
},
};
}
const Privacy = () => {
const tPrivacyPolicy = useTranslations("PrivacyPolicy");
return (
<div className="privacy page-padding">
<BreadCrumbs homeRequired />
<Typography element="h2">{tPrivacyPolicy("titel0")}</Typography>
<div className="privacy__descriptions">
<ol>
<h3><span>{tPrivacyPolicy("title1")}</span></h3>
</ol>
<p><span>
{tPrivacyPolicy("p1")}
<a href='https://askjournal.kg/'>https://askjournal.kg/</a>
{tPrivacyPolicy("p1o")}
</span></p>
<h3><span>{tPrivacyPolicy("title2")}</span></h3>
<p><span>{tPrivacyPolicy("p2")}</span></p>
<h3><span>{tPrivacyPolicy("title3")}</span></h3>
<p><span>{tPrivacyPolicy("p3")}</span></p>
<p><span>{tPrivacyPolicy("p4")}</span></p>
<p><span>{tPrivacyPolicy("p5")}</span></p>
<p><span>{tPrivacyPolicy("p6")}</span></p>
<p><span>{tPrivacyPolicy("p7")}</span></p>
<h3><span>{tPrivacyPolicy("title4")}</span></h3>
<p><span>{tPrivacyPolicy("p8")}</span></p>
<h3><span>{tPrivacyPolicy("title5")}</span></h3>
<p><span>{tPrivacyPolicy("p9")}</span></p>
<p><span>{tPrivacyPolicy("p10")}</span></p>
<p><span>{tPrivacyPolicy("p11")}</span></p>
<h3><span>{tPrivacyPolicy("title6")}</span></h3>
<p><span>{tPrivacyPolicy("p12")}</span></p>
<p><span>{tPrivacyPolicy("p13")}</span></p>
<p><span>{tPrivacyPolicy("p14")}</span></p>
<p><span>{tPrivacyPolicy("p15")}</span></p>
<p><span>{tPrivacyPolicy("p16")}</span></p>
<h3><span>{tPrivacyPolicy("title7")}</span></h3>
<p><span>{tPrivacyPolicy("p17")}</span></p>
<p><span>{tPrivacyPolicy("p18")}</span></p>
<p><span>{tPrivacyPolicy("p19")}</span></p>
</div>
</div>
);
};
export default Privacy;

View File

@ -0,0 +1,31 @@
"use client";
import { apiInstance } from "@/shared/config/apiConfig";
import { AxiosError } from "axios";
import { signOut, useSession } from "next-auth/react";
import { useEffect } from "react";
const AuthGuard = ({ children }: { children: React.ReactNode }) => {
const session = useSession();
const verifyToken = async () => {
try {
const data = {
token: session.data?.access_token,
};
await apiInstance.post("/token/verify/", data);
} catch (error: unknown) {
signOut({
callbackUrl: "/",
});
}
};
useEffect(() => {
if (session.status === "loading") return;
verifyToken();
}, [session.status]);
return <>{children}</>;
};
export default AuthGuard;

View File

@ -0,0 +1,8 @@
.profile {
display: flex;
flex-direction: column;
h2 {
margin-bottom: 50px;
}
}

View File

@ -0,0 +1,55 @@
import Typography from "@/shared/ui/components/Typography/Typography";
import "./Profile.scss";
import ProfileNav from "@/widgets/ProfileNav/ProfileNav";
import AuthGuard from "./AuthGuard";
import { AxiosError } from "axios";
import { apiInstance } from "@/shared/config/apiConfig";
import { getServerSession } from "next-auth";
import { authConfig } from "@/shared/config/authConfig";
import { Metadata } from "next";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
export const metadata: Metadata = {
title: "KG ROAD | Профиль",
description: "Страница профиля KG ROAD",
};
const Profile = async ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
const session = await getServerSession(authConfig);
const getProfile = async () => {
const Authorization = `Bearer ${session?.access_token}`;
const config = {
headers: {
Authorization,
},
};
try {
const response = await apiInstance.get<{
report_count: number;
}>("/users/profile/", config);
return response.data;
} catch (error: unknown) {
if (error instanceof AxiosError) console.log(error.message);
}
};
const data = await getProfile();
return (
<div className="profile page-padding">
<BreadCrumbs />
<Typography element="h2">Личный кабинет</Typography>
<ProfileNav report_count={data?.report_count as number} />
<AuthGuard>{children}</AuthGuard>
</div>
);
};
export default Profile;

View File

@ -0,0 +1,16 @@
import ProfileTable from "@/widgets/tables/ProfileTable/ProfileTable";
import React from "react";
const MyReports = async ({
searchParams,
}: {
searchParams: { ["страница-обращений"]: string };
}) => {
return (
<div>
<ProfileTable searchParams={searchParams} />
</div>
);
};
export default MyReports;

View File

@ -0,0 +1,13 @@
"use client";
import { useRouter } from "@/shared/config/navigation";
import { useEffect } from "react";
const Profile = () => {
const router = useRouter();
useEffect(() => {
router.push("/profile/personal");
}, []);
return <></>;
};
export default Profile;

View File

@ -0,0 +1,48 @@
"use server";
import ProfileAvatar from "@/features/ProfileAvatar/ProfileAvatar";
import { apiInstance } from "@/shared/config/apiConfig";
import { authConfig } from "@/shared/config/authConfig";
import { IProfile } from "@/shared/types/profile-type";
import ProfileForm from "@/widgets/forms/ProfileForm/ProfileForm";
import { AxiosError } from "axios";
import { getServerSession } from "next-auth";
import React from "react";
const Personal = async () => {
const session = await getServerSession(authConfig);
const getProfile = async () => {
const Authorization = `Bearer ${session?.access_token}`;
const config = {
headers: {
Authorization,
},
};
try {
const response = await apiInstance.get<IProfile>(
"/users/profile/",
config
);
return response.data;
} catch (error: unknown) {
if (error instanceof AxiosError) console.log(error.message);
}
};
const data = await getProfile();
return (
<div className="personal">
<ProfileAvatar img={data?.image as string} />
<ProfileForm
id={data?.id as number}
first_name={data?.first_name as string}
last_name={data?.last_name as string}
email={data?.email as string}
/>
</div>
);
};
export default Personal;

View File

@ -0,0 +1,37 @@
.report-details {
display: flex;
flex-direction: column;
gap: 75px;
&__container {
display: grid;
grid-template-columns: 1.05fr 1fr;
gap: 76px;
}
&__map {
grid-row: 2 / 3;
grid-column: 1 / 3;
}
}
@media screen and (max-width: 1024px) {
.report-details {
&__container {
grid-template-columns: 1fr;
gap: 45px;
}
&__map {
grid-row: 2 / 3;
grid-column: 1 / 2;
}
}
}
@media screen and (max-width: 550px) {
.report-details {
&__container {
gap: 40px;
}
}
}

View File

@ -0,0 +1,9 @@
"use client";
import NotFound from "@/widgets/NotFound/NotFound";
const error = () => {
return <NotFound />;
};
export default error;

View File

@ -0,0 +1,8 @@
<svg width="38.337891" height="38.336914" viewBox="0 0 38.3379 38.3369" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs/>
<path id="Icon" d="M15.7637 0.834961L22.5762 0.834961C24.8477 0.834961 26.6543 0.834961 28.1113 0.954102C29.6035 1.07617 30.877 1.33105 32.043 1.9248C33.9238 2.88379 35.4531 4.41309 36.4121 6.29492C37.0059 7.46094 37.2617 8.7334 37.3828 10.2256C37.502 11.6836 37.502 13.4902 37.502 15.7617L37.502 22.5752C37.502 24.8467 37.502 26.6533 37.3828 28.1104C37.2617 29.6035 37.0059 30.876 36.4121 32.042C35.4531 33.9229 33.9238 35.4531 32.043 36.4121C30.877 37.0059 29.6035 37.2607 28.1113 37.3828C26.6543 37.502 24.8477 37.502 22.5762 37.502L15.7617 37.502C13.4902 37.502 11.6836 37.502 10.2266 37.3828C8.73438 37.2607 7.46094 37.0059 6.29688 36.4121C4.41406 35.4531 2.88477 33.9229 1.92578 32.042C1.33203 30.876 1.07617 29.6035 0.955078 28.1104C0.835938 26.6533 0.835938 24.8467 0.835938 22.5742L0.835938 15.7617C0.835938 13.4902 0.835938 11.6836 0.955078 10.2256C1.07617 8.7334 1.33203 7.46094 1.92578 6.29492C2.88477 4.41309 4.41406 2.88379 6.29688 1.9248C7.46094 1.33105 8.73438 1.07617 10.2266 0.954102C11.6836 0.834961 13.4922 0.834961 15.7637 0.834961ZM10.498 4.27637C9.21094 4.38184 8.42578 4.58008 7.80859 4.89453C6.55469 5.53418 5.53516 6.55371 4.89648 7.80859C4.58203 8.42578 4.38281 9.21094 4.27734 10.4971C4.16992 11.8037 4.16992 13.4736 4.16992 15.835L4.16992 17.502L6.8125 14.8584C8.11328 13.5566 10.2246 13.5566 11.5254 14.8584L21.9141 25.2461C22.2383 25.5713 22.7656 25.5713 23.0918 25.2461L26.8125 21.5254C28.1133 20.2236 30.2246 20.2236 31.5254 21.5254L34.168 24.166C34.1699 23.6523 34.1699 23.0986 34.1699 22.502L34.1699 15.835C34.1699 13.4736 34.168 11.8037 34.0605 10.4971C33.957 9.21094 33.7578 8.42578 33.4434 7.80859C32.8027 6.55371 31.7832 5.53418 30.5293 4.89453C29.9121 4.58008 29.127 4.38184 27.8398 4.27637C26.5352 4.16992 24.8633 4.16797 22.502 4.16797L15.8359 4.16797C13.4746 4.16797 11.8047 4.16992 10.498 4.27637ZM23.3359 7.50195C21.0352 7.50195 19.1699 9.36719 19.1699 11.668C19.1699 13.9697 21.0352 15.835 23.3359 15.835C25.6367 15.835 27.502 13.9697 27.502 11.668C27.502 9.36719 25.6367 7.50195 23.3359 7.50195Z" fill="#7C8B9D" fill-opacity="1.000000" fill-rule="evenodd"/>
<path id="Icon" d="M23.3359 7.50195C25.6367 7.50195 27.502 9.36719 27.502 11.668C27.502 13.9697 25.6367 15.835 23.3359 15.835C21.0352 15.835 19.1699 13.9697 19.1699 11.668C19.1699 9.36719 21.0352 7.50195 23.3359 7.50195ZM10.2266 0.954102C8.73438 1.07617 7.46094 1.33105 6.29688 1.9248C4.41406 2.88379 2.88477 4.41309 1.92578 6.29492C1.33203 7.46094 1.07617 8.7334 0.955078 10.2256C0.835938 11.6836 0.835938 13.4902 0.835938 15.7617L0.835938 22.5742C0.835938 24.8467 0.835938 26.6533 0.955078 28.1104C1.07617 29.6035 1.33203 30.876 1.92578 32.042C2.88477 33.9229 4.41406 35.4531 6.29688 36.4121C7.46094 37.0059 8.73438 37.2607 10.2266 37.3828C11.6836 37.502 13.4902 37.502 15.7637 37.502L22.5762 37.502C24.8477 37.502 26.6543 37.502 28.1113 37.3828C29.6035 37.2607 30.877 37.0059 32.043 36.4121C33.9238 35.4531 35.4531 33.9229 36.4121 32.042C37.0059 30.876 37.2617 29.6035 37.3828 28.1104C37.502 26.6533 37.502 24.8467 37.502 22.5752L37.502 15.7617C37.502 13.4902 37.502 11.6836 37.3828 10.2256C37.2617 8.7334 37.0059 7.46094 36.4121 6.29492C35.4531 4.41309 33.9238 2.88379 32.043 1.9248C30.877 1.33105 29.6035 1.07617 28.1113 0.954102C26.6543 0.834961 24.8477 0.834961 22.5762 0.834961L15.7637 0.834961C13.4922 0.834961 11.6836 0.834961 10.2266 0.954102ZM10.498 4.27637C11.8047 4.16992 13.4746 4.16797 15.8359 4.16797L22.502 4.16797C24.8633 4.16797 26.5352 4.16992 27.8398 4.27637C29.127 4.38184 29.9121 4.58008 30.5293 4.89453C31.7832 5.53418 32.8027 6.55371 33.4434 7.80859C33.7578 8.42578 33.957 9.21094 34.0605 10.4971C34.168 11.8037 34.1699 13.4736 34.1699 15.835L34.1699 22.502C34.1699 23.0986 34.1699 23.6523 34.168 24.166L31.5254 21.5254C30.2246 20.2236 28.1133 20.2236 26.8125 21.5254L23.0918 25.2461C22.7656 25.5713 22.2383 25.5713 21.9141 25.2461L11.5254 14.8584C10.2246 13.5566 8.11328 13.5566 6.8125 14.8584L4.16992 17.502L4.16992 15.835C4.16992 13.4736 4.16992 11.8037 4.27734 10.4971C4.38281 9.21094 4.58203 8.42578 4.89648 7.80859C5.53516 6.55371 6.55469 5.53418 7.80859 4.89453C8.42578 4.58008 9.21094 4.38184 10.498 4.27637Z" stroke="#7C8B9D" stroke-opacity="1.000000" stroke-width="1.670000"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,85 @@
import "./ReportDetails.scss";
import { IReport } from "@/shared/types/report-type";
import ReviewSection from "@/widgets/ReviewSection/ReviewSection";
import { Metadata } from "next";
import ReportInformation from "@/widgets/report-details/ReportInformation/ReportInformation";
import ReportImages from "@/widgets/report-details/ReportImages/ReportImages";
import dynamic from "next/dynamic";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import { apiInstance } from "@/shared/config/apiConfig";
const DynamicMap = dynamic(
() => import("@/widgets/report-details/ReportMap/ReportMap"),
{
ssr: false,
}
);
export async function generateMetadata({
params,
}: {
params: { id: string };
}): Promise<Metadata> {
const response = await apiInstance.get<IReport>(
`/report/${params.id}/`
);
return {
title: `KG ROAD | ${response.data.description}`,
description: `${response.data.location[0].address}, ${response.data.description}`,
openGraph: {
title: `KG ROAD | ${response.data.description}`,
description: `${response.data.location[0].address}, ${response.data.description}`,
images: [response.data.image[0].image],
type: "article",
},
};
}
const ReportDetails = async ({
params,
}: {
params: { id: string };
}) => {
const getReportDetails = async () => {
try {
const res = await fetch(
`${process.env.NEXT_PUBLIC_BASE_API}/report/${params.id}/`,
{ cache: "no-store" }
);
return res.json();
} catch (error) {
console.log(error);
}
};
const report: IReport = await getReportDetails();
return (
<div className="report-details page-padding">
<BreadCrumbs homeRequired />
<div className="report-details__container">
<ReportInformation
id={report.id}
location={report.location[0]}
date={report.created_at}
description={report.description}
category={report.category}
total_likes={report.total_likes}
author={report.author}
/>
<div className="report-details__map">
<DynamicMap
location={report.location}
category={report.category}
/>
</div>
<ReportImages images={report.image} />
</div>
<ReviewSection endpoint="report" id={+params.id} />
</div>
);
};
export default ReportDetails;

View File

@ -0,0 +1,16 @@
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import "@/shared/ui/auth-classes.scss";
import ForgotPasswordForm from "@/widgets/forms/ForgotPasswordForm/ForgotPasswordForm";
const ForgotPassword = () => {
return (
<div className="page-padding">
<BreadCrumbs />
<div className="auth-page">
<ForgotPasswordForm />
</div>
</div>
);
};
export default ForgotPassword;

View File

@ -0,0 +1,14 @@
<svg width="24.000000" height="24.000000" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip51_17162">
<rect id="log-in-04" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="log-in-04" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip51_17162)">
<path id="Icon" d="M12 8L16 12L12 16M3 12L16 12M3.33789 7C5.06641 4.01074 8.29883 2 12 2C17.5225 2 22 6.47754 22 12C22 17.5225 17.5225 22 12 22C8.29883 22 5.06641 19.9893 3.33789 17" stroke="#000000" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 768 B

View File

@ -0,0 +1,35 @@
import "@/shared/ui/auth-classes.scss";
import Image from "next/image";
import sign_in_icon from "./icons/sign-in_icon.svg";
import SignInForm from "@/widgets/forms/SignInForm/SignInForm";
import { Metadata } from "next";
import { Link } from "@/shared/config/navigation";
export const metadata: Metadata = {
title: "KG ROAD | Вход",
description: "Страница входа KG ROAD",
};
const SignIn = () => {
return (
<div className="auth-page">
<div className="auth-wrapper">
<div className="auth-icon">
<Image src={sign_in_icon} alt="Sign In Icon" />
</div>
<div className="auth-header">
<h2>Войдите в аккаунт</h2>
<p>Пожалуйста, введите свои данные</p>
</div>
<SignInForm />
<p className="auth-redirect">
Еще нет аккаунта?{" "}
<Link href="/sign-up">Зарегистрируйтесь</Link>
</p>
</div>
</div>
);
};
export default SignIn;

View File

@ -0,0 +1,14 @@
<svg width="24.000000" height="24.000000" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip1668_53329">
<rect id="key" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="key" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip1668_53329)">
<path id="Vector" d="M21 2L19 4L15.5 7.5L11.3906 11.6104L11.3896 11.6113C10.3516 10.6094 8.96289 10.0547 7.52051 10.0674C6.07812 10.0801 4.69922 10.6582 3.67969 11.6777C2.65918 12.6973 2.08105 14.0771 2.06836 15.5195C2.05566 16.9609 2.61035 18.3506 3.6123 19.3877C4.12207 19.9043 4.72852 20.3145 5.39746 20.5957C6.06543 20.877 6.7832 21.0225 7.50879 21.0254C8.23438 21.0273 8.95312 20.8867 9.62402 20.6104C10.2939 20.333 10.9033 19.9268 11.416 19.4141C11.9297 18.9014 12.3359 18.292 12.6123 17.6211C12.8887 16.9502 13.0293 16.2324 13.0273 15.5068C13.0244 14.7812 12.8789 14.0635 12.5977 13.3945C12.3174 12.7256 11.9062 12.1191 11.3906 11.6104M15.5 7.5L18.5 10.5L22 7L19 4" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,28 @@
import "@/shared/ui/auth-classes.scss";
import Image from "next/image";
import key from "./icons/key.svg";
import ResetCodeForm from "@/widgets/forms/ResetCodeForm/ResetCodeForm";
const ResetCode = () => {
return (
<div className="auth-page">
<div className="auth-wrapper">
<div className="auth-icon2">
<Image src={key} alt="Key Icon" />
</div>
<div className="auth-header">
<h2>Введите новый пароль</h2>
<p>
Пароль должен содерждать минимум 8 символов, 1 заглавная
буква и цифра
</p>
</div>
<ResetCodeForm />
</div>
</div>
);
};
export default ResetCode;

View File

@ -0,0 +1,14 @@
<svg width="24.000000" height="24.000000" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip55_8430">
<rect id="mail" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="mail" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip55_8430)">
<path id="Icon" d="M20 4C21.0996 4 22 4.90039 22 6L22 18C22 19.0996 21.0996 20 20 20L4 20C2.90039 20 2 19.0996 2 18L2 6C2 4.90039 2.90039 4 4 4L20 4ZM22 6L12 13L2 6" stroke="#489FE1" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 739 B

View File

@ -0,0 +1,38 @@
import "@/shared/ui/auth-classes.scss";
import Image from "next/image";
import mail from "./icons/mail.svg";
import ConfirmEmailForm from "@/widgets/forms/ConfirmEmailForm/ConfirmEmailForm";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
const ConfirmEmail = ({
searchParams,
}: {
searchParams: {
email: string;
};
}) => {
return (
<div className="page-padding">
<BreadCrumbs />
<div className="auth-page">
<div className="auth-wrapper">
<div className="auth-icon2">
<Image
src={mail}
alt="Mail icon"
width={56}
height={56}
/>
</div>
<div className="auth-header">
<h2>Проверьте свою почту</h2>
<p>Мы отправили код на почту {searchParams.email}</p>
</div>
<ConfirmEmailForm email={searchParams.email} />
</div>
</div>
</div>
);
};
export default ConfirmEmail;

View File

@ -0,0 +1,7 @@
<svg width="20.317383" height="21.000977" viewBox="0 0 20.3174 21.001" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs/>
<path id="Icon" d="M12.2998 5.22266L18.2529 5.22266C18.7256 5.22266 18.9609 5.22266 19.0996 5.32129C19.2197 5.4082 19.2979 5.54102 19.3154 5.68848C19.335 5.85742 19.2207 6.06445 18.9912 6.47656L17.5703 9.03418C17.4873 9.18359 17.4453 9.25879 17.4297 9.33789C17.415 9.4082 17.415 9.48047 17.4297 9.55078C17.4453 9.62988 17.4873 9.70508 17.5703 9.85449L18.9912 12.4121C19.2207 12.8252 19.335 13.0312 19.3154 13.2002C19.2979 13.3477 19.2197 13.4805 19.0996 13.5674C18.9609 13.667 18.7256 13.667 18.2529 13.667L10.8213 13.667C10.2305 13.667 9.93457 13.667 9.70898 13.5518C9.51074 13.4502 9.34863 13.2891 9.24805 13.0898C9.13281 12.8643 9.13281 12.5693 9.13281 11.9775L9.13281 9.44434M10.6104 1C11.2021 1 11.4971 1 11.7227 1.11523C11.9219 1.21582 12.083 1.37793 12.1846 1.57617C12.2998 1.80176 12.2998 2.09766 12.2998 2.68848L12.2998 7.75586C12.2998 8.34668 12.2998 8.64258 12.1846 8.86816C12.083 9.06641 11.9219 9.22852 11.7227 9.3291C11.4971 9.44434 11.2021 9.44434 10.6104 9.44434L2.7998 9.44434L1.21289 3.09863C1.03418 2.38281 0.944336 2.02539 1.03125 1.74414C1.1084 1.49707 1.27246 1.28711 1.49316 1.15234C1.74512 1 2.11426 1 2.85156 1L10.6104 1ZM5.43848 20L1.21582 3.11133" stroke="#000000" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,37 @@
import "@/shared/ui/auth-classes.scss";
import Image from "next/image";
import flag from "./icons/flag.svg";
import SignUpForm from "@/widgets/forms/SignUpForm/SignUpForm";
import { Metadata } from "next";
import { Link } from "@/shared/config/navigation";
export const metadata: Metadata = {
title: "KG ROAD | Регистрация",
description:
"Страница регистрации KG ROAD",
};
const SignUp = () => {
return (
<div className="auth-page">
<div className="auth-wrapper">
<div className="auth-icon">
<Image src={flag} alt="Flag Icon" />
</div>
<div className="auth-header">
<h2>Регистрация</h2>
<p>Пожалуйста, введите свои данные</p>
</div>
<SignUpForm />
<p className="auth-redirect">
Уже есть аккаунт?{" "}
<Link href="/sign-in">Войти в аккаунт</Link>
</p>
</div>
</div>
);
};
export default SignUp;

View File

@ -0,0 +1,25 @@
.statistics {
display: flex;
flex-direction: column;
h2 {
width: fit-content;
margin-bottom: 40px;
}
}
@media screen and (max-width: 768px) {
.statistics {
h2 {
margin-bottom: 30px;
}
}
}
@media screen and (max-width: 550px) {
.statistics {
h2 {
margin-bottom: 20px;
}
}
}

View File

@ -0,0 +1,56 @@
import Typography from "@/shared/ui/components/Typography/Typography";
import "./Statistics.scss";
import StatisticsTable from "@/widgets/tables/StatisticsTable/StatisticsTable";
import { Metadata } from "next";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import { IMetatag } from "@/shared/types/metatag-type";
import { apiInstance } from "@/shared/config/apiConfig";
export async function generateMetadata(): Promise<Metadata> {
const data = await apiInstance
.get<IMetatag[]>("/metatags/")
.then((res) => res.data)
.catch((e) => console.log(e));
if (!data)
return {
title: "KG ROAD | Статистика",
description: `Статистика по населенным пунктам Кыргызстана`,
keywords: ["Бишкек", "Чуй", "Кыргызстан", "Дороги"],
};
const metadata = data.filter((tag) => tag.page === "statistics")[0];
if (!metadata) {
return {
title: "KG ROAD | Статистика",
description: `Статистика по населенным пунктам Кыргызстана`,
};
}
return {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
keywords: metadata.keywords.split(","),
openGraph: {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
type: "website",
},
};
}
const Statistics = ({
searchParams,
}: {
searchParams: { ["поиск-населенного-пункта"]: string };
}) => {
return (
<div className="statistics page-padding">
<BreadCrumbs homeRequired />
<Typography element="h2">Статистика</Typography>
<StatisticsTable searchParams={searchParams} />
</div>
);
};
export default Statistics;

View File

@ -0,0 +1,83 @@
.support {
display: flex;
flex-direction: column;
gap: 25px;
&__input {
display: flex;
flex-direction: column;
gap: 10px;
label {
font-size: 18px;
font-weight: 400;
line-height: 135%;
color: #686b7c;
}
div {
display: flex;
align-items: center;
gap: 16px;
input, textarea {
width: 100%;
max-width: 957px;
padding: 16px;
border: 1px solid rgb(221, 222, 226);
border-radius: 8px;
background: rgb(255, 255, 255);
font-size: 18px;
font-weight: 400;
line-height: 135%;
color: #09090b;
}
textarea {
height: 276px;
}
img {
width: 22px;
height: 22px;
}
}
}
&__btn,
&__btn_active {
margin-top: 50px;
width: fit-content;
padding: 10px;
border-radius: 8px;
background-color: rgb(158, 167, 175);
cursor: auto;
font-size: 16px;
font-weight: 800;
line-height: 19px;
color: rgb(255, 255, 255);
}
&__btn_active {
background-color: rgb(57, 152, 232);
cursor: pointer;
}
&__logout {
display: none;
}
}
@media screen and (max-width: 550px) {
.support {
&__btn,
&__btn_active {
align-self: flex-end;
margin-top: 15px;
margin-bottom: 150px;
}
&__logout {
display: block;
}
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 332 KiB

View File

@ -0,0 +1,126 @@
"use client";
import Typography from "@/shared/ui/components/Typography/Typography";
import "./Support.scss";
import Image from "next/image";
import header from "./assets/header.svg";
import { Metadata } from "next";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import { apiInstance } from "@/shared/config/apiConfig";
import { IMetatag } from "@/shared/types/metatag-type";
import { useState } from "react";
const Support = () => {
const [isSent, setIsSent] = useState(false);
const [userName, setUserName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const handleSubmit: React.MouseEventHandler<
HTMLFormElement
> = async (e) => {
e.preventDefault();
const data = {
name: userName,
phone: phone,
email: email,
message: message
};
try {
const res = await apiInstance.post(
"/support/",
data
);
} catch (error) {
console.log(error);
}
setIsSent(true);
};
function buttonIsActive()
{
return message === "" || userName === "";
}
return (
<div className="support page-padding">
<BreadCrumbs homeRequired />
<Typography element="h2">Поддержка</Typography>
<div className="support__descriptions">
<h3>
<span id="blue-point" /> Напишите нам
</h3>
{!isSent ? (
<form onSubmit={handleSubmit} className="support">
<div className="support__input">
<label>Как вас зовут*:</label>
<div>
<input name="name"
type="text"
value={userName}
onChange={(e) => setUserName(e.target.value)}
/></div>
</div>
<div className="support__input">
<label>Телефон:</label>
<div>
<input
name="phone"
type="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
</div>
</div>
<div className="support__input">
<label>Email:</label>
<div>
<input
name="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
</div>
<div className="support__input">
<label>Сообщение*:</label>
<div>
<div className="support__input">
<textarea name="review"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
</div>
<button type="submit"
className={`support__btn${
buttonIsActive() ? "" : "_active"
}`}
disabled={buttonIsActive()}>Отправить</button>
</div>
</form>
) : (
<p>Спасибо за ваше сообщение!</p>
)
}
</div>
</div>
);
};
export default Support;

View File

@ -0,0 +1,25 @@
.volunteers {
display: flex;
flex-direction: column;
h2 {
width: fit-content;
margin-bottom: 40px;
}
}
@media screen and (max-width: 768px) {
.volunteers {
h2 {
margin-bottom: 30px;
}
}
}
@media screen and (max-width: 550px) {
.volunteers {
h2 {
margin-bottom: 20px;
}
}
}

View File

@ -0,0 +1,54 @@
import Typography from "@/shared/ui/components/Typography/Typography";
import "./Volunteers.scss";
import VolunteersTable from "@/widgets/tables/VolunteersTable/VolunteersTable";
import { Metadata } from "next";
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
import { apiInstance } from "@/shared/config/apiConfig";
import { IMetatag } from "@/shared/types/metatag-type";
export async function generateMetadata(): Promise<Metadata> {
const data = await apiInstance
.get<IMetatag[]>("/metatags/")
.then((res) => res.data)
.catch((e) => console.log(e));
if (!data)
return {
title: "KG ROAD | Волонтеры",
description:
"Страница лучших волонтеров Кыргызской Республики!",
};
const metadata = data.filter((tag) => tag.page === "volunteers")[0];
if (!metadata) {
return {
title: "KG ROAD | Волонтеры",
description:
"Страница лучших волонтеров Кыргызской Республики!",
};
}
return {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
keywords: metadata.keywords.split(","),
openGraph: {
title: `KG ROAD | ${metadata.title}`,
description: metadata.description,
type: "website",
},
};
}
const Volunteers = () => {
return (
<div className="volunteers page-padding">
<BreadCrumbs homeRequired />
<Typography element="h2">Волонтеры</Typography>
<VolunteersTable />
</div>
);
};
export default Volunteers;

View File

@ -0,0 +1,6 @@
import NextAuth from "next-auth";
import { authConfig } from "@/shared/config/authConfig";
const handler = NextAuth(authConfig);
export { handler as GET, handler as POST };

BIN
src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

57
src/app/globals.scss Normal file
View File

@ -0,0 +1,57 @@
@import url("https://fonts.cdnfonts.com/css/inter");
* {
box-sizing: border-box;
padding: 0;
margin: 0;
font-family: "Tilda Sans";
}
button,
a,
input {
font-size: 16px;
}
button {
border: none;
background-color: transparent;
cursor: pointer;
}
a {
text-decoration: none;
color: black;
}
input {
border: none;
outline: none;
}
table {
width: 100%;
display: flex;
flex-direction: column;
}
ul,
ol {
list-style-type: none;
}
.page-padding {
padding: 40px 90px;
}
@media screen and (max-width: 1024px) {
.page-padding {
padding: 40px 30px;
}
}
@media screen and (max-width: 550px) {
.page-padding {
padding: 40px 16px;
}
}

32
src/app/layout.tsx Normal file
View File

@ -0,0 +1,32 @@
import { ReactNode } from "react";
type Props = {
children: ReactNode;
};
/*
<<<<<<< HEAD
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<head>
</head>
<body>
<Providers>
<Navbar />
<div className="app">{children}</div>
<Footer />
</Providers>
</body>
{<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS || ""} />}
</html>
);
=======*/
export default function RootLayout({ children }: Props) {
return children;
//>>>>>>> ali
}

15
src/app/not-found.tsx Normal file
View File

@ -0,0 +1,15 @@
"use client";
import NotFound from "@/widgets/NotFound/NotFound";
import "./globals.scss";
import "@/shared/fonts/fonts.scss";
export default function NotFoundPage() {
return (
<html lang="en">
<body>
<NotFound />
</body>
</html>
);
}

View File

@ -3,6 +3,7 @@
import Image, { StaticImageData } from "next/image";
import "./NewsCard.scss";
import Link from "next/link";
import { useTranslations } from "next-intl";
interface INewsCard {
id: number;
@ -19,6 +20,7 @@ const NewsCard: React.FC<INewsCard> = ({
description,
date,
}: INewsCard) => {
const t = useTranslations("general");
const sliceTitle = (title: string) => {
if (title.length > 35) {
return `${title.slice(0, 35)}...`;
@ -27,10 +29,7 @@ const NewsCard: React.FC<INewsCard> = ({
return title;
};
const sliceDate = (date: string) => {
return `${date.slice(8, 10)}/${date.slice(5, 7)}/${date.slice(
0,
4
)}`;
return `${date.slice(8, 10)}/${date.slice(5, 7)}/${date.slice(0, 4)}`;
};
const sliceDescription = (description: string) => {
@ -56,8 +55,14 @@ const NewsCard: React.FC<INewsCard> = ({
<p>{sliceDescription(description)}</p>
</div>
<Link href={`/news/${id}`} className="news-card__more-btn">
Подробнее
<Link
href={{
pathname: `/news/${id}`,
query: { новость: title },
}}
className="news-card__more-btn"
>
{t("details")}
</Link>
</div>
);

View File

@ -0,0 +1,55 @@
@import "@/shared/ui/variables.scss";
.auth-input {
display: flex;
flex-direction: column;
gap: 6px;
label {
font-size: 14px;
font-weight: 400;
line-height: 20px;
color: $gray-700;
}
&__field,
&__field-with-error {
padding: 10px 14px;
display: flex;
align-items: center;
border: 1px solid rgb(208, 213, 221);
border-radius: 8px;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
background: rgb(255, 255, 255);
input {
width: 100%;
font-size: 16px;
font-weight: 400;
line-height: 24px;
color: $gray-900;
::placeholder {
font-size: 16px;
font-weight: 400;
line-height: 24px;
color: $gray-500;
}
}
}
&__field-with-error {
border: 1px solid rgb(240, 68, 56);
}
p {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
font-weight: 400;
line-height: 20px;
color: $red-500;
}
}

View File

@ -0,0 +1,55 @@
"use client";
import Image from "next/image";
import "./AuthInput.scss";
import eye_off from "./icons/eye-off.svg";
import eye_on from "./icons/eye-on.svg";
import alert from "./icons/alert-circle.svg";
import { useState } from "react";
interface IAuthInputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
isPassword?: boolean;
label: string;
error: string;
}
const AuthInput: React.FC<IAuthInputProps> = ({
isPassword,
label,
error,
placeholder,
name,
type,
}: IAuthInputProps) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
return (
<div className="auth-input">
<label>{label}</label>
<div
className={`auth-input__field${error ? "-with-error" : ""}`}
>
<input
name={name}
placeholder={placeholder}
type={!isPassword ? type : isOpen ? type : "password"}
/>
{isPassword && (
<button
onClick={() => setIsOpen((prev) => !prev)}
type="button"
>
<Image src={isOpen ? eye_on : eye_off} alt="Eye Icon" />
</button>
)}
</div>
{error ? (
<p>
{error} <Image src={alert} alt="Alert Icon" />
</p>
) : null}
</div>
);
};
export default AuthInput;

View File

@ -0,0 +1,7 @@
<svg width="14.666992" height="14.666992" viewBox="0 0 14.667 14.667" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs/>
<path id="Icon" d="M7.33398 14C3.65137 14 0.666992 11.0156 0.666992 7.33398C0.666992 3.65137 3.65137 0.666992 7.33398 0.666992C11.0156 0.666992 14 3.65137 14 7.33398C14 11.0156 11.0156 14 7.33398 14ZM7.33398 4.66699L7.33398 7.33398M7.33398 10L7.33984 10" stroke="#F04438" stroke-opacity="1.000000" stroke-width="1.333333" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 566 B

View File

@ -0,0 +1,15 @@
<svg width="24.000000" height="24.000000" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip1668_50526">
<rect id="eye-off" width="24.000000" height="24.000000" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="eye-off" width="24.000000" height="24.000000" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip1668_50526)">
<path id="Vector" d="M17.9404 17.9404C16.2305 19.2432 14.1494 19.9648 12 20C5 20 1 12 1 12C2.24414 9.68164 3.96875 7.65625 6.05957 6.05957M9.90039 4.24023C10.5879 4.0791 11.293 3.99805 12 4C19 4 23 12 23 12C22.3926 13.1357 21.6689 14.2051 20.8398 15.1904M14.1201 14.1201C13.8457 14.415 13.5137 14.6514 13.1465 14.8154C12.7783 14.9795 12.3809 15.0674 11.9785 15.0742C11.5752 15.0811 11.1748 15.0078 10.8018 14.8564C10.4277 14.7061 10.0889 14.4814 9.80371 14.1963C9.51855 13.9111 9.29395 13.5723 9.14355 13.1982C8.99219 12.8252 8.91895 12.4248 8.92578 12.0215C8.93262 11.6191 9.02051 11.2217 9.18457 10.8535C9.34863 10.4863 9.58496 10.1543 9.87988 9.87988" stroke="#979797" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round"/>
<path id="Vector" d="M1 1L23 23" stroke="#979797" stroke-opacity="1.000000" stroke-width="2.000000" stroke-linejoin="round" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Some files were not shown because too many files have changed in this diff Show More