-
-
Notifications
You must be signed in to change notification settings - Fork 773
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Refactor part category tree - New "NavigationTree" using native mantine components - Make it generic, too * Replace existing <StockLocationTree /> component * Adjust API filtering for location tree endpoint * Added playwright tests * Update api filter classes * Fix for DetailsImage - Update to @mantine/core had changed the <AspectRatio> component * fix for identifierString function * Adjust playwright tests
- Loading branch information
1 parent
c90ee43
commit f3223c6
Showing
18 changed files
with
361 additions
and
389 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
import { | ||
ActionIcon, | ||
Anchor, | ||
Divider, | ||
Drawer, | ||
Group, | ||
LoadingOverlay, | ||
RenderTreeNodePayload, | ||
Space, | ||
Stack, | ||
Tree, | ||
TreeNodeData, | ||
useTree | ||
} from '@mantine/core'; | ||
import { | ||
IconChevronDown, | ||
IconChevronRight, | ||
IconPoint, | ||
IconSitemap | ||
} from '@tabler/icons-react'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { useCallback, useMemo } from 'react'; | ||
import { useNavigate } from 'react-router-dom'; | ||
|
||
import { api } from '../../App'; | ||
import { ApiEndpoints } from '../../enums/ApiEndpoints'; | ||
import { ModelType } from '../../enums/ModelType'; | ||
import { navigateToLink } from '../../functions/navigation'; | ||
import { getDetailUrl } from '../../functions/urls'; | ||
import { apiUrl } from '../../states/ApiState'; | ||
import { StylishText } from '../items/StylishText'; | ||
|
||
/* | ||
* A generic navigation tree component. | ||
*/ | ||
export default function NavigationTree({ | ||
title, | ||
opened, | ||
onClose, | ||
selectedId, | ||
modelType, | ||
endpoint | ||
}: { | ||
title: string; | ||
opened: boolean; | ||
onClose: () => void; | ||
selectedId?: number | null; | ||
modelType: ModelType; | ||
endpoint: ApiEndpoints; | ||
}) { | ||
const navigate = useNavigate(); | ||
const treeState = useTree(); | ||
|
||
// Data query to fetch the tree data from server | ||
const query = useQuery({ | ||
enabled: opened, | ||
queryKey: [modelType, opened], | ||
queryFn: async () => | ||
api | ||
.get(apiUrl(endpoint), { | ||
data: { | ||
ordering: 'level' | ||
} | ||
}) | ||
.then((response) => response.data ?? []) | ||
.catch((error) => { | ||
console.error(`Error fetching ${modelType} tree`); | ||
return []; | ||
}) | ||
}); | ||
|
||
const follow = useCallback( | ||
(node: TreeNodeData, event?: any) => { | ||
const url = getDetailUrl(modelType, node.value); | ||
if (event?.shiftKey || event?.ctrlKey) { | ||
navigateToLink(url, navigate, event); | ||
} else { | ||
onClose(); | ||
navigate(url); | ||
} | ||
}, | ||
[modelType, navigate] | ||
); | ||
|
||
// Map returned query to a "tree" structure | ||
const data: TreeNodeData[] = useMemo(() => { | ||
/* | ||
* Reconstruct the navigation tree from the provided data. | ||
* It is required (and assumed) that the data is first sorted by level. | ||
*/ | ||
|
||
let nodes: Record<number, any> = {}; | ||
let tree: TreeNodeData[] = []; | ||
|
||
if (!query?.data?.length) { | ||
return []; | ||
} | ||
|
||
for (let ii = 0; ii < query.data.length; ii++) { | ||
let node = { | ||
...query.data[ii], | ||
children: [], | ||
label: query.data[ii].name, | ||
value: query.data[ii].pk.toString(), | ||
selected: query.data[ii].pk === selectedId | ||
}; | ||
|
||
const pk: number = node.pk; | ||
const parent: number | null = node.parent; | ||
|
||
if (!parent) { | ||
// This is a top level node | ||
tree.push(node); | ||
} else { | ||
// This is *not* a top level node, so the parent *must* already exist | ||
nodes[parent]?.children.push(node); | ||
} | ||
|
||
// Finally, add this node | ||
nodes[pk] = node; | ||
|
||
if (pk === selectedId) { | ||
// Expand all parents | ||
let parent = nodes[node.parent]; | ||
while (parent) { | ||
parent.expanded = true; | ||
parent = nodes[parent.parent]; | ||
} | ||
} | ||
} | ||
|
||
return tree; | ||
}, [selectedId, query.data]); | ||
|
||
const renderNode = useCallback( | ||
(payload: RenderTreeNodePayload) => { | ||
return ( | ||
<Group | ||
justify="left" | ||
key={payload.node.value} | ||
wrap="nowrap" | ||
onClick={() => { | ||
if (payload.hasChildren) { | ||
treeState.toggleExpanded(payload.node.value); | ||
} | ||
}} | ||
> | ||
<Space w={5 * payload.level} /> | ||
<ActionIcon | ||
size="sm" | ||
variant="transparent" | ||
aria-label={`nav-tree-toggle-${payload.node.value}}`} | ||
> | ||
{payload.hasChildren ? ( | ||
payload.expanded ? ( | ||
<IconChevronDown /> | ||
) : ( | ||
<IconChevronRight /> | ||
) | ||
) : ( | ||
<IconPoint /> | ||
)} | ||
</ActionIcon> | ||
<Anchor | ||
onClick={(event: any) => follow(payload.node, event)} | ||
aria-label={`nav-tree-item-${payload.node.value}`} | ||
> | ||
{payload.node.label} | ||
</Anchor> | ||
</Group> | ||
); | ||
}, | ||
[treeState] | ||
); | ||
|
||
return ( | ||
<Drawer | ||
opened={opened} | ||
size="md" | ||
position="left" | ||
onClose={onClose} | ||
withCloseButton={true} | ||
styles={{ | ||
header: { | ||
width: '100%' | ||
}, | ||
title: { | ||
width: '100%' | ||
} | ||
}} | ||
title={ | ||
<Group justify="left" p="ms" gap="md" wrap="nowrap"> | ||
<IconSitemap /> | ||
<StylishText size="lg">{title}</StylishText> | ||
</Group> | ||
} | ||
> | ||
<Stack gap="xs"> | ||
<Divider /> | ||
<LoadingOverlay visible={query.isFetching || query.isLoading} /> | ||
<Tree data={data} tree={treeState} renderNode={renderNode} /> | ||
</Stack> | ||
</Drawer> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.