"use client"; import Card from "@mui/material/Card"; import CardContent from "@mui/material/CardContent"; import Grid from "@mui/material/Grid"; import Typography from "@mui/material/Typography"; import { useTranslation } from "react-i18next"; import TransferList from "../TransferList"; import Button from "@mui/material/Button"; import React, { SyntheticEvent, useCallback, useMemo, useState } from "react"; import CardActions from "@mui/material/CardActions"; import RestartAlt from "@mui/icons-material/RestartAlt"; import FormControl from "@mui/material/FormControl"; import Select, { SelectChangeEvent } from "@mui/material/Select"; import MenuItem from "@mui/material/MenuItem"; import InputLabel from "@mui/material/InputLabel"; import { Task, TaskTemplate } from "@/app/api/tasks"; import { useFormContext } from "react-hook-form"; import { CreateProjectInputs, ManhourAllocation } from "@/app/api/projects/actions"; import isNumber from "lodash/isNumber"; import intersectionWith from "lodash/intersectionWith"; import { difference } from "lodash"; import { Autocomplete, TextField } from "@mui/material"; interface Props { allTasks: Task[]; taskTemplates: TaskTemplate[]; isActive: boolean; } const TaskSetup: React.FC = ({ allTasks: tasks, taskTemplates, isActive, }) => { const { t } = useTranslation(); const { setValue, watch, clearErrors, setError, formState: { defaultValues } } = useFormContext(); const currentTaskGroups = watch("taskGroups"); const currentTaskIds = Object.values(currentTaskGroups).reduce( (acc, group) => { return group.taskIds && group.taskIds.length > 0 ? [...acc, ...group.taskIds] : acc; }, [], ); const onReset = useCallback(() => { setValue("taskGroups", {}); }, [setValue]); const [selectedTaskTemplateId, setSelectedTaskTemplateId] = useState< "All" | number >(watch("taskTemplateId") ?? "All"); const onSelectTaskTemplate = useCallback( (event: SyntheticEvent, value: NonNullable<{id: number | string, name: string}>) => { if (value.id === "All" || isNumber(value.id)) { setSelectedTaskTemplateId(value.id); // onReset(); } }, [onReset], ); const items = useMemo(() => { const selectedTaskTemplate = taskTemplates.find( (template) => template.id === selectedTaskTemplateId, ) if (selectedTaskTemplateId !== "All" && selectedTaskTemplateId !== watch("taskTemplateId")) { // update the "manhour allocation by grade" by task template const updatedManhourPercentageByGrade: ManhourAllocation = watch("manhourPercentageByGrade") selectedTaskTemplate?.gradeAllocations.forEach((gradeAllocation) => { updatedManhourPercentageByGrade[gradeAllocation.grade.id] = gradeAllocation?.percentage }) setValue("manhourPercentageByGrade", updatedManhourPercentageByGrade) if (Object.values(updatedManhourPercentageByGrade).reduce((acc, value) => acc + value, 0) === 100) clearErrors("manhourPercentageByGrade") else setError("manhourPercentageByGrade", { message: "manhourPercentageByGrade value is not valid", type: "invalid" }) // update the "manhour allocation by grade by stage" by task template const updatedTaskGroups = watch("taskGroups") const taskGroupsKeys = Object.keys(updatedTaskGroups) selectedTaskTemplate?.groupAllocations.forEach((groupAllocation) => { const taskGroupId = groupAllocation.taskGroup.id if (taskGroupsKeys.includes(taskGroupId.toString())) { updatedTaskGroups[taskGroupId] = { ...updatedTaskGroups[taskGroupId], percentAllocation: groupAllocation?.percentage } } }) const percentageToZeroGroupIds = difference(taskGroupsKeys.map(key => parseFloat(key)), selectedTaskTemplate?.groupAllocations.map(groupAllocation => groupAllocation.taskGroup.id)!!) percentageToZeroGroupIds.forEach((percentageToZeroGroupId) => { updatedTaskGroups[percentageToZeroGroupId] = { ...updatedTaskGroups[percentageToZeroGroupId], percentAllocation: 0 } }) setValue("taskGroups", updatedTaskGroups) if (Object.values(updatedTaskGroups).reduce((acc, value) => acc + value.percentAllocation, 0) === 100) clearErrors("taskGroups") else setError("taskGroups", { message: "Task Groups value is not invalid", type: "invalid" }) } setValue("taskTemplateId", selectedTaskTemplateId) const taskList = selectedTaskTemplateId === "All" ? tasks : selectedTaskTemplate?.tasks || tasks; return taskList.map((t) => ({ id: t.id, label: t.name, group: t.taskGroup, })); }, [tasks, selectedTaskTemplateId, taskTemplates]); const selectedItems = useMemo(() => { return intersectionWith( tasks, currentTaskIds, (task, taskId) => task.id === taskId, ).map((t) => ({ id: t.id, label: t.name, group: t.taskGroup })); }, [currentTaskIds, tasks]); return ( {t("Task List Setup")} taskTemplate.id === selectedTaskTemplateId)} options={[{id: "All", name: t("All tasks")}, ...taskTemplates.map(taskTemplate => ({id: taskTemplate.id, name: taskTemplate.name}))]} getOptionLabel={(taskTemplate) => taskTemplate.name} isOptionEqualToValue={(option, value) => option?.id === value?.id} renderOption={(params, option) => { return ( {option.name} ); }} onChange={onSelectTaskTemplate} renderInput={(params) => } /> { const newTaskGroups = selectedTasks.reduce< CreateProjectInputs["taskGroups"] >((acc, item) => { if (!item.group) { // TODO: this should not happen (all tasks are part of a group) return acc; } if (!acc[item.group.id]) { return { ...acc, [item.group.id]: { taskIds: [item.id], percentAllocation: currentTaskGroups[item.group.id]?.percentAllocation || 0, }, }; } return { ...acc, [item.group.id]: { ...acc[item.group.id], taskIds: [...acc[item.group.id].taskIds, item.id], }, }; }, {}); // update the "manhour allocation by grade by stage" by task template const taskGroupsKeys = Object.keys(newTaskGroups) const selectedTaskTemplate = taskTemplates.find( (template) => template.id === selectedTaskTemplateId, ) selectedTaskTemplate?.groupAllocations.forEach((groupAllocation) => { const taskGroupId = groupAllocation.taskGroup.id if (taskGroupsKeys.includes(taskGroupId.toString())) { newTaskGroups[taskGroupId] = { ...newTaskGroups[taskGroupId], percentAllocation: groupAllocation?.percentage } } }) const percentageToZeroGroupIds = difference(taskGroupsKeys.map(key => parseFloat(key)), selectedTaskTemplate?.groupAllocations.map(groupAllocation => groupAllocation.taskGroup.id)!!) percentageToZeroGroupIds.forEach((percentageToZeroGroupId) => { newTaskGroups[percentageToZeroGroupId] = { ...newTaskGroups[percentageToZeroGroupId], percentAllocation: 0 } }) setValue("taskGroups", newTaskGroups) if (Object.values(newTaskGroups).reduce((acc, value) => acc + value.percentAllocation, 0) === 100) clearErrors("taskGroups") else setError("taskGroups", { message: "Task Groups value is not invalid", type: "invalid" }) }} allItemsLabel={t("Task Pool")} selectedItemsLabel={t("Project Task List")} /> {/* */} ); }; export default TaskSetup;