import React, { useEffect, useState } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { PencilSquareIcon, TrashIcon, FolderIcon, Squares2X2Icon } from "@heroicons/react/24/outline"; import TaskList from "../Task/TaskList"; import ProjectModal from "../Project/ProjectModal"; import ConfirmDialog from "../Shared/ConfirmDialog"; import { useStore } from "../../store/useStore"; import NewTask from "../Task/NewTask"; import { Project } from "../../entities/Project"; import { PriorityType, Task } from "../../entities/Task"; import { fetchProjectById, updateProject, deleteProject } from "../../utils/projectsService"; import { createTask, updateTask, deleteTask } from "../../utils/tasksService"; import { fetchAreas } from "../../utils/areasService"; import { isAuthError } from "../../utils/authUtils"; import { CalendarDaysIcon, InformationCircleIcon } from "@heroicons/react/24/solid"; type PriorityStyles = Record & { default: string }; const priorityStyles: PriorityStyles = { high: 'bg-red-500', medium: 'bg-yellow-500', low: 'bg-green-500', default: 'bg-gray-400', }; const ProjectDetails: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { t, i18n } = useTranslation(); const areas = useStore((state) => state.areasStore.areas); const [project, setProject] = useState(undefined); const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); const [showCompleted, setShowCompleted] = useState(false); useEffect(() => { const loadProjectData = async () => { if (!id) { console.error("Project ID is missing."); return; } setLoading(true); try { fetchAreas(); const projectData = await fetchProjectById(id); console.log("Project data received:", projectData); console.log("Tasks in project:", projectData.tasks); console.log("Tasks (capital T):", projectData.Tasks); setProject(projectData); // Handle both 'tasks' and 'Tasks' property names const projectTasks = projectData.tasks || projectData.Tasks || []; setTasks(projectTasks); } catch (error) { console.error("Error fetching project data:", error); } finally { setLoading(false); } }; loadProjectData(); }, [id, fetchAreas]); const handleTaskCreate = async (taskName: string) => { if (!project) { console.error("Cannot create task: Project is missing"); throw new Error("Cannot create task: Project is missing"); } try { const newTask = await createTask({ name: taskName, status: "not_started", project_id: project.id, }); setTasks((prevTasks) => [...prevTasks, newTask]); } catch (err: any) { console.error("Error creating task:", err); // Check if it's an authentication error if (isAuthError(err)) { return; } throw err; // Re-throw to allow proper error handling by NewTask component } }; const handleTaskUpdate = async (updatedTask: Task) => { if (!updatedTask.id) { console.error("Cannot update task: Task ID is missing"); return; } try { await updateTask(updatedTask.id, updatedTask); setTasks((prevTasks) => prevTasks.map((task) => task.id === updatedTask.id ? updatedTask : task ) ); } catch (err) { console.error("Error updating task:", err); } }; const handleTaskDelete = async (taskId: number | undefined) => { if (!taskId) { console.error("Cannot delete task: Task ID is missing"); return; } try { await deleteTask(taskId); setTasks((prevTasks) => prevTasks.filter((task) => task.id !== taskId)); } catch (err) { console.error("Error deleting task:", err); } }; const handleEditProject = () => { setIsModalOpen(true); }; const handleSaveProject = async (updatedProject: Project) => { if (!updatedProject.id) { console.error("Cannot save project: Project ID is missing"); return; } try { const savedProject = await updateProject(updatedProject.id, updatedProject); setProject(savedProject); setIsModalOpen(false); } catch (err) { console.error("Error saving project:", err); } }; const handleDeleteProject = async () => { if (!project?.id) { console.error("Cannot delete project: Project ID is missing"); return; } try { await deleteProject(project.id); navigate("/projects"); } catch (err) { console.error("Error deleting project:", err); } }; if (loading) { return (
Loading project details...
); } if (error) { return (
{error}
); } if (!project) { return (
Project not found.
); } const activeTasks = tasks?.filter((task) => { return typeof task.status === 'number' ? task.status !== 2 : task.status !== 'done'; }) || []; //TODO: Also add archived const completedTasks = tasks?.filter((task) => { return typeof task.status === 'number' ? task.status === 2 : task.status === 'done'; }); const displayTasks = showCompleted ? [...activeTasks, ...completedTasks] : activeTasks; const formatProjectDueDate = (dateString: string) => { const date = new Date(dateString); const currentLang = i18n.language; // Format based on language const formatOptions: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }; return date.toLocaleDateString(currentLang, formatOptions); }; return (
{/* Project Banner Image */} {project.image_url && (
{project.name} {/* Title Overlay */}

{project.name}

{/* Priority Indicator on Image */} {project.priority && (
)} {/* Edit/Delete Buttons on Image */}
)} {/* Project Metadata Box */} {(project.description || project.area || project.due_date_at || (project.tags && project.tags.length > 0)) && (
{project.description && (
Description:

{project.description}

)} {project.area && (
Area: {project.area.name}
)} {project.due_date_at && (
Due Date: {formatProjectDueDate(project.due_date_at)}
)} {project.tags && project.tags.length > 0 && (
Tags:
{project.tags.map((tag, index) => ( {tag.name} ))}
)}
)} {/* Project Header - Only show when no image */} {!project.image_url && (

{project.name}

{/* Show priority indicator only when no image */} {project.priority && (
)}
)}

Tasks

{completedTasks.length > 0 && ( )}
{displayTasks.length > 0 ? ( ) : (

No tasks.

)}
setIsModalOpen(false)} onSave={handleSaveProject} project={project} areas={areas} /> {isConfirmDialogOpen && ( setIsConfirmDialogOpen(false)} /> )}
); }; const priorityLabel = (priority: PriorityType) => { switch (priority) { case 'high': return 'High'; case 'medium': return 'Medium'; case 'low': return 'Low'; default: return ''; } }; export default ProjectDetails;