"use client"; import { ColumnDef } from "ikoncomponents"; import { useEffect, useRef, useState, useMemo } from "react"; import { Button, ComboboxInput, ComboboxItemProps, DataTableLayout, Input, } from "ikoncomponents"; import { Plus } from "lucide-react"; import { createProjectApi, getProjectsApi } from "@/app/utils/api/projectApi"; import ProjectCard from "./projectCard/projectCard"; import { Project } from "./types/project"; import { mapProjectToCard } from "./types/project-mapper"; import CreateProjectModal from "./projectModal/projectModal"; import { ProjectFormData } from "./projectModal/zodProject"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useAppCache } from "@/app/utils/context/AppCacheContext"; import { createUserMaps, findUsersInRole, } from "@/app/utils/api/user-dashboard-platform/mappingFunction"; // import FileUploader from "./uploadFile"; export default function ProjectsPage() { const [projects, setProjects] = useState([]); const [statusesDropdown, setStatusesDropdown] = useState( [], ); const [managerDropdown, setManagerDropdown] = useState( [], ); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(""); const [filterQuery, setFilterQuery] = useState< Partial> >({}); // let statusesDropdown: ComboboxItemProps[] = []; const router = useRouter(); // Project Manager options + the userId -> userName map come from the shared // app-wide cache. The map resolves a project's manager UUID to a display name. const { projectManagerOptions, userNameMap } = useAppCache(); // DataTableLayout has no prop to set the initial view, so once the table is // rendered we click its built-in "Grid View" toggle once to default to cards. const tableContainerRef = useRef(null); const defaultedToGrid = useRef(false); const setDropdownFilters = async (statusDropdown: ComboboxItemProps[]) => { setStatusesDropdown(statusDropdown); }; // ===================== // DataTable Columns // ===================== const columns: ColumnDef[] = [ { accessorKey: "projectName", header: () =>
Project Name
, cell: (row) => { console.log("row", row.projectIdentifier); return ( {row.projectName} ); }, }, { accessorKey: "projectIdentifier", header: () =>
Code
, // cell: ({ row }) => {row.original.projectIdentifier}, }, { accessorKey: "contractedStartDate", header: () =>
Start Date
, // cell: ({ row }) => {row.original.contractedStartDate}, }, { accessorKey: "contractedEndDate", header: () =>
End Date
, // cell: ({ row }) => {row.original.contractedEndDate}, }, { accessorKey: "projectStatus", header: () =>
Status
, // cell: ({ row }) => {row.original.projectStatus}, }, ]; // ===================== // Load projects // ===================== useEffect(() => { const loadProjects = async () => { try { const data = await getProjectsApi(); if (Array.isArray(data)) { setProjects(data); } else { console.error("Failed to fetch projects, received:", data); setProjects([]); } } finally { setLoading(false); } }; const loadManagerData = async () => { const projectManagerMemberIds = await findUsersInRole("Project manager"); const { nameMap } = await createUserMaps(); const dropdown: ComboboxItemProps[] = [ { label: "All Managers", value: "All Managers" }, ]; projectManagerMemberIds?.map((id) => { const userName = nameMap.get(`${id}`); dropdown.push({ label: userName, value: id }); }); setManagerDropdown(dropdown); }; loadManagerData(); loadProjects(); }, []); // Switch the table into grid (card) view as soon as it mounts, once. useEffect(() => { if (loading || defaultedToGrid.current) return; const gridButton = tableContainerRef.current?.querySelector( 'button[title="Grid View"]', ); if (gridButton) { gridButton.click(); defaultedToGrid.current = true; } }, [loading]); // Build the manager filter dropdown from the cached Project Manager options. useEffect(() => { const dropdown: ComboboxItemProps[] = [ { label: "All Managers", value: "All Managers" }, ]; projectManagerOptions.forEach((opt) => { dropdown.push({ label: opt.label, value: opt.value }); }); setManagerDropdown(dropdown); }, [projectManagerOptions]); useEffect(() => { const statuses: string[] = ["Active", "Ongoing", "Completed"]; const statusDropdown: ComboboxItemProps[] = [ { label: "All Statuses", value: "All Statuses" }, ]; // projects.map((project) => { // if (!statuses.includes(project.projectStatus)) { // statuses.push(project.projectStatus); // } // }); statuses.map((status) => statusDropdown.push({ label: status, value: status }), ); setDropdownFilters(statusDropdown); }, [projects]); // ===================== // Create project // ===================== const handleCreateProject = async (data: ProjectFormData) => { try { console.log("Project data------", data); const response = await createProjectApi(data); console.log("Project payload-----", response); setOpen(false); const refreshed = await getProjectsApi(); if (Array.isArray(refreshed)) { setProjects(refreshed); } } catch (error) { console.error("Failed to create project", error); } }; // ===================== // Search filter // ===================== const filteredProjects = useMemo(() => { if (!search.trim() && Object.keys(filterQuery).length === 0) return projects; return projects.filter((project: Project) => { const searchMatch = project.projectName ?.toLowerCase() .includes(search.toLowerCase()); const filterMatch = Object.entries(filterQuery).every(([key, value]) => { // Check if the project matches the filter query return value ? project[key] === value : true; }); return searchMatch && filterMatch; }); }, [projects, search, filterQuery]); const onFilterChange = (key: string, value: string) => { setFilterQuery((prev) => { // "All …" entries are show-everything sentinels, not real filter values — // map them to "" so they don't get matched against real fields (e.g. a UUID // projectManager). Covers "All Statuses", "All Managers", etc. const isSentinel = value.startsWith("All "); return { ...prev, [key]: isSentinel || value === "All Managers" ? "" : value, }; }); }; return (
{/* ================= HEADER ================= */}

Projects

{filteredProjects.length} of {projects.length} projects

{/* ================= CONTENT ================= */} {loading ? (
Loading projects...
) : ( <> {/* LIST VIEW */}
{ console.log("row", row); return row.projectIdentifier; }, onRowClick: (row) => { router.push( `/main/planning/projects/${row.projectIdentifier}`, ); }, totalPages: 0, currentPage: 0, actionNode: (
{/* Search */}
setSearch(e.target.value)} />
{/* Status Filter */} {/* */}
{ if (Array.isArray(value)) { onFilterChange("projectStatus", value[0]); } else { onFilterChange("projectStatus", value); } }} /> {/* Manager Filter */} {/* */} { if (Array.isArray(value)) { onFilterChange("projectManager", value[0]); } else { onFilterChange("projectManager", value); } }} />
{/* Source Filter */} {/* */} {/* View Toggle */} {/*
*/}
), gridComponent: (data: Project[]) => (
{data && data.length > 0 && data.map((project, index) => { const cardProps = mapProjectToCard(project); const resolved = userNameMap.get( project.projectManager, ); // TEMP diagnostic: shows whether the manager UUID resolves to a name. console.log("[manager check]", { project: project.projectName, managerId: project.projectManager, mapHasId: userNameMap.has(project.projectManager), resolvedName: resolved, mapSize: userNameMap.size, }); const managerName = resolved ?? cardProps.projectManager; return ( ); })}
), }} />
)} {/* ================= MODAL ================= */}
); }