forked from Transparency/kgroad-frontend2
fixed sort in profile-reports table and in statistics table, improved error messages, improved metatags by fetching metadata from server
This commit is contained in:
parent
0221a79015
commit
0ba0f1df89
6
lib/next-auth.d.ts
vendored
6
lib/next-auth.d.ts
vendored
@ -4,13 +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 {
|
interface User {
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
access_token: string;
|
access_token: string;
|
||||||
expires_in?: string;
|
expires_in: Date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,26 +4,52 @@ import Image from "next/image";
|
|||||||
import header from "./assets/header.svg";
|
import header from "./assets/header.svg";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
||||||
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
|
import { IMetatag } from "@/shared/types/metatag-type";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
title: "KG ROAD | О нас",
|
const data = await apiInstance
|
||||||
description:
|
.get<IMetatag[]>("/metatags/")
|
||||||
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
|
.then((res) => res.data)
|
||||||
keywords: [
|
.catch((e) => console.log(e));
|
||||||
"Миссия Transparency International - Кыргызстан",
|
|
||||||
"Цели и приоритеты ТИ-Кыргызстан",
|
if (!data)
|
||||||
],
|
return {
|
||||||
openGraph: {
|
title: "KG ROAD | О нас",
|
||||||
images: [
|
description:
|
||||||
{
|
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
|
||||||
url: header.src,
|
openGraph: {
|
||||||
|
title: "KG ROAD | О нас",
|
||||||
|
description:
|
||||||
|
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
|
||||||
},
|
},
|
||||||
],
|
};
|
||||||
title: "KG ROAD | О нас",
|
|
||||||
description:
|
const metadata = data.filter((tag) => tag.page === "about-us")[0];
|
||||||
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
|
if (!metadata) {
|
||||||
},
|
return {
|
||||||
};
|
title: "KG ROAD | О нас",
|
||||||
|
description:
|
||||||
|
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
|
||||||
|
openGraph: {
|
||||||
|
title: "KG ROAD | О нас",
|
||||||
|
description:
|
||||||
|
"Transparency International - Кыргызстан - филиал международной организации Transparency International в Кыргызской Республике.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: `KG ROAD | ${metadata.title}`,
|
||||||
|
description: metadata.description,
|
||||||
|
keywords: metadata.keywords.split(","),
|
||||||
|
openGraph: {
|
||||||
|
title: `KG ROAD | ${metadata.title}`,
|
||||||
|
description: metadata.description,
|
||||||
|
type: "website",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const AboutUs = () => {
|
const AboutUs = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -26,7 +26,6 @@ export async function generateMetadata({
|
|||||||
type: "article",
|
type: "article",
|
||||||
publishedTime: response.data.created_at,
|
publishedTime: response.data.created_at,
|
||||||
},
|
},
|
||||||
keywords: ["Новости КР", "Кыргызстан"],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,40 @@ import "./News.scss";
|
|||||||
import Typography from "@/shared/ui/components/Typography/Typography";
|
import Typography from "@/shared/ui/components/Typography/Typography";
|
||||||
import NewsList from "@/widgets/NewsList/NewsList";
|
import NewsList from "@/widgets/NewsList/NewsList";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
|
import { IMetatag } from "@/shared/types/metatag-type";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
title: "KG ROAD | Новости",
|
const data = await apiInstance
|
||||||
description: "Страница новостей KG ROAD",
|
.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 = ({
|
const News = ({
|
||||||
searchParams,
|
searchParams,
|
||||||
|
@ -4,29 +4,55 @@ import RatingSection from "@/widgets/home/RatingSection/RatingSection";
|
|||||||
import NewsSection from "@/widgets/home/NewsSection/NewsSection";
|
import NewsSection from "@/widgets/home/NewsSection/NewsSection";
|
||||||
import MapSection from "@/widgets/home/MapSection/MapSection";
|
import MapSection from "@/widgets/home/MapSection/MapSection";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
|
import { IMetatag } from "@/shared/types/metatag-type";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
title: "KG ROAD | Главная",
|
const data = await apiInstance
|
||||||
description:
|
.get<IMetatag[]>("/metatags/")
|
||||||
"Главная страница KG ROAD | Сделаем дороги безопасными!",
|
.then((res) => res.data)
|
||||||
keywords: [
|
.catch((e) => console.log(e));
|
||||||
"Новости",
|
|
||||||
"Разбитые дороги",
|
if (!data)
|
||||||
"Очаги аварийности",
|
return {
|
||||||
"Локальные дефекты",
|
title: "KG ROAD | Главная",
|
||||||
"Дороги В планах ремонта",
|
description:
|
||||||
"Отремонтированные дороги",
|
"Главная страница KG ROAD | Сделаем дороги безопасными!",
|
||||||
"Исправленные локальные дефекты",
|
openGraph: {
|
||||||
"Карта дорог",
|
title: "KG ROAD | Главная",
|
||||||
"Рейтинг",
|
description:
|
||||||
],
|
"Главная страница KG ROAD | Сделаем дороги безопасными!",
|
||||||
openGraph: {
|
type: "website",
|
||||||
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 ({
|
const Home = async ({
|
||||||
searchParams,
|
searchParams,
|
||||||
|
@ -11,6 +11,7 @@ import React from "react";
|
|||||||
|
|
||||||
const Personal = async () => {
|
const Personal = async () => {
|
||||||
const session = await getServerSession(authConfig);
|
const session = await getServerSession(authConfig);
|
||||||
|
console.log(session?.expires_in);
|
||||||
const getProfile = async () => {
|
const getProfile = async () => {
|
||||||
const Authorization = `Bearer ${session?.access_token}`;
|
const Authorization = `Bearer ${session?.access_token}`;
|
||||||
const config = {
|
const config = {
|
||||||
|
@ -32,7 +32,7 @@ export async function generateMetadata({
|
|||||||
title: `KG ROAD | ${response.data.location[0].address}`,
|
title: `KG ROAD | ${response.data.location[0].address}`,
|
||||||
description: response.data.description,
|
description: response.data.description,
|
||||||
images: [response.data.image[0].image],
|
images: [response.data.image[0].image],
|
||||||
type: "website",
|
type: "article",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,42 @@ import "./Statistics.scss";
|
|||||||
import StatisticsTable from "@/widgets/tables/StatisticsTable/StatisticsTable";
|
import StatisticsTable from "@/widgets/tables/StatisticsTable/StatisticsTable";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
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: `Статистика по населенным пунктам Кыргызстана`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateMetadata({
|
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
searchParams: { "поиск-населенного-пункта": string };
|
|
||||||
}): Promise<Metadata> {
|
|
||||||
return {
|
return {
|
||||||
title: "KG ROAD | Статистика",
|
title: `KG ROAD | ${metadata.title}`,
|
||||||
description: `Статистика по ${searchParams["поиск-населенного-пункта"]} KG ROAD`,
|
description: metadata.description,
|
||||||
keywords: ["Бишкек", "Чуй", "Кыргызстан", "Дороги"],
|
keywords: metadata.keywords.split(","),
|
||||||
|
openGraph: {
|
||||||
|
title: `KG ROAD | ${metadata.title}`,
|
||||||
|
description: metadata.description,
|
||||||
|
type: "website",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Statistics = ({
|
const Statistics = ({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
|
@ -3,11 +3,42 @@ import "./Volunteers.scss";
|
|||||||
import VolunteersTable from "@/widgets/tables/VolunteersTable/VolunteersTable";
|
import VolunteersTable from "@/widgets/tables/VolunteersTable/VolunteersTable";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
import BreadCrumbs from "@/features/BreadCrumbs/BreadCrumbs";
|
||||||
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
|
import { IMetatag } from "@/shared/types/metatag-type";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
title: "KG ROAD | Волонтеры",
|
const data = await apiInstance
|
||||||
description: "Страница лучших волонтеров Кыргызской Республики!",
|
.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 = () => {
|
const Volunteers = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -19,16 +19,7 @@ const verifyToken = async (access_token: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const refreshToken = async (token: JWT): Promise<JWT> => {
|
const refreshToken = async (token: JWT): Promise<JWT> => {
|
||||||
// const date = new Date().toLocaleTimeString();
|
const UTC = new Date();
|
||||||
// const expire = new Date(token.expires_in).toLocaleTimeString();
|
|
||||||
|
|
||||||
const verify = await verifyToken(token.access_token);
|
|
||||||
|
|
||||||
if (verify) {
|
|
||||||
return {
|
|
||||||
...token,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
refresh: token.refresh_token,
|
refresh: token.refresh_token,
|
||||||
@ -39,10 +30,16 @@ const refreshToken = async (token: JWT): Promise<JWT> => {
|
|||||||
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: response.data.expires_at,
|
expires_in: expirationTime,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,20 +55,37 @@ export const authConfig: AuthOptions = {
|
|||||||
},
|
},
|
||||||
password: { label: "Password", type: "password" },
|
password: { label: "Password", type: "password" },
|
||||||
},
|
},
|
||||||
async authorize(credentials, req) {
|
async authorize(credentials, req): Promise<any> {
|
||||||
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 = {
|
const data = {
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await apiInstance.post("/users/login/", data);
|
const res = await apiInstance.post<ITokens>(
|
||||||
|
"/users/login/",
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
if ([200, 201].includes(res.status)) {
|
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;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,9 +119,15 @@ export const authConfig: AuthOptions = {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentTime = new Date();
|
||||||
|
|
||||||
|
const expirationTime = new Date(
|
||||||
|
currentTime.getTime() + 15 * 60000
|
||||||
|
);
|
||||||
|
|
||||||
user.access_token = res.data.access_token;
|
user.access_token = res.data.access_token;
|
||||||
user.refresh_token = res.data.refresh_token;
|
user.refresh_token = res.data.refresh_token;
|
||||||
user.expires_in = res.data.expires_in;
|
user.expires_in = expirationTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -115,12 +135,26 @@ export const authConfig: AuthOptions = {
|
|||||||
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;
|
||||||
},
|
},
|
||||||
|
8
src/shared/types/metatag-type.ts
Normal file
8
src/shared/types/metatag-type.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface IMetatag {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
keywords: string;
|
||||||
|
og_image?: string;
|
||||||
|
page: string;
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||||
import { useRouter } from "@/shared/config/navigation";
|
import { useRouter } from "@/shared/config/navigation";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
interface IConfirmEmailFormProps {
|
interface IConfirmEmailFormProps {
|
||||||
email: string;
|
email: string;
|
||||||
@ -54,24 +55,38 @@ const ConfirmEmailForm: React.FC<IConfirmEmailFormProps> = ({
|
|||||||
if (response.status === 200 || response.status === 201) {
|
if (response.status === 200 || response.status === 201) {
|
||||||
router.push("/sign-in");
|
router.push("/sign-in");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
setError("Возникла ошибка");
|
if (error instanceof AxiosError) {
|
||||||
|
if ([400, 404].includes(error.response?.status as number)) {
|
||||||
|
setError("Неверный код подтверждения");
|
||||||
|
} else {
|
||||||
|
setError("Ошибка на стороне сервера");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("Произошла непредвиденная ошибка");
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoader(false);
|
setLoader(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = async () => {
|
const handleClick = async () => {
|
||||||
const data = {
|
try {
|
||||||
email,
|
const data = {
|
||||||
};
|
email,
|
||||||
const response = await apiInstance.post(
|
};
|
||||||
"/users/resend_code/",
|
const response = await apiInstance.post(
|
||||||
data
|
"/users/resend_code/",
|
||||||
);
|
data
|
||||||
|
);
|
||||||
|
|
||||||
if (response.status === 200 || response.status === 201) {
|
if ([200, 201].includes(response.status)) {
|
||||||
setMinutes(1);
|
setMinutes(1);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setError(
|
||||||
|
"Проблема на стороне сервера или вы достигли максимальное количество попыток"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import { apiInstance } from "@/shared/config/apiConfig";
|
|||||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||||
import { ITokens } from "@/shared/types/token-type";
|
import { ITokens } from "@/shared/types/token-type";
|
||||||
import { useRouter } from "@/shared/config/navigation";
|
import { useRouter } from "@/shared/config/navigation";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
interface IConfirmCodeProps {
|
interface IConfirmCodeProps {
|
||||||
setChangeForm: (boolean: boolean) => void;
|
setChangeForm: (boolean: boolean) => void;
|
||||||
@ -45,8 +46,16 @@ const ConfirmCode: React.FC<IConfirmCodeProps> = ({
|
|||||||
router.push("/sign-in/reset-code");
|
router.push("/sign-in/reset-code");
|
||||||
}
|
}
|
||||||
setLoader(false);
|
setLoader(false);
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
setError("An error ocured");
|
if (error instanceof AxiosError) {
|
||||||
|
if (error.response?.status === 400) {
|
||||||
|
setError("Неверный код");
|
||||||
|
} else {
|
||||||
|
setError("Ошибка на стороне сервера");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("Произошла непредвиденная ошибка");
|
||||||
|
}
|
||||||
setLoader(false);
|
setLoader(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ import "./send-email.scss";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
interface ISendEmailProps {
|
interface ISendEmailProps {
|
||||||
setChangeForm: (boolean: boolean) => void;
|
setChangeForm: (boolean: boolean) => void;
|
||||||
@ -34,11 +35,21 @@ const SendEmail: React.FC<ISendEmailProps> = ({
|
|||||||
formData
|
formData
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.status === 200 || res.status === 201) {
|
if ([200, 201].includes(res.status)) {
|
||||||
|
console.log(res.data);
|
||||||
setChangeForm(true);
|
setChangeForm(true);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
setError("An error ocured");
|
if (error instanceof AxiosError) {
|
||||||
|
console.log(error);
|
||||||
|
if (error.response?.status === 400) {
|
||||||
|
setError("Пользователь с таким email не найден");
|
||||||
|
} else {
|
||||||
|
setError("Ошибка на стороне сервера");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("Произошла непредвиденная ошибка");
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoader(false);
|
setLoader(false);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { useState } from "react";
|
|||||||
import { apiInstance } from "@/shared/config/apiConfig";
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
interface IChangePasswordProps {
|
interface IChangePasswordProps {
|
||||||
closeWindow: (bool: boolean) => void;
|
closeWindow: (bool: boolean) => void;
|
||||||
@ -90,8 +91,16 @@ const ChangePassword: React.FC<IChangePasswordProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ([200, 201].includes(res.status)) return setSuccess(true);
|
if ([200, 201].includes(res.status)) return setSuccess(true);
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
setError(error.message);
|
if (error instanceof AxiosError) {
|
||||||
|
if (error.response?.status === 400) {
|
||||||
|
setError(
|
||||||
|
"Некорректный старый пароль или недопустимый новый пароль"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("Произошла непредвиденная ошибка");
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoader(false);
|
setLoader(false);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,11 @@ const ProfileForm: React.FC<IProfileFormProps> = ({
|
|||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof AxiosError) {
|
if (error instanceof AxiosError) {
|
||||||
setError(error.message);
|
if (error.response?.status === 400) {
|
||||||
|
setError("Были введены неккоректные данные");
|
||||||
|
} else {
|
||||||
|
setError("Ошибка на стороне сервера");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setError("Возникла непредвиденная ошибка");
|
setError("Возникла непредвиденная ошибка");
|
||||||
}
|
}
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
import "./CreateReportMap.scss";
|
|
||||||
|
|
||||||
const CreateReportMap = () => {
|
|
||||||
return <div>CreateReportMap</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CreateReportMap;
|
|
@ -171,9 +171,10 @@
|
|||||||
button[type="submit"] {
|
button[type="submit"] {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
width: fit-content;
|
max-width: 320px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
border: 1px solid rgb(72, 159, 225), rgb(72, 159, 225);
|
border: 1px solid rgb(72, 159, 225), rgb(72, 159, 225);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -182,6 +183,10 @@
|
|||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
|
|
||||||
|
div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import Image from "next/image";
|
|||||||
import {
|
import {
|
||||||
MapContainer,
|
MapContainer,
|
||||||
Marker,
|
Marker,
|
||||||
|
Popup,
|
||||||
TileLayer,
|
TileLayer,
|
||||||
useMapEvents,
|
useMapEvents,
|
||||||
} from "react-leaflet";
|
} from "react-leaflet";
|
||||||
@ -66,7 +67,7 @@ const ReportForm = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData(e.currentTarget);
|
const formData = new FormData(e.currentTarget);
|
||||||
|
|
||||||
if (displayLatLng.length === 0) {
|
if (displayLatLng.length === 0 || latLng.length === 0) {
|
||||||
setDescriptionWarning("");
|
setDescriptionWarning("");
|
||||||
setImageWarning("");
|
setImageWarning("");
|
||||||
setError("");
|
setError("");
|
||||||
@ -109,8 +110,10 @@ const ReportForm = () => {
|
|||||||
});
|
});
|
||||||
formData.append("latitude1", latLng[0].lat.toString());
|
formData.append("latitude1", latLng[0].lat.toString());
|
||||||
formData.append("longitude1", latLng[0].lng.toString());
|
formData.append("longitude1", latLng[0].lng.toString());
|
||||||
|
// if (latLng.length === 2) {
|
||||||
formData.append("latitude2", latLng[1].lat.toString());
|
formData.append("latitude2", latLng[1].lat.toString());
|
||||||
formData.append("longitude2", latLng[1].lng.toString());
|
formData.append("longitude2", latLng[1].lng.toString());
|
||||||
|
// }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoader(true);
|
setLoader(true);
|
||||||
@ -125,6 +128,7 @@ const ReportForm = () => {
|
|||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof AxiosError) {
|
if (error instanceof AxiosError) {
|
||||||
|
console.log(error);
|
||||||
setError(error.message);
|
setError(error.message);
|
||||||
} else {
|
} else {
|
||||||
setError("Произошла непредвиденная ошибка");
|
setError("Произошла непредвиденная ошибка");
|
||||||
@ -144,12 +148,20 @@ const ReportForm = () => {
|
|||||||
.get(
|
.get(
|
||||||
`https://nominatim.openstreetmap.org/reverse?lat=${e.latlng.lat}&lon=${e.latlng.lng}&format=json`
|
`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([
|
setDisplayLatLng([
|
||||||
...displayLatLng,
|
...displayLatLng,
|
||||||
res.data.display_name,
|
res.data.display_name,
|
||||||
])
|
]);
|
||||||
)
|
})
|
||||||
.catch((error) => console.log(error));
|
.catch((error) => console.log(error));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -272,8 +284,14 @@ const ReportForm = () => {
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<button disabled={loader} type="submit">
|
<button disabled={loader} type="submit">
|
||||||
{loader ? <Loader /> : "Отправить на модерацию"}
|
{loader ? (
|
||||||
<Image src={arrow_right} alt="Arrow Right Icon" />
|
<Loader />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Отправить на модерацию
|
||||||
|
<Image src={arrow_right} alt="Arrow Right Icon" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
{error ? <p className="report-form__error">{error}</p> : null}
|
{error ? <p className="report-form__error">{error}</p> : null}
|
||||||
</form>
|
</form>
|
||||||
|
@ -7,6 +7,7 @@ import { apiInstance } from "@/shared/config/apiConfig";
|
|||||||
import { ITokens } from "@/shared/types/token-type";
|
import { ITokens } from "@/shared/types/token-type";
|
||||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
import Loader from "@/shared/ui/components/Loader/Loader";
|
||||||
import { useRouter } from "@/shared/config/navigation";
|
import { useRouter } from "@/shared/config/navigation";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
const ResetCodeForm = () => {
|
const ResetCodeForm = () => {
|
||||||
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
||||||
@ -62,12 +63,28 @@ const ResetCodeForm = () => {
|
|||||||
config
|
config
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status === 200 || response.status === 201) {
|
if ([200, 201].includes(response.status)) {
|
||||||
localStorage.removeItem("transitional");
|
localStorage.removeItem("transitional");
|
||||||
router.push("/sign-in");
|
router.push("/sign-in");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} 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 {
|
} finally {
|
||||||
setLoader(false);
|
setLoader(false);
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@ import AuthInput from "@/features/AuthInput/AuthInput";
|
|||||||
import GoogleButton from "@/features/GoogleButton/GoogleButton";
|
import GoogleButton from "@/features/GoogleButton/GoogleButton";
|
||||||
import { Link, useRouter } from "@/shared/config/navigation";
|
import { Link, useRouter } from "@/shared/config/navigation";
|
||||||
import Loader from "@/shared/ui/components/Loader/Loader";
|
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";
|
import { useState } from "react";
|
||||||
|
|
||||||
const SignInForm = () => {
|
const SignInForm = () => {
|
||||||
|
const session = useSession();
|
||||||
const [emailWarning, setEmailWarning] = useState<string>("");
|
const [emailWarning, setEmailWarning] = useState<string>("");
|
||||||
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
@ -49,8 +50,8 @@ const SignInForm = () => {
|
|||||||
setLoader(false);
|
setLoader(false);
|
||||||
|
|
||||||
if (res?.ok && !res.error) {
|
if (res?.ok && !res.error) {
|
||||||
router.push("/profile/personal");
|
// router.push("/profile/personal");
|
||||||
} else if (res?.status.toString().slice(0, 1) === "4") {
|
} else if ([400, 401, 404].includes(res?.status as number)) {
|
||||||
setError("Неверный Email или Пароль");
|
setError("Неверный Email или Пароль");
|
||||||
} else {
|
} else {
|
||||||
setError("Произошла непредвиденная ошибка");
|
setError("Произошла непредвиденная ошибка");
|
||||||
|
@ -29,7 +29,8 @@ const ProfileTable: React.FC<IProfileTableProps> = ({
|
|||||||
} = useProfileReportsStore();
|
} = useProfileReportsStore();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [sort, setSort] = useState("date");
|
const [sort, setSort] = useState("");
|
||||||
|
const [status, setStatus] = useState<number>(2);
|
||||||
const [activePage, setActivePage] = useState<number>(
|
const [activePage, setActivePage] = useState<number>(
|
||||||
+searchParams["страница-обращений"] || 1
|
+searchParams["страница-обращений"] || 1
|
||||||
);
|
);
|
||||||
@ -37,11 +38,19 @@ const ProfileTable: React.FC<IProfileTableProps> = ({
|
|||||||
{
|
{
|
||||||
param: "Дата",
|
param: "Дата",
|
||||||
handleClick() {
|
handleClick() {
|
||||||
setSort("date");
|
setSort("");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ param: "Адрес" },
|
{ param: "Адрес" },
|
||||||
{ param: "Статус" },
|
{
|
||||||
|
param: "Статус",
|
||||||
|
handleClick() {
|
||||||
|
setStatus((prev) =>
|
||||||
|
prev === 1 ? 2 : prev === 2 ? 3 : prev === 3 ? 1 : 2
|
||||||
|
);
|
||||||
|
setSort("status");
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
param: "Комментарии",
|
param: "Комментарии",
|
||||||
handleClick() {
|
handleClick() {
|
||||||
@ -51,21 +60,13 @@ const ProfileTable: React.FC<IProfileTableProps> = ({
|
|||||||
{
|
{
|
||||||
param: "Рейтинг",
|
param: "Рейтинг",
|
||||||
handleClick() {
|
handleClick() {
|
||||||
setSort("rating");
|
setSort("likes");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session.status === "loading") return;
|
if (session.status === "loading") return;
|
||||||
getMyReports(
|
|
||||||
sort,
|
|
||||||
activePage,
|
|
||||||
session.data?.access_token as string
|
|
||||||
);
|
|
||||||
}, [session]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
router.push(
|
router.push(
|
||||||
`/profile/my-reports?страница-обращений=${activePage}`,
|
`/profile/my-reports?страница-обращений=${activePage}`,
|
||||||
{ scroll: false }
|
{ scroll: false }
|
||||||
@ -73,9 +74,10 @@ const ProfileTable: React.FC<IProfileTableProps> = ({
|
|||||||
getMyReports(
|
getMyReports(
|
||||||
sort,
|
sort,
|
||||||
activePage,
|
activePage,
|
||||||
session.data?.access_token as string
|
session.data?.access_token as string,
|
||||||
|
status
|
||||||
);
|
);
|
||||||
}, [activePage, sort]);
|
}, [activePage, sort, status, session]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="profile-table">
|
<div className="profile-table">
|
||||||
|
@ -7,17 +7,13 @@ import {
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
const filterCategories: Record<string, string> = {
|
|
||||||
count_reviews: "count_reviews",
|
|
||||||
total_likes: "total_likes",
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IProfileReportsStore extends IFetch {
|
interface IProfileReportsStore extends IFetch {
|
||||||
data: IMyReportsList;
|
data: IMyReportsList;
|
||||||
getMyReports: (
|
getMyReports: (
|
||||||
filter: string,
|
filter: string,
|
||||||
page: number,
|
page: number,
|
||||||
access_token: string
|
access_token: string,
|
||||||
|
status: number
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +30,8 @@ export const useProfileReportsStore = create<IProfileReportsStore>(
|
|||||||
getMyReports: async (
|
getMyReports: async (
|
||||||
sort: string,
|
sort: string,
|
||||||
page: number,
|
page: number,
|
||||||
access_token: string
|
access_token: string,
|
||||||
|
status: number
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const Authorization = `Bearer ${access_token}`;
|
const Authorization = `Bearer ${access_token}`;
|
||||||
@ -44,27 +41,21 @@ export const useProfileReportsStore = create<IProfileReportsStore>(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
set({ isLoading: true });
|
set({ isLoading: true });
|
||||||
const data = await apiInstance
|
let data;
|
||||||
.get<IMyReportsList>(
|
if (sort === "status") {
|
||||||
`/users/reports/?page=${page}&page_size=8`,
|
data = await apiInstance
|
||||||
config
|
.get<IMyReportsList>(
|
||||||
)
|
`/users/reports/?page=${page}&page_size=8&status=${status}`,
|
||||||
.then((res) => res.data);
|
config
|
||||||
|
)
|
||||||
if (sort === "date") {
|
.then((res) => res.data);
|
||||||
data.results = data.results.sort((a, b) => {
|
} else {
|
||||||
const dateA = new Date(b.created_at) as unknown as number;
|
data = await apiInstance
|
||||||
const dateB = new Date(a.created_at) as unknown as number;
|
.get<IMyReportsList>(
|
||||||
return dateA - dateB;
|
`/users/reports/?page=${page}&page_size=8&sort_by=${sort}`,
|
||||||
});
|
config
|
||||||
} else if (sort === "reviews") {
|
)
|
||||||
data.results = data.results.sort(
|
.then((res) => res.data);
|
||||||
(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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set({ data: data });
|
set({ data: data });
|
||||||
|
@ -5,15 +5,6 @@ import { IStatistics } from "@/shared/types/statistics-type";
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
const filterCategories: Record<string, string> = {
|
|
||||||
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 {
|
interface IStatisticsStore extends IFetch {
|
||||||
data: IStatistics[];
|
data: IStatistics[];
|
||||||
getStatistics: (
|
getStatistics: (
|
||||||
@ -35,16 +26,14 @@ export const useStatisticsStore = create<IStatisticsStore>((set) => ({
|
|||||||
try {
|
try {
|
||||||
set({ isLoading: true });
|
set({ isLoading: true });
|
||||||
const response = await apiInstance.get<IStatistics[]>(
|
const response = await apiInstance.get<IStatistics[]>(
|
||||||
`/report/${endpoint}/stats`
|
`/report/${endpoint}/stats?sort_by=${sort}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = response.data.filter((loc) =>
|
let data = response.data.filter((loc) =>
|
||||||
loc.name.toLowerCase().includes(query.toLowerCase())
|
loc.name.toLowerCase().includes(query.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
const sorted = data.sort((a: any, b: any) => a[sort] - b[sort]);
|
set({ data: data });
|
||||||
|
|
||||||
set({ data: sorted });
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof AxiosError) {
|
if (error instanceof AxiosError) {
|
||||||
set({ error: error.message });
|
set({ error: error.message });
|
||||||
|
Loading…
Reference in New Issue
Block a user