import React, { useState, useEffect } from "react"; import { MagnifyingGlassIcon, FolderIcon, Squares2X2Icon, Bars3Icon, } from "@heroicons/react/24/solid"; import ConfirmDialog from "./Shared/ConfirmDialog"; import ProjectModal from "./Project/ProjectModal"; import { useStore } from "../store/useStore"; import { fetchGroupedProjects, createProject, updateProject, deleteProject } from "../utils/projectsService"; import { fetchAreas } from "../utils/areasService"; import { useTranslation } from "react-i18next"; import { Project } from "../entities/Project"; import { useModalEvents } from "../hooks/useModalEvents"; import { PriorityType } from "../entities/Task"; import { useSearchParams } from "react-router-dom"; import ProjectItem from "./Project/ProjectItem"; const getPriorityStyles = (priority: PriorityType) => { switch (priority) { case "low": return { color: "bg-green-500" }; case "medium": return { color: "bg-yellow-500" }; case "high": return { color: "bg-red-500" }; default: return { color: "bg-gray-500" }; } }; const Projects: React.FC = () => { const { t } = useTranslation(); const { areas, setAreas, setLoading: setAreasLoading, setError: setAreasError } = useStore((state) => state.areasStore); const { setLoading: setProjectsLoading, setError: setProjectsError } = useStore((state) => state.projectsStore); const { isLoading, isError } = useStore((state) => state.projectsStore); const [groupedProjects, setGroupedProjects] = useState>({}); const [isProjectModalOpen, setIsProjectModalOpen] = useState(false); const [projectToEdit, setProjectToEdit] = useState(null); const [projectToDelete, setProjectToDelete] = useState(null); const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); const [activeDropdown, setActiveDropdown] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const [viewMode, setViewMode] = useState<"cards" | "list">("cards"); const [searchParams, setSearchParams] = useSearchParams(); const activeFilter = searchParams.get("active") || "all"; // Dispatch global modal events useModalEvents(isProjectModalOpen); const areaFilter = searchParams.get("area_id") || ""; useEffect(() => { const loadAreas = async () => { try { const areasData = await fetchAreas(); setAreas(areasData); } catch (error) { console.error("Failed to fetch areas:", error); setAreasError(true); } }; loadAreas(); }, []); useEffect(() => { const loadProjects = async () => { try { const groupedProjectsData = await fetchGroupedProjects(activeFilter, areaFilter); setGroupedProjects(groupedProjectsData); } catch (error) { console.error("Failed to fetch projects:", error); setProjectsError(true); } }; loadProjects(); }, [activeFilter, areaFilter]); const handleSaveProject = async (project: Project) => { setProjectsLoading(true); try { if (project.id) { await updateProject(project.id, project); } else { await createProject(project); } const groupedProjectsData = await fetchGroupedProjects(activeFilter, areaFilter); setGroupedProjects(groupedProjectsData); } catch (error) { console.error("Error saving project:", error); setProjectsError(true); } finally { setProjectsLoading(false); setIsProjectModalOpen(false); } }; const handleEditProject = (project: Project) => { setProjectToEdit(project); setIsProjectModalOpen(true); }; const handleDeleteProject = async () => { if (!projectToDelete) return; try { if (projectToDelete.id !== undefined) { setProjectsLoading(true); await deleteProject(projectToDelete.id); const groupedProjectsData = await fetchGroupedProjects(activeFilter, areaFilter); setGroupedProjects(groupedProjectsData); } else { console.error("Cannot delete project: ID is undefined."); } } catch (error) { console.error("Error deleting project:", error); setProjectsError(true); } finally { setProjectsLoading(false); setIsConfirmDialogOpen(false); setProjectToDelete(null); } }; const getCompletionPercentage = (project: Project) => { // Now the completion percentage comes directly from the backend return (project as any).completion_percentage || 0; }; const handleActiveFilterChange = (e: React.ChangeEvent) => { const newActiveFilter = e.target.value; const params = new URLSearchParams(searchParams); if (newActiveFilter === "all") { params.delete("active"); } else { params.set("active", newActiveFilter); } setSearchParams(params); }; const handleAreaFilterChange = (e: React.ChangeEvent) => { const newAreaFilter = e.target.value; const params = new URLSearchParams(searchParams); if (newAreaFilter === "") { params.delete("area_id"); } else { params.set("area_id", newAreaFilter); } setSearchParams(params); }; // Apply search filter to the grouped projects from backend const searchFilteredGroupedProjects = Object.keys(groupedProjects).reduce>( (acc, areaName) => { const projectsInArea = groupedProjects[areaName]; // Defensive check: ensure projectsInArea is an array if (!Array.isArray(projectsInArea)) { console.warn(`Projects for area "${areaName}" is not an array:`, projectsInArea); return acc; } const filteredProjects = projectsInArea.filter((project) => project.name.toLowerCase().includes(searchQuery.toLowerCase()) ); if (filteredProjects.length > 0) { acc[areaName] = filteredProjects; } return acc; }, {} ); if (isLoading) { return (
{t('projects.loading')}
); } if (isError) { return (
{t('projects.error')}
); } return (

{t('projects.title')}

{/* View Mode and Filters */}
{/* Search Bar */}
setSearchQuery(e.target.value)} className="w-full bg-transparent border-none focus:ring-0 focus:outline-none dark:text-white" />
{/* Projects Grid/List */}
{Object.keys(searchFilteredGroupedProjects).length === 0 ? (
{t('projects.noProjectsFound')}
) : ( Object.keys(searchFilteredGroupedProjects).map((areaName) => (

{areaName}

{searchFilteredGroupedProjects[areaName].map((project) => { const { color } = getPriorityStyles(project.priority || "low"); return ( getCompletionPercentage(project)} activeDropdown={activeDropdown} setActiveDropdown={setActiveDropdown} handleEditProject={handleEditProject} setProjectToDelete={setProjectToDelete} setIsConfirmDialogOpen={setIsConfirmDialogOpen} /> ); })}
)) )}
{isProjectModalOpen && ( { setIsProjectModalOpen(false); setProjectToEdit(null); }} onSave={handleSaveProject} project={projectToEdit || undefined} areas={areas} /> )} {isConfirmDialogOpen && ( setIsConfirmDialogOpen(false)} /> )}
); }; export default Projects;