a7e05fb7f0
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>
130 lines
7.0 KiB
Markdown
130 lines
7.0 KiB
Markdown
# Property Match
|
|
|
|
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.
|
|
|
|
Verbindliche Entwicklungsregeln stehen in [CLAUDE.md](./CLAUDE.md), Architekturdiagramme in
|
|
[ARCHITECTURE.md](./ARCHITECTURE.md), Zustandsregeln in [STATE_MANAGEMENT.md](./STATE_MANAGEMENT.md).
|
|
|
|
## Starten
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev # Entwicklungsserver auf http://localhost:5173
|
|
```
|
|
|
|
Es gibt kein Backend und keine Datenbank. Sämtliche Daten stammen aus TypeScript-Mockdaten
|
|
unter `src/mock-data/` und werden über Mockup-Provider bereitgestellt.
|
|
|
|
| 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
|
|
|
|
```
|
|
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.
|