diff --git a/buildtime/client/material-editor/package-lock.json b/buildtime/client/material-editor/package-lock.json index 477df4d6b..891c3ba50 100644 --- a/buildtime/client/material-editor/package-lock.json +++ b/buildtime/client/material-editor/package-lock.json @@ -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", diff --git a/buildtime/client/material-editor/package.json b/buildtime/client/material-editor/package.json index 52533958c..a3b9fc832 100644 --- a/buildtime/client/material-editor/package.json +++ b/buildtime/client/material-editor/package.json @@ -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", diff --git a/buildtime/client/material-editor/src/App.tsx b/buildtime/client/material-editor/src/App.tsx index 152d901a1..60209b0b2 100644 --- a/buildtime/client/material-editor/src/App.tsx +++ b/buildtime/client/material-editor/src/App.tsx @@ -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) }, ], }, diff --git a/buildtime/client/material-editor/src/api/language/productGroup.api.ts b/buildtime/client/material-editor/src/api/language/productGroup.api.ts new file mode 100644 index 000000000..8896afac8 --- /dev/null +++ b/buildtime/client/material-editor/src/api/language/productGroup.api.ts @@ -0,0 +1,22 @@ +import ky from 'ky' +import type { ProductGroup } from '../../data/productGroup' + +export const getProductGroups = async (): Promise => { + return ky.get(`http://localhost:8088/productgroups`).json() +} + +export const getProductGroupById = async (id: number): Promise => { + return ky.get(`http://localhost:8088/productgroup/${id}`).json() +} + +export const createProductGroup = async (productGroup: ProductGroup): Promise => { + return ky.post(`http://localhost:8088/productgroup`, { json: productGroup }).json() +} + +export const updateProductGroup = async (productGroup: ProductGroup): Promise => { + await ky.put(`http://localhost:8088/productgroup`, { json: productGroup }) +} + +export const deleteProductGroup = async (id: number): Promise => { + await ky.delete(`http://localhost:8088/productgroup/${id}`) +} diff --git a/buildtime/client/material-editor/src/components/NavBar.tsx b/buildtime/client/material-editor/src/components/NavBar.tsx index 76894da19..dc859e1c6 100644 --- a/buildtime/client/material-editor/src/components/NavBar.tsx +++ b/buildtime/client/material-editor/src/components/NavBar.tsx @@ -18,7 +18,7 @@ function NavBar() { - + diff --git a/buildtime/client/material-editor/src/components/ProductGroupTreeView.tsx b/buildtime/client/material-editor/src/components/ProductGroupTreeView.tsx new file mode 100644 index 000000000..a2b1ea305 --- /dev/null +++ b/buildtime/client/material-editor/src/components/ProductGroupTreeView.tsx @@ -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) => ( + + {node.childProductGroups && node.childProductGroups.map((ProductGroup) => ( + renderTree(ProductGroup) + ))} + + ); + + return ( + + {ProductGroups.map((productGroup) => renderTree(productGroup))} + + ); +} diff --git a/buildtime/client/material-editor/src/data/product.ts b/buildtime/client/material-editor/src/data/product.ts index e69de29bb..64ab772f3 100644 --- a/buildtime/client/material-editor/src/data/product.ts +++ b/buildtime/client/material-editor/src/data/product.ts @@ -0,0 +1,9 @@ +export type Product = { + id: number; + label: string; + product_identifier: string; + properties: unknown[]; + products: Product[]; + productStructureNodes: unknown[]; + isEqualPropertiesEqualValues: boolean; +}; diff --git a/buildtime/client/material-editor/src/data/productGroup.ts b/buildtime/client/material-editor/src/data/productGroup.ts index 326093d27..21192e37f 100644 --- a/buildtime/client/material-editor/src/data/productGroup.ts +++ b/buildtime/client/material-editor/src/data/productGroup.ts @@ -7,7 +7,7 @@ export type ProductGroup = { parentGroupId: number | null; isVisible: boolean; path: string; - SubGroups: ProductGroup[]; + childProductGroups: ProductGroup[]; PropertyTypes: PropertyType[]; Products: Product[]; }; \ No newline at end of file diff --git a/buildtime/client/material-editor/src/pages/ProductPage.tsx b/buildtime/client/material-editor/src/pages/ProductPage.tsx new file mode 100644 index 000000000..b1131b5fe --- /dev/null +++ b/buildtime/client/material-editor/src/pages/ProductPage.tsx @@ -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(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 ( + + + Property Type Management + + + + + + ); + }; + +export default ProductPage; diff --git a/database/dump-simply_cpq_master_data-202608112212.sql b/database/dump-simply_cpq_master_data-202608112212.sql new file mode 100644 index 000000000..d59ba24cd Binary files /dev/null and b/database/dump-simply_cpq_master_data-202608112212.sql differ