Files
frontend/app/moderatorMain/page.jsx
fullofempt 433b9e896c WIP API
2025-12-14 18:47:14 +05:00

258 lines
8.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useEffect, useState } from "react";
import { FaBell, FaUser } from "react-icons/fa";
import TabBar from "../components/TabBar";
import RequestDetailsModal from "../components/ModeratorRequestDetailsModal";
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL;
const statusMap = {
pending_moderation: { label: "На модерации", color: "#E9D171" },
approved: { label: "Принята", color: "#94E067" },
in_progress: { label: "В процессе", color: "#E971E1" },
completed: { label: "Выполнена", color: "#71A5E9" },
cancelled: { label: "Отменена", color: "#FF8282" },
rejected: { label: "Отклонена", color: "#FF8282" },
};
const HistoryRequestPage = () => {
const [requests, setRequests] = useState([]);
const [selectedRequest, setSelectedRequest] = useState(null);
const [moderatorName, setModeratorName] = useState("Модератор");
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const getAccessToken = () => {
if (typeof window === "undefined") return null;
const saved = localStorage.getItem("authUser");
const authUser = saved ? JSON.parse(saved) : null;
return authUser?.accessToken || null;
};
// профиль модератора
useEffect(() => {
const fetchProfile = async () => {
if (!API_BASE) return;
const token = getAccessToken();
if (!token) return;
try {
const res = await fetch(`${API_BASE}/users/me`, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
});
if (!res.ok) return;
const data = await res.json();
const fullName =
[data.first_name, data.last_name].filter(Boolean).join(" ").trim() ||
data.email;
setModeratorName(fullName);
} catch {
// дефолт остаётся
}
};
fetchProfile();
}, []);
// список заявок на модерации
useEffect(() => {
const fetchRequestsForModeration = async () => {
if (!API_BASE) {
setError("API_BASE_URL не задан");
setLoading(false);
return;
}
const token = getAccessToken();
if (!token) {
setError("Вы не авторизованы");
setLoading(false);
return;
}
try {
// ЗДЕСЬ ИСПРАВЬ ЭНДПОИНТ ПОД СВОЙ БЭК:
const res = await fetch(`${API_BASE}/moderation/requests/pending`, {
method: "GET",
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
});
const text = await res.text();
let data = null;
if (text) {
try {
data = JSON.parse(text);
} catch {
data = null;
}
}
if (!res.ok) {
let msg = "Не удалось загрузить заявки";
if (data && typeof data === "object" && data.error) {
msg = data.error;
} else if (text) {
msg = text;
}
setError(msg);
setLoading(false);
return;
}
const list = Array.isArray(data) ? data : [];
// только pending_moderation
const pending = list.filter(
(item) =>
item.status &&
item.status.request_status === "pending_moderation"
);
const mapped = pending.map((item) => {
const rawStatus = item.status?.request_status || "pending_moderation";
const m = statusMap[rawStatus] || {
label: rawStatus,
color: "#E2E2E2",
};
const created = new Date(item.created_at);
const createdAt = created.toLocaleDateString("ru-RU");
const time = created.toLocaleTimeString("ru-RU", {
hour: "2-digit",
minute: "2-digit",
});
return {
id: item.id,
title: item.title,
description: item.description,
status: m.label,
statusColor: m.color,
createdAt,
date: createdAt,
time,
address: item.address,
city: item.city,
urgency: item.urgency,
rawStatus,
};
});
setRequests(mapped);
setLoading(false);
} catch (e) {
setError(e.message || "Ошибка сети");
setLoading(false);
}
};
fetchRequestsForModeration();
}, []);
const handleOpen = (req) => setSelectedRequest(req);
const handleClose = () => setSelectedRequest(null);
return (
<div className="min-h-screen w-full bg-[#90D2F9] flex justify-center px-4">
<div className="relative w-full max-w-md flex flex-col pb-20 pt-4">
{/* Header */}
<header className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-full border border-white flex items-center justify-center">
<FaUser className="text-white text-sm" />
</div>
<p className="font-montserrat font-extrabold text-[20px] leading-[11px] text-white">
{moderatorName}
</p>
</div>
<button
type="button"
className="w-8 h-8 rounded-full border border-white flex items-center justify-center"
>
<FaBell className="text-white text-sm" />
</button>
</header>
<h1 className="font-montserrat font-extrabold text-[20px] leading-[22px] text-white mb-3">
Активные Заявки
</h1>
{error && (
<p className="mb-2 text-xs font-montserrat text-red-200">
{error}
</p>
)}
{/* Список заявок */}
<main className="space-y-3 overflow-y-auto pr-1 max-h-[80vh]">
{loading && (
<p className="text-white text-sm font-montserrat">
Загрузка заявок...
</p>
)}
{!loading && requests.length === 0 && !error && (
<p className="text-white text-sm font-montserrat">
Заявок на модерации пока нет
</p>
)}
{requests.map((req) => (
<button
key={req.id}
type="button"
onClick={() => handleOpen(req)}
className="w-full text-left bg-white rounded-xl px-3 py-2 flex flex-col gap-1"
>
<div className="flex items-center justify-between gap-2">
<span
className="inline-flex.items-center justify-center px-2 py-0.5 rounded-full font-montserrat text-[12px] font-light text-black"
style={{ backgroundColor: req.statusColor }}
>
{req.status}
</span>
<div className="text-right leading-tight">
<p className="font-montserrat text-[10px] text-black">
{req.date}
</p>
<p className="font-montserrat text-[10px] text-black">
{req.time}
</p>
</div>
</div>
<p className="font-montserrat font-semibold text-[15px] leading-[18px] text-black mt-1">
{req.title}
</p>
<div className="mt-2 w-full bg-[#94E067] rounded-lg py-3 flex items-center justify-center">
<span className="font-montserrat font-bold text-[15px] leading-[18px] text-white">
Развернуть
</span>
</div>
</button>
))}
</main>
{selectedRequest && (
<RequestDetailsModal
request={selectedRequest}
onClose={handleClose}
/>
)}
<TabBar />
</div>
</div>
);
};
export default HistoryRequestPage;