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

264 lines
8.4 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, { useState, useEffect } from "react";
import { FaBell, FaUser } from "react-icons/fa";
import TabBar from "../components/TabBar";
import RequestDetailsModal from "../components/RequestDetailsModal";
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL;
// маппинг статусов API -> текст/цвет для UI
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 [error, setError] = useState("");
const [loading, setLoading] = useState(true);
const [userName, setUserName] = useState("Пользователь");
const [profileError, setProfileError] = useState("");
// профиль: /users/me
useEffect(() => {
const fetchProfile = async () => {
if (!API_BASE) return;
const saved =
typeof window !== "undefined"
? localStorage.getItem("authUser")
: null;
const authUser = saved ? JSON.parse(saved) : null;
const accessToken = authUser?.accessToken;
if (!accessToken) return;
try {
const res = await fetch(`${API_BASE}/users/me`, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${accessToken}`,
},
});
if (!res.ok) {
setProfileError("Не удалось загрузить профиль");
return;
}
const data = await res.json();
const fullName =
[data.first_name, data.last_name]
.filter(Boolean)
.join(" ")
.trim() || data.email;
setUserName(fullName);
} catch {
setProfileError("Ошибка загрузки профиля");
}
};
fetchProfile();
}, []);
// заявки: /requests/my
useEffect(() => {
const fetchRequests = async () => {
if (!API_BASE) {
setError("API_BASE_URL не задан");
setLoading(false);
return;
}
const saved =
typeof window !== "undefined"
? localStorage.getItem("authUser")
: null;
const authUser = saved ? JSON.parse(saved) : null;
const accessToken = authUser?.accessToken;
if (!accessToken) {
setError("Вы не авторизованы");
setLoading(false);
return;
}
try {
const res = await fetch(`${API_BASE}/requests/my`, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${accessToken}`,
},
});
if (!res.ok) {
let msg = "Не удалось загрузить заявки";
try {
const data = await res.json();
if (data.error) msg = data.error;
} catch {
const text = await res.text();
if (text) msg = text;
}
setError(msg);
setLoading(false);
return;
}
const data = await res.json();
console.log("requests/my data:", data);
const mapped = data.map((item) => {
const rawStatus =
typeof item.status === "string"
? item.status
: item.status?.request_status;
const m = statusMap[rawStatus] || {
label: rawStatus || "unknown",
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,
status: m.label,
statusColor: m.color,
createdAt,
date: createdAt,
time,
description: item.description,
// если позже появятся причина/оценка — можно добавить сюда
};
});
setRequests(mapped);
setLoading(false);
} catch (e) {
setError(e.message || "Ошибка сети");
setLoading(false);
}
};
fetchRequests();
}, []);
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>
<div>
<p className="font-montserrat font-extrabold text-[20px] leading-[11px] text-white">
{userName}
</p>
{profileError && (
<p className="text-[10px] text-red-200 font-montserrat mt-1">
{profileError}
</p>
)}
</div>
</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 && (
<div className="mb-2 bg-red-500 text-white text-xs font-montserrat px-3 py-2 rounded-lg">
{error}
</div>
)}
{/* Список заявок */}
<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;