Files
fullofempt e4bfbd30cc end
2025-12-15 13:18:18 +05:00

279 lines
8.8 KiB
JavaScript
Raw Permalink 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/ValounterRequestDetailsModal";
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL;
// Статусы ОТКЛИКА волонтёра (ResponseStatus)
const responseStatusMap = {
pending: { label: "Ожидает ответа", color: "#E9D171" },
accepted: { label: "Принят", color: "#94E067" },
rejected: { label: "Отклонён", color: "#FF8282" },
cancelled: { label: "Отменён", color: "#FF8282" },
};
const HistoryRequestPage = () => {
const [userName, setUserName] = useState("Волонтёр");
const [requests, setRequests] = useState([]);
const [selectedRequest, setSelectedRequest] = useState(null);
const [error, setError] = useState("");
const [loading, setLoading] = useState(true);
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;
setUserName(fullName);
} catch {
// игнорируем
}
};
fetchProfile();
}, []);
// история откликов волонтёра: /responses/my
useEffect(() => {
const fetchVolunteerRequests = 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}/responses/my`, {
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 : [];
const mapped = list.map((item) => {
// VolunteerResponse.status: pending | accepted | rejected | cancelled
const rawStatus = String(
item.status?.response_status || item.status || ""
).toLowerCase();
const m = responseStatusMap[rawStatus] || {
label: rawStatus || "Неизвестен",
color: "#E2E2E2",
};
const created = item.responded_at
? new Date(item.responded_at)
: item.created_at
? new Date(item.created_at)
: null;
const date = created
? created.toLocaleDateString("ru-RU")
: "";
const time = created
? created.toLocaleTimeString("ru-RU", {
hour: "2-digit",
minute: "2-digit",
})
: "";
return {
// отклик
id: item.id,
rawStatus, // сырой статус для модалки
status: m.label, // русский текст для списка
statusColor: m.color, // цвет плашки
// данные по заявке, если backend их уже кладёт в ответ
requestId: item.request_id,
title: item.request_title || item.title || "Заявка",
description: item.request_description || item.description || "",
address: item.request_address || item.address || "",
requesterName: item.requester_name || "",
requestTypeName: item.request_type_name || "",
date,
time,
createdAt: date,
};
});
setRequests(mapped);
setLoading(false);
} catch (e) {
setError(e.message || "Ошибка сети");
setLoading(false);
}
};
fetchVolunteerRequests();
}, []);
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-[22px] text-white">
{userName}
</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-semibold text-white"
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>
{req.requesterName && (
<p className="font-montserrat text-[11px] text-black/80">
{req.requesterName}
</p>
)}
{req.address && (
<p className="font-montserrat text-[10px] text-black/70">
{req.address}
</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;