"use client"; import { useTranslation } from "react-i18next"; import React, { useEffect, useMemo } from "react"; import RestartAlt from "@mui/icons-material/RestartAlt"; import SearchResults, { Column } from "../SearchResults"; import { Search, Clear, PersonAdd, PersonRemove } from "@mui/icons-material"; import { Stack, Typography, Grid, TextField, InputAdornment, IconButton, FormControl, InputLabel, Select, MenuItem, Box, Button, Card, CardActions, CardContent, TabsProps, Tab, Tabs, SelectChangeEvent, } from "@mui/material"; import differenceWith from "lodash/differenceWith"; import intersectionWith from "lodash/intersectionWith"; import uniq from "lodash/uniq"; import ResourceCapacity from "./ResourceCapacity"; import { useFormContext } from "react-hook-form"; import { CreateProjectInputs } from "@/app/api/projects/actions"; import ResourceAllocation from "./ResourceAllocation"; import { Task } from "@/app/api/tasks"; import { Grade } from "@/app/api/grades"; import { StaffResult } from "@/app/api/staff"; const staffComparator = (a: StaffResult, b: StaffResult) => { return ( a.team?.localeCompare(b.team) || a.grade?.localeCompare(b.grade) || a.id - b.id ); }; export interface Props { allStaffs: StaffResult[]; isActive: boolean; defaultManhourBreakdownByGrade?: { [gradeId: number]: number }; allTasks: Task[]; grades: Grade[]; } const StaffAllocation: React.FC = ({ allStaffs, allTasks, isActive, defaultManhourBreakdownByGrade, grades, }) => { const { t } = useTranslation(); const { setValue, getValues, watch } = useFormContext(); const [filteredStaff, setFilteredStaff] = React.useState( allStaffs.sort(staffComparator), ); const selectedStaffIds = watch("allocatedStaffIds"); // Adding / Removing staff const addStaff = React.useCallback( (staff: StaffResult) => { const currentStaffIds = getValues("allocatedStaffIds"); setValue("allocatedStaffIds", [...currentStaffIds, staff.id]); }, [getValues, setValue], ); const removeStaff = React.useCallback( (staff: StaffResult) => { const currentStaffIds = getValues("allocatedStaffIds"); setValue( "allocatedStaffIds", currentStaffIds.filter((id) => id !== staff.id), ); }, [getValues, setValue], ); const clearStaff = React.useCallback(() => { setValue("allocatedStaffIds", []); }, [setValue]); const staffPoolColumns = React.useMemo[]>( () => [ { label: t("Add"), name: "id", onClick: addStaff, buttonIcon: , }, { label: t("Team"), name: "team" }, { label: t("Grade"), name: "grade" }, { label: t("Staff ID"), name: "staffId" }, { label: t("Staff Name"), name: "name" }, { label: t("Title"), name: "currentPosition" }, ], [addStaff, t], ); const allocatedStaffColumns = React.useMemo[]>( () => [ { label: t("Remove"), name: "id", onClick: removeStaff, buttonIcon: , }, { label: t("Team"), name: "team" }, { label: t("Grade"), name: "grade" }, { label: t("Staff ID"), name: "id" }, { label: t("Staff Name"), name: "name" }, { label: t("Title"), name: "currentPosition" }, ], [removeStaff, t], ); // Query related const [query, setQuery] = React.useState(""); const onQueryInputChange = React.useCallback< React.ChangeEventHandler >((e) => { setQuery(e.target.value); }, []); const clearQueryInput = React.useCallback(() => { setQuery(""); }, []); const columnFilters = React.useMemo<(keyof StaffResult)[]>( () => ["team", "grade"], [], ); const filterValues = React.useMemo(() => { return columnFilters.reduce<{ [filter in keyof StaffResult]?: string[] }>( (acc, filter) => { return { ...acc, [filter]: uniq(allStaffs.map((staff) => staff[filter])), }; }, {}, ); }, [columnFilters, allStaffs]); const defaultFilterValues = React.useMemo(() => { return columnFilters.reduce<{ [filter in keyof StaffResult]?: string }>( (acc, filter) => { return { ...acc, [filter]: "All" }; }, {}, ); }, [columnFilters]); const [filters, setFilters] = React.useState(defaultFilterValues); const makeFilterSelect = React.useCallback( (filter: keyof StaffResult) => (event: SelectChangeEvent) => { setFilters((f) => ({ ...f, [filter]: event.target.value })); }, [], ); useEffect(() => { setFilteredStaff( allStaffs.filter((staff) => { const q = query.toLowerCase(); return ( (staff.name.toLowerCase().includes(q) || staff.id.toString().includes(q) || staff.currentPosition.toLowerCase().includes(q)) && Object.entries(filters).every(([filterKey, filterValue]) => { const staffColumnValue = staff[filterKey as keyof StaffResult]; return staffColumnValue === filterValue || filterValue === "All"; }) ); }), ); }, [allStaffs, filters, query]); // Tab related const [tabIndex, setTabIndex] = React.useState(0); const handleTabChange = React.useCallback>( (_e, newValue) => { setTabIndex(newValue); }, [], ); const reset = React.useCallback(() => { clearQueryInput(); clearStaff(); setFilters(defaultFilterValues); }, [clearQueryInput, clearStaff, defaultFilterValues]); return ( <> {t("Staff Allocation")} ), }} /> {columnFilters.map((filter, idx) => { const label = staffPoolColumns.find( (c) => c.name === filter, )!.label; return ( {label} ); })} {tabIndex === 0 && ( staff.id === staffId, )} columns={staffPoolColumns} /> )} {tabIndex === 1 && ( staff.id === staffId, )} columns={allocatedStaffColumns} /> )} {/* MUI X-Grid will throw an error if it is rendered without any dimensions; so not rendering when not active */} {isActive && ( )} ); }; export default StaffAllocation;