import { useState, useEffect, useRef } from "react"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import { useParams, useNavigate, useLocation, Link } from "react-router-dom"; import { motion } from "framer-motion"; import Forbidden from "../components/Forbidden"; import ConfirmModal from "../components/ConfirmModal"; import FormField from "../components/FormField"; import AdminDatePicker from "../components/AdminDatePicker"; import ProjectFileManager from "../components/ProjectFileManager"; import apiFetch from "../utils/api"; const API_BASE = "/api/admin"; const STATUS_LABELS: Record = { aktivni: "Aktivní", dokonceny: "Dokončený", zruseny: "Zrušený", }; function formatNoteDate(dateStr: string) { if (!dateStr) return ""; const d = new Date(dateStr); const day = d.getDate(); const month = d.getMonth() + 1; const year = d.getFullYear(); const hours = String(d.getHours()).padStart(2, "0"); const mins = String(d.getMinutes()).padStart(2, "0"); return `${day}. ${month}. ${year} ${hours}:${mins}`; } interface Note { id: number; content: string; user_name: string; created_at: string; } interface User { id: number; name: string; } interface ProjectData { id: number; project_number: string; name: string; status: string; start_date: string; end_date: string; customer_name: string; responsible_user_id: string; notes?: string; order_id?: number; order_number?: string; order_status?: string; quotation_id?: number; quotation_number?: string; has_nas_folder?: boolean; } interface ProjectForm { name: string; status: string; start_date: string; end_date: string; responsible_user_id: string; } export default function ProjectDetail() { const { id } = useParams(); const alert = useAlert(); const { hasPermission, isAdmin } = useAuth(); const navigate = useNavigate(); const location = useLocation(); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [project, setProject] = useState(null); const [form, setForm] = useState({ name: "", status: "aktivni", start_date: "", end_date: "", responsible_user_id: "", }); const [users, setUsers] = useState([]); const [deleteConfirm, setDeleteConfirm] = useState(false); const [deleting, setDeleting] = useState(false); const [deleteFiles, setDeleteFiles] = useState(false); // Dynamic notes const [notes, setNotes] = useState([]); const [notesLoading, setNotesLoading] = useState(true); const [newNote, setNewNote] = useState(""); const [addingNote, setAddingNote] = useState(false); const [deletingNoteId, setDeletingNoteId] = useState(null); const createdShown = useRef(false); useEffect(() => { if ( (location.state as { created?: boolean })?.created && !createdShown.current ) { createdShown.current = true; alert.success("Projekt byl vytvořen"); navigate(location.pathname, { replace: true, state: {} }); } }, [location.state, location.pathname, alert, navigate]); const fetchNotes = async () => { try { const response = await apiFetch(`${API_BASE}/projects/${id}`); if (response.status === 401) return; const result = await response.json(); if (result.success) { setNotes(result.data.project_notes || []); } } catch { // silent - notes are supplementary } finally { setNotesLoading(false); } }; useEffect(() => { const fetchDetail = async () => { try { const response = await apiFetch(`${API_BASE}/projects/${id}`); if (response.status === 401) return; const result = await response.json(); if (result.success) { const p = result.data; setProject(p); setForm({ name: p.name || "", status: p.status || "aktivni", start_date: (p.start_date || "").substring(0, 10), end_date: (p.end_date || "").substring(0, 10), responsible_user_id: p.responsible_user_id || "", }); } else { alert.error(result.error || "Nepodařilo se načíst projekt"); navigate("/projects"); } } catch { alert.error("Chyba připojení"); navigate("/projects"); } finally { setLoading(false); } }; const fetchUsers = async () => { try { const res = await apiFetch(`${API_BASE}/users`); if (res.status === 401) return; const data = await res.json(); if (data.success) { const raw = Array.isArray(data.data) ? data.data : data.data?.items || []; setUsers( raw.map((u: any) => ({ id: u.id, name: `${u.first_name || ""} ${u.last_name || ""}`.trim() || u.username, })), ); } } catch { // silent } }; fetchDetail(); fetchNotes(); fetchUsers(); }, [id, alert, navigate]); // eslint-disable-line react-hooks/exhaustive-deps if (!hasPermission("projects.view")) return ; const updateForm = (field: keyof ProjectForm, value: string) => setForm((prev) => ({ ...prev, [field]: value })); const handleSave = async () => { if (!form.name.trim()) { alert.error("Název projektu je povinný"); return; } setSaving(true); try { const response = await apiFetch(`${API_BASE}/projects/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: form.name, status: form.status, start_date: form.start_date || null, end_date: form.end_date || null, responsible_user_id: form.responsible_user_id || null, }), }); const result = await response.json(); if (result.success) { alert.success(result.message || "Projekt byl aktualizován"); } else { alert.error(result.error || "Nepodařilo se uložit projekt"); } } catch { alert.error("Chyba připojení"); } finally { setSaving(false); } }; const handleDelete = async () => { setDeleting(true); try { const response = await apiFetch(`${API_BASE}/projects/${id}`, { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ delete_files: deleteFiles }), }); const result = await response.json(); if (result.success) { navigate("/projects"); setTimeout(() => alert.success("Projekt byl smazán"), 300); } else { alert.error(result.error || "Nepodařilo se smazat projekt"); } } catch { alert.error("Chyba připojení"); } finally { setDeleting(false); } }; const handleAddNote = async () => { if (!newNote.trim()) return; setAddingNote(true); try { const response = await apiFetch(`${API_BASE}/projects/${id}/notes`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content: newNote.trim() }), }); const result = await response.json(); if (result.success) { setNotes((prev) => [result.data.note, ...prev]); setNewNote(""); alert.success("Poznámka byla přidána"); } else { alert.error(result.error || "Nepodařilo se přidat poznámku"); } } catch { alert.error("Chyba připojení"); } finally { setAddingNote(false); } }; const handleDeleteNote = async (noteId: number) => { setDeletingNoteId(noteId); try { const response = await apiFetch( `${API_BASE}/projects/${id}/notes/${noteId}`, { method: "DELETE", }, ); const result = await response.json(); if (result.success) { setNotes((prev) => prev.filter((n) => n.id !== noteId)); alert.success("Poznámka byla smazána"); } else { alert.error(result.error || "Nepodařilo se smazat poznámku"); } } catch { alert.error("Chyba připojení"); } finally { setDeletingNoteId(null); } }; if (loading) { return (
{[0, 1, 2, 3].map((i) => (
))}
); } if (!project) return null; const canEdit = hasPermission("projects.edit"); return (
{/* Header */}

Projekt {project.project_number}

{canEdit && (
{!project.order_id && ( )}
)}
{/* Form */}

Základní údaje

updateForm("name", e.target.value)} className="admin-form-input" placeholder="Název projektu" disabled={!canEdit} />
updateForm("start_date", val)} disabled={!canEdit} /> updateForm("end_date", val)} disabled={!canEdit} />
{/* Notes */}

Poznámky

{/* Add note */}