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:
1 parent
0221a79015
commit
0ba0f1df89
25 files changed
+413
-171
No files matched your search
@@ -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<IConfirmEmailFormProps> = ({
|
||||
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(
|
||||
"Проблема на стороне сервера или вы достигли максимальное количество попыток"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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<IConfirmCodeProps> = ({
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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<ISendEmailProps> = ({
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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<IChangePasswordProps> = ({
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,11 @@ const ProfileForm: React.FC<IProfileFormProps> = ({
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AxiosError) {
|
||||
setError(error.message);
|
||||
if (error.response?.status === 400) {
|
||||
setError("Были введены неккоректные данные");
|
||||
} else {
|
||||
setError("Ошибка на стороне сервера");
|
||||
}
|
||||
} else {
|
||||
setError("Возникла непредвиденная ошибка");
|
||||
}
|
||||
|
||||
Whitespace-only changes.
@@ -1,7 +0,0 @@
|
||||
import "./CreateReportMap.scss";
|
||||
|
||||
const CreateReportMap = () => {
|
||||
return <div>CreateReportMap</div>;
|
||||
};
|
||||
|
||||
export default CreateReportMap;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
</div>
|
||||
<button disabled={loader} type="submit">
|
||||
{loader ? <Loader /> : "Отправить на модерацию"}
|
||||
<Image src={arrow_right} alt="Arrow Right Icon" />
|
||||
{loader ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<>
|
||||
Отправить на модерацию
|
||||
<Image src={arrow_right} alt="Arrow Right Icon" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
{error ? <p className="report-form__error">{error}</p> : null}
|
||||
</form>
|
||||
|
||||
@@ -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<string>("");
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<string>("");
|
||||
const [passwordWarning, setPasswordWarning] = useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
@@ -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("Произошла непредвиденная ошибка");
|
||||
|
||||
@@ -29,7 +29,8 @@ const ProfileTable: React.FC<IProfileTableProps> = ({
|
||||
} = useProfileReportsStore();
|
||||
const session = useSession();
|
||||
const router = useRouter();
|
||||
const [sort, setSort] = useState("date");
|
||||
const [sort, setSort] = useState("");
|
||||
const [status, setStatus] = useState<number>(2);
|
||||
const [activePage, setActivePage] = useState<number>(
|
||||
+searchParams["страница-обращений"] || 1
|
||||
);
|
||||
@@ -37,11 +38,19 @@ const ProfileTable: React.FC<IProfileTableProps> = ({
|
||||
{
|
||||
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<IProfileTableProps> = ({
|
||||
{
|
||||
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<IProfileTableProps> = ({
|
||||
getMyReports(
|
||||
sort,
|
||||
activePage,
|
||||
session.data?.access_token as string
|
||||
session.data?.access_token as string,
|
||||
status
|
||||
);
|
||||
}, [activePage, sort]);
|
||||
}, [activePage, sort, status, session]);
|
||||
|
||||
return (
|
||||
<div className="profile-table">
|
||||
|
||||
@@ -7,17 +7,13 @@ import {
|
||||
import { AxiosError } from "axios";
|
||||
import { create } from "zustand";
|
||||
|
||||
const filterCategories: Record<string, string> = {
|
||||
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<IProfileReportsStore>(
|
||||
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<IProfileReportsStore>(
|
||||
},
|
||||
};
|
||||
set({ isLoading: true });
|
||||
const data = await apiInstance
|
||||
.get<IMyReportsList>(
|
||||
`/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<IMyReportsList>(
|
||||
`/users/reports/?page=${page}&page_size=8&status=${status}`,
|
||||
config
|
||||
)
|
||||
.then((res) => res.data);
|
||||
} else {
|
||||
data = await apiInstance
|
||||
.get<IMyReportsList>(
|
||||
`/users/reports/?page=${page}&page_size=8&sort_by=${sort}`,
|
||||
config
|
||||
)
|
||||
.then((res) => res.data);
|
||||
}
|
||||
|
||||
set({ data: data });
|
||||
|
||||
@@ -5,15 +5,6 @@ import { IStatistics } from "@/shared/types/statistics-type";
|
||||
import { AxiosError } from "axios";
|
||||
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 {
|
||||
data: IStatistics[];
|
||||
getStatistics: (
|
||||
@@ -35,16 +26,14 @@ export const useStatisticsStore = create<IStatisticsStore>((set) => ({
|
||||
try {
|
||||
set({ isLoading: true });
|
||||
const response = await apiInstance.get<IStatistics[]>(
|
||||
`/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 });
|
||||
|
||||
Reference in new issue
Block a user