test(team): Render-Harness, 151 neue Tests und README
Vorher gab es im Repo kein Muster für Komponententests — @testing-library/react wurde ausschliesslich für renderHook verwendet. test/teamTestUtils.tsx stellt den fehlenden Kontext bereit (Router, MUI-Theme, frischer QueryClient je Test). test/setup.ts registriert jetzt afterEach(cleanup). vitest.config.ts setzt `globals` nicht, deshalb erkennt Testing Library kein globales afterEach und richtet sein automatisches Cleanup nie ein; ohne diese Zeile stapeln sich gerenderte Komponenten und getByRole scheitert ab dem zweiten Test einer Datei. Abgedeckt: Kennzahlenberechnung, Freigabe-, Zurückweisungs-, Anpassungs- und Rückfrageprozess, lokale Persistenz samt Schemaversion und Demo-Reset, Badge-Familie, Vorgangs- und Agentenkarte, Aufgaben-Aktivierung, Einstellungsvalidierung und die Position des Navigationseintrags. Die Tests haben fünf echte Mängel aufgedeckt, alle in diesem Commit behoben: - AgentAvatar hatte keinen zugänglichen Namen. MUI reicht `alt` nur an den img-Slot weiter, und ohne `src` rendert Avatar gar kein <img> — der Wert landete nirgends im DOM. Jetzt role="img" + aria-label direkt am Element. - WorkItemCard verschluckte objectLabel, wenn keine objectId gesetzt war. Genau dort steht bei zuordnungsfreien Vorgängen die entscheidende Einordnung. - aria-current rendert nicht mehr "false", sondern entfällt. - AgentChannelBadge war auf `string` statt AgentChannelType typisiert. 414 Tests grün (vorher 263), Build grün, ESLint über den Property-On-Code sauber, check:tokens unverändert bei 2249. README ersetzt das unveränderte Vite-Template durch Startanleitung und die Architekturentscheidungen samt bekannter Altlasten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,73 +1,129 @@
|
||||
# React + TypeScript + Vite
|
||||
# Property Match
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
Decision-Intelligence-Plattform für kommerziell genutzte Immobilien — kein Portal, sondern
|
||||
eine Oberfläche, die Treffer bewertet, Trade-offs erklärt und eine nächste Handlung empfiehlt.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
Verbindliche Entwicklungsregeln stehen in [CLAUDE.md](./CLAUDE.md), Architekturdiagramme in
|
||||
[ARCHITECTURE.md](./ARCHITECTURE.md), Zustandsregeln in [STATE_MANAGEMENT.md](./STATE_MANAGEMENT.md).
|
||||
|
||||
- [@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/)
|
||||
## Starten
|
||||
|
||||
## 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...
|
||||
},
|
||||
},
|
||||
])
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Entwicklungsserver auf http://localhost:5173
|
||||
```
|
||||
|
||||
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:
|
||||
Es gibt kein Backend und keine Datenbank. Sämtliche Daten stammen aus TypeScript-Mockdaten
|
||||
unter `src/mock-data/` und werden über Mockup-Provider bereitgestellt.
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import reactX from 'eslint-plugin-react-x'
|
||||
import reactDom from 'eslint-plugin-react-dom'
|
||||
| Skript | Zweck |
|
||||
|---|---|
|
||||
| `npm run dev` | Entwicklungsserver mit HMR |
|
||||
| `npm run build` | Typecheck (`tsc -b`) und Produktionsbuild |
|
||||
| `npm run lint` | ESLint über das gesamte Projekt |
|
||||
| `npm test` | Vitest einmalig ausführen |
|
||||
| `npm run test:watch` | Vitest im Watch-Modus |
|
||||
| `npm run check:tokens` | Zählt rohe Hex-Farbwerte gegen einen Schwellwert |
|
||||
|
||||
## Architektur in vier Schichten
|
||||
|
||||
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...
|
||||
},
|
||||
},
|
||||
])
|
||||
```
|
||||
Provider → Service → Hook (React Query) → Component
|
||||
```
|
||||
|
||||
- **Provider** (`src/provider/`) sind der einzige Ort, der Datenhaltung berührt.
|
||||
- **Services** (`src/services/`) tragen die Fachlogik und liefern `ListResponse<T>` / `ItemResponse<T>`.
|
||||
- **Hooks** (`src/hooks/`) kapseln React Query; Stale-Zeiten kommen aus `src/lib/constants.ts`.
|
||||
- **Komponenten** (`src/components/`, `src/pages/`) sind reine Darstellung.
|
||||
|
||||
Drei geschützte Arbeitsbereiche: `/supply/*` (Bewirtschaftung), `/demand/*` (Suche),
|
||||
`/ops/*` (interner Betrieb).
|
||||
|
||||
---
|
||||
|
||||
## Property On — «Teamübersicht»
|
||||
|
||||
Property On ist als Funktionsbereich in Property Match eingebettet, nicht als eigene
|
||||
Anwendung. Es gibt keine zweite App-Shell, keine zweite Sidebar und kein eigenes Branding.
|
||||
Erreichbar über den Hauptreiter **Teamübersicht** in der Bewirtschaftungs-Navigation, mit den
|
||||
Subreitern **Personalverwaltung**, **Bearbeitungsverlauf** und **Kanäle & Systeme**.
|
||||
|
||||
Bedienprinzip: **human-led, agent-operated.** Sieben digitale Mitarbeiter führen Arbeiten aus,
|
||||
der Mensch gibt frei, passt an oder weist zurück. Jede Entscheidung erzeugt einen
|
||||
Protokolleintrag, aktualisiert die Kennzahlen und meldet sich mit einer Rückmeldung.
|
||||
|
||||
### Routen
|
||||
|
||||
```
|
||||
/supply/team Teamübersicht (Startseite)
|
||||
/supply/team/personalverwaltung Agentenliste + Dossier
|
||||
/supply/team/personalverwaltung/:agentId Personalblatt
|
||||
/supply/team/personalverwaltung/:agentId/:tab Dossier-Reiter, deep-linkbar
|
||||
tab: aufgaben | kanaele | systeme
|
||||
einstellungen | protokoll
|
||||
/supply/team/bearbeitungsverlauf Vorgänge
|
||||
/supply/team/bearbeitungsverlauf/:tab tab: pendente-anfragen | erledigte-auftraege
|
||||
/supply/team/kanaele-systeme Verbindungen der Organisation
|
||||
```
|
||||
|
||||
### Architekturentscheidungen
|
||||
|
||||
**Der Entitätstyp heisst `TeamAgent`, nicht `Agent`.** Im Repo existiert bereits `PowerOn` als
|
||||
Name des KI-Backend-Proxys, und Claude Code legt Arbeitskopien unter `agent-*` ab. Ein nackter
|
||||
Typ `Agent` wäre in Suchen praktisch nicht auffindbar. Alle Satellitentypen tragen das Präfix
|
||||
`Agent*` — `AgentStatus`, `AgentChannelType`, `AgentProtocolEntry` —, weil `src/domain/index.ts`
|
||||
per `export *` bündelt und generische Namen dort kollidieren würden.
|
||||
|
||||
**Eigener Protokolltyp statt `ActivityEvent`.** Den Namen gibt es im Repo dreifach und
|
||||
gegenseitig inkompatibel: in `domain/activityEvent.ts`, lokal in `services/governanceService.ts`
|
||||
und noch einmal in `components/supply/PropertyActivityLogPanel.tsx`. Ein Anschluss an einen
|
||||
dieser Typen hätte den Konflikt zementiert; `AgentProtocolEntry` steht bewusst daneben.
|
||||
|
||||
**Verbindungen sind von `DataSource` getrennt.** `DataSource`/`ConnectorRun` modelliert
|
||||
Crawler-Quellen mit AGB-Status und Crawl-Läufen. «Kanäle & Systeme» beschreibt angebundene
|
||||
Kommunikationskanäle und Fachsysteme — fachlich etwas anderes. Die Ops-Komponenten dienten als
|
||||
visuelles Vorbild, das Datenmodell ist eigenständig.
|
||||
|
||||
**Eine Demo-Uhr statt der Systemzeit** (`src/lib/teamClock.ts`). Die Mockdaten sind fachlich
|
||||
auf den 20.05.2026 verankert. Gegen die echte Systemzeit gerechnet wären die Zeitraumfilter
|
||||
«heute / diese Woche / dieser Monat» an jedem anderen Kalendertag leer, und die Auswertung
|
||||
sähe kaputt aus, obwohl sie korrekt arbeitet. Die Demo-Uhr startet am Anker und läuft ab
|
||||
Anwendungsstart in Echtzeit weiter — jede Freigabe während einer Vorführung landet damit
|
||||
verlässlich im Bucket «heute». Sie gilt ausschliesslich für Property On.
|
||||
|
||||
**Persistenz liegt in der Providerschicht.** Geänderte Einstellungen, Aufgabenschalter,
|
||||
erledigte Vorgänge, Verbindungsstatus und pausierte Mitarbeiter überleben einen Reload über
|
||||
den Local Storage. Die Bestände tragen eine Schemaversion (`TEAM_SCHEMA_VERSION`): passt sie
|
||||
nicht, greift wieder der Seed aus `src/mock-data/` — sonst überlagerte ein alter Eintrag
|
||||
stillschweigend geänderte Mockdaten. «Demo zurücksetzen» löscht genau diese Schlüssel.
|
||||
|
||||
**Subreiter sind echte Routen, keine lokalen Tabs.** Die Anwendung kannte bis dahin keine
|
||||
Feature-Route mit eigenem `<Outlet/>`, und Tabs werden sonst über lokalen `useState` gebaut.
|
||||
Hier war das nicht tragfähig: die drei Subreiter müssen in der Sidebar sichtbar sein und
|
||||
deep-linkbar bleiben. Das `NavItem`-Modell in `appShellConfig.ts` wurde deshalb um `children`
|
||||
erweitert; die Routen sind flache Geschwister, kein Outlet-Baum.
|
||||
|
||||
**Kein neuer Stack.** Der ursprüngliche Auftrag nannte shadcn/ui, React Hook Form, date-fns und
|
||||
Playwright. Nichts davon ist installiert, und die Vorgabe, die bestehenden Property-Match-Muster
|
||||
zu übernehmen, wiegt schwerer. Umgesetzt mit MUI v9, TanStack Query, Zustand und Zod;
|
||||
Datumsformatierung über `Intl` in `src/lib/utils.ts` und `src/lib/teamClock.ts`. Besonders
|
||||
relevant: Tailwind-Preflight ist bewusst nicht importiert — shadcn erwartet es und würde den
|
||||
`CssBaseline`-Reset von MUI überschreiben.
|
||||
|
||||
### Simulation
|
||||
|
||||
Alles ist Frontend-Simulation. Es gibt keine echten Systemverbindungen, keine echten
|
||||
Zugangsdaten und keine echten Modellaufrufe. Simulierte Ladezeiten liegen zwischen 300 und
|
||||
800 ms. Der Test einer Verbindung ist **deterministisch** und nicht zufällig: verbundene
|
||||
Kanäle gelingen, nicht verbundene und geplante scheitern reproduzierbar — ein Zufallsfehler
|
||||
würde eine Kundendemo unvorhersehbar machen.
|
||||
|
||||
### Bekannte Altlasten
|
||||
|
||||
- `npm run check:tokens` liegt bei 2249 rohen Hex-Werten über dem Schwellwert von 1958. Der
|
||||
Wert stammt vollständig aus dem Bestand; Property On hat null hinzugefügt. Der Schwellwert
|
||||
darf laut Skript nicht angehoben werden.
|
||||
- `npm run lint` meldet im Bestand weiterhin Fehler und Warnungen. Der Property-On-Code ist
|
||||
frei davon: `npx eslint src/components/team src/pages/supply/Teamuebersicht.tsx …` ist grün.
|
||||
- In `tsconfig` ist `strict` entgegen CLAUDE.md §14 nicht gesetzt; `strictNullChecks` fehlt
|
||||
damit projektweit.
|
||||
|
||||
Reference in New Issue
Block a user