TreeView for ProductGroups inital implementation

This commit is contained in:
2026-08-11 23:29:39 +02:00
parent e0042524ce
commit 0e8ad1b0ab
10 changed files with 157 additions and 16 deletions
+54 -14
View File
@@ -14,6 +14,7 @@
"@mui/icons-material": "^9.2.0",
"@mui/material": "^9.2.0",
"@mui/x-data-grid": "^9.10.1",
"@mui/x-tree-view": "^9.11.0",
"keycloak-js": "^26.2.4",
"ky": "^2.0.2",
"react": "^19.2.8",
@@ -924,12 +925,12 @@
}
},
"node_modules/@mui/types": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-9.1.1.tgz",
"integrity": "sha512-Zjt7u8wNvDg40rPTGoL+TnfkpuSKjwubsNSFRH1KAVZLcaV4I3AFNHIFbvH7p4F3alEibSbdd90xAgn5Rnfndg==",
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-9.3.0.tgz",
"integrity": "sha512-2JSxyfpEFWNUB2vKs/T1BvkfyNisMHWph8bLMj8T0uHwmLl/0qfAwQkfwMT6kxLXN9uIum9AEbECXU8er3amIg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2"
"@babel/runtime": "^7.29.7"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -941,17 +942,17 @@
}
},
"node_modules/@mui/utils": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.2.0.tgz",
"integrity": "sha512-OsUH5zhlSOM4xmLl53+agug1M1UyWb4zxFxWQCqwKTKUeQPvTENtg3JhrroBD2qpCLKsX5W/DYGERJ4mBUbc8g==",
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.3.0.tgz",
"integrity": "sha512-2HZdHwWJ6eB+7lVGSOHsByGw8jeRulT4g0NZ608Wb8Q57DE2jbNqrWPFuJsvkQQiBiTmlpvQL3i+/62zsiPrkw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/types": "^9.1.1",
"@babel/runtime": "^7.29.7",
"@mui/types": "^9.3.0",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.6"
"react-is": "^19.2.8"
},
"engines": {
"node": ">=14.0.0"
@@ -1010,14 +1011,14 @@
}
},
"node_modules/@mui/x-internals": {
"version": "9.10.1",
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-9.10.1.tgz",
"integrity": "sha512-pKFVzFQHB34TuMG3gOVx+LfIR8VBsi/ZhX/erRX2WOXu+ewEYGvdeK1mW9hLe7e2v3uyUbgT8RNeN0cy10cDjg==",
"version": "9.11.0",
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-9.11.0.tgz",
"integrity": "sha512-JjKe9k1+gVWNPwMTZLNSc92eVmoZqP5Xq3Ui6mJkstotUrbWIZC0o9+4AfTR1lYqWwnKmqm+VtLLVCa7MEBXWQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.7",
"@base-ui/utils": "^0.3.1",
"@mui/utils": "^9.2.0",
"@mui/utils": "^9.3.0",
"reselect": "^5.2.0",
"use-sync-external-store": "^1.6.0"
},
@@ -1032,6 +1033,45 @@
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@mui/x-tree-view": {
"version": "9.11.0",
"resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-9.11.0.tgz",
"integrity": "sha512-ZJcnlOr3LwQF9n+xa/4Xh0w8wDCY250smbLGmRzK107ClifYSDTR8/IrRQg8yKBwpDYWDQZW85t0voQcverk1g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.7",
"@base-ui/utils": "^0.3.1",
"@mui/utils": "^9.3.0",
"@mui/x-internals": "^9.11.0",
"@types/react-transition-group": "^4.4.12",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^7.3.0 || ^9.0.0",
"@mui/system": "^7.3.0 || ^9.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
}
},
"node_modules/@mui/x-virtualizer": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/@mui/x-virtualizer/-/x-virtualizer-0.6.2.tgz",
@@ -16,6 +16,7 @@
"@mui/icons-material": "^9.2.0",
"@mui/material": "^9.2.0",
"@mui/x-data-grid": "^9.10.1",
"@mui/x-tree-view": "^9.11.0",
"keycloak-js": "^26.2.4",
"ky": "^2.0.2",
"react": "^19.2.8",
@@ -7,6 +7,7 @@ const Layout = lazy(() => import('./Layout.tsx'))
const HomePage = lazy(() => import('./pages/HomePage.tsx'))
const LanguagesPage = lazy(() => import('./pages/LanguagesPage.tsx'))
const PropertyPage = lazy(() => import('./pages/PropertyPage.tsx'))
const ProductPage = lazy(() => import('./pages/ProductPage.tsx'))
const NotFoundPage = lazy(() => import('./pages/NotFoundPage.tsx'))
const renderLazyRoute = (Component: ComponentType) => (
@@ -24,6 +25,7 @@ const router = createBrowserRouter([
{ index: true, element: renderLazyRoute(HomePage) },
{ path: 'language', element: renderLazyRoute(LanguagesPage) },
{ path: 'propertypool', element: renderLazyRoute(PropertyPage) },
{ path: 'product', element: renderLazyRoute(ProductPage) },
{ path: '*', element: renderLazyRoute(NotFoundPage) },
],
},
@@ -0,0 +1,22 @@
import ky from 'ky'
import type { ProductGroup } from '../../data/productGroup'
export const getProductGroups = async (): Promise<ProductGroup[]> => {
return ky.get(`http://localhost:8088/productgroups`).json<ProductGroup[]>()
}
export const getProductGroupById = async (id: number): Promise<ProductGroup> => {
return ky.get(`http://localhost:8088/productgroup/${id}`).json<ProductGroup>()
}
export const createProductGroup = async (productGroup: ProductGroup): Promise<ProductGroup> => {
return ky.post(`http://localhost:8088/productgroup`, { json: productGroup }).json<ProductGroup>()
}
export const updateProductGroup = async (productGroup: ProductGroup): Promise<void> => {
await ky.put(`http://localhost:8088/productgroup`, { json: productGroup })
}
export const deleteProductGroup = async (id: number): Promise<void> => {
await ky.delete(`http://localhost:8088/productgroup/${id}`)
}
@@ -18,7 +18,7 @@ function NavBar() {
</Typography>
<Stack direction="row" spacing={2}>
<Button color="inherit" onClick={() => navigate('/propertypool')}>Property Pool</Button>
<Button color="inherit">Products</Button>
<Button color="inherit" onClick={() => navigate('/product')}>Products</Button>
<Button color="inherit">Conditions</Button>
<Button color="inherit" onClick={() => navigate('/language')}>Languages</Button>
<Button color="inherit" onClick={() => navigate('/')}>Home</Button>
@@ -0,0 +1,27 @@
import type { ProductGroup } from '../data/productGroup';
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem } from '@mui/x-tree-view/TreeItem';
type ProductGroupTreeViewProps = {
ProductGroups?: ProductGroup[];
};
export default function ProductGroupTreeView({ ProductGroups = [] }: ProductGroupTreeViewProps) {
const renderTree = (node: ProductGroup) => (
<TreeItem key={node.id} itemId={node.id.toString()} label={node.label}>
{node.childProductGroups && node.childProductGroups.map((ProductGroup) => (
renderTree(ProductGroup)
))}
</TreeItem>
);
return (
<SimpleTreeView
aria-label="Product Group Tree View"
sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }}
>
{ProductGroups.map((productGroup) => renderTree(productGroup))}
</SimpleTreeView>
);
}
@@ -0,0 +1,9 @@
export type Product = {
id: number;
label: string;
product_identifier: string;
properties: unknown[];
products: Product[];
productStructureNodes: unknown[];
isEqualPropertiesEqualValues: boolean;
};
@@ -7,7 +7,7 @@ export type ProductGroup = {
parentGroupId: number | null;
isVisible: boolean;
path: string;
SubGroups: ProductGroup[];
childProductGroups: ProductGroup[];
PropertyTypes: PropertyType[];
Products: Product[];
};
@@ -0,0 +1,40 @@
import React, { useEffect, useState } from 'react';
import ProductGroupTreeView from '../components/ProductGroupTreeView';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import type { ProductGroup } from '../data/productGroup';
import { getProductGroupById } from '../api/language/productGroup.api';
const ProductPage: React.FC = () => {
const [productGroup, setProductGroup] = useState<ProductGroup | null>(null);
const loadProductGroup = React.useCallback(async () => {
const loadedProductGroup = await getProductGroupById(3); // Assuming 3 is the ID for product groups
setProductGroup(loadedProductGroup);
}, []);
useEffect(() => {
loadProductGroup();
}, [loadProductGroup]);
return (
<Box component="section" sx={{ p: 2, border: '1px dashed grey' }}>
<Typography variant="h3">
Property Type Management
</Typography>
<Box
sx={{
display: "flex", // Arrange children in a row
flexDirection: "row",
width: "100vw", // Full width of the parent
p: 2, // Padding around the flex container
bgcolor: "#f5f5f5"
}}
>
<ProductGroupTreeView ProductGroups={productGroup ? [productGroup] : []} />
</Box>
</Box>
);
};
export default ProductPage;