Der nachgebaute Meetingraum entfällt. Eine gezeichnete Kulisse bleibt neben
fotografierten Porträts erkennbar gezeichnet — und ein Raum allein macht
niemanden menschlich. Menschlich wirkt im echten Büro dasselbe wie hier: dass
jemand da ist, etwas zu sagen hat und nachweisbar gearbeitet hat.
Die Startseite zeigt deshalb sechs Personenkarten vor einer abgedunkelten
Büroaufnahme. Jede Karte trägt, was ein Personalblatt trägt:
- Porträt mit Dienstpunkt — ist da oder pausiert
- Name, Funktion, Abteilung, Personalnummer
- Autonomiestufe: «Autonom», «Freigabe erforderlich», «Nur Vorschlag».
Sie sagt, wie viel Verantwortung jemand trägt, und trennt Personal von
Automatisierung.
- Ein Satz in der ersten Person zur aktuellen Lage, statt einer Kennzahl:
«Bei 12 Fristen ist der Termin durch. Fürs Eskalieren brauche ich deine
Freigabe.»
- Die zuletzt protokollierte Handlung mit Zeitangabe. Diese Zeile ist der
Unterschied zwischen Behauptung und Beleg — eine Kennzahl kann jede
Software anzeigen.
Darüber fasst ein Anriss zusammen, wie viele Mitarbeitende heute eine
Entscheidung brauchen, samt Dienststand der Belegschaft.
Schichten nach CLAUDE.md §4.2: `agentPresenceService` entscheidet, was als
letzte Handlung gilt und wie eine Zeitangabe in Alltagssprache lautet;
`agentWorkloadService` hält Zeitfenster, Schwellen und Formulierungen. Beide
nehmen den Stichzeitpunkt als Parameter — dieselbe Datenlage ergibt immer
dasselbe Ergebnis und ist ohne Renderer prüfbar (22 Tests).
Giorgio hat kein Personalblatt; Abteilung, Personalnummer und Stufe kommen
aus der zentralen Rollendefinition. Wer kein Dossier hat, gilt als im Dienst —
sonst stünde die halbe Belegschaft still, weil Stammdaten fehlen.
Bild: Unsplash, freie kommerzielle Lizenz. Herkunft und Austauschhinweis in
`src/assets/meeting-room/HERKUNFT.md`.
Geprüft: 435 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Browser 1280–1920 px und Tablet: sechs Karten ohne Überlappung, Titel und
«Filter & Übersicht» beim Einstieg sichtbar, alle sechs Klicks auf der
richtigen Subpage, keine Konsolenfehler, keine 404.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die gezeichnete Raumkulisse ist ersetzt. Aus Farbflächen konstruierte Wände
bleiben neben fotografierten Agentenporträts erkennbar gezeichnet — die
Bildwirkung muss aus dem Foto kommen, nicht aus Geometrie. Entfallen sind
damit die beiden SVG-Stadtansichten, die CSS-3D-Flächen und die Farbwelt
DS_MEETING_ROOM.
Neu trägt die Szene die Basler Aussicht aus dem Auftrag als Foto (§5), mit
abgestuftem Verlauf für Lesbarkeit und Übergang zur Objektliste. Ein Austausch
des Motivs ist ein Einzeiler in `roomBackdrop.ts`.
Mehrwert statt Kulisse: Jedes Namensschild zeigt die aktuelle Auslastung
seines Agenten — überfällige Fristen, Termine der Woche, offene Exposés, neue
Marktchancen, Objekte mit Datenlücken, offene Freigaben. Darüber fasst ein
Anriss zusammen, wie viele Bereiche heute Aufmerksamkeit brauchen. Damit
beantwortet die Startseite die Frage, mit der sie geöffnet wird, statt sie
auf sechs Zahlen zu verteilen.
- `agentWorkloadService` hält die fachlichen Festlegungen: Zeitfenster,
Dringlichkeitsschwellen und Formulierungen. Der Stichzeitpunkt ist ein
Parameter, nicht die Systemuhr — dieselbe Datenlage ergibt dasselbe
Ergebnis, und der Dienst ist ohne Renderer prüfbar (10 neue Tests).
- `useAgentWorkload` sammelt nur die Bestände ein.
- Noras Zahl wird nie «dringend»: sie schlägt vor, sie entscheidet nicht.
- Die Schilder bemessen ihre Höchstbreite an der Szenenbreite (`cqw`), nicht
am Fenster. Am Fenster gemessen überlappten sie auf schmalen Laptops, weil
die Szene um das Seitenmenü schmaler ist.
Geprüft: 423 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Im Browser 1280–1920 px: keine Überlappungen, Titel und «Filter & Übersicht»
beim Einstieg sichtbar, alle sechs Agentenklicks auf der richtigen Subpage,
Tablet-Ansicht mit allen sechs Agenten, keine Konsolenfehler, keine 404.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Hauptnavigation und Startseite (§2)
- Hauptseite «Übersicht» samt Route, Menüeintrag, Hook, Service und den nur
dort verwendeten Widgets entfernt; /supply/dashboard leitet auf die
Startseite um, damit Lesezeichen nicht ins Leere laufen.
- «Meine Objekte» heisst im Menü «Startseite»; die frühere Überschrift
«Objektverwaltung» heisst jetzt «Meine Objekte».
- Die Startseite scrollt als Ganzes. Die Höhe des Meetingraums rechnet gegen
die Viewport-Höhe, damit Titel und Balken «Filter & Übersicht» beim Einstieg
auf jeder Monitorgrösse sichtbar bleiben (geprüft 1280–1920 px).
Agenten-Meetingraum (§3)
- Zentralperspektive aus clip-path-Flächen, ohne 3D-Engine und ohne
Animationsbibliothek: rechts geschlossene Wand, links und gegenüber
boden- bis deckenhohe Glasfronten mit Blick auf Rhein, Basler Münster,
Altstadt und Wettsteinbrücke.
- Sitzordnung, Zielrouten, Grössen und Plakettenversatz liegen in einer
zentralen Konfiguration (meetingRoomSeats.ts).
- Neuer Agent Giorgio, Personalverwalter, sitzt vis-à-vis und führt in
«Meine Agenten». Er steht bewusst nicht in AGENT_WORKSPACES: er hat keine
Arbeitsseite, sondern ist die Adminfläche selbst.
- Auf schmalen Schirmen tritt eine vereinfachte Kachelanordnung an die Stelle
der Perspektive; ausgeblendet wird kein Agent.
Sticky Chat bei allen Agenten (§5, §9–§11)
- Scrollt der Chatkopf unter den globalen Balken, übernimmt dort eine kompakte
Fassung mit Porträt, Name, Frage und Eingabefeld.
- Ein gemeinsamer Zustand (agentChatStore) statt zweier Chat-Instanzen:
Eingabetext und Fokus überstehen den Wechsel in beide Richtungen.
- Noras Seite scrollte bisher gar nicht — Chatkopf ausserhalb des
Scrollbereichs, zwei getrennt scrollende Spalten darunter. Behoben über den
Seitenaufbau, nicht über eine Nora-Sonderlösung.
Nora: CRM-Kanal und gemeinsame Lead-Liste (§12, §13)
- Dritter Kanal «CRM» mit eigenem Domänentyp, Mockdaten, Provider, Service und
Hook nach dem bestehenden Schichtenmuster.
- Die Reiter «KI-Signale»/«Netzwerk» sind einer gemeinsamen Liste gewichen.
Jede Zeile trägt ihre Kanalherkunft als Badge; die Herkunft steht im
Datenmodell (LeadChannel) und wird nicht aus der Darstellung geraten.
- Der frühere Reiterbalken ist der Filter «Kanaltyp» mit Alle/KI Signal/
Netzwerk/CRM, Standard «Alle», mit Empty State und Rücksetzung.
Produkttour und Profilmenü (§8)
- Tour auf die neue Struktur umgeschrieben: Startseite, Meetingraum, Agenten,
sticky Chat, Leadkanäle. «Übersicht» kommt nicht mehr vor, «Meine Objekte»
bezeichnet nur noch den Objektbereich.
- Bereich «Demo-Modus» mit «Verwaltung» und «Bürosuche» entfernt.
Geprüft: 413 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Im Browser: Navigation, Agentenklicks aller sechs Plätze, Kanalfilter und
Detailschubladen, Sticky Chat bei vier Agenten, 1280–1920 px sowie
Tablet- und Mobilbreite, keine 404 für Bilder oder Routen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Setzt das Umsetzungsbriefing Runde 4 im bestehenden Frontend um.
Informationsarchitektur
- «Teamübersicht» heisst «Meine Agenten»; ihr bisheriger Inhalt (Kreisgrafik,
Auswertung, Verbindungszusammenfassung) ist entfallen.
- Personalverwaltung, Bearbeitungsverlauf und Kanäle & Systeme sind jetzt
Reiter derselben Seite (?section=), gestaltet wie die Auswahl im
Bearbeitungsverlauf. Es wird nur der gewählte Bereich gerendert.
- Die alten Unterseitenpfade leiten um, damit Lesezeichen nicht brechen.
Agentenbestand
- Reto und Lea vollständig entfernt — aus Navigation, Dossiers, Protokoll,
Verbindungen, Vorgängen und Porträtbestand.
- Retos Aufgaben liegen bei Bruno: Nachbereitung, WhatsApp-Anruf,
Protokoll und Kundennotiz, Ablage im CRM.
- Livia ist «Exposé Master»: Lageberichte, Inserate, Angebotsbroschüren.
- Sidebar führt Ferdi, Bruno, Livia, Nora, Sina mit Porträt und Funktion.
Agentenseiten
- Ein gemeinsamer AgentWorkspaceHero auf allen fünf Seiten.
- Ferdi: Auswertungskarten, Priorität und Typfarben entfallen; Fälligkeit nur
bei fünf Tagen oder weniger rot; Objektlinks nach «Meine Objekte»; neu die
Terminplanung im verbundenen Kalender mit typgerechtem PDF-Ausschnitt.
- Sina: reduzierte, filter- und sortierbare Objektübersicht; Detailansicht
direkt editierbar, leere Pflichtfelder rot umrandet.
- Nora: Signale ohne Prozentsätze und Konfidenzstufen, nur belegbare Angaben;
Mehrfachauswahl leitet Objekte an Livia weiter.
- Livia: aktive und archivierte Leads, Arbeitsbereich gleitet an den oberen
Rand; dreistufiger Exposé-Prozess Hochladen → Exposé → Export.
- Bruno: neue Seite mit Auftragsliste und Vor-/Nachbereitungs-Drawer;
Glocke warnt bei Besichtigung unter 24 Stunden ohne Bericht.
Datenschicht
- Neu: Kalender, Exposé-Leads, Exposé-Entwürfe, Besichtigungsaufträge —
je Domain, Provider, Service und Hook.
- IAIService um generateExposeText erweitert; der Entwurf nutzt ausschliesslich
erfasste Objektdaten und meldet Lücken, statt sie zu füllen.
- Alle Objektverweise zeigen auf reale Einträge aus «Meine Objekte»; neue
Detailroute /supply/properties/:propertyId.
Offen: Chat, Kalender, CRM und DMS sind Frontend-Simulation ohne Anbindung.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kopfzeile: Der zusaetzliche Titelbalken mit dem Chip Verwaltung und dem
wiederholten Seitennamen entfaellt auf allen Hauptseiten, ebenso der
Einstieg AI Assistent. Ohne Trennlinie geht die Zeile bruchlos in den
Seiteninhalt ueber; Glocke und Profil bleiben rechts stehen. Der
Pilotkunde heisst sichtbar Beispiel Immobilien AG, die technische Kennung
org-wincasa bleibt unangetastet.
Flaechensuche und Anfragencenter sind aus Navigation, Routing und Branch
entfernt. Verweise, die dadurch ins Leere zeigten, haengen neu am Match
Center; der Quicklink ins Anfragencenter im Vorgangsdetail ist entfallen.
Meine Objekte: feste vier Karten je Zeile auf Desktop, zwei auf Tablet,
eine auf Mobil. Die Dichteauswahl 3/5/10 und der Datenpflege-Hinweis sind
weg; die Datenpflege bleibt als eigener Hauptreiter erreichbar.
Objektbilder liegen erstmals lokal. Zuvor zeigten 98 Objekte auf Unsplash
und teilten sich 29 Fotos, verschiedene Adressen trugen also dasselbe
Bild. Neu traegt jede Objekt-ID genau eine eigene Datei; die Zuordnung
laeuft ueber die stabile ID, nicht ueber den Titel. Die Dateien sind
erzeugte Vektorgrafiken, keine Fotografien - echte Aufnahmen ersetzen sie
spaeter unter demselben Dateinamen ohne Codeaenderung. Vite bettet sie
nicht mehr als Base64 ein, damit alle Objekte gleich behandelt sind.
Teamuebersicht zeigt nur noch das Kernteam; die Stufenfilter sind
entfallen und die aeusseren Ringe werden nicht mehr gezeichnet statt nur
durchsichtig zu sein - unsichtbare, aber anspringbare Schaltflaechen sind
eine Falle. Die Nabe nennt daher die Zahl der gezeigten Mitarbeitenden
statt der Gesamtzahl. Die Personalverwaltung hat nur noch die
Dossieransicht, der Umschalter und das drehbare Organigramm sind weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Darstellung reservierte Platz fuer Ringe, die gar nicht gezeichnet
wurden: das Kernteam sass auf dem innersten Ring, zwei Drittel des
Quadrats blieben leer. Neu rueckt der aeusserste sichtbare Ring an den
Rand, Portraits und Schrift wachsen gedaempft mit.
Zweitens verdeckte die Nabe die Beschriftungen. Der Transform am Ring
eroeffnet einen eigenen Stacking-Context, in dem das zIndex der Portraits
gefangen blieb; die spaeter im DOM stehende Nabe gewann. Der Ring traegt
nun selbst ein zIndex.
Die Nabe ergibt sich ausserdem aus dem tatsaechlich freien Raum bis zum
Kernring statt aus starren 19 Prozent, und Beschriftungen sind auf den
Bogenabstand zum Nachbarn begrenzt, damit sie sich nicht ueberschreiben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gezielte UI-Bereinigung ohne neue Architektur. Bestehende Komponenten angepasst,
Datenstrukturen und Routing unverändert.
GLOBAL
Chip «Verwaltung» und Seitenname in der Kopfzeile entfallen auf /supply/team/*;
die Seiten tragen ihren Titel bereits gross im Inhalt. Andere Module behalten die
Zeile. Demo-Modus-Hinweis und alle Untertitel entfernt. Autonomiegrad aus der
Oberfläche genommen — die Daten bleiben unangetastet.
PERSONALVERWALTUNG
Beschrieb auf zwei Sätze gekürzt, Input/Kernablauf/Output entfallen: das war
Prozessnotation, sie beschrieb wie gearbeitet wird, nicht wofür man jemanden hat.
Zuständigkeiten stehen jetzt im Kopfbereich statt tief im Dossier. Reiterfolge
neu: Agentenbeschrieb, Aufgaben, Kennzahlen, Kanäle & Systeme, Protokoll.
Kanäle und Systeme sind über eine dünne Hülle zusammengeführt — die bestehende
Logik samt Konfigurationsdialog bleibt unberührt. Aufgaben dreispaltig ohne
Zeitplan, Ereignisfilter im Protokoll entfernt.
Der abgelöste Reiter «Systeme» wird weiterhin aufgelöst, damit bestehende
Verweise nicht still auf den ersten Reiter zurückfallen.
ORGANIGRAMM — die eigentliche Korrektur
Der falsch gewählte Agent lag an der Geometrie: `rotate(a) translateY(r)` schiebt
bei a = 0 nach UNTEN, die Drehung wurde aber als `180 − index·step` berechnet.
Damit stand der gewählte Mitarbeitende oben und das Dossier darunter gehörte zum
falschen. Jetzt `−index·step`.
Dazu beruhigt: Zugempfindlichkeit von 0.5 auf 0.22 Grad je Pixel, 10 px Totzone
gegen Zittern, kurzer Zug wechselt höchstens einen Platz, Einrasten in 250 ms,
Auswahl erst nach dem Einrasten. Kreis von 720 auf 460 px.
BEARBEITUNGSVERLAUF
Filter ohne Vorgangstyp, Priorität und Kanal; Suche in Zeile eins, Rest in Zeile
zwei. Karten tragen nur noch Agent, Titel mit Liegenschaft, zwei Zeilen Text und
Zeitstempel — Priorität, Status, Vorgangstyp und Kanal standen auf praktisch
jeder Karte und trugen damit keine Information mehr.
Drawer ohne Verarbeitungsschritte und Aktionshistorie; nur die auslösende
Nachricht plus Quicklink ins Anfragencenter statt des ganzen Verlaufs.
«Fundstellen» heisst «Quellen». Der Entscheidungshinweis heisst jetzt
«Hierzu brauche ich Ihre Entscheidung». Aktionsleiste: pendent Freigeben,
Zurückweisen, Quellen anzeigen, Inserat anzeigen — erledigt nur «Objekt anzeigen».
KANÄLE & SYSTEME, TEAMÜBERSICHT
Kanäle vor Systemen als eigene Abschnitte, vier Karten pro Zeile, Beschreibung
und Statusbadge raus, Regler mit Klartext Aktiv/Inaktiv/Geplant. Die Kreisgrafik
skaliert nach Anzahl sichtbarer Ringe und ist auf die Viewport-Höhe begrenzt,
damit das Kernteam ohne Scrollen vollständig sichtbar bleibt.
Drei Testzusicherungen prüfen jetzt die Abwesenheit statt der Anwesenheit von
Objekt-ID und Rückfragegrund auf der Karte — beides wurde bewusst entfernt.
Typecheck grün, ESLint über die geänderten Dateien grün, Build grün, 413 Tests grün.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Setzt die Referenzgrafik «Agenten_Bewirtschaftung_v2» um: die 36 digitalen
Mitarbeitenden in konzentrischen Ringen, Kernteam innen und gold gerahmt.
DATENBASIS
Alle 36 Mitarbeitenden samt Porträts sind aus dem Konzeptdokument extrahiert.
Die Stufen stecken bereits in den Quelldaten und decken sich exakt mit den
Bereichen: lvl 1 = 7 Kernteam, lvl 2 = 18 Zentrale Dienste, lvl 3 = 7 GB
Kommerziell, lvl 4 = 4 GB Wohnen. Auch die Reiterbeschriftungen sind wörtlich
übernommen — keine neuen Stufennamen erfunden.
Neu getrennt: `AgentDirectoryEntry` trägt die Stammdaten aller 36 für die
Kreisdarstellung, `TeamAgent` bleibt das vollständige Dossier der sieben
Kernteammitglieder. Für die übrigen 29 werden bewusst keine Aufgaben, Kanäle
oder Tätigkeiten erfunden; ein Klick zeigt nur eine Vorschau aus dem Verzeichnis.
ABWEICHUNG VOM AUFTRAG
Der Auftrag nennt als Kernteam Ferdi, Sami, Leo, Bruno, Reto, Milo, Nora.
Referenzgrafik und Konzeptdaten sagen übereinstimmend Ferdi, Sina, Lea, Bruno,
Reto, Livia, Nora — dort tragen genau diese sieben `kern: true` und `lvl: 1`.
Da der Auftrag die Grafik selbst als verbindlich erklärt und Namen laut §10 kein
Primärschlüssel sind, folge ich Grafik und Daten. Sami, Leo und Milo kommen in
keiner Quelle vor.
OBERFLÄCHE
- Teamübersicht: Beschreibungstext und «Demo zurücksetzen» entfernt, Kreis mit
kumulativen Stufenreitern direkt unter dem Titel
- Personalverwaltung: Umschaltung Dossieransicht / Organigramm über ?view=,
Organigramm als oben abgeschnittener Halbkreis mit drehbarem Agentenrad
(Ziehen, Touch, Pfeiltasten, Klick), aktiv ist wer unten mittig einrastet
- Dossier: Untermenü direkt unter den Stammdaten, «Agentenbeschrieb» als erster
Reiter, Zuständigkeiten auf drei gekürzt
- Kanäle & Systeme: vier Karten pro Zeile, Kategorie-Sinnbild links vor dem
Titel, Porträtgruppe statt Namensliste, ein Regler statt Verbinden/Trennen
- Bearbeitungsverlauf: neutrale Karten, Farbe nur noch bei Ausnahmepriorität und
abweichendem Status, mehr Weissraum
Markenlogos gibt es im Repo nicht und wurden nicht nachgezeichnet — je Kategorie
steht ein neutrales Sinnbild. Fehlendes Asset, bewusst so gelöst.
Positionsdaten stehen ausschliesslich in `agentRingConfig.ts`; Vollkreis und
Halbkreis teilen sich dieselbe Geometrie.
AUFGERÄUMT
`AgentCard` ist durch die Kreisdarstellung ersetzt und entfernt, seine Zusagen
sind in `TeamOverviewRing.test.tsx` übergegangen — inklusive der wichtigsten:
ausserhalb des Kernteams führt kein Klick in ein Dossier.
Zwei echte Fehler unterwegs behoben: jsdom kennt keinen `ResizeObserver` (jetzt
mit Rückfallebene statt Absturz), und das Rad las eine Ref im Render.
413 Tests grün, Build grün, ESLint über den Property-On-Code sauber,
check:tokens unverändert bei 2249.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bisher zeigten die Avatare Initialen auf einer Tonfarbe. Ich hatte das bewusst
so gebaut, weil erfundene Gesichter in einer Kundendemo irreführend wären — das
Konzeptdokument «Agenten_Bewirtschaftung_v2.html» trifft diese Entscheidung
jedoch bereits: dort hängt an jedem der 36 Agenten ein Porträt als eingebettetes
JPEG. Die sieben Kernagenten sind daraus extrahiert.
- src/assets/team/*.jpg — sieben Porträts, je rund 6 KB, Zuordnung über die
Mailadresse aus dem Konzept, die exakt unseren Agenten-IDs entspricht
- agentPhotos.ts hält die Zuordnung in der Darstellungsschicht. Ein Domain-Typ,
der Binärdateien importiert, wäre an den Bundler gekoppelt und weder im Test
noch später gegen ein echtes Backend sauber verwendbar.
- Fehlt zu einer ID ein Bild, zeigt der Avatar weiterhin die Initialen; dasselbe
greift, wenn das Laden fehlschlägt
Der zugängliche Name hängt weiterhin an genau einem Element, nie an zweien: mit
Porträt am erzeugten <img> über `alt`, ohne Porträt am Container über role="img"
und aria-label.
vite-env.d.ts ergänzt — die Referenz auf vite/client fehlte, ohne sie kennt
TypeScript die Modul-Deklarationen für Assets nicht.
415 Tests grün, Build grün, die sieben JPEGs werden korrekt als Assets emittiert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Vier Seiten, eingebettet in die bestehende App-Shell — keine zweite Sidebar,
keine zweite Kopfzeile, kein eigenes Property-On-Branding:
- Teamübersicht: Auswertung (drei Kennzahlen, gemeinsam filterbar nach Zeitraum,
Bereich und Mitarbeiter), Kernteam, Kanäle & Systeme — in dieser Reihenfolge
- Bearbeitungsverlauf: zwei Reiter, neun Filterdimensionen, Detail-Drawer mit
Ergebnis, Eingabedaten, Verarbeitungsschritten, Fundstellen, Nachrichtenverlauf
und Aktionshistorie; Freigeben, Anpassen, Zurückweisen, Rückfrage beantworten
- Personalverwaltung: dreigeteilt (Hauptnavigation, Agentenliste, Dossier) mit
fünf Reitern — Aufgaben, Kanäle, Systeme, Einstellungen, Protokoll
- Kanäle & Systeme: neun Verbindungen, mehrstufiger Assistent, ERP-Auswahl,
deterministischer Verbindungstest
Navigation: NavItem um `children` erweitert, "Teamübersicht" steht exakt zwischen
"Meine Objekte" und "Reminder Manager", die drei Subreiter bleiben eingerückt
sichtbar, solange man im Funktionsbereich ist.
Subreiter sind echte Routen, keine lokalen Tabs. Die App kannte bisher keine
Feature-Route mit eigenem Outlet und baut Tabs sonst über lokalen useState — hier
trug das nicht: die Subreiter müssen in der Sidebar stehen und deep-linkbar sein.
Die Routen sind flache Geschwister, kein Outlet-Baum.
Freigabepflichtige Aktionen laufen über einen Bestätigungsdialog mit sichtbarer
Konsequenz; die 1-Klick-Bestätigung ist die fachlich vorgesehene Ausnahme. Jeder
Status trägt neben der Farbe immer einen Text.
Null rohe Hex-Werte: check:tokens bleibt unverändert bei 2249. Der in der
Spezifikation genannte dunkelblaue Banner "Heute geleistet …" existiert in dieser
Codebasis nicht — er wird deshalb auch nicht erzeugt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Property On wird als Funktionsbereich "Teamübersicht" in Property Match
eingebettet — sieben digitale Mitarbeiter, die ausführen, während der Mensch
freigibt, anpasst oder zurückweist.
Durchgehend Provider → Service → Hook, kein übersprungener Layer:
- domain/: teamAgent, agentWorkItem, agentProtocol, agentConnection, agentFilters
- mock-data/: 7 Personalblätter aus dem Agenten-Katalog (Aufgabenzahlen exakt:
Ferdi 9, Sina 7, Lea 11, Bruno 9, Reto 10, Livia 11, Nora 8), 14 Vorgänge,
38 Protokolleinträge, 9 Verbindungen
- provider/: vier Mockup-Provider mit versionierter localStorage-Persistenz
- services/: Fachlogik inkl. Freigabe, Zurückweisung, Kennzahlen, Demo-Reset
- hooks/: React Query, Mutationen entwerten Vorgänge, Kennzahlen und Protokoll
gemeinsam — sonst zeigt die Oberfläche widersprüchliche Zahlen
Namensgebung: Der Entitätstyp heisst TeamAgent, nicht Agent. Im Repo existiert
bereits PowerOn als KI-Backend-Proxy, und Claude Code legt Arbeitskopien unter
agent-* ab; ein nackter Typ Agent wäre in Suchen nicht auffindbar. Alle
Satellitentypen sind Agent*-präfixiert, weil domain/index.ts per export *
bündelt und generische Namen dort kollidieren.
Eigener Protokolltyp statt ActivityEvent: den Namen gibt es im Repo dreifach
und gegenseitig inkompatibel (domain/activityEvent.ts, governanceService.ts,
PropertyActivityLogPanel.tsx). Ein Anschluss hätte den Konflikt zementiert.
Demo-Uhr in lib/teamClock.ts: die Mockdaten sind auf den 20.05.2026 verankert.
Gegen die Systemzeit gerechnet wären die Filter "heute / diese Woche / dieser
Monat" an jedem anderen Kalendertag leer und die Auswertung sähe kaputt aus,
obwohl sie korrekt rechnet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`npx tsc -b` brach mit 16 Fehlern ab. Neue Fehler wären darin untergegangen.
Echte Defekte:
- LatentInquiriesTab: OwnPropertyMatchList wurde verwendet, aber nie importiert
— der Zweig "Eigene Objekte" auf Mobil hätte zur Laufzeit geworfen
- unitService: throwServiceError nimmt ein Argument, nicht zwei
- SavedNeedDetail: MUI v9 kennt kein `inputProps` mehr, ersetzt durch slotProps
- Review{Status,Priority}Badge: `showBorder` existiert an GenericBadge nicht;
die Default-Variante "outlined" zeichnet den Rand ohnehin, Darstellung unverändert
- UnifiedResultFeed: die Ternärkette verglich theme.text gegen zwei Werte, die
SCORE_THEME seit einer Palettenumstellung nicht mehr führt — sie lief immer in
denselben Zweig. Auf diesen einen Wert zusammengezogen, Darstellung unverändert,
nebenbei zwei rohe Hex-Werte weniger
Der Rest waren ungenutzte Importe und Bindings (noUnusedLocals).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
.claude/worktrees/agent-a82a3716/ lag mit 369 getrackten Dateien im Repo — eine
vollständige, veraltete Kopie von src/. Zwei Folgen: `eslint .` brach mit 965
fatalen Parse-Errors ab, weil der Worktree eine eigene tsconfig mitbringt und
tsconfigRootDir mehrdeutig wurde. Und jeder repo-weite Glob traf Duplikate, was
die reale Gefahr barg, Änderungen in der Kopie statt im Original zu machen.
- git rm -r --cached auf das Verzeichnis, Dateien auf der Platte bleiben
- .claude/worktrees/ in .gitignore
- .claude/** in die ESLint-Ignoreliste; Untracking allein genügt ESLint nicht,
es liest die Platte, nicht den Index
ESLint liefert damit statt 965 Parse-Errors wieder echte Befunde.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 3/5/10-Spalten-Umschalter im Ergebnis-Header (nur Grid-Ansicht, in localStorage gemerkt) analog zu "Meine Objekte"
- Karten-Bild auf aspectRatio 15/8 statt fester Höhe → keine Verzerrung bei beliebiger Spaltenzahl
- Orts-Overlay vom Bild entfernt → Match-Score überdeckt das Ort-Label nicht mehr
- Sidebar bleibt bis < lg (1200px) ausgeklappt → Navigation + Account auf Laptops sichtbar
- Größere vertikale Abstände im Ergebnis-Feed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add EmbeddedPropertyCard and surface matching properties inside the
public need detail view. Add needToParsedCriteria() mapper to convert a
stored Need back into parsed criteria for re-running search/matching.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "Gesendet" empty-state description embedded an ASCII double-quote inside a German „…" pair, which Vite/oxc parsed as the end of the attribute string (PARSE_ERROR). Reworded without embedded quotes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the messaging rework: one conversation/thread model (Inquiry) is the single source of truth, read perspectivally by both sides.
- domain/inquiry: add kind (INQUIRY|OFFER), tenantOrgId (demand org), offeredPropertyIds
- provider/service: InquiryFilters gains tenantOrgId+kind; new createInquiry (demand→supply) & createOffer (supply→demand) materialize threads; addMessage bumps unread; reply carries senderType (tenant vs supply_user)
- hooks: useCreateInquiry, useCreateOffer; reply payload carries sender perspective
- demand: InquiryQuickDialog → createInquiry via provider (session-based tenant/org, no hardcode); Anfragen.tsx reads via React Query (tenantOrgId) with Gesendet/Erhalten tabs, replies via provider, shows offered properties for OFFER threads; inquiryStore reduced to dialog-only (removes Zustand server-data island)
- supply: OfferChatComposer send now materializes an OFFER conversation into the seeker's inbox (routed via latentNeed.tenantOrgId); Anfragencenter gains a "Gesendet" tab; ActiveInquiriesTab filters by perspective (org + kind)
- seed: merge demand inquiries with tenantOrgId=org-mobimo + 2 OFFER seeds; provider seeds from both sets
Closes the loop: a sent inquiry reaches the Verwaltung and a sent offer reaches the Nachfrager — both answerable in one thread.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A newly created need (from "Jetzt suchen") had no organizationId, so it fell out of the org-filtered need list — the results "Aktive Suche" banner then showed a different (first saved) profile while the result cards already reflected the new search. needService.create now injects the current user's org, so banner and results stay consistent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
External third-party scrapes (Homegate/ImmoScout/newhome/MatchOffice) were mis-tagged as VERIFIED_PORTFOLIO in mock data and surfaced as "own platform" matches. Skip them in match generation — demand results now only contain genuine platform objects, Maison Work, and Future Availability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Released pre-market unit now shows a single always-visible config: fit-out/parking/cost-bearer (auto-save) THEN the KI price recommendation with explicit deviation vs. current price
- Removes the duplicate price (base price no longer shown here; only the pre-market expected price is set), and removes the edit-only pencil for released units
- Not-yet-released units keep the compact read strip + pencil editor
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- getMarketRent now returns CHF/m²/YEAR (stored medians are monthly → ×12); fixes annual-vs-monthly mismatch that also affected MarketPricePanel and NegotiationInsightsPanel
- recommendPreMarketRent: anchor on the unit's current rent and adjust within a bounded ±15% by market momentum (vacancy=supply, demand strength, days-on-market, trend) instead of jumping to the city-wide (prime-skewed) median — no more "double the price" suggestions; median only used as a headroom check
- tests updated to annual market values
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MockupUnitProvider.update: immutable update (new unit/units/property refs) so React Query detects the change and re-renders — fixes "parking/price edit doesn't take effect"
- UnitFieldsEditor: group Ausbaustandard + Wer-baut-aus + MAB into one "Ausbau" section; conditions (price/availability/parking) on top; removed the plain expected-price field
- PreMarketUnitGrid: expected price is now a KI recommendation view (indexed suggestion + rationale) shown only for released units, with an adjustable override + "Vorschlag übernehmen"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Extract UnitFieldsEditor (price, availability, Ausbaustandard, Wer-baut-aus, MAB, parking allocation, expected price) — single source for per-unit editing
- UnitStructurePanel: always-visible per-unit details strip (fit-out, cost-bearer, parking, expected price) in read mode; pencil opens the shared editor
- PreMarketUnitGrid: per-unit details shown + same editor reachable per unit (pencil) so fit-out/price/parking are adjustable right in the release flow
- Removes the previous edit-only / fragmented per-unit fields
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- lib/rentEstimate: deterministic market estimate from Standort-Intelligence (median rent benchmark + vacancy/trend) → fair rent, verdict BELOW/AT/ABOVE, deltaPct, rationale; suggestFutureRent indexes today's price by city rent trend
- MarketPricePanel in MatchDetail: asking vs. fair market rent + verdict badge + rationale; shows pre-market expected price (heute → erwartet)
- unit.expectedRentPerSqm: future pre-market price; scorer prefers it over current rent for FUTURE_AVAILABILITY
- UnitStructurePanel editor: expected-price field (with indexed suggestion) shown for pre-market-released units
- tests: rentEstimate verdict thresholds + future-rent indexing
Note: rent estimate is a deterministic market-data calc (not an LLM) — honest & testable; can be routed through IAIService later if a real model is wanted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- unit: add fitOutByLandlord, mieterausbaubeitragPerSqm, parkingSpots (parking = allocation from object pool)
- lib/unitFacts.resolveUnitFacts: single resolver (unit value ?? object value) used by scorer + card adapter
- matchSyncService: pre-market unit scored via resolved facts (fit-out, MAB, cost-bearer, parking allocation)
- matchCardAdapter: fit-out label/viability/investment via resolveUnitFacts
- UnitStructurePanel: consolidate ALL per-unit overrides into the one expandable row editor (price, availability, Ausbaustandard, Wer-baut-aus, MAB, parking allocation); show parking on row
- PreMarketUnitGrid/Panel: drop the duplicate per-unit fit-out select (now in the unit editor)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- unit.availableFrom: explicit per-unit availability date
- UnitStructurePanel: inline expandable editor (pencil) for per-unit price + availability via useUpdateUnit
- fitOutByLandlord now boolean|undefined ("not chosen"); toggle has no default for SHELL/BASIC
- NewListing: validate() blocks save until cost-bearer chosen for Rohbau/Edelrohbau
- PropertyDetailView.saveEdit: same guard for existing listings; required asterisk + hint in both forms
- newListingMapper persists explicit tenant(false) choice (was collapsed to undefined)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- domain/unit: optional per-unit fitOut override (falls back to property.hardFacts.fitOut)
- matchSyncService: pre-market unit scored with its own fitOut; matchCardAdapter prefers unit fitOut
- PreMarketUnitGrid/PreMarketPanel: per-unit Ausbaustandard override in the release list (default "Wie Objekt")
- PropertyDetailOverview: edit/read "Wer baut aus?" for existing listings (SHELL/BASIC), central FIT_OUT_LABELS
- Phase 1 unit-centric overview: header shows area+count, price as range, "Verfügbar ab → pro Einheit"; unit table shows per-unit price + availability; "Objekt & Lage" labels marked as object-defaults for multi-unit
- NeedExtendedRequirements: "Eigenes Ausbaubudget" only shown when min fit-out set + clearer helper text
- UnitStructurePanel: spin-off prefill carries fitOutByLandlord
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mieterausbau / fit-out economics — surface true total cost of occupancy:
- Annuity calc (annuityFactor + effectiveAnnualBurdenPerSqm) replaces straight-line ÷5; FITOUT_ANNUITY_RATE=5%
- New hardFacts.fitOutByLandlord: "Wer baut aus?" toggle in NewListing — landlord-borne fit-out is priced into rent (no surcharge), tenant-borne SHELL/BASIC adds annuitized cost minus MAB
- scoreBudget now compares effective annual burden (rent + fit-out annuity) vs budget instead of cold rent only; FULL/PREMIUM and landlord-borne unchanged
- FitOutCostPanel + CompareTableBody compute annuitized, tenant-aware burden
- Central FIT_OUT_LABELS with industry/international vocabulary (Rohbau·Core&Shell, Edelrohbau·CAT A, etc.)
- Activate existing generateFitOutAdvice via useFitOutAdvice hook + new FitOutAdvicePanel (MIETERAUSBAU/BKZ/MAB-Amortisation + negotiation tip), shown for tenant-borne SHELL/BASIC
- MAB field only asked when tenant builds out (optional) — one new toggle, no extra data burden for property managers
Fix: DQ/confidence modifiers were computed but never applied to finalScore (hardcoded 0 in output) — now folded into rawFinal and exposed. Trust-first: weak data quality lowers the score. Resolves 3 pre-existing red tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add per-profile notification config (toggle, score threshold, email) with Accordion UI in SavedNeedDetail
- NotificationButton: badge only lights up for unseen matches, dismisses on open (localStorage), uses per-profile minScore
- SavedNeedDetail: replace deterministicMatchScore with real match data (useMatchesByNeed), sort by score desc, deduplicate by property
- SavedProfilesTab & SavedNeedCard: top score bar now shows best real match score instead of criteria confidence
- "Treffer" count uses per-profile notificationConfig.minScore (≥80 default) consistently across card, panel, and notification badge
- Extract PropertyMatchRow to own file with Anfrage/Merken/Vergleichen/Details→/Zur Einheit→ action buttons
- Details → navigates to /demand/results/:matchId using real match ID format (m__propId__prop__needId)
- Add InquiryQuickDialog + AddToPipelineDialog to AISearch.tsx so dialogs work from SavedProfilesTab
- Org-isolation: useNeeds filters by organizationId, org-mobimo gets 4 own search profiles
- Mock data: all 18 org-wincasa need names changed to descriptive profile names; 4 new org-mobimo needs added
- useUpdateNeed mutation hook for updating need fields (notificationConfig etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All page header subtitles now use variant="body2" color="text.secondary"
(0.875rem gray). Fixed caption→body2 in DataQuality and MarketIntelligence,
and aligned DashboardHeader title/subtitle to the global standard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restructured from scrollable content-box to flex-column with a
fixed header bar (borderBottom #e8e7e4, py:2.5) and a scrollable
content area below — identical pattern to all other pages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All page-level headers now use fontSize:1.125rem, fontWeight:700,
color:#0f172a, container py:2.5, borderBottom:#e8e7e4 — matching
the standard established in PageHeader. Removes variant="h5"/h6"
inconsistencies across Supply, Demand, and shared components.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PageHeader titles, MatchCardCompact score badge, and IntelligenceMatchCard
score badge were the only 3 places using the serif override. All now use
the theme-defined Inter stack (fontWeight 600/700, letterSpacing tight).
PDF/print previews (BerichtDialog, LatentInquiryReportPreview) intentionally
keep their serif fonts — those are document renderers, not UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Match cards reduced to 168px wide, 60px image height — supplementary context
should not visually dominate the primary chat content. Reply textarea grows
to 7 rows so the composer takes more of the available space.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces fixed height: 90px with aspectRatio: '3/2' — the same ratio used in
supply property cards. Ensures consistent, undistorted image crops across all
card surfaces. objectFit: cover guaranteed via <img> element.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace flex-stretch layout (cards filling full width = distorted images)
with fixed 240px cards in a horizontal overflow-auto rail. Shows as many
cards as fit the screen naturally; image height increased to 90px for a
cleaner 240×90 (≈ 8:3) proportion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Layout: property context bar (44x44 thumbnail + title + key facts + Ansehen)
sits between inquiry header and chat. Bottom strip now shows only the
2-card matches slider — no duplicate property info. Images use objectFit:cover
via <img> for consistent non-distorted rendering. Match score shown as badge
overlay on image. Reply composer gains KI-Entwurf button (Sparkles, purple)
that fills a salutation-prefixed template; an inline label marks it as
AI-generated until the user edits.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- InquiryListRow: selected state now uses bright blue (4px #2563eb border,
#eff6ff bg, blue tenant name) — unmistakable at a glance
- RelatedPropertyCardPanel compact: left panel is a mini property card matching
supply card style (border, borderRadius 2, hover shadow, 78px image);
right panel shows exactly 2 match slider cards with ChevronLeft/Right nav
when more than 2 exist; match cards mirror property card visual language
- Strip maxHeight raised to 260px; reply composer rows 4→5
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Compact strip redesign: left side shows 72x80px property thumbnail + key facts
+ "Ansehen" link. Right side shows a horizontal scroll rail (148px cards) with
placeholder/actual image, title, score, and top match reason for each
additional property. Uses imageUrl from AdditionalPropertyMatch domain.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace 3-panel side-by-side layout with a vertical stack on the right:
InquiryChat takes full height (flex:1), RelatedPropertyCardPanel becomes a
compact 200px bottom strip with a 2-column layout (property info left,
further matches right). Active row selection background deepened to #eef2f8
for clearer focus indication.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace SaaS-generic KPI icon boxes with flat inline layout.
Replace MUI color-prop status chips with design-system colors
matching the rest of the supply workspace.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add anonymous flag to schattenmarktRelease on PropertyUnit and Property.
When a unit is enabled in the pre-market panel, an Anonym toggle appears
inline — hides address and property name in the matching feed, showing
only area, type and region to prospective tenants.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace building-level Miet- & Mieterinformationen section with
unit-level lease display. Each unit row now shows tenant name and
lease end date directly; expand reveals full contract details
(Mietbeginn, Mietende, Laufzeit, Breakout, source system, doc link).
Floor grouping groups consecutive same-floor units under one label.
Domain model extended with Lease and Tenant types; deprecated
property-level tenant fields preserved via @deprecated for compat.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scoreFactors/score were stale prop names; the component expects
scoreBreakdown and allFactors. Added null-guard for scoreBreakdown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FUTURE_SIGNAL is a demand/AI market intelligence concept — it applies
to properties not in the manager's portfolio. Own properties can only
be AVAILABLE_NOW, AVAILABLE_SOON, or OCCUPIED.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>