From 0ba0f1df8906a138792551f79ea3b744f7b8ff7b Mon Sep 17 00:00:00 2001 From: Alibek Date: Sat, 16 Mar 2024 02:19:33 +0600 Subject: [PATCH] fixed sort in profile-reports table and in statistics table, improved error messages, improved metatags by fetching metadata from server --- lib/next-auth.d.ts | 6 +- src/app/[locale]/about-us/page.tsx | 62 +++++++++++----- src/app/[locale]/news/[id]/page.tsx | 1 - src/app/[locale]/news/page.tsx | 37 ++++++++-- src/app/[locale]/page.tsx | 70 +++++++++++++------ src/app/[locale]/profile/personal/page.tsx | 1 + src/app/[locale]/report/[id]/page.tsx | 2 +- src/app/[locale]/statistics/page.tsx | 40 ++++++++--- src/app/[locale]/volunteers/page.tsx | 39 +++++++++-- src/shared/config/authConfig.ts | 68 +++++++++++++----- src/shared/types/metatag-type.ts | 8 +++ .../ConfirmEmailForm/ConfirmEmailForm.tsx | 37 +++++++--- .../confirm-code/confirm-code.tsx | 13 +++- .../send-email/send-email.tsx | 17 ++++- .../ChangePassword/ChangePassword.tsx | 13 +++- src/widgets/forms/ProfileForm/ProfileForm.tsx | 6 +- .../CreateReportMap/CreateReportMap.scss | 0 .../CreateReportMap/CreateReportMap.tsx | 7 -- src/widgets/forms/ReportForm/ReportForm.scss | 7 +- src/widgets/forms/ReportForm/ReportForm.tsx | 30 ++++++-- .../forms/ResetCodeForm/ResetCodeForm.tsx | 21 +++++- src/widgets/forms/SignInForm/SignInForm.tsx | 7 +- .../tables/ProfileTable/ProfileTable.tsx | 30 ++++---- .../ProfileTable/profile-reports.store.ts | 47 +++++-------- .../StatisticsTable/statistics.store.ts | 15 +--- 25 files changed, 413 insertions(+), 171 deletions(-) create mode 100644 src/shared/types/metatag-type.ts delete mode 100644 src/widgets/forms/ReportForm/CreateReportMap/CreateReportMap.scss delete mode 100644 src/widgets/forms/ReportForm/CreateReportMap/CreateReportMap.tsx diff --git a/lib/next-auth.d.ts b/lib/next-auth.d.ts index e27115c..d0e228b 100644 --- a/lib/next-auth.d.ts +++ b/lib/next-auth.d.ts @@ -4,13 +4,13 @@ declare module "next-auth" { interface Session { refresh_token: string; access_token: string; - expires_in?: string; + expires_in: Date; } interface User { refresh_token: string; access_token: string; - expires_in?: string; + expires_in: Date; } } @@ -20,6 +20,6 @@ declare module "next-auth/jwt" { interface JWT { refresh_token: string; access_token: string; - expires_in?: string; + expires_in: Date; } } diff --git a/src/app/[locale]/about-us/page.tsx b/src/app/[locale]/about-us/page.tsx index f259970..8fba7c8 100644 --- a/src/app/[locale]/about-us/page.tsx +++ b/src/app/[locale]/about-us/page.tsx @@ -4,26 +4,52 @@ 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"; -export const metadata: Metadata = { - title: "KG ROAD | О нас", - description: - "Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.", - keywords: [ - "Миссия Transparency International - Кыргызстан", - "Цели и приоритеты ТИ-Кыргызстан", - ], - openGraph: { - images: [ - { - url: header.src, +export async function generateMetadata(): Promise { + const data = await apiInstance + .get("/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 в Кыргызской Республике.", }, - ], - 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 ( diff --git a/src/app/[locale]/news/[id]/page.tsx b/src/app/[locale]/news/[id]/page.tsx index ceef954..249bdd2 100644 --- a/src/app/[locale]/news/[id]/page.tsx +++ b/src/app/[locale]/news/[id]/page.tsx @@ -26,7 +26,6 @@ export async function generateMetadata({ type: "article", publishedTime: response.data.created_at, }, - keywords: ["Новости КР", "Кыргызстан"], }; } diff --git a/src/app/[locale]/news/page.tsx b/src/app/[locale]/news/page.tsx index 5c8f5b4..f992d65 100644 --- a/src/app/[locale]/news/page.tsx +++ b/src/app/[locale]/news/page.tsx @@ -3,11 +3,40 @@ 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 const metadata: Metadata = { - title: "KG ROAD | Новости", - description: "Страница новостей KG ROAD", -}; +export async function generateMetadata(): Promise { + const data = await apiInstance + .get("/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, diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx index a414579..43c026e 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/[locale]/page.tsx @@ -4,29 +4,55 @@ 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 const metadata: Metadata = { - title: "KG ROAD | Главная", - description: - "Главная страница KG ROAD | Сделаем дороги безопасными!", - keywords: [ - "Новости", - "Разбитые дороги", - "Очаги аварийности", - "Локальные дефекты", - "Дороги В планах ремонта", - "Отремонтированные дороги", - "Исправленные локальные дефекты", - "Карта дорог", - "Рейтинг", - ], - openGraph: { - title: "KG ROAD | Главная", - description: - "Главная страница KG ROAD | Сделаем дороги безопасными!", - type: "website", - }, -}; +export async function generateMetadata(): Promise { + const data = await apiInstance + .get("/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, diff --git a/src/app/[locale]/profile/personal/page.tsx b/src/app/[locale]/profile/personal/page.tsx index 65922c8..d5076ba 100644 --- a/src/app/[locale]/profile/personal/page.tsx +++ b/src/app/[locale]/profile/personal/page.tsx @@ -11,6 +11,7 @@ import React from "react"; const Personal = async () => { const session = await getServerSession(authConfig); + console.log(session?.expires_in); const getProfile = async () => { const Authorization = `Bearer ${session?.access_token}`; const config = { diff --git a/src/app/[locale]/report/[id]/page.tsx b/src/app/[locale]/report/[id]/page.tsx index ac2d6a2..715fca5 100644 --- a/src/app/[locale]/report/[id]/page.tsx +++ b/src/app/[locale]/report/[id]/page.tsx @@ -32,7 +32,7 @@ export async function generateMetadata({ title: `KG ROAD | ${response.data.location[0].address}`, description: response.data.description, images: [response.data.image[0].image], - type: "website", + type: "article", }, }; } diff --git a/src/app/[locale]/statistics/page.tsx b/src/app/[locale]/statistics/page.tsx index 763bebf..2a37ac7 100644 --- a/src/app/[locale]/statistics/page.tsx +++ b/src/app/[locale]/statistics/page.tsx @@ -3,18 +3,42 @@ 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 { + const data = await apiInstance + .get("/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: `Статистика по населенным пунктам Кыргызстана`, + }; + } -export async function generateMetadata({ - searchParams, -}: { - searchParams: { "поиск-населенного-пункта": string }; -}): Promise { return { - title: "KG ROAD | Статистика", - description: `Статистика по ${searchParams["поиск-населенного-пункта"]} KG ROAD`, - keywords: ["Бишкек", "Чуй", "Кыргызстан", "Дороги"], + 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, }: { diff --git a/src/app/[locale]/volunteers/page.tsx b/src/app/[locale]/volunteers/page.tsx index d5ed4ac..d46ba37 100644 --- a/src/app/[locale]/volunteers/page.tsx +++ b/src/app/[locale]/volunteers/page.tsx @@ -3,11 +3,42 @@ 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 const metadata: Metadata = { - title: "KG ROAD | Волонтеры", - description: "Страница лучших волонтеров Кыргызской Республики!", -}; +export async function generateMetadata(): Promise { + const data = await apiInstance + .get("/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 ( diff --git a/src/shared/config/authConfig.ts b/src/shared/config/authConfig.ts index a764a8e..8dc4640 100644 --- a/src/shared/config/authConfig.ts +++ b/src/shared/config/authConfig.ts @@ -19,16 +19,7 @@ const verifyToken = async (access_token: string) => { }; const refreshToken = async (token: JWT): Promise => { - // const date = new Date().toLocaleTimeString(); - // const expire = new Date(token.expires_in).toLocaleTimeString(); - - const verify = await verifyToken(token.access_token); - - if (verify) { - return { - ...token, - }; - } + const UTC = new Date(); const data = { refresh: token.refresh_token, @@ -39,10 +30,16 @@ const refreshToken = async (token: JWT): Promise => { data ); + const expirationTime = new Date(UTC.getTime() + 14 * 60000); + expirationTime.setTime( + expirationTime.getTime() + + expirationTime.getTimezoneOffset() * 60 * 1000 * -1 + ); + return { ...token, access_token: response.data.access, - expires_in: response.data.expires_at, + expires_in: expirationTime, }; }; @@ -58,20 +55,37 @@ export const authConfig: AuthOptions = { }, password: { label: "Password", type: "password" }, }, - async authorize(credentials, req) { + async authorize(credentials, req): Promise { if (!credentials?.email || !credentials?.password) return null; - const { email, password } = credentials as any; + const { email, password } = credentials; const data = { email, password, }; - const res = await apiInstance.post("/users/login/", data); + const res = await apiInstance.post( + "/users/login/", + data + ); if ([200, 201].includes(res.status)) { - const user = res.data; + 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; } @@ -105,9 +119,15 @@ export const authConfig: AuthOptions = { 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 = res.data.expires_in; + user.expires_in = expirationTime; } return true; @@ -115,12 +135,26 @@ export const authConfig: AuthOptions = { async jwt({ 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 }) { session.access_token = token.access_token; session.refresh_token = token.refresh_token; + session.expires_in = token.expires_in; return session; }, diff --git a/src/shared/types/metatag-type.ts b/src/shared/types/metatag-type.ts new file mode 100644 index 0000000..1920201 --- /dev/null +++ b/src/shared/types/metatag-type.ts @@ -0,0 +1,8 @@ +export interface IMetatag { + id: number; + title: string; + description: string; + keywords: string; + og_image?: string; + page: string; +} diff --git a/src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.tsx b/src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.tsx index 439bac4..2242161 100644 --- a/src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.tsx +++ b/src/widgets/forms/ConfirmEmailForm/ConfirmEmailForm.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from "react"; import { apiInstance } from "@/shared/config/apiConfig"; import Loader from "@/shared/ui/components/Loader/Loader"; import { useRouter } from "@/shared/config/navigation"; +import { AxiosError } from "axios"; interface IConfirmEmailFormProps { email: string; @@ -54,24 +55,38 @@ const ConfirmEmailForm: React.FC = ({ if (response.status === 200 || response.status === 201) { router.push("/sign-in"); } - } catch (error) { - setError("Возникла ошибка"); + } catch (error: unknown) { + if (error instanceof AxiosError) { + if ([400, 404].includes(error.response?.status as number)) { + setError("Неверный код подтверждения"); + } else { + setError("Ошибка на стороне сервера"); + } + } else { + setError("Произошла непредвиденная ошибка"); + } } finally { setLoader(false); } }; const handleClick = async () => { - const data = { - email, - }; - const response = await apiInstance.post( - "/users/resend_code/", - data - ); + try { + const data = { + email, + }; + const response = await apiInstance.post( + "/users/resend_code/", + data + ); - if (response.status === 200 || response.status === 201) { - setMinutes(1); + if ([200, 201].includes(response.status)) { + setMinutes(1); + } + } catch (error) { + setError( + "Проблема на стороне сервера или вы достигли максимальное количество попыток" + ); } }; diff --git a/src/widgets/forms/ForgotPasswordForm/confirm-code/confirm-code.tsx b/src/widgets/forms/ForgotPasswordForm/confirm-code/confirm-code.tsx index 9a34346..7546f29 100644 --- a/src/widgets/forms/ForgotPasswordForm/confirm-code/confirm-code.tsx +++ b/src/widgets/forms/ForgotPasswordForm/confirm-code/confirm-code.tsx @@ -7,6 +7,7 @@ import { apiInstance } from "@/shared/config/apiConfig"; import Loader from "@/shared/ui/components/Loader/Loader"; import { ITokens } from "@/shared/types/token-type"; import { useRouter } from "@/shared/config/navigation"; +import { AxiosError } from "axios"; interface IConfirmCodeProps { setChangeForm: (boolean: boolean) => void; @@ -45,8 +46,16 @@ const ConfirmCode: React.FC = ({ router.push("/sign-in/reset-code"); } setLoader(false); - } catch (error) { - setError("An error ocured"); + } catch (error: unknown) { + if (error instanceof AxiosError) { + if (error.response?.status === 400) { + setError("Неверный код"); + } else { + setError("Ошибка на стороне сервера"); + } + } else { + setError("Произошла непредвиденная ошибка"); + } setLoader(false); } }; diff --git a/src/widgets/forms/ForgotPasswordForm/send-email/send-email.tsx b/src/widgets/forms/ForgotPasswordForm/send-email/send-email.tsx index 774a825..c82e5f4 100644 --- a/src/widgets/forms/ForgotPasswordForm/send-email/send-email.tsx +++ b/src/widgets/forms/ForgotPasswordForm/send-email/send-email.tsx @@ -5,6 +5,7 @@ import "./send-email.scss"; import { useState } from "react"; import { apiInstance } from "@/shared/config/apiConfig"; import Loader from "@/shared/ui/components/Loader/Loader"; +import { AxiosError } from "axios"; interface ISendEmailProps { setChangeForm: (boolean: boolean) => void; @@ -34,11 +35,21 @@ const SendEmail: React.FC = ({ formData ); - if (res.status === 200 || res.status === 201) { + if ([200, 201].includes(res.status)) { + console.log(res.data); setChangeForm(true); } - } catch (error) { - setError("An error ocured"); + } catch (error: unknown) { + if (error instanceof AxiosError) { + console.log(error); + if (error.response?.status === 400) { + setError("Пользователь с таким email не найден"); + } else { + setError("Ошибка на стороне сервера"); + } + } else { + setError("Произошла непредвиденная ошибка"); + } } finally { setLoader(false); } diff --git a/src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx b/src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx index cc82e7d..e4ac7a2 100644 --- a/src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx +++ b/src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx @@ -5,6 +5,7 @@ import { useState } from "react"; import { apiInstance } from "@/shared/config/apiConfig"; import { useSession } from "next-auth/react"; import Loader from "@/shared/ui/components/Loader/Loader"; +import { AxiosError } from "axios"; interface IChangePasswordProps { closeWindow: (bool: boolean) => void; @@ -90,8 +91,16 @@ const ChangePassword: React.FC = ({ ); if ([200, 201].includes(res.status)) return setSuccess(true); - } catch (error: any) { - setError(error.message); + } catch (error: unknown) { + if (error instanceof AxiosError) { + if (error.response?.status === 400) { + setError( + "Некорректный старый пароль или недопустимый новый пароль" + ); + } + } else { + setError("Произошла непредвиденная ошибка"); + } } finally { setLoader(false); } diff --git a/src/widgets/forms/ProfileForm/ProfileForm.tsx b/src/widgets/forms/ProfileForm/ProfileForm.tsx index e3ac883..2f6f8d9 100644 --- a/src/widgets/forms/ProfileForm/ProfileForm.tsx +++ b/src/widgets/forms/ProfileForm/ProfileForm.tsx @@ -74,7 +74,11 @@ const ProfileForm: React.FC = ({ } } catch (error: unknown) { if (error instanceof AxiosError) { - setError(error.message); + if (error.response?.status === 400) { + setError("Были введены неккоректные данные"); + } else { + setError("Ошибка на стороне сервера"); + } } else { setError("Возникла непредвиденная ошибка"); } diff --git a/src/widgets/forms/ReportForm/CreateReportMap/CreateReportMap.scss b/src/widgets/forms/ReportForm/CreateReportMap/CreateReportMap.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/widgets/forms/ReportForm/CreateReportMap/CreateReportMap.tsx b/src/widgets/forms/ReportForm/CreateReportMap/CreateReportMap.tsx deleted file mode 100644 index 9112c92..0000000 --- a/src/widgets/forms/ReportForm/CreateReportMap/CreateReportMap.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import "./CreateReportMap.scss"; - -const CreateReportMap = () => { - return
CreateReportMap
; -}; - -export default CreateReportMap; diff --git a/src/widgets/forms/ReportForm/ReportForm.scss b/src/widgets/forms/ReportForm/ReportForm.scss index 1a1d24f..6e9c6ba 100644 --- a/src/widgets/forms/ReportForm/ReportForm.scss +++ b/src/widgets/forms/ReportForm/ReportForm.scss @@ -171,9 +171,10 @@ button[type="submit"] { margin-top: 40px; padding: 15px; - width: fit-content; + max-width: 320px; display: flex; align-items: center; + justify-content: center; gap: 10px; border: 1px solid rgb(72, 159, 225), rgb(72, 159, 225); border-radius: 5px; @@ -182,6 +183,10 @@ font-size: 22px; font-weight: 400; line-height: 26px; + + div { + flex: 1; + } } } diff --git a/src/widgets/forms/ReportForm/ReportForm.tsx b/src/widgets/forms/ReportForm/ReportForm.tsx index 59bda6f..8af60f8 100644 --- a/src/widgets/forms/ReportForm/ReportForm.tsx +++ b/src/widgets/forms/ReportForm/ReportForm.tsx @@ -6,6 +6,7 @@ import Image from "next/image"; import { MapContainer, Marker, + Popup, TileLayer, useMapEvents, } from "react-leaflet"; @@ -66,7 +67,7 @@ const ReportForm = () => { e.preventDefault(); const formData = new FormData(e.currentTarget); - if (displayLatLng.length === 0) { + if (displayLatLng.length === 0 || latLng.length === 0) { setDescriptionWarning(""); setImageWarning(""); setError(""); @@ -109,8 +110,10 @@ const ReportForm = () => { }); formData.append("latitude1", latLng[0].lat.toString()); formData.append("longitude1", latLng[0].lng.toString()); + // if (latLng.length === 2) { formData.append("latitude2", latLng[1].lat.toString()); formData.append("longitude2", latLng[1].lng.toString()); + // } try { setLoader(true); @@ -125,6 +128,7 @@ const ReportForm = () => { } } catch (error: unknown) { if (error instanceof AxiosError) { + console.log(error); setError(error.message); } else { setError("Произошла непредвиденная ошибка"); @@ -144,12 +148,20 @@ const ReportForm = () => { .get( `https://nominatim.openstreetmap.org/reverse?lat=${e.latlng.lat}&lon=${e.latlng.lng}&format=json` ) - .then((res) => + .then((res) => { + console.log(res.data); + if (res.data.address.country_code !== "kg") { + setLocationWarning("Выберите точку в Кыргызстане"); + return; + } else if (res.data.address.road === undefined) { + setLocationWarning("Выберите точку на дороге"); + return; + } setDisplayLatLng([ ...displayLatLng, res.data.display_name, - ]) - ) + ]); + }) .catch((error) => console.log(error)); } }, @@ -272,8 +284,14 @@ const ReportForm = () => { ) : null} {error ?

{error}

: null} diff --git a/src/widgets/forms/ResetCodeForm/ResetCodeForm.tsx b/src/widgets/forms/ResetCodeForm/ResetCodeForm.tsx index 7cfb14f..a09baac 100644 --- a/src/widgets/forms/ResetCodeForm/ResetCodeForm.tsx +++ b/src/widgets/forms/ResetCodeForm/ResetCodeForm.tsx @@ -7,6 +7,7 @@ import { apiInstance } from "@/shared/config/apiConfig"; import { ITokens } from "@/shared/types/token-type"; import Loader from "@/shared/ui/components/Loader/Loader"; import { useRouter } from "@/shared/config/navigation"; +import { AxiosError } from "axios"; const ResetCodeForm = () => { const [passwordWarning, setPasswordWarning] = useState(""); @@ -62,12 +63,28 @@ const ResetCodeForm = () => { config ); - if (response.status === 200 || response.status === 201) { + if ([200, 201].includes(response.status)) { localStorage.removeItem("transitional"); router.push("/sign-in"); } } catch (error) { - setError("An error ocured"); + if (error instanceof AxiosError) { + if ( + [500, 501, 502, 503, 504].includes( + error.response?.status as number + ) + ) { + setError("Ошибка на стороне сервера"); + } else if ( + [400, 404].includes(error.response?.status as number) + ) { + setError( + "Слабый пароль, прошу избегайте очевидных паролей" + ); + } + } else { + setError("Произошла непредвиденная ошибка"); + } } finally { setLoader(false); } diff --git a/src/widgets/forms/SignInForm/SignInForm.tsx b/src/widgets/forms/SignInForm/SignInForm.tsx index 71a84a2..bd08d1a 100644 --- a/src/widgets/forms/SignInForm/SignInForm.tsx +++ b/src/widgets/forms/SignInForm/SignInForm.tsx @@ -5,10 +5,11 @@ import AuthInput from "@/features/AuthInput/AuthInput"; import GoogleButton from "@/features/GoogleButton/GoogleButton"; import { Link, useRouter } from "@/shared/config/navigation"; import Loader from "@/shared/ui/components/Loader/Loader"; -import { signIn } from "next-auth/react"; +import { signIn, useSession } from "next-auth/react"; import { useState } from "react"; const SignInForm = () => { + const session = useSession(); const [emailWarning, setEmailWarning] = useState(""); const [passwordWarning, setPasswordWarning] = useState(""); const [error, setError] = useState(""); @@ -49,8 +50,8 @@ const SignInForm = () => { setLoader(false); if (res?.ok && !res.error) { - router.push("/profile/personal"); - } else if (res?.status.toString().slice(0, 1) === "4") { + // router.push("/profile/personal"); + } else if ([400, 401, 404].includes(res?.status as number)) { setError("Неверный Email или Пароль"); } else { setError("Произошла непредвиденная ошибка"); diff --git a/src/widgets/tables/ProfileTable/ProfileTable.tsx b/src/widgets/tables/ProfileTable/ProfileTable.tsx index 66331a6..dc41e7f 100644 --- a/src/widgets/tables/ProfileTable/ProfileTable.tsx +++ b/src/widgets/tables/ProfileTable/ProfileTable.tsx @@ -29,7 +29,8 @@ const ProfileTable: React.FC = ({ } = useProfileReportsStore(); const session = useSession(); const router = useRouter(); - const [sort, setSort] = useState("date"); + const [sort, setSort] = useState(""); + const [status, setStatus] = useState(2); const [activePage, setActivePage] = useState( +searchParams["страница-обращений"] || 1 ); @@ -37,11 +38,19 @@ const ProfileTable: React.FC = ({ { param: "Дата", handleClick() { - setSort("date"); + setSort(""); }, }, { param: "Адрес" }, - { param: "Статус" }, + { + param: "Статус", + handleClick() { + setStatus((prev) => + prev === 1 ? 2 : prev === 2 ? 3 : prev === 3 ? 1 : 2 + ); + setSort("status"); + }, + }, { param: "Комментарии", handleClick() { @@ -51,21 +60,13 @@ const ProfileTable: React.FC = ({ { param: "Рейтинг", handleClick() { - setSort("rating"); + setSort("likes"); }, }, ]; useEffect(() => { if (session.status === "loading") return; - getMyReports( - sort, - activePage, - session.data?.access_token as string - ); - }, [session]); - - useEffect(() => { router.push( `/profile/my-reports?страница-обращений=${activePage}`, { scroll: false } @@ -73,9 +74,10 @@ const ProfileTable: React.FC = ({ getMyReports( sort, activePage, - session.data?.access_token as string + session.data?.access_token as string, + status ); - }, [activePage, sort]); + }, [activePage, sort, status, session]); return (
diff --git a/src/widgets/tables/ProfileTable/profile-reports.store.ts b/src/widgets/tables/ProfileTable/profile-reports.store.ts index ba228c6..5622d3d 100644 --- a/src/widgets/tables/ProfileTable/profile-reports.store.ts +++ b/src/widgets/tables/ProfileTable/profile-reports.store.ts @@ -7,17 +7,13 @@ import { import { AxiosError } from "axios"; import { create } from "zustand"; -const filterCategories: Record = { - count_reviews: "count_reviews", - total_likes: "total_likes", -}; - interface IProfileReportsStore extends IFetch { data: IMyReportsList; getMyReports: ( filter: string, page: number, - access_token: string + access_token: string, + status: number ) => void; } @@ -34,7 +30,8 @@ export const useProfileReportsStore = create( getMyReports: async ( sort: string, page: number, - access_token: string + access_token: string, + status: number ) => { try { const Authorization = `Bearer ${access_token}`; @@ -44,27 +41,21 @@ export const useProfileReportsStore = create( }, }; set({ isLoading: true }); - const data = await apiInstance - .get( - `/users/reports/?page=${page}&page_size=8`, - config - ) - .then((res) => res.data); - - if (sort === "date") { - data.results = data.results.sort((a, b) => { - const dateA = new Date(b.created_at) as unknown as number; - const dateB = new Date(a.created_at) as unknown as number; - return dateA - dateB; - }); - } else if (sort === "reviews") { - data.results = data.results.sort( - (a, b) => b.count_reviews - a.count_reviews - ); - } else if (sort === "rating") { - data.results = data.results.sort( - (a, b) => b.total_likes - a.total_likes - ); + let data; + if (sort === "status") { + data = await apiInstance + .get( + `/users/reports/?page=${page}&page_size=8&status=${status}`, + config + ) + .then((res) => res.data); + } else { + data = await apiInstance + .get( + `/users/reports/?page=${page}&page_size=8&sort_by=${sort}`, + config + ) + .then((res) => res.data); } set({ data: data }); diff --git a/src/widgets/tables/StatisticsTable/statistics.store.ts b/src/widgets/tables/StatisticsTable/statistics.store.ts index a181e39..8477eac 100644 --- a/src/widgets/tables/StatisticsTable/statistics.store.ts +++ b/src/widgets/tables/StatisticsTable/statistics.store.ts @@ -5,15 +5,6 @@ import { IStatistics } from "@/shared/types/statistics-type"; import { AxiosError } from "axios"; import { create } from "zustand"; -const filterCategories: Record = { - broken_road_1: "broken_road_1", - hotbed_of_accidents_2: "hotbed_of_accidents_2", - local_defect_3: "local_defect_3", - repair_plans_4: "repair_plans_4", - repaired_5: "repaired_5", - local_defect_fixed_6: "local_defect_fixed_6", -}; - interface IStatisticsStore extends IFetch { data: IStatistics[]; getStatistics: ( @@ -35,16 +26,14 @@ export const useStatisticsStore = create((set) => ({ try { set({ isLoading: true }); const response = await apiInstance.get( - `/report/${endpoint}/stats` + `/report/${endpoint}/stats?sort_by=${sort}` ); let data = response.data.filter((loc) => loc.name.toLowerCase().includes(query.toLowerCase()) ); - const sorted = data.sort((a: any, b: any) => a[sort] - b[sort]); - - set({ data: sorted }); + set({ data: data }); } catch (error: unknown) { if (error instanceof AxiosError) { set({ error: error.message });