Files
frontend/app/components/ValounterRequestDetailsModal.jsx
fullofempt e4bfbd30cc end
2025-12-15 13:18:18 +05:00

255 lines
8.3 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";
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 VolunteerRequestDetailsModal = ({ request, onClose }) => {
const [details, setDetails] = useState(null);
const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState("");
// статус отклика (из списка /responses/my)
const responseRawStatus = String(
request.rawStatus || request.status || ""
).toLowerCase();
const responseStatus =
responseStatusMap[responseRawStatus] ||
{ label: "Неизвестен", color: "#E2E2E2" };
const isAccepted = responseRawStatus === "accepted";
const isDone = false; // для волонтёра нет completed в ResponseStatus
const isInProgress = isAccepted; // принятый отклик ~ «в процессе»
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 fetchDetails = async () => {
if (!API_BASE) {
setLoadError("API_BASE_URL не задан");
setLoading(false);
return;
}
const token = getAccessToken();
if (!token) {
setLoadError("Вы не авторизованы");
setLoading(false);
return;
}
try {
const res = await fetch(
`${API_BASE}/requests/${request.requestId || request.request_id || request.id}`,
{
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;
}
setLoadError(msg);
setLoading(false);
return;
}
setDetails(data);
setLoading(false);
} catch (e) {
setLoadError(e.message || "Ошибка сети");
setLoading(false);
}
};
fetchDetails();
}, [request.requestId, request.request_id, request.id]);
if (loading) {
return (
<div className="fixed inset-0 z-40 flex items-center justify-center bg-[#90D2F9]/80">
<p className="text-white font-montserrat text-sm">Загрузка заявки...</p>
</div>
);
}
if (loadError || !details) {
return (
<div className="fixed inset-0 z-40 flex flex-col items-center justify-center bg-[#90D2F9]/80 px-4">
<p className="text-white font-montserrat text-sm mb-3">
{loadError || "Заявка не найдена"}
</p>
<button
type="button"
onClick={onClose}
className="px-4 py-2 bg-white rounded-xl font-montserrat text-sm"
>
Закрыть
</button>
</div>
);
}
// Тип заявки из RequestDetail
const requestTypeName = details.request_type?.name || "Не указан";
const urgencyText = (() => {
switch (details.urgency) {
case "low":
return "Низкая";
case "medium":
return "Средняя";
case "high":
return "Высокая";
case "urgent":
return "Срочно";
default:
return null;
}
})();
const place = [details.address, details.city].filter(Boolean).join(", ");
const requesterName =
(details.requester &&
[details.requester.first_name, details.requester.last_name]
.filter(Boolean)
.join(" ")
.trim()) ||
details.requester?.email ||
"Заявитель";
const created = details.created_at ? new Date(details.created_at) : null;
const createdDate = created ? created.toLocaleDateString("ru-RU") : "";
const createdTime = created
? created.toLocaleTimeString("ru-RU", {
hour: "2-digit",
minute: "2-digit",
})
: "";
let deadlineText = "Не указано";
if (details.desired_completion_date) {
const d = new Date(details.desired_completion_date);
if (!Number.isNaN(d.getTime())) {
deadlineText = d.toLocaleDateString("ru-RU", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
}
}
return (
<div className="fixed inset-0 z-40 flex flex-col bg-[#90D2F9] px-4 pt-4 pb-20">
{/* Хедер */}
<div className="flex items-center gap-2 mb-3">
<button
type="button"
onClick={onClose}
className="text-white w-7 h-7 rounded-full flex items-center justify-center text-lg"
>
</button>
<p className="flex-1 text-center font-montserrat font-extrabold text-[20px] leading-[24px] text-white">
Заявка от {createdDate}
</p>
<span className="w-7" />
</div>
{/* Карточка */}
<div className="flex-1 flex items-start justify-center">
<div className="w-full max-w-[360px] bg-white rounded-2xl p-4 flex flex-col gap-4 shadow-lg">
{/* Статус ОТКЛИКА + дата/время */}
<div className="flex items-start justify-between">
<span
className="inline-flex items-center justify-center px-3 py-1 rounded-full font-montserrat text-[10px] font-semibold text-white"
style={{ backgroundColor: responseStatus.color }}
>
{responseStatus.label}
</span>
<div className="text-right leading-tight">
<p className="font-montserrat text-[10px] text-black">
{createdDate}
</p>
<p className="font-montserrat text-[10px] text-black">
{createdTime}
</p>
</div>
</div>
{/* Название задачи */}
<p className="font-montserrat font-semibold text-[16px] leading-[20px] text-black">
{details.title}
</p>
{/* Полная информация о заявке */}
<div className="flex flex-col gap-1 text-[12px] font-montserrat text-black">
<p>Тип: {requestTypeName}</p>
<p>Заявитель: {requesterName}</p>
<p>Адрес: {place || "Не указан"}</p>
{urgencyText && <p>Срочность: {urgencyText}</p>}
{details.contact_phone && <p>Телефон: {details.contact_phone}</p>}
{details.contact_notes && (
<p>Комментарий к контакту: {details.contact_notes}</p>
)}
<p>Выполнить до: {deadlineText}</p>
</div>
{/* Описание */}
{details.description && (
<div className="bg-[#E4E4E4] rounded-2xl px-3 py-2 max-h-[160px] overflow-y-auto">
<p className="text-[11px] leading-[13px] font-montserrat whitespace-pre-line">
{details.description}
</p>
</div>
)}
{/* Блок для принятых откликов */}
{(isAccepted || isInProgress || isDone) && details.assigned_volunteer && (
<div className="bg-[#F3F8FF] rounded-2xl px-3 py-2">
<p className="font-montserrat text-[11px] font-semibold text-black mb-1">
Вы назначены волонтёром
</p>
<p className="font-montserrat text-[11px] text-black">
Контакты заявителя: {details.contact_phone || "не указаны"}
</p>
</div>
)}
</div>
</div>
</div>
);
};
export default VolunteerRequestDetailsModal;