Skip to content

Commit 4db880e

Browse files
authored
Merge pull request #4035 from Northeastern-Electric-Racing/#3909-Gantt-Page-Filtering
#3909 gantt page filtering
2 parents 5cc0b31 + c6e43c1 commit 4db880e

8 files changed

Lines changed: 156 additions & 229 deletions

File tree

src/backend/src/prisma-query-args/teams.query-args.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const getTeamPreviewQueryArgs = (organizationId: string) =>
3838
include: {
3939
members: getUserQueryArgs(organizationId),
4040
head: getUserQueryArgs(organizationId),
41-
leads: getUserQueryArgs(organizationId)
41+
leads: getUserQueryArgs(organizationId),
42+
teamType: true
4243
}
4344
});

src/backend/src/transformers/teams.transformer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export const teamPreviewTransformer = (team: Prisma.TeamGetPayload<TeamPreviewQu
3838
leads: team.leads.map(userTransformer),
3939
members: team.members.map(userTransformer),
4040
head: userTransformer(team.head),
41-
dateArchived: team.dateArchived ?? undefined
41+
dateArchived: team.dateArchived ?? undefined,
42+
teamType: team.teamType ? teamTypeTransformer(team.teamType) : undefined
4243
};
4344
};
4445

src/frontend/src/pages/GanttPage/GanttChart/GanttChartCollectionSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const GanttChartCollectionSection = <E, T>({
7070

7171
const ignoreBool = () => false;
7272

73-
return (
73+
return collection.tasks.length > 0 ? (
7474
<Box sx={collectionSectionBackgroundStyle}>
7575
<Box sx={collectionDescriptionContainerStyle}>
7676
<Typography variant="h6" fontWeight={400}>
@@ -108,7 +108,7 @@ const GanttChartCollectionSection = <E, T>({
108108
/>
109109
</Box>
110110
</Box>
111-
);
111+
) : null;
112112
};
113113

114114
export default GanttChartCollectionSection;

src/frontend/src/pages/GanttPage/GanttChart/GanttChartSection.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
OnMouseOverOptions,
1212
RequestEventChange
1313
} from '../../../utils/gantt.utils';
14-
import { Box, Typography } from '@mui/material';
14+
import { Box } from '@mui/material';
1515
import { useState } from 'react';
1616
import GanttTaskBar from './GanttChartComponents/GanttTaskBar/GanttTaskBar';
1717
import GanttToolTip from './GanttChartComponents/GanttToolTip';
@@ -64,7 +64,7 @@ const GanttChartSection = <T,>({
6464
setCurrentTooltipOptions(undefined);
6565
};
6666

67-
return tasks.length > 0 ? (
67+
return (
6868
<ArcherContainer strokeColor="#ef4545">
6969
<Box sx={{ width: 'fit-content' }}>
7070
<Box sx={{ mt: '1rem', width: 'fit-content' }}>
@@ -102,8 +102,6 @@ const GanttChartSection = <T,>({
102102
)}
103103
</Box>
104104
</ArcherContainer>
105-
) : (
106-
<Typography sx={{ marginTop: 5 }}>No Projects to Display</Typography>
107105
);
108106
};
109107

src/frontend/src/pages/GanttPage/ProjectGanttChart/GanttChartFilters.tsx

Lines changed: 136 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,86 @@
33
* See the LICENSE file in the repository root folder for details.
44
*/
55

6-
import { Box, Checkbox, Chip, IconButton, Typography, useTheme } from '@mui/material';
7-
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
8-
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
9-
import RestartAltIcon from '@mui/icons-material/RestartAlt';
10-
import { ChangeEvent } from 'react';
11-
12-
const FilterChipButton = ({
13-
buttonText,
14-
onChange,
15-
defaultChecked,
16-
checked
17-
}: {
18-
buttonText: string;
19-
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
20-
defaultChecked: boolean;
21-
checked: boolean;
22-
}) => {
23-
const theme = useTheme();
6+
import {
7+
Box,
8+
Checkbox,
9+
Typography,
10+
useTheme,
11+
Accordion,
12+
AccordionSummary,
13+
AccordionDetails,
14+
FormControlLabel,
15+
IconButton
16+
} from '@mui/material';
17+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
18+
import CloseIcon from '@mui/icons-material/Close';
19+
import { ChangeEvent, useState } from 'react';
2420

25-
return (
26-
<Checkbox
27-
onChange={onChange}
28-
sx={{
29-
'&:hover': {
30-
backgroundColor: 'transparent'
21+
const FilterCheckboxes = ({
22+
handlers
23+
}: {
24+
handlers: { filterLabel: string; handler: (event: ChangeEvent<HTMLInputElement>) => void; defaultChecked: boolean }[];
25+
}) => (
26+
<Box display="flex" flexDirection="column">
27+
{handlers.map((handler) => (
28+
<FormControlLabel
29+
key={handler.filterLabel}
30+
slotProps={{ typography: { fontSize: '14px' } }}
31+
control={
32+
<Checkbox
33+
size="small"
34+
onChange={handler.handler}
35+
defaultChecked={handler.defaultChecked}
36+
sx={{ padding: '2px 7px' }}
37+
/>
3138
}
32-
}}
33-
icon={<Chip label={buttonText} sx={{ borderRadius: '20px', paddingX: 1 }} />}
34-
checkedIcon={
35-
<Chip label={buttonText} sx={{ borderRadius: '20px', paddingX: 1, backgroundColor: theme.palette.primary.main }} />
36-
}
37-
defaultChecked={defaultChecked}
38-
checked={checked}
39-
/>
40-
);
41-
};
39+
label={handler.filterLabel}
40+
/>
41+
))}
42+
</Box>
43+
);
4244

43-
const FilterRow = ({
45+
const FilterAccordion = ({
4446
label,
45-
buttons
47+
children,
48+
expanded,
49+
onChange
4650
}: {
4751
label: string;
48-
buttons: { filterLabel: string; handler: (event: ChangeEvent<HTMLInputElement>) => void; defaultChecked: boolean }[];
52+
children: React.ReactNode;
53+
expanded: boolean;
54+
onChange: () => void;
4955
}) => {
50-
const checkedMap: { [filterLabel: string]: boolean } = {};
56+
const theme = useTheme();
5157

52-
buttons.forEach((button) => {
53-
checkedMap[button.filterLabel] = button.defaultChecked;
54-
});
5558
return (
56-
<Box width={label === 'Team' ? '60%' : undefined}>
57-
<Typography variant="h6" component="label" textAlign="right">
58-
{label}
59-
</Typography>
60-
<Box display={'flex'} flexDirection={label === 'Team' ? undefined : 'column'} flexWrap={'wrap'}>
61-
{buttons.map((button) => (
62-
<FilterChipButton
63-
buttonText={button.filterLabel}
64-
onChange={button.handler}
65-
defaultChecked={button.defaultChecked}
66-
checked={checkedMap[button.filterLabel]}
67-
/>
68-
))}
69-
</Box>
70-
</Box>
59+
<Accordion
60+
disableGutters
61+
elevation={0}
62+
expanded={expanded}
63+
onChange={onChange}
64+
sx={{
65+
backgroundColor: 'transparent',
66+
'&:before': { display: 'none' }
67+
}}
68+
>
69+
<AccordionSummary
70+
expandIcon={<ExpandMoreIcon sx={{ color: theme.palette.text.secondary }} />}
71+
sx={{
72+
minHeight: '36px',
73+
px: 0,
74+
flexDirection: 'row-reverse',
75+
gap: 1,
76+
'& .MuiAccordionSummary-expandIconWrapper': { marginRight: 0, marginLeft: 0 },
77+
'& .MuiAccordionSummary-content': { margin: '6px 0' }
78+
}}
79+
>
80+
<Typography variant="h6" fontWeight="normal" color="text.primary">
81+
{label}
82+
</Typography>
83+
</AccordionSummary>
84+
<AccordionDetails sx={{ padding: '0 0 0 36px' }}>{children}</AccordionDetails>
85+
</Accordion>
7186
);
7287
};
7388

@@ -79,99 +94,88 @@ interface GanttChartFiltersProps {
7994
defaultChecked: boolean;
8095
}[];
8196
teamHandlers: { filterLabel: string; handler: (event: ChangeEvent<HTMLInputElement>) => void; defaultChecked: boolean }[];
82-
overdueHandler: {
83-
filterLabel: string;
84-
handler: (event: ChangeEvent<HTMLInputElement>) => void;
85-
defaultChecked?: boolean;
86-
}[];
87-
hideTasksHandler: {
88-
filterLabel: string;
89-
handler: (event: ChangeEvent<HTMLInputElement>) => void;
90-
defaultChecked?: boolean;
91-
}[];
9297
resetHandler: () => void;
93-
collapseHandler: () => void;
94-
expandHandler: () => void;
98+
onClose: () => void;
9599
}
96100

97101
const GanttChartFilters = ({
98102
carHandlers,
99103
teamTypeHandlers,
100104
teamHandlers,
101-
overdueHandler,
102-
hideTasksHandler,
103105
resetHandler,
104-
collapseHandler,
105-
expandHandler
106+
onClose
106107
}: GanttChartFiltersProps) => {
107-
const FilterButtons = () => {
108-
return (
109-
<Box display={'flex'} flexDirection={'column'} alignItems={'center'} mt={-1} mb={1}>
110-
<IconButton onClick={expandHandler}>
111-
<UnfoldMoreIcon sx={{ color: '#ef4345' }} />
112-
</IconButton>
113-
<Typography fontSize={'10px'} sx={{ color: '#ef4345' }}>
114-
Expand
115-
</Typography>
116-
<IconButton onClick={collapseHandler}>
117-
<UnfoldLessIcon sx={{ color: '#ef4345' }} />
118-
</IconButton>
119-
<Typography fontSize={'10px'} sx={{ color: '#ef4345' }}>
120-
Collapse
108+
const theme = useTheme();
109+
110+
const [expanded, setExpanded] = useState({
111+
car: true,
112+
division: true,
113+
team: true
114+
});
115+
116+
const [resetKey, setResetKey] = useState(0);
117+
118+
const handleReset = () => {
119+
setResetKey((prev) => prev + 1);
120+
resetHandler();
121+
};
122+
123+
const toggle = (section: keyof typeof expanded) => {
124+
setExpanded((prev) => ({ ...prev, [section]: !prev[section] }));
125+
};
126+
127+
return (
128+
<Box
129+
display="flex"
130+
flexDirection="column"
131+
sx={{ padding: 2, width: '20rem', backgroundColor: theme.palette.background.paper, borderRadius: 1 }}
132+
>
133+
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center" sx={{ mb: 0.5 }}>
134+
<Typography variant="h5" fontWeight="normal" color="text.primary">
135+
Filters
121136
</Typography>
122-
<IconButton onClick={resetHandler}>
123-
<RestartAltIcon sx={{ color: '#ef4345' }} />
137+
<IconButton size="small" onClick={onClose}>
138+
<CloseIcon fontSize="small" />
124139
</IconButton>
125-
<Typography fontSize={'10px'} sx={{ color: '#ef4345' }}>
140+
</Box>
141+
142+
<Box display="flex" flexDirection="row" alignItems="center" gap={0.5} sx={{ mb: -0.5 }}>
143+
<Typography
144+
fontSize="14px"
145+
sx={{ color: theme.palette.primary.main, cursor: 'pointer', '&:hover': { textDecoration: 'underline' } }}
146+
onClick={handleReset}
147+
>
126148
Reset
127149
</Typography>
128-
<Checkbox
129-
onChange={overdueHandler[0].handler}
130-
sx={{
131-
'&:hover': {
132-
backgroundColor: 'transparent'
133-
},
134-
color: '#ef4345'
135-
}}
136-
defaultChecked={overdueHandler[0].defaultChecked}
137-
checked={overdueHandler[0].defaultChecked}
138-
/>
139-
<Typography fontSize={'10px'} sx={{ color: '#ef4345' }}>
140-
Overdue
150+
<Typography sx={{ color: theme.palette.text.primary, lineHeight: 2.75, fontSize: '0.5rem' }}></Typography>
151+
<Typography
152+
fontSize="13px"
153+
sx={{ color: theme.palette.primary.main, cursor: 'pointer', '&:hover': { textDecoration: 'underline' } }}
154+
onClick={() => setExpanded({ car: true, division: true, team: true })}
155+
>
156+
Expand All
141157
</Typography>
142-
<Checkbox
143-
onChange={hideTasksHandler[0].handler}
144-
sx={{
145-
'&:hover': {
146-
backgroundColor: 'transparent'
147-
},
148-
color: '#ef4345'
149-
}}
150-
defaultChecked={hideTasksHandler[0].defaultChecked}
151-
checked={hideTasksHandler[0].defaultChecked}
152-
/>
153-
<Typography fontSize={'10px'} sx={{ color: '#ef4345' }}>
154-
Hide Tasks
158+
<Typography sx={{ color: theme.palette.text.primary, lineHeight: 2.75, fontSize: '0.5rem' }}></Typography>
159+
<Typography
160+
fontSize="13px"
161+
sx={{ color: theme.palette.primary.main, cursor: 'pointer', '&:hover': { textDecoration: 'underline' } }}
162+
onClick={() => setExpanded({ car: false, division: false, team: false })}
163+
>
164+
Collapse All
155165
</Typography>
156166
</Box>
157-
);
158-
};
159167

160-
return (
161-
<Box
162-
display={'flex'}
163-
sx={{
164-
justifyContent: 'start',
165-
alignItems: 'start',
166-
paddingLeft: 2,
167-
paddingTop: 2,
168-
maxWidth: '45rem'
169-
}}
170-
>
171-
<FilterRow label="Car" buttons={carHandlers} />
172-
<FilterRow label="Subteam" buttons={teamTypeHandlers} />
173-
<FilterRow label="Team" buttons={teamHandlers} />
174-
<FilterButtons />
168+
<FilterAccordion label="Car" expanded={expanded.car} onChange={() => toggle('car')}>
169+
<FilterCheckboxes key={`car-${resetKey}`} handlers={carHandlers} />
170+
</FilterAccordion>
171+
172+
<FilterAccordion label="Division" expanded={expanded.division} onChange={() => toggle('division')}>
173+
<FilterCheckboxes key={`division-${resetKey}`} handlers={teamTypeHandlers} />
174+
</FilterAccordion>
175+
176+
<FilterAccordion label="Team" expanded={expanded.team} onChange={() => toggle('team')}>
177+
<FilterCheckboxes key={`team-${resetKey}`} handlers={teamHandlers} />
178+
</FilterAccordion>
175179
</Box>
176180
);
177181
};

0 commit comments

Comments
 (0)