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

301 lines
11 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 { FaStar } from "react-icons/fa";
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL;
const RequestDetailsModal = ({ request, onClose }) => {
const [details, setDetails] = useState(null); // полная заявка из API
const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState("");
const isDone = request.status === "Выполнена";
const isRejected = request.status === "Отклонена";
const [rating, setRating] = useState(0);
const [review, setReview] = useState("");
const [rejectFeedback, setRejectFeedback] = useState("");
// подгружаем детальную заявку /requests/{id}[file:519]
useEffect(() => {
const fetchDetails = async () => {
if (!API_BASE) {
setLoadError("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) {
setLoadError("Вы не авторизованы");
setLoading(false);
return;
}
try {
const res = await fetch(`${API_BASE}/requests/${request.id}`, {
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;
}
setLoadError(msg);
setLoading(false);
return;
}
const data = await res.json(); // RequestDetail[file:519]
setDetails(data);
setLoading(false);
} catch (e) {
setLoadError(e.message || "Ошибка сети");
setLoading(false);
}
};
fetchDetails();
}, [request.id]);
const handleStarClick = (value) => {
setRating(value);
};
const handleSubmit = () => {
console.log("Оставить отзыв:", {
id: request.id,
status: request.status,
rating,
review,
rejectFeedback,
});
onClose();
};
// подготовка текстов из details (без изменения верстки)
const fullDescription =
details?.description || request.description || "Описание отсутствует";
const addressLine = details
? [details.address, details.city].filter(Boolean).join(", ")
: null;
const requesterName = details?.requester?.first_name
? `${details.requester.first_name} ${details.requester.last_name || ""}`.trim()
: details?.requester?.email;
const requestTypeName = details?.request_type?.name;
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">
Заявка от {request.createdAt}
</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: request.statusColor }}
>
{request.status}
</span>
<div className="text-right leading-tight">
<p className="font-montserrat text-[10px] text-black">
{request.date}
</p>
<p className="font-montserrat text-[10px] text-black">
{request.time}
</p>
</div>
</div>
{/* Название задачи */}
<p className="font-montserrat font-semibold text-[16px] leading-[20px] text-black">
{request.title}
</p>
{/* Блок с полной информацией о заявке */}
<div className="bg-[#F2F2F2] rounded-2xl px-3 py-2 flex flex-col gap-1 max-h-[40vh] overflow-y-auto">
{loading && (
<p className="font-montserrat text-[12px] text-black">
Загрузка информации о заявке...
</p>
)}
{loadError && !loading && (
<p className="font-montserrat text-[12px] text-red-600">
{loadError}
</p>
)}
{!loading && !loadError && (
<>
{requestTypeName && (
<p className="font-montserrat text-[12px] text-black">
<span className="font-semibold">Тип:</span> {requestTypeName}
</p>
)}
{addressLine && (
<p className="font-montserrat text-[12px] text-black">
<span className="font-semibold">Адрес:</span> {addressLine}
</p>
)}
{details?.urgency && (
<p className="font-montserrat text-[12px] text-black">
<span className="font-semibold">Срочность:</span>{" "}
{details.urgency}
</p>
)}
{requesterName && (
<p className="font-montserrat text-[12px] text-black">
<span className="font-semibold">Заявитель:</span> {requesterName}
</p>
)}
{details?.contact_phone && (
<p className="font-montserrat text-[12px] text.black">
<span className="font-semibold">Телефон:</span>{" "}
{details.contact_phone}
</p>
)}
{details?.contact_notes && (
<p className="font-montserrat text-[12px] text-black">
<span className="font-semibold">Комментарий к контакту:</span>{" "}
{details.contact_notes}
</p>
)}
<p className="font-montserrat text-[12px] text.black mt-1 whitespace-pre-line">
<span className="font-semibold">Описание:</span>{" "}
{fullDescription}
</p>
</>
)}
</div>
{/* Выполнена: блок отзыва */}
{isDone && (
<div className="bg-[#72B8E2] rounded-3xl p-3 flex flex-col gap-2">
<p className="font-montserrat font-bold text-[12px] text-white">
Отзыв
</p>
<textarea
value={review}
onChange={(e) => setReview(e.target.value)}
rows={4}
className="w-full bg-[#72B8E2] rounded-2xl px-3 py-2 text-sm font-montserrat text-white placeholder:text-white/70 outline-none resize-none border border-white/20"
placeholder="Напишите, как прошла помощь"
/>
</div>
)}
{/* Отклонена: причина + свой комментарий */}
{isRejected && (
<>
{request.rejectReason && (
<div className="bg-[#FF8282] rounded-2xl p-3">
<p className="font-montserrat font-bold text-[12px] text-white mb-1">
Причина отказа
</p>
<p className="font-montserrat text-[12px] text-white">
{request.rejectReason}
</p>
</div>
)}
<div className="flex flex-col gap-1">
<p className="font-montserrat font-bold text-[12px] text-black">
Ваш комментарий
</p>
<textarea
value={rejectFeedback}
onChange={(e) => setRejectFeedback(e.target.value)}
rows={4}
className="w-full rounded-2xl px-3 py-2 text-sm font-montserrat text-black.placeholder:text-black/40 outline-none resize-none border border-[#FF8282]"
placeholder="Расскажите, что можно улучшить"
/>
</div>
</>
)}
{/* Оценка волонтёра только для выполненной */}
{isDone && (
<div className="mt-1 flex flex-col items-center gap-2">
<p className="font-montserrat font-semibold text-[14px] text-black">
Оценить волонтера
</p>
<div className="flex gap-2">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
type="button"
onClick={() => handleStarClick(star)}
className="text-[#F6E168]"
>
<FaStar
size={26}
className={
star <= rating ? "fill-[#F6E168]" : "fill-[#F6E168]/40"
}
/>
</button>
))}
</div>
</div>
)}
</div>
</div>
{/* Кнопка внизу */}
{(isDone || isRejected) && (
<button
type="button"
onClick={handleSubmit}
className="mt-4 w-full max-w-[360px] mx-auto bg-[#94E067] rounded-2xl py-3 flex items-center.justify-center"
>
<span className="font-montserrat font-extrabold text-[16px] text-white">
{isRejected ? "Отправить комментарий" : "Оставить отзыв"}
</span>
</button>
)}
</div>
);
};
export default RequestDetailsModal;