1 Commits
Author SHA1 Message Date
Vladislav Khorev a86d1adc57 prepare for uat 2024-02-04 13:53:49 +00:00
325 changed files with 6244 additions and 12118 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
NEXTAUTH_SECRET=";sadmfxflpdk"
NEXTAUTH_URL="http://localhost:3000"
-1
View File
@@ -5,7 +5,6 @@
/.pnp /.pnp
.pnp.js .pnp.js
.yarn/install-state.gz .yarn/install-state.gz
.env
# testing # testing
/coverage /coverage
+7
View File
@@ -0,0 +1,7 @@
import React from "react";
const page = () => {
return <div>page</div>;
};
export default page;
+3
View File
@@ -0,0 +1,3 @@
import RootLayout from "@/app/App";
export default RootLayout;
+3
View File
@@ -0,0 +1,3 @@
import Home from "@/pages/Home/Home";
export default Home;
+7
View File
@@ -0,0 +1,7 @@
import React from "react";
const page = () => {
return <div>page</div>;
};
export default page;
+7
View File
@@ -0,0 +1,7 @@
import React from "react";
const page = () => {
return <div>page</div>;
};
export default page;
+1 -8
View File
@@ -4,13 +4,6 @@ declare module "next-auth" {
interface Session { interface Session {
refresh_token: string; refresh_token: string;
access_token: string; access_token: string;
expires_in: Date;
}
interface User {
refresh_token: string;
access_token: string;
expires_in: Date;
} }
} }
@@ -20,6 +13,6 @@ declare module "next-auth/jwt" {
interface JWT { interface JWT {
refresh_token: string; refresh_token: string;
access_token: string; access_token: string;
expires_in: Date; exp: number;
} }
} }
-194
View File
@@ -1,194 +0,0 @@
{
"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"
}
}
-194
View File
@@ -1,194 +0,0 @@
{
"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 башкы буюк тамга жана 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": "Бардык укуктар корголгон"
}
}
-194
View File
@@ -1,194 +0,0 @@
{
"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": "Все права защищены"
}
}
+1 -9
View File
@@ -1,9 +1,5 @@
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
distDir: "build",
images: { images: {
remotePatterns: [ remotePatterns: [
{ {
@@ -12,10 +8,6 @@ const nextConfig = {
}, },
], ],
}, },
env: {
CLIENT_ID: process.env.CLIENT_ID,
CLIENT_SECRET: process.env.CLIENT_SECRET,
},
}; };
export default withNextIntl(nextConfig); export default nextConfig;
+4497
View File
File diff suppressed because it is too large. Load diff
+3 -7
View File
@@ -5,28 +5,24 @@
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start -p 8003", "start": "next start -p 8004",
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@next/third-parties": "^14.1.0",
"axios": "^1.6.5", "axios": "^1.6.5",
"dotenv": "^16.4.1",
"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",
"sass": "^1.70.0", "sass": "^1.70.0",
"sharp": "^0.33.2",
"use-debounce": "^10.0.0", "use-debounce": "^10.0.0",
"zustand": "^4.5.0" "zustand": "^4.5.0"
}, },
"devDependencies": { "devDependencies": {
"@types/leaflet": "^1.9.8", "@types/leaflet": "^1.9.8",
"@types/node": "^20.11.16", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"@types/react-leaflet": "^3.0.0", "@types/react-leaflet": "^3.0.0",
File renamed without changes.
+21
View File
@@ -0,0 +1,21 @@
import "./globals.scss";
import "./fonts.scss";
import "./App.scss";
import Footer from "@/widgets/Footer/Footer";
import Navbar from "@/widgets/Navbar/Navbar";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<Navbar />
<div className="app">{children}</div>
<Footer />
</body>
</html>
);
}
+9
View File
@@ -0,0 +1,9 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700;800;900&display=swap");
@import url("https://fonts.cdnfonts.com/css/arial");
@import url("../shared/fonts/TildaSans.css");
* {
font-family: Tilda Sans;
}
+25
View File
@@ -0,0 +1,25 @@
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
button,
a {
cursor: pointer;
color: black;
display: flex;
align-items: center;
justify-content: center;
border: none;
background-color: transparent;
text-decoration: none;
font-size: 16px;
}
ul,
ol,
li {
list-style-type: none;
list-style: none;
}
File renamed without changes.
@@ -3,7 +3,6 @@
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;
@@ -20,7 +19,6 @@ 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)}...`;
@@ -58,14 +56,8 @@ const NewsCard: React.FC<INewsCard> = ({
<p>{sliceDescription(description)}</p> <p>{sliceDescription(description)}</p>
</div> </div>
<Link <Link href={`/news/${id}`} className="news-card__more-btn">
href={{ Подробнее
pathname: `/news/${id}`,
query: { новость: title },
}}
className="news-card__more-btn"
>
{t("details")}
</Link> </Link>
</div> </div>
); );
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,47 @@
.section-header {
margin-bottom: 52px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
text-align: center;
h3 {
font-size: 42px;
font-weight: 500;
line-height: 50px;
}
p {
font-size: 24px;
font-weight: 300;
line-height: 29px;
}
}
@media screen and (max-width: 768px) {
.section-header {
margin-bottom: 42px;
h3 {
font-size: 36px;
line-height: 43px;
}
}
}
@media screen and (max-width: 550px) {
.section-header {
margin-bottom: 42px;
h3 {
font-size: 24px;
line-height: 29px;
}
p {
font-size: 18px;
line-height: 22px;
}
}
}
@@ -0,0 +1,22 @@
import "./SectionHeader.scss";
interface ISectionHeaderProps {
title: string;
description?: string;
style?: object;
}
const SectionHeader: React.FC<ISectionHeaderProps> = ({
title,
description,
style,
}: ISectionHeaderProps) => {
return (
<div style={style} className="section-header">
<h3>{title}</h3>
{description && <p>{description}</p>}
</div>
);
};
export default SectionHeader;
+2
View File
@@ -0,0 +1,2 @@
export const baseAPI =
"https://api.kgroaduat.fishrungames.com/api/v1";
File renamed without changes.
@@ -5,5 +5,4 @@
url("TildaSans-Black.woff2") format("woff2"), url("TildaSans-Black.woff2") format("woff2"),
url("TildaSans-Black.woff") format("woff"); url("TildaSans-Black.woff") format("woff");
font-weight: 900; font-weight: 900;
font-style: normal;
} }
@@ -5,5 +5,4 @@
url("TildaSans-Regular.woff2") format("woff2"), url("TildaSans-Regular.woff2") format("woff2"),
url("TildaSans-Regular.woff") format("woff"); url("TildaSans-Regular.woff") format("woff");
font-weight: 400; font-weight: 400;
font-style: normal;
} }
File renamed without changes.
@@ -1,5 +1,5 @@
export interface IFetch { export interface IFetch {
error: string; error: string;
data?: any; data: any;
isLoading: boolean; isLoading: boolean;
} }
+5
View File
@@ -0,0 +1,5 @@
export interface IList {
count: number | null;
previous: number | null;
next: number | null;
}
@@ -1,5 +1,3 @@
import { IList } from "./list-type";
export interface INews { export interface INews {
id: number; id: number;
image: string; image: string;
@@ -10,7 +8,3 @@ export interface INews {
updated_at: string; updated_at: string;
count_reviews: number; count_reviews: number;
} }
export interface INewsList extends IList {
results: INews[];
}
+32
View File
@@ -0,0 +1,32 @@
export interface IAuthor {
id: number;
first_name: string;
last_name: string;
govern_status: unknown;
}
export interface ILocation {
id: 4;
latitude: number;
longitude: number;
address: string;
}
export interface IReportImage {
id: number;
image: string;
}
export interface IReport {
results: any;
id: number;
image: IReportImage[];
description: string;
author: IAuthor;
created_at: string;
updated_at: string;
location: ILocation[];
category: number;
total_likes: number;
count_reviews: number;
}
@@ -6,5 +6,4 @@ $light-green: rgb(74, 192, 63);
$gray-300: #d0d5dd; $gray-300: #d0d5dd;
$gray-500: #667085; $gray-500: #667085;
$gray-700: #344054; $gray-700: #344054;
$gray-900: rgb(16, 24, 40);
$black: rgb(50, 48, 58); $black: rgb(50, 48, 58);
+7
View File
@@ -0,0 +1,7 @@
export const LINKS = [
{ id: 1, pagename: "Главная", pathname: "/" },
{ id: 2, pagename: "О нас", pathname: "/about-us" },
{ id: 3, pagename: "Статистика", pathname: "/statistics" },
{ id: 4, pagename: "Новости", pathname: "/news" },
{ id: 5, pagename: "Волонтеры", pathname: "/volunteers" },
];
+26
View File
@@ -0,0 +1,26 @@
export const ROAD_TYPES_STATS: Record<number, string> = {
1: "Разбитых дорог",
2: "Очагов аварийности",
3: "Локальных дефектов",
4: "В планах ремонта",
5: "Отремонтировано",
6: "Локальных дефектов исправлено",
};
export const ROAD_TYPES: Record<number, string> = {
1: "Разбитая дорога",
2: "Очаг аварийности",
3: "Локальный дефект",
4: "В планах ремонта",
5: "Отремонтировано",
6: "Локальный дефект исправлен",
};
export const ROAD_TYPES_COLORS: Record<number, string> = {
1: "rgb(230, 68, 82)",
2: "rgb(194, 136, 226)",
3: "rgb(135, 40, 157)",
4: "rgb(255, 172, 51)",
5: "rgb(254, 211, 99)",
6: "rgb(143, 222, 106)",
};
@@ -4,26 +4,13 @@
grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 30px; gap: 30px;
background-color: rgb(15, 23, 42); background-color: rgb(15, 23, 42);
&__logo {
display: flex;
flex-direction: column;
gap: 24px;
p {
color: white;
font-family: "Inter", sans-serif;
font-size: 16px;
}
}
a, a,
li, li,
h4 { h4 {
height: fit-content; height: fit-content;
color: white; color: white;
justify-content: flex-start; justify-content: flex-start;
font-family: "Inter", sans-serif; font-family: "Inter";
font-size: 16px; font-size: 16px;
} }
@@ -1,65 +1,53 @@
import "./Footer.scss"; import "./Footer.scss";
import logo from "../../shared/assets/logo.svg"; import logo from "../../shared/assets/logo.svg";
import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { LINKS } from "@/shared/variables/links_footer"; import { LINKS } from "@/shared/variables/links";
import youtube from "./icons/youtube.svg"; import youtube from "./icons/youtube.svg";
import facebook from "./icons/facebook.svg"; import facebook from "./icons/facebook.svg";
import instagram from "./icons/instagram.svg"; import instagram from "./icons/instagram.svg";
import app_store_btn from "./icons/app-store-btn.svg"; import app_store_btn from "./icons/app-store-btn.svg";
import play_market_btn from "./icons/play-market-btn.svg"; import play_market_btn from "./icons/play-market-btn.svg";
import { Link } from "@/shared/config/navigation";
import { useTranslations } from "next-intl";
import NetKgTracker from "@/widgets/NetKgTracker/NetKgTracker";
const Footer = () => { const Footer = () => {
const t = useTranslations("general");
const tDisclaimer = useTranslations("disclaimer");
const tRights = useTranslations("rights");
return ( return (
<footer className="footer"> <footer className="footer">
<div className="footer__logo"> <Link href="/">
<Link href="/"> <Image src={logo} alt="Logo" />
<Image src={logo} alt="Logo" /> </Link>
</Link>
<p>© {tRights("text")}</p>
<p>{tDisclaimer("text")}</p>
</div>
<div className="footer__links"> <div className="footer__links">
<h4>{t("navigation")}</h4> <h4>Навигация</h4>
<ul> <ul>
{LINKS().map((link) => ( {LINKS.map((link) => (
<li key={link.id}> <li>
<Link href={link.pathname} key={link.id}> <Link href={link.pathname} key={link.id}>
{link.pagename} {link.pagename}
</Link> </Link>
</li> </li>
))} ))}
</ul> </ul>
<NetKgTracker />
</div> </div>
<div className="footer__contacts"> <div className="footer__contacts">
<h4>{t("contacts")}</h4> <h4>Контакты</h4>
<ul> <ul>
<li>admin@kgroad.org</li> <li>namename@gmail.com</li>
<li>+9960312394038</li> <li>+09646895467</li>
<li> <li>
{[youtube, facebook, instagram].map((net, i) => ( {[youtube, facebook, instagram].map((net) => (
<Link key={i} href="#"> <Link href="#">
<Image src={net} alt="Net Icon" key={net} /> <Image src={net} alt="Net Icon" key={net} />
</Link> </Link>
))} ))}
</li> </li>
<li>Photo By ThomasG, CC BY-SA 3.0</li>
</ul> </ul>
</div> </div>
<div className="footer__apps"> <div className="footer__apps">
<h4>{t("download_our_app")}</h4> <h4>Скачивай наше приложение</h4>
<div className="footer__apps-btns"> <div className="footer__apps-btns">
{[app_store_btn, play_market_btn].map((app, i) => ( {[app_store_btn, play_market_btn].map((app) => (
<Link key={i} href="#"> <Link href="#">
<Image src={app} alt="App Button" key={app} /> <Image src={app} alt="App Button" key={app} />
</Link> </Link>
))} ))}
File renamed without changes.
File renamed without changes.
@@ -13,7 +13,6 @@
} }
&__text { &__text {
width: 100%;
padding: 0 90px; padding: 0 90px;
height: 100%; height: 100%;
position: relative; position: relative;
@@ -2,10 +2,8 @@ import "./Header.scss";
import Image from "next/image"; import Image from "next/image";
import background from "./assets/background.svg"; import background from "./assets/background.svg";
import HeaderLink from "./HeaderLink/HeaderLink"; import HeaderLink from "./HeaderLink/HeaderLink";
import { useTranslations } from "next-intl";
const Header = () => { const Header = () => {
const t = useTranslations("home");
return ( return (
<header className="header"> <header className="header">
<Image <Image
@@ -14,8 +12,12 @@ const Header = () => {
alt="Background Image" alt="Background Image"
/> />
<div className="header__text"> <div className="header__text">
<h1>{t("title")}</h1> <h1>
<p>{t("subtitle")}</p> Borem ipsum dolor sit amet, consectetur adipiscing elit.
</h1>
<p>
Borem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
</div> </div>
<HeaderLink /> <HeaderLink />
@@ -7,9 +7,6 @@
font-size: 20px; font-size: 20px;
font-weight: 500; font-weight: 500;
line-height: 24px; line-height: 24px;
display: flex;
justify-content: center;
align-items: center;
gap: 30px; gap: 30px;
border-radius: 15px; border-radius: 15px;
background-color: #fff; background-color: #fff;
@@ -1,15 +1,12 @@
import "./HeaderLink.scss"; import "./HeaderLink.scss";
import Image from "next/image"; import Image from "next/image";
import arrow_right from "./icons/arrow-right-circle.svg"; import arrow_right from "./icons/arrow-right-circle.svg";
import { Link } from "@/shared/config/navigation"; import Link from "next/link";
import { useTranslations } from "next-intl";
const HeaderLink = () => { const HeaderLink = () => {
const t = useTranslations("home");
return ( return (
<Link href="/create-report" className="header-link"> <Link href="/create-report" className="header-link">
{t("report_broken_road")} Отметить разбитую дорогу
<Image src={arrow_right} alt="Arrow Right Icon" /> <Image src={arrow_right} alt="Arrow Right Icon" />
</Link> </Link>
); );
@@ -0,0 +1,88 @@
"use client";
import "./HomeMap.scss";
import "leaflet/dist/leaflet.css";
import {
MapContainer,
Marker,
Popup,
TileLayer,
} from "react-leaflet";
import geo_green_icon from "./icons/geo-green.svg";
import geo_orange_icon from "./icons/geo-orange.svg";
import geo_pink_icon from "./icons/geo-pink.svg";
import geo_purple_icon from "./icons/geo-purple.svg";
import geo_red_icon from "./icons/geo-red.svg";
import geo_yellow_icon from "./icons/geo-yellow.svg";
import { DivIcon, Icon, LatLngTuple } from "leaflet";
import { StaticImageData } from "next/image";
import Link from "next/link";
import { ILocation } from "@/shared/types/report-type";
interface IData {
id: number;
location: ILocation[];
category: number;
}
interface IHomeMapProps {
data: IData[] | undefined;
}
const HomeMap: React.FC<IHomeMapProps> = ({
data,
}: IHomeMapProps) => {
const createCustomIcon = (icon: StaticImageData) => {
const customIcon = new Icon({
iconUrl: icon.src,
iconSize: [32, 32],
});
return customIcon;
};
const icons: Record<string, DivIcon> = {
1: createCustomIcon(geo_red_icon),
2: createCustomIcon(geo_pink_icon),
3: createCustomIcon(geo_purple_icon),
4: createCustomIcon(geo_orange_icon),
5: createCustomIcon(geo_green_icon),
6: createCustomIcon(geo_yellow_icon),
};
const position = [42.8746, 74.606];
return (
<MapContainer
center={position as LatLngTuple}
zoom={14}
scrollWheelZoom={false}
className="home-map"
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{data?.map((marker) => (
<Marker
key={marker.id}
icon={icons[marker.category]}
position={
[
+marker.location[0].latitude,
+marker.location[0].longitude,
] as LatLngTuple
}
>
<Popup>
<Link href={`/report/${marker.location[0].id}`}>
{marker.location[0].address}
</Link>
</Popup>
</Marker>
))}
</MapContainer>
);
};
export default HomeMap;
@@ -4,15 +4,6 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
&__header {
margin-bottom: 50px;
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
gap: 24px;
}
&__categories { &__categories {
margin-bottom: 60px; margin-bottom: 60px;
display: flex; display: flex;
@@ -37,13 +28,9 @@
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.map-section { .map-section {
&__header {
margin-bottom: 40px;
gap: 20px;
}
&__categories { &__categories {
margin-bottom: 40px; margin-bottom: 40px;
gap: 25px; gap: 25px;
} }
} }
@@ -51,13 +38,9 @@
@media screen and (max-width: 550px) { @media screen and (max-width: 550px) {
.map-section { .map-section {
&__header {
margin-bottom: 25px;
gap: 16px;
}
&__categories { &__categories {
margin-bottom: 25px; margin-bottom: 25px;
padding: 0 16px; padding: 0 16px;
width: 100%; width: 100%;
align-items: flex-start; align-items: flex-start;
+123
View File
@@ -0,0 +1,123 @@
"use client";
import "./MapSection.scss";
import SearchForm from "@/features/SearchBar/SearchForm";
import { useEffect, useState } from "react";
import SectionHeader from "@/entities/SectionHeader/SectionHeader";
import {
ROAD_TYPES,
ROAD_TYPES_COLORS,
} from "@/shared/variables/road-types";
import HomeMap from "./HomeMap/HomeMap";
import { useRouter } from "next/navigation";
import { useMapStore } from "./mapSectionStore";
import Switch from "./Switch/Switch";
import { useShallow } from "zustand/react/shallow";
interface IMapSectionProps {
[key: string]: string;
}
const MapSection: React.FC<IMapSectionProps> = ({
categories = "1,2,3,4,5,6",
queryMap,
queryRating,
}: IMapSectionProps) => {
const [mapSearch, setMapSearch] = useState<string>(queryMap || "");
const data = useMapStore(useShallow((state) => state.data));
const getReports = useMapStore(
useShallow((state) => state.getReports)
);
const router = useRouter();
useEffect(() => {
getReports(categories);
}, [categories]);
const handleSubmit: React.FormEventHandler<
HTMLFormElement
> = async (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
setMapSearch(formData.get("map-search") as string);
router.push(
`/?тип-дороги=${categories}${
mapSearch ? `&поиск-на-карте=${mapSearch}` : ""
}${queryRating ? `&поиск-рейтинг=${queryRating}` : ""}`,
{
scroll: false,
}
);
};
const setSearchParams = (category: string) => {
const availableCategories = ["1", "2", "3", "4", "5", "6"];
if (!categories || !availableCategories.includes(category))
return categories;
if (categories?.includes(category)) {
const updatedCategories = categories
?.replace(category + ",", "")
.replace("," + category, "")
.replace(category, "");
return updatedCategories;
} else {
const newValue = category + ",";
const updatedCategories = newValue + categories;
return updatedCategories;
}
};
return (
<section className="map-section">
<SectionHeader
title="Карта дорог"
description="Будьте в курсе последних новостей о дорожном движении, строительствах и мероприятиях!"
/>
<SearchForm
onChange={(e) => setMapSearch(e.target.value)}
name="map-search"
value={mapSearch}
placeholder="Введите город, село или регион"
handleSubmit={handleSubmit}
/>
<div className="map-section__categories">
<ul className="map-section__categories_left">
{[1, 2, 3].map((sw) => (
<li key={sw}>
<Switch
href={`/?тип-дороги=${setSearchParams(
sw.toString()
)}`}
color={ROAD_TYPES_COLORS[sw]}
defaultState={categories.includes(sw.toString())}
/>
<label>{ROAD_TYPES[sw]}</label>
</li>
))}
</ul>
<ul className="map-section__categories_right">
{[4, 5, 6].map((sw) => (
<li key={sw}>
<Switch
href={`/?тип-дороги=${setSearchParams(
sw.toString()
)}`}
color={ROAD_TYPES_COLORS[sw]}
defaultState={categories.includes(sw.toString())}
/>
<label>{ROAD_TYPES[sw]}</label>
</li>
))}
</ul>
</div>
<HomeMap data={data?.results} />
</section>
);
};
export default MapSection;
@@ -5,7 +5,7 @@ import "./Switch.scss";
import Link from "next/link"; import Link from "next/link";
interface ISwitchProps { interface ISwitchProps {
defaultState: boolean; defaultState?: boolean;
color?: string; color?: string;
href: string; href: string;
} }
@@ -25,7 +25,7 @@ const Switch: React.FC<ISwitchProps> = ({
onClick={() => setToggleSwitch((prev) => !prev)} onClick={() => setToggleSwitch((prev) => !prev)}
style={{ style={{
backgroundColor: !toggleSwitch backgroundColor: !toggleSwitch
? "rgb(71, 85, 105)" ? "rgb(50, 48, 58)"
: color : color
? color ? color
: "rgb(230, 68, 82)", : "rgb(230, 68, 82)",
+41
View File
@@ -0,0 +1,41 @@
import { baseAPI } from "@/shared/api/baseAPI";
import { IFetch } from "@/shared/types/fetch-type";
import { IList } from "@/shared/types/list-type";
import { IReport } from "@/shared/types/report-type";
import axios from "axios";
import { create } from "zustand";
interface IFetchReports extends IList {
results: IReport[];
}
interface IMapStore extends IFetch {
data: IFetchReports;
getReports: (categories: string) => Promise<void>;
}
export const useMapStore = create<IMapStore>((set) => ({
data: {
count: 0,
previous: null,
next: null,
results: [],
},
isLoading: false,
error: "",
getReports: async (categories: string = "1,2,3,4,5,6") => {
try {
set({ isLoading: true });
const response = await axios.get<IFetchReports>(
`${baseAPI}/report/?category=${categories}`
);
set({ data: response.data });
} catch (error: any) {
set({ error: error.message });
} finally {
set({ isLoading: false });
}
},
}));
@@ -1,41 +1,31 @@
import Link from "next/link";
import "./NavAuth.scss"; import "./NavAuth.scss";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useSession } from "next-auth/react";
import { Link } from "@/shared/config/navigation";
import { useTranslations } from "next-intl";
interface INavAuthProps { interface INavAuthProps {
responsible?: boolean; responsible?: boolean;
setOpenMenu: (boolean: boolean) => void;
} }
const NavAuth: React.FC<INavAuthProps> = ({ const NavAuth: React.FC<INavAuthProps> = ({
responsible, responsible,
setOpenMenu,
}: INavAuthProps) => { }: INavAuthProps) => {
const t = useTranslations("navigation"); const auth = false;
const session = useSession();
const auth = session.status === "authenticated" ? true : false;
const pathname = usePathname(); const pathname = usePathname();
return ( return (
<> <>
{auth ? ( {auth ? (
<Link <Link
onClick={() => setOpenMenu(false)} href="/profile"
href="/profile/personal"
className={`nav-auth-profile-${ className={`nav-auth-profile-${
responsible responsible
? `sm${pathname === "/profile" ? "_active" : ""}` ? `sm${pathname === "/profile" ? "_active" : ""}`
: "lg" : "lg"
}`} }`}
> >
{t("profile")} Профиль
</Link> </Link>
) : ( ) : (
<Link <Link
onClick={() => setOpenMenu(false)}
href="/sign-in" href="/sign-in"
className={`nav-auth-signin-${ className={`nav-auth-signin-${
responsible responsible
@@ -43,7 +33,7 @@ const NavAuth: React.FC<INavAuthProps> = ({
: "lg" : "lg"
}`} }`}
> >
{t("login")} Войти
</Link> </Link>
)} )}
</> </>
@@ -14,17 +14,14 @@
&__option, &__option,
&__option_active { &__option_active {
padding: 10px 14px;
width: 100%; width: 100%;
display: flex; padding: 10px 14px;
font-weight: 900; font-weight: 900;
justify-content: flex-start; justify-content: flex-start;
background-color: #fff;
} }
&__option_active { &__option_active {
background-color: rgb(249, 250, 251); background-color: rgb(249, 250, 251);
align-items: center;
justify-content: space-between; justify-content: space-between;
} }
@@ -0,0 +1,47 @@
import "./NavLanguage.scss";
import Image from "next/image";
import { useState } from "react";
import globus from "./icons/globus.svg";
import chevron from "./icons/chevron-down.svg";
import check from "./icons/check.svg";
const NavLanguage = () => {
const [language, setLanguage] = useState<string>("ru");
const [openMenu, setOpenMenu] = useState<boolean>(false);
const LANGUAGES = [
{ id: 1, language: "Русский", index: "ru" },
{ id: 2, language: "Кыргызча", index: "kg" },
{ id: 3, language: "English", index: "en" },
];
return (
<div className="nav-language">
<button
onClick={() => setOpenMenu((prev) => !prev)}
className="nav-language__btn"
>
<Image src={globus} alt="globus icon" />
<Image src={chevron} alt="chevron icon" />
</button>
{openMenu && (
<div className="nav-language__select">
{LANGUAGES.map((lang) => (
<button
className={`nav-language__option${
language === lang.index ? "_active" : ""
}`}
onClick={() => setLanguage(lang.index)}
key={lang.id}
>
{lang.language}
{lang.index === language ? (
<Image src={check} alt="check icon" />
) : null}
</button>
))}
</div>
)}
</div>
);
};
export default NavLanguage;
+25
View File
@@ -0,0 +1,25 @@
@import "@/shared/ui/variables.scss";
.nav-menu {
padding: 48px 30px 30px 30px;
width: 100%;
height: 100vh;
min-height: 500px;
position: fixed;
top: 72px;
left: 0;
display: flex;
flex-direction: column;
gap: 26px;
background-color: white;
a {
justify-content: flex-start;
font-size: 24px;
font-weight: 500;
}
&__link_active {
color: $light-blue;
}
}
+29
View File
@@ -0,0 +1,29 @@
import { LINKS } from "@/shared/variables/links";
import "./NavMenu.scss";
import Link from "next/link";
import { usePathname } from "next/navigation";
import NavAuth from "../NavAuth/NavAuth";
const NavMenu = () => {
const auth = false;
const pathname = usePathname();
return (
<nav className="nav-menu">
{LINKS.map((link) => (
<Link
className={`nav-menu__link${
pathname === link.pathname ? "_active" : ""
}`}
href={link.pathname}
key={link.id}
>
{link.pagename}
</Link>
))}
<NavAuth responsible />
</nav>
);
};
export default NavMenu;
@@ -5,26 +5,14 @@
width: 100%; width: 100%;
height: 78px; height: 78px;
position: fixed; position: fixed;
z-index: 10005; z-index: 10000;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
background-color: #fff; background-color: #fff;
&__logo {
display: flex;
align-items: center;
gap: 10px;
&_last {
min-width: 78px;
max-width: 78px;
height: 100%;
}
}
&__links { &__links {
height: 40%; height: 60%;
display: flex; display: flex;
gap: 60px; gap: 60px;
} }
@@ -58,16 +46,6 @@
} }
} }
@media screen and (max-width: 1220px) {
.navbar {
padding: 0 60px;
&__links {
gap: 40px;
}
}
}
@media screen and (max-width: 1024px) { @media screen and (max-width: 1024px) {
.navbar { .navbar {
padding: 0 30px; padding: 0 30px;
@@ -82,7 +60,7 @@
} }
} }
@media screen and (max-width: 900px) { @media screen and (max-width: 768px) {
.navbar { .navbar {
height: 72px; height: 72px;
@@ -1,48 +1,31 @@
"use client"; "use client";
import "./Navbar.scss"; import "./Navbar.scss";
import Image, { StaticImageData } from "next/image"; import Image from "next/image";
import { Link } from "@/shared/config/navigation"; import Link from "next/link";
import { usePathname } from "@/shared/config/navigation"; import { usePathname } from "next/navigation";
import logo from "@/shared/assets/logo.svg"; import logo from "@/shared/assets/logo.svg";
import { LINKS } from "@/shared/variables/links"; import { LINKS } from "@/shared/variables/links";
import NavLanguage from "./NavLanguage/NavLanguage"; import NavLanguage from "./NavLanguage/NavLanguage";
import NavAuth from "./NavAuth/NavAuth"; import NavAuth from "./NavAuth/NavAuth";
import menu from "./icons/menu.svg"; import menu from "./icons/menu.svg";
import cross from "./icons/cross.svg"; import cross from "./icons/cross.svg";
import NavMenu from "./NavMenu/NavMenu"; import NavMenu from "./NavMenu/NavMenu";
import { useState } from "react"; import { useState } from "react";
import founded_ru from "./assets/founded-ru.png";
import founded_en from "./assets/founded-en.png";
import founded_kg from "./assets/founded-kg.png";
import { useParams } from "next/navigation";
const Navbar = () => { const Navbar = () => {
const pathname = usePathname(); const pathname = usePathname();
const [openMenu, setOpenMenu] = useState<boolean>(false); const [openMenu, setOpenMenu] = useState<boolean>(false);
const { locale } = useParams();
const FOUNDED: Record<string, StaticImageData> = {
ru: founded_ru,
kg: founded_kg,
en: founded_en,
};
return ( return (
<section className="navbar"> <section className="navbar">
<div className="navbar__logo"> <Link href="/">
<Link href="/"> <Image src={logo} alt="Logo" />
<Image src={logo} alt="Logo" /> </Link>
</Link>
<Image
className="navbar__logo_last"
src={FOUNDED[locale as string]}
alt="Founded by EU Image"
/>
</div>
<nav className="navbar__links"> <nav className="navbar__links">
{LINKS().map((link) => ( {LINKS.map((link) => (
<Link <Link
className={`navbar__link${ className={`navbar__link${
pathname === link.pathname ? "_active" : "" pathname === link.pathname ? "_active" : ""
@@ -57,7 +40,7 @@ const Navbar = () => {
<div className="navbar__lang-and-auth"> <div className="navbar__lang-and-auth">
<NavLanguage /> <NavLanguage />
<NavAuth setOpenMenu={setOpenMenu} /> <NavAuth />
</div> </div>
<button <button
@@ -67,7 +50,7 @@ const Navbar = () => {
<Image src={openMenu ? cross : menu} alt="menu icon" /> <Image src={openMenu ? cross : menu} alt="menu icon" />
</button> </button>
{openMenu && <NavMenu setOpenMenu={setOpenMenu} />} {openMenu && <NavMenu />}
</section> </section>
); );
}; };
File renamed without changes.
Loaded 100 of 325 files, more files were not shown because too many files have changed in this diff. Show more