material editor update

This commit is contained in:
2026-08-01 20:09:18 +02:00
parent 693b7aff36
commit 7fc8012dac
33 changed files with 4406 additions and 5 deletions
+82 -4
View File
@@ -1,45 +1,123 @@
# Simply CPQ Buildtime Client
Dieser Ordner enthält alle relevanten Dateien und Informationen zum Simply CPQ Buildtime Client. Dieser dient der Pflege von Produktdaten, Regeln, Konditionen und Administration der Simply CPQ Anwendung.
## Material-Editor
Dieser Editor basiert auf react und der Material UI Bibliothek.
### Zusätzliche Open Source Bibliotheken:
#### Umgebung
Für die Entwicklung wird die Software Node.JS benötigt, hier zu finden:
https://nodejs.org/
Mit
```bash
npm create vite@latest material-editor -- --template react-ts
```
kann dann die Umgebung erzeugt werden.
Mit
```bash
npm run dev
```
kann die Entwicklungsumgebung gestartet werden.
React Router für das Page Routing:
```bash
npm install react-router
```
#### UI: Material UI
Material UI ist eine React UI Bibliothek. Der Material-Editor nutzt die Open-Source Komponenten mit der MIT-Lizenz. [MUI: The React component library you always wanted](https://mui.com/)
Darüber Hinaus wird der "Inter"-Font und der "Roboto"-Font genutzt.
Zur Installation im Root-Verzeichnis des Client-Quellcodes
```bash
npm install @mui/material @emotion/react @emotion/styled
npm install @fontsource-variable/inter
npm install @mui/icons-material
npm install @mui/x-data-grid
```
aufrufen.
### Datenbank Zugriff mit ky
Um auf den Simply CPQ Master-Data Server zuzugreifen wird die Bibliothek *ky* genutzt. [ky - npm](https://www.npmjs.com/package/ky)
Zur Installation im Root-Verzeichnis des Client-Quellcodes
```bash
npm install ky
```
aufrufen.
# Alt
## Genutzte Techologie
Der Client ist eine Web-Applikation, die auf der React-Library aufgebaut ist.
[React](https://react.dev/)
### Zusätzliche Open Source Bibliotheken:
#### Umgebung
Für die Entwicklung wird die Software Node.JS benötigt, hier zu finden:
https://nodejs.org/
Mit
```bash
npm create vite@latest logical-editor --template react
```
kann dann die Umgebung erzeugt werden.
Mit
```bash
npm run dev
```
kann die Entwicklungsumgebung gestartet werden.
Folgende Komponenten müssen noch installiert werden:
##### React Router
```bash
npm install react-router
```
#### Tree View Komponente (react-arborist)
Diese Komponente wird zur Anzeige von Bäumen auf der Oberfläche genutzt. Hier zu finden:
[jameskerr/react-arborist: The complete tree view component for React](https://github.com/jameskerr/react-arborist)
Zur Installation im Root-Verzeichnis des Client-Quellcodes
```bash
npm install react-arborist
```
aufrufen.
#### Icon-Library:
HeroIcon Lib mit der MIT Lizenz ([tailwindlabs/heroicons: A set of free MIT-licensed high-quality SVG icons for UI development.](https://github.com/tailwindlabs/heroicons)
).
@@ -48,15 +126,16 @@ npm install @heroicons/react
```
#### CSS Helper Tailwind CSS
Zur schnellen Erstellung von Layout: [tailwindlabs/tailwindcss: A utility-first CSS framework for rapid UI development.](https://github.com/tailwindlabs/tailwindcss)
```bash
npm install tailwindcss @tailwindcss/vite
```
Dann die Datei vite.config.ts editieren
```javascript
edit vite.config.ts
import { defineConfig } from 'vite'
@@ -67,13 +146,12 @@ export default defineConfig({
tailwindcss(),
],
})
```
In der Scriptdatei:
```javascript
@import "tailwindcss";
```
Dokumentation :
@@ -10,7 +10,7 @@ const EditPropertyGroup = ({ propertyGroup, onClose }) => {
<div className="editor">
<label>
<span className={`flex flex-row gap-2`}>
<p>Name:</p>
<p className={``}>Name:</p>
<input className='input_text' type="text" value={labelTranslation} onChange={(e) => setLabelTranslation(e.target.value)} />
</span>
</label>
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
@@ -0,0 +1,75 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
@@ -0,0 +1,22 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
globals: globals.browser,
},
},
])
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/SimplyCPQ_Logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>material-editor</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,38 @@
{
"name": "material-editor",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@fontsource-variable/inter": "^5.3.0",
"@mui/icons-material": "^9.2.0",
"@mui/material": "^9.2.0",
"@mui/x-data-grid": "^9.10.1",
"ky": "^2.0.2",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-router": "^8.3.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^24.13.3",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.4",
"eslint": "^10.8.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.3",
"globals": "^17.7.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.65.0",
"vite": "^8.2.0"
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

@@ -0,0 +1,28 @@
import { createBrowserRouter, RouterProvider } from 'react-router'
import Layout from './Layout.tsx'
import HomePage from './pages/HomePage.tsx'
import LanguagesPage from './pages/LanguagesPage.tsx'
import PropertyPage from './pages/PropertyPage.tsx'
import NotFoundPage from './pages/NotFoundPage.tsx'
import './App.css'
// routing configuration
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <HomePage /> },
{ path: 'language', element: <LanguagesPage /> },
{ path: 'propertypool', element: <PropertyPage /> },
{ path: '*', element: <NotFoundPage /> },
],
},
])
function App() {
return <RouterProvider router={router} />
}
export default App
@@ -0,0 +1,20 @@
// Layouting Main Application
import { Outlet, Link } from 'react-router'
import NavBar from './components/NavBar'
import Footer from './components/Footer'
function Layout() {
return (
<div>
<NavBar />
<main>
<Outlet />
</main>
<Footer />
</div>
)
}
export default Layout
@@ -0,0 +1,22 @@
import ky from 'ky'
import type { Language } from '../../data/language'
export const getLanguages = async (): Promise<Language[]> => {
return ky.get(`http://localhost:8088/languages`).json<Language[]>()
}
export const getLanguageById = async (id: number): Promise<Language> => {
return ky.get(`http://localhost:8088/language/${id}`).json<Language>()
}
export const createLanguage = async (language: Language): Promise<Language> => {
return ky.post(`http://localhost:8088/language`, { json: language }).json<Language>()
}
export const updateLanguage = async (language: Language): Promise<Language> => {
return ky.put(`http://localhost:8088/language`, { json: language }).json<Language>()
}
export const deleteLanguage = async (id: number): Promise<void> => {
return ky.delete(`http://localhost:8088/language/${id}`).json<void>()
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

@@ -0,0 +1,20 @@
import { Toolbar, Typography } from '@mui/material'
import AppBar from '@mui/material/AppBar'
function Footer() {
return (
<AppBar position="static">
<Toolbar>
<Typography variant="body2" component="div" sx={{ flexGrow: 1 }}>
SimplyCPQ Material Editor (c) 2026 Dr. Thomas Sindt
</Typography>
</Toolbar>
</AppBar>
)
}
export default Footer;
@@ -0,0 +1,76 @@
import {useState, useEffect} from 'react';
import Tooltip from '@mui/material/Tooltip';
import { ExportCsv } from '@mui/x-data-grid';
import type {Language} from '../data/language';
import { DataGrid, Toolbar, ToolbarButton, useGridApiRef } from '@mui/x-data-grid';
import Paper from '@mui/material/Paper';
import DeleteIcon from '@mui/icons-material/Delete';
import AddIcon from '@mui/icons-material/Add';
import FileDownloadIcon from '@mui/icons-material/FileDownload';
import {getLanguages} from '../api/language/languages.api';
import {updateLanguage} from '../api/language/languages.api';
import { Typography } from '@mui/material';
const LanguagesTableView = () => {
const [languages, setLanguages] = useState<Language[]>([]);
const apiRef = useGridApiRef();
function CustomToolbar() {
return (
<Toolbar>
<Typography variant="body1" component="div" sx={{ flexGrow: 1 }}>
Here you see all your available languages for translations.
</Typography>
<Tooltip title="Download as CSV">
<ExportCsv render={<ToolbarButton />}>
<FileDownloadIcon fontSize="small" />
</ExportCsv>
</Tooltip>
<ToolbarButton onClick={() => console.log('Delete Row clicked')}>
<DeleteIcon fontSize="small" sx={{ ml: 'auto' }} />
</ToolbarButton>
<ToolbarButton onClick={() => console.log('Add Row clicked')}>
<AddIcon fontSize="small" sx={{ ml: 'auto' }} />
</ToolbarButton>
</Toolbar>
)
}
useEffect(() => {
getLanguages().then(setLanguages);
}, []);
return (
<div>
<Paper sx={{ height: 600, width: '50%' }}>
<DataGrid
initialState={{
sorting: {
sortModel: [{ field: 'language', sort: 'asc' }],
},
}}
rows={languages}
columns={[
{ field: 'id', headerName: 'ID', width: 100 ,align: 'right'},
{ field: 'locale', headerName: 'Locale', width: 200, editable: true },
{ field: 'language', headerName: 'Language', width: 200 , editable: true},
]}
slots={{
toolbar: CustomToolbar,
}}
pageSize={5}
rowsPerPageOptions={[5]}
processRowUpdate={(updatedRow, originalRow) => {
updateLanguage({id: updatedRow.id, locale: updatedRow.locale, language: updatedRow.language});
return updatedRow;
}}
showToolbar
/>
</Paper>
</div>
);
};
export default LanguagesTableView;
@@ -0,0 +1,33 @@
import { Toolbar, IconButton, Typography, Stack, Button } from '@mui/material'
import AppBar from '@mui/material/AppBar'
import { useNavigate } from 'react-router'
import SimplyCPQLogo from '../assets/SimplyCPQ_Logo.svg'
function NavBar() {
const navigate = useNavigate();
return (
<AppBar position="static">
<Toolbar>
<IconButton edge="start" color="inherit" aria-label="SimplyCPQ Logo">
<img src={SimplyCPQLogo} alt="SimplyCPQ Logo" style={{ width: '40px', height: '40px' }} />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
SimplyCPQ Material Editor
</Typography>
<Stack direction="row" spacing={2}>
<Button color="inherit" onClick={() => navigate('/propertypool')}>Property Pool</Button>
<Button color="inherit">Products</Button>
<Button color="inherit">Conditions</Button>
<Button color="inherit" onClick={() => navigate('/language')}>Languages</Button>
<Button color="inherit" onClick={() => navigate('/')}>Home</Button>
</Stack>
</Toolbar>
</AppBar>
)
}
export default NavBar;
@@ -0,0 +1,6 @@
export type Language = {
id: number;
locale: string;
language: string;
};
@@ -0,0 +1,4 @@
body {
font-family: 'Inter Variable', sans-serif;
}
@@ -0,0 +1,12 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
// Supports weights 100-900
import '@fontsource-variable/inter/wght.css';
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
@@ -0,0 +1,12 @@
import React from 'react';
const HomePage: React.FC = () => {
return (
<div>
<h1>Welcome to the Home Page</h1>
<p>This is the main landing page of the application.</p>
</div>
);
};
export default HomePage;
@@ -0,0 +1,17 @@
import React from 'react';
import LanguagesTableView from '../components/LanguagesTableView';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
const LanguagesPage: React.FC = () => {
return (
<Box component="section" sx={{ p: 2, border: '1px dashed grey' }}>
<Typography variant="h3">
Language Management
</Typography>
<LanguagesTableView />
</Box>
);
};
export default LanguagesPage;
@@ -0,0 +1,12 @@
import React from 'react';
const NotFoundPage: React.FC = () => {
return (
<div>
<h1>Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
</div>
);
};
export default NotFoundPage;
@@ -0,0 +1,12 @@
import React from 'react';
const PropertyPage: React.FC = () => {
return (
<div>
<h1>Welcome to the Property Page</h1>
<p>This is the main landing page for Property handling.</p>
</div>
);
};
export default PropertyPage;
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023", "DOM"],
"module": "esnext",
"types": ["vite/client"],
"allowArbitraryExtensions": true,
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023"],
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"module": "nodenext",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})