feat(dashboard): Tailwind CSS + shadcn/ui eingeführt, Dashboard modernisiert
Kachel-Größe/Ausblenden-Fix (flexBasis pro Kachel statt nur Aufgaben-Kachel, sichtbare Liste ausgeblendeter Kacheln zum Zurückholen). Tailwind v4 + shadcn-Basiskomponenten (Button/Card/Badge) neu eingeführt, gezielt fürs Dashboard-Redesign genutzt (kein Preflight, global.css bleibt für restliche App unangetastet). TileFrame nutzt jetzt Card mit farbigem Akzentbalken je Kritikalität (Mängel/Prüftermine/Ablaufdaten/Einsatzbereitschaft), ReadinessBadge von Emoji auf CSS-Punkte umgestellt, größere Touch-Ziele im Anpassen-Modus, Kiosk-Look im Status-Monitor (Chrome ausgeblendet), "zuletzt aktualisiert"-Anzeige. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -6311,3 +6311,34 @@ Keine Commits in dieser Session.
|
||||
- frontend/src/pages/kontrolle/PositionCard.tsx | 48 ++++++++++++++++++++++++++++++++++--------------
|
||||
|
||||
---
|
||||
## 2026-09-07 12:02 – 12:03 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** frontend
|
||||
|
||||
### Commits
|
||||
- 5fc8079 feat(dashboard): Dashboard-Start für Verantwortliche, Kachel-Anpassung, Status-Monitor
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 1923 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- backend/DEVLOG.md | 128 ++++
|
||||
- frontend/DEVLOG.md | 267 ++++++++
|
||||
- frontend/src/App.tsx | 5 +-
|
||||
- frontend/src/components/AppShell.tsx | 2 -
|
||||
- frontend/src/components/StartseiteAuswahl.tsx | 29 -
|
||||
- frontend/src/pages/DashboardPage.tsx | 349 ----------
|
||||
- frontend/src/pages/LoginPage.tsx | 3 +-
|
||||
- frontend/src/pages/dashboard/DashboardPage.tsx | 215 ++++++
|
||||
- frontend/src/pages/dashboard/TileFrame.tsx | 49 ++
|
||||
- frontend/src/pages/dashboard/layout.ts | 55 ++
|
||||
- frontend/src/pages/dashboard/tiles/AblaufdatenTile.tsx | 28 +
|
||||
- frontend/src/pages/dashboard/tiles/AktivitaetTile.tsx | 34 +
|
||||
- frontend/src/pages/dashboard/tiles/AufgabenTile.tsx | 58 ++
|
||||
- frontend/src/pages/dashboard/tiles/EinsatzbereitschaftTile.tsx | 74 +++
|
||||
- frontend/src/pages/dashboard/tiles/KennzahlenTile.tsx | 19 +
|
||||
- frontend/src/pages/dashboard/tiles/MaengelTile.tsx | 58 ++
|
||||
- frontend/src/pages/dashboard/tiles/PrueftermineTile.tsx | 28 +
|
||||
- frontend/src/pages/dashboard/tiles/QualifikationTile.tsx | 26 +
|
||||
- frontend/src/pages/dashboard/types.ts | 55 ++
|
||||
- frontend/src/settings/startseite.ts | 30 -
|
||||
|
||||
---
|
||||
|
||||
@@ -551,3 +551,65 @@ Keine Commits in dieser Session.
|
||||
- frontend/src/pages/kontrolle/PositionCard.tsx | 48 ++++++++++++++++++++++++++++++++++--------------
|
||||
|
||||
---
|
||||
## 2026-09-07 12:05 – 12:07 (2m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** asb-material
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 1923 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- backend/DEVLOG.md | 128 ++++
|
||||
- frontend/DEVLOG.md | 267 ++++++++
|
||||
- frontend/src/App.tsx | 5 +-
|
||||
- frontend/src/components/AppShell.tsx | 2 -
|
||||
- frontend/src/components/StartseiteAuswahl.tsx | 29 -
|
||||
- frontend/src/pages/DashboardPage.tsx | 349 ----------
|
||||
- frontend/src/pages/LoginPage.tsx | 3 +-
|
||||
- frontend/src/pages/dashboard/DashboardPage.tsx | 215 ++++++
|
||||
- frontend/src/pages/dashboard/TileFrame.tsx | 49 ++
|
||||
- frontend/src/pages/dashboard/layout.ts | 55 ++
|
||||
- frontend/src/pages/dashboard/tiles/AblaufdatenTile.tsx | 28 +
|
||||
- frontend/src/pages/dashboard/tiles/AktivitaetTile.tsx | 34 +
|
||||
- frontend/src/pages/dashboard/tiles/AufgabenTile.tsx | 58 ++
|
||||
- frontend/src/pages/dashboard/tiles/EinsatzbereitschaftTile.tsx | 74 +++
|
||||
- frontend/src/pages/dashboard/tiles/KennzahlenTile.tsx | 19 +
|
||||
- frontend/src/pages/dashboard/tiles/MaengelTile.tsx | 58 ++
|
||||
- frontend/src/pages/dashboard/tiles/PrueftermineTile.tsx | 28 +
|
||||
- frontend/src/pages/dashboard/tiles/QualifikationTile.tsx | 26 +
|
||||
- frontend/src/pages/dashboard/types.ts | 55 ++
|
||||
- frontend/src/settings/startseite.ts | 30 -
|
||||
|
||||
---
|
||||
## 2026-09-07 12:08 – 12:08 (0m)
|
||||
**Beschreibung:** Claude Code Session
|
||||
**Projekt:** frontend
|
||||
|
||||
### Commits
|
||||
Keine Commits in dieser Session.
|
||||
|
||||
### Geänderte Dateien
|
||||
- DEVLOG.md | 1923 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
- backend/DEVLOG.md | 128 ++++
|
||||
- frontend/DEVLOG.md | 267 ++++++++
|
||||
- frontend/src/App.tsx | 5 +-
|
||||
- frontend/src/components/AppShell.tsx | 2 -
|
||||
- frontend/src/components/StartseiteAuswahl.tsx | 29 -
|
||||
- frontend/src/pages/DashboardPage.tsx | 349 ----------
|
||||
- frontend/src/pages/LoginPage.tsx | 3 +-
|
||||
- frontend/src/pages/dashboard/DashboardPage.tsx | 215 ++++++
|
||||
- frontend/src/pages/dashboard/TileFrame.tsx | 49 ++
|
||||
- frontend/src/pages/dashboard/layout.ts | 55 ++
|
||||
- frontend/src/pages/dashboard/tiles/AblaufdatenTile.tsx | 28 +
|
||||
- frontend/src/pages/dashboard/tiles/AktivitaetTile.tsx | 34 +
|
||||
- frontend/src/pages/dashboard/tiles/AufgabenTile.tsx | 58 ++
|
||||
- frontend/src/pages/dashboard/tiles/EinsatzbereitschaftTile.tsx | 74 +++
|
||||
- frontend/src/pages/dashboard/tiles/KennzahlenTile.tsx | 19 +
|
||||
- frontend/src/pages/dashboard/tiles/MaengelTile.tsx | 58 ++
|
||||
- frontend/src/pages/dashboard/tiles/PrueftermineTile.tsx | 28 +
|
||||
- frontend/src/pages/dashboard/tiles/QualifikationTile.tsx | 26 +
|
||||
- frontend/src/pages/dashboard/types.ts | 55 ++
|
||||
- frontend/src/settings/startseite.ts | 30 -
|
||||
|
||||
---
|
||||
|
||||
Generated
+672
-4
@@ -8,17 +8,24 @@
|
||||
"name": "mabea-frontend",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "^1.3.3",
|
||||
"@zxing/browser": "^0.1.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"lucide-react": "^1.42.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.26.2"
|
||||
"react-router-dom": "^6.26.2",
|
||||
"tailwind-merge": "^3.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.47.2",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.6",
|
||||
"vite-plugin-pwa": "^0.20.5"
|
||||
@@ -2110,6 +2117,39 @@
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz",
|
||||
"integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz",
|
||||
"integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.4.tgz",
|
||||
@@ -2596,6 +2636,278 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tailwindcss/node": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz",
|
||||
"integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"enhanced-resolve": "^5.24.1",
|
||||
"jiti": "^2.7.0",
|
||||
"lightningcss": "1.32.0",
|
||||
"magic-string": "^0.30.21",
|
||||
"source-map-js": "^1.2.1",
|
||||
"tailwindcss": "4.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz",
|
||||
"integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-android-arm64": "4.3.3",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.3.3",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.3.3",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.3.3",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.3.3",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.3.3",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.3.3",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.3.3",
|
||||
"@tailwindcss/oxide-wasm32-wasi": "4.3.3",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.3.3",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz",
|
||||
"integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz",
|
||||
"integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz",
|
||||
"integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz",
|
||||
"integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz",
|
||||
"integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz",
|
||||
"integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz",
|
||||
"integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz",
|
||||
"integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz",
|
||||
"integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz",
|
||||
"integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==",
|
||||
"bundleDependencies": [
|
||||
"@napi-rs/wasm-runtime",
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@tybys/wasm-util",
|
||||
"@emnapi/wasi-threads",
|
||||
"tslib"
|
||||
],
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.11.1",
|
||||
"@emnapi/runtime": "^1.11.1",
|
||||
"@emnapi/wasi-threads": "^1.2.2",
|
||||
"@napi-rs/wasm-runtime": "^1.1.4",
|
||||
"@tybys/wasm-util": "^0.10.2",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz",
|
||||
"integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz",
|
||||
"integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/vite": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz",
|
||||
"integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tailwindcss/node": "4.3.3",
|
||||
"@tailwindcss/oxide": "4.3.3",
|
||||
"tailwindcss": "4.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.2.0 || ^6 || ^7 || ^8"
|
||||
}
|
||||
},
|
||||
"node_modules/@trickfilm400/rollup-plugin-off-main-thread": {
|
||||
"version": "3.0.0-pre1",
|
||||
"resolved": "https://registry.npmjs.org/@trickfilm400/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-3.0.0-pre1.tgz",
|
||||
@@ -2668,14 +2980,14 @@
|
||||
"version": "15.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
||||
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
|
||||
"integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
@@ -3064,6 +3376,27 @@
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
@@ -3134,7 +3467,7 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-view-buffer": {
|
||||
@@ -3255,6 +3588,16 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@@ -3293,6 +3636,20 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.24.5",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz",
|
||||
"integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.24.2",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz",
|
||||
@@ -4448,6 +4805,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
|
||||
"integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -4520,6 +4887,267 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
||||
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"lightningcss-android-arm64": "1.32.0",
|
||||
"lightningcss-darwin-arm64": "1.32.0",
|
||||
"lightningcss-darwin-x64": "1.32.0",
|
||||
"lightningcss-freebsd-x64": "1.32.0",
|
||||
"lightningcss-linux-arm-gnueabihf": "1.32.0",
|
||||
"lightningcss-linux-arm64-gnu": "1.32.0",
|
||||
"lightningcss-linux-arm64-musl": "1.32.0",
|
||||
"lightningcss-linux-x64-gnu": "1.32.0",
|
||||
"lightningcss-linux-x64-musl": "1.32.0",
|
||||
"lightningcss-win32-arm64-msvc": "1.32.0",
|
||||
"lightningcss-win32-x64-msvc": "1.32.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-android-arm64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
|
||||
"integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-arm64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
|
||||
"integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-x64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
|
||||
"integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-freebsd-x64": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
|
||||
"integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
|
||||
"integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
|
||||
"integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-musl": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
|
||||
"integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-gnu": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
|
||||
"integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-musl": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
|
||||
"integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
|
||||
"integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-x64-msvc": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
|
||||
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
@@ -4549,6 +5177,15 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "1.42.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.42.0.tgz",
|
||||
"integrity": "sha512-b3jprplnoLS8n5etw1z8xODe3hF/yjKATrTitsrKrnjUhCef5BdDct6Ppv3zVvzFwmtfWgLO6XNM3C9fAD93ug==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -5529,6 +6166,37 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
|
||||
"integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz",
|
||||
"integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
|
||||
"integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/temp-dir": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
||||
|
||||
@@ -11,17 +11,24 @@
|
||||
"test:e2e:ui": "playwright test --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "^1.3.3",
|
||||
"@zxing/browser": "^0.1.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"lucide-react": "^1.42.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.26.2"
|
||||
"react-router-dom": "^6.26.2",
|
||||
"tailwind-merge": "^3.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.47.2",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.6",
|
||||
"vite-plugin-pwa": "^0.20.5"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Badge } from "../ui/badge";
|
||||
|
||||
export type Readiness = "ready" | "limited" | "not-ready" | "unknown";
|
||||
|
||||
const LABEL: Record<Readiness, string> = {
|
||||
@@ -7,18 +9,18 @@ const LABEL: Record<Readiness, string> = {
|
||||
unknown: "Unbekannt",
|
||||
};
|
||||
|
||||
const ICON: Record<Readiness, string> = {
|
||||
ready: "🟢",
|
||||
limited: "🟡",
|
||||
"not-ready": "🔴",
|
||||
unknown: "⚪",
|
||||
const VARIANT: Record<Readiness, "success" | "warning" | "danger" | "neutral"> = {
|
||||
ready: "success",
|
||||
limited: "warning",
|
||||
"not-ready": "danger",
|
||||
unknown: "neutral",
|
||||
};
|
||||
|
||||
const KLASSE: Record<Readiness, string> = {
|
||||
ready: "badge-ready",
|
||||
limited: "badge-limited",
|
||||
"not-ready": "badge-not-ready",
|
||||
unknown: "badge-unknown",
|
||||
const PUNKT_FARBE: Record<Readiness, string> = {
|
||||
ready: "bg-success",
|
||||
limited: "bg-warning",
|
||||
"not-ready": "bg-danger",
|
||||
unknown: "bg-muted-foreground",
|
||||
};
|
||||
|
||||
interface Props {
|
||||
@@ -29,12 +31,14 @@ interface Props {
|
||||
/**
|
||||
* einzige Stelle, die Einsatzbereitschaft farblich/textlich
|
||||
* darstellt - "unbekannt" (noch nie kontrolliert) sieht bewusst NIE wie
|
||||
* "einsatzbereit" aus (eigene Farbe/Icon, siehe global.css badge-unknown).
|
||||
* "einsatzbereit" aus (eigene Farbe, kein Emoji-Icon mehr - konsistenter auf
|
||||
* Wandmonitoren, wo Emoji-Rendering je nach OS variiert).
|
||||
*/
|
||||
export function ReadinessBadge({ status, label }: Props) {
|
||||
return (
|
||||
<span className={`badge ${KLASSE[status]}`}>
|
||||
{ICON[status]} {label ?? LABEL[status]}
|
||||
</span>
|
||||
<Badge variant={VARIANT[status]}>
|
||||
<span className={`h-1.5 w-1.5 rounded-full ${PUNKT_FARBE[status]}`} />
|
||||
{label ?? LABEL[status]}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import type { HTMLAttributes } from "react";
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
const badgeVariants = cva("inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium", {
|
||||
variants: {
|
||||
variant: {
|
||||
neutral: "bg-surface-sunken text-muted-foreground",
|
||||
success: "bg-success-bg text-success",
|
||||
warning: "bg-warning-bg text-warning",
|
||||
danger: "bg-danger-bg text-danger",
|
||||
primary: "bg-primary-soft text-primary",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "neutral",
|
||||
},
|
||||
});
|
||||
|
||||
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {}
|
||||
|
||||
export function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { forwardRef, type ButtonHTMLAttributes } from "react";
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-white hover:bg-primary-hover",
|
||||
secondary: "bg-surface-sunken text-foreground border border-border hover:bg-border/40",
|
||||
ghost: "hover:bg-surface-sunken text-foreground",
|
||||
danger: "bg-danger text-white hover:opacity-90",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 px-3 text-xs",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
|
||||
}
|
||||
);
|
||||
Button.displayName = "Button";
|
||||
@@ -0,0 +1,29 @@
|
||||
import { forwardRef, type HTMLAttributes } from "react";
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
export const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("rounded-xl border border-border bg-surface text-foreground shadow-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Card.displayName = "Card";
|
||||
|
||||
export const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex items-center justify-between gap-2 px-5 pt-4 pb-2", className)} {...props} />
|
||||
));
|
||||
CardHeader.displayName = "CardHeader";
|
||||
|
||||
export const CardTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h3 ref={ref} className={cn("text-sm font-semibold tracking-wide text-muted-foreground uppercase", className)} {...props} />
|
||||
)
|
||||
);
|
||||
CardTitle.displayName = "CardTitle";
|
||||
|
||||
export const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("px-5 pb-5", className)} {...props} />
|
||||
));
|
||||
CardContent.displayName = "CardContent";
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]): string {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
import App from "./App";
|
||||
import "./styles/global.css";
|
||||
import "./styles/tailwind.css";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
|
||||
@@ -2,8 +2,11 @@ import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { apiRequest } from "../../api/client";
|
||||
import type { HistorieEintrag, Mangel } from "../../api/types";
|
||||
import type { DashboardTileId } from "./layout";
|
||||
import { layoutLaden, layoutSpeichern, STANDARD_REIHENFOLGE } from "./layout";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { cn } from "../../lib/utils";
|
||||
import type { DashboardTileGroesse, DashboardTileId } from "./layout";
|
||||
import { flexBasisFuerGroesse, GROESSEN_REIHENFOLGE, layoutLaden, layoutSpeichern, STANDARD_REIHENFOLGE } from "./layout";
|
||||
import type { TileAkzent } from "./TileFrame";
|
||||
import { TileFrame } from "./TileFrame";
|
||||
import { AblaufdatenTile } from "./tiles/AblaufdatenTile";
|
||||
import { AktivitaetTile } from "./tiles/AktivitaetTile";
|
||||
@@ -41,6 +44,7 @@ export function DashboardPage() {
|
||||
const [layout, setLayout] = useState(() => layoutLaden());
|
||||
const [bearbeitenModus, setBearbeitenModus] = useState(false);
|
||||
const [monitorModus, setMonitorModus] = useState(false);
|
||||
const [zuletztAktualisiert, setZuletztAktualisiert] = useState<Date | null>(null);
|
||||
|
||||
const ladeDaten = useCallback(() => {
|
||||
return Promise.all([
|
||||
@@ -61,6 +65,7 @@ export function DashboardPage() {
|
||||
setQualifikationsablaeufe(q);
|
||||
setAktivitaet(h);
|
||||
setFehler(null);
|
||||
setZuletztAktualisiert(new Date());
|
||||
})
|
||||
.catch(() => setFehler("Dashboard-Daten konnten nicht geladen werden."));
|
||||
}, []);
|
||||
@@ -102,8 +107,16 @@ export function DashboardPage() {
|
||||
layoutSpeichern(neuesLayout);
|
||||
}
|
||||
|
||||
function groesseAendern(id: DashboardTileId) {
|
||||
const aktuelle = layout.groessen[id] ?? "normal";
|
||||
const naechsteIndex = (GROESSEN_REIHENFOLGE.indexOf(aktuelle) + 1) % GROESSEN_REIHENFOLGE.length;
|
||||
const neuesLayout = { ...layout, groessen: { ...layout.groessen, [id]: GROESSEN_REIHENFOLGE[naechsteIndex] } };
|
||||
setLayout(neuesLayout);
|
||||
layoutSpeichern(neuesLayout);
|
||||
}
|
||||
|
||||
function layoutZuruecksetzen() {
|
||||
const neuesLayout = { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [] };
|
||||
const neuesLayout: typeof layout = { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [], groessen: {} };
|
||||
setLayout(neuesLayout);
|
||||
layoutSpeichern(neuesLayout);
|
||||
}
|
||||
@@ -128,6 +141,25 @@ export function DashboardPage() {
|
||||
const pruefungenUeberfaellig = prueftermine.filter((p) => p.status === "abgelaufen").length;
|
||||
const ablaufdatenAbgelaufen = ablaufdaten.filter((a) => a.status === "abgelaufen").length;
|
||||
|
||||
function tileAkzent(id: DashboardTileId): TileAkzent {
|
||||
switch (id) {
|
||||
case "maengel":
|
||||
if (maengelKritischAnzahl > 0) return "danger";
|
||||
if (maengelNeuAnzahl > 0) return "warning";
|
||||
return "neutral";
|
||||
case "prueftermine":
|
||||
return pruefungenUeberfaellig > 0 ? "danger" : "neutral";
|
||||
case "ablaufdaten":
|
||||
return ablaufdatenAbgelaufen > 0 ? "danger" : "neutral";
|
||||
case "einsatzbereitschaft":
|
||||
if (bereitschaft && bereitschaft.nicht_einsatzbereit > 0) return "danger";
|
||||
if (bereitschaft && bereitschaft.eingeschraenkt_einsatzbereit > 0) return "warning";
|
||||
return "neutral";
|
||||
default:
|
||||
return "neutral";
|
||||
}
|
||||
}
|
||||
|
||||
function tileInhalt(id: DashboardTileId) {
|
||||
switch (id) {
|
||||
case "aufgaben":
|
||||
@@ -163,44 +195,73 @@ export function DashboardPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="page-wide">
|
||||
<div className="row-between">
|
||||
<h1>Dashboard</h1>
|
||||
<div className="row" style={{ gap: "0.4rem" }}>
|
||||
<main className={cn("page-wide", monitorModus && "bg-background min-h-screen")}>
|
||||
{!monitorModus && (
|
||||
<button className="btn btn-secondary" onClick={() => setBearbeitenModus(!bearbeitenModus)}>
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
|
||||
{zuletztAktualisiert && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Aktualisiert um {zuletztAktualisiert.toLocaleTimeString("de-DE")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="secondary" onClick={() => setBearbeitenModus(!bearbeitenModus)}>
|
||||
{bearbeitenModus ? "Fertig" : "Anpassen"}
|
||||
</button>
|
||||
)}
|
||||
</Button>
|
||||
{bearbeitenModus && (
|
||||
<button className="btn btn-secondary" onClick={layoutZuruecksetzen}>
|
||||
<Button variant="secondary" onClick={layoutZuruecksetzen}>
|
||||
Zurücksetzen
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
<button className="btn btn-secondary" onClick={monitorUmschalten}>
|
||||
{monitorModus ? "Monitor beenden" : "Status-Monitor"}
|
||||
</button>
|
||||
<Button variant="secondary" onClick={monitorUmschalten}>
|
||||
Status-Monitor
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{monitorModus && (
|
||||
<Button variant="secondary" size="sm" className="fixed right-3 top-3 z-10 opacity-60" onClick={monitorUmschalten}>
|
||||
Monitor beenden
|
||||
</Button>
|
||||
)}
|
||||
{fehler && <div className="alert alert-danger">{fehler}</div>}
|
||||
{laedt && <p className="text-muted">Lade…</p>}
|
||||
{bearbeitenModus && layout.ausgeblendet.length > 0 && (
|
||||
<div className="mt-3 rounded-md border border-border bg-surface-sunken px-4 py-3 text-sm">
|
||||
Ausgeblendet:{" "}
|
||||
{layout.ausgeblendet.map((id, i) => (
|
||||
<span key={id}>
|
||||
{i > 0 && ", "}
|
||||
<button className="text-primary underline underline-offset-2" onClick={() => ausblendenUmschalten(id)}>
|
||||
{TITEL[id]} wieder einblenden
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!laedt && bereitschaft && (
|
||||
<div className="card-grid" style={monitorModus ? { fontSize: "1.15rem" } : undefined}>
|
||||
<div className={cn("card-grid mt-4", monitorModus && "text-lg")}>
|
||||
{layout.reihenfolge.map((id, index) => {
|
||||
const ausgeblendet = layout.ausgeblendet.includes(id);
|
||||
if (ausgeblendet && !bearbeitenModus) return null;
|
||||
const inhalt = tileInhalt(id);
|
||||
if (!inhalt) return null;
|
||||
const groesse: DashboardTileGroesse = layout.groessen[id] ?? (id === "aufgaben" ? "voll" : "normal");
|
||||
return (
|
||||
<TileFrame
|
||||
key={id}
|
||||
title={TITEL[id]}
|
||||
vollbreite={id === "aufgaben"}
|
||||
flexBasis={flexBasisFuerGroesse(groesse)}
|
||||
groesse={groesse}
|
||||
akzent={tileAkzent(id)}
|
||||
bearbeitenAktiv={bearbeitenModus}
|
||||
ausgeblendet={ausgeblendet}
|
||||
kannHochVerschieben={index > 0}
|
||||
kannRunterVerschieben={index < layout.reihenfolge.length - 1}
|
||||
onAusblendenUmschalten={() => ausblendenUmschalten(id)}
|
||||
onGroesseAendern={() => groesseAendern(id)}
|
||||
onHoch={() => tileVerschieben(id, -1)}
|
||||
onRunter={() => tileVerschieben(id, 1)}
|
||||
>
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { GROESSE_LABEL, type DashboardTileGroesse } from "./layout";
|
||||
|
||||
export type TileAkzent = "neutral" | "warning" | "danger";
|
||||
|
||||
const AKZENT_KLASSE: Record<TileAkzent, string> = {
|
||||
neutral: "border-l-border",
|
||||
warning: "border-l-warning",
|
||||
danger: "border-l-danger",
|
||||
};
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
vollbreite?: boolean;
|
||||
flexBasis: string;
|
||||
groesse: DashboardTileGroesse;
|
||||
akzent?: TileAkzent;
|
||||
bearbeitenAktiv: boolean;
|
||||
ausgeblendet: boolean;
|
||||
kannHochVerschieben: boolean;
|
||||
kannRunterVerschieben: boolean;
|
||||
onAusblendenUmschalten: () => void;
|
||||
onGroesseAendern: () => void;
|
||||
onHoch: () => void;
|
||||
onRunter: () => void;
|
||||
children: ReactNode;
|
||||
@@ -15,35 +31,58 @@ interface Props {
|
||||
|
||||
export function TileFrame({
|
||||
title,
|
||||
vollbreite,
|
||||
flexBasis,
|
||||
groesse,
|
||||
akzent = "neutral",
|
||||
bearbeitenAktiv,
|
||||
ausgeblendet,
|
||||
kannHochVerschieben,
|
||||
kannRunterVerschieben,
|
||||
onAusblendenUmschalten,
|
||||
onGroesseAendern,
|
||||
onHoch,
|
||||
onRunter,
|
||||
children,
|
||||
}: Props) {
|
||||
return (
|
||||
<div className="card" style={{ flexBasis: vollbreite ? "100%" : undefined, opacity: ausgeblendet ? 0.5 : 1 }}>
|
||||
<div className="row-between" style={{ marginBottom: "0.4rem" }}>
|
||||
<h3 style={{ margin: 0 }}>{title}</h3>
|
||||
<Card
|
||||
style={{ flexBasis }}
|
||||
className={cn("border-l-4 transition-opacity duration-300", AKZENT_KLASSE[akzent], ausgeblendet && "opacity-50")}
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
{bearbeitenAktiv && (
|
||||
<div className="row" style={{ gap: "0.3rem" }}>
|
||||
<button className="btn btn-secondary" onClick={onHoch} disabled={!kannHochVerschieben} title="Nach oben">
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="icon"
|
||||
className="min-h-11 min-w-11"
|
||||
onClick={onHoch}
|
||||
disabled={!kannHochVerschieben}
|
||||
title="Nach oben"
|
||||
>
|
||||
↑
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={onRunter} disabled={!kannRunterVerschieben} title="Nach unten">
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="icon"
|
||||
className="min-h-11 min-w-11"
|
||||
onClick={onRunter}
|
||||
disabled={!kannRunterVerschieben}
|
||||
title="Nach unten"
|
||||
>
|
||||
↓
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={onAusblendenUmschalten}>
|
||||
</Button>
|
||||
<Button variant="secondary" size="sm" className="min-h-11" onClick={onGroesseAendern} title="Größe ändern">
|
||||
{GROESSE_LABEL[groesse]}
|
||||
</Button>
|
||||
<Button variant="secondary" size="sm" className="min-h-11" onClick={onAusblendenUmschalten}>
|
||||
{ausgeblendet ? "Einblenden" : "Ausblenden"}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!ausgeblendet && children}
|
||||
</div>
|
||||
</CardHeader>
|
||||
{!ausgeblendet && <CardContent>{children}</CardContent>}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,9 +19,21 @@ export const STANDARD_REIHENFOLGE: DashboardTileId[] = [
|
||||
"aktivitaet",
|
||||
];
|
||||
|
||||
export type DashboardTileGroesse = "schmal" | "normal" | "breit" | "voll";
|
||||
|
||||
export const GROESSEN_REIHENFOLGE: DashboardTileGroesse[] = ["schmal", "normal", "breit", "voll"];
|
||||
|
||||
export const GROESSE_LABEL: Record<DashboardTileGroesse, string> = {
|
||||
schmal: "Schmal",
|
||||
normal: "Normal",
|
||||
breit: "Breit",
|
||||
voll: "Volle Breite",
|
||||
};
|
||||
|
||||
export interface DashboardLayout {
|
||||
reihenfolge: DashboardTileId[];
|
||||
ausgeblendet: DashboardTileId[];
|
||||
groessen: Partial<Record<DashboardTileId, DashboardTileGroesse>>;
|
||||
}
|
||||
|
||||
const SPEICHER_KEY = "mabea-dashboard-layout";
|
||||
@@ -30,19 +42,42 @@ function istGueltigeId(wert: unknown): wert is DashboardTileId {
|
||||
return typeof wert === "string" && (STANDARD_REIHENFOLGE as string[]).includes(wert);
|
||||
}
|
||||
|
||||
function istGueltigeGroesse(wert: unknown): wert is DashboardTileGroesse {
|
||||
return typeof wert === "string" && (GROESSEN_REIHENFOLGE as string[]).includes(wert);
|
||||
}
|
||||
|
||||
export function layoutLaden(): DashboardLayout {
|
||||
try {
|
||||
const roh = localStorage.getItem(SPEICHER_KEY);
|
||||
if (!roh) return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [] };
|
||||
if (!roh) return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [], groessen: {} };
|
||||
const geparst = JSON.parse(roh);
|
||||
const gespeicherteReihenfolge = Array.isArray(geparst.reihenfolge)
|
||||
? geparst.reihenfolge.filter(istGueltigeId)
|
||||
: [];
|
||||
const fehlende = STANDARD_REIHENFOLGE.filter((id) => !gespeicherteReihenfolge.includes(id));
|
||||
const ausgeblendet = Array.isArray(geparst.ausgeblendet) ? geparst.ausgeblendet.filter(istGueltigeId) : [];
|
||||
return { reihenfolge: [...gespeicherteReihenfolge, ...fehlende], ausgeblendet };
|
||||
const groessen: Partial<Record<DashboardTileId, DashboardTileGroesse>> = {};
|
||||
if (geparst.groessen && typeof geparst.groessen === "object") {
|
||||
for (const [id, groesse] of Object.entries(geparst.groessen)) {
|
||||
if (istGueltigeId(id) && istGueltigeGroesse(groesse)) groessen[id] = groesse;
|
||||
}
|
||||
}
|
||||
return { reihenfolge: [...gespeicherteReihenfolge, ...fehlende], ausgeblendet, groessen };
|
||||
} catch {
|
||||
return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [] };
|
||||
return { reihenfolge: STANDARD_REIHENFOLGE, ausgeblendet: [], groessen: {} };
|
||||
}
|
||||
}
|
||||
|
||||
export function flexBasisFuerGroesse(groesse: DashboardTileGroesse): string {
|
||||
switch (groesse) {
|
||||
case "schmal":
|
||||
return "14rem";
|
||||
case "breit":
|
||||
return "28rem";
|
||||
case "voll":
|
||||
return "100%";
|
||||
default:
|
||||
return "18rem";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Nur Theme+Utilities, kein Preflight - global.css bleibt für den Rest der App
|
||||
die Basis, Tailwind wird gezielt fürs Dashboard-Redesign genutzt. */
|
||||
@import "tailwindcss/theme.css" layer(theme);
|
||||
@import "tailwindcss/utilities.css" layer(utilities);
|
||||
|
||||
@theme {
|
||||
--color-background: var(--color-bg);
|
||||
--color-surface: var(--color-surface);
|
||||
--color-surface-sunken: var(--color-surface-sunken);
|
||||
--color-border: var(--color-border);
|
||||
--color-border-strong: var(--color-border-strong);
|
||||
--color-foreground: var(--color-text);
|
||||
--color-muted-foreground: var(--color-text-muted);
|
||||
--color-primary: var(--color-primary);
|
||||
--color-primary-hover: var(--color-primary-hover);
|
||||
--color-primary-soft: var(--color-primary-soft);
|
||||
--color-danger: var(--color-danger);
|
||||
--color-danger-bg: var(--color-danger-bg);
|
||||
--color-warning: var(--color-warning);
|
||||
--color-warning-bg: var(--color-warning-bg);
|
||||
--color-success: var(--color-success);
|
||||
--color-success-bg: var(--color-success-bg);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { defineConfig } from "vite";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
@@ -9,6 +10,7 @@ import { VitePWA } from "vite-plugin-pwa";
|
||||
// nicht aus, ohne Background-Sync-Komplexität einzuführen.
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
react(),
|
||||
VitePWA({
|
||||
registerType: "autoUpdate",
|
||||
|
||||
Reference in New Issue
Block a user