Skip to content

Commit d4b7943

Browse files
authored
Merge pull request #2653 from Northeastern-Electric-Racing/Display-Blocked-By-Relations
2 parents 6b2068c + d6836ae commit d4b7943

11 files changed

Lines changed: 134 additions & 64 deletions

File tree

src/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"google-auth-library": "^8.1.1",
2727
"pdf-lib": "^1.17.1",
2828
"react": "18.2.0",
29+
"react-archer": "^4.4.0",
2930
"react-dom": "^18.2.0",
3031
"react-draggable": "^4.4.6",
3132
"react-google-charts": "^4.0.0",

src/frontend/src/components/NERAutocomplete.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface NERAutocompleteProps {
2626
listboxProps?: HTMLAttributes<HTMLUListElement>;
2727
filterSelectedOptions?: boolean;
2828
errorMessage?: FieldError;
29+
required?: boolean;
2930
}
3031

3132
const NERAutocomplete: React.FC<NERAutocompleteProps> = ({
@@ -38,7 +39,8 @@ const NERAutocomplete: React.FC<NERAutocompleteProps> = ({
3839
value,
3940
listboxProps,
4041
filterSelectedOptions,
41-
errorMessage
42+
errorMessage,
43+
required = true
4244
}) => {
4345
const theme = useTheme();
4446

@@ -59,7 +61,7 @@ const NERAutocomplete: React.FC<NERAutocompleteProps> = ({
5961
sx: { height: '56px' }
6062
}}
6163
placeholder={placeholder}
62-
required
64+
required={required}
6365
/>
6466
);
6567
};

src/frontend/src/pages/GanttPage/GanttChartComponents/GanttTaskBar/GanttTaskBar.tsx

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { dateToString, getMonday } from '../../../../utils/datetime.utils';
88
import GanttTaskBarEdit from './GanttTaskBarEdit';
99
import GanttTaskBarView from './GanttTaskBarView';
1010
import { WorkPackage } from 'shared';
11+
import { ArcherContainer } from 'react-archer';
12+
import { useRef } from 'react';
13+
import { ArcherContainerHandle } from 'react-archer/lib/ArcherContainer/ArcherContainer.types';
1114

1215
const GanttTaskBar = ({
1316
days,
@@ -35,6 +38,7 @@ const GanttTaskBar = ({
3538
getNewWorkPackageNumber: (projectId: string) => number;
3639
}) => {
3740
const isProject = !task.projectId;
41+
const archerRef = useRef<ArcherContainerHandle>(null);
3842

3943
const getStartCol = (start: Date) => {
4044
const startCol = days.findIndex((day) => dateToString(day) === dateToString(getMonday(start))) + 1;
@@ -50,35 +54,46 @@ const GanttTaskBar = ({
5054
return endCol;
5155
};
5256

57+
const handleChange = (change: GanttChange) => {
58+
createChange(change);
59+
setTimeout(() => {
60+
if (archerRef.current) {
61+
archerRef.current.refreshScreen();
62+
}
63+
}, 100); // wait for the change to be added to the state and the DOM to update
64+
};
65+
5366
return (
54-
<div id={`gantt-task-${task.id}`}>
55-
{isEditMode ? (
56-
<GanttTaskBarEdit
57-
days={days}
58-
task={task}
59-
createChange={createChange}
60-
getStartCol={getStartCol}
61-
getEndCol={getEndCol}
62-
isProject={isProject}
63-
addWorkPackage={addWorkPackage}
64-
getNewWorkPackageNumber={getNewWorkPackageNumber}
65-
/>
66-
) : (
67-
<GanttTaskBarView
68-
days={days}
69-
task={task}
70-
getStartCol={getStartCol}
71-
getEndCol={getEndCol}
72-
isProject={isProject}
73-
handleOnMouseOver={handleOnMouseOver}
74-
handleOnMouseLeave={handleOnMouseLeave}
75-
onWorkPackageToggle={onWorkPackageToggle}
76-
showWorkPackages={showWorkPackages}
77-
highlightedChange={highlightedChange}
78-
getNewWorkPackageNumber={getNewWorkPackageNumber}
79-
/>
80-
)}
81-
</div>
67+
<ArcherContainer ref={archerRef} strokeColor="#ef4545">
68+
<div id={`gantt-task-${task.id}`}>
69+
{isEditMode ? (
70+
<GanttTaskBarEdit
71+
days={days}
72+
task={task}
73+
createChange={handleChange}
74+
getStartCol={getStartCol}
75+
getEndCol={getEndCol}
76+
isProject={isProject}
77+
addWorkPackage={addWorkPackage}
78+
getNewWorkPackageNumber={getNewWorkPackageNumber}
79+
/>
80+
) : (
81+
<GanttTaskBarView
82+
days={days}
83+
task={task}
84+
getStartCol={getStartCol}
85+
getEndCol={getEndCol}
86+
isProject={isProject}
87+
handleOnMouseOver={handleOnMouseOver}
88+
handleOnMouseLeave={handleOnMouseLeave}
89+
onWorkPackageToggle={onWorkPackageToggle}
90+
showWorkPackages={showWorkPackages}
91+
highlightedChange={highlightedChange}
92+
getNewWorkPackageNumber={getNewWorkPackageNumber}
93+
/>
94+
)}
95+
</div>
96+
</ArcherContainer>
8297
);
8398
};
8499

src/frontend/src/pages/GanttPage/GanttChartComponents/GanttTaskBar/GanttTaskBarDisplay.tsx

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
webKitBoxStyles
1919
} from './GanttTaskBarDisplayStyles';
2020
import { CSSProperties } from 'react';
21+
import { ArcherElement } from 'react-archer';
2122

2223
interface GanttTaskBarDisplayProps {
2324
days: Date[];
@@ -87,7 +88,6 @@ const GanttTaskBarDisplay = ({
8788
};
8889

8990
const highlightedChangeBoxStyles = (highlightedChange: RequestEventChange): CSSProperties => {
90-
console.log('highlightedChange', highlightedChange);
9191
return {
9292
paddingTop: '2px',
9393
paddingLeft: '5px',
@@ -104,18 +104,30 @@ const GanttTaskBarDisplay = ({
104104
};
105105

106106
return (
107-
<Box style={ganttTaskBarContainerStyles()}>
107+
<div id={task.teamName + wbsPipe(task)} style={ganttTaskBarContainerStyles()}>
108108
<Box sx={ganttTaskBarBackgroundStyles(days.length)}>
109-
<div
110-
style={ganttTaskBarHoverDetectionBoxStyles}
111-
onMouseOver={(e) => handleOnMouseOver(e, task)}
112-
onMouseLeave={handleOnMouseLeave}
113-
onClick={() => history.push(`${routes.PROJECTS}/${task.id}`)}
109+
<ArcherElement
110+
id={task.teamName + wbsPipe(task)}
111+
relations={task.blocking.map((blocking) => {
112+
return {
113+
targetId: task.teamName + wbsPipe(blocking),
114+
targetAnchor: 'left',
115+
sourceAnchor: 'right',
116+
style: { strokeDasharray: '5,5', noCurves: true, endMarker: false }
117+
};
118+
})}
114119
>
115-
<Box sx={webKitBoxContainerStyles()}>
116-
<Box sx={webKitBoxStyles()} />
117-
</Box>
118-
</div>
120+
<div
121+
style={ganttTaskBarHoverDetectionBoxStyles}
122+
onMouseOver={(e) => handleOnMouseOver(e, task)}
123+
onMouseLeave={handleOnMouseLeave}
124+
onClick={() => history.push(`${routes.PROJECTS}/${task.id}`)}
125+
>
126+
<Box sx={webKitBoxContainerStyles()}>
127+
<Box sx={webKitBoxStyles()} />
128+
</Box>
129+
</div>
130+
</ArcherElement>
119131
<div
120132
style={ganttTaskBarDetailsBoxStyles}
121133
onMouseOver={(e) => handleOnMouseOver(e, task)}
@@ -163,7 +175,7 @@ const GanttTaskBarDisplay = ({
163175
</div>
164176
)}
165177
</Box>
166-
</Box>
178+
</div>
167179
);
168180
};
169181

src/frontend/src/pages/GanttPage/GanttChartComponents/GanttTaskBar/GanttTaskBarEditView.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import useId from '@mui/material/utils/useId';
33
import { useTheme } from '@mui/system';
44
import { CSSProperties, DragEvent, MouseEvent, useEffect, useState } from 'react';
55
import useMeasure from 'react-use-measure';
6-
import { addDaysToDate, WbsElementStatus, WorkPackage, WorkPackageStage } from 'shared';
6+
import { addDaysToDate, WbsElementStatus, wbsPipe, WorkPackage, WorkPackageStage } from 'shared';
77
import {
88
GanttChange,
99
GanttTask,
@@ -19,6 +19,7 @@ import {
1919
webKitBoxContainerStyles,
2020
webKitBoxStyles
2121
} from './GanttTaskBarDisplayStyles';
22+
import { ArcherElement } from 'react-archer';
2223

2324
interface GanttTaskBarEditProps {
2425
days: Date[];
@@ -207,19 +208,31 @@ export const GanttTaskBarEditView = ({
207208
))}
208209
</Box>
209210
<Box sx={ganttTaskBarBackgroundStyles(days.length)}>
210-
<div ref={measureRef} style={taskBarDisplayStyles}>
211-
<Box sx={webKitBoxContainerStyles()}>
212-
<Box draggable={!isProject} onDrag={onDragStart} onDragEnd={onDragEnd} sx={webKitBoxStyles()}>
213-
<Box sx={{ display: 'flex', flexDirection: 'row' }}>
214-
<Typography variant="body1" sx={taskNameContainerStyles(task)}>
215-
{task.name}
216-
</Typography>
211+
<ArcherElement
212+
id={task.teamName + wbsPipe(task)}
213+
relations={task.blocking.map((blocking) => {
214+
return {
215+
targetId: task.teamName + wbsPipe(blocking),
216+
targetAnchor: 'left',
217+
sourceAnchor: 'right',
218+
style: { strokeDasharray: '5,5', noCurves: true, endMarker: false }
219+
};
220+
})}
221+
>
222+
<div ref={measureRef} style={taskBarDisplayStyles}>
223+
<Box sx={webKitBoxContainerStyles()}>
224+
<Box draggable={!isProject} onDrag={onDragStart} onDragEnd={onDragEnd} sx={webKitBoxStyles()}>
225+
<Box sx={{ display: 'flex', flexDirection: 'row' }}>
226+
<Typography variant="body1" sx={taskNameContainerStyles(task)}>
227+
{task.name}
228+
</Typography>
229+
</Box>
217230
</Box>
218-
</Box>
219231

220-
<Box sx={hoverContainerBoxStyles} onMouseDown={isProject ? undefined : handleMouseDown} />
221-
</Box>
222-
</div>
232+
<Box sx={hoverContainerBoxStyles} onMouseDown={isProject ? undefined : handleMouseDown} />
233+
</Box>
234+
</div>
235+
</ArcherElement>
223236
{isProject && (
224237
<Chip
225238
label={'+'}

src/frontend/src/pages/GanttPage/GanttChartComponents/GanttTaskBar/GanttTaskBarView.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
isHighlightedChangeOnWbsProject
66
} from '../../../../utils/gantt.utils';
77
import { Collapse } from '@mui/material';
8-
98
import GanttTaskBar from './GanttTaskBar';
109
import BlockedGanttTaskView from './BlockedTaskBarView';
1110
import { wbsPipe } from 'shared';

src/frontend/src/pages/GanttPage/GanttChartTeamSection.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ const GanttChartTeamSection = ({
5959

6060
useEffect(() => {
6161
if (!IsProjectPreviewsEqual(projectsState, deeplyCopiedProjects.concat(addedProjects))) {
62-
console.log('Projects state changed');
6362
setProjectsState([...deeplyCopiedProjects, ...addedProjects]);
6463
}
6564
}, [addedProjects, projectsState, deeplyCopiedProjects]);

src/frontend/src/pages/WorkPackageDetailPage/WorkPackagePage.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ const WorkPackagePage: React.FC<WorkPackagePageProps> = ({ wbsNum }) => {
3030
return <EditWorkPackageForm wbsNum={wbsNum} workPackageName={data?.name} setPageMode={setEditMode} />;
3131
}
3232

33-
console.log(data.startDate);
34-
3533
return (
3634
<WorkPackageViewContainer
3735
workPackage={data}

src/frontend/src/pages/WorkPackageForm/WorkPackageFormDetails.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ const WorkPackageFormDetails: React.FC<Props> = ({
145145
size="small"
146146
placeholder="Select a Project Lead"
147147
value={userToOption(usersForLead.find((user) => user.userId.toString() === lead))}
148+
required={false}
148149
/>
149150
</Grid>
150151
<Grid item xs={12} md={6}>
@@ -157,6 +158,7 @@ const WorkPackageFormDetails: React.FC<Props> = ({
157158
size="small"
158159
placeholder="Select a Project Manager"
159160
value={userToOption(usersForManager.find((user) => user.userId.toString() === manager))}
161+
required={false}
160162
/>
161163
</Grid>
162164
</>

src/frontend/src/utils/gantt.utils.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,20 +196,21 @@ export const applyChangesToWBSElement = (
196196
if (isWorkPackage(wbsElement.wbsNum)) {
197197
// If its a work package were gonna loop through and see if we need to apply changes
198198
const workPackage = wbsElement as WorkPackage;
199-
ganttChanges.forEach((change) => {
199+
for (const change of ganttChanges) {
200200
if (wbsPipe(change.element.wbsNum) === wbsPipe(wbsElement.wbsNum)) {
201201
// If the change is for this work package then were gonna apply it
202-
if (change.type === 'change-end-date') {
202+
if (change.type === 'create-work-package') {
203+
break; // We dont want to apply the changes to a new work package because the changes get tracked already when the user edit the created work package
204+
} else if (change.type === 'change-end-date') {
203205
workPackage.endDate = change.newEnd;
204206
} else if (change.type === 'shift-by-days') {
205-
const newStartDate = dayjs(workPackage.startDate).add(change.days, 'day').toDate();
206-
workPackage.startDate = newStartDate;
207+
workPackage.startDate = dayjs(workPackage.startDate).add(change.days, 'day').toDate();
207208
workPackage.endDate = dayjs(workPackage.endDate).add(change.days, 'day').toDate();
208209
}
209210

210211
applyChangesToBlockedBy(workPackage, parentProject.workPackages, change); // Apply the changes to all of the blocked work packages
211212
}
212-
});
213+
}
213214
}
214215

215216
if (isProject(wbsElement.wbsNum)) {
@@ -282,7 +283,6 @@ export const filterGanttProjects = (
282283

283284
deepCopy = deepCopy.filter((project) => getProjectEndDate(project).getFullYear() !== 1969); // Filter out projects with no end date
284285

285-
console.log(deepCopy, team.teamName);
286286
return deepCopy;
287287
};
288288

0 commit comments

Comments
 (0)