diff --git a/src/frontend/src/pages/GanttPage/GanttChart/GanttChartCollectionSection.tsx b/src/frontend/src/pages/GanttPage/GanttChart/GanttChartCollectionSection.tsx index c4630412a8..5223fdc373 100644 --- a/src/frontend/src/pages/GanttPage/GanttChart/GanttChartCollectionSection.tsx +++ b/src/frontend/src/pages/GanttPage/GanttChart/GanttChartCollectionSection.tsx @@ -1,8 +1,8 @@ import { Edit } from '@mui/icons-material'; import { Box, Chip, IconButton, Typography, useTheme } from '@mui/material'; import GanttChartSection from './GanttChartSection'; -import { GanttCollection } from '../../../utils/gantt.utils'; -import { useState } from 'react'; +import { GanttCollection, GanttTask } from '../../../utils/gantt.utils'; +import { useCallback, useState } from 'react'; import { GanttEditability } from './GanttChart'; interface GanttChartCollectionSectionProps { @@ -57,9 +57,14 @@ const GanttChartCollectionSection = ({ setIsEditMode(true); }; - const ignore = () => {}; + // Sorting the work packages of each project based on their start date + collection.tasks.forEach((task) => { + task.children.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()); + }); - const ignoreBool = () => false; + const ignore = useCallback(() => {}, []); + + const ignoreBool = useCallback(() => false, []); return ( diff --git a/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBar.tsx b/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBar.tsx index 7b281698a1..2a1f8fac7a 100644 --- a/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBar.tsx +++ b/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBar.tsx @@ -5,6 +5,10 @@ import GanttTaskBarEdit from './GanttTaskBarEdit'; import GanttTaskBarView from './GanttTaskBarView'; +import { ArcherContainer } from 'react-archer'; +import { useCallback } from 'react'; +import { useRef } from 'react'; +import { ArcherContainerHandle } from 'react-archer/lib/ArcherContainer/ArcherContainer.types'; import { GanttChange, GanttTask, @@ -42,18 +46,27 @@ const GanttTaskBar = ({ highlightTaskComparator, onToggle }: GanttTaskBarProps) => { - const getStartCol = (start: Date) => { - const startCol = days.findIndex((day) => toDateString(day) === toDateString(getMonday(start))) + 1; - return startCol; - }; + const archerRef = useRef(null); - const getEndCol = (end: Date) => { - const endCol = - days.findIndex((day) => toDateString(day) === toDateString(getMonday(end))) === -1 - ? days.length + 1 - : days.findIndex((day) => toDateString(day) === toDateString(getMonday(end))) + 2; - return endCol; - }; + const getStartCol = useCallback( + (start: Date) => { + const startCol = days.findIndex((day) => dateToString(day) === dateToString(getMonday(start))) + 1; + return startCol; + }, + [days] + ); + + // if the end date doesn't exist within the timeframe, have it span to the end + const getEndCol = useCallback( + (end: Date) => { + const endCol = + days.findIndex((day) => dateToString(day) === dateToString(getMonday(end))) === -1 + ? days.length + 1 + : days.findIndex((day) => dateToString(day) === dateToString(getMonday(end))) + 2; + return endCol; + }, + [days] + ); return (
diff --git a/src/frontend/src/pages/GanttPage/ProjectGanttChart/ProjectGanttChartPage.tsx b/src/frontend/src/pages/GanttPage/ProjectGanttChart/ProjectGanttChartPage.tsx index 427f8d1304..003707a2b1 100644 --- a/src/frontend/src/pages/GanttPage/ProjectGanttChart/ProjectGanttChartPage.tsx +++ b/src/frontend/src/pages/GanttPage/ProjectGanttChart/ProjectGanttChartPage.tsx @@ -3,7 +3,7 @@ * See the LICENSE file in the repository root folder for details. */ -import React, { ChangeEvent, FC, useEffect, useState } from 'react'; +import React, { ChangeEvent, FC, useEffect, useState, useCallback, useMemo } from 'react'; import LoadingIndicator from '../../../components/LoadingIndicator'; import { useAllProjectsGantt } from '../../../hooks/projects.hooks'; import ErrorPage from '../../ErrorPage'; @@ -55,15 +55,13 @@ import { v4 as uuidv4 } from 'uuid'; import { projectWbsPipe } from '../../../utils/pipes'; import { projectGanttTransformer } from '../../../apis/transformers/projects.transformers'; import { useCurrentUser } from '../../../hooks/users.hooks'; +import { all } from 'axios'; const getElementId = (element: WbsElementPreview | Task) => { return (element as WbsElementPreview).id ?? (element as Task).taskId; }; const ProjectGanttChartPage: FC = () => { - const history = useHistory(); - const toast = useToast(); - const { isLoading: projectsIsLoading, isError: projectsIsError, @@ -71,6 +69,7 @@ const ProjectGanttChartPage: FC = () => { error: projectsError } = useAllProjectsGantt(); + /******************** Filters ***************************/ const { isLoading: teamTypesIsLoading, isError: teamTypesIsError, @@ -80,23 +79,44 @@ const ProjectGanttChartPage: FC = () => { const { isLoading: carsIsLoading, isError: carsIsError, data: cars, error: carsError } = useGetAllCars(); const { isLoading: teamsIsLoading, isError: teamsIsError, data: teams, error: teamsError } = useAllTeams(); + + if ( + projectsIsLoading || + teamTypesIsLoading || + teamsIsLoading || + !teams || + !projects || + !teamTypes || + carsIsLoading || + !cars + ) + return ; + + if (projectsIsError) return ; + if (teamTypesIsError) return ; + if (teamsIsError) return ; + if (carsIsError) return ; + + return ; +}; + +interface ProjectGanttChartPageDataProps { + projects: ProjectGantt[]; + teams: TeamPreview[]; + teamTypes: TeamType[]; + cars: { wbsNum: { carNumber: number } }[]; +} + +const ProjectGanttChartPageData: FC = ({ projects, teams, teamTypes, cars }) => { + const history = useHistory(); + const toast = useToast(); + + const { filters, setFilters } = useGanttFilters('project-gantt'); const [searchText, setSearchText] = useState(''); const [addedProjects, setAddedProjects] = useState([]); - const [showAddProjectModal, setShowAddProjectModal] = useState(false); - const [showAddWorkPackageModal, setShowAddWorkPackageModal] = useState(false); - const [showAddTaskModal, setShowAddTaskModal] = useState(false); - const [showSelectionModal, setShowSelectionModal] = useState(false); - const [ganttChanges, setGanttChanges] = useState[]>([]); - const [requestEventChanges, setRequestEventChanges] = useState[]>([]); - const [selectedProject, setSelectedProject] = useState(undefined); - const [selectedTeam, setSelectedTeam] = useState(undefined); - const [collections, setCollections] = useState[]>([]); const [allProjects, setAllProjects] = useState([]); const [editedProjects, setEditedProjects] = useState([]); - const user = useCurrentUser(); - - /******************** Filters ***************************/ - const { filters, setFilters } = useGanttFilters('project-gantt'); + const [collections, setCollections] = useState[]>([]); useEffect(() => { const requestRefresh = ( @@ -132,26 +152,23 @@ const ProjectGanttChartPage: FC = () => { } }, [teams, projects, addedProjects, setAllProjects, setCollections, editedProjects, filters, searchText, history]); + const [showWorkPackagesMap, setShowWorkPackagesMap] = useState>(new Map()); + + const [showAddProjectModal, setShowAddProjectModal] = useState(false); + const [showAddWorkPackageModal, setShowAddWorkPackageModal] = useState(false); + const [showAddTaskModal, setShowAddTaskModal] = useState(false); + const [showSelectionModal, setShowSelectionModal] = useState(false); + const [ganttChanges, setGanttChanges] = useState[]>([]); + const [requestEventChanges, setRequestEventChanges] = useState[]>([]); + const [selectedProject, setSelectedProject] = useState(undefined); + const [selectedTeam, setSelectedTeam] = useState(undefined); + + const user = useCurrentUser(); + const handleSetGanttFilters = (newFilters: GanttFilters) => { setFilters(newFilters); }; - if ( - projectsIsLoading || - teamTypesIsLoading || - teamsIsLoading || - !teams || - !projects || - !teamTypes || - carsIsLoading || - !cars - ) - return ; - if (projectsIsError) return ; - if (teamTypesIsError) return ; - if (teamsIsError) return ; - if (carsIsError) return ; - const carFilterHandler = (car: number) => { return (event: ChangeEvent) => { handleSetGanttFilters( @@ -248,25 +265,25 @@ const ProjectGanttChartPage: FC = () => { /* **************************************************** */ /* ****************** Editability ********************* */ - const handleCancel = (_collection?: GanttCollection) => { + const handleCancel = useCallback((_collection?: GanttCollection) => { //TODO Filter by gantt collection setAddedProjects([]); setEditedProjects([]); setSelectedTeam(undefined); setSelectedProject(undefined); - }; + }, []); - const onAddNewSubtask = (parent: GanttTask) => { + const onAddNewSubtask = useCallback((parent: GanttTask) => { if (isProjectPreview(parent.element)) { setSelectedProject(parent.element); setShowSelectionModal(true); } - }; + }, []); - const onAddNewTask = (collection: GanttCollection) => { + const onAddNewTask = useCallback((collection: GanttCollection) => { setSelectedTeam(collection.element); setShowAddProjectModal(true); - }; + }, []); const handleAddWorkPackageInfo = ( workPackageInfo: { name: string; stage?: WorkPackageStage }, @@ -416,20 +433,23 @@ const ProjectGanttChartPage: FC = () => { setGanttChanges([...ganttChanges, change]); }; - const createChangeHandler = (change: GanttChange) => { - const parentProject = allProjects.find((project) => wbsPipe(project.wbsNum) === projectWbsPipe(change.element.wbsNum)); // Find the project that either the change is on, or the changes work package is a part of - if (!parentProject) return; + const createChangeHandler = useCallback( + (change: GanttChange) => { + const parentProject = allProjects.find((project) => wbsPipe(project.wbsNum) === projectWbsPipe(change.element.wbsNum)); // Find the project that either the change is on, or the changes work package is a part of + if (!parentProject) return; - const { updatedProject } = applyChangesToWBSElement([change], change.element, parentProject); - const addedProject = addedProjects.find((proj) => proj.id === updatedProject.id); - if (addedProject) { - setAddedProjects((prev) => [...prev.filter((project) => project.id !== updatedProject.id), updatedProject]); - } else { - setEditedProjects((prev) => [...prev.filter((project) => project.id !== updatedProject.id), updatedProject]); - } + const { updatedProject } = applyChangesToWBSElement([change], change.element, parentProject); + const addedProject = addedProjects.find((proj) => proj.id === updatedProject.id); + if (addedProject) { + setAddedProjects((prev) => [...prev.filter((project) => project.id !== updatedProject.id), updatedProject]); + } else { + setEditedProjects((prev) => [...prev.filter((project) => project.id !== updatedProject.id), updatedProject]); + } - createChange(change); - }; + createChange(change); + }, + [allProjects, addedProjects] + ); const saveChanges = async () => { try { @@ -579,19 +599,19 @@ const ProjectGanttChartPage: FC = () => { } }; - const highlightProjectComparator = ( - highlightedElement: WbsElementPreview | Task, - wbsElement: WbsElementPreview | Task - ) => { - return projectWbsPipe(highlightedElement.wbsNum) === projectWbsPipe(wbsElement.wbsNum); - }; + const highlightProjectComparator = useCallback( + (highlightedElement: WbsElementPreview | Task, wbsElement: WbsElementPreview | Task) => { + return projectWbsPipe(highlightedElement.wbsNum) === projectWbsPipe(wbsElement.wbsNum); + }, + [] + ); - const highlightWorkPackageComparator = ( - highlightedElement: WbsElementPreview | Task, - wbsElement: WbsElementPreview | Task - ) => { - return wbsPipe(highlightedElement.wbsNum) === wbsPipe(wbsElement.wbsNum); - }; + const highlightWorkPackageComparator = useCallback( + (highlightedElement: WbsElementPreview | Task, wbsElement: WbsElementPreview | Task) => { + return wbsPipe(highlightedElement.wbsNum) === wbsPipe(wbsElement.wbsNum); + }, + [] + ); /* **************************************************** */ @@ -623,6 +643,22 @@ const ProjectGanttChartPage: FC = () => { ) : add(Date.now(), { weeks: 15 }); + const collapseHandler = () => { + allProjects.forEach((project) => { + setShowWorkPackagesMap((prev) => new Map(prev.set(project.id, false))); + }); + }; + + const expandHandler = () => { + allProjects.forEach((project) => { + setShowWorkPackagesMap((prev) => new Map(prev.set(project.id, true))); + }); + }; + + const toggleElementShowChildren = useCallback((element: WbsElementPreview | Task) => { + setShowWorkPackagesMap((prev) => new Map(prev.set(getElementId(element), !prev.get(getElementId(element))))); + }, []); + const headerRight = ( @@ -674,3 +710,9 @@ const ProjectGanttChartPage: FC = () => { }; export default ProjectGanttChartPage; + +/* +function useCallback(arg0: (_collection?: GanttCollection) => void) { + throw new Error('Function not implemented.'); +} +*/