Skip to content

Commit c612ffd

Browse files
committed
tree
1 parent fe3429d commit c612ffd

File tree

9 files changed

+298
-73
lines changed

9 files changed

+298
-73
lines changed

src/App.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,6 @@ const routeObject: RouteObject[] = [
219219
Component: Systems,
220220
loader: systemsLayoutLoader(queryClient),
221221
},
222-
{
223-
path: 'tree',
224-
Component: SystemsTree,
225-
},
226222
{
227223
path: paths.systemTree,
228224
Component: SystemsTree,

src/api/catalogueItems.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export const useGetCatalogueItems = (
6868
});
6969
};
7070

71-
const getCatalogueItem = async (
71+
export const getCatalogueItem = async (
7272
catalogueCategoryId: string | undefined
7373
): Promise<CatalogueItem> => {
7474
const queryParams = new URLSearchParams();

src/api/items.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export const usePostItems = (): UseMutationResult<
8989
});
9090
};
9191

92-
const getItems = async (
92+
export const getItems = async (
9393
system_id?: string,
9494
catalogue_item_id?: string
9595
): Promise<Item[]> => {

src/api/systems.tsx

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ import {
1818
SystemImportanceType,
1919
SystemPatch,
2020
SystemPost,
21+
type CatalogueItem,
2122
} from './api.types';
23+
import { getCatalogueItem } from './catalogueItems';
24+
import { getItems } from './items';
2225

2326
/** Utility for turning an importance into an MUI palette colour to display */
2427
export const getSystemImportanceColour = (
@@ -87,23 +90,66 @@ export const useGetSystem = (
8790
return useQuery(getSystemQuery(id));
8891
};
8992

90-
export interface SystemTree extends System {
93+
export interface SystemTree extends Partial<System> {
94+
catalogueItems: (CatalogueItem & { itemsQuantity: number })[];
9195
subsystems?: SystemTree[];
9296
}
9397

94-
const getSystemTree = async (parent_id?: string): Promise<SystemTree[]> => {
98+
const getSystemTree = async (parent_id: string): Promise<SystemTree[]> => {
9599
// Fetch the systems at the current level
100+
101+
const rootSystem = await getSystem(parent_id);
102+
96103
const systems = await getSystems(parent_id || 'null');
97104

98-
// Fetch subsystems for each system recursively
99-
const systemsWithTree = await Promise.all(
105+
// Fetch subsystems and catalogue items for each system recursively
106+
const systemsWithTree: SystemTree[] = await Promise.all(
100107
systems.map(async (system) => {
101-
const subsystems = await getSystemTree(system.id); // Fetch subsystems
102-
return { ...system, subsystems }; // Attach subsystems
108+
// Fetch subsystems recursively
109+
const subsystems = await getSystemTree(system.id);
110+
111+
// Fetch all items for the current system
112+
const items = await getItems(system.id);
113+
114+
// Group items into catalogue categories and fetch catalogue item details
115+
const catalogueItemIdSet = new Set<string>(
116+
items.map((item) => item.catalogue_item_id)
117+
);
118+
119+
const catalogueItems: SystemTree['catalogueItems'] = await Promise.all(
120+
Array.from(catalogueItemIdSet).map(async (id) => {
121+
const catalogueItem = await getCatalogueItem(id);
122+
const categoryItems = items.filter(
123+
(item) => item.catalogue_item_id === id
124+
);
125+
return { ...catalogueItem, itemsQuantity: categoryItems.length };
126+
})
127+
);
128+
129+
return { ...system, subsystems, catalogueItems };
130+
})
131+
);
132+
133+
// Handle the case when there are no systems (leaf nodes or empty levels)
134+
135+
const items = await getItems(parent_id);
136+
137+
// Group items into catalogue categories and fetch catalogue item details
138+
const catalogueItemIdSet = new Set<string>(
139+
items.map((item) => item.catalogue_item_id)
140+
);
141+
142+
const catalogueItems: SystemTree['catalogueItems'] = await Promise.all(
143+
Array.from(catalogueItemIdSet).map(async (id) => {
144+
const catalogueItem = await getCatalogueItem(id);
145+
const categoryItems = items.filter(
146+
(item) => item.catalogue_item_id === id
147+
);
148+
return { ...catalogueItem, itemsQuantity: categoryItems.length };
103149
})
104150
);
105151

106-
return systemsWithTree;
152+
return [{ ...rootSystem, catalogueItems, subsystems: systemsWithTree }];
107153
};
108154

109155
export const useGetSystemsTree = (

src/common/baseLayoutHeader.component.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,11 @@ function BaseLayoutHeader(props: BaseLayoutHeaderProps) {
1515
const { breadcrumbsInfo, children, homeLocation } = props;
1616
const navigate = useNavigate();
1717

18-
// Check if we are in Tree View or Normal View by looking for '/tree' in the URL
19-
const isTreeView = location.pathname.includes('tree');
2018
const onChangeNode = React.useCallback(
2119
(id: string | null) => {
22-
navigate(
23-
`/${RoutesHomeLocation[homeLocation]}${id ? `/${id}` : ''}${isTreeView && !id && homeLocation == 'Systems' ? '/tree' : ''}`
24-
);
20+
navigate(`/${RoutesHomeLocation[homeLocation]}${id ? `/${id}` : ''}`);
2521
},
26-
[homeLocation, isTreeView, navigate]
22+
[homeLocation, navigate]
2723
);
2824
return (
2925
<Box height="100%" width="100%">

src/systems/systemItemsTable.component.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ export function SystemItemsTable(props: SystemItemsTableProps) {
211211
header: 'Catalogue Item',
212212
Header: TableHeaderOverflowTip,
213213
accessorFn: (row) => row.catalogueItem?.name,
214+
getGroupingValue: (row) => row.catalogueItem?.id ?? '',
214215
id: 'catalogueItem.name',
215216
Cell:
216217
type === 'normal'

src/systems/systemsLayout.component.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,25 @@ function SystemsLayout() {
7373
]) ?? [],
7474
}}
7575
>
76-
<ToggleButtonGroup
77-
value={isTreeView ? 'tree' : 'normal'}
78-
exclusive
79-
onChange={handleViewChange}
80-
aria-label="view mode toggle"
81-
size="small"
82-
sx={{ margin: 1 }}
83-
>
84-
<ToggleButton value="normal" aria-label="normal view">
85-
<ViewModuleIcon sx={{ marginRight: 1 }} fontSize="small" />
86-
Normal View
87-
</ToggleButton>
88-
<ToggleButton value="tree" aria-label="tree view">
89-
<AccountTreeIcon sx={{ marginRight: 1 }} fontSize="small" />
90-
Tree View
91-
</ToggleButton>
92-
</ToggleButtonGroup>
76+
{location.pathname !== '/systems' && (
77+
<ToggleButtonGroup
78+
value={isTreeView ? 'tree' : 'normal'}
79+
exclusive
80+
onChange={handleViewChange}
81+
aria-label="view mode toggle"
82+
size="small"
83+
sx={{ margin: 1 }}
84+
>
85+
<ToggleButton value="normal" aria-label="normal view">
86+
<ViewModuleIcon sx={{ marginRight: 1 }} fontSize="small" />
87+
Normal View
88+
</ToggleButton>
89+
<ToggleButton value="tree" aria-label="tree view">
90+
<AccountTreeIcon sx={{ marginRight: 1 }} fontSize="small" />
91+
Tree View
92+
</ToggleButton>
93+
</ToggleButtonGroup>
94+
)}
9395
<Outlet />
9496
</BaseLayoutHeader>
9597
);
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { MoreHoriz } from '@mui/icons-material';
2+
import { Grid, IconButton } from '@mui/material';
3+
import { Handle, Position } from '@xyflow/react';
4+
import React from 'react';
5+
import { OverflowTip } from '../utils';
6+
7+
interface SystemsNodeHeaderProps {
8+
data: {
9+
title: string | React.ReactNode;
10+
label: string | React.ReactNode;
11+
direction?: 'TB' | 'LR';
12+
setNodeDimensions: (nodeId: string, width: number, height: number) => void;
13+
nodeId: string;
14+
};
15+
}
16+
17+
const SystemsNodeHeader = (props: SystemsNodeHeaderProps) => {
18+
const { data } = props;
19+
20+
const containerRef = React.useRef<HTMLDivElement | null>(null);
21+
22+
React.useEffect(() => {
23+
if (containerRef.current) {
24+
const { offsetWidth, offsetHeight } = containerRef.current;
25+
data.setNodeDimensions(data.nodeId, offsetWidth, offsetHeight);
26+
}
27+
}, [data, containerRef]);
28+
29+
const isHorizontal = data.direction === 'LR';
30+
return (
31+
<Grid
32+
ref={containerRef}
33+
container
34+
direction="column"
35+
sx={{
36+
border: '1px solid #ddd',
37+
borderRadius: 1,
38+
overflow: 'hidden',
39+
boxShadow: 1,
40+
width: '100%',
41+
padding: 2,
42+
backgroundColor: 'white',
43+
}}
44+
>
45+
{/* Header Section */}
46+
<Grid
47+
item
48+
container
49+
alignItems="center"
50+
justifyContent="space-between"
51+
sx={{
52+
borderBottom: '1px solid #ddd',
53+
paddingBottom: 1,
54+
marginBottom: 1,
55+
}}
56+
>
57+
<Grid
58+
item
59+
sx={{ display: 'flex', alignItems: 'center', gap: 1 }}
60+
xs={8}
61+
>
62+
<OverflowTip sx={{ fontWeight: 'bold', typography: 'h6' }}>
63+
{data.title}
64+
</OverflowTip>
65+
</Grid>
66+
<Grid
67+
item
68+
sx={{ display: 'flex', alignItems: 'center', margin: 1 }}
69+
xs={2}
70+
>
71+
{/* Actions Menu */}
72+
<IconButton size="small">
73+
<MoreHoriz />
74+
</IconButton>
75+
</Grid>
76+
</Grid>
77+
78+
{/* Label Section */}
79+
<Grid item>
80+
{typeof data.label === 'string' ? (
81+
<OverflowTip sx={{ fontWeight: 'bold', typography: 'body2' }}>
82+
{data.label}
83+
</OverflowTip>
84+
) : (
85+
data.label
86+
)}
87+
</Grid>
88+
89+
<Handle
90+
type="source"
91+
position={isHorizontal ? Position.Right : Position.Bottom}
92+
/>
93+
<Handle
94+
type="target"
95+
position={isHorizontal ? Position.Left : Position.Top}
96+
/>
97+
</Grid>
98+
);
99+
};
100+
101+
export default SystemsNodeHeader;

0 commit comments

Comments
 (0)