Skip to content

Commit d84e7d3

Browse files
committed
Fix Persistent Filtering Add Dynamic Start Date
1 parent a875c05 commit d84e7d3

11 files changed

Lines changed: 221 additions & 121 deletions

File tree

src/backend/src/controllers/retrospectives.controllers.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import RetrospectiveService from '../services/retrospective.services';
44
export default class RetrospectiveController {
55
static async getRetrospectiveTimelines(req: Request, res: Response, next: NextFunction) {
66
try {
7-
const projects = await RetrospectiveService.getRetrospectiveTimelines(req.organization.organizationId);
7+
const { start, end } = req.query;
8+
const projects = await RetrospectiveService.getRetrospectiveTimelines(
9+
req.organization.organizationId,
10+
start ? new Date(start as string) : undefined,
11+
end ? new Date(end as string) : undefined
12+
);
813
res.json(projects);
914
} catch (error) {
1015
next(error);

src/backend/src/services/retrospective.services.ts

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import { getProjectQueryArgs } from '../prisma-query-args/projects.query-args';
77
import { ProjectPreview, RetrospectiveProjectPreview } from 'shared';
88

99
export default class RetrospectiveService {
10-
static async getRetrospectiveTimelines(organizationId: string): Promise<RetrospectiveProjectPreview[]> {
10+
static async getRetrospectiveTimelines(
11+
organizationId: string,
12+
startDate?: Date,
13+
endDate?: Date
14+
): Promise<RetrospectiveProjectPreview[]> {
1115
const projects = await prisma.project.findMany({
1216
where: { wbsElement: { organizationId } },
1317
...getProjectQueryArgs(organizationId)
@@ -22,20 +26,54 @@ export default class RetrospectiveService {
2226
originalDuration: workPackage.duration,
2327
originalStartDate: workPackage.startDate
2428
};
29+
let minDurationChangeDate: Date | undefined = undefined;
30+
let minStartChangeDate: Date | undefined = undefined;
31+
let maxDurationChangeDate: Date | undefined = undefined;
32+
let maxStartChangeDate: Date | undefined = undefined;
2533
workPackage.wbsElement.changes.forEach((change) => {
26-
if (change.detail.toLowerCase().includes('added duration')) {
34+
if (startDate && change.dateImplemented.getTime() < startDate?.getTime()) return;
35+
36+
if (endDate && change.dateImplemented.getTime() > endDate?.getTime()) return;
37+
38+
if (
39+
change.detail.toLowerCase().includes('Added duration') ||
40+
change.detail.toLowerCase().includes('changed duration')
41+
) {
2742
const split = change.detail.split('"');
2843
if (split.length > 0) {
29-
const originalValueSplit = split[1].split('"');
30-
const [originalValue] = originalValueSplit;
31-
retroWorkpackage.originalDuration = parseFloat(originalValue);
44+
if (!minDurationChangeDate || minDurationChangeDate.getTime() > change.dateImplemented.getTime()) {
45+
const [, originalValue] = split;
46+
retroWorkpackage.originalDuration = parseFloat(originalValue);
47+
minDurationChangeDate = change.dateImplemented;
48+
}
49+
if (
50+
split.length > 3 &&
51+
(!maxDurationChangeDate || maxDurationChangeDate.getTime() < change.dateImplemented.getTime())
52+
) {
53+
const [, , , newValue] = split;
54+
retroWorkpackage.duration = parseFloat(newValue);
55+
maxDurationChangeDate = change.dateImplemented;
56+
}
3257
}
33-
} else if (change.detail.toLowerCase().includes('added start date')) {
58+
} else if (
59+
change.detail.toLowerCase().includes('added start date') ||
60+
change.detail.toLowerCase().includes('changed start date')
61+
) {
3462
const split = change.detail.split('"');
3563
if (split.length > 0) {
36-
const originalValueSplit = split[1].split('"');
37-
const [originalValue] = originalValueSplit;
38-
retroWorkpackage.originalStartDate = new Date(originalValue);
64+
if (!minStartChangeDate || minStartChangeDate.getTime() > change.dateImplemented.getTime()) {
65+
const [, originalValue] = split;
66+
retroWorkpackage.originalStartDate = new Date(originalValue);
67+
minStartChangeDate = change.dateImplemented;
68+
}
69+
if (
70+
split.length > 3 &&
71+
(!maxStartChangeDate || maxStartChangeDate.getTime() < change.dateImplemented.getTime())
72+
) {
73+
const [, , , newValue] = split;
74+
retroWorkpackage.startDate = new Date(newValue);
75+
maxStartChangeDate = change.dateImplemented;
76+
}
3977
}
4078
}
4179
});

src/frontend/src/apis/retrospective.api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import axios from '../utils/axios';
33
import { apiUrls } from '../utils/urls';
44
import { retrospectiveProjectPreviewTransformer } from './transformers/projects.transformers';
55

6-
export const getRetrospectiveTimelines = () => {
7-
return axios.get<RetrospectiveProjectPreview[]>(apiUrls.retrospectiveTimelines(), {
6+
export const getRetrospectiveTimelines = (startDate?: Date, endDate?: Date) => {
7+
return axios.get<RetrospectiveProjectPreview[]>(apiUrls.retrospectiveTimelines(startDate, endDate), {
88
transformResponse: (data) => JSON.parse(data).map(retrospectiveProjectPreviewTransformer)
99
});
1010
};

src/frontend/src/hooks/retrospective.hooks.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { useQuery } from 'react-query';
22
import { RetrospectiveProjectPreview } from 'shared';
33
import { getRetrospectiveBudgets, getRetrospectiveTimelines } from '../apis/retrospective.api';
44

5-
export const useGetRetrospectiveTimelines = () =>
6-
useQuery<RetrospectiveProjectPreview[], Error>(['retrospective-timelines'], async () => {
7-
const { data } = await getRetrospectiveTimelines();
5+
export const useGetRetrospectiveTimelines = (startDate?: Date, endDate?: Date) =>
6+
useQuery<RetrospectiveProjectPreview[], Error>(['retrospective-timelines', startDate, endDate], async () => {
7+
const { data } = await getRetrospectiveTimelines(startDate, endDate);
88
return data;
99
});
1010

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,11 @@ const GanttTaskBarDisplay = <T,>({
132132
height: '2rem',
133133
border: `1px solid ${theme.palette.text.primary}`,
134134
borderRadius: '0.25rem',
135-
backgroundColor: '#ef4345',
136-
opacity: 0.5,
135+
backgroundImage: `
136+
repeating-linear-gradient(-45deg, #000 0, #000 1px, transparent 1px, transparent 10px)
137+
`,
138+
backgroundColor: grey[100],
139+
opacity: 0.3,
137140
cursor: 'pointer',
138141
gridRow: 1,
139142
zIndex: 1

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ interface GanttChartFiltersProps {
8282
overdueHandler: {
8383
filterLabel: string;
8484
handler: (event: ChangeEvent<HTMLInputElement>) => void;
85-
defaultChecked: boolean;
85+
defaultChecked?: boolean;
8686
}[];
8787
resetHandler: () => void;
8888
collapseHandler: () => void;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface GanttChartFiltersButtonProps {
1414
overdueHandler: {
1515
filterLabel: string;
1616
handler: (event: ChangeEvent<HTMLInputElement>) => void;
17-
defaultChecked: boolean;
17+
defaultChecked?: boolean;
1818
}[];
1919
resetHandler: () => void;
2020
collapseHandler: () => void;

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

Lines changed: 24 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ import LoadingIndicator from '../../../components/LoadingIndicator';
88
import { useAllProjects } from '../../../hooks/projects.hooks';
99
import ErrorPage from '../../ErrorPage';
1010
import { add, sub } from 'date-fns';
11-
import { useQuery } from '../../../hooks/utils.hooks';
1211
import { useHistory } from 'react-router-dom';
1312
import {
1413
applyChangesToWBSElement,
15-
buildGanttSearchParams,
1614
constructCollectionsFromTeamPreviewAndProjects,
1715
constructFinalizedChanges,
1816
GanttChange,
@@ -21,7 +19,8 @@ import {
2119
GanttTask,
2220
isProjectPreview,
2321
RequestEventChange,
24-
transformProjectToGanttTask
22+
transformProjectToGanttTask,
23+
useGanttFilters
2524
} from '../../../utils/gantt.utils';
2625
import { routes } from '../../../utils/routes';
2726
import { Box } from '@mui/material';
@@ -52,15 +51,9 @@ import { projectWbsPipe } from '../../../utils/pipes';
5251
import { projectPreviewTransformer } from '../../../apis/transformers/projects.transformers';
5352

5453
const ProjectGanttChartPage: FC = () => {
55-
const query = useQuery();
5654
const history = useHistory();
5755
const toast = useToast();
5856

59-
const ganttParams = localStorage.getItem('ganttURL');
60-
if (ganttParams && history.location.search !== ganttParams) {
61-
history.push(`${history.location.pathname + ganttParams}`);
62-
}
63-
6457
const { isLoading: projectsIsLoading, isError: projectsIsError, data: projects, error: projectsError } = useAllProjects();
6558

6659
const {
@@ -87,20 +80,7 @@ const ProjectGanttChartPage: FC = () => {
8780
const [editedProjects, setEditedProjects] = useState<ProjectPreview[]>([]);
8881

8982
/******************** Filters ***************************/
90-
const showCars = query.getAll('car').map((car) => parseInt(car));
91-
92-
const showTeamTypes = query.getAll('teamType');
93-
94-
const showTeams = query.getAll('team');
95-
96-
const showOnlyOverdue = query.get('overdue') ? query.get('overdue') === 'true' : false;
97-
98-
const [ganttFilters, setGanttFilters] = useState<GanttFilters>({
99-
showCars,
100-
showTeamTypes,
101-
showTeams,
102-
showOnlyOverdue
103-
});
83+
const { filters, setFilters } = useGanttFilters('project-gantt');
10484

10585
useEffect(() => {
10686
const requestRefresh = (
@@ -129,13 +109,16 @@ const ProjectGanttChartPage: FC = () => {
129109
projectPreviewTransformer
130110
)
131111
);
132-
history.push(`${history.location.pathname + buildGanttSearchParams(ganttFilters)}`);
133112
};
134113

135114
if (projects && teams) {
136-
requestRefresh(projects, teams, editedProjects, addedProjects, ganttFilters, searchText);
115+
requestRefresh(projects, teams, editedProjects, addedProjects, filters, searchText);
137116
}
138-
}, [teams, projects, addedProjects, setAllProjects, setCollections, editedProjects, ganttFilters, searchText, history]);
117+
}, [teams, projects, addedProjects, setAllProjects, setCollections, editedProjects, filters, searchText, history]);
118+
119+
const handleSetGanttFilters = (newFilters: GanttFilters) => {
120+
setFilters(newFilters);
121+
};
139122

140123
if (
141124
projectsIsLoading ||
@@ -155,34 +138,33 @@ const ProjectGanttChartPage: FC = () => {
155138

156139
const carFilterHandler = (car: number) => {
157140
return (event: ChangeEvent<HTMLInputElement>) => {
158-
setGanttFilters(
141+
handleSetGanttFilters(
159142
event.target.checked
160-
? { ...ganttFilters, showCars: Array.from(new Set([...ganttFilters.showCars, car])) }
161-
: { ...ganttFilters, showCars: ganttFilters.showCars.filter((c) => c !== car) }
143+
? { ...filters, showCars: Array.from(new Set([...filters.showCars, car])) }
144+
: { ...filters, showCars: filters.showCars.filter((c) => c !== car) }
162145
);
163146
};
164147
};
165148

166149
const teamTypeFilterHandler = (teamType: TeamType) => {
167150
return (event: ChangeEvent<HTMLInputElement>) => {
168-
setGanttFilters(
151+
handleSetGanttFilters(
169152
event.target.checked
170153
? {
171-
...ganttFilters,
172-
showTeamTypes: Array.from(new Set([...ganttFilters.showTeamTypes, teamType.name]))
154+
...filters,
155+
showTeamTypes: Array.from(new Set([...filters.showTeamTypes, teamType.name]))
173156
}
174-
: { ...ganttFilters, showTeamTypes: ganttFilters.showTeamTypes.filter((t) => t !== teamType.name) }
157+
: { ...filters, showTeamTypes: filters.showTeamTypes.filter((t) => t !== teamType.name) }
175158
);
176-
history.push(`${history.location.pathname + buildGanttSearchParams(ganttFilters)}`);
177159
};
178160
};
179161

180162
const teamFilterHandler = (team: TeamPreview) => {
181163
return (event: ChangeEvent<HTMLInputElement>) => {
182-
setGanttFilters(
164+
handleSetGanttFilters(
183165
event.target.checked
184-
? { ...ganttFilters, showTeams: Array.from(new Set([...ganttFilters.showTeams, team.teamName])) }
185-
: { ...ganttFilters, showTeams: ganttFilters.showTeams.filter((t) => t !== team.teamName) }
166+
? { ...filters, showTeams: Array.from(new Set([...filters.showTeams, team.teamName])) }
167+
: { ...filters, showTeams: filters.showTeams.filter((t) => t !== team.teamName) }
186168
);
187169
};
188170
};
@@ -195,7 +177,7 @@ const ProjectGanttChartPage: FC = () => {
195177
return {
196178
filterLabel: teamType.name,
197179
handler: teamTypeFilterHandler(teamType),
198-
defaultChecked: ganttFilters.showTeamTypes.includes(teamType.name)
180+
defaultChecked: filters.showTeamTypes.includes(teamType.name)
199181
};
200182
});
201183

@@ -207,16 +189,16 @@ const ProjectGanttChartPage: FC = () => {
207189
return {
208190
filterLabel: team.teamName,
209191
handler: teamFilterHandler(team),
210-
defaultChecked: ganttFilters.showTeams.includes(team.teamName)
192+
defaultChecked: filters.showTeams.includes(team.teamName)
211193
};
212194
});
213195

214196
const overdueHandler = [
215197
{
216198
filterLabel: 'Overdue',
217199
handler: (event: ChangeEvent<HTMLInputElement>) =>
218-
setGanttFilters({ ...ganttFilters, showOnlyOverdue: event.target.checked }),
219-
defaultChecked: ganttFilters.showOnlyOverdue
200+
handleSetGanttFilters({ ...filters, showOnlyOverdue: event.target.checked }),
201+
defaultChecked: filters.showOnlyOverdue
220202
}
221203
];
222204

@@ -229,7 +211,7 @@ const ProjectGanttChartPage: FC = () => {
229211
return {
230212
filterLabel: carNum === 0 ? 'None' : `Car ${carNum}`,
231213
handler: carFilterHandler(carNum),
232-
defaultChecked: ganttFilters.showCars.includes(carNum)
214+
defaultChecked: filters.showCars.includes(carNum)
233215
};
234216
});
235217

0 commit comments

Comments
 (0)