Skip to content

Commit 717fd42

Browse files
authored
Merge pull request #173 from don-aot/CONDITIONS-task#155
CR-155:Breadcrumbs should navigate back to Consolidated Conditions if user clicks into a condition from that screen
2 parents d5cb7c4 + 4082e04 commit 717fd42

File tree

5 files changed

+157
-133
lines changed

5 files changed

+157
-133
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { PageGrid } from "@/components/Shared/PageGrid";
2+
import { Grid } from "@mui/material";
3+
import { ConditionModel } from "@/models/Condition";
4+
import { ConsolidatedConditions, ConsolidatedConditionsSkeleton } from ".";
5+
6+
type Props = {
7+
isLoading: boolean;
8+
projectName?: string;
9+
projectId: string;
10+
documentCategory?: string;
11+
documentCategoryId: string;
12+
conditions?: ConditionModel[];
13+
consolidationLevel?: string;
14+
};
15+
16+
export const ConsolidatedConditionsPageContent = ({
17+
isLoading,
18+
projectName,
19+
projectId,
20+
documentCategory,
21+
documentCategoryId,
22+
conditions,
23+
consolidationLevel,
24+
}: Props) => {
25+
if (isLoading) {
26+
return (
27+
<PageGrid>
28+
<Grid item xs={12}>
29+
<ConsolidatedConditionsSkeleton />
30+
</Grid>
31+
</PageGrid>
32+
);
33+
}
34+
35+
return (
36+
<PageGrid>
37+
<Grid item xs={12}>
38+
<ConsolidatedConditions
39+
projectName={projectName}
40+
projectId={projectId}
41+
documentCategory={documentCategory}
42+
documentCategoryId={documentCategoryId}
43+
conditions={conditions}
44+
consolidationLevel={consolidationLevel}
45+
/>
46+
</Grid>
47+
</PageGrid>
48+
);
49+
};

condition-web/src/components/ConsolidatedConditions/index.tsx

Lines changed: 46 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
import { useState, useEffect } from "react";
1+
import { useState, useMemo } from "react";
22
import { useNavigate } from "@tanstack/react-router";
33
import { BCDesignTokens } from "epic.theme";
4-
import { ConditionModel } from "@/models/Condition";
5-
import { Box, Button, FormControlLabel, Grid, styled, Stack, Switch, Typography } from "@mui/material";
4+
import { CONDITION_STATUS, ConditionModel, ConditionStatus } from "@/models/Condition";
5+
import { DocumentStatus } from "@/models/Document";
6+
import { Box, Button, FormControlLabel, Grid, Stack, Switch, Typography, styled } from "@mui/material";
7+
import LayersOutlinedIcon from '@mui/icons-material/LayersOutlined';
8+
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
69
import { ContentBoxSkeleton } from "../Shared/ContentBox/ContentBoxSkeleton";
710
import { ContentBox } from "../Shared/ContentBox";
811
import ConditionTable from "../Conditions/ConditionsTable";
9-
import { DocumentStatus } from "@/models/Document";
1012
import DocumentStatusChip from "../Projects/DocumentStatusChip";
11-
import LayersOutlinedIcon from '@mui/icons-material/LayersOutlined';
12-
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
1313
import ConsolidatedConditionFilters from "@/components/Filters/ConsolidatedConditionFilters";
1414
import { useConditionFilters } from "@/components/Filters/conditionFilterStore";
15-
import { CONDITION_STATUS, ConditionStatus } from "@/models/Condition";
1615
import { useExportConsolidatedConditionsPDF } from "@/hooks/api/useConsolidatedConditions";
1716

1817
export const CardInnerBox = styled(Box)({
@@ -42,40 +41,58 @@ export const ConsolidatedConditions = ({
4241
consolidationLevel
4342
}: ConditionsParam) => {
4443
const navigate = useNavigate();
45-
const [noConditions, setNoConditions] = useState(conditions?.length === 0);
46-
const [allApproved, setAllApproved] = useState(false);
47-
const [hasAmendments, setHasAmendments] = useState(false);
48-
const [isLoading, setIsLoading] = useState(true);
4944
const [isToggled, setIsToggled] = useState(true);
5045

46+
const noConditions = useMemo(() => {
47+
if (!conditions || conditions.length === 0) return true;
48+
if (conditions.length === 1) {
49+
return conditions.some(
50+
(c) => !c.condition_name || !c.condition_number || c.is_approved === null
51+
);
52+
}
53+
return false;
54+
}, [conditions]);
55+
56+
const allApproved = useMemo(
57+
() => conditions?.every((c) => c.is_approved === true) ?? false,
58+
[conditions]
59+
);
60+
61+
const hasAmendments = useMemo(
62+
() => conditions?.some((c) => c.amendment_names != null) ?? false,
63+
[conditions]
64+
);
65+
5166
const { filters } = useConditionFilters();
5267
const { mutate: exportPDF, isPending: isExporting } = useExportConsolidatedConditionsPDF(projectName);
5368

5469
const handleExportPDF = () => exportPDF(projectId);
5570

56-
const filteredConditions = conditions?.filter((condition) => {
57-
const matchesSearch = filters.search_text
58-
? condition.condition_name?.toLowerCase().includes(filters.search_text.toLowerCase()) ?? false
59-
: true;
71+
const filteredConditions = useMemo(() =>
72+
conditions?.filter((condition) => {
73+
const matchesSearch = filters.search_text
74+
? condition.condition_name?.toLowerCase().includes(filters.search_text.toLowerCase()) ?? false
75+
: true;
6076

61-
const matchesSource = filters.source_document
62-
? condition.source_document?.toLowerCase().includes(filters.source_document.toLowerCase()) ?? false
63-
: true;
77+
const matchesSource = filters.source_document
78+
? condition.source_document?.toLowerCase().includes(filters.source_document.toLowerCase()) ?? false
79+
: true;
6480

65-
const matchesAmendment = filters.amendment_names
66-
? condition.amendment_names?.toLowerCase().includes(filters.amendment_names.toLowerCase()) ?? false
67-
: true;
81+
const matchesAmendment = filters.amendment_names
82+
? condition.amendment_names?.toLowerCase().includes(filters.amendment_names.toLowerCase()) ?? false
83+
: true;
6884

69-
const conditionStatus: ConditionStatus = condition.is_approved
70-
? CONDITION_STATUS.true.value
71-
: CONDITION_STATUS.false.value;
85+
const conditionStatus: ConditionStatus = condition.is_approved
86+
? CONDITION_STATUS.true.value
87+
: CONDITION_STATUS.false.value;
7288

73-
const matchesStatus = filters.status && filters.status.length > 0
74-
? filters.status.includes(conditionStatus)
75-
: true;
89+
const matchesStatus = filters.status?.length > 0
90+
? filters.status.includes(conditionStatus)
91+
: true;
7692

77-
return matchesSearch && matchesSource && matchesAmendment && matchesStatus;
78-
});
93+
return matchesSearch && matchesSource && matchesAmendment && matchesStatus;
94+
}),
95+
[conditions, filters]);
7996

8097
const handleToggle = (event: React.ChangeEvent<HTMLInputElement>) => {
8198
const checked = event.target.checked;
@@ -87,31 +104,6 @@ export const ConsolidatedConditions = ({
87104
}
88105
};
89106

90-
useEffect(() => {
91-
// Check if all conditions have status as true
92-
if (conditions && conditions.length > 0) {
93-
const checkIfAllApproved = conditions.every((condition) => condition.is_approved === true);
94-
const conditionHasAmendments = conditions.some(condition => condition.amendment_names != null);
95-
96-
const invalidConditions =
97-
conditions.length === 1 &&
98-
conditions.some(
99-
(condition) =>
100-
!condition.condition_name ||
101-
!condition.condition_number ||
102-
condition.is_approved === null
103-
);
104-
setNoConditions(invalidConditions);
105-
setAllApproved(checkIfAllApproved);
106-
setHasAmendments(conditionHasAmendments);
107-
}
108-
setIsLoading(false);
109-
}, [conditions]);
110-
111-
if (isLoading) {
112-
return <div>Loading...</div>;
113-
}
114-
115107
return (
116108
<Stack spacing={2} direction={"column"} sx={{ width: '100%' }}>
117109
<ContentBox
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useEffect, useLayoutEffect } from "react";
2+
import { useBreadCrumb } from "@/components/Shared/layout/SideNav/breadCrumbStore";
3+
4+
/**
5+
* Manages breadcrumbs for the project-level consolidated conditions page.
6+
* Uses useLayoutEffect to synchronously reset the isFromConsolidated flag and
7+
* set placeholder breadcrumbs before the first paint, preventing stale breadcrumbs
8+
* from the condition detail page from briefly appearing on navigation back.
9+
*/
10+
export const useProjectConsolidatedBreadcrumbs = (projectId: string, projectName?: string) => {
11+
const { setBreadcrumbs, setIsFromConsolidated } = useBreadCrumb();
12+
13+
useLayoutEffect(() => {
14+
setIsFromConsolidated(false);
15+
setBreadcrumbs([
16+
{ title: "Home", path: "/projects", clickable: true },
17+
{ title: projectId, path: `/projects/${projectId}`, clickable: true },
18+
{ title: "Consolidated Conditions", path: undefined, clickable: false },
19+
]);
20+
}, [projectId, setIsFromConsolidated, setBreadcrumbs]);
21+
22+
useEffect(() => {
23+
if (projectName) {
24+
setBreadcrumbs([
25+
{ title: "Home", path: "/projects", clickable: true },
26+
{ title: projectName, path: `/projects/${projectId}`, clickable: true },
27+
{ title: "Consolidated Conditions", path: undefined, clickable: false },
28+
]);
29+
}
30+
}, [projectId, projectName, setBreadcrumbs]);
31+
};
Lines changed: 14 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { useEffect } from "react";
2-
import { PageGrid } from "@/components/Shared/PageGrid";
3-
import { Grid } from "@mui/material";
42
import { createFileRoute, Navigate, useParams } from "@tanstack/react-router";
53
import { useGetConsolidatedConditions } from "@/hooks/api/useConsolidatedConditions";
6-
import { ConsolidatedConditions, ConsolidatedConditionsSkeleton } from "@/components/ConsolidatedConditions";
4+
import { ConsolidatedConditionsPageContent } from "@/components/ConsolidatedConditions/ConsolidatedConditionsPageContent";
75
import { notify } from "@/components/Shared/Snackbar/snackbarStore";
8-
import { useBreadCrumb } from "@/components/Shared/layout/SideNav/breadCrumbStore";
6+
import { useProjectConsolidatedBreadcrumbs } from "@/hooks/useProjectConsolidatedBreadcrumbs";
97

108
export const Route = createFileRoute(
119
"/_authenticated/_dashboard/projects/$projectId/consolidated-conditions/"
@@ -24,55 +22,28 @@ function ConditionPage() {
2422
const {
2523
data: consolidatedConditions,
2624
isPending: isConditionsLoading,
27-
isError: isConditionsError
28-
} = useGetConsolidatedConditions(projectId, '', true);
25+
isError: isConditionsError,
26+
} = useGetConsolidatedConditions(projectId, "", true);
2927

3028
useEffect(() => {
3129
if (isConditionsError) {
3230
notify.error("Failed to load conditions");
3331
}
3432
}, [isConditionsError]);
3533

36-
const { setBreadcrumbs, setIsFromConsolidated } = useBreadCrumb();
37-
38-
useEffect(() => {
39-
setIsFromConsolidated(false);
40-
}, [setIsFromConsolidated]);
41-
42-
useEffect(() => {
43-
if (consolidatedConditions) {
44-
setBreadcrumbs([
45-
{ title: "Home", path: "/projects", clickable: true },
46-
{ title: consolidatedConditions?.project_name || projectId, path: `/projects/${projectId}`, clickable: true },
47-
{ title: "Consolidated Conditions", path: undefined, clickable: false }
48-
]);
49-
}
50-
}, [consolidatedConditions, projectId, setBreadcrumbs]);
34+
useProjectConsolidatedBreadcrumbs(projectId, consolidatedConditions?.project_name);
5135

5236
if (isConditionsError) return <Navigate to="/error" />;
5337

54-
if (isConditionsLoading) {
55-
return (
56-
<PageGrid>
57-
<Grid item xs={12}>
58-
<ConsolidatedConditionsSkeleton />
59-
</Grid>
60-
</PageGrid>
61-
);
62-
}
63-
6438
return (
65-
<PageGrid>
66-
<Grid item xs={12}>
67-
<ConsolidatedConditions
68-
projectName={consolidatedConditions?.project_name}
69-
projectId={projectId}
70-
documentCategory={consolidatedConditions?.document_category}
71-
conditions={consolidatedConditions?.conditions}
72-
documentCategoryId={''}
73-
consolidationLevel={'project'}
74-
/>
75-
</Grid>
76-
</PageGrid>
39+
<ConsolidatedConditionsPageContent
40+
isLoading={isConditionsLoading}
41+
projectName={consolidatedConditions?.project_name}
42+
projectId={projectId}
43+
documentCategory={consolidatedConditions?.document_category}
44+
documentCategoryId=""
45+
conditions={consolidatedConditions?.conditions}
46+
consolidationLevel="project"
47+
/>
7748
);
7849
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { useEffect } from "react";
2-
import { PageGrid } from "@/components/Shared/PageGrid";
3-
import { Grid } from "@mui/material";
42
import { createFileRoute, Navigate, useParams } from "@tanstack/react-router";
53
import { useGetConsolidatedConditions } from "@/hooks/api/useConsolidatedConditions";
6-
import { ConsolidatedConditions, ConsolidatedConditionsSkeleton } from "@/components/ConsolidatedConditions";
4+
import { ConsolidatedConditionsPageContent } from "@/components/ConsolidatedConditions/ConsolidatedConditionsPageContent";
75
import { notify } from "@/components/Shared/Snackbar/snackbarStore";
86
import { useBreadCrumb } from "@/components/Shared/layout/SideNav/breadCrumbStore";
97

@@ -18,7 +16,7 @@ export const Route = createFileRoute(
1816
{ title: "Home", path: "/projects/" },
1917
{ title: `${params.projectId}`, path: `/projects/` },
2018
{ title: `Document Category`, path: `/documents/projects/${params.projectId}/document-category/` },
21-
{ title: `Consolidated Conditions`, path: undefined }
19+
{ title: `Consolidated Conditions`, path: undefined },
2220
],
2321
});
2422

@@ -30,7 +28,7 @@ function ConditionPage() {
3028
const {
3129
data: consolidatedConditions,
3230
isPending: isConditionsLoading,
33-
isError: isConditionsError
31+
isError: isConditionsError,
3432
} = useGetConsolidatedConditions(projectId, categoryId, false);
3533

3634
useEffect(() => {
@@ -39,54 +37,37 @@ function ConditionPage() {
3937
}
4038
}, [isConditionsError]);
4139

42-
const META_PROJECT_TITLE = `${projectId}`;
43-
const META_DOCUMENT_CATEGORY = `Document Category`;
4440
const { replaceBreadcrumb } = useBreadCrumb();
4541

4642
useEffect(() => {
4743
if (consolidatedConditions) {
4844
replaceBreadcrumb("Home", "Home", "/projects", true);
49-
5045
replaceBreadcrumb(
51-
META_PROJECT_TITLE,
52-
consolidatedConditions?.project_name || META_PROJECT_TITLE,
46+
projectId,
47+
consolidatedConditions.project_name || projectId,
5348
`/projects/${projectId}`,
5449
true
5550
);
56-
5751
replaceBreadcrumb(
58-
META_DOCUMENT_CATEGORY,
59-
consolidatedConditions?.document_category || META_DOCUMENT_CATEGORY,
52+
"Document Category",
53+
consolidatedConditions.document_category || "Document Category",
6054
`/documents/project/${projectId}/document-category/${categoryId}/`,
6155
true
6256
);
6357
}
64-
}, [consolidatedConditions, replaceBreadcrumb, META_PROJECT_TITLE, META_DOCUMENT_CATEGORY, categoryId, projectId]);
58+
}, [consolidatedConditions, replaceBreadcrumb, categoryId, projectId]);
6559

6660
if (isConditionsError) return <Navigate to="/error" />;
6761

68-
if (isConditionsLoading) {
69-
return (
70-
<PageGrid>
71-
<Grid item xs={12}>
72-
<ConsolidatedConditionsSkeleton />
73-
</Grid>
74-
</PageGrid>
75-
);
76-
}
77-
7862
return (
79-
<PageGrid>
80-
<Grid item xs={12}>
81-
<ConsolidatedConditions
82-
projectName = {consolidatedConditions?.project_name}
83-
projectId = {projectId}
84-
documentCategory = {consolidatedConditions?.document_category}
85-
documentCategoryId = {categoryId}
86-
conditions={consolidatedConditions?.conditions}
87-
consolidationLevel={'document-category'}
88-
/>
89-
</Grid>
90-
</PageGrid>
63+
<ConsolidatedConditionsPageContent
64+
isLoading={isConditionsLoading}
65+
projectName={consolidatedConditions?.project_name}
66+
projectId={projectId}
67+
documentCategory={consolidatedConditions?.document_category}
68+
documentCategoryId={categoryId}
69+
conditions={consolidatedConditions?.conditions}
70+
consolidationLevel="document-category"
71+
/>
9172
);
9273
}

0 commit comments

Comments
 (0)