Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b5feaa43f | ||
|
|
545285f1c3 | ||
|
|
aa4bf468cd | ||
|
|
4f26c31c36 | ||
|
|
90acfefe80 | ||
|
|
e9509e9396 | ||
|
|
6837cfa46a | ||
|
|
c7f7380821 | ||
|
|
57fab57b9d | ||
|
|
ffe8c84d3d | ||
|
|
f38f7ad7bd | ||
|
|
d9ac888d48 | ||
|
|
b885c9c9bc | ||
|
|
2228025e0f | ||
|
|
046a17122d | ||
|
|
5fb5487ea9 | ||
|
|
80a3f658b9 | ||
|
|
45650996c3 | ||
|
|
36896b1fad | ||
|
|
779f37a1a3 | ||
|
|
a520214eb9 | ||
|
|
c54131197e | ||
|
|
006a1a7b7b | ||
|
|
18f173820f | ||
|
|
40a4116c03 | ||
|
|
0ba0f1df89 | ||
|
|
0221a79015 | ||
|
|
ad86f2e79e | ||
|
|
7d36510a9b | ||
|
|
7bc3e2ccb5 | ||
|
|
74b15060a1 | ||
|
|
bcc7279d39 | ||
|
|
5330f21a73 | ||
|
|
65a700dad7 | ||
|
|
0a7513dd89 | ||
|
|
6650a98503 | ||
|
|
e2db27b56f | ||
|
|
6637ab4eae | ||
|
|
d1440f3907 |
No files matched your search
@@ -4,7 +4,13 @@ declare module "next-auth" {
|
|||||||
interface Session {
|
interface Session {
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
access_token: string;
|
access_token: string;
|
||||||
expires_in: string;
|
expires_in: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
refresh_token: string;
|
||||||
|
access_token: string;
|
||||||
|
expires_in: Date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,6 +20,6 @@ declare module "next-auth/jwt" {
|
|||||||
interface JWT {
|
interface JWT {
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
access_token: string;
|
access_token: string;
|
||||||
expires_in: string;
|
expires_in: Date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
{
|
||||||
|
"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",
|
||||||
|
"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": "Transparency International-Kyrgyzstan",
|
||||||
|
"description": "Branch of the international organization Transparency International in the Kyrgyz Republic.",
|
||||||
|
"mission": "Promoting effective public policy and good governance to prevent corruption and strengthen democracy in the country.",
|
||||||
|
"goals_and_priorities": {
|
||||||
|
"anti-corruption_education": "Anti-corruption education of the population, raising public awareness of the importance and significance of the fight against corruption in Kyrgyzstan;",
|
||||||
|
"study_of_corruption_practices": "Organization of the study of the practice and theory of combating corruption and the participation of civil society structures in Kyrgyzstan and other countries;",
|
||||||
|
"supporting_citizens_and_organizations": "Assistance to citizens and organizations in the implementation of their constitutional rights and freedoms;",
|
||||||
|
"international_experience": "Preferential orientation to international experience in reducing corruption, mastering its technologies and resources, as well as involving civil society structures in the international dialogue on combating corruption."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
{
|
||||||
|
"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": "Биздин тиркемени жүктөп алыңыз",
|
||||||
|
"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": "Кеңирирээк"
|
||||||
|
},
|
||||||
|
"transparency_international_kyrgyzstan": {
|
||||||
|
"name": "Транспаренттыктык Интернационал-Кыргызстан",
|
||||||
|
"description": "Транспаренттыктык Интернационалдын Кыргызстан Республикасы бөлүмү.",
|
||||||
|
"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 баш тамгадан жана сандан турушу керек",
|
||||||
|
"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": "Кодду active1User@mail.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":"Уюмдун веб-сайтына өзүнүн жеке маалыматтарын жана маалыматын берген конок ушул Купуялык саясатынын жоболоруна макул болот. Колдонуучунун макулдугусуз Колдонуучулар тарабынан билдирилген жеке маалыматтарга уруксатсыз кирүүдөн коргоо тутумун андан ары өркүндөтүү максатында, биз каалаган убакта ушул Саясатка өзгөртүүлөрдү киргизүү укугун сактайбыз. Купуялык саясатыбызга олуттуу өзгөртүүлөрдү киргизгенде, биз Купуялык саясатыбыздын жаңыртылган версиясы менен бирге веб-сайтыбызга билдирүү жарыялайбыз. Бул Саясат үчүнчү жактардын аракеттерине жана интернет-ресурстарына жайылтылбайт."
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
{
|
||||||
|
"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": "Скачивай наше приложение",
|
||||||
|
"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": "Transparency International-Кыргызстан",
|
||||||
|
"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 заглавная буква и цифра",
|
||||||
|
"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": "Посетитель сайта Организации, предоставляющий свои персональные данные и информацию, тем самым соглашается с положениями данной Политики Конфиденциальности. Мы оставляем за собой право вносить любые изменения в Политику в любое время по своему усмотрению с целью дальнейшего совершенствования системы защиты от несанкционированного доступа к сообщаемым Пользователями персональным данным без согласия Пользователя. Когда мы вносим существенные изменения в Политику Конфиденциальности, на нашем сайте размещается соответствующее уведомление вместе с обновлённой версией Политики Конфиденциальности. Действие настоящей Политики не распространяется на действия и интернет-ресурсы третьих лиц."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import createNextIntlPlugin from "next-intl/plugin";
|
||||||
|
const withNextIntl = createNextIntlPlugin();
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
distDir: "build",
|
distDir: "build",
|
||||||
@@ -9,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);
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"next": "^14.1.0",
|
"next": "^14.1.0",
|
||||||
"next-auth": "^4.24.5",
|
"next-auth": "^4.24.5",
|
||||||
|
"next-intl": "^3.9.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-leaflet": "^4.2.1",
|
"react-leaflet": "^4.2.1",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
&__image {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
margin-bottom: 65px;
|
margin-bottom: 65px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -36,6 +36,22 @@
|
|||||||
line-height: 34px;
|
line-height: 34px;
|
||||||
color: rgb(62, 50, 50);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
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 = () => {
|
||||||
|
return (
|
||||||
|
<div className="about-us page-padding">
|
||||||
|
<BreadCrumbs homeRequired />
|
||||||
|
<Typography element="h2">О нас</Typography>
|
||||||
|
|
||||||
|
<div className="about-us__descriptions">
|
||||||
|
<h3>
|
||||||
|
Transparency International-Кыргызстан - филиал международной
|
||||||
|
организации Transparency International в Кыргызской
|
||||||
|
Республике.
|
||||||
|
</h3>
|
||||||
|
<h3>Миссия ТИ-Кыргызстан</h3>
|
||||||
|
<p>
|
||||||
|
Продвижение эффективной общественной политики и надлежащего
|
||||||
|
управления в целях предотвращения коррупции и усиления
|
||||||
|
демократии в стране.{" "}
|
||||||
|
</p>
|
||||||
|
<h3>Цели и приоритеты ТИ-Кыргызстан:</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
- антикоррупционное просвещение населения, повышение
|
||||||
|
общественного осознания значимости и важности борьбы с
|
||||||
|
коррупцией в Кыргызстане;
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
- организация изучения практики и теории борьбы с
|
||||||
|
коррупцией и участия в ней структур гражданского общества
|
||||||
|
в Кыргызстане и других странах;
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
- содействие гражданам и организациям в реализации их
|
||||||
|
конституционных прав и свобод;
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
- преимущественная ориентация на международный опыт
|
||||||
|
уменьшения коррупции, освоение его технологий, ресурсов, а
|
||||||
|
также включение структур гражданского общества в
|
||||||
|
международный диалог борьбы с коррупцией.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p className="about-us__author">
|
||||||
|
Photo By Mzximvs VdB from Brussels, belgium - Road to
|
||||||
|
Issyk-Kul (south shore), CC BY-SA 2.0
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AboutUs;
|
||||||
@@ -2,15 +2,15 @@ import Typography from "@/shared/ui/components/Typography/Typography";
|
|||||||
import "./CreateReport.scss";
|
import "./CreateReport.scss";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "KG ROAD | Написать обращение",
|
title: "KG ROAD | Написать обращение",
|
||||||
description:
|
description: "Написать обращение KG ROAD",
|
||||||
"Написать обращение KG ROAD",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DynamicForm = dynamic(
|
const DynamicForm = dynamic(
|
||||||
() => import("@/widgets/ReportForm/ReportForm"),
|
() => import("@/widgets/forms/ReportForm/ReportForm"),
|
||||||
{
|
{
|
||||||
ssr: false,
|
ssr: false,
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@ const DynamicForm = dynamic(
|
|||||||
const CreateReport = () => {
|
const CreateReport = () => {
|
||||||
return (
|
return (
|
||||||
<div className="create-report page-padding">
|
<div className="create-report page-padding">
|
||||||
|
<BreadCrumbs homeRequired />
|
||||||
<Typography element="h2">Написать обращение</Typography>
|
<Typography element="h2">Написать обращение</Typography>
|
||||||
|
|
||||||
<DynamicForm />
|
<DynamicForm />
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
.news {
|
.news {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 40px;
|
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.news {
|
.news {
|
||||||
gap: 30px;
|
h2 {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
@media screen and (max-width: 550px) {
|
||||||
.news {
|
.news {
|
||||||
gap: 20px;
|
h2 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import NotFound from "@/widgets/NotFound/NotFound";
|
||||||
|
|
||||||
|
const error = () => {
|
||||||
|
return <NotFound />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default error;
|
||||||
@@ -6,11 +6,33 @@ import Image from "next/image";
|
|||||||
import message from "./icons/message.svg";
|
import message from "./icons/message.svg";
|
||||||
import calendar from "./icons/calendar.svg";
|
import calendar from "./icons/calendar.svg";
|
||||||
import ReviewSection from "@/widgets/ReviewSection/ReviewSection";
|
import ReviewSection from "@/widgets/ReviewSection/ReviewSection";
|
||||||
|
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const NewsDetails = async ({
|
const NewsDetails = async ({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
params: { id: string };
|
params: { id: string; новость: string };
|
||||||
}) => {
|
}) => {
|
||||||
const getNewsById = async () => {
|
const getNewsById = async () => {
|
||||||
const response = await apiInstance.get<INews>(
|
const response = await apiInstance.get<INews>(
|
||||||
@@ -39,6 +61,7 @@ const NewsDetails = async ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="news-details page-padding">
|
<div className="news-details page-padding">
|
||||||
|
<BreadCrumbs />
|
||||||
<Typography element="h2">{data.title}</Typography>
|
<Typography element="h2">{data.title}</Typography>
|
||||||
|
|
||||||
<div className="news-details__img">
|
<div className="news-details__img">
|
||||||
@@ -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;
|
||||||
@@ -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 />;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<svg width="1072.000000" height="598.000000" viewBox="0 0 1072 598" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<desc>
|
||||||
|
Created with Pixso.
|
||||||
|
</desc>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern_627_213000" patternContentUnits="objectBoundingBox" width="1.000000" height="1.000000">
|
||||||
|
<use xlink:href="#image627_21300_0" transform="matrix(0.002315,0,0,0.004149,-0.056678,0)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image627_21300_0" width="481.000000" height="241.000000" xlink:href="data:image/png;base64,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 truncated
|
||||||
|
</defs>
|
||||||
|
<rect id="unsplash:NrN5Rvl89Lo" width="1072.000000" height="598.000000" fill="url(#pattern_627_213000)" fill-opacity="1.000000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 332 KiB |
@@ -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;
|
||||||
@@ -14,13 +14,9 @@ const AuthGuard = ({ children }: { children: React.ReactNode }) => {
|
|||||||
};
|
};
|
||||||
await apiInstance.post("/token/verify/", data);
|
await apiInstance.post("/token/verify/", data);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof AxiosError) {
|
signOut({
|
||||||
if (error.response?.data.code === "token_not_valid") {
|
callbackUrl: "/",
|
||||||
signOut({
|
});
|
||||||
callbackUrl: "/",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -6,13 +6,12 @@ import { AxiosError } from "axios";
|
|||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { authConfig } from "@/shared/config/authConfig";
|
import { authConfig } from "@/shared/config/authConfig";
|
||||||
import { IProfile } from "@/shared/types/profile-type";
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "KG ROAD | Профиль",
|
title: "KG ROAD | Профиль",
|
||||||
description:
|
description: "Страница профиля KG ROAD",
|
||||||
"Страница профиля KG ROAD",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Profile = async ({
|
const Profile = async ({
|
||||||
@@ -44,6 +43,7 @@ const Profile = async ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="profile page-padding">
|
<div className="profile page-padding">
|
||||||
|
<BreadCrumbs />
|
||||||
<Typography element="h2">Личный кабинет</Typography>
|
<Typography element="h2">Личный кабинет</Typography>
|
||||||
<ProfileNav report_count={data?.report_count as number} />
|
<ProfileNav report_count={data?.report_count as number} />
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import ProfileTable from "@/widgets/ProfileTable/ProfileTable";
|
import ProfileTable from "@/widgets/tables/ProfileTable/ProfileTable";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const MyReports = async ({
|
const MyReports = async ({
|
||||||
@@ -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;
|
||||||
@@ -4,7 +4,7 @@ import ProfileAvatar from "@/features/ProfileAvatar/ProfileAvatar";
|
|||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import { authConfig } from "@/shared/config/authConfig";
|
import { authConfig } from "@/shared/config/authConfig";
|
||||||
import { IProfile } from "@/shared/types/profile-type";
|
import { IProfile } from "@/shared/types/profile-type";
|
||||||
import ProfileForm from "@/widgets/ProfileForm/ProfileForm";
|
import ProfileForm from "@/widgets/forms/ProfileForm/ProfileForm";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import NotFound from "@/widgets/NotFound/NotFound";
|
||||||
|
|
||||||
|
const error = () => {
|
||||||
|
return <NotFound />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default error;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import "@/shared/ui/auth-classes.scss";
|
import "@/shared/ui/auth-classes.scss";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import sign_in_icon from "./icons/sign-in_icon.svg";
|
import sign_in_icon from "./icons/sign-in_icon.svg";
|
||||||
import SignInForm from "@/widgets/SignInForm/SignInForm";
|
import SignInForm from "@/widgets/forms/SignInForm/SignInForm";
|
||||||
import Link from "next/link";
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { Link } from "@/shared/config/navigation";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "KG ROAD | Вход",
|
title: "KG ROAD | Вход",
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import "@/shared/ui/auth-classes.scss";
|
import "@/shared/ui/auth-classes.scss";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import key from "./icons/key.svg";
|
import key from "./icons/key.svg";
|
||||||
import ResetCodeForm from "@/widgets/ResetCodeForm/ResetCodeForm";
|
import ResetCodeForm from "@/widgets/forms/ResetCodeForm/ResetCodeForm";
|
||||||
|
|
||||||
const ResetCode = () => {
|
const ResetCode = () => {
|
||||||
return (
|
return (
|
||||||
@@ -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;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import "@/shared/ui/auth-classes.scss";
|
import "@/shared/ui/auth-classes.scss";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import flag from "./icons/flag.svg";
|
import flag from "./icons/flag.svg";
|
||||||
import Link from "next/link";
|
import SignUpForm from "@/widgets/forms/SignUpForm/SignUpForm";
|
||||||
import SignUpForm from "@/widgets/SignUpForm/SignUpForm";
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { Link } from "@/shared/config/navigation";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "KG ROAD | Регистрация",
|
title: "KG ROAD | Регистрация",
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
.statistics {
|
.statistics {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 40px;
|
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.statistics {
|
.statistics {
|
||||||
gap: 30px;
|
h2 {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
@media screen and (max-width: 550px) {
|
||||||
.statistics {
|
.statistics {
|
||||||
gap: 20px;
|
h2 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<svg width="1072.000000" height="598.000000" viewBox="0 0 1072 598" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<desc>
|
||||||
|
Created with Pixso.
|
||||||
|
</desc>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern_627_213000" patternContentUnits="objectBoundingBox" width="1.000000" height="1.000000">
|
||||||
|
<use xlink:href="#image627_21300_0" transform="matrix(0.002315,0,0,0.004149,-0.056678,0)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image627_21300_0" width="481.000000" height="241.000000" xlink:href="data:image/png;base64,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 truncated
|
||||||
|
</defs>
|
||||||
|
<rect id="unsplash:NrN5Rvl89Lo" width="1072.000000" height="598.000000" fill="url(#pattern_627_213000)" fill-opacity="1.000000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 332 KiB |
@@ -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;
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
.volunteers {
|
.volunteers {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 40px;
|
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.volunteers {
|
.volunteers {
|
||||||
gap: 30px;
|
h2 {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
@media screen and (max-width: 550px) {
|
||||||
.volunteers {
|
.volunteers {
|
||||||
gap: 20px;
|
h2 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
|
||||||
import "./AboutUs.scss";
|
|
||||||
import Image from "next/image";
|
|
||||||
import header from "./assets/header.svg";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "KG ROAD | О нас",
|
|
||||||
description:
|
|
||||||
'Страница "О Нас" KG ROAD',
|
|
||||||
};
|
|
||||||
|
|
||||||
const AboutUs = () => {
|
|
||||||
return (
|
|
||||||
<div className="about-us page-padding">
|
|
||||||
<Typography element="h2">О нас</Typography>
|
|
||||||
|
|
||||||
<Image src={header} alt="Header Image" />
|
|
||||||
<div className="about-us__descriptions">
|
|
||||||
<p>Transparency International-Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.</p>
|
|
||||||
<h4>Миссия ТИ-Кыргызстан</h4>
|
|
||||||
<p>Продвижение эффективной общественной политики и надлежащего управления в целях предотвращения коррупции и усиления демократии в стране. </p>
|
|
||||||
<h4>Цели и приоритеты ТИ-Кыргызстан:</h4>
|
|
||||||
<ul>
|
|
||||||
<li> - антикоррупционное просвещение населения, повышение общественного осознания значимости и важности борьбы с коррупцией в Кыргызстане;</li>
|
|
||||||
<li> - организация изучения практики и теории борьбы с коррупцией и участия в ней структур гражданского общества в Кыргызстане и других странах;</li>
|
|
||||||
<li> - содействие гражданам и организациям в реализации их конституционных прав и свобод;</li>
|
|
||||||
<li> - преимущественная ориентация на международный опыт уменьшения коррупции, освоение его технологий, ресурсов, а также включение структур гражданского общества в международный диалог борьбы с коррупцией.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p>Photo By Mzximvs VdB from Brussels, belgium - Road to Issyk-Kul (south shore), CC BY-SA 2.0</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AboutUs;
|
|
||||||
@@ -4,10 +4,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont,
|
font-family: "Tilda Sans";
|
||||||
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
|
|
||||||
"Helvetica Neue", sans-serif;
|
|
||||||
// font-family: "Tilda Sans";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button,
|
button,
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import type { Metadata } from "next";
|
import { ReactNode } from "react";
|
||||||
import "./globals.scss";
|
|
||||||
import "./App.scss";
|
|
||||||
// import "@/shared/fonts/fonts.scss";
|
|
||||||
import { Providers } from "./Providers";
|
|
||||||
import Navbar from "@/widgets/Navbar/Navbar";
|
|
||||||
import Footer from "@/widgets/Footer/Footer";
|
|
||||||
import { GoogleAnalytics } from '@next/third-parties/google'
|
|
||||||
|
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
<<<<<<< HEAD
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
@@ -28,4 +25,8 @@ export default function RootLayout({
|
|||||||
{<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS || ""} />}
|
{<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS || ""} />}
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
=======*/
|
||||||
|
export default function RootLayout({ children }: Props) {
|
||||||
|
return children;
|
||||||
|
//>>>>>>> ali
|
||||||
}
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import "./News.scss";
|
|
||||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
|
||||||
import { INewsList } from "@/shared/types/news-type";
|
|
||||||
import NewsList from "@/widgets/NewsList/NewsList";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "KG ROAD | Новости",
|
|
||||||
description:
|
|
||||||
"Страница новостей KG ROAD",
|
|
||||||
};
|
|
||||||
|
|
||||||
const News = ({
|
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
searchParams: {
|
|
||||||
["страница-новостей"]: string;
|
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className="news page-padding">
|
|
||||||
<Typography element="h2">Новости</Typography>
|
|
||||||
|
|
||||||
<NewsList searchParams={searchParams} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default News;
|
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import Header from "@/widgets/Header/Header";
|
|
||||||
import StatisticsSection from "@/widgets/StatisticsSection/StatisticsSection";
|
|
||||||
import RatingSection from "@/widgets/RatingSection/RatingSection";
|
|
||||||
import NewsSection from "@/widgets/NewsSection/NewsSection";
|
|
||||||
import MapSection from "@/widgets/MapSection/MapSection";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "KG ROAD | Главная",
|
|
||||||
description:
|
|
||||||
"Главная страница KG ROAD",
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
const page = () => {
|
|
||||||
return <div>page</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default page;
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
import "./ReportDetails.scss";
|
|
||||||
import Image from "next/image";
|
|
||||||
import RoadType from "@/entities/RoadType/RoadType";
|
|
||||||
import ReportLike from "@/features/ReportLike/ReportLike";
|
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
|
||||||
import { IReport } from "@/shared/types/report-type";
|
|
||||||
import {
|
|
||||||
ROAD_TYPES,
|
|
||||||
ROAD_TYPES_COLORS,
|
|
||||||
} from "@/shared/variables/road-types";
|
|
||||||
import calendar from "./icons/calendar.svg";
|
|
||||||
import map_pin from "./icons/map-pin.svg";
|
|
||||||
import def_image from "./icons/def_image.svg";
|
|
||||||
import ReviewSection from "@/widgets/ReviewSection/ReviewSection";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "KG ROAD | Обращение",
|
|
||||||
description:
|
|
||||||
"Страница обращения KG ROAD",
|
|
||||||
};
|
|
||||||
|
|
||||||
const ReportDetails = async ({
|
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: { id: string };
|
|
||||||
}) => {
|
|
||||||
const getReportDetails = async () => {
|
|
||||||
const res = await fetch(
|
|
||||||
`${process.env.NEXT_PUBLIC_BASE_API}/report/${params.id}/`,
|
|
||||||
{ cache: "no-store" }
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.json();
|
|
||||||
};
|
|
||||||
const report: IReport = await getReportDetails();
|
|
||||||
|
|
||||||
const months: Record<string, string> = {
|
|
||||||
"01": "Январь",
|
|
||||||
"02": "Февраль",
|
|
||||||
"03": "Март",
|
|
||||||
"04": "Апрель",
|
|
||||||
"05": "Май",
|
|
||||||
"06": "Июнь",
|
|
||||||
"07": "Июль",
|
|
||||||
"08": "Август",
|
|
||||||
"09": "Сентябрь",
|
|
||||||
"10": "Октябрь",
|
|
||||||
"11": "Ноябрь",
|
|
||||||
"12": "Декабрь",
|
|
||||||
};
|
|
||||||
|
|
||||||
const showImages = () => {
|
|
||||||
const images = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < 5; i++) {
|
|
||||||
if (report.image[i]) {
|
|
||||||
const image = (
|
|
||||||
<img
|
|
||||||
className={`report-images__exist report-images__item${
|
|
||||||
i + 1
|
|
||||||
}`}
|
|
||||||
key={i}
|
|
||||||
src={report.image[i].image}
|
|
||||||
alt="Report Image"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
images.push(image);
|
|
||||||
} else {
|
|
||||||
const defImage = (
|
|
||||||
<div
|
|
||||||
className={`report-images__default report-images__item${
|
|
||||||
i + 1
|
|
||||||
}`}
|
|
||||||
key={i}
|
|
||||||
>
|
|
||||||
<Image src={def_image} alt="Default Image" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
images.push(defImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return images;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="report-details page-padding">
|
|
||||||
<div className="report-details__container">
|
|
||||||
<div className="report-information">
|
|
||||||
<RoadType color={ROAD_TYPES_COLORS[report.category]}>
|
|
||||||
{ROAD_TYPES[report.category]}
|
|
||||||
</RoadType>
|
|
||||||
<h2>{report.location[0].address}</h2>
|
|
||||||
<div className="report-information__date-and-like">
|
|
||||||
<div className="report-information__date">
|
|
||||||
<Image src={calendar} alt="Calendar Icon" />
|
|
||||||
<p>
|
|
||||||
{months[report.created_at.slice(5, 7)]}{" "}
|
|
||||||
{report.created_at.slice(5, 7).slice(0, 1) === "0"
|
|
||||||
? report.created_at.slice(6, 7)
|
|
||||||
: report.created_at.slice(5, 7)}
|
|
||||||
, {report.created_at.slice(0, 4)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<ReportLike
|
|
||||||
count={report.total_likes}
|
|
||||||
report_id={report.id}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="report-information__description">
|
|
||||||
{report.description}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p className="report-information__author">
|
|
||||||
Автор обращения:{" "}
|
|
||||||
<span>
|
|
||||||
{report.author.first_name}{" "}
|
|
||||||
{report.author.last_name.slice(0, 1)}.
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<button className="report-information__show-map">
|
|
||||||
<Image src={map_pin} alt="Map Pin Icon" />
|
|
||||||
Показать на карте
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="report-images">
|
|
||||||
{showImages().map((image) => image)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ReviewSection endpoint="report" id={+params.id} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReportDetails;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import "@/shared/ui/auth-classes.scss";
|
|
||||||
import ForgotPasswordForm from "@/widgets/ForgotPasswordForm/ForgotPasswordForm";
|
|
||||||
|
|
||||||
const ForgotPassword = () => {
|
|
||||||
return (
|
|
||||||
<div className="auth-page">
|
|
||||||
<ForgotPasswordForm />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ForgotPassword;
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import "@/shared/ui/auth-classes.scss";
|
|
||||||
import Image from "next/image";
|
|
||||||
import mail from "./icons/mail.svg";
|
|
||||||
import ConfirmEmailForm from "@/widgets/ConfirmEmailForm/ConfirmEmailForm";
|
|
||||||
|
|
||||||
const ConfirmEmail = ({
|
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
searchParams: {
|
|
||||||
email: string;
|
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConfirmEmail;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
|
||||||
import "./Statistics.scss";
|
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
|
||||||
import { IStatistics } from "@/shared/types/statistics-type";
|
|
||||||
import { AxiosError } from "axios";
|
|
||||||
import StatisticsTable from "@/widgets/StatisticsTable/StatisticsTable";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "KG ROAD | Статистика",
|
|
||||||
description:
|
|
||||||
"Страница статистики KG ROAD",
|
|
||||||
};
|
|
||||||
|
|
||||||
const Statistics = ({
|
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
searchParams: { ["поиск-населенного-пункта"]: string };
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className="statistics page-padding">
|
|
||||||
<Typography element="h2">Статистика</Typography>
|
|
||||||
<StatisticsTable searchParams={searchParams} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Statistics;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
|
||||||
import "./Volunteers.scss";
|
|
||||||
import VolunteersTable from "@/widgets/VolunteersTable/VolunteersTable";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "KG ROAD | Волонтеры",
|
|
||||||
description:
|
|
||||||
"Страница волонтеров KG ROAD",
|
|
||||||
};
|
|
||||||
|
|
||||||
const Volunteers = () => {
|
|
||||||
return (
|
|
||||||
<div className="volunteers page-padding">
|
|
||||||
<Typography element="h2">Волонтеры</Typography>
|
|
||||||
<VolunteersTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Volunteers;
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import Image, { StaticImageData } from "next/image";
|
import Image, { StaticImageData } from "next/image";
|
||||||
import "./NewsCard.scss";
|
import "./NewsCard.scss";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
interface INewsCard {
|
interface INewsCard {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -19,6 +20,7 @@ const NewsCard: React.FC<INewsCard> = ({
|
|||||||
description,
|
description,
|
||||||
date,
|
date,
|
||||||
}: INewsCard) => {
|
}: INewsCard) => {
|
||||||
|
const t = useTranslations("general");
|
||||||
const sliceTitle = (title: string) => {
|
const sliceTitle = (title: string) => {
|
||||||
if (title.length > 35) {
|
if (title.length > 35) {
|
||||||
return `${title.slice(0, 35)}...`;
|
return `${title.slice(0, 35)}...`;
|
||||||
@@ -56,8 +58,14 @@ const NewsCard: React.FC<INewsCard> = ({
|
|||||||
<p>{sliceDescription(description)}</p>
|
<p>{sliceDescription(description)}</p>
|
||||||
</div>
|
</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>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.breadcrumbs {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
span,
|
||||||
|
a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
gap: 8px;
|
||||||
|
color: rgb(72, 159, 225);
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: rgb(50, 48, 58);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Link, usePathname } from "@/shared/config/navigation";
|
||||||
|
import "./BreadCrumbs.scss";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import Image from "next/image";
|
||||||
|
import chevron from "./icons/chevron-right.svg";
|
||||||
|
|
||||||
|
interface IBreadcrumbsProps {
|
||||||
|
homeRequired?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BreadCrumbs: React.FC<IBreadcrumbsProps> = ({
|
||||||
|
homeRequired,
|
||||||
|
}: IBreadcrumbsProps) => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const query = useSearchParams().get("новость");
|
||||||
|
const routes = pathname.split("/").filter((route) => route !== "");
|
||||||
|
const tRoutes: Record<string, string> = {
|
||||||
|
"about-us": "О нас",
|
||||||
|
"create-report": "Написать обращение",
|
||||||
|
news: "Новости",
|
||||||
|
profile: "Профиль",
|
||||||
|
"my-reports": "Мои обращения",
|
||||||
|
personal: "Личные данные",
|
||||||
|
report: "Обращение",
|
||||||
|
"sign-in": "Войти в аккаунт",
|
||||||
|
"forgot-password": "Восстановление пароля",
|
||||||
|
"reset-code": "Сброс пароля",
|
||||||
|
"sign-up": "Регистрация",
|
||||||
|
"confirm-email": "Потверждение почты",
|
||||||
|
statistics: "Статистика",
|
||||||
|
volunteers: "Волонтеры",
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="breadcrumbs">
|
||||||
|
{homeRequired && (
|
||||||
|
<Link href="/">
|
||||||
|
Главная <Image src={chevron} alt="Chevron Right Icon" />
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
{routes.map((route, i, array) => {
|
||||||
|
if (routes.length === 1 && routes[0] === "profile")
|
||||||
|
return null;
|
||||||
|
if (parseInt(route)) {
|
||||||
|
if (routes[0] === "news") {
|
||||||
|
return <span key={query}>{query}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i === array.length - 1) {
|
||||||
|
return <span key={route}>{tRoutes[route]}</span>;
|
||||||
|
} else {
|
||||||
|
return route === "report" ? (
|
||||||
|
<span key={route}>{tRoutes[route]}</span>
|
||||||
|
) : (
|
||||||
|
<Link key={route} href={`/${route}`}>
|
||||||
|
{tRoutes[route]}
|
||||||
|
<Image src={chevron} alt="Chevron Right Icon" />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BreadCrumbs;
|
||||||
@@ -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="clip7_52716">
|
||||||
|
<rect id="chevron-right" width="16.000000" height="16.000000" fill="white" fill-opacity="0"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<rect id="chevron-right" width="16.000000" height="16.000000" fill="#FFFFFF" fill-opacity="0"/>
|
||||||
|
<g clip-path="url(#clip7_52716)">
|
||||||
|
<path id="Vector" d="M6 4L10 8L6 12" stroke="#667085" stroke-opacity="1.000000" stroke-width="1.000000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 651 B |
@@ -1,10 +1,26 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import "./GoogleButton.scss";
|
import "./GoogleButton.scss";
|
||||||
import google from "./icons/google.svg";
|
import google from "./icons/google.svg";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { signIn } from "next-auth/react";
|
||||||
|
|
||||||
const GoogleButton = () => {
|
const GoogleButton = () => {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const callbackUrl =
|
||||||
|
searchParams.get("callbackUrl") || "/profile/personal";
|
||||||
|
|
||||||
|
const googleLogin = async () => {
|
||||||
|
await signIn("google", { callbackUrl });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className="google-btn">
|
<button
|
||||||
|
type="button"
|
||||||
|
className="google-btn"
|
||||||
|
onClick={googleLogin}
|
||||||
|
>
|
||||||
<Image src={google} alt="Google Icon" />
|
<Image src={google} alt="Google Icon" />
|
||||||
Войти через Google
|
Войти через Google
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -11,11 +11,7 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="file"] {
|
&__change-btn {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
@@ -34,4 +30,126 @@
|
|||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__modal {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
position: relative;
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 15px;
|
||||||
|
box-shadow: 0px 16px 24px 0px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 28px;
|
||||||
|
color: rgb(51, 65, 85);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid rgb(226, 232, 240);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
width: 90%;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
color: rgb(100, 116, 139);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__user-img {
|
||||||
|
margin: 24px 0;
|
||||||
|
width: 130px;
|
||||||
|
height: 130px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="file"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__btns {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
button,
|
||||||
|
label {
|
||||||
|
min-width: 110px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin-top: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__blue-btn {
|
||||||
|
color: white;
|
||||||
|
background-color: rgb(72, 159, 225);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__gray-btn {
|
||||||
|
color: rgb(51, 65, 85);
|
||||||
|
border: 1px solid rgb(226, 232, 240);
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 32px;
|
||||||
|
padding: 8px;
|
||||||
|
width: 302px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px;
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,14 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import "./ProfileAvatar.scss";
|
import "./ProfileAvatar.scss";
|
||||||
import pen from "./icons/pen.svg";
|
import pen from "./icons/pen.svg";
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import close from "./icons/close.svg";
|
||||||
import { useSession } from "next-auth/react";
|
import close_white from "./icons/close-white.svg";
|
||||||
|
import { authInstanse } from "@/shared/config/apiConfig";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||||
|
|
||||||
interface IProfileAvatarProps {
|
interface IProfileAvatarProps {
|
||||||
img: string;
|
img: string;
|
||||||
@@ -14,35 +19,109 @@ interface IProfileAvatarProps {
|
|||||||
const ProfileAvatar: React.FC<IProfileAvatarProps> = ({
|
const ProfileAvatar: React.FC<IProfileAvatarProps> = ({
|
||||||
img,
|
img,
|
||||||
}: IProfileAvatarProps) => {
|
}: IProfileAvatarProps) => {
|
||||||
|
const [modal, setModal] = useState<boolean>(false);
|
||||||
|
const [display_image, setDisplayImage] = useState<File | string>(
|
||||||
|
img
|
||||||
|
);
|
||||||
|
const [isDeleting, setIsDeleting] = useState<boolean>(false);
|
||||||
|
const [message, setMessage] = useState<boolean>(false);
|
||||||
|
const def = process.env.NEXT_PUBLIC_PROFILE_AVATAR_URL as string;
|
||||||
|
|
||||||
|
|
||||||
|
const [success, setSuccess] = useState<string>("");
|
||||||
|
const [loader, setLoader] = useState<boolean>(false);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const changeImage: React.ChangeEventHandler<
|
|
||||||
HTMLInputElement
|
|
||||||
> = async (e) => {
|
|
||||||
const Authorization = `Bearer ${session.data?.access_token}`;
|
|
||||||
const config = {
|
|
||||||
headers: {
|
|
||||||
Authorization,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const formData = new FormData();
|
|
||||||
if (e.target.files) {
|
|
||||||
const image = Array.from(e.target.files);
|
|
||||||
formData.append("image", image[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (
|
||||||
const res = await apiInstance.patch(
|
e
|
||||||
"/users/update_image/",
|
) => {
|
||||||
formData,
|
if (e.target.files) {
|
||||||
config
|
setDisplayImage(e.target.files[0]);
|
||||||
);
|
|
||||||
router.refresh();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changeImage = async () => {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
if (session.status === "unauthenticated") return;
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof display_image === typeof "string" ||
|
||||||
|
display_image === img
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
formData.append("image", display_image);
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoader(true);
|
||||||
|
const res = await authInstanse(
|
||||||
|
session.data?.access_token as string
|
||||||
|
).patch("/users/update_image/", formData);
|
||||||
|
|
||||||
|
setError("");
|
||||||
|
setSuccess("Фото профиля обновлено");
|
||||||
|
setMessage(true);
|
||||||
|
router.refresh();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setMessage(false);
|
||||||
|
}, 3000);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
setSuccess("");
|
||||||
|
setError(error.message);
|
||||||
|
setMessage(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setMessage(false);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const returnDefaultImage = async () => {
|
||||||
|
if (session.status === "unauthenticated") return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoader(true);
|
||||||
|
const res = await authInstanse(
|
||||||
|
session.data?.access_token as string
|
||||||
|
).patch("/users/delete_image/", {});
|
||||||
|
|
||||||
|
setError("");
|
||||||
|
setSuccess("Фото профиля удалено");
|
||||||
|
setMessage(true);
|
||||||
|
setDisplayImage(def);
|
||||||
|
router.refresh();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setMessage(false);
|
||||||
|
}, 3000);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
setSuccess("");
|
||||||
|
setError(error.message);
|
||||||
|
setMessage(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setMessage(false);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const imageIsString =
|
||||||
|
typeof display_image === "string"
|
||||||
|
? display_image
|
||||||
|
: typeof display_image === "undefined"
|
||||||
|
? ""
|
||||||
|
: URL.createObjectURL(display_image as File);
|
||||||
return (
|
return (
|
||||||
<div className="profile-avatar">
|
<div className="profile-avatar">
|
||||||
<img
|
<img
|
||||||
@@ -50,10 +129,123 @@ const ProfileAvatar: React.FC<IProfileAvatarProps> = ({
|
|||||||
src={img}
|
src={img}
|
||||||
alt="User Image"
|
alt="User Image"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="profile-image">
|
<button
|
||||||
|
onClick={() => setModal(true)}
|
||||||
|
className="profile-avatar__change-btn"
|
||||||
|
>
|
||||||
<Image src={pen} alt="Pen Icon" />
|
<Image src={pen} alt="Pen Icon" />
|
||||||
</label>
|
</button>
|
||||||
<input onChange={changeImage} id="profile-image" type="file" />
|
|
||||||
|
{modal && (
|
||||||
|
<div
|
||||||
|
onClick={() => setModal(false)}
|
||||||
|
className="profile-avatar__modal"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
className="profile-avatar__wrapper"
|
||||||
|
>
|
||||||
|
<div className="profile-avatar__text">
|
||||||
|
<div>
|
||||||
|
<h4>Фото профиля</h4>
|
||||||
|
<button onClick={() => setModal(false)}>
|
||||||
|
<Image src={close} alt="Close Icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
По фото профиля другие люди смогут вас узнавать, а вам
|
||||||
|
будет проще определять, в какой аккаунт вы вошли.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img
|
||||||
|
className="profile-avatar__user-img"
|
||||||
|
src={imageIsString}
|
||||||
|
alt="User image"
|
||||||
|
/>
|
||||||
|
<div className="profile-avatar__btns">
|
||||||
|
{img === def && display_image === def ? (
|
||||||
|
<>
|
||||||
|
<label
|
||||||
|
className="profile-avatar__blue-btn"
|
||||||
|
htmlFor="change-image"
|
||||||
|
>
|
||||||
|
Добавить фото профиля
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
onChange={handleChange}
|
||||||
|
id="change-image"
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : isDeleting ? (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsDeleting(false)}
|
||||||
|
className="profile-avatar__gray-btn"
|
||||||
|
>
|
||||||
|
Отмена
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={returnDefaultImage}
|
||||||
|
disabled={loader}
|
||||||
|
className="profile-avatar__blue-btn"
|
||||||
|
>
|
||||||
|
{loader ? <Loader /> : "Удалить"}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : img === display_image || success !== "" ? (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsDeleting(true)}
|
||||||
|
className="profile-avatar__gray-btn"
|
||||||
|
>
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
<label
|
||||||
|
className="profile-avatar__blue-btn"
|
||||||
|
htmlFor="change-image"
|
||||||
|
>
|
||||||
|
Сменить
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
onChange={handleChange}
|
||||||
|
id="change-image"
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
onClick={() => setDisplayImage(img)}
|
||||||
|
className="profile-avatar__gray-btn"
|
||||||
|
>
|
||||||
|
Назад
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={loader}
|
||||||
|
onClick={changeImage}
|
||||||
|
className="profile-avatar__blue-btn"
|
||||||
|
>
|
||||||
|
{loader ? <Loader /> : "Сохранить"}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{message && (
|
||||||
|
<div className="profile-avatar__message">
|
||||||
|
<p>
|
||||||
|
{success} {error}
|
||||||
|
</p>
|
||||||
|
<button onClick={() => setMessage(false)}>
|
||||||
|
<Image src={close_white} alt="Close Icon White" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<svg width="20.000000" height="20.000000" viewBox="0 0 20 20" 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="clip2704_56355">
|
||||||
|
<rect id="icon" width="20.000000" height="20.000000" fill="white" fill-opacity="0"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#clip2704_56355)">
|
||||||
|
<path id="Vector" d="M14.375 5.625L5.625 14.375" stroke="#FFFFFF" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||||
|
<path id="Vector" d="M5.625 5.625L14.375 14.375" stroke="#FFFFFF" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 730 B |
@@ -0,0 +1,14 @@
|
|||||||
|
<svg width="12.000000" height="12.000000" viewBox="0 0 12 12" 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="clip4_25648">
|
||||||
|
<rect id="close" width="12.000000" height="12.000000" fill="white" fill-opacity="0"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#clip4_25648)">
|
||||||
|
<path id="Vector" d="M8.625 3.375L3.375 8.625" stroke="#334155" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||||
|
<path id="Vector" d="M3.375 3.375L8.625 8.625" stroke="#334155" stroke-opacity="1.000000" stroke-width="1.500000" stroke-linejoin="round" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 721 B |
@@ -4,9 +4,9 @@ import "./ReportLike.scss";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import like from "./icons/like.svg";
|
import like from "./icons/like.svg";
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import { useRouter } from "next/navigation";
|
import { signIn, useSession } from "next-auth/react";
|
||||||
import { useSession } from "next-auth/react";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { usePathname, useRouter } from "@/shared/config/navigation";
|
||||||
|
|
||||||
interface IReportLikeProps {
|
interface IReportLikeProps {
|
||||||
count: number;
|
count: number;
|
||||||
@@ -20,6 +20,7 @@ const ReportLike: React.FC<IReportLikeProps> = ({
|
|||||||
const session = useSession();
|
const session = useSession();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
const Authorization = `Bearer ${session?.data?.access_token}`;
|
const Authorization = `Bearer ${session?.data?.access_token}`;
|
||||||
const config = {
|
const config = {
|
||||||
@@ -29,6 +30,10 @@ const ReportLike: React.FC<IReportLikeProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const checkLike = async () => {
|
const checkLike = async () => {
|
||||||
|
if (session.status === "unauthenticated") {
|
||||||
|
signIn(undefined, { callbackUrl: pathname });
|
||||||
|
}
|
||||||
|
|
||||||
const res = await apiInstance.get<{ detail: string }>(
|
const res = await apiInstance.get<{ detail: string }>(
|
||||||
`/report/${report_id}/like/check/`,
|
`/report/${report_id}/like/check/`,
|
||||||
config
|
config
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
.report-message {
|
||||||
|
padding: 16px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 30000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.04),
|
||||||
|
0px 20px 24px -4px rgba(16, 24, 40, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: rgb(16, 24, 40);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: rgb(71, 84, 103);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding: 10px 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid rgb(72, 159, 225);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||||
|
background: rgb(72, 159, 225);
|
||||||
|
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Link, useRouter } from "@/shared/config/navigation";
|
||||||
|
import "./ReportMessage.scss";
|
||||||
|
import Image from "next/image";
|
||||||
|
import close from "./icons/close.svg";
|
||||||
|
import check from "./icons/check.svg";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
interface IReportMessageProps {
|
||||||
|
setShowMessage: (value: boolean) => void;
|
||||||
|
showMessage: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReportMessage: React.FC<IReportMessageProps> = ({
|
||||||
|
setShowMessage,
|
||||||
|
showMessage,
|
||||||
|
}: IReportMessageProps) => {
|
||||||
|
const router = useRouter();
|
||||||
|
useEffect(() => {
|
||||||
|
if (showMessage) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowMessage(false);
|
||||||
|
router.push("/");
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}, [showMessage]);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={() => setShowMessage(false)}
|
||||||
|
className="report-message"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
className="report-message__wrapper"
|
||||||
|
>
|
||||||
|
<div className="report-message__header">
|
||||||
|
<Image src={check} alt="Check Icon" />
|
||||||
|
<button onClick={() => setShowMessage(false)} type="button">
|
||||||
|
<Image src={close} alt="Close Icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="report-message__text">
|
||||||
|
<h4>Ваше обращение отправлено</h4>
|
||||||
|
<p>
|
||||||
|
Спасибо за ваше обращение. На данный момент оно в
|
||||||
|
модерации.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Link href="/profile/my-reports">Смотреть мои обращения</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportMessage;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="4" y="4" width="48" height="48" rx="24" fill="#D1FADF"/>
|
||||||
|
<rect x="4" y="4" width="48" height="48" rx="24" stroke="#ECFDF3" stroke-width="8"/>
|
||||||
|
<path d="M23.5 28L26.5 31L32.5 25M38 28C38 33.5228 33.5228 38 28 38C22.4772 38 18 33.5228 18 28C18 22.4772 22.4772 18 28 18C33.5228 18 38 22.4772 38 28Z" stroke="#039855" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 491 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M13 1L1 13M1 1L13 13" stroke="#667085" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 217 B |
@@ -3,6 +3,7 @@
|
|||||||
import "./SearchForm.scss";
|
import "./SearchForm.scss";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import search from "./icons/search.svg";
|
import search from "./icons/search.svg";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
interface ISearchFormProps
|
interface ISearchFormProps
|
||||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||||
@@ -17,6 +18,7 @@ const SearchForm: React.FC<ISearchFormProps> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
style,
|
style,
|
||||||
}: ISearchFormProps) => {
|
}: ISearchFormProps) => {
|
||||||
|
const t = useTranslations("general");
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
style={style}
|
style={style}
|
||||||
@@ -33,7 +35,7 @@ const SearchForm: React.FC<ISearchFormProps> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit">Поиск</button>
|
<button type="submit">{t("search")}</button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import map_pin from "./icons/map-pin.svg";
|
||||||
|
import { useReportStore } from "@/widgets/report-details/reportStore";
|
||||||
|
|
||||||
|
const ShowMapButton = () => {
|
||||||
|
const { setShowMap, showMap } = useReportStore();
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={() => setShowMap(!showMap)}
|
||||||
|
className="report-information__show-map"
|
||||||
|
>
|
||||||
|
<Image src={map_pin} alt="Map Pin Icon" />
|
||||||
|
Показать на карте
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ShowMapButton;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { notFound } from "next/navigation";
|
||||||
|
import { getRequestConfig } from "next-intl/server";
|
||||||
|
import { locales } from "@/shared/config/navigation";
|
||||||
|
|
||||||
|
export default getRequestConfig(async ({ locale }) => {
|
||||||
|
if (!locales.includes(locale as any)) notFound();
|
||||||
|
|
||||||
|
return {
|
||||||
|
messages: (await import(`../messages/${locale}.json`)).default,
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,9 +1,51 @@
|
|||||||
export { default } from "next-auth/middleware";
|
import { withAuth } from "next-auth/middleware";
|
||||||
|
import createIntlMiddleware from "next-intl/middleware";
|
||||||
|
import { NextRequest } from "next/server";
|
||||||
|
import { locales, localePrefix } from "./shared/config/navigation";
|
||||||
|
|
||||||
|
const privatePages = [
|
||||||
|
"/profile",
|
||||||
|
"/profile/personal",
|
||||||
|
"/profile/my-reports",
|
||||||
|
"/create-report",
|
||||||
|
];
|
||||||
|
|
||||||
|
const intlMiddleware = createIntlMiddleware({
|
||||||
|
locales,
|
||||||
|
localePrefix,
|
||||||
|
defaultLocale: "ru",
|
||||||
|
});
|
||||||
|
|
||||||
|
const authMiddleware = withAuth(
|
||||||
|
function onSuccess(req) {
|
||||||
|
return intlMiddleware(req);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
callbacks: {
|
||||||
|
authorized: ({ token }) => token != null,
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
signIn: "/sign-in",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function middleware(req: NextRequest) {
|
||||||
|
const publicPathnameRegex = RegExp(
|
||||||
|
`^(/(${locales.join("|")}))?(${privatePages
|
||||||
|
.flatMap((p) => (p === "/" ? ["", "/"] : p))
|
||||||
|
.join("|")})/?$`,
|
||||||
|
"i"
|
||||||
|
);
|
||||||
|
const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname);
|
||||||
|
|
||||||
|
if (!isPublicPage) {
|
||||||
|
return intlMiddleware(req);
|
||||||
|
} else {
|
||||||
|
return (authMiddleware as any)(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: [
|
matcher: ["/((?!api|_next|.*\\..*).*)"],
|
||||||
"/profile/personal",
|
|
||||||
"/profile/my-reports",
|
|
||||||
"/create-report",
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
@@ -5,3 +5,13 @@ const API_URL = process.env["NEXT_PUBLIC_BASE_API"];
|
|||||||
export const apiInstance = axios.create({
|
export const apiInstance = axios.create({
|
||||||
baseURL: API_URL,
|
baseURL: API_URL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const authInstanse = (access_token: string) => {
|
||||||
|
return axios.create({
|
||||||
|
baseURL: API_URL,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${access_token}`,
|
||||||
|
},
|
||||||
|
method: "",
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -2,24 +2,32 @@ import axios from "axios";
|
|||||||
import { AuthOptions } from "next-auth";
|
import { AuthOptions } from "next-auth";
|
||||||
import { JWT } from "next-auth/jwt";
|
import { JWT } from "next-auth/jwt";
|
||||||
import CredentialsProvider from "next-auth/providers/credentials";
|
import CredentialsProvider from "next-auth/providers/credentials";
|
||||||
|
import { apiInstance } from "./apiConfig";
|
||||||
interface IToken {
|
import { IRefresh, ITokens } from "../types/token-type";
|
||||||
access: string;
|
import GoogleProvider from "next-auth/providers/google";
|
||||||
}
|
|
||||||
|
|
||||||
const refreshToken = async (token: JWT): Promise<JWT> => {
|
const refreshToken = async (token: JWT): Promise<JWT> => {
|
||||||
|
const UTC = new Date();
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
refresh: token.refresh_token,
|
refresh: token.refresh_token,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axios.post<IToken>(
|
const response = await apiInstance.post<IRefresh>(
|
||||||
"https://api.kgroad.org/api/v1/token/refresh/",
|
"/users/refresh/",
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expirationTime = new Date(UTC.getTime() + 14 * 60000);
|
||||||
|
expirationTime.setTime(
|
||||||
|
expirationTime.getTime() +
|
||||||
|
expirationTime.getTimezoneOffset() * 60 * 1000 * -1
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...token,
|
...token,
|
||||||
access_token: response.data.access,
|
access_token: response.data.access,
|
||||||
|
expires_in: expirationTime,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,7 +35,6 @@ export const authConfig: AuthOptions = {
|
|||||||
providers: [
|
providers: [
|
||||||
CredentialsProvider({
|
CredentialsProvider({
|
||||||
name: "Credentials",
|
name: "Credentials",
|
||||||
|
|
||||||
credentials: {
|
credentials: {
|
||||||
email: {
|
email: {
|
||||||
label: "Email",
|
label: "Email",
|
||||||
@@ -36,35 +43,47 @@ export const authConfig: AuthOptions = {
|
|||||||
},
|
},
|
||||||
password: { label: "Password", type: "password" },
|
password: { label: "Password", type: "password" },
|
||||||
},
|
},
|
||||||
|
async authorize(credentials, req): Promise<any> {
|
||||||
async authorize(credentials, req) {
|
|
||||||
if (!credentials?.email || !credentials?.password)
|
if (!credentials?.email || !credentials?.password)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const { email, password } = credentials as any;
|
const { email, password } = credentials;
|
||||||
|
const data = {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await apiInstance.post<ITokens>(
|
||||||
"https://api.kgroad.org/api/v1/users/login/",
|
"/users/login/",
|
||||||
{
|
data
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.status.toString()[0] === "4") {
|
if ([200, 201].includes(res.status)) {
|
||||||
return null;
|
const currentTime = new Date();
|
||||||
|
const expirationTime = new Date(
|
||||||
|
currentTime.getTime() + 14 * 60000
|
||||||
|
);
|
||||||
|
expirationTime.setTime(
|
||||||
|
expirationTime.getTime() +
|
||||||
|
expirationTime.getTimezoneOffset() * 60 * 1000 * -1
|
||||||
|
);
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
refresh_token: res.data.refresh_token,
|
||||||
|
access_token: res.data.access_token,
|
||||||
|
expires_in: expirationTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await res.json();
|
return null;
|
||||||
return user;
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
GoogleProvider({
|
||||||
|
clientId: process.env.CLIENT_ID as string,
|
||||||
|
clientSecret: process.env.CLIENT_SECRET as string,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
pages: {
|
pages: {
|
||||||
signIn: "/sign-in",
|
signIn: "/sign-in",
|
||||||
@@ -73,15 +92,57 @@ export const authConfig: AuthOptions = {
|
|||||||
strategy: "jwt",
|
strategy: "jwt",
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
|
async signIn({ account, user }) {
|
||||||
|
if (account?.provider === "google") {
|
||||||
|
const data = {
|
||||||
|
auth_token: account?.id_token,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await apiInstance.post<ITokens>(
|
||||||
|
"/users/google/",
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (![200, 201].includes(res.status)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = new Date();
|
||||||
|
|
||||||
|
const expirationTime = new Date(
|
||||||
|
currentTime.getTime() + 15 * 60000
|
||||||
|
);
|
||||||
|
|
||||||
|
user.access_token = res.data.access_token;
|
||||||
|
user.refresh_token = res.data.refresh_token;
|
||||||
|
user.expires_in = expirationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
async jwt({ token, user }) {
|
async jwt({ token, user }) {
|
||||||
if (user) return { ...token, ...user };
|
if (user) return { ...token, ...user };
|
||||||
|
|
||||||
return refreshToken(token);
|
const UTC = new Date();
|
||||||
|
const currentTime = new Date(UTC.getTime());
|
||||||
|
currentTime.setTime(
|
||||||
|
currentTime.getTime() +
|
||||||
|
currentTime.getTimezoneOffset() * 60 * 1000 * -1
|
||||||
|
);
|
||||||
|
|
||||||
|
const isValid =
|
||||||
|
new Date(currentTime).getTime() <=
|
||||||
|
new Date(token.expires_in).getTime();
|
||||||
|
|
||||||
|
if (isValid) return token;
|
||||||
|
|
||||||
|
return await refreshToken(token);
|
||||||
},
|
},
|
||||||
|
|
||||||
async session({ token, session }) {
|
async session({ token, session }) {
|
||||||
session.access_token = token.access_token;
|
session.access_token = token.access_token;
|
||||||
session.refresh_token = token.refresh_token;
|
session.refresh_token = token.refresh_token;
|
||||||
|
session.expires_in = token.expires_in;
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { createSharedPathnamesNavigation } from "next-intl/navigation";
|
||||||
|
|
||||||
|
export const locales = ["en", "ru", "kg"] as const;
|
||||||
|
export const localePrefix = "always";
|
||||||
|
|
||||||
|
export const { Link, redirect, usePathname, useRouter } =
|
||||||
|
createSharedPathnamesNavigation({ locales, localePrefix });
|
||||||
@@ -3,5 +3,5 @@
|
|||||||
@import "./TildaSans-Medium/TildaSans-Medium.css";
|
@import "./TildaSans-Medium/TildaSans-Medium.css";
|
||||||
@import "./TildaSans-Semibold/TildaSans-Semibold.css";
|
@import "./TildaSans-Semibold/TildaSans-Semibold.css";
|
||||||
@import "./TildaSans-Bold/TildaSans-Bold.css";
|
@import "./TildaSans-Bold/TildaSans-Bold.css";
|
||||||
@import "./TildaSans-Extrabold/TildaSans-Extrabold.css";
|
@import "./TildaSans-ExtraBold/TildaSans-ExtraBold.css";
|
||||||
@import "./TildaSans-Black/TildaSans-Black.css";
|
@import "./TildaSans-Black/TildaSans-Black.css";
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface IList {
|
export interface IList {
|
||||||
count: number | null;
|
count: number;
|
||||||
previous: string | null;
|
previous: string | null;
|
||||||
next: string | null;
|
next: string | null;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface ILocation {
|
export interface ILocation {
|
||||||
id: 4;
|
id: number;
|
||||||
latitude: string;
|
latitude: string;
|
||||||
longitude: string;
|
longitude: string;
|
||||||
address: string;
|
address: string;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export interface IMetatag {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
keywords: string;
|
||||||
|
og_image?: string;
|
||||||
|
page: string;
|
||||||
|
}
|
||||||
@@ -15,3 +15,17 @@ export interface IReport {
|
|||||||
total_likes: number;
|
total_likes: number;
|
||||||
count_reviews: number;
|
count_reviews: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IRatingReport {
|
||||||
|
created_at: string;
|
||||||
|
category: null;
|
||||||
|
author: {
|
||||||
|
id: number;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
govern_status: null;
|
||||||
|
};
|
||||||
|
total_likes: number;
|
||||||
|
count_reviews: number;
|
||||||
|
address: string;
|
||||||
|
}
|
||||||
@@ -12,3 +12,7 @@ export interface IReview {
|
|||||||
review: string;
|
review: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IReviewList extends IList {
|
||||||
|
results: IReview[];
|
||||||
|
}
|
||||||
@@ -3,3 +3,8 @@ export interface ITokens {
|
|||||||
access_token: string;
|
access_token: string;
|
||||||
expires_in: string;
|
expires_in: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IRefresh {
|
||||||
|
access: string;
|
||||||
|
expires_at: string;
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
export interface IUserRatings {
|
export interface IUserRatings {
|
||||||
user_id: number;
|
user_id: number;
|
||||||
username: string;
|
username: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
report_count: number;
|
report_count: number;
|
||||||
likes_given_count: number;
|
likes_given_count: number;
|
||||||
likes_received_count: number;
|
likes_received_count: number;
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.auth-icon2 {
|
.auth-icon2 {
|
||||||
width: 60px;
|
min-width: 60px;
|
||||||
height: 60px;
|
min-height: 60px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -90,7 +90,6 @@
|
|||||||
@media screen and (max-width: 550px) {
|
@media screen and (max-width: 550px) {
|
||||||
.auth-page {
|
.auth-page {
|
||||||
padding: 48px 16px;
|
padding: 48px 16px;
|
||||||
min-height: 600px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-wrapper {
|
.auth-wrapper {
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
import "./Loader.scss";
|
import "./Loader.scss";
|
||||||
|
|
||||||
const Loader = () => {
|
interface ILoader {
|
||||||
return <span className="loader"></span>;
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Loader: React.FC<ILoader> = ({ color }: ILoader) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="loader"
|
||||||
|
style={{ borderTop: `3px solid ${color}` }}
|
||||||
|
></span>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Loader;
|
export default Loader;
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
export const LINKS = [
|
import { useTranslations } from "next-intl";
|
||||||
{ id: 1, pagename: "Главная", pathname: "/" },
|
|
||||||
{ id: 2, pagename: "О нас", pathname: "/about-us" },
|
export const LINKS = () => {
|
||||||
{ id: 3, pagename: "Статистика", pathname: "/statistics" },
|
const t = useTranslations("navigation");
|
||||||
{ id: 4, pagename: "Новости", pathname: "/news" },
|
|
||||||
{ id: 5, pagename: "Волонтеры", pathname: "/volunteers" },
|
const LINKS = [
|
||||||
];
|
{ id: 1, pagename: t("home"), pathname: "/" },
|
||||||
|
{ id: 2, pagename: t("about_us"), pathname: "/about-us" },
|
||||||
|
{ id: 4, pagename: t("statistics"), pathname: "/statistics" },
|
||||||
|
{ id: 5, pagename: t("news"), pathname: "/news" },
|
||||||
|
{ id: 6, pagename: t("volunteers"), pathname: "/volunteers" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return LINKS;
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
export const LINKS = () => {
|
||||||
|
const t = useTranslations("navigation");
|
||||||
|
|
||||||
|
const LINKS = [
|
||||||
|
{ id: 1, pagename: t("home"), pathname: "/" },
|
||||||
|
{ id: 2, pagename: t("about_us"), pathname: "/about-us" },
|
||||||
|
{ id: 3, pagename: t("privacy"), pathname: "/privacy" },
|
||||||
|
{ id: 3, pagename: t("support"), pathname: "/support" },
|
||||||
|
{ id: 4, pagename: t("statistics"), pathname: "/statistics" },
|
||||||
|
{ id: 5, pagename: t("news"), pathname: "/news" },
|
||||||
|
{ id: 6, pagename: t("volunteers"), pathname: "/volunteers" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return LINKS;
|
||||||
|
};
|
||||||