removed documentation
This commit is contained in:
parent
8e2c4adcb5
commit
ff6fc3f96c
56 changed files with 0 additions and 28293 deletions
|
|
@ -1,418 +0,0 @@
|
|||
# Datenmodell Architektur-Planungs-App
|
||||
|
||||
## Übersicht
|
||||
|
||||
Dieses Datenmodell bildet die Grundlage für eine Schweizer Architektur-Planungs-App zur Verwaltung von Bauprojekten, Parzellen, Dokumenten und regulatorischen Kontextinformationen.
|
||||
|
||||
## Wichtige Hinweise zum Datenmodell
|
||||
|
||||
**Objektmodell vs. Datenbank-Repräsentation:**
|
||||
|
||||
Dieses Dokument beschreibt ein **Objektmodell** für die Arbeit im Code. Es handelt sich **NICHT** um ein relationales Datenbankmodell mit Junction Tables.
|
||||
|
||||
- **Im Code-Modell**: Alle Beziehungen werden als Objektreferenzen oder Listen von Objekten dargestellt (z.B. `dokumente: list[Dokument]`, `parzellen: list[Parzelle]`).
|
||||
- **Für die Datenbank-Serialisierung**: Bei der Persistierung können Junction Tables verwendet werden, um n:m-Beziehungen in der Datenbank abzubilden. Dies ist jedoch ein Implementierungsdetail der Datenbank-Schicht und gehört nicht zum Hauptmodell.
|
||||
|
||||
**Systemattribute:**
|
||||
|
||||
Alle Datenobjekte haben automatisch die folgenden Systemattribute:
|
||||
- `_createdAt`: Float (Timestamp UTC)
|
||||
- `_createdBy`: String (User-ID)
|
||||
- `_modifiedAt`: Float (Timestamp UTC)
|
||||
- `_modifiedBy`: String (User-ID)
|
||||
|
||||
**Timestamps:**
|
||||
- Alle Timestamps sind im Float-Format UTC im Datenmodell gespeichert.
|
||||
- Die Darstellung im UI erfolgt mit der lokalen Zeitzone des Benutzers.
|
||||
|
||||
## Datenfluss-Diagramm
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Hauptflüsse - Architektur-Planungs-App
|
||||
---
|
||||
flowchart LR
|
||||
subgraph Admin[Administrative Ebene]
|
||||
Land[Land<br/>Schweiz]
|
||||
Kanton[Kanton<br/>z.B. Zürich]
|
||||
Gemeinde[Gemeinde<br/>z.B. Zürich Stadt]
|
||||
Land --> Kanton
|
||||
Kanton --> Gemeinde
|
||||
end
|
||||
|
||||
subgraph Geo[Geografische Daten]
|
||||
GeoPolylinie[GeoPolylinie<br/>Linie/Polygon]
|
||||
GeoPunkt[GeoPunkt<br/>Koordinaten]
|
||||
GeoPolylinie --> GeoPunkt
|
||||
end
|
||||
|
||||
subgraph Core[Kern-Business-Logik]
|
||||
Projekt[Projekt<br/>Bauprojekt]
|
||||
Parzelle[Parzelle<br/>Grundstück mit<br/>Bauparametern]
|
||||
Gemeinde --> Parzelle
|
||||
Projekt --> Parzelle
|
||||
Projekt --> GeoPolylinie
|
||||
Parzelle --> GeoPolylinie
|
||||
end
|
||||
|
||||
subgraph Support[Unterstützende Daten]
|
||||
Dokument[Dokument<br/>Dateien & URLs]
|
||||
Kontext[Kontext<br/>Zusatzinfos]
|
||||
end
|
||||
|
||||
style Land fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||
style Kanton fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||
style Gemeinde fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||
style Parzelle fill:#FF6B6B,stroke:#C92A2A,stroke-width:4px,color:#fff
|
||||
style Projekt fill:#FF6B6B,stroke:#C92A2A,stroke-width:4px,color:#fff
|
||||
style Dokument fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||
style GeoPolylinie fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||
style GeoPunkt fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||
style Kontext fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Alle Datenobjekte als Tabellen
|
||||
|
||||
### Übersichtstabelle
|
||||
|
||||
| Objekt | Typ | Beschreibung | Hauptfelder |
|
||||
|--------|-----|--------------|-------------|
|
||||
| **Projekt** | Kernentität | Bauprojekt mit Status und Perimeter | id, label, statusProzess, perimeter, baulinie, parzellen |
|
||||
| **Parzelle** | Hauptentität | Grundstück mit Bauparametern | id, label, plz, bauzone, AZ, BZ, perimeter, baulinie, laermschutzzone, hochwasserschutzzone, grundwasserschutzzone |
|
||||
| **Dokument** | Unterstützend | Dateien und URLs mit Versionierung | id, label, dokumentTyp, quelle, mimeType, kategorienTags |
|
||||
| **Kontext** | Unterstützend | Flexible Zusatzinformationen | id, thema, inhalt |
|
||||
| **GeoPolylinie** | Hilfsobjekt | Geometrische Linie/Polygon | id, closed, punkte |
|
||||
| **Land** | Admin | Nationale Ebene | id, label, abk |
|
||||
| **Kanton** | Admin | Kantonale Ebene mit Baurecht | id, label, abk, Baureglement |
|
||||
| **Gemeinde** | Admin | Gemeinde-Ebene mit BZO | id, label, plz, BZO |
|
||||
| **GeoPunkt** | Hilfsobjekt | 3D-Koordinate | koordinatensystem, x, y, z, referenz |
|
||||
| **GeoTag** | Enum | Geopunkt-Kategorien | - |
|
||||
| **JaNein** | Enum | Drei-wertiger Status | "", "Ja", "Nein" |
|
||||
| **StatusProzess** | Enum | Projektstatus | 7 Werte |
|
||||
| **DokumentTyp** | Enum | Dokumenttyp | 6 Werte |
|
||||
|
||||
---
|
||||
|
||||
## Zentrale Entitäten
|
||||
|
||||
### 1. Projekt
|
||||
**Das Kernobjekt, das ein Bauprojekt repräsentiert.**
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `label` | String | ✓ | Projektbezeichnung |
|
||||
| `statusProzess` | Enum[StatusProzess] | - | Projektstatus: Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv |
|
||||
| `perimeter` | GeoPolylinie | - | Umhüllende aller Parzellen des Projektes |
|
||||
| `baulinie` | GeoPolylinie | - | Baulinie des Projektes |
|
||||
| `parzellen` | list[Parzelle] | - | Alle Parzellen des Projektes |
|
||||
| `dokumente` | list[Dokument] | - | Projektspezifische Dokumente |
|
||||
| `kontextInformationen` | list[Kontext] | - | Projektspezifische Kontextinfos |
|
||||
|
||||
---
|
||||
|
||||
### 2. Parzelle
|
||||
**Repräsentiert ein Grundstück mit allen baurechtlichen Eigenschaften als ein einheitliches Objekt.**
|
||||
|
||||
#### Grunddaten
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `label` | String | ✓ | Parzellenbezeichnung |
|
||||
| `parzellenAliasTags` | list[String] | - | Weitere Parzellennamen oder Flurnamen |
|
||||
| `eigentuemerschaft` | String | - | Eigentümer der Parzelle |
|
||||
| `strasseNr` | String | - | Straße und Hausnummer |
|
||||
| `plz` | String | - | Postleitzahl der Parzelle (insbesondere bei Gemeinden mit mehreren PLZ) |
|
||||
|
||||
#### Geografischer Kontext
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `perimeter` | GeoPolylinie | - | Parzellengrenze als geschlossene GeoPolylinie |
|
||||
| `baulinie` | GeoPolylinie | - | Baulinie der Parzelle |
|
||||
| `kontextGemeinde` | Gemeinde | - | Gemeinde der Parzelle |
|
||||
|
||||
#### Bebauungsparameter
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `bauzone` | String | - | Bauzonenbezeichnung (z.B. W3, WG2, etc.) |
|
||||
| `az` | Float | - | Ausnützungsziffer |
|
||||
| `bz` | Float | - | Bebauungsziffer |
|
||||
| `vollgeschossZahl` | Integer | - | Anzahl zulässiger Vollgeschosse |
|
||||
| `anrechenbarDachgeschoss` | Float | - | Anrechenbarer Anteil Dachgeschoss (0.0 - 1.0) |
|
||||
| `anrechenbarUntergeschoss` | Float | - | Anrechenbarer Anteil Untergeschoss (0.0 - 1.0) |
|
||||
| `gebaeudehoeheMax` | Float | - | Maximale Gebäudehöhe in Metern |
|
||||
|
||||
#### Abstandsregelungen
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `regelnGrenzabstand` | list[String] | - | Regelungen zum Grenzabstand |
|
||||
| `regelnMehrlaengenzuschlag` | list[String] | - | Regelungen zum Mehrlängenzuschlag |
|
||||
| `regelnMehrhoehenzuschlag` | list[String] | - | Regelungen zum Mehrhöhenzuschlag |
|
||||
|
||||
#### Eigenschaften (Ja/Nein)
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `parzelleBebaut` | JaNein | - | Ist die Parzelle bebaut? ("", "Ja", "Nein") |
|
||||
| `parzelleErschlossen` | JaNein | - | Ist die Parzelle erschlossen? ("", "Ja", "Nein") |
|
||||
| `parzelleHanglage` | JaNein | - | Liegt die Parzelle in Hanglage? ("", "Ja", "Nein") |
|
||||
|
||||
#### Schutzzonen
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `laermschutzzone` | String | - | Lärmschutzzone (z.B. "II") |
|
||||
| `hochwasserschutzzone` | String | - | Hochwasserschutzzone (z.B. "tief") |
|
||||
| `grundwasserschutzzone` | String | - | Grundwasserschutzzone |
|
||||
|
||||
#### Beziehungen
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `parzellenNachbarschaft` | list[Parzelle] | - | Nachbarparzellen |
|
||||
| `dokumente` | list[Dokument] | - | Parzellenspezifische Dokumente |
|
||||
| `kontextInformationen` | list[Kontext] | - | Parzellenspezifische Kontextinfos |
|
||||
|
||||
---
|
||||
|
||||
### 3. Dokument
|
||||
**Unterstützendes Datenobjekt zur Verwaltung von Dateien und URLs mit Versionierung.**
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `label` | String | ✓ | Dokumentbezeichnung |
|
||||
| `versionsbezeichnung` | String | - | Versionsnummer oder -bezeichnung (z.B. "v1.0", "Rev. A") |
|
||||
| `dokumentTyp` | Enum[DokumentTyp] | - | Typ des Dokuments (siehe DokumentTyp-Enum) |
|
||||
| `dokumentReferenz` | String | ✓ | Dateipfad oder URL |
|
||||
| `quelle` | String | - | Quelle des Dokuments |
|
||||
| `mimeType` | String | - | MIME-Type des Dokuments (z.B. "application/pdf", "image/png") |
|
||||
| `kategorienTags` | list[String] | - | Kategorisierung des Dokuments |
|
||||
|
||||
**Hinweis:**
|
||||
Aktuelle Dokumente (z.B. aktuelle Baureglemente, BZO) können anhand des `dokumentTyp`-Attributs identifiziert werden. Die entsprechenden Dokumente finden sich in der `dokumente`-Liste der jeweiligen Entität (Kanton, Gemeinde).
|
||||
|
||||
#### Beispiel-Kategorien (nicht abschliessend)
|
||||
|
||||
| Kategorie | Beschreibung |
|
||||
|-----------|--------------|
|
||||
| `Kataster Objekte` | Amtliche Vermessung |
|
||||
| `Kataster Werkeleitungen` | Leitungskataster |
|
||||
| `Kataster Belastete Standorte` | Altlasten |
|
||||
| `Kataster Bäume` | Baumkataster |
|
||||
| `Zonenplan` | Zonenpläne |
|
||||
| `Planungs- und Baugesetz (PGB)` | Kantonale Baugesetze |
|
||||
| `Bau- und Zonenordnung (BZO)` | Gemeinde BZO |
|
||||
| `Parkplatzverordnung` | Parkplatzregelungen |
|
||||
| `Eigentümerauskunft` | Grundbuch-Auszüge Eigentümer |
|
||||
| `Grundbuchauszug` | Vollständige Grundbuch-Auszüge |
|
||||
| `Bauherrschaft` | Dokumente von der Bauherrschaft |
|
||||
| `Planung` | Planungsdokumente |
|
||||
|
||||
---
|
||||
|
||||
### 4. Geografische Entitäten
|
||||
|
||||
#### GeoPolylinie
|
||||
**Repräsentiert eine Linie oder ein Polygon aus mehreren GeoPunkten.**
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `closed` | Boolean | ✓ | Ist die GeoPolylinie geschlossen (Polygon)? |
|
||||
| `punkte` | list[GeoPunkt] | ✓ | Liste der GeoPunkte, die die GeoPolylinie bilden |
|
||||
|
||||
**Verwendung:**
|
||||
- Parzellenperimeter (geschlossene GeoPolylinie)
|
||||
- Baulinie (offene oder geschlossene GeoPolylinie)
|
||||
- Projektperimeter (geschlossene GeoPolylinie)
|
||||
|
||||
#### GeoPunkt
|
||||
**Repräsentiert einen 3D-Punkt mit Referenzangabe.**
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `koordinatensystem` | String | ✓ | Koordinatensystem (z.B. "LV95", "EPSG:2056") |
|
||||
| `x` | Float | ✓ | Ostwert (E) [m], typisch 2'480'000 - 2'840'000 |
|
||||
| `y` | Float | ✓ | Nordwert (N) [m], typisch 1'070'000 - 1'300'000 |
|
||||
| `z` | Float | - | Höhe über Meer [m] |
|
||||
| `referenz` | Enum[GeoTag] | - | Kategorisierung des Punktes |
|
||||
|
||||
**Verwendung:**
|
||||
- Als Teil einer GeoPolylinie (Parzellenperimeter, Baulinie)
|
||||
- Einzelne Referenzpunkte
|
||||
|
||||
#### GeoTag (Enum)
|
||||
|
||||
| Kategorie | Beschreibung |
|
||||
|-----------|--------------|
|
||||
| `K1` | Fixpunkt höchster Genauigkeit |
|
||||
| `K2` | Fixpunkt mittlerer Genauigkeit |
|
||||
| `K3` | Fixpunkt niedriger Genauigkeit |
|
||||
| `Geometer` | Vom Geometer vermessener Punkt |
|
||||
|
||||
**Beispiel (Zürich Hauptbahnhof):**
|
||||
- `koordinatensystem`: "LV95" oder "EPSG:2056"
|
||||
- `x`: 2'683'140 [m]
|
||||
- `y`: 1'247'850 [m]
|
||||
- `z`: 408 [m]
|
||||
- `referenz`: "K1" (oder ein anderer GeoTag-Wert)
|
||||
|
||||
---
|
||||
|
||||
### 5. Administrative Hierarchie
|
||||
|
||||
#### Land
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `label` | String | ✓ | Landesname (z.B. "Schweiz") |
|
||||
| `abk` | String | - | Abkürzung (z.B. "CH") |
|
||||
| `dokumente` | list[Dokument] | - | Nationale Gesetze |
|
||||
| `kontextInformationen` | list[Kontext] | - | Nationale Kontextinformationen |
|
||||
|
||||
---
|
||||
|
||||
#### Kanton
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `label` | String | ✓ | Kantonsname (z.B. "Zürich") |
|
||||
| `id_land` | [land] | eindeutiger Link zum land, also in welchem land kanton liegt |
|
||||
| `abk` | String | - | Abkürzung (z.B. "ZH") |
|
||||
| `dokumente` | list[Dokument] | - | Kantonale Dokumente |
|
||||
| `kontextInformationen` | list[Kontext] | - | Kantonsspezifische Kontextinfos |
|
||||
|
||||
---
|
||||
|
||||
#### Gemeinde
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `label` | String | ✓ | Gemeindename (z.B. "Zürich") |
|
||||
| `id_kanton` | [kanton] | eindeutiger Link zur gemeinde, also im welchem kanton gemeinde liegt |
|
||||
| `plz` | String | - | Postleitzahl (bei Gemeinden mit mehreren PLZ kann dies eine Haupt-PLZ sein) |
|
||||
| `dokumente` | list[Dokument] | - | Gemeindedokumente |
|
||||
| `kontextInformationen` | list[Kontext] | - | Gemeindespezifische Kontextinfos |
|
||||
|
||||
**Hinweis:**
|
||||
Bei Gemeinden mit mehreren Postleitzahlen (z.B. Zürich, Bern) wird die konkrete PLZ der Parzelle im Attribut `plz` der Parzelle erfasst.
|
||||
|
||||
---
|
||||
|
||||
### 6. Kontext
|
||||
**Unterstützendes Datenobjekt für flexible Zusatzinformationen und Hinweise.**
|
||||
|
||||
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||
|------|----------|---------|--------------|
|
||||
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||
| `thema` | String | ✓ | Bezeichnung des Themas |
|
||||
| `inhalt` | String | ✓ | Detaillierte Information (Text) |
|
||||
|
||||
**Verwendung:**
|
||||
Kontext-Objekte werden als Listen in den jeweiligen Entitäten gespeichert:
|
||||
- `projekt.kontextInformationen: list[Kontext]`
|
||||
- `parzelle.kontextInformationen: list[Kontext]`
|
||||
- `land.kontextInformationen: list[Kontext]`
|
||||
- `kanton.kontextInformationen: list[Kontext]`
|
||||
- `gemeinde.kontextInformationen: list[Kontext]`
|
||||
|
||||
#### Beispielthemen (nicht abschliessend)
|
||||
|
||||
| Themenbereich | Beispiele |
|
||||
|---------------|-----------|
|
||||
| **Nutzung** | Vorgaben zur Erdgeschossnutzung (Wohnen erlaubt oder Pflicht für Gewerbe) |
|
||||
| **Rechte** | Dienstbarkeiten (Wegrechte, Nähebaurechte, etc.) |
|
||||
| **Parkierung** | Anforderung Parkplätze (Berechnung / Reduktionsfaktoren) |
|
||||
| **Ausnützung** | Ausnützungsübertragungen |
|
||||
| **Umwelt** | Schadstoffbelastungen auf Parzellen |
|
||||
| **Planung** | Aktive Gestaltungspläne |
|
||||
| **Lärm** | Lärmempfindlichkeitsstufen |
|
||||
| **Energie** | Mögliche Wärmenutzung (Wärmeverbundnetze; Fernwärme, Anergie) |
|
||||
| **Natur** | Baumbestand auf privaten Grundstücken |
|
||||
| **Schutz** | Isos (Ortsbild, Schutzstatus, Denkmalschutz, Weilergebiet, etc.) |
|
||||
| **Gefahren** | Naturgefahren (z.B. Objektschutzmassnahmen (Hochwasser)) |
|
||||
| **Revision** | Verweis auf aktuell in oder zukünftig in Revision befindlichen Normen/Gesetze (z.B. Revision PBG mit aktuell negativer Vorwirkung) |
|
||||
|
||||
**Design-Rationale:**
|
||||
Das Kontext-Objekt ermöglicht flexibles Hinzufügen von projektspezifischen, parzellen-spezifischen oder regionalen Informationen ohne Schemaänderungen.
|
||||
|
||||
---
|
||||
|
||||
### 7. Hilfsentitäten & Enumerationen
|
||||
|
||||
#### JaNein (Enum)
|
||||
**Drei-wertiger Zustand für optionale Ja/Nein-Fragen.**
|
||||
|
||||
| Wert | Bedeutung |
|
||||
|------|-----------|
|
||||
| `""` (leer) | Unbekannt / Nicht erfasst |
|
||||
| `"Ja"` | Ja / Zutreffend |
|
||||
| `"Nein"` | Nein / Nicht zutreffend |
|
||||
|
||||
**Verwendung:**
|
||||
- `parzelleBebaut`: Ist die Parzelle bebaut?
|
||||
- `parzelleErschlossen`: Ist die Parzelle erschlossen?
|
||||
- `parzelleHanglage`: Liegt die Parzelle in Hanglage?
|
||||
|
||||
---
|
||||
|
||||
#### StatusProzess (Enum)
|
||||
**Projektstatus zur Nachverfolgung des Projektfortschritts.**
|
||||
|
||||
| Wert | Beschreibung |
|
||||
|------|--------------|
|
||||
| `Eingang` | Projekt wurde eingereicht |
|
||||
| `Analyse` | Projekt wird analysiert |
|
||||
| `Studie` | Machbarkeitsstudie läuft |
|
||||
| `Planung` | Planungsphase |
|
||||
| `Baurechtsverfahren` | Baubewilligung läuft |
|
||||
| `Umsetzung` | Bauprojekt in Umsetzung |
|
||||
| `Archiv` | Projekt abgeschlossen |
|
||||
|
||||
---
|
||||
|
||||
#### DokumentTyp (Enum)
|
||||
**Dokumenttyp zur Kategorisierung von Dokumenten, insbesondere für kantonale und kommunale Dokumente.**
|
||||
|
||||
| Wert | Beschreibung |
|
||||
|------|--------------|
|
||||
| `kantonBaureglementAktuell` | Aktuelles Baureglement (Kanton) |
|
||||
| `kantonBaureglementRevision` | Baureglement in Revision (Kanton) |
|
||||
| `kantonBauverordnungAktuell` | Aktuelle Bauverordnung (Kanton) |
|
||||
| `kantonBauverordnungRevision` | Bauverordnung in Revision (Kanton) |
|
||||
| `gemeindeBzoAktuell` | Aktuelle Bau- und Zonenordnung (BZO) (Gemeinde) |
|
||||
| `gemeindeBzoRevision` | BZO in Revision (Gemeinde) |
|
||||
|
||||
**Verwendung:**
|
||||
- Dokumente in der `dokumente`-Liste von Kanton oder Gemeinde können über `dokumentTyp` identifiziert werden
|
||||
- Ermöglicht die Suche nach aktuellen oder in Revision befindlichen Dokumenten
|
||||
|
||||
---
|
||||
|
||||
## Q & A
|
||||
|
||||
1. **Versionierung**: Sollen Änderungen an Parzellen historisiert werden? --> Vorerst nicht
|
||||
2. **Mehrsprachigkeit**: Labels in DE/FR/IT? --> Wir im Pydantic Model später umgesetzt
|
||||
3. **Benutzer & Rollen**: Wer darf was bearbeiten? --> In der App über Roles und Permissions gesteuert
|
||||
4. **Workflow-Engine**: Für Statusübergänge und Genehmigungen? --> In der App über Workflow-Engine gesteuert
|
||||
5. **Integration**: Anbindung an amtliche Geodaten (z.B. Swisstopo API)? --> In der App über Integrationen gesteuert
|
||||
6. **Berechnungen**: Sollen Ausnützungsberechnungen automatisiert werden? --> In der App über Berechnungen gesteuert
|
||||
|
||||
---
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. **Validierung**: Review mit PEK
|
||||
2. **Prototyp**: Implementierung der Datenmodell-Klassen
|
||||
3. **GIS-Integration**: PostGIS aufsetzen, Test-Geodaten importieren
|
||||
4. **API-Design**: RESTful API (FastAPI) mit OpenAPI-Dokumentation
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,209 +0,0 @@
|
|||
# Architecture Overview
|
||||
|
||||
High-level architecture diagram of the Gateway project.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
%% Entry Point
|
||||
App[app.py<br/>FastAPI Application]
|
||||
|
||||
%% Middleware Layer
|
||||
App --> Security[Security<br/>Auth, CSRF, JWT, Token Refresh]
|
||||
App --> CORS[CORS Middleware]
|
||||
|
||||
%% API Layer
|
||||
App --> Routes[Routes<br/>API Endpoints]
|
||||
|
||||
%% Business Logic Layer
|
||||
Routes --> Features[Features<br/>Business Logic Modules]
|
||||
Routes --> Services[Services<br/>Service Layer]
|
||||
|
||||
%% Features can use Services
|
||||
Features --> Services
|
||||
|
||||
%% Data Access Layer
|
||||
Services --> Interfaces[Interfaces<br/>Data Access Layer]
|
||||
Features --> Interfaces
|
||||
|
||||
%% External Connections
|
||||
Interfaces --> Connectors[Connectors<br/>External System Connections]
|
||||
Interfaces --> Database[(Database<br/>PostgreSQL)]
|
||||
|
||||
%% Connectors connect to external systems
|
||||
Connectors --> Database
|
||||
Connectors --> External[External Systems<br/>Jira, ClickUp, Google, etc.]
|
||||
|
||||
%% Shared Resources
|
||||
App -.-> Shared[Shared Modules<br/>Configuration, Logging, Utils]
|
||||
Routes -.-> Shared
|
||||
Features -.-> Shared
|
||||
Services -.-> Shared
|
||||
Interfaces -.-> Shared
|
||||
|
||||
%% Data Models used throughout
|
||||
Routes -.-> DataModels[Data Models<br/>Request/Response Schemas]
|
||||
Features -.-> DataModels
|
||||
Services -.-> DataModels
|
||||
Interfaces -.-> DataModels
|
||||
|
||||
%% Feature Lifecycle Management
|
||||
App --> FeaturesLifecycle[Features Lifecycle<br/>Startup/Shutdown Management]
|
||||
FeaturesLifecycle --> Features
|
||||
|
||||
%% Styling
|
||||
classDef entryPoint fill:#e1f5ff,stroke:#01579b,stroke-width:3px
|
||||
classDef apiLayer fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
|
||||
classDef businessLogic fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
|
||||
classDef dataAccess fill:#fff3e0,stroke:#e65100,stroke-width:2px
|
||||
classDef external fill:#fce4ec,stroke:#880e4f,stroke-width:2px
|
||||
classDef shared fill:#f5f5f5,stroke:#424242,stroke-width:1px,stroke-dasharray: 5 5
|
||||
|
||||
class App entryPoint
|
||||
class Routes,Security,CORS apiLayer
|
||||
class Features,Services businessLogic
|
||||
class Interfaces,Connectors dataAccess
|
||||
class Database,External external
|
||||
class Shared,DataModels,FeaturesLifecycle shared
|
||||
```
|
||||
|
||||
## Data Flow Diagram
|
||||
|
||||
The following sequence diagram shows how data flows through the same architectural layers from the architecture diagram above.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant App as app.py<br/>FastAPI Application
|
||||
participant Security as Security<br/>Auth, CSRF, JWT
|
||||
participant Routes as Routes<br/>API Endpoints
|
||||
participant Features as Features<br/>Business Logic
|
||||
participant Services as Services<br/>Service Layer
|
||||
participant Interfaces as Interfaces<br/>Data Access Layer
|
||||
participant Connectors as Connectors<br/>External Connections
|
||||
participant Database as Database<br/>PostgreSQL
|
||||
participant External as External Systems<br/>Jira, ClickUp, etc.
|
||||
|
||||
%% Request Flow
|
||||
Client->>App: HTTP Request
|
||||
App->>Security: Validate Auth & CSRF
|
||||
Security-->>App: Authenticated
|
||||
App->>Routes: Validated Request
|
||||
Routes->>Routes: Validate Data Models
|
||||
|
||||
alt Route delegates to Features
|
||||
Routes->>Features: Delegate Request
|
||||
Features->>Services: Use Service (optional)
|
||||
Services-->>Features: Service Result
|
||||
Features->>Interfaces: Request Data Access
|
||||
else Route delegates to Services
|
||||
Routes->>Services: Delegate Request
|
||||
Services->>Interfaces: Request Data Access
|
||||
end
|
||||
|
||||
Interfaces->>Connectors: Query Request
|
||||
|
||||
alt Database Query
|
||||
Connectors->>Database: Execute Query
|
||||
Database-->>Connectors: Raw Data
|
||||
else External API Call
|
||||
Connectors->>External: API Call
|
||||
External-->>Connectors: API Response
|
||||
end
|
||||
|
||||
%% Response Flow
|
||||
Connectors-->>Interfaces: Raw Data
|
||||
Interfaces->>Interfaces: Transform to Domain Objects
|
||||
Interfaces-->>Features: Domain Objects
|
||||
Interfaces-->>Services: Domain Objects
|
||||
|
||||
Features->>Features: Process Business Logic
|
||||
Services->>Services: Process Service Logic
|
||||
|
||||
Features-->>Routes: Processed Data
|
||||
Services-->>Routes: Processed Data
|
||||
|
||||
Routes->>Routes: Serialize to Response Models
|
||||
Routes-->>App: HTTP Response
|
||||
App-->>Client: HTTP Response
|
||||
```
|
||||
|
||||
### Request Flow (Top to Bottom)
|
||||
|
||||
1. **Client** sends HTTP request to **app.py**
|
||||
2. **app.py** forwards to **Security** middleware for authentication and CSRF validation
|
||||
3. **Security** validates and returns authenticated request to **app.py**
|
||||
4. **app.py** forwards validated request to **Routes**
|
||||
5. **Routes** validate request data using Data Models, then delegate to either:
|
||||
- **Features** (which may use **Services**), or
|
||||
- **Services** directly
|
||||
6. **Features/Services** call **Interfaces** for data access
|
||||
7. **Interfaces** use **Connectors** to execute queries
|
||||
8. **Connectors** query **Database** or call **External Systems**
|
||||
|
||||
### Response Flow (Bottom to Top)
|
||||
|
||||
1. **Database/External Systems** return raw data to **Connectors**
|
||||
2. **Connectors** pass raw data to **Interfaces**
|
||||
3. **Interfaces** transform raw data into domain objects
|
||||
4. **Interfaces** return domain objects to **Features/Services**
|
||||
5. **Features/Services** process business logic and return processed data to **Routes**
|
||||
6. **Routes** serialize data to response models and return HTTP response to **app.py**
|
||||
7. **app.py** returns HTTP response to **Client**
|
||||
|
||||
### Data Transformations
|
||||
|
||||
- **Routes**: HTTP Request ↔ Validated Data Models (Pydantic)
|
||||
- **Features/Services**: Data Models ↔ Domain Objects (Business Logic Processing)
|
||||
- **Interfaces**: Domain Objects ↔ Raw Data (SQL/API Format)
|
||||
- **Connectors**: Raw Data ↔ Database Queries/API Calls
|
||||
|
||||
## Layer Descriptions
|
||||
|
||||
### Entry Point Layer
|
||||
**app.py** - The FastAPI application entry point that orchestrates the entire system. It initializes logging, configures CORS and security middleware, registers all route routers, and manages the application lifecycle (startup/shutdown). This is where the application server starts and all components are wired together.
|
||||
|
||||
### API Layer
|
||||
**Routes** - HTTP endpoints that define the REST API surface. Routes receive client requests, validate input using data models, delegate to features or services for business logic, and return structured responses. Each route module handles a specific domain (e.g., Real Estate, Chat, Workflows, Security).
|
||||
|
||||
**Security** - Middleware and services that handle authentication, authorization, CSRF protection, JWT token management, and token refresh. Ensures all requests are properly authenticated and authorized before reaching business logic. See [Security Component Documentation](./security-component.md) for detailed documentation.
|
||||
|
||||
**CORS** - Cross-Origin Resource Sharing middleware that controls which external domains can access the API, enabling secure cross-origin requests from web applications.
|
||||
|
||||
### Business Logic Layer
|
||||
**Features** - Domain-specific business logic modules that implement core functionality for specific use cases (e.g., Real Estate management, Chat workflows, Data neutralization). Features are stateless and orchestrate services to fulfill business requirements. They can be called directly from routes or managed by the Features Lifecycle for background processing.
|
||||
|
||||
**Services** - Reusable, composable service components that provide cross-cutting functionality (AI processing, document extraction, content generation, chat operations, ticket management, etc.). Services encapsulate complex operations and can be used by multiple features. They typically use interfaces to access data and may call other services.
|
||||
|
||||
### Data Access Layer
|
||||
**Interfaces** - Abstraction layer that provides a clean, domain-oriented API for accessing data. Interfaces hide the complexity of database connections and external system integrations, offering high-level methods for CRUD operations. They handle user context, access control, and data transformation between the application and persistence layers.
|
||||
|
||||
**Connectors** - Concrete implementations that handle low-level communication with external systems. Database connectors manage PostgreSQL connections, query execution, and transaction handling. External connectors integrate with third-party services (Jira, ClickUp, Google Voice, SharePoint) using their specific APIs and protocols.
|
||||
|
||||
### External Systems Layer
|
||||
**Database** - PostgreSQL databases that persist application data. Multiple databases may exist for different domains (e.g., chat data, real estate data, management data). Connectors handle all database interactions.
|
||||
|
||||
**External Systems** - Third-party services and APIs that the application integrates with. These include ticketing systems (Jira, ClickUp), cloud services (Google Voice, SharePoint), and other external platforms. Connectors abstract away the specifics of each integration.
|
||||
|
||||
### Shared Resources Layer
|
||||
**Shared Modules** - Common utilities and infrastructure used throughout the application. Includes configuration management, logging utilities, time/date helpers, JSON processing, attribute utilities, and audit logging. These modules provide cross-cutting concerns that don't belong to any specific domain.
|
||||
|
||||
**Data Models** - Pydantic models that define data structures for requests, responses, and database entities. They provide validation, serialization, and type safety across all layers. Models are organized by domain (e.g., Real Estate, Chat, Security, AI).
|
||||
|
||||
**Features Lifecycle** - Manages the startup and shutdown of features that require background processing, scheduled tasks, or event-driven operations. Coordinates initialization and cleanup of features that need persistent processes or event listeners.
|
||||
|
||||
## Request/Response Flow Summary
|
||||
|
||||
For a detailed visual representation, see the [Data Flow Diagram](#data-flow-diagram) above.
|
||||
|
||||
**Simplified Request Flow**: `Client Request` → `CORS` → `Security (Auth/CSRF)` → `Routes` → `Features/Services` → `Interfaces` → `Connectors` → `Database/External Systems`
|
||||
|
||||
**Simplified Response Flow**: `Database/External Systems` → `Connectors` → `Interfaces` → `Features/Services` → `Routes` → `Transform & Log` → `Client Response`
|
||||
|
||||
## Key Architectural Patterns
|
||||
|
||||
- **Layered Architecture**: Clear separation between API, business logic, and data access layers
|
||||
- **Dependency Injection**: Services and interfaces are injected where needed
|
||||
- **Interface Abstraction**: Interfaces abstract away database and connector details
|
||||
- **Stateless Design**: Features operate statelessly without session management
|
||||
- **Shared Utilities**: Common functionality centralized in shared modules
|
||||
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,365 +0,0 @@
|
|||
# Date and Time Handling
|
||||
|
||||
This document describes how dates and times are handled throughout the gateway project, including storage formats, transformations, and retrieval mechanisms.
|
||||
|
||||
## Overview
|
||||
|
||||
The gateway uses **Unix timestamps** (floats representing seconds since epoch) as the standard format for all date and time values. This ensures consistency across different database backends and eliminates timezone-related issues by always working in UTC.
|
||||
|
||||
## Core Components
|
||||
|
||||
### Time Utilities Module
|
||||
|
||||
The primary module for date/time handling is `modules/shared/timeUtils.py`, which provides:
|
||||
|
||||
#### `getUtcTimestamp() -> float`
|
||||
|
||||
Returns the current UTC timestamp as a float (seconds since Unix epoch with millisecond precision).
|
||||
|
||||
**Implementation:**
|
||||
- Uses Python's `time.time()` which returns seconds since epoch as a float
|
||||
- Provides millisecond precision
|
||||
- Always returns UTC time
|
||||
|
||||
#### `getUtcNow() -> datetime`
|
||||
|
||||
Returns the current UTC time as a `datetime` object with timezone information.
|
||||
|
||||
#### `parseTimestamp(value: Any, default: Optional[float] = None) -> Optional[float]`
|
||||
|
||||
**Critical function for database retrieval** - Parses timestamp values from various formats and converts them to float.
|
||||
|
||||
This function handles the transformation/decoding of timestamps when reading from the database, especially important for PostgreSQL connectors that may return numeric fields as strings in some environments (e.g., Azure PostgreSQL).
|
||||
|
||||
**Supported input formats:**
|
||||
- `float`: Returns as-is
|
||||
- `int`: Converts to float
|
||||
- `str`: Attempts to parse as numeric string (e.g., "1234567890.123")
|
||||
- `None`: Returns default value (or None if no default provided)
|
||||
|
||||
**Usage:** Call `parseTimestamp()` with the database value and an optional default. For example: `parseTimestamp(db_value, default=getUtcTimestamp())`.
|
||||
|
||||
**Why this is needed:**
|
||||
- PostgreSQL connectors (especially Azure PostgreSQL) may return numeric fields as strings
|
||||
- Ensures consistent float type regardless of database return format
|
||||
- Provides safe fallback with default values
|
||||
|
||||
#### `createExpirationTimestamp(expiresInSeconds: int) -> float`
|
||||
|
||||
Creates an expiration timestamp by adding seconds to the current UTC timestamp. Call with the number of seconds until expiration, for example: `createExpirationTimestamp(3600)` for 1 hour from now.
|
||||
|
||||
## Database Storage
|
||||
|
||||
### Storage Format
|
||||
|
||||
All timestamps are stored in the database as **floats** (Unix timestamps in seconds):
|
||||
|
||||
- **PostgreSQL**: Stored as `NUMERIC` or `DOUBLE PRECISION` type
|
||||
- **JSON Database**: Stored as JSON number (float) in record files
|
||||
|
||||
### Saving Timestamps
|
||||
|
||||
#### PostgreSQL Connector (`connectorDbPostgre.py`)
|
||||
|
||||
When saving records, timestamp fields (`_createdAt`, `_modifiedAt`) are handled specially. The connector checks if the value is a string and attempts to convert it to float before storing.
|
||||
|
||||
**Process:**
|
||||
1. Timestamp values are converted to float if they're strings
|
||||
2. Stored directly as numeric values in PostgreSQL
|
||||
3. No encryption or encoding - stored as plain numeric values
|
||||
|
||||
#### JSON Connector (`connectorDbJson.py`)
|
||||
|
||||
Timestamps are stored directly as JSON numbers. The connector calls `getUtcTimestamp()` to get the current time and sets `_createdAt` on creation and `_modifiedAt` on every save operation.
|
||||
|
||||
**Process:**
|
||||
1. Timestamps are generated as floats using `getUtcTimestamp()`
|
||||
2. Stored as JSON numbers in record files
|
||||
3. No transformation needed - JSON natively supports float numbers
|
||||
|
||||
### Retrieving Timestamps
|
||||
|
||||
#### PostgreSQL Connector
|
||||
|
||||
When reading from PostgreSQL, timestamps may be returned in different formats:
|
||||
|
||||
- **Expected**: Float values
|
||||
- **Actual (Azure PostgreSQL)**: Sometimes returned as strings
|
||||
- **Solution**: Use `parseTimestamp()` to normalize
|
||||
|
||||
**Example from code:** In `interfaceDbChatObjects.py`, timestamps are parsed using `parseTimestamp(msg.get("publishedAt"), default=getUtcTimestamp())`.
|
||||
|
||||
#### JSON Connector
|
||||
|
||||
Timestamps are read directly from JSON and typically remain as floats, but `parseTimestamp()` is still used for safety to ensure consistent float type regardless of JSON parsing quirks. Call `parseTimestamp(record.get("timestamp"), default=getUtcTimestamp())` when reading timestamp fields.
|
||||
|
||||
## Data Model Definitions
|
||||
|
||||
Timestamps are defined in Pydantic models using the `float` type with `getUtcTimestamp` as the default factory. For example, in `ChatLog` model, the `timestamp` field uses `Field(default_factory=getUtcTimestamp, ...)`.
|
||||
|
||||
**Common timestamp fields:**
|
||||
- `_createdAt`: When the record was created
|
||||
- `_modifiedAt`: When the record was last modified
|
||||
- `publishedAt`: When a message was published
|
||||
- `timestamp`: Generic timestamp field
|
||||
- `expiresAt`: When something expires
|
||||
- `connectedAt`: When a connection was established
|
||||
- `lastChecked`: When something was last verified
|
||||
- `creationDate`: When something was created
|
||||
|
||||
## Transformation Flow
|
||||
|
||||
### Saving to Database
|
||||
|
||||
```
|
||||
Application Code
|
||||
↓
|
||||
getUtcTimestamp() → float (e.g., 1234567890.123)
|
||||
↓
|
||||
Database Connector
|
||||
↓
|
||||
PostgreSQL: Store as NUMERIC/DOUBLE PRECISION
|
||||
JSON: Store as JSON number
|
||||
```
|
||||
|
||||
### Reading from Database
|
||||
|
||||
```
|
||||
Database
|
||||
↓
|
||||
PostgreSQL: May return as float, int, or string
|
||||
JSON: Returns as float (or int if no decimals)
|
||||
↓
|
||||
parseTimestamp() → Normalizes to float
|
||||
↓
|
||||
Application Code (always receives float)
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
1. **No Encryption**: Timestamps are **not encrypted**. They are stored as plain numeric values (floats) in the database. The "encrypted format" you may have seen refers to the numeric Unix timestamp format, which is not human-readable but is not encrypted.
|
||||
|
||||
2. **Type Transformation**: The main transformation happens when **reading** from the database:
|
||||
- Database may return timestamps as strings (especially Azure PostgreSQL)
|
||||
- `parseTimestamp()` converts strings/int/float → float
|
||||
- This ensures consistent float type in application code
|
||||
|
||||
3. **UTC Only**: All timestamps are in UTC. No timezone conversions happen at the database level.
|
||||
|
||||
4. **Millisecond Precision**: Timestamps use float type, providing millisecond precision (e.g., `1234567890.123`).
|
||||
|
||||
5. **Default Values**: When timestamps are missing or invalid, `parseTimestamp()` can provide safe defaults using the `default` parameter.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating a Record with Timestamps
|
||||
|
||||
When creating records, call `getUtcTimestamp()` to set `_createdAt` and `_modifiedAt` fields. These are typically auto-set by the database connectors.
|
||||
|
||||
### Reading and Using Timestamps
|
||||
|
||||
After retrieving a record from the database, use `parseTimestamp(record.get("publishedAt"), default=getUtcTimestamp())` to safely parse the timestamp value before using it for filtering or comparison.
|
||||
|
||||
### Filtering by Timestamp
|
||||
|
||||
When filtering messages or records by timestamp, iterate through the results and use `parseTimestamp(msg.get("publishedAt"), default=getUtcTimestamp())` to parse each timestamp, then compare against your threshold value.
|
||||
|
||||
### Sorting by Timestamp
|
||||
|
||||
When sorting logs or records by timestamp, use `parseTimestamp()` in the sort key function, for example: `logs.sort(key=lambda x: parseTimestamp(x.get("timestamp"), default=0))`.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Automatic Timestamp Management
|
||||
|
||||
Database connectors automatically manage `_createdAt` and `_modifiedAt`:
|
||||
|
||||
- `_createdAt`: Set only on record creation (if not already present)
|
||||
- `_modifiedAt`: Updated on every save operation
|
||||
|
||||
### Timestamp Filtering
|
||||
|
||||
When filtering records by timestamp, always use `parseTimestamp()` to handle all database return types. Call `parseTimestamp(record.get("timestamp"), default=0)` before comparison. Do not directly compare timestamp values as they may be strings from the database.
|
||||
|
||||
### Expiration Timestamps
|
||||
|
||||
For expiration logic, use `createExpirationTimestamp(3600)` to create an expiration timestamp, then compare it with the current time using `getUtcTimestamp()`. Parse any stored expiration timestamps using `parseTimestamp()` before comparison.
|
||||
|
||||
## Database-Specific Notes
|
||||
|
||||
### PostgreSQL (Azure)
|
||||
|
||||
- May return numeric fields as strings
|
||||
- Always use `parseTimestamp()` when reading timestamp fields
|
||||
- Stored as `NUMERIC` or `DOUBLE PRECISION` type
|
||||
|
||||
### JSON Database
|
||||
|
||||
- Timestamps stored as JSON numbers
|
||||
- Usually returned as floats, but `parseTimestamp()` provides safety
|
||||
- No special handling needed beyond normalization
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Timestamp is a string instead of float
|
||||
|
||||
**Solution**: Use `parseTimestamp()` to convert: `parseTimestamp(string_timestamp, default=getUtcTimestamp())`.
|
||||
|
||||
### Issue: Timestamp is None
|
||||
|
||||
**Solution**: Provide a default value: `parseTimestamp(record.get("timestamp"), default=getUtcTimestamp())`.
|
||||
|
||||
### Issue: Timestamp comparison fails
|
||||
|
||||
**Cause**: Comparing string to float
|
||||
**Solution**: Always parse first using `parseTimestamp(value, default=0)` before any comparison operations.
|
||||
|
||||
## Summary
|
||||
|
||||
The date/time handling system:
|
||||
|
||||
1. **Stores** timestamps as Unix timestamps (floats) in UTC
|
||||
2. **Transforms** database return values (string/int/float) → float using `parseTimestamp()`
|
||||
3. **Provides** utilities for timestamp generation, parsing, and expiration
|
||||
4. **Ensures** consistency across different database backends
|
||||
5. **Handles** edge cases (None values, string returns, invalid formats)
|
||||
|
||||
The transformation you mentioned is the **type normalization** that happens when reading from the database, not encryption. Timestamps are stored as plain numeric values and converted to consistent float types for application use.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Requirements and Implementation
|
||||
|
||||
### API Response Format
|
||||
|
||||
The backend API returns timestamps as **Unix timestamps (floats)** in all JSON responses. Timestamps are always in UTC and represented as numbers (not strings).
|
||||
|
||||
**Example API Response:** The API returns timestamps as numeric values in JSON, for example: `_createdAt: 1704067200.123`, `_modifiedAt: 1704067300.456`, `publishedAt: 1704067250.789`, `expiresAt: 1704153600.0`.
|
||||
|
||||
**Important Notes:**
|
||||
- Timestamps are **always numbers** (float) in JSON responses
|
||||
- All timestamps are in **UTC** (no timezone information in the value)
|
||||
- Timestamps have **millisecond precision** (decimal places)
|
||||
- Timestamp fields may be `null` or `undefined` if not set
|
||||
|
||||
### Frontend Requirements
|
||||
|
||||
#### 1. Timestamp Decoding Module
|
||||
|
||||
The frontend **must** implement a core timestamp utility module that handles:
|
||||
|
||||
- **Decoding/parsing** timestamp values from API responses
|
||||
- **Type normalization** (handles number, string, null, undefined)
|
||||
- **Conversion** to JavaScript Date objects
|
||||
- **Formatting** for display (relative time, absolute date/time)
|
||||
- **Timezone handling** (convert UTC to user's local timezone)
|
||||
|
||||
#### 2. Required Functionality
|
||||
|
||||
The frontend timestamp module must provide:
|
||||
|
||||
1. **Parse/Decode Function**: Convert API timestamp (number/string/null) → JavaScript Date
|
||||
2. **Format Functions**: Convert Date → human-readable strings
|
||||
3. **Relative Time**: "2 hours ago", "in 5 minutes", etc.
|
||||
4. **Absolute Time**: "2024-01-01 12:30:45 UTC" or localized format
|
||||
5. **Comparison Utilities**: Compare timestamps, check expiration, etc.
|
||||
6. **Safe Defaults**: Handle null/undefined/invalid values gracefully
|
||||
|
||||
#### 3. Display Requirements
|
||||
|
||||
Based on frontend documentation requirements, timestamps should be displayed as:
|
||||
|
||||
- **List Views**: Relative time ("2 hours ago") for recent items, absolute date for older items
|
||||
- **Detail Views**: Absolute date/time with timezone information
|
||||
- **Filters**: Date/time pickers that convert to/from Unix timestamps
|
||||
- **Sorting**: Sort by timestamp value (numeric comparison)
|
||||
|
||||
### Core Frontend Timestamp Module
|
||||
|
||||
The frontend should implement a core timestamp utility module in `src/utils/timestampUtils.ts` (or `.js`) that provides the following functions:
|
||||
|
||||
#### Required Functions
|
||||
|
||||
- **`parseTimestamp(value, defaultValue)`**: Parse a timestamp value from the API and convert to JavaScript Date. Handles number, string, null, and undefined inputs. Validates timestamp range (1970-2100) and converts seconds to milliseconds for JavaScript Date constructor.
|
||||
|
||||
- **`getUtcTimestamp()`**: Get current UTC timestamp as Unix timestamp (float). Returns `Date.now() / 1000`.
|
||||
|
||||
- **`formatRelativeTime(timestamp, options)`**: Format timestamp as relative time string. Returns strings like "just now", "2 minutes ago", "3 hours ago", "2 days ago", or "in 5 minutes" for future timestamps. Options include `includeSeconds`, `futurePrefix`, and `pastSuffix`.
|
||||
|
||||
- **`formatAbsoluteTime(timestamp, options)`**: Format timestamp as absolute date/time string. Options include `includeTime`, `includeSeconds`, `includeTimezone`, and `format` ('iso', 'local', or 'utc').
|
||||
|
||||
- **`formatTimestamp(timestamp, options)`**: Smart formatting that uses relative time for recent items (default < 24 hours) and absolute time for older items. Options include `relativeThreshold`, `showRelative`, and `showAbsolute`.
|
||||
|
||||
- **`isExpired(expiresAt)`**: Check if a timestamp has expired (is in the past). Returns true if expired, false otherwise.
|
||||
|
||||
- **`isFuture(timestamp)`**: Check if a timestamp is in the future. Returns true if in future, false otherwise.
|
||||
|
||||
- **`compareTimestamps(timestamp1, timestamp2)`**: Compare two timestamps. Returns -1 if timestamp1 < timestamp2, 0 if equal, 1 if timestamp1 > timestamp2.
|
||||
|
||||
- **`dateToTimestamp(date)`**: Convert JavaScript Date to Unix timestamp (float). Defaults to current date if not provided.
|
||||
|
||||
- **`toISOString(timestamp)`**: Convert Unix timestamp to ISO 8601 string.
|
||||
|
||||
#### Usage Examples
|
||||
|
||||
**Basic Usage:** Import functions from the timestamp utils module. Use `parseTimestamp(apiData._createdAt)` to parse timestamps from API responses. Use `formatTimestamp(apiData._createdAt)` for smart display formatting (returns relative time for recent items, absolute time for older). Use `formatRelativeTime(apiData._createdAt)` for relative time strings like "2 hours ago". Use `formatAbsoluteTime(apiData._createdAt, { format: 'utc' })` for absolute time strings.
|
||||
|
||||
**In React Components:** Import `parseTimestamp`, `formatTimestamp`, and `isExpired` from the timestamp utils module. Call `parseTimestamp(workflow._createdAt)` to parse timestamps. Use `formatTimestamp(workflow._createdAt)` to display formatted time. Use `isExpired(expiresAt)` to check if a token has expired.
|
||||
|
||||
**Sorting:** Import `compareTimestamps` and use it in sort functions: `workflows.sort((a, b) => compareTimestamps(a._createdAt, b._createdAt))`.
|
||||
|
||||
**Filtering:** Import `isExpired` and use it to filter arrays: `tokens.filter(token => !isExpired(token.expiresAt))`.
|
||||
|
||||
### Frontend Integration Guidelines
|
||||
|
||||
#### 1. Always Use the Timestamp Module
|
||||
|
||||
**✅ DO:** Import and use `parseTimestamp()` from the timestamp utils module. Call `parseTimestamp(apiData._createdAt)` to safely parse timestamps.
|
||||
|
||||
**❌ DON'T:** Do not assume timestamps are always numbers and directly create Date objects like `new Date(apiData._createdAt * 1000)` as this may fail if the value is a string or null.
|
||||
|
||||
#### 2. Handle Null/Undefined Values
|
||||
|
||||
**✅ DO:** Always provide a default value when calling `parseTimestamp()`, for example: `parseTimestamp(apiData.expiresAt, new Date())`. Check if the returned date is valid before using it.
|
||||
|
||||
**❌ DON'T:** Do not assume timestamps always exist. Do not directly create Date objects without checking for null values first.
|
||||
|
||||
#### 3. Use Appropriate Formatting
|
||||
|
||||
- **List Views**: Use `formatTimestamp()` for smart relative/absolute formatting
|
||||
- **Detail Views**: Use `formatAbsoluteTime()` with timezone information
|
||||
- **Filters**: Convert Date picker values to Unix timestamps using `dateToTimestamp()`
|
||||
|
||||
#### 4. Timezone Considerations
|
||||
|
||||
- All backend timestamps are in **UTC**
|
||||
- Frontend should convert to **user's local timezone** for display
|
||||
- Use `formatAbsoluteTime()` with `format: 'local'` for user-friendly display
|
||||
- Always show timezone information in detail views
|
||||
|
||||
### Testing the Timestamp Module
|
||||
|
||||
**Test Cases to Cover:**
|
||||
1. Parse number timestamps (float and int)
|
||||
2. Parse string timestamps
|
||||
3. Handle null/undefined values
|
||||
4. Handle invalid values (NaN, out of range)
|
||||
5. Format relative time (past and future)
|
||||
6. Format absolute time (UTC and local)
|
||||
7. Compare timestamps
|
||||
8. Check expiration
|
||||
9. Convert Date to timestamp
|
||||
|
||||
**Example Test:** Import functions from the timestamp utils module and test that `parseTimestamp()` correctly handles number timestamps, string timestamps, and null values. Test that `formatRelativeTime()` correctly formats timestamps as relative time strings. Test that `isExpired()` correctly identifies expired timestamps.
|
||||
|
||||
### Summary: Frontend Timestamp Handling
|
||||
|
||||
1. **Always decode** API timestamps using `parseTimestamp()` - handles number/string/null
|
||||
2. **Format appropriately** - relative for recent, absolute for older items
|
||||
3. **Handle timezones** - convert UTC to user's local timezone for display
|
||||
4. **Provide defaults** - handle null/undefined gracefully
|
||||
5. **Use consistently** - use the core module throughout the project
|
||||
6. **Test thoroughly** - cover all input formats and edge cases
|
||||
|
||||
The core timestamp module ensures consistent, safe handling of timestamps across the entire frontend application, matching the backend's `parseTimestamp()` functionality for type normalization and error handling.
|
||||
|
||||
|
|
@ -1,981 +0,0 @@
|
|||
# Features Component Documentation
|
||||
|
||||
Comprehensive documentation of the Features layer in the Gateway application, explaining the architecture, patterns, and implementation details of all feature modules and their relationship to connectors, services, and workflows.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [What is a Feature?](#what-is-a-feature)
|
||||
3. [Features vs Services vs Workflows](#features-vs-services-vs-workflows)
|
||||
4. [Feature Architecture](#feature-architecture)
|
||||
5. [Feature Lifecycle Management](#feature-lifecycle-management)
|
||||
6. [Connectors in the Architecture](#connectors-in-the-architecture)
|
||||
7. [Individual Features](#individual-features)
|
||||
8. [Feature Patterns and Best Practices](#feature-patterns-and-best-practices)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The **Features Layer** is a domain-specific business logic layer that implements core functionality for specific use cases. Features serve as **temporary solutions** that bridge the gap between initial requirements and full service implementation or workflow integration. They provide rapid prototyping capabilities while maintaining clean architectural boundaries.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Application Layers"
|
||||
Routes[Routes Layer<br/>API Endpoints]
|
||||
Features[Features Layer<br/>Domain-Specific Logic]
|
||||
Services[Services Layer<br/>Reusable Components]
|
||||
Workflows[Workflows Layer<br/>Orchestration Engine]
|
||||
Interfaces[Interfaces Layer<br/>Data Access]
|
||||
Connectors[Connectors Layer<br/>External Systems]
|
||||
end
|
||||
|
||||
Routes --> Features
|
||||
Routes --> Services
|
||||
Routes --> Workflows
|
||||
Features --> Services
|
||||
Features --> Interfaces
|
||||
Workflows --> Services
|
||||
Services --> Interfaces
|
||||
Interfaces --> Connectors
|
||||
|
||||
style Features fill:#e8f5e9,stroke:#1b5e20,stroke-width:3px
|
||||
style Connectors fill:#fff3e0,stroke:#e65100,stroke-width:2px
|
||||
```
|
||||
|
||||
### Key Characteristics
|
||||
|
||||
- **Domain-Specific**: Each feature addresses a specific business domain or use case
|
||||
- **Temporary by Design**: Features are intended to be migrated to services or workflows over time
|
||||
- **Stateless**: Features operate without maintaining session state
|
||||
- **Service-Dependent**: Features leverage services for cross-cutting functionality
|
||||
- **Interface-Dependent**: Features use interfaces to access data through connectors
|
||||
- **Lifecycle-Managed**: Background features are managed through the Features Lifecycle system
|
||||
|
||||
---
|
||||
|
||||
## What is a Feature?
|
||||
|
||||
A **Feature** is a domain-specific business logic module that implements functionality for a particular use case. Features are designed to:
|
||||
|
||||
1. **Rapid Prototyping**: Enable quick implementation of new functionality without full service architecture
|
||||
2. **Domain Encapsulation**: Group related business logic for a specific domain (e.g., Real Estate, Chat, Data Synchronization)
|
||||
3. **Temporary Solutions**: Serve as interim implementations before migration to services or workflows
|
||||
4. **Orchestration**: Coordinate between services, interfaces, and external systems to fulfill business requirements
|
||||
5. **Background Processing**: Support scheduled tasks, event-driven operations, and background managers
|
||||
|
||||
### Feature Lifecycle Philosophy
|
||||
|
||||
Features follow a natural evolution path:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Initial Requirement] --> B[Feature Implementation]
|
||||
B --> C{Stability & Usage}
|
||||
C -->|Mature| D[Service Migration]
|
||||
C -->|Complex Workflow| E[Workflow Integration]
|
||||
C -->|Still Experimental| B
|
||||
|
||||
D --> F[Production Service]
|
||||
E --> G[Workflow Component]
|
||||
|
||||
style B fill:#fff3e0,stroke:#e65100
|
||||
style D fill:#e8f5e9,stroke:#1b5e20
|
||||
style E fill:#e1f5ff,stroke:#01579b
|
||||
```
|
||||
|
||||
**When to Use Features:**
|
||||
- New functionality that needs rapid development
|
||||
- Domain-specific logic that may not be reusable
|
||||
- Experimental or proof-of-concept implementations
|
||||
- Background tasks requiring scheduled execution
|
||||
- Integrations that are still being refined
|
||||
|
||||
**When to Migrate to Services:**
|
||||
- Functionality becomes reusable across multiple domains
|
||||
- The feature is stable and well-tested
|
||||
- Multiple features or routes need the same functionality
|
||||
- The logic should be part of the core service layer
|
||||
|
||||
**When to Migrate to Workflows:**
|
||||
- The feature involves complex multi-step user interactions
|
||||
- Task planning and adaptive learning are required
|
||||
- The feature needs workflow orchestration capabilities
|
||||
- User interactions require state management and progress tracking
|
||||
|
||||
---
|
||||
|
||||
## Features vs Services vs Workflows
|
||||
|
||||
Understanding the distinction between Features, Services, and Workflows is crucial for architectural decisions.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Comparison Matrix"
|
||||
A[Route Request] --> B{What Type?}
|
||||
B -->|Domain-Specific<br/>Single Use Case| C[Feature]
|
||||
B -->|Reusable<br/>Cross-Cutting| D[Service]
|
||||
B -->|Complex Multi-Step<br/>User Interaction| E[Workflow]
|
||||
|
||||
C --> F[Uses Services]
|
||||
C --> G[Uses Interfaces]
|
||||
D --> H[Uses Other Services]
|
||||
D --> G
|
||||
E --> D
|
||||
E --> I[Uses Methods]
|
||||
|
||||
G --> J[Uses Connectors]
|
||||
end
|
||||
|
||||
style C fill:#fff3e0,stroke:#e65100
|
||||
style D fill:#e8f5e9,stroke:#1b5e20
|
||||
style E fill:#e1f5ff,stroke:#01579b
|
||||
style J fill:#fce4ec,stroke:#880e4f
|
||||
```
|
||||
|
||||
| Aspect | Feature | Service | Workflow |
|
||||
|--------|---------|---------|----------|
|
||||
| **Purpose** | Domain-specific business logic | Cross-cutting, reusable functionality | Complex multi-step orchestration |
|
||||
| **Scope** | Single use case or domain | Multiple use cases | User interaction flows |
|
||||
| **Reusability** | Low (domain-specific) | High (cross-domain) | Medium (workflow patterns) |
|
||||
| **State Management** | Stateless | Stateless | Stateful (workflow state) |
|
||||
| **Dependencies** | Uses services and interfaces | Uses other services and interfaces | Uses services and methods |
|
||||
| **Lifecycle** | Temporary, may migrate | Permanent core component | Permanent orchestration engine |
|
||||
| **Examples** | Real Estate queries, Chat Althaus scheduler | AI processing, Document extraction | Chat workflows, Task planning |
|
||||
|
||||
### Decision Flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[New Functionality Required] --> Q1{Is it reusable<br/>across domains?}
|
||||
Q1 -->|Yes| Service[Implement as Service]
|
||||
Q1 -->|No| Q2{Does it require<br/>complex multi-step<br/>user interaction?}
|
||||
Q2 -->|Yes| Workflow[Implement as Workflow]
|
||||
Q2 -->|No| Q3{Is it domain-specific<br/>or experimental?}
|
||||
Q3 -->|Yes| Feature[Implement as Feature]
|
||||
Q3 -->|No| Service
|
||||
|
||||
Feature --> Q4{Feature Matures}
|
||||
Q4 -->|Stable & Reusable| Service
|
||||
Q4 -->|Complex Interactions| Workflow
|
||||
Q4 -->|Still Experimental| Feature
|
||||
|
||||
style Feature fill:#fff3e0,stroke:#e65100
|
||||
style Service fill:#e8f5e9,stroke:#1b5e20
|
||||
style Workflow fill:#e1f5ff,stroke:#01579b
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Feature Architecture
|
||||
|
||||
### High-Level Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Entry Point"
|
||||
App[app.py<br/>FastAPI Application]
|
||||
Lifecycle[Features Lifecycle<br/>featuresLifecycle.py]
|
||||
end
|
||||
|
||||
subgraph "API Layer"
|
||||
Routes[Routes<br/>routeRealEstate.py<br/>routeChatPlayground.py<br/>routeDataNeutralization.py]
|
||||
end
|
||||
|
||||
subgraph "Feature Layer"
|
||||
RE[Real Estate Feature<br/>mainRealEstate.py]
|
||||
CA[Chat Althaus Feature<br/>mainChatAlthaus.py]
|
||||
SD[Sync Delta Feature<br/>mainSyncDelta.py]
|
||||
CP[Chat Playground Feature<br/>mainChatPlayground.py]
|
||||
NP[Neutralize Playground Feature<br/>mainNeutralizePlayground.py]
|
||||
end
|
||||
|
||||
subgraph "Service Layer"
|
||||
Services[Services Container<br/>AI, Chat, SharePoint, etc.]
|
||||
end
|
||||
|
||||
subgraph "Interface Layer"
|
||||
Interfaces[Interfaces<br/>Database, Ticket, etc.]
|
||||
end
|
||||
|
||||
subgraph "Connector Layer"
|
||||
DBConn[Database Connector<br/>connectorDbPostgre.py]
|
||||
TicketConn[Ticket Connectors<br/>connectorTicketsJira.py<br/>connectorTicketsClickup.py]
|
||||
VoiceConn[Voice Connector<br/>connectorVoiceGoogle.py]
|
||||
JsonConn[JSON Connector<br/>connectorDbJson.py]
|
||||
end
|
||||
|
||||
subgraph "External Systems"
|
||||
DB[(PostgreSQL Database)]
|
||||
Jira[Jira API]
|
||||
ClickUp[ClickUp API]
|
||||
SharePoint[SharePoint API]
|
||||
GoogleVoice[Google Voice API]
|
||||
end
|
||||
|
||||
App --> Lifecycle
|
||||
App --> Routes
|
||||
Lifecycle --> CA
|
||||
Lifecycle --> SD
|
||||
Routes --> RE
|
||||
Routes --> CP
|
||||
Routes --> NP
|
||||
|
||||
RE --> Services
|
||||
CA --> Services
|
||||
SD --> Services
|
||||
CP --> Services
|
||||
NP --> Services
|
||||
|
||||
Services --> Interfaces
|
||||
Interfaces --> DBConn
|
||||
Interfaces --> TicketConn
|
||||
Interfaces --> VoiceConn
|
||||
Interfaces --> JsonConn
|
||||
|
||||
DBConn --> DB
|
||||
TicketConn --> Jira
|
||||
TicketConn --> ClickUp
|
||||
VoiceConn --> GoogleVoice
|
||||
Services --> SharePoint
|
||||
|
||||
style Features fill:#fff3e0,stroke:#e65100
|
||||
style Connectors fill:#fce4ec,stroke:#880e4f
|
||||
```
|
||||
|
||||
### Feature Request Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Route as Route<br/>routeRealEstate.py
|
||||
participant Feature as Feature<br/>mainRealEstate.py
|
||||
participant Service as Service<br/>Services Container
|
||||
participant Interface as Interface<br/>interfaceDbRealEstateObjects.py
|
||||
participant Connector as Connector<br/>connectorDbPostgre.py
|
||||
participant DB as Database<br/>PostgreSQL
|
||||
|
||||
Client->>Route: HTTP Request
|
||||
Route->>Route: Validate Request Data
|
||||
Route->>Feature: Call Feature Function
|
||||
Feature->>Service: Use Service (e.g., AI Service)
|
||||
Service-->>Feature: Service Result
|
||||
Feature->>Interface: Request Data Access
|
||||
Interface->>Connector: Execute Query
|
||||
Connector->>DB: SQL Query
|
||||
DB-->>Connector: Raw Data
|
||||
Connector-->>Interface: Raw Data
|
||||
Interface->>Interface: Transform to Domain Objects
|
||||
Interface-->>Feature: Domain Objects
|
||||
Feature->>Feature: Process Business Logic
|
||||
Feature-->>Route: Processed Result
|
||||
Route->>Route: Serialize Response
|
||||
Route-->>Client: HTTP Response
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Feature Lifecycle Management
|
||||
|
||||
Features that require background processing, scheduled tasks, or event-driven operations are managed through the **Features Lifecycle** system.
|
||||
|
||||
### Lifecycle Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Application Startup"
|
||||
App[app.py<br/>FastAPI Application]
|
||||
Lifespan[Lifespan Context Manager]
|
||||
end
|
||||
|
||||
subgraph "Features Lifecycle"
|
||||
Lifecycle[featuresLifecycle.py]
|
||||
Start[start function]
|
||||
Stop[stop function]
|
||||
end
|
||||
|
||||
subgraph "Background Features"
|
||||
SyncDelta[SyncDelta Manager<br/>startSyncManager]
|
||||
ChatAlthaus[ChatAlthaus Manager<br/>startDataScheduler]
|
||||
AutomationEvents[Automation Events<br/>syncAutomationEvents]
|
||||
end
|
||||
|
||||
App --> Lifespan
|
||||
Lifespan -->|On Startup| Lifecycle
|
||||
Lifecycle --> Start
|
||||
Start --> SyncDelta
|
||||
Start --> ChatAlthaus
|
||||
Start --> AutomationEvents
|
||||
|
||||
Lifespan -->|On Shutdown| Lifecycle
|
||||
Lifecycle --> Stop
|
||||
Stop --> SyncDelta
|
||||
Stop --> ChatAlthaus
|
||||
|
||||
style Lifecycle fill:#e8f5e9,stroke:#1b5e20
|
||||
style SyncDelta fill:#fff3e0,stroke:#e65100
|
||||
style ChatAlthaus fill:#fff3e0,stroke:#e65100
|
||||
```
|
||||
|
||||
### Lifecycle Sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as app.py
|
||||
participant Lifespan as Lifespan Manager
|
||||
participant Lifecycle as featuresLifecycle
|
||||
participant EventUser as Event User
|
||||
participant SyncDelta as SyncDelta Manager
|
||||
participant ChatAlthaus as ChatAlthaus Manager
|
||||
|
||||
App->>Lifespan: Application Startup
|
||||
Lifespan->>Lifecycle: start()
|
||||
Lifecycle->>EventUser: getRootInterface().getUserByUsername("event")
|
||||
EventUser-->>Lifecycle: Event User Object
|
||||
|
||||
Lifecycle->>ChatAlthaus: syncAutomationEvents()
|
||||
ChatAlthaus-->>Lifecycle: Events Synced
|
||||
|
||||
Lifecycle->>SyncDelta: startSyncManager(eventUser)
|
||||
SyncDelta->>SyncDelta: Initialize Background Thread
|
||||
SyncDelta-->>Lifecycle: Manager Started
|
||||
|
||||
Lifecycle->>ChatAlthaus: startDataScheduler(eventUser)
|
||||
ChatAlthaus->>ChatAlthaus: Initialize Scheduler
|
||||
ChatAlthaus-->>Lifecycle: Scheduler Started
|
||||
|
||||
Lifecycle->>ChatAlthaus: performDataUpdate(eventUser)
|
||||
ChatAlthaus-->>Lifecycle: Initial Update Complete
|
||||
|
||||
Lifecycle-->>Lifespan: Startup Complete
|
||||
Lifespan-->>App: Application Ready
|
||||
|
||||
Note over App: Application Running...
|
||||
|
||||
App->>Lifespan: Application Shutdown
|
||||
Lifespan->>Lifecycle: stop()
|
||||
Lifecycle->>SyncDelta: Stop Manager
|
||||
Lifecycle->>ChatAlthaus: Stop Scheduler
|
||||
Lifecycle-->>Lifespan: Shutdown Complete
|
||||
```
|
||||
|
||||
### Lifecycle-Managed Features
|
||||
|
||||
Features managed through the lifecycle system include:
|
||||
|
||||
1. **SyncDelta**: Background synchronization manager for ticket synchronization
|
||||
2. **ChatAlthaus**: Scheduled data updates for Althaus preprocessing service
|
||||
3. **Automation Events**: Event synchronization for chat automation
|
||||
|
||||
These features run continuously in the background and require proper initialization and cleanup during application startup and shutdown.
|
||||
|
||||
---
|
||||
|
||||
## Connectors in the Architecture
|
||||
|
||||
Connectors are the lowest-level abstraction for communicating with external systems. They provide concrete implementations for database connections, API integrations, and external service communication.
|
||||
|
||||
### Connector Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Connector Types"
|
||||
DBConn[Database Connectors<br/>connectorDbPostgre.py<br/>connectorDbJson.py]
|
||||
TicketConn[Ticket Connectors<br/>connectorTicketsJira.py<br/>connectorTicketsClickup.py]
|
||||
VoiceConn[Voice Connectors<br/>connectorVoiceGoogle.py]
|
||||
end
|
||||
|
||||
subgraph "Connector Responsibilities"
|
||||
Connection[Connection Management<br/>Establish & Maintain Connections]
|
||||
Query[Query Execution<br/>Execute Queries & API Calls]
|
||||
Transform[Data Transformation<br/>Raw Data ↔ Application Format]
|
||||
Error[Error Handling<br/>Connection Errors & Retries]
|
||||
end
|
||||
|
||||
subgraph "External Systems"
|
||||
PostgreSQL[(PostgreSQL Database)]
|
||||
JSONFile[JSON Files]
|
||||
JiraAPI[Jira API]
|
||||
ClickUpAPI[ClickUp API]
|
||||
GoogleVoiceAPI[Google Voice API]
|
||||
end
|
||||
|
||||
DBConn --> Connection
|
||||
TicketConn --> Connection
|
||||
VoiceConn --> Connection
|
||||
|
||||
Connection --> Query
|
||||
Query --> Transform
|
||||
Transform --> Error
|
||||
|
||||
DBConn --> PostgreSQL
|
||||
DBConn --> JSONFile
|
||||
TicketConn --> JiraAPI
|
||||
TicketConn --> ClickUpAPI
|
||||
VoiceConn --> GoogleVoiceAPI
|
||||
|
||||
style DBConn fill:#fce4ec,stroke:#880e4f
|
||||
style TicketConn fill:#fce4ec,stroke:#880e4f
|
||||
style VoiceConn fill:#fce4ec,stroke:#880e4f
|
||||
```
|
||||
|
||||
### Connector Usage Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Feature as Feature
|
||||
participant Service as Service
|
||||
participant Interface as Interface
|
||||
participant Connector as Connector
|
||||
participant External as External System
|
||||
|
||||
Feature->>Service: Use Service
|
||||
Service->>Interface: Request Data Access
|
||||
Interface->>Connector: Initialize Connection
|
||||
Connector->>External: Establish Connection
|
||||
External-->>Connector: Connection Established
|
||||
|
||||
Interface->>Connector: Execute Query/API Call
|
||||
Connector->>Connector: Format Request
|
||||
Connector->>External: Send Request
|
||||
External-->>Connector: Raw Response
|
||||
Connector->>Connector: Parse Response
|
||||
Connector-->>Interface: Formatted Data
|
||||
Interface->>Interface: Transform to Domain Objects
|
||||
Interface-->>Service: Domain Objects
|
||||
Service-->>Feature: Processed Result
|
||||
```
|
||||
|
||||
### Available Connectors
|
||||
|
||||
#### Database Connectors
|
||||
|
||||
**connectorDbPostgre.py** - PostgreSQL Database Connector
|
||||
- Manages PostgreSQL database connections
|
||||
- Executes SQL queries with parameterization
|
||||
- Handles JSONB column types
|
||||
- Provides transaction support
|
||||
- Used by: Real Estate interfaces, Chat interfaces, Application interfaces
|
||||
|
||||
**connectorDbJson.py** - JSON File Database Connector
|
||||
- Provides file-based data storage using JSON
|
||||
- Useful for development and testing
|
||||
- Lightweight alternative to PostgreSQL
|
||||
- Used by: Development environments, Testing scenarios
|
||||
|
||||
#### Ticket Connectors
|
||||
|
||||
**connectorTicketsJira.py** - Jira Ticket Connector
|
||||
- Integrates with Jira REST API
|
||||
- Manages Jira tickets, issues, and projects
|
||||
- Handles field mapping and synchronization
|
||||
- Used by: SyncDelta feature, Ticket interfaces
|
||||
|
||||
**connectorTicketsClickup.py** - ClickUp Ticket Connector
|
||||
- Integrates with ClickUp API
|
||||
- Manages ClickUp tasks and lists
|
||||
- Handles task synchronization
|
||||
- Used by: Ticket interfaces
|
||||
|
||||
#### Voice Connectors
|
||||
|
||||
**connectorVoiceGoogle.py** - Google Voice Connector
|
||||
- Integrates with Google Voice API
|
||||
- Handles voice transcription and processing
|
||||
- Manages voice data and audio files
|
||||
- Used by: Voice-related services and features
|
||||
|
||||
### Connector Integration Pattern
|
||||
|
||||
Connectors are never directly accessed by features. Instead, they follow this integration pattern:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Feature] --> B[Service]
|
||||
B --> C[Interface]
|
||||
C --> D[Connector]
|
||||
D --> E[External System]
|
||||
|
||||
style A fill:#fff3e0,stroke:#e65100
|
||||
style B fill:#e8f5e9,stroke:#1b5e20
|
||||
style C fill:#e1f5ff,stroke:#01579b
|
||||
style D fill:#fce4ec,stroke:#880e4f
|
||||
style E fill:#f5f5f5,stroke:#424242
|
||||
```
|
||||
|
||||
**Why This Pattern?**
|
||||
- **Abstraction**: Interfaces hide connector implementation details
|
||||
- **Flexibility**: Connectors can be swapped without affecting features
|
||||
- **Testability**: Interfaces can be mocked for testing
|
||||
- **Consistency**: All data access follows the same pattern
|
||||
- **User Context**: Interfaces handle user context and access control
|
||||
|
||||
---
|
||||
|
||||
## Individual Features
|
||||
|
||||
### Real Estate Feature
|
||||
|
||||
**Location**: `modules/features/realEstate/mainRealEstate.py`
|
||||
|
||||
**Purpose**: Provides AI-powered natural language processing for Real Estate database operations. Enables users to interact with Real Estate data using natural language commands that are translated into CRUD operations.
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Real Estate Feature"
|
||||
Route[routeRealEstate.py]
|
||||
Feature[mainRealEstate.py]
|
||||
Intent[Intent Analysis<br/>analyzeUserIntent]
|
||||
CRUD[CRUD Operations<br/>executeIntentBasedOperation]
|
||||
Query[Direct Queries<br/>executeDirectQuery]
|
||||
end
|
||||
|
||||
subgraph "Dependencies"
|
||||
AIService[AI Service<br/>Intent Recognition]
|
||||
REInterface[Real Estate Interface<br/>interfaceDbRealEstateObjects.py]
|
||||
DBConnector[Database Connector<br/>connectorDbPostgre.py]
|
||||
end
|
||||
|
||||
subgraph "Data Models"
|
||||
REModels[Real Estate Models<br/>Projekt, Parzelle, etc.]
|
||||
end
|
||||
|
||||
Route --> Feature
|
||||
Feature --> Intent
|
||||
Feature --> CRUD
|
||||
Feature --> Query
|
||||
|
||||
Intent --> AIService
|
||||
CRUD --> REInterface
|
||||
Query --> REInterface
|
||||
|
||||
REInterface --> DBConnector
|
||||
REInterface --> REModels
|
||||
|
||||
style Feature fill:#fff3e0,stroke:#e65100
|
||||
```
|
||||
|
||||
**Key Functions**:
|
||||
- `processNaturalLanguageCommand()`: Main entry point for natural language processing
|
||||
- `analyzeUserIntent()`: Uses AI to analyze user input and extract intent, entity, and parameters
|
||||
- `executeIntentBasedOperation()`: Executes CRUD operations based on analyzed intent
|
||||
- `executeDirectQuery()`: Executes direct SQL queries without AI processing
|
||||
|
||||
**Connector Usage**:
|
||||
- Uses **Database Connector** (`connectorDbPostgre.py`) through the Real Estate Interface
|
||||
- Accesses PostgreSQL database for Real Estate data
|
||||
- Handles CRUD operations on entities like Projekt, Parzelle, Dokument
|
||||
|
||||
**Service Integration**:
|
||||
- Uses **AI Service** for intent recognition and natural language understanding
|
||||
- Leverages AI planning capabilities to analyze user commands
|
||||
|
||||
**Migration Path**:
|
||||
- May evolve into a **Service** if Real Estate operations become reusable across domains
|
||||
- Could integrate with **Workflows** for complex multi-step Real Estate processes
|
||||
|
||||
---
|
||||
|
||||
### Chat Althaus Feature
|
||||
|
||||
**Location**: `modules/features/chatAlthaus/mainChatAlthaus.py`
|
||||
|
||||
**Purpose**: Manages scheduled data updates for the Althaus preprocessing service. Triggers daily updates to synchronize database configuration with external preprocessing service.
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Chat Althaus Feature"
|
||||
Lifecycle[featuresLifecycle.py]
|
||||
Manager[ManagerChatAlthaus]
|
||||
Scheduler[Data Scheduler]
|
||||
Updater[Data Updater<br/>updateDatabaseWithConfig]
|
||||
end
|
||||
|
||||
subgraph "Dependencies"
|
||||
Services[Services Container]
|
||||
HTTPClient[HTTP Client<br/>aiohttp]
|
||||
Config[Configuration<br/>APP_CONFIG]
|
||||
end
|
||||
|
||||
subgraph "External System"
|
||||
AlthausAPI[Althaus Preprocessing API<br/>Azure Function]
|
||||
end
|
||||
|
||||
Lifecycle --> Manager
|
||||
Manager --> Scheduler
|
||||
Manager --> Updater
|
||||
|
||||
Updater --> Services
|
||||
Updater --> HTTPClient
|
||||
Updater --> Config
|
||||
|
||||
Updater --> AlthausAPI
|
||||
|
||||
style Manager fill:#fff3e0,stroke:#e65100
|
||||
```
|
||||
|
||||
**Key Functions**:
|
||||
- `startDataScheduler()`: Initializes and starts the scheduled data update manager
|
||||
- `performDataUpdate()`: Executes immediate data update
|
||||
- `updateDatabaseWithConfig()`: Sends configuration to Althaus preprocessing service
|
||||
|
||||
**Scheduling**:
|
||||
- Runs daily at 01:00 UTC
|
||||
- Uses background scheduler for automated execution
|
||||
- Managed through Features Lifecycle system
|
||||
|
||||
**Connector Usage**:
|
||||
- Uses **HTTP Client** (aiohttp) for API communication
|
||||
- No database connector (uses external API)
|
||||
|
||||
**Service Integration**:
|
||||
- Uses **Services Container** for configuration access
|
||||
- Leverages shared configuration utilities
|
||||
|
||||
**Migration Path**:
|
||||
- Could become a **Service** if data synchronization becomes a core capability
|
||||
- May integrate with **Workflows** for complex data processing pipelines
|
||||
|
||||
---
|
||||
|
||||
### Sync Delta Feature
|
||||
|
||||
**Location**: `modules/features/syncDelta/mainSyncDelta.py`
|
||||
|
||||
**Purpose**: Synchronizes tickets between Jira and SharePoint. Manages bidirectional synchronization of ticket data, supporting both CSV and Excel file formats.
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Sync Delta Feature"
|
||||
Lifecycle[featuresLifecycle.py]
|
||||
Manager[ManagerSyncDelta]
|
||||
Sync[syncTicketsOverSharepoint]
|
||||
Merge[Data Merging Logic]
|
||||
Audit[Audit Logging]
|
||||
end
|
||||
|
||||
subgraph "Dependencies"
|
||||
TicketService[Ticket Service]
|
||||
SharePointService[SharePoint Service]
|
||||
JiraConnector[Jira Connector<br/>connectorTicketsJira.py]
|
||||
end
|
||||
|
||||
subgraph "External Systems"
|
||||
Jira[Jira API]
|
||||
SharePoint[SharePoint API]
|
||||
end
|
||||
|
||||
Lifecycle --> Manager
|
||||
Manager --> Sync
|
||||
Sync --> Merge
|
||||
Sync --> Audit
|
||||
|
||||
Sync --> TicketService
|
||||
Sync --> SharePointService
|
||||
|
||||
TicketService --> JiraConnector
|
||||
JiraConnector --> Jira
|
||||
SharePointService --> SharePoint
|
||||
|
||||
style Manager fill:#fff3e0,stroke:#e65100
|
||||
```
|
||||
|
||||
**Key Functions**:
|
||||
- `startSyncManager()`: Initializes background synchronization manager
|
||||
- `syncTicketsOverSharepoint()`: Performs synchronization between Jira and SharePoint
|
||||
- `initializeInterface()`: Sets up connectors and validates connections
|
||||
- `_logAuditEvent()`: Logs synchronization events for auditing
|
||||
|
||||
**Synchronization Modes**:
|
||||
- **CSV Mode**: Uses CSV files for data exchange
|
||||
- **Excel Mode**: Uses Excel (.xlsx) files for data exchange
|
||||
|
||||
**Connector Usage**:
|
||||
- Uses **Jira Connector** (`connectorTicketsJira.py`) through Ticket Service
|
||||
- Uses **SharePoint Service** for file operations
|
||||
- Manages field mapping between Jira and SharePoint formats
|
||||
|
||||
**Service Integration**:
|
||||
- Uses **Ticket Service** for ticket interface creation
|
||||
- Uses **SharePoint Service** for file upload/download
|
||||
- Leverages **Services Container** for configuration and utilities
|
||||
|
||||
**Migration Path**:
|
||||
- Likely candidate for **Service** migration as ticket synchronization becomes core functionality
|
||||
- Could integrate with **Workflows** for complex synchronization scenarios
|
||||
|
||||
---
|
||||
|
||||
### Chat Playground Feature
|
||||
|
||||
**Location**: `modules/features/chatPlayground/mainChatPlayground.py`
|
||||
|
||||
**Purpose**: Provides entry point for chat workflow functionality. Acts as a thin wrapper around the WorkflowManager for chat-based interactions.
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Chat Playground Feature"
|
||||
Route[routeChatPlayground.py]
|
||||
Feature[mainChatPlayground.py]
|
||||
Start[chatStart]
|
||||
Stop[chatStop]
|
||||
end
|
||||
|
||||
subgraph "Workflow System"
|
||||
WorkflowManager[WorkflowManager]
|
||||
WorkflowProcessor[WorkflowProcessor]
|
||||
Methods[Workflow Methods]
|
||||
end
|
||||
|
||||
subgraph "Dependencies"
|
||||
Services[Services Container]
|
||||
end
|
||||
|
||||
Route --> Feature
|
||||
Feature --> Start
|
||||
Feature --> Stop
|
||||
|
||||
Start --> WorkflowManager
|
||||
Stop --> WorkflowManager
|
||||
|
||||
WorkflowManager --> WorkflowProcessor
|
||||
WorkflowProcessor --> Methods
|
||||
|
||||
WorkflowManager --> Services
|
||||
|
||||
style Feature fill:#fff3e0,stroke:#e65100
|
||||
style WorkflowManager fill:#e1f5ff,stroke:#01579b
|
||||
```
|
||||
|
||||
**Key Functions**:
|
||||
- `chatStart()`: Starts a new chat workflow or continues an existing one
|
||||
- `chatStop()`: Stops a running chat workflow
|
||||
|
||||
**Relationship to Workflows**:
|
||||
- This feature is a **bridge** between routes and the workflow system
|
||||
- Delegates all processing to WorkflowManager
|
||||
- Demonstrates how features can integrate with workflows
|
||||
|
||||
**Connector Usage**:
|
||||
- No direct connector usage (delegates to workflows)
|
||||
- Workflows use connectors through services and methods
|
||||
|
||||
**Service Integration**:
|
||||
- Uses **Services Container** for workflow management
|
||||
- Leverages workflow services for chat operations
|
||||
|
||||
**Migration Path**:
|
||||
- Already integrated with **Workflows** system
|
||||
- May be simplified or removed as workflows become the primary interface
|
||||
|
||||
---
|
||||
|
||||
### Neutralize Playground Feature
|
||||
|
||||
**Location**: `modules/features/neutralizePlayground/mainNeutralizePlayground.py`
|
||||
|
||||
**Purpose**: Provides a playground interface for data neutralization functionality. Wraps the Neutralization Service for testing and experimentation.
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Neutralize Playground Feature"
|
||||
Route[routeDataNeutralization.py]
|
||||
Feature[NeutralizationPlayground]
|
||||
ProcessText[processText]
|
||||
ProcessFiles[processFiles]
|
||||
CleanAttributes[cleanAttributes]
|
||||
Stats[getStats]
|
||||
Config[getConfig/saveConfig]
|
||||
end
|
||||
|
||||
subgraph "Dependencies"
|
||||
NeutralizationService[Neutralization Service]
|
||||
end
|
||||
|
||||
Route --> Feature
|
||||
Feature --> ProcessText
|
||||
Feature --> ProcessFiles
|
||||
Feature --> CleanAttributes
|
||||
Feature --> Stats
|
||||
Feature --> Config
|
||||
|
||||
ProcessText --> NeutralizationService
|
||||
ProcessFiles --> NeutralizationService
|
||||
CleanAttributes --> NeutralizationService
|
||||
Stats --> NeutralizationService
|
||||
Config --> NeutralizationService
|
||||
|
||||
style Feature fill:#fff3e0,stroke:#e65100
|
||||
style NeutralizationService fill:#e8f5e9,stroke:#1b5e20
|
||||
```
|
||||
|
||||
**Key Functions**:
|
||||
- `processText()`: Processes text for data neutralization
|
||||
- `processFiles()`: Processes files for data neutralization
|
||||
- `cleanAttributes()`: Cleans neutralization attributes
|
||||
- `getStats()`: Retrieves neutralization statistics
|
||||
- `getConfig()` / `saveConfig()`: Manages neutralization configuration
|
||||
|
||||
**Purpose as Playground**:
|
||||
- Provides testing interface for neutralization functionality
|
||||
- Allows experimentation with neutralization patterns
|
||||
- Demonstrates service usage patterns
|
||||
|
||||
**Connector Usage**:
|
||||
- No direct connector usage (uses Neutralization Service)
|
||||
- Service handles all data access internally
|
||||
|
||||
**Service Integration**:
|
||||
- Wraps **Neutralization Service** for easy access
|
||||
- Provides playground-specific functionality
|
||||
|
||||
**Migration Path**:
|
||||
- May be removed once neutralization is fully integrated
|
||||
- Functionality may move directly to routes using the service
|
||||
|
||||
---
|
||||
|
||||
## Feature Patterns and Best Practices
|
||||
|
||||
### Pattern 1: Stateless Feature Design
|
||||
|
||||
Features should be stateless and operate without session management. Each request should be independent and self-contained.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Request] --> B[Feature Function]
|
||||
B --> C[Process Request]
|
||||
C --> D[Return Result]
|
||||
|
||||
style B fill:#fff3e0,stroke:#e65100
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Simpler implementation
|
||||
- Better scalability
|
||||
- Easier testing
|
||||
- No state management overhead
|
||||
|
||||
### Pattern 2: Service Delegation
|
||||
|
||||
Features should delegate cross-cutting functionality to services rather than implementing it directly.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A[Feature] --> B{Needs Functionality}
|
||||
B -->|AI Processing| C[AI Service]
|
||||
B -->|Data Access| D[Interface]
|
||||
B -->|File Operations| E[File Service]
|
||||
B -->|Other| F[Other Services]
|
||||
|
||||
style A fill:#fff3e0,stroke:#e65100
|
||||
style C fill:#e8f5e9,stroke:#1b5e20
|
||||
style D fill:#e1f5ff,stroke:#01579b
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Code reuse
|
||||
- Consistent behavior
|
||||
- Easier maintenance
|
||||
- Better separation of concerns
|
||||
|
||||
### Pattern 3: Interface Abstraction
|
||||
|
||||
Features should never directly access connectors. All data access should go through interfaces.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Feature] --> B[Interface]
|
||||
B --> C[Connector]
|
||||
C --> D[External System]
|
||||
|
||||
style A fill:#fff3e0,stroke:#e65100
|
||||
style B fill:#e1f5ff,stroke:#01579b
|
||||
style C fill:#fce4ec,stroke:#880e4f
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Abstraction of implementation details
|
||||
- Flexibility to change connectors
|
||||
- Consistent data access patterns
|
||||
- User context handling
|
||||
|
||||
### Pattern 4: Background Processing
|
||||
|
||||
Features requiring background processing should use the Features Lifecycle system.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as Application
|
||||
participant Lifecycle as Features Lifecycle
|
||||
participant Feature as Background Feature
|
||||
participant Scheduler as Scheduler
|
||||
|
||||
App->>Lifecycle: Startup
|
||||
Lifecycle->>Feature: Initialize Manager
|
||||
Feature->>Scheduler: Start Background Task
|
||||
Scheduler-->>Feature: Task Running
|
||||
|
||||
Note over Feature,Scheduler: Background Processing...
|
||||
|
||||
App->>Lifecycle: Shutdown
|
||||
Lifecycle->>Feature: Stop Manager
|
||||
Feature->>Scheduler: Stop Background Task
|
||||
Scheduler-->>Feature: Task Stopped
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Proper lifecycle management
|
||||
- Clean startup/shutdown
|
||||
- Resource management
|
||||
- Error handling
|
||||
|
||||
### Pattern 5: Migration Planning
|
||||
|
||||
Features should be designed with migration in mind. Consider future migration to services or workflows during design.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Feature Design] --> B{Consider Migration}
|
||||
B -->|Reusable Logic| C[Design as Service]
|
||||
B -->|Complex Flow| D[Design for Workflow]
|
||||
B -->|Temporary| E[Keep as Feature]
|
||||
|
||||
style A fill:#fff3e0,stroke:#e65100
|
||||
style C fill:#e8f5e9,stroke:#1b5e20
|
||||
style D fill:#e1f5ff,stroke:#01579b
|
||||
```
|
||||
|
||||
**Best Practices**:
|
||||
- Document migration path
|
||||
- Keep dependencies minimal
|
||||
- Use standard patterns
|
||||
- Plan for refactoring
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The Features component provides a flexible, domain-specific business logic layer that enables rapid development while maintaining architectural boundaries. Features serve as temporary solutions that bridge the gap between initial requirements and full service or workflow implementation.
|
||||
|
||||
**Key Takeaways**:
|
||||
|
||||
1. **Features are Temporary**: Designed to be migrated to services or workflows as they mature
|
||||
2. **Domain-Specific**: Each feature addresses a specific business domain or use case
|
||||
3. **Service-Dependent**: Features leverage services for cross-cutting functionality
|
||||
4. **Interface-Abstracted**: Features access data through interfaces, never directly through connectors
|
||||
5. **Lifecycle-Managed**: Background features are managed through the Features Lifecycle system
|
||||
6. **Connector Integration**: Connectors are accessed through interfaces, providing abstraction and flexibility
|
||||
|
||||
The architecture supports a natural evolution path from features to services or workflows, ensuring that the codebase remains maintainable and scalable as functionality matures.
|
||||
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,509 +0,0 @@
|
|||
# Security API Documentation
|
||||
|
||||
API documentation for security-related endpoints and how to use security components in routes.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Authentication Endpoints](#authentication-endpoints)
|
||||
3. [Authentication Flows](#authentication-flows)
|
||||
4. [Using Security in Routes](#using-security-in-routes)
|
||||
5. [API Examples](#api-examples)
|
||||
|
||||
## Overview
|
||||
|
||||
The Security component provides authentication endpoints and utilities that routes can use to protect their endpoints and access user context. This document focuses on the API surface and how to use security components from route handlers.
|
||||
|
||||
For detailed information about the Security component architecture and internal workings, see [Security Component Documentation](./security-component.md).
|
||||
|
||||
## Authentication Endpoints
|
||||
|
||||
### Local Authentication
|
||||
|
||||
**Base Path**: `/api/local`
|
||||
|
||||
#### POST `/api/local/login`
|
||||
Authenticate with username and password.
|
||||
|
||||
**Request**:
|
||||
- Content-Type: `application/x-www-form-urlencoded`
|
||||
- Headers: `X-CSRF-Token` (required)
|
||||
- Body: `username`, `password` (form data)
|
||||
|
||||
**Response**:
|
||||
- Status: `200 OK`
|
||||
- Cookies: `auth_token` (httpOnly), `refresh_token` (httpOnly)
|
||||
- Body:
|
||||
```json
|
||||
{
|
||||
"type": "local_auth_success",
|
||||
"message": "Login successful - tokens set in httpOnly cookies",
|
||||
"authenticationAuthority": "local",
|
||||
"expires_at": "2024-01-01T12:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
**Rate Limit**: 30 requests per minute
|
||||
|
||||
#### POST `/api/local/register`
|
||||
Register a new user account.
|
||||
|
||||
**Request**:
|
||||
- Content-Type: `application/json`
|
||||
- Body: User object + password
|
||||
|
||||
**Response**: User object
|
||||
|
||||
**Rate Limit**: 10 requests per minute
|
||||
|
||||
#### GET `/api/local/me`
|
||||
Get current authenticated user information.
|
||||
|
||||
**Request**:
|
||||
- Headers: `Authorization: Bearer <token>` OR Cookie: `auth_token`
|
||||
|
||||
**Response**: User object
|
||||
|
||||
**Rate Limit**: 30 requests per minute
|
||||
|
||||
#### POST `/api/local/refresh`
|
||||
Refresh access token using refresh token.
|
||||
|
||||
**Request**:
|
||||
- Cookie: `refresh_token` (httpOnly)
|
||||
|
||||
**Response**:
|
||||
- Cookies: New `auth_token` and `refresh_token`
|
||||
- Body: Token refresh response
|
||||
|
||||
**Rate Limit**: 60 requests per minute
|
||||
|
||||
#### POST `/api/local/logout`
|
||||
Logout current user and invalidate tokens.
|
||||
|
||||
**Request**:
|
||||
- Headers: `Authorization: Bearer <token>` OR Cookie: `auth_token`
|
||||
|
||||
**Response**: Logout confirmation
|
||||
|
||||
**Rate Limit**: 10 requests per minute
|
||||
|
||||
### Microsoft OAuth Authentication
|
||||
|
||||
**Base Path**: `/api/msft`
|
||||
|
||||
#### GET `/api/msft/login`
|
||||
Initiate Microsoft OAuth login flow.
|
||||
|
||||
**Response**: Redirects to Microsoft OAuth login page
|
||||
|
||||
#### GET `/api/msft/auth/callback`
|
||||
OAuth callback endpoint (handled by Microsoft).
|
||||
|
||||
**Query Parameters**:
|
||||
- `code`: Authorization code from Microsoft
|
||||
- `state`: State parameter for CSRF protection
|
||||
|
||||
**Response**: HTML page that sets cookies and redirects
|
||||
|
||||
#### GET `/api/msft/me`
|
||||
Get current authenticated user information.
|
||||
|
||||
**Request**:
|
||||
- Headers: `Authorization: Bearer <token>` OR Cookie: `auth_token`
|
||||
|
||||
**Response**: User object
|
||||
|
||||
#### POST `/api/msft/logout`
|
||||
Logout current user.
|
||||
|
||||
**Request**:
|
||||
- Headers: `Authorization: Bearer <token>` OR Cookie: `auth_token`
|
||||
|
||||
**Response**: Logout confirmation
|
||||
|
||||
### Google OAuth Authentication
|
||||
|
||||
**Base Path**: `/api/google`
|
||||
|
||||
#### GET `/api/google/login`
|
||||
Initiate Google OAuth login flow.
|
||||
|
||||
**Query Parameters**:
|
||||
- `state`: Optional state parameter (default: "login")
|
||||
- `connectionId`: Optional connection ID for connection flow
|
||||
|
||||
**Response**: Redirects to Google OAuth login page
|
||||
|
||||
#### GET `/api/google/auth/callback`
|
||||
OAuth callback endpoint (handled by Google).
|
||||
|
||||
**Query Parameters**:
|
||||
- `code`: Authorization code from Google
|
||||
- `state`: State parameter for CSRF protection
|
||||
|
||||
**Response**: HTML page that sets cookies and redirects
|
||||
|
||||
#### GET `/api/google/me`
|
||||
Get current authenticated user information.
|
||||
|
||||
**Request**:
|
||||
- Headers: `Authorization: Bearer <token>` OR Cookie: `auth_token`
|
||||
|
||||
**Response**: User object
|
||||
|
||||
#### POST `/api/google/logout`
|
||||
Logout current user.
|
||||
|
||||
**Request**:
|
||||
- Headers: `Authorization: Bearer <token>` OR Cookie: `auth_token`
|
||||
|
||||
**Response**: Logout confirmation
|
||||
|
||||
## Authentication Flows
|
||||
|
||||
### Login Flow (Local Authentication)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Route as Login Route<br/>POST /api/local/login
|
||||
participant Auth as auth.py
|
||||
participant JWT as jwtService.py
|
||||
participant Interface as Interface Layer
|
||||
participant DB as Database
|
||||
|
||||
Client->>Route: POST /api/local/login<br/>(username, password, X-CSRF-Token)
|
||||
Route->>Route: Validate CSRF Token
|
||||
Route->>Interface: authenticateLocalUser(username, password)
|
||||
Interface->>DB: Query User & Verify Password
|
||||
DB-->>Interface: User Record
|
||||
|
||||
alt Invalid Credentials
|
||||
Interface-->>Route: None
|
||||
Route-->>Client: HTTPException 401
|
||||
end
|
||||
|
||||
Route->>JWT: createAccessToken(userData)
|
||||
JWT->>JWT: Generate JTI (UUID)
|
||||
JWT->>JWT: Set Expiration
|
||||
JWT->>JWT: Sign JWT
|
||||
JWT-->>Route: Access Token + Expires At
|
||||
|
||||
Route->>JWT: createRefreshToken(userData)
|
||||
JWT-->>Route: Refresh Token + Expires At
|
||||
|
||||
Route->>Interface: Save Token to DB<br/>(for LOCAL authority)
|
||||
Interface->>DB: Insert Token Record
|
||||
|
||||
Route->>JWT: setAccessTokenCookie(response, token)
|
||||
JWT->>JWT: Set httpOnly Cookie<br/>(secure, samesite=strict)
|
||||
|
||||
Route->>JWT: setRefreshTokenCookie(response, token)
|
||||
JWT->>JWT: Set httpOnly Cookie
|
||||
|
||||
Route-->>Client: HTTP Response<br/>(with Cookies)
|
||||
```
|
||||
|
||||
### OAuth Flow (Microsoft/Google)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Route as OAuth Route<br/>GET /api/{provider}/login
|
||||
participant Provider as OAuth Provider<br/>(MSFT/Google)
|
||||
participant JWT as jwtService.py
|
||||
participant Interface as Interface Layer
|
||||
participant DB as Database
|
||||
|
||||
Client->>Route: GET /api/{provider}/login
|
||||
Route->>Route: Generate OAuth State
|
||||
Route->>Provider: Redirect to OAuth URL<br/>(with state, client_id, redirect_uri)
|
||||
Provider-->>Client: OAuth Login Page
|
||||
|
||||
Client->>Provider: User Authenticates
|
||||
Provider->>Route: GET /api/{provider}/callback<br/>(code, state)
|
||||
|
||||
Route->>Route: Validate State
|
||||
Route->>Provider: Exchange Code for Tokens<br/>(POST /token)
|
||||
Provider-->>Route: Access Token + Refresh Token
|
||||
|
||||
Route->>Provider: Get User Info<br/>(using Access Token)
|
||||
Provider-->>Route: User Profile Data
|
||||
|
||||
Route->>Interface: Find/Create User<br/>(by external ID)
|
||||
Interface->>DB: Query/Create User
|
||||
DB-->>Interface: User Record
|
||||
Interface-->>Route: User Object
|
||||
|
||||
Route->>Interface: Save/Create Connection
|
||||
Interface->>DB: Save Connection Record
|
||||
|
||||
Route->>Interface: Save Token
|
||||
Interface->>DB: Save Token Record
|
||||
|
||||
Route->>JWT: createAccessToken(userData)
|
||||
JWT-->>Route: Gateway JWT Token
|
||||
|
||||
Route->>JWT: setAccessTokenCookie(response, token)
|
||||
Route->>JWT: setRefreshTokenCookie(response, token)
|
||||
|
||||
Route-->>Client: HTTP Response<br/>(with Gateway Cookies)
|
||||
```
|
||||
|
||||
## Using Security in Routes
|
||||
|
||||
### Basic Authentication Pattern
|
||||
|
||||
All protected routes use the `getCurrentUser` dependency to access the authenticated user:
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter, Depends
|
||||
from modules.security.auth import getCurrentUser
|
||||
from modules.datamodels.datamodelUam import User
|
||||
|
||||
router = APIRouter(prefix="/api/example", tags=["Example"])
|
||||
|
||||
@router.get("/protected")
|
||||
async def protected_endpoint(
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
) -> dict:
|
||||
"""
|
||||
Protected endpoint that requires authentication.
|
||||
The currentUser parameter is automatically populated by getCurrentUser.
|
||||
"""
|
||||
return {
|
||||
"message": f"Hello, {currentUser.username}!",
|
||||
"userId": currentUser.id,
|
||||
"mandateId": currentUser.mandateId
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
Use the `limiter` from the security module to add rate limiting:
|
||||
|
||||
```python
|
||||
from modules.security.auth import getCurrentUser, limiter
|
||||
from fastapi import Request
|
||||
|
||||
@router.post("/action")
|
||||
@limiter.limit("30/minute")
|
||||
async def rate_limited_endpoint(
|
||||
request: Request,
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
) -> dict:
|
||||
"""Endpoint with rate limiting (30 requests per minute)"""
|
||||
return {"status": "success"}
|
||||
```
|
||||
|
||||
### Cookie vs Header Authentication
|
||||
|
||||
The security component supports both authentication methods:
|
||||
|
||||
1. **Cookie-based** (preferred for web apps):
|
||||
- Tokens are automatically sent via httpOnly cookies
|
||||
- More secure (not accessible via JavaScript)
|
||||
- Automatically included in requests from same origin
|
||||
|
||||
2. **Header-based** (for API clients):
|
||||
- Use `Authorization: Bearer <token>` header
|
||||
- Required for programmatic API access
|
||||
- Tokens can be obtained from login endpoints
|
||||
|
||||
The `CookieAuth` class checks cookies first, then falls back to headers.
|
||||
|
||||
### Creating Tokens in Routes
|
||||
|
||||
When implementing custom authentication endpoints, use the JWT service:
|
||||
|
||||
```python
|
||||
from modules.security.jwtService import (
|
||||
createAccessToken,
|
||||
createRefreshToken,
|
||||
setAccessTokenCookie,
|
||||
setRefreshTokenCookie
|
||||
)
|
||||
from modules.datamodels.datamodelUam import AuthAuthority
|
||||
|
||||
@router.post("/custom-login")
|
||||
async def custom_login(
|
||||
response: Response,
|
||||
username: str,
|
||||
password: str
|
||||
):
|
||||
# Verify credentials...
|
||||
user = verify_user(username, password)
|
||||
|
||||
# Create token data
|
||||
token_data = {
|
||||
"sub": user.username,
|
||||
"mandateId": str(user.mandateId),
|
||||
"userId": str(user.id),
|
||||
"authenticationAuthority": AuthAuthority.LOCAL
|
||||
}
|
||||
|
||||
# Create tokens
|
||||
access_token, expires_at = createAccessToken(token_data)
|
||||
refresh_token, _ = createRefreshToken(token_data)
|
||||
|
||||
# Set cookies
|
||||
setAccessTokenCookie(response, access_token)
|
||||
setRefreshTokenCookie(response, refresh_token)
|
||||
|
||||
return {"status": "success", "expires_at": expires_at.isoformat()}
|
||||
```
|
||||
|
||||
### Clearing Tokens (Logout)
|
||||
|
||||
Use the JWT service cookie clearing functions:
|
||||
|
||||
```python
|
||||
from modules.security.jwtService import (
|
||||
clearAccessTokenCookie,
|
||||
clearRefreshTokenCookie
|
||||
)
|
||||
|
||||
@router.post("/logout")
|
||||
async def logout(
|
||||
response: Response,
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
):
|
||||
# Clear cookies
|
||||
clearAccessTokenCookie(response)
|
||||
clearRefreshTokenCookie(response)
|
||||
|
||||
# Optionally revoke token in database
|
||||
# interface.revokeToken(tokenId)
|
||||
|
||||
return {"status": "logged_out"}
|
||||
```
|
||||
|
||||
## API Examples
|
||||
|
||||
### Example: Protected Endpoint
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from modules.security.auth import getCurrentUser, limiter
|
||||
from modules.datamodels.datamodelUam import User
|
||||
from fastapi import Request
|
||||
|
||||
router = APIRouter(prefix="/api/data", tags=["Data"])
|
||||
|
||||
@router.get("/items")
|
||||
@limiter.limit("60/minute")
|
||||
async def get_items(
|
||||
request: Request,
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
) -> list:
|
||||
"""
|
||||
Get items for the current user.
|
||||
Requires authentication.
|
||||
"""
|
||||
# User is guaranteed to be authenticated and enabled
|
||||
# Access user properties: currentUser.id, currentUser.mandateId, etc.
|
||||
|
||||
items = fetch_user_items(currentUser.id)
|
||||
return items
|
||||
```
|
||||
|
||||
### Example: Role-Based Access
|
||||
|
||||
```python
|
||||
from modules.datamodels.datamodelUam import UserPrivilege
|
||||
|
||||
@router.delete("/admin/items/{item_id}")
|
||||
async def delete_item(
|
||||
item_id: str,
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
):
|
||||
"""
|
||||
Delete item (admin only).
|
||||
"""
|
||||
# Check user privilege
|
||||
if currentUser.privilege not in [UserPrivilege.ADMIN, UserPrivilege.SYSADMIN]:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Admin access required"
|
||||
)
|
||||
|
||||
delete_item_by_id(item_id)
|
||||
return {"status": "deleted"}
|
||||
```
|
||||
|
||||
### Example: Mandate-Scoped Access
|
||||
|
||||
```python
|
||||
@router.get("/mandate/data")
|
||||
async def get_mandate_data(
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
):
|
||||
"""
|
||||
Get data scoped to user's mandate.
|
||||
"""
|
||||
# User's mandateId is automatically validated during authentication
|
||||
# Token context must match user's current mandate
|
||||
|
||||
data = fetch_mandate_data(currentUser.mandateId)
|
||||
return data
|
||||
```
|
||||
|
||||
### Example: CSRF Protection
|
||||
|
||||
For state-changing operations, ensure CSRF token is included:
|
||||
|
||||
```python
|
||||
@router.post("/update")
|
||||
async def update_data(
|
||||
request: Request,
|
||||
data: dict,
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
):
|
||||
"""
|
||||
Update data (requires CSRF token).
|
||||
CSRF middleware automatically validates X-CSRF-Token header.
|
||||
"""
|
||||
# CSRF validation happens automatically via middleware
|
||||
# Just ensure client sends X-CSRF-Token header
|
||||
|
||||
update_user_data(currentUser.id, data)
|
||||
return {"status": "updated"}
|
||||
```
|
||||
|
||||
### Example: Error Handling
|
||||
|
||||
```python
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
@router.get("/sensitive")
|
||||
async def get_sensitive_data(
|
||||
currentUser: User = Depends(getCurrentUser)
|
||||
):
|
||||
"""
|
||||
Get sensitive data with proper error handling.
|
||||
"""
|
||||
try:
|
||||
# getCurrentUser already validates:
|
||||
# - Token is valid and not expired
|
||||
# - User exists and is enabled
|
||||
# - Context matches (mandateId, userId)
|
||||
|
||||
data = fetch_sensitive_data(currentUser.id)
|
||||
return data
|
||||
|
||||
except ValueError as e:
|
||||
# Handle business logic errors
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e)
|
||||
)
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Security Component Documentation](./security-component.md) - Component architecture and internal workings
|
||||
- [Architecture Overview](./architecture-overview.md) - Overall system architecture
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,786 +0,0 @@
|
|||
# Chat Playground Page Requirements
|
||||
|
||||
This document contains the complete frontend requirements for the chat playground page and workflow management components. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Page Structure and Layout](#page-structure-and-layout)
|
||||
3. [User Interactions and Functionality](#user-interactions-and-functionality)
|
||||
4. [Backend Routes and API Integration](#backend-routes-and-api-integration)
|
||||
5. [Field and Attribute Reference](#field-and-attribute-reference)
|
||||
6. [Dynamic Rendering Guidelines](#dynamic-rendering-guidelines)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The chat playground page enables users to create, manage, and track AI-powered workflows that process user requests through task planning and action execution. The frontend consists of a single page (`/chat-playground` or `/workflows/playground`) with different views/states:
|
||||
|
||||
- **Input View** - Start new workflows or continue existing ones with prompt input, mode selection, and file attachments
|
||||
- **Workflow View** - Live tracking of workflow execution with messages, logs, and statistics
|
||||
- **Workflow History View** - View completed workflows with full message history
|
||||
|
||||
All views use backend-driven UI generation, meaning field definitions, labels, validation rules, and UI structure come entirely from backend metadata through the `/api/attributes/ChatWorkflow` and `/api/attributes/UserInputRequest` endpoints. Views are managed through component state and routing within the same page, not as separate routes.
|
||||
|
||||
**Note:** Workflows are user-scoped. Users can only see and manage their own workflows. The backend enforces this security.
|
||||
|
||||
---
|
||||
|
||||
## Page Structure and Layout
|
||||
|
||||
### Chat Playground Page (`/chat-playground` or `/workflows/playground`)
|
||||
|
||||
The chat playground page uses different views/states to handle different user interactions. Views are managed through component state and routing within the same page component.
|
||||
|
||||
### Input View
|
||||
|
||||
**State:** `view === 'input'` or `selectedWorkflowId === null` (starting new workflow)
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Main Content Area:**
|
||||
- Chat input form with the following components:
|
||||
- **Workflow Dropdown** - Select previous workflow to continue (optional, shows "New Workflow" by default)
|
||||
- **Prompt Dropdown** - Select saved prompt template (optional)
|
||||
- **Prompt Text Input** - Multi-line text area for user input (required)
|
||||
- **Workflow Mode Selector** - Radio buttons or tabs for Actionplan, Dynamic, or Template (required)
|
||||
- **Neutralization Toggle** - Checkbox/toggle to enable/disable data neutralization (optional)
|
||||
- **File Attachment Area** - Drop zone with drag-and-drop support, plus "Attach File" button and "Browse Files" button
|
||||
- **Action Buttons:**
|
||||
- "Start Workflow" button (when no workflow selected)
|
||||
- "Continue Workflow" or "Send" button (when workflow selected)
|
||||
|
||||
- **File Attachment Display:**
|
||||
- List of attached files with preview (name, size, type)
|
||||
- Remove button for each attached file
|
||||
- Visual feedback for drag-and-drop (highlight, border, overlay when dragging)
|
||||
|
||||
- **Workflow Context Display** (when workflow selected):
|
||||
- Previous messages from selected workflow displayed in chat view
|
||||
- Workflow status indicator
|
||||
- Workflow mode pre-selected from selected workflow
|
||||
|
||||
### Workflow View
|
||||
|
||||
**State:** `view === 'workflow'` and `selectedWorkflowId !== null` (active workflow tracking)
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Main Content Area:**
|
||||
- **Chat Interface:**
|
||||
- Message history displayed chronologically
|
||||
- User messages and assistant responses
|
||||
- File attachments displayed as part of each message
|
||||
- Each file attachment shows:
|
||||
- File name, size, and type/icon
|
||||
- Action buttons: "Show", "Download", "Delete"
|
||||
- File preview (if applicable) when "Show" is clicked
|
||||
- Auto-scroll to latest message (optional, user can disable)
|
||||
|
||||
- **Log Panel** (optional, can be toggled):
|
||||
- Execution logs displayed chronologically
|
||||
- Log level indicators (info, warning, error)
|
||||
- Timestamps for each log entry
|
||||
|
||||
- **Statistics Panel** (optional, can be toggled):
|
||||
- Performance metrics
|
||||
- Task completion status
|
||||
- Execution time
|
||||
|
||||
- **Workflow Status Indicator:**
|
||||
- Current workflow status (running, completed, stopped, failed)
|
||||
- Visual status badge with color coding
|
||||
- Neutralization indicator (if enabled)
|
||||
|
||||
- **Action Buttons:**
|
||||
- "Stop" button (when workflow is running)
|
||||
- "Delete Message" button (on individual messages)
|
||||
- **File Action Buttons** (on each file attachment in messages):
|
||||
- "Show" button - Opens file preview/viewer
|
||||
- "Download" button - Downloads file to user's device
|
||||
- "Delete" button - Removes file from message (with confirmation)
|
||||
- "Back to Input" button (return to input view)
|
||||
|
||||
- **Live Updates:**
|
||||
- Real-time polling for new messages, logs, and statistics
|
||||
- Visual indicators for new content
|
||||
- Automatic UI updates without page refresh
|
||||
|
||||
---
|
||||
|
||||
## User Interactions and Functionality
|
||||
|
||||
### Starting a New Workflow
|
||||
|
||||
**Workflow Selection:**
|
||||
- User sees workflow dropdown with "New Workflow" as default option
|
||||
- Dropdown shows list of previous workflows (name, status, last activity)
|
||||
- Selecting "New Workflow" clears any selected workflow context
|
||||
- Selecting a previous workflow loads its context (messages, mode)
|
||||
|
||||
**Prompt Selection:**
|
||||
- User can optionally select a saved prompt from dropdown
|
||||
- Selecting a prompt pre-fills the prompt text field
|
||||
- User can still edit the pre-filled text
|
||||
- Prompt selection is optional
|
||||
|
||||
**Workflow Mode Selection:**
|
||||
- User must select one of three modes:
|
||||
- **Actionplan** - Traditional task planning (shows description on selection)
|
||||
- **Dynamic** - Iterative dynamic-style processing (shows description on selection)
|
||||
- **Template** - Template-based processing (shows description on selection)
|
||||
- Mode selection is required before starting workflow
|
||||
- Visual indicator shows selected mode
|
||||
|
||||
**Neutralization Toggle:**
|
||||
- User can toggle neutralization on/off
|
||||
- Frontend checks neutralization config status
|
||||
- If config exists and enabled, neutralization is enabled for workflow
|
||||
- If config disabled or not found, neutralization is disabled
|
||||
- User can configure neutralization settings on separate settings page
|
||||
- Toggle shows current state (enabled/disabled indicator)
|
||||
|
||||
**File Attachment:**
|
||||
- **Drag and Drop:**
|
||||
- User drags files over drop zone
|
||||
- Visual feedback shown (highlight, border, overlay)
|
||||
- User drops files onto drop zone
|
||||
- Files processed and uploaded automatically
|
||||
- File preview shown immediately
|
||||
|
||||
- **Button Upload:**
|
||||
- User clicks "Attach File" or "Upload File" button
|
||||
- File picker dialog opens
|
||||
- User selects files from device
|
||||
- Files uploaded and preview shown
|
||||
|
||||
- **Browse Existing Files:**
|
||||
- User clicks "Browse Files" or "Select File" button
|
||||
- File browser/selector opens with paginated file list
|
||||
- User selects existing files
|
||||
- Selected files added to attachment list
|
||||
|
||||
- **File Management:**
|
||||
- Attached files shown in list with name, size, type
|
||||
- Remove button for each file
|
||||
- Multiple files can be attached
|
||||
- Files validated before upload (size limits, type restrictions)
|
||||
|
||||
**Form Validation:**
|
||||
- Required fields: prompt text, workflow mode
|
||||
- Optional fields: prompt selection, neutralization, files
|
||||
- Validation errors shown inline
|
||||
- Form cannot be submitted until required fields are filled
|
||||
|
||||
**Workflow Submission:**
|
||||
- User clicks "Start Workflow" button
|
||||
- Form validates (required fields, types, workflowMode)
|
||||
- If validation fails, show errors
|
||||
- If validation passes:
|
||||
- Build UserInputRequest with prompt, listFileId (if files attached), userLanguage
|
||||
- Show loading state
|
||||
- Submit to `/api/chat/playground/start` with workflowMode query param
|
||||
- On success: Navigate to workflow view, start polling
|
||||
- On error: Show error message, revert optimistic updates
|
||||
|
||||
### Continuing an Existing Workflow
|
||||
|
||||
**Workflow Selection:**
|
||||
- User selects previous workflow from dropdown
|
||||
- Frontend loads workflow data via `GET /api/workflows/workflowId`
|
||||
- Previous messages displayed in chat view
|
||||
- Workflow mode pre-selected from selected workflow
|
||||
- Selected workflow name/ID shown
|
||||
|
||||
**Additional Input:**
|
||||
- User can optionally select a prompt (pre-fills text)
|
||||
- User enters or edits prompt text
|
||||
- User can toggle neutralization
|
||||
- User can attach files (same methods as starting new workflow)
|
||||
|
||||
**Workflow Continuation:**
|
||||
- User clicks "Continue" or "Send" button
|
||||
- Form validates (required fields)
|
||||
- If validation passes:
|
||||
- Build UserInputRequest with prompt, listFileId (if files attached)
|
||||
- Show optimistic update (add user message to UI)
|
||||
- Submit to `/api/chat/playground/start` with workflowId and workflowMode query params
|
||||
- On success: Continue polling, update workflow view
|
||||
- On error: Revert optimistic update, show error
|
||||
|
||||
### Live Tracking Workflow Progress
|
||||
|
||||
**Polling Mechanism:**
|
||||
- Start polling when workflow view is active
|
||||
- Poll interval: every 2 seconds (configurable)
|
||||
- Poll endpoint: `GET /api/chat/playground/{workflowId}/chatData`
|
||||
- Use `afterTimestamp` query param for selective data transfer
|
||||
|
||||
**Data Updates:**
|
||||
- Compare new data with existing data
|
||||
- If new data available:
|
||||
- Append new messages to chat view
|
||||
- Append new logs to log panel
|
||||
- Update statistics display
|
||||
- Update workflow status indicator
|
||||
- Scroll to latest content (if auto-scroll enabled)
|
||||
- If no new data, continue polling silently
|
||||
|
||||
**Polling Control:**
|
||||
- Stop polling when workflow status is "completed", "stopped", or "failed"
|
||||
- Stop polling when user navigates away
|
||||
- Show final status when workflow completes
|
||||
- Handle errors gracefully (404 stops polling, shows error)
|
||||
|
||||
**Visual Feedback:**
|
||||
- Loading indicators during polling
|
||||
- New content indicators
|
||||
- Status change animations
|
||||
- Progress indicators for long-running workflows
|
||||
|
||||
### Stopping a Running Workflow
|
||||
|
||||
**Stop Action:**
|
||||
- User clicks "Stop" button (only visible when workflow is running)
|
||||
- Confirmation dialog appears: "Stop this workflow?"
|
||||
- User can cancel or confirm
|
||||
|
||||
**Stop Submission:**
|
||||
- If user confirms:
|
||||
- Show optimistic update (status to "stopping")
|
||||
- Submit to `POST /api/chat/playground/{workflowId}/stop`
|
||||
- On success: Stop polling, update UI with stopped status
|
||||
- On error: Revert optimistic update, show error
|
||||
|
||||
### Viewing Files in Messages
|
||||
|
||||
**Show File:**
|
||||
- User clicks "Show" button on file attachment in message
|
||||
- Frontend fetches file data via `GET /api/files/{fileId}/download` or preview endpoint
|
||||
- File preview/viewer opens (modal, sidebar, or inline)
|
||||
- Preview supports different file types:
|
||||
- Images → Display image viewer
|
||||
- PDFs → Display PDF viewer
|
||||
- Text files → Display text content
|
||||
- Other types → Show file info or download prompt
|
||||
- User can close preview to return to message view
|
||||
|
||||
**Download File:**
|
||||
- User clicks "Download" button on file attachment in message
|
||||
- Frontend triggers download via `GET /api/files/{fileId}/download`
|
||||
- Browser automatically downloads file with proper filename
|
||||
- File name properly encoded for Unicode characters
|
||||
- Handle 403 (permission denied) and 404 (file not found) errors
|
||||
|
||||
### Deleting Context from a Workflow
|
||||
|
||||
**Delete Message:**
|
||||
- User clicks "Delete" button on message
|
||||
- Confirmation dialog: "Delete this message? This will remove it from the workflow context."
|
||||
- If user confirms:
|
||||
- Show optimistic update (remove message from UI)
|
||||
- Submit to `DELETE /api/workflows/{workflowId}/messages/{messageId}`
|
||||
- On success: Keep optimistic update, refresh workflow data
|
||||
- On error: Revert optimistic update, show error
|
||||
|
||||
**Delete File from Message:**
|
||||
- User clicks "Delete" button on file attachment in message
|
||||
- Confirmation dialog: "Delete this file? This will remove it from the message context."
|
||||
- If user confirms:
|
||||
- Show optimistic update (remove file from UI)
|
||||
- Submit to `DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}`
|
||||
- On success: Keep optimistic update, refresh message data
|
||||
- On error: Revert optimistic update, show error
|
||||
|
||||
---
|
||||
|
||||
## Backend Routes and API Integration
|
||||
|
||||
### Complete Route Reference
|
||||
|
||||
All backend routes used by chat playground pages:
|
||||
|
||||
| Route | Method | Purpose | When Used | Access Control |
|
||||
|-------|--------|---------|-----------|----------------|
|
||||
| `/api/chat/playground/start` | POST | Start new workflow or continue existing | User clicks "Start Workflow" or "Continue" | Current user only |
|
||||
| `/api/chat/playground/{workflowId}/stop` | POST | Stop running workflow | User clicks "Stop" button | Current user only |
|
||||
| `/api/chat/playground/{workflowId}/chatData` | GET | Get unified chat data for polling | Live tracking (polling every 2 seconds) | Current user only |
|
||||
| `/api/workflows/` | GET | Get all workflows for current user | Load workflow dropdown, initial page load | Current user only |
|
||||
| `/api/workflows/{workflowId}` | GET | Get workflow by ID | User selects workflow from dropdown | Current user only |
|
||||
| `/api/workflows/{workflowId}/messages/{messageId}` | DELETE | Delete message from workflow | User confirms message deletion | Current user only |
|
||||
| `/api/workflows/{workflowId}/messages/{messageId}/files/{fileId}` | DELETE | Delete file from message | User confirms file deletion | Current user only |
|
||||
| `/api/prompts` | GET | Get all prompts for current user | Load prompt dropdown | Current user only |
|
||||
| `/api/prompts/{promptId}` | GET | Get prompt by ID | User selects prompt from dropdown | Current user only |
|
||||
| `/api/files/upload` | POST | Upload new file | User drags/drops or clicks upload | Current user only |
|
||||
| `/api/files/list` | GET | Get all files for current user | User clicks "Browse Files" | Current user only |
|
||||
| `/api/files/{fileId}/download` | GET | Download file | User clicks "Download" button on file | Current user only |
|
||||
| `/api/neutralization/config` | GET | Get neutralization config | User toggles neutralization | Current user only |
|
||||
| `/api/attributes/ChatWorkflow` | GET | Get field definitions | Page load (once per page) | All authenticated users |
|
||||
| `/api/attributes/UserInputRequest` | GET | Get field definitions | Page load (once per page) | All authenticated users |
|
||||
|
||||
### API Request Patterns
|
||||
|
||||
**Start Workflow Request:**
|
||||
```
|
||||
POST /api/chat/playground/start?workflowMode=Dynamic
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"prompt": "User prompt text here...",
|
||||
"listFileId": ["fileId1", "fileId2"],
|
||||
"userLanguage": "en"
|
||||
}
|
||||
```
|
||||
- `workflowMode` query parameter is required: "Actionplan", "Dynamic", or "Template"
|
||||
- `workflowId` query parameter is optional (for continuing existing workflow)
|
||||
- `listFileId` array contains file IDs to attach (optional)
|
||||
- `prompt` field contains user's prompt text (required)
|
||||
- `userLanguage` should match user's preferred language
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Continue Workflow Request:**
|
||||
```
|
||||
POST /api/chat/playground/start?workflowId=workflow-id&workflowMode=Dynamic
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"prompt": "Additional prompt text...",
|
||||
"listFileId": ["fileId1"],
|
||||
"userLanguage": "en"
|
||||
}
|
||||
```
|
||||
- `workflowId` query parameter is required (ID of workflow to continue)
|
||||
- `workflowMode` query parameter is required (from selected workflow)
|
||||
- Same body structure as start workflow
|
||||
- Handle 403, 404 (workflow not found), and 400 errors
|
||||
|
||||
**Stop Workflow Request:**
|
||||
```
|
||||
POST /api/chat/playground/{workflowId}/stop
|
||||
```
|
||||
- No body required
|
||||
- Returns updated ChatWorkflow with status "stopped"
|
||||
- Handle 403 (permission denied) and 404 (workflow not found)
|
||||
|
||||
**Get Chat Data Request (Polling):**
|
||||
```
|
||||
GET /api/chat/playground/{workflowId}/chatData?afterTimestamp=1234567890.123
|
||||
```
|
||||
- `afterTimestamp` query parameter is optional (Unix timestamp for selective data transfer)
|
||||
- Returns unified chat data: `{messages: [], logs: [], stats: []}`
|
||||
- Data is chronologically ordered by `_createdAt` timestamp
|
||||
- Handle 404 (workflow not found) - stop polling
|
||||
|
||||
**Get Workflows Request:**
|
||||
```
|
||||
GET /api/workflows/
|
||||
```
|
||||
- Returns paginated workflows list
|
||||
- Can include pagination parameter: `?pagination={"page":1,"pageSize":10,"sort":[]}`
|
||||
- Used to populate workflow dropdown
|
||||
- Returns only current user's workflows
|
||||
|
||||
**Get Workflow Request:**
|
||||
```
|
||||
GET /api/workflows/{workflowId}
|
||||
```
|
||||
- Returns full workflow data including messages, logs, stats
|
||||
- Used when user selects workflow from dropdown
|
||||
- Handle 404 (workflow not found)
|
||||
|
||||
**Upload File Request:**
|
||||
```
|
||||
POST /api/files/upload
|
||||
Content-Type: multipart/form-data
|
||||
Body: {
|
||||
file: <file data>,
|
||||
workflowId: "optional-workflow-id" (optional)
|
||||
}
|
||||
```
|
||||
- File sent as multipart/form-data
|
||||
- `workflowId` is optional form field
|
||||
- Handle 403 (permission denied), 413 (file too large), and 400 (validation errors)
|
||||
- Response includes duplicate information
|
||||
|
||||
**Get Prompts Request:**
|
||||
```
|
||||
GET /api/prompts
|
||||
```
|
||||
- Returns paginated prompts list
|
||||
- Used to populate prompt dropdown
|
||||
- Returns only current user's prompts
|
||||
|
||||
**Get Prompt Request:**
|
||||
```
|
||||
GET /api/prompts/{promptId}
|
||||
```
|
||||
- Returns prompt details (name, content)
|
||||
- Used when user selects prompt from dropdown
|
||||
- Handle 404 (prompt not found)
|
||||
|
||||
**Get Neutralization Config Request:**
|
||||
```
|
||||
GET /api/neutralization/config
|
||||
```
|
||||
- Returns neutralization configuration (enabled status, settings)
|
||||
- Used when user toggles neutralization
|
||||
- Returns default config if none exists
|
||||
|
||||
**Delete Message Request:**
|
||||
```
|
||||
DELETE /api/workflows/{workflowId}/messages/{messageId}
|
||||
```
|
||||
- Deletes message from workflow
|
||||
- Updates workflow's messageIds list
|
||||
- Handle 403 (permission denied) and 404 (message not found)
|
||||
|
||||
**Download File Request:**
|
||||
```
|
||||
GET /api/files/{fileId}/download
|
||||
```
|
||||
- Returns file content with Content-Disposition header
|
||||
- Browser automatically triggers download
|
||||
- Filename properly encoded for Unicode characters
|
||||
- Handle 403 (permission denied) and 404 (file not found)
|
||||
|
||||
**Delete File Request:**
|
||||
```
|
||||
DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
```
|
||||
- Deletes file reference from message
|
||||
- Handle 403 (permission denied) and 404 (file not found)
|
||||
|
||||
### Response Handling
|
||||
|
||||
**Start/Continue Workflow Response:**
|
||||
```json
|
||||
{
|
||||
"id": "workflow-id",
|
||||
"status": "running",
|
||||
"workflowMode": "Dynamic",
|
||||
"messages": [...],
|
||||
"logs": [...],
|
||||
"stats": [...],
|
||||
...
|
||||
}
|
||||
```
|
||||
- Returns ChatWorkflow object with status "running"
|
||||
- Includes all workflow data
|
||||
|
||||
**Chat Data Response (Polling):**
|
||||
```json
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"id": "message-id",
|
||||
"role": "user",
|
||||
"content": "...",
|
||||
"_createdAt": 1234567890.123,
|
||||
...
|
||||
}
|
||||
],
|
||||
"logs": [
|
||||
{
|
||||
"id": "log-id",
|
||||
"level": "info",
|
||||
"message": "...",
|
||||
"_createdAt": 1234567890.124,
|
||||
...
|
||||
}
|
||||
],
|
||||
"stats": [
|
||||
{
|
||||
"id": "stat-id",
|
||||
"metric": "...",
|
||||
"value": 123,
|
||||
"_createdAt": 1234567890.125,
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
- All data types in chronological order by `_createdAt`
|
||||
- Empty arrays if no new data
|
||||
|
||||
**Error Responses:**
|
||||
- 403 Forbidden → Show permission error message
|
||||
- 404 Not Found → Show "not found" error message
|
||||
- 400 Bad Request → Display validation errors from response
|
||||
- 413 Request Entity Too Large → Show file size error
|
||||
- 500 Internal Server Error → Show generic error message
|
||||
|
||||
---
|
||||
|
||||
## Field and Attribute Reference
|
||||
|
||||
### Complete Field List
|
||||
|
||||
The following is a comprehensive list of all parameters, attributes, and fields that will be displayed for workflows and user input. All of these are provided by the backend through the `/api/attributes/ChatWorkflow` and `/api/attributes/UserInputRequest` endpoints and should never be hardcoded in the frontend.
|
||||
|
||||
#### ChatWorkflow Fields
|
||||
|
||||
**Identification Fields:**
|
||||
- `id` - Unique workflow identifier (text, readonly, not required, visible)
|
||||
- `mandateId` - ID of the mandate this workflow belongs to (text, readonly, not required, visible)
|
||||
- `name` - Workflow name (text, editable, not required, visible)
|
||||
|
||||
**Workflow Properties:**
|
||||
- `status` - Workflow status (select, readonly, not required, visible)
|
||||
- Options: "running", "completed", "stopped", "failed"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `workflowMode` - Execution mode (select, readonly, not required, visible)
|
||||
- Options: "Actionplan", "Dynamic", "Template"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `currentRound` - Current user interaction round (number, readonly, not required, visible)
|
||||
- `currentTask` - Current task in task plan (number, readonly, not required, visible)
|
||||
- `currentAction` - Current action within task (number, readonly, not required, visible)
|
||||
- `totalTasks` - Number of tasks in task plan (number, readonly, not required, visible)
|
||||
- `totalActions` - Number of actions in current task (number, readonly, not required, visible)
|
||||
- `maxSteps` - Maximum steps for Dynamic mode (number, readonly, not required, visible)
|
||||
|
||||
**Timestamp Fields:**
|
||||
- `startedAt` - When workflow was created (timestamp, readonly, not required, visible)
|
||||
- `lastActivity` - Last activity timestamp (timestamp, readonly, not required, visible)
|
||||
|
||||
**Related Data:**
|
||||
- `messages` - List of ChatMessage objects (array, readonly, not required, visible)
|
||||
- `logs` - List of ChatLog objects (array, readonly, not required, visible)
|
||||
- `stats` - List of ChatStat objects (array, readonly, not required, visible)
|
||||
- `tasks` - List of task objects (array, readonly, not required, visible)
|
||||
|
||||
#### UserInputRequest Fields
|
||||
|
||||
**Input Fields:**
|
||||
- `prompt` - User prompt text (textarea, editable, required, visible)
|
||||
- `listFileId` - Array of file IDs to attach (array, editable, not required, visible)
|
||||
- `userLanguage` - User's preferred language (select, editable, not required, visible)
|
||||
- Options: "en", "fr", etc.
|
||||
- Each option has localized labels
|
||||
|
||||
#### Prompt Fields
|
||||
|
||||
**Prompt Properties:**
|
||||
- `id` - Unique prompt identifier (text, readonly, not required, visible)
|
||||
- `mandateId` - ID of the mandate this prompt belongs to (text, readonly, not required, visible)
|
||||
- `name` - Prompt name (text, readonly, not required, visible)
|
||||
- `content` - Prompt content (textarea, readonly, not required, visible)
|
||||
|
||||
### Attribute Definition Structure
|
||||
|
||||
Each field returned from `/api/attributes/ChatWorkflow` and `/api/attributes/UserInputRequest` contains:
|
||||
|
||||
- `name` - Field name (e.g., "status", "workflowMode", "prompt", "listFileId")
|
||||
- `type` - Field data type (e.g., "text", "select", "textarea", "array", "timestamp", "number")
|
||||
- `label` - Localized field label (object with language keys: {"en": "English Label", "fr": "French Label"})
|
||||
- `description` - Field description text
|
||||
- `required` - Boolean indicating if field is required
|
||||
- `readonly` - Boolean indicating if field is read-only
|
||||
- `editable` - Boolean indicating if field can be edited (inverse of readonly)
|
||||
- `visible` - Boolean indicating if field should be displayed in UI
|
||||
- `options` - Array of options for select fields (each option has `value` and localized `label`)
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Rendering Guidelines
|
||||
|
||||
The frontend must render all UI components dynamically based on backend metadata. No field definitions, labels, validation rules, or UI structure should be hardcoded.
|
||||
|
||||
### Form Field Generation
|
||||
|
||||
When rendering input forms:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/UserInputRequest` and `/api/attributes/ChatWorkflow`
|
||||
2. Filter attributes where `visible: true` AND `editable: true` to determine which fields to show
|
||||
3. For each editable field, generate appropriate form input based on `type`:
|
||||
- `text` fields → Text input
|
||||
- `textarea` fields → Multi-line text area
|
||||
- `select` fields → Dropdown/select input with options from `options` array (use localized labels)
|
||||
- `array` fields → Array input with add/remove controls
|
||||
- `number` fields → Number input
|
||||
4. Use `label` property for field labels (localized)
|
||||
5. Use `required` property to show required indicators (asterisk, etc.)
|
||||
6. Use `description` property to show help text or tooltips
|
||||
7. Validate form before submission:
|
||||
- Check all `required: true` fields have values
|
||||
- Validate types (e.g., array fields must be arrays)
|
||||
- Validate select fields (value must be in options array)
|
||||
8. On submit, build UserInputRequest object with all form data
|
||||
|
||||
### Workflow Mode Selector Generation
|
||||
|
||||
When rendering workflow mode selector:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/ChatWorkflow`
|
||||
2. Find `workflowMode` field
|
||||
3. Extract `options` array from field definition
|
||||
4. Generate radio buttons or tabs for each option
|
||||
5. Use localized labels from options: `option.label[userLanguage]`
|
||||
6. Show description/help text when option is selected
|
||||
7. Mark one option as required (user must select before starting)
|
||||
|
||||
### Dropdown Generation
|
||||
|
||||
**Workflow Dropdown:**
|
||||
1. Fetch workflows via `GET /api/workflows/`
|
||||
2. Display workflows in dropdown with name, status, last activity
|
||||
3. Add "New Workflow" as default/first option
|
||||
4. When workflow selected, load full workflow data
|
||||
5. Display previous messages when workflow selected
|
||||
|
||||
**Prompt Dropdown:**
|
||||
1. Fetch prompts via `GET /api/prompts`
|
||||
2. Display prompts in dropdown with name
|
||||
3. When prompt selected, fetch prompt details and pre-fill text field
|
||||
4. Allow user to edit pre-filled text
|
||||
|
||||
### File Attachment UI Generation
|
||||
|
||||
**Drop Zone:**
|
||||
1. Create drop zone area with visual boundaries
|
||||
2. Handle drag events (dragenter, dragover, dragleave, drop)
|
||||
3. Show visual feedback when files dragged over (highlight, border, overlay)
|
||||
4. Process dropped files and upload automatically
|
||||
5. Show file preview immediately after drop
|
||||
|
||||
**File Upload Button:**
|
||||
1. Create file input element (hidden)
|
||||
2. Trigger file picker on button click
|
||||
3. Handle multiple file selection
|
||||
4. Upload files and show preview
|
||||
|
||||
**File Browser:**
|
||||
1. Fetch files via `GET /api/files/list`
|
||||
2. Display files in browser/selector with pagination
|
||||
3. Allow multiple file selection
|
||||
4. Add selected files to attachment list
|
||||
|
||||
**File List Display:**
|
||||
1. Show attached files with name, size, type
|
||||
2. Display remove button for each file
|
||||
3. Update list when files added or removed
|
||||
|
||||
### Message Display Generation
|
||||
|
||||
When rendering messages in workflow view:
|
||||
|
||||
1. Fetch messages from workflow data or chatData endpoint
|
||||
2. Display messages chronologically
|
||||
3. Format based on message role:
|
||||
- User messages → Right-aligned or distinct styling
|
||||
- Assistant messages → Left-aligned or distinct styling
|
||||
4. Display file attachments as part of each message:
|
||||
- Show file name, size, and type/icon for each attachment
|
||||
- Display file attachments in organized list or grid within message
|
||||
- For each file attachment, render action buttons:
|
||||
- **"Show" button** - Opens file preview/viewer
|
||||
- **"Download" button** - Triggers file download
|
||||
- **"Delete" button** - Removes file from message (with confirmation)
|
||||
- File preview can be displayed inline, in modal, or sidebar
|
||||
- Support different file type previews (images, PDFs, text, etc.)
|
||||
5. Show timestamps (relative or absolute)
|
||||
6. Add delete button for each message (if user has permission)
|
||||
|
||||
### Log Display Generation
|
||||
|
||||
When rendering logs in log panel:
|
||||
|
||||
1. Fetch logs from workflow data or chatData endpoint
|
||||
2. Display logs chronologically
|
||||
3. Format based on log level:
|
||||
- Info → Default styling
|
||||
- Warning → Yellow/orange styling
|
||||
- Error → Red styling
|
||||
4. Show timestamps for each log entry
|
||||
5. Support filtering by log level (optional)
|
||||
|
||||
### Statistics Display Generation
|
||||
|
||||
When rendering statistics in stats panel:
|
||||
|
||||
1. Fetch stats from workflow data or chatData endpoint
|
||||
2. Display statistics in organized format
|
||||
3. Format based on metric type:
|
||||
- Numbers → Display with appropriate units
|
||||
- Percentages → Display as percentage
|
||||
- Timestamps → Format as duration or absolute time
|
||||
4. Update statistics in real-time during polling
|
||||
|
||||
### Display Formatting
|
||||
|
||||
When displaying field values:
|
||||
|
||||
1. Use `type` property to determine formatting:
|
||||
- `text` → Display as-is (may need HTML escaping)
|
||||
- `textarea` → Preserve line breaks
|
||||
- `select` → Look up value in `options` array and display localized label
|
||||
- `timestamp` → Format as relative time or absolute date/time
|
||||
- `number` → Format with appropriate decimal places
|
||||
- `array` → Display as list or comma-separated
|
||||
2. Handle `null` or `undefined` values gracefully (show "-" or "Not set")
|
||||
3. Use `readonly` property to determine if field should show edit indicators
|
||||
4. Special formatting for workflow fields:
|
||||
- Status → Color-code badges (Running = blue, Completed = green, Stopped = gray, Failed = red)
|
||||
- Workflow Mode → Display as badge with icon
|
||||
- Timestamps → Show relative time in list view, absolute date in detail view
|
||||
|
||||
### Localization
|
||||
|
||||
All labels and options support multiple languages:
|
||||
|
||||
1. Use user's preferred language (from user settings or browser locale)
|
||||
2. Access localized labels from `label` object: `label[userLanguage]` or `label.en` as fallback
|
||||
3. For select options, use `option.label[userLanguage]` or `option.label.en` as fallback
|
||||
4. If label for current language is missing, fall back to English
|
||||
|
||||
### Polling Implementation
|
||||
|
||||
**Polling Setup:**
|
||||
1. Start polling when workflow view is active
|
||||
2. Use `setInterval` or similar mechanism (2 second interval)
|
||||
3. Store last timestamp to use in `afterTimestamp` parameter
|
||||
4. Stop polling when workflow completes or user navigates away
|
||||
|
||||
**Polling Logic:**
|
||||
1. Call `GET /api/chat/playground/{workflowId}/chatData?afterTimestamp={lastTimestamp}`
|
||||
2. Compare returned data with existing data
|
||||
3. Append new messages, logs, stats to UI
|
||||
4. Update last timestamp
|
||||
5. Check workflow status - stop polling if completed/stopped/failed
|
||||
6. Handle errors gracefully (404 stops polling)
|
||||
|
||||
**Performance Considerations:**
|
||||
1. Debounce rapid updates
|
||||
2. Batch UI updates
|
||||
3. Use virtual scrolling for long message lists
|
||||
4. Limit polling frequency
|
||||
5. Stop polling when tab is not visible (Page Visibility API)
|
||||
|
||||
### Key Principles
|
||||
|
||||
- Never hardcode field names, labels, types, or validation rules
|
||||
- Always fetch attribute definitions from backend before rendering UI
|
||||
- Use attribute metadata to determine what to display and how to display it
|
||||
- Support all field types dynamically - if backend adds new types, frontend should handle them
|
||||
- Respect `visible`, `editable`, `readonly`, and `required` flags from backend
|
||||
- Use localized labels from backend metadata
|
||||
- Generate forms, dropdowns, and displays entirely from attribute definitions
|
||||
- When backend adds new fields, frontend should automatically display them without code changes
|
||||
- Handle all error cases gracefully (403, 404, 400, 413, 500)
|
||||
- Provide user feedback for all actions (loading states, success messages, error messages)
|
||||
- **All workflows are user-scoped** (backend enforces this)
|
||||
- **Polling should be efficient** (use selective data transfer with afterTimestamp)
|
||||
- **Optimistic updates** for better UX (show changes immediately, revert on error)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This document provides complete frontend requirements for the chat playground page and workflow management components. All requirements follow the principle of **backend-driven UI generation**—no hardcoding of field definitions, labels, or validation rules. The frontend should dynamically generate all UI components from backend metadata provided through the `/api/attributes/ChatWorkflow` and `/api/attributes/UserInputRequest` endpoints.
|
||||
|
||||
**Key Architecture Pattern:** The chat playground interface is a single page (`/chat-playground`) with different views managed through component state. All interactions happen within the same page component without separate routes.
|
||||
|
||||
**Security Note:** Workflows are user-scoped. Users can only see and manage their own workflows. The backend enforces this security, and the frontend should never attempt to access other users' workflows.
|
||||
|
||||
**Real-Time Updates:** The workflow view uses polling-based live tracking to show real-time updates. Polling should be efficient using selective data transfer with timestamps.
|
||||
|
||||
For generic patterns that apply across all entity types (not just workflows), see the [Dynamic Forms and Pagination documentation](./dynamic-forms-and-pagination.md).
|
||||
|
||||
|
|
@ -1,542 +0,0 @@
|
|||
# Chat Playground and Workflow Live Tracking Frontend Documentation
|
||||
|
||||
This document describes customer journeys for managing chat playground workflows and live tracking workflow execution through the frontend, focusing on how users interact with workflow management and how the backend routes support these experiences. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Customer Journey 1: Starting a New Workflow](#customer-journey-1-starting-a-new-workflow)
|
||||
3. [Customer Journey 2: Continuing an Existing Workflow](#customer-journey-2-continuing-an-existing-workflow)
|
||||
4. [Customer Journey 3: Live Tracking Workflow Progress](#customer-journey-3-live-tracking-workflow-progress)
|
||||
5. [Customer Journey 4: Stopping a Running Workflow](#customer-journey-4-stopping-a-running-workflow)
|
||||
6. [Customer Journey 5: Deleting Context from a Workflow](#customer-journey-5-deleting-context-from-a-workflow)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The chat playground routes (`/api/chat/playground`) and workflow routes (`/api/workflows`) enable users to create, manage, and track AI-powered workflows that process user requests through task planning and action execution. These routes support **workflow lifecycle management** including creation, live tracking, modification, and deletion.
|
||||
|
||||
**Key Principles:**
|
||||
- **User-Centric**: Documentation organized around what users want to accomplish
|
||||
- **Backend-Driven**: All forms, tables, and UI components generated from backend metadata
|
||||
- **No Hardcoding**: Field definitions, labels, validation rules, and options come from the backend
|
||||
- **Permission-Aware**: Backend enforces permissions; frontend handles gracefully
|
||||
- **User-Scoped**: Users can only see and manage their own workflows
|
||||
- **Real-Time Updates**: Polling-based live tracking for workflow progress
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 1: Starting a New Workflow
|
||||
|
||||
### User Goal
|
||||
"I want to start a new AI workflow to process my request."
|
||||
|
||||
### User Story
|
||||
As a user, I want to start a new workflow by optionally selecting a saved prompt template, providing or editing my input prompt, choosing a workflow mode (Actionplan for traditional task planning, Dynamic for iterative processing, or Template for template-based processing), optionally enabling data neutralization for privacy compliance, and optionally attaching files, so the system can begin processing my request in the appropriate mode with sensitive data protected if needed.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Navigate to chat playground
|
||||
Frontend->>Backend: GET /api/attributes/ChatWorkflow
|
||||
Backend-->>Frontend: Attribute definitions (fields, labels, types)
|
||||
Frontend->>Backend: GET /api/attributes/UserInputRequest
|
||||
Backend-->>Frontend: Input request field definitions
|
||||
Frontend->>Frontend: Generate input form from attributes
|
||||
Frontend->>Backend: GET /api/prompts
|
||||
Backend-->>Frontend: Paginated prompts list
|
||||
Frontend-->>User: Display chat input form with mode selector<br/>(Actionplan, Dynamic, Template)<br/>+ prompt dropdown selector
|
||||
|
||||
alt User optionally selects a prompt
|
||||
User->>Frontend: Click prompt dropdown
|
||||
Frontend->>Frontend: Display available prompts list<br/>(from cached prompts data)
|
||||
User->>Frontend: Select prompt from dropdown
|
||||
Frontend->>Backend: GET /api/prompts/promptId
|
||||
Backend-->>Frontend: Selected prompt details<br/>(name, content)
|
||||
Frontend->>Frontend: Pre-fill prompt text field with prompt content
|
||||
Frontend->>Frontend: Show selected prompt name
|
||||
Frontend-->>User: Display prompt text pre-filled<br/>(user can still edit)
|
||||
end
|
||||
|
||||
User->>Frontend: Enter or edit prompt text
|
||||
|
||||
alt User selects workflow mode
|
||||
alt User selects "Actionplan" mode
|
||||
User->>Frontend: Click "Actionplan" mode option
|
||||
Frontend->>Frontend: Show mode description/help text<br/>(traditional task planning)
|
||||
Frontend->>Frontend: Set workflowMode to "Actionplan"
|
||||
Frontend-->>User: Display selected mode indicator
|
||||
else User selects "Dynamic" mode
|
||||
User->>Frontend: Click "Dynamic" mode option
|
||||
Frontend->>Frontend: Show mode description/help text<br/>(iterative dynamic-style processing)
|
||||
Frontend->>Frontend: Set workflowMode to "Dynamic"
|
||||
Frontend-->>User: Display selected mode indicator
|
||||
else User selects "Template" mode
|
||||
User->>Frontend: Click "Template" mode option
|
||||
Frontend->>Frontend: Show mode description/help text<br/>(template-based processing)
|
||||
Frontend->>Frontend: Set workflowMode to "Template"
|
||||
Frontend-->>User: Display selected mode indicator
|
||||
end
|
||||
end
|
||||
|
||||
alt User optionally enables neutralization
|
||||
User->>Frontend: Click "Enable Neutralization" toggle
|
||||
Frontend->>Backend: GET /api/neutralization/config
|
||||
Backend-->>Frontend: Neutralization config (enabled status)
|
||||
alt Config exists and enabled
|
||||
Frontend->>Frontend: Mark neutralization as enabled for workflow
|
||||
Frontend-->>User: Show neutralization enabled indicator<br/>(uses existing config)
|
||||
else Config disabled or not found
|
||||
Frontend->>Frontend: Mark neutralization as disabled
|
||||
Frontend-->>User: Show neutralization disabled<br/>(user can configure on settings page)
|
||||
end
|
||||
end
|
||||
|
||||
alt User optionally attaches files
|
||||
alt User drags and drops files
|
||||
User->>Frontend: Drag file(s) over drop zone
|
||||
Frontend->>Frontend: Show visual feedback<br/>(highlight drop zone, border, overlay)
|
||||
Frontend-->>User: Display drop zone active state
|
||||
User->>Frontend: Drop file(s) onto drop zone
|
||||
Frontend->>Frontend: Process dropped files
|
||||
Frontend->>Frontend: Show file preview with name and size
|
||||
Frontend->>Frontend: Optimistic update: Add file to attachment list
|
||||
Frontend->>Backend: POST /api/files/upload<br/>+ multipart/form-data (file)
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show permission error
|
||||
else File too large (413)
|
||||
Backend-->>Frontend: 413 Request Entity Too Large
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show file size error
|
||||
else Success (201)
|
||||
Backend-->>Frontend: FileItem object + duplicate info
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Store fileId for later use
|
||||
Frontend-->>User: Show file attached successfully
|
||||
end
|
||||
else User uploads new file via button
|
||||
User->>Frontend: Click "Attach File" or "Upload File" button
|
||||
Frontend->>Frontend: Open file picker dialog
|
||||
User->>Frontend: Select file(s) from device
|
||||
Frontend->>Frontend: Show file preview with name and size
|
||||
Frontend->>Frontend: Optimistic update: Add file to attachment list
|
||||
Frontend->>Backend: POST /api/files/upload<br/>+ multipart/form-data (file)
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show permission error
|
||||
else File too large (413)
|
||||
Backend-->>Frontend: 413 Request Entity Too Large
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show file size error
|
||||
else Success (201)
|
||||
Backend-->>Frontend: FileItem object + duplicate info
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Store fileId for later use
|
||||
Frontend-->>User: Show file attached successfully
|
||||
end
|
||||
else User selects existing file
|
||||
User->>Frontend: Click "Browse Files" or "Select File" button
|
||||
Frontend->>Backend: GET /api/files/list
|
||||
Backend-->>Frontend: Paginated files list
|
||||
Frontend->>Frontend: Display file browser/selector
|
||||
Frontend-->>User: Show available files
|
||||
User->>Frontend: Select file(s) from list
|
||||
Frontend->>Frontend: Add fileId(s) to attachment list
|
||||
Frontend-->>User: Show selected files in attachment list
|
||||
end
|
||||
end
|
||||
|
||||
User->>Frontend: Click "Start Workflow" button
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types, workflowMode)
|
||||
alt Validation fails (missing workflowMode or prompt)
|
||||
Frontend-->>User: Show validation errors<br/>(e.g., "Workflow mode is required")
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Build UserInputRequest with listFileId array (if files attached)
|
||||
alt Neutralization enabled
|
||||
Frontend->>Frontend: Neutralization will be applied automatically<br/>by backend during workflow processing
|
||||
end
|
||||
Frontend->>Frontend: Optimistic update: Show loading state, create workflow placeholder
|
||||
Frontend->>Backend: POST /api/chat/playground/start<br/>+ workflowMode query param (Actionplan/Dynamic/Template)<br/>+ UserInputRequest body<br/>(includes listFileId if files attached)
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Created ChatWorkflow (status: "running")<br/>with files attached (if any)<br/>(neutralized if neutralization enabled)
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Navigate to workflow view
|
||||
Frontend->>Frontend: Start polling for updates
|
||||
alt Neutralization enabled
|
||||
Frontend->>Frontend: Show neutralization indicator in workflow view
|
||||
Frontend->>Frontend: Option to resolve text back to original when viewing
|
||||
end
|
||||
Frontend-->>User: Display workflow with live tracking<br/>(files visible if attached, neutralized if enabled)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 2: Continuing an Existing Workflow
|
||||
|
||||
### User Goal
|
||||
"I want to continue a workflow I started earlier by selecting it from a list and adding more input."
|
||||
|
||||
### User Story
|
||||
As a user, I want to select a previous workflow from a dropdown on the chat interface page and continue it by providing additional input, so the system can process my new request in the context of the previous conversation.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Navigate to chat playground
|
||||
Frontend->>Backend: GET /api/attributes/ChatWorkflow
|
||||
Backend-->>Frontend: Attribute definitions (fields, labels, types)
|
||||
Frontend->>Backend: GET /api/attributes/UserInputRequest
|
||||
Backend-->>Frontend: Input request field definitions
|
||||
Frontend->>Backend: GET /api/workflows/
|
||||
Backend-->>Frontend: Paginated workflows list
|
||||
Frontend->>Backend: GET /api/prompts
|
||||
Backend-->>Frontend: Paginated prompts list
|
||||
Frontend->>Frontend: Generate input form from attributes
|
||||
Frontend-->>User: Display chat input form with<br/>workflow dropdown selector<br/>+ mode selector + prompt dropdown
|
||||
|
||||
alt User selects previous workflow from dropdown
|
||||
User->>Frontend: Click workflow dropdown
|
||||
Frontend->>Frontend: Display available workflows list<br/>(from cached workflows data)
|
||||
User->>Frontend: Select workflow from dropdown
|
||||
Frontend->>Backend: GET /api/workflows/workflowId
|
||||
Backend-->>Frontend: Selected workflow data<br/>(status, messages, workflowMode, etc.)
|
||||
Frontend->>Frontend: Load workflow history
|
||||
Frontend->>Frontend: Display previous messages in chat view
|
||||
Frontend->>Frontend: Set workflowMode from selected workflow
|
||||
Frontend->>Frontend: Show selected workflow name/ID
|
||||
Frontend-->>User: Display workflow context<br/>(previous messages visible,<br/>workflow mode pre-selected)
|
||||
end
|
||||
|
||||
alt User optionally selects a prompt
|
||||
User->>Frontend: Click prompt dropdown
|
||||
Frontend->>Frontend: Display available prompts list<br/>(from cached prompts data)
|
||||
User->>Frontend: Select prompt from dropdown
|
||||
Frontend->>Backend: GET /api/prompts/promptId
|
||||
Backend-->>Frontend: Selected prompt details<br/>(name, content)
|
||||
Frontend->>Frontend: Pre-fill prompt text field with prompt content
|
||||
Frontend->>Frontend: Show selected prompt name
|
||||
Frontend-->>User: Display prompt text pre-filled<br/>(user can still edit)
|
||||
end
|
||||
|
||||
User->>Frontend: Enter or edit prompt text
|
||||
|
||||
alt User optionally enables/disables neutralization
|
||||
User->>Frontend: Click "Enable Neutralization" toggle
|
||||
Frontend->>Backend: GET /api/neutralization/config
|
||||
Backend-->>Frontend: Neutralization config (enabled status)
|
||||
alt Config exists and enabled
|
||||
Frontend->>Frontend: Mark neutralization as enabled for workflow
|
||||
Frontend-->>User: Show neutralization enabled indicator<br/>(uses existing config)
|
||||
else Config disabled or not found
|
||||
Frontend->>Frontend: Mark neutralization as disabled
|
||||
Frontend-->>User: Show neutralization disabled<br/>(user can configure on settings page)
|
||||
end
|
||||
end
|
||||
|
||||
alt User optionally attaches files
|
||||
alt User drags and drops files
|
||||
User->>Frontend: Drag file(s) over drop zone
|
||||
Frontend->>Frontend: Show visual feedback<br/>(highlight drop zone, border, overlay)
|
||||
Frontend-->>User: Display drop zone active state
|
||||
User->>Frontend: Drop file(s) onto drop zone
|
||||
Frontend->>Frontend: Process dropped files
|
||||
Frontend->>Frontend: Show file preview with name and size
|
||||
Frontend->>Frontend: Optimistic update: Add file to attachment list
|
||||
Frontend->>Backend: POST /api/files/upload<br/>+ multipart/form-data (file)<br/>+ workflowId (optional)
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show permission error
|
||||
else File too large (413)
|
||||
Backend-->>Frontend: 413 Request Entity Too Large
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show file size error
|
||||
else Success (201)
|
||||
Backend-->>Frontend: FileItem object + duplicate info
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Store fileId for later use
|
||||
Frontend-->>User: Show file attached successfully
|
||||
end
|
||||
else User uploads new file via button
|
||||
User->>Frontend: Click "Attach File" or "Upload File" button
|
||||
Frontend->>Frontend: Open file picker dialog
|
||||
User->>Frontend: Select file(s) from device
|
||||
Frontend->>Frontend: Show file preview with name and size
|
||||
Frontend->>Frontend: Optimistic update: Add file to attachment list
|
||||
Frontend->>Backend: POST /api/files/upload<br/>+ multipart/form-data (file)<br/>+ workflowId (optional)
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show permission error
|
||||
else File too large (413)
|
||||
Backend-->>Frontend: 413 Request Entity Too Large
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from list
|
||||
Frontend-->>User: Show file size error
|
||||
else Success (201)
|
||||
Backend-->>Frontend: FileItem object + duplicate info
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Store fileId for later use
|
||||
Frontend-->>User: Show file attached successfully
|
||||
end
|
||||
else User selects existing file
|
||||
User->>Frontend: Click "Browse Files" or "Select File" button
|
||||
Frontend->>Backend: GET /api/files/list
|
||||
Backend-->>Frontend: Paginated files list
|
||||
Frontend->>Frontend: Display file browser/selector
|
||||
Frontend-->>User: Show available files
|
||||
User->>Frontend: Select file(s) from list
|
||||
Frontend->>Frontend: Add fileId(s) to attachment list
|
||||
Frontend-->>User: Show selected files in attachment list
|
||||
end
|
||||
end
|
||||
|
||||
User->>Frontend: Click "Continue" or "Send" button
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, workflowMode)
|
||||
alt Validation fails (missing workflowMode or prompt)
|
||||
Frontend-->>User: Show validation errors<br/>(e.g., "Workflow mode is required")
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Build UserInputRequest with listFileId array (if files attached)
|
||||
alt Neutralization enabled
|
||||
Frontend->>Frontend: Neutralization will be applied automatically<br/>by backend during workflow processing
|
||||
end
|
||||
Frontend->>Frontend: Optimistic update: Add user message to UI
|
||||
Frontend->>Backend: POST /api/chat/playground/start<br/>+ workflowId query param (from selected workflow)<br/>+ workflowMode query param (from selected workflow)<br/>+ UserInputRequest body<br/>(includes listFileId if files attached)
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error
|
||||
else Workflow not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show not found error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Updated ChatWorkflow (status: "running")<br/>with new files attached (if any)<br/>(neutralized if neutralization enabled)
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Navigate to workflow view or update current view
|
||||
Frontend->>Frontend: Start/continue polling for updates
|
||||
alt Neutralization enabled
|
||||
Frontend->>Frontend: Show neutralization indicator in workflow view
|
||||
Frontend->>Frontend: Option to resolve text back to original when viewing
|
||||
end
|
||||
Frontend-->>User: Display updated workflow with new input<br/>(files visible if attached, neutralized if enabled)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 3: Live Tracking Workflow Progress
|
||||
|
||||
### User Goal
|
||||
"I want to see real-time updates as my workflow processes my request."
|
||||
|
||||
### User Story
|
||||
As a user, I want to see live updates including new messages, execution logs, and statistics as my workflow processes, so I can monitor progress and understand what the system is doing.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: View active workflow
|
||||
Frontend->>Frontend: Start polling interval (e.g., every 2 seconds)
|
||||
|
||||
loop Polling cycle
|
||||
Frontend->>Backend: GET /api/chat/playground/workflowId/chatData<br/>+ afterTimestamp query param (optional)
|
||||
alt Workflow not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Stop polling
|
||||
Frontend-->>User: Show error, navigate away
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Unified chat data<br/>{messages: [], logs: [], stats: []}<br/>(chronologically ordered by _createdAt)
|
||||
Frontend->>Frontend: Compare with existing data
|
||||
alt New data available
|
||||
Frontend->>Frontend: Append new messages to chat view
|
||||
Frontend->>Frontend: Append new logs to log panel
|
||||
Frontend->>Frontend: Update statistics display
|
||||
Frontend->>Frontend: Update workflow status indicator
|
||||
Frontend->>Frontend: Scroll to latest content (if auto-scroll enabled)
|
||||
Frontend-->>User: Display new messages, logs, stats
|
||||
else No new data
|
||||
Frontend->>Frontend: Continue polling silently
|
||||
end
|
||||
|
||||
alt Workflow status is "completed" or "stopped" or "failed"
|
||||
Frontend->>Frontend: Stop polling
|
||||
Frontend->>Frontend: Show final status
|
||||
Frontend-->>User: Display completed workflow
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alt User manually stops polling
|
||||
User->>Frontend: Navigate away or close tab
|
||||
Frontend->>Frontend: Stop polling interval
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 4: Stopping a Running Workflow
|
||||
|
||||
### User Goal
|
||||
"I want to stop a workflow that is currently running."
|
||||
|
||||
### User Story
|
||||
As a user, I want to stop a running workflow by clicking a stop button, so I can halt processing if I no longer need the results or if something is taking too long.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: View running workflow
|
||||
Frontend->>Frontend: Display "Stop" button
|
||||
User->>Frontend: Click "Stop" button
|
||||
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Stop this workflow?"
|
||||
alt User cancels
|
||||
User->>Frontend: Click "Cancel"
|
||||
Frontend->>Frontend: Close dialog
|
||||
Frontend-->>User: Dialog closed, workflow continues
|
||||
else User confirms
|
||||
User->>Frontend: Click "Confirm"
|
||||
Frontend->>Frontend: Optimistic update: Update status to "stopping"
|
||||
Frontend->>Backend: POST /api/chat/playground/workflowId/stop
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error
|
||||
else Workflow not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Updated ChatWorkflow (status: "stopped")
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Stop polling for updates
|
||||
Frontend->>Backend: GET /api/workflows/workflowId (refetch)
|
||||
Backend-->>Frontend: Final workflow state
|
||||
Frontend->>Frontend: Update UI with stopped status
|
||||
Frontend-->>User: Show stopped workflow with final state
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 5: Deleting Context from a Workflow
|
||||
|
||||
### User Goal
|
||||
"I want to remove messages or files from a workflow to clean up the context."
|
||||
|
||||
### User Story
|
||||
As a user, I want to delete individual messages or files attached to messages in a workflow, so I can remove unwanted context and keep the workflow focused on what's relevant.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
alt User deletes message from workflow
|
||||
User->>Frontend: View workflow with messages
|
||||
User->>Frontend: Click "Delete" button on message
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Delete this message?<br/>This will remove it from the workflow context."
|
||||
alt User cancels
|
||||
User->>Frontend: Click "Cancel"
|
||||
Frontend->>Frontend: Close dialog
|
||||
Frontend-->>User: Dialog closed, message remains
|
||||
else User confirms
|
||||
User->>Frontend: Click "Confirm" or "Delete"
|
||||
Frontend->>Frontend: Optimistic update: Remove message from UI
|
||||
Frontend->>Backend: DELETE /api/workflows/workflowId/messages/messageId
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Restore message
|
||||
Frontend-->>User: Show permission error
|
||||
else Message not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update: Restore message
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Success response<br/>{workflowId, messageId, message: "Message deleted successfully"}
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: GET /api/workflows/workflowId (refetch)
|
||||
Backend-->>Frontend: Updated workflow (messageIds updated)
|
||||
Frontend->>Frontend: Update UI with fresh workflow data
|
||||
Frontend-->>User: Show message removed from workflow
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alt User deletes file from message
|
||||
User->>Frontend: View message with file attachments
|
||||
User->>Frontend: Click "Delete" button on file attachment
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Delete this file?<br/>This will remove it from the message context."
|
||||
alt User cancels
|
||||
User->>Frontend: Click "Cancel"
|
||||
Frontend->>Frontend: Close dialog
|
||||
Frontend-->>User: Dialog closed, file remains
|
||||
else User confirms
|
||||
User->>Frontend: Click "Confirm" or "Delete"
|
||||
Frontend->>Frontend: Optimistic update: Remove file from UI
|
||||
Frontend->>Backend: DELETE /api/workflows/workflowId/messages/messageId/files/fileId
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Restore file
|
||||
Frontend-->>User: Show permission error
|
||||
else File not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update: Restore file
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Success response<br/>{workflowId, messageId, fileId, message: "File reference deleted successfully"}
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: GET /api/workflows/workflowId/messages/messageId (refetch)
|
||||
Backend-->>Frontend: Updated message (files list updated)
|
||||
Frontend->>Frontend: Update UI with fresh message data
|
||||
Frontend-->>User: Show file removed from message
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
# Connection Routes Frontend Documentation
|
||||
|
||||
This document describes customer journeys for managing connections through the frontend, focusing on how users interact with connection management and how the backend routes support these experiences. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Customer Journey 1: Discovering and Browsing Connections](#customer-journey-1-discovering-and-browsing-connections)
|
||||
3. [Customer Journey 2: Creating New Connections](#customer-journey-2-creating-new-connections)
|
||||
4. [Customer Journey 3: Editing Connection Properties](#customer-journey-3-editing-connection-properties)
|
||||
5. [Customer Journey 4: Refreshing Connection Tokens](#customer-journey-4-refreshing-connection-tokens)
|
||||
6. [Customer Journey 5: Deleting Connections](#customer-journey-5-deleting-connections)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The connection routes (`/api/connections`) enable users to manage their OAuth connections (Google, Microsoft) within their account. These routes focus on **connection administration** including creation, editing, token refresh, and deletion.
|
||||
|
||||
**Key Principles:**
|
||||
- **User-Centric**: Documentation organized around what users want to accomplish
|
||||
- **Backend-Driven**: All forms, tables, and UI components generated from backend metadata
|
||||
- **No Hardcoding**: Field definitions, labels, validation rules, and options come from the backend
|
||||
- **Permission-Aware**: Backend enforces permissions; frontend handles gracefully
|
||||
- **User-Scoped**: Users can only see and manage their own connections
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 1: Discovering and Browsing Connections
|
||||
|
||||
### User Goal
|
||||
"I want to see all my connections and find the one I'm looking for."
|
||||
|
||||
### User Story
|
||||
As a user, I want to browse my connections, search for specific connections, filter by any field, sort them by different criteria, and quickly identify connections by their authority, status, and token status.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Navigate to /connections
|
||||
Frontend->>Backend: GET /api/attributes/UserConnection
|
||||
Backend-->>Frontend: Attribute definitions (fields, labels, types)
|
||||
Frontend->>Backend: GET /api/connections/
|
||||
Backend-->>Frontend: Connections list (with token status)
|
||||
Frontend->>Frontend: Generate table columns from attributes
|
||||
Frontend->>Frontend: Generate filter controls from attributes
|
||||
Frontend->>Frontend: Render connections table + search + filters
|
||||
Frontend->>Frontend: Render action buttons in table (Edit, Delete, Refresh Token)
|
||||
Frontend-->>User: Display connection list with search/filter UI + action buttons
|
||||
|
||||
alt User performs general search
|
||||
User->>Frontend: Type in search box (e.g., "google")
|
||||
Frontend->>Frontend: Update search query
|
||||
Frontend->>Backend: GET /api/connections/ (client-side filter)
|
||||
Frontend-->>User: Display matching connections
|
||||
end
|
||||
|
||||
alt User applies field filter
|
||||
User->>Frontend: Select filter field (e.g., "Authority")
|
||||
Frontend->>Frontend: Show filter options from attribute metadata
|
||||
User->>Frontend: Select filter value (e.g., "google")
|
||||
Frontend->>Frontend: Update filter parameters
|
||||
Frontend->>Backend: GET /api/connections/ (client-side filter)
|
||||
Frontend-->>User: Display filtered connections
|
||||
end
|
||||
|
||||
alt User combines search + filter + sort
|
||||
User->>Frontend: Apply search + filter + sort
|
||||
Frontend->>Frontend: Combine all parameters
|
||||
Frontend->>Backend: GET /api/connections/ (client-side filter/sort)
|
||||
Frontend-->>User: Display results
|
||||
end
|
||||
|
||||
User->>Frontend: Click column header (e.g., "Status")
|
||||
Frontend->>Frontend: Update sort parameters
|
||||
Frontend->>Backend: GET /api/connections/ (client-side sort)
|
||||
Backend-->>Frontend: Connections list
|
||||
Frontend-->>User: Display sorted connections
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 2: Creating New Connections
|
||||
|
||||
### User Goal
|
||||
"I want to create new OAuth connections for my account."
|
||||
|
||||
### User Story
|
||||
As a user, I want to create a new connection by clicking either "Connect Google" or "Connect Microsoft" button, which will immediately initiate the OAuth flow.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
alt User clicks "Connect Google" button
|
||||
User->>Frontend: Click "Connect Google" button
|
||||
Frontend->>Frontend: Optimistic update: Show loading state, add connection to UI immediately
|
||||
Frontend->>Backend: POST /api/connections/ + {type: "google"}
|
||||
else User clicks "Connect Microsoft" button
|
||||
User->>Frontend: Click "Connect Microsoft" button
|
||||
Frontend->>Frontend: Optimistic update: Show loading state, add connection to UI immediately
|
||||
Frontend->>Backend: POST /api/connections/ + {type: "msft"}
|
||||
end
|
||||
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Remove connection from UI
|
||||
Frontend-->>User: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update: Remove connection from UI
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Created connection object (status: PENDING)
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: POST /api/connections/connectionId/connect
|
||||
Backend-->>Frontend: {authUrl: "/api/google/login?state=..."} or {authUrl: "/api/msft/login?state=..."}
|
||||
Frontend->>Frontend: Navigate to authUrl (OAuth flow)
|
||||
Frontend-->>User: Redirect to OAuth provider
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 3: Editing Connection Properties
|
||||
|
||||
### User Goal
|
||||
"I want to change connection settings like its status or external email."
|
||||
|
||||
### User Story
|
||||
As a user, I want to edit a connection's properties through a popup/modal form that only shows fields I'm allowed to edit, with validation and clear error messages.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Edit" button in table row
|
||||
Frontend->>Backend: GET /api/connections/connectionId
|
||||
Backend-->>Frontend: Current connection data
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/UserConnection
|
||||
Backend-->>Frontend: Field definitions (editable fields)
|
||||
|
||||
Frontend->>Frontend: Filter editable fields
|
||||
Frontend->>Frontend: Generate form from attributes
|
||||
Frontend->>Frontend: Pre-populate form with connection data
|
||||
Frontend->>Frontend: Open popup/modal with edit form
|
||||
Frontend-->>User: Display edit form in popup/modal
|
||||
|
||||
User->>Frontend: Modify form fields
|
||||
User->>Frontend: Click "Save"
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types)
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors in modal
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Optimistic update: Apply changes to UI immediately
|
||||
Frontend->>Backend: PUT /api/connections/connectionId + form data
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error in modal
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show backend validation errors in modal
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Updated connection
|
||||
Frontend->>Frontend: Keep optimistic update (or refresh from response)
|
||||
Frontend->>Backend: GET /api/connections/ (refetch)
|
||||
Backend-->>Frontend: Updated connections list
|
||||
Frontend->>Frontend: Update table with fresh data
|
||||
Frontend->>Frontend: Close popup/modal
|
||||
Frontend-->>User: Show updated connection in table
|
||||
end
|
||||
end
|
||||
|
||||
alt User clicks Cancel
|
||||
User->>Frontend: Click "Cancel" button
|
||||
Frontend->>Frontend: Close popup/modal without saving
|
||||
Frontend-->>User: Modal closed, no changes
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 4: Refreshing Connection Tokens
|
||||
|
||||
### User Goal
|
||||
"I want to refresh the OAuth token for a connection that is expired or about to expire."
|
||||
|
||||
### User Story
|
||||
As a user, I want to refresh the OAuth token for a connection by clicking a refresh button, so the connection remains active.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Refresh Token" button in table
|
||||
Frontend->>Frontend: Show loading state
|
||||
Frontend->>Frontend: Optimistic update: Update token status to "refreshing"
|
||||
|
||||
alt Connection authority is Microsoft
|
||||
Frontend->>Backend: POST /api/msft/refresh + {connectionId: connectionId}
|
||||
else Connection authority is Google
|
||||
Frontend->>Backend: POST /api/google/refresh + {connectionId: connectionId}
|
||||
end
|
||||
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error
|
||||
else Connection not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: {message: "Token refreshed successfully", expires_at: timestamp, expires_in_seconds: number}
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: GET /api/connections/ (refetch)
|
||||
Backend-->>Frontend: Updated connections list with new token status
|
||||
Frontend->>Frontend: Update table with fresh data
|
||||
Frontend-->>User: Show success message, updated token status
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 5: Deleting Connections
|
||||
|
||||
### User Goal
|
||||
"I want to delete connections that are no longer needed."
|
||||
|
||||
### User Story
|
||||
As a user, I want to delete connections with a clear confirmation to prevent accidental deletion.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Delete" button
|
||||
Frontend->>Backend: GET /api/connections/
|
||||
Backend-->>Frontend: Connections list
|
||||
Frontend->>Frontend: Find connection by ID (for name/authority)
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Delete 'Google Connection'?"
|
||||
User->>Frontend: Cancel deletion
|
||||
Frontend-->>User: Dialog closed, no action
|
||||
|
||||
User->>Frontend: Click "Delete" button again
|
||||
Frontend->>Backend: GET /api/connections/
|
||||
Backend-->>Frontend: Connections list
|
||||
Frontend->>Frontend: Show confirmation dialog
|
||||
User->>Frontend: Confirm deletion
|
||||
Frontend->>Frontend: Optimistic update: Remove connection from UI immediately
|
||||
Frontend->>Backend: DELETE /api/connections/connectionId
|
||||
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Restore connection in UI
|
||||
Frontend-->>User: Show permission error
|
||||
else Not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update: Restore connection in UI
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend->>Frontend: Keep optimistic update (connection already removed)
|
||||
Frontend->>Frontend: Show success message
|
||||
Frontend-->>User: Display connection list (without deleted connection)
|
||||
end
|
||||
```
|
||||
|
||||
|
|
@ -1,491 +0,0 @@
|
|||
# Connection Page Requirements
|
||||
|
||||
This document contains the complete frontend requirements for all connection management pages and components. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Page Structure and Layout](#page-structure-and-layout)
|
||||
3. [User Interactions and Functionality](#user-interactions-and-functionality)
|
||||
4. [Backend Routes and API Integration](#backend-routes-and-api-integration)
|
||||
5. [Field and Attribute Reference](#field-and-attribute-reference)
|
||||
6. [Dynamic Rendering Guidelines](#dynamic-rendering-guidelines)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The connection management page enables users to manage their OAuth connections (Google, Microsoft) within their account. The frontend consists of a single page (`/connections`) with different views/states:
|
||||
|
||||
- **List View** - Browse, search, filter, and sort connections (all information displayed in table)
|
||||
- **Edit View** - Edit connection properties in popup/modal
|
||||
|
||||
All views use backend-driven UI generation, meaning field definitions, labels, validation rules, and UI structure come entirely from backend metadata through the `/api/attributes/UserConnection` endpoint. Views are managed through component state and routing within the same page, not as separate routes.
|
||||
|
||||
**Note:** Connections are user-scoped. Users can only see and manage their own connections. The backend enforces this security.
|
||||
|
||||
---
|
||||
|
||||
## Page Structure and Layout
|
||||
|
||||
### Connection Management Page (`/connections`)
|
||||
|
||||
The connection management page uses different views/states to handle different user interactions. Views are managed through component state and routing within the same page component.
|
||||
|
||||
### List View
|
||||
|
||||
**State:** `view === 'list'` or `selectedConnectionId === null`
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Main Content Area:**
|
||||
- Table or card grid displaying all connections for the current user
|
||||
- Each connection row/card shows: authority, external username, external email, status, token status, connected at, last checked
|
||||
- **Action buttons column** in table (rendered on first load):
|
||||
- "Edit" button - opens popup/modal edit form
|
||||
- "Delete" button - shows confirmation dialog and deletes connection
|
||||
- "Refresh Token" button - refreshes OAuth token for the connection
|
||||
- All necessary connection information displayed in table (no detail view needed)
|
||||
|
||||
- **Search and Filter Controls:**
|
||||
- General search input box (searches across all text fields)
|
||||
- Field-specific filter controls (one filter per visible field)
|
||||
- Active filter indicators (chips/badges showing applied filters)
|
||||
- "Clear all filters" button when filters are active
|
||||
- **Note:** Filtering and sorting are done client-side (connections endpoint returns all user connections)
|
||||
|
||||
- **Sorting Controls:**
|
||||
- Clickable column headers with sort indicators (up/down arrows)
|
||||
- Visual indication of current sort field and direction
|
||||
- Support for multi-level sorting
|
||||
- **Note:** Sorting is done client-side
|
||||
|
||||
- **Action Buttons:**
|
||||
- "Connect Google" button - creates Google connection and initiates OAuth flow
|
||||
- "Connect Microsoft" button - creates Microsoft connection and initiates OAuth flow
|
||||
|
||||
### Edit View (Popup/Modal)
|
||||
|
||||
**State:** `view === 'edit'` and `selectedConnectionId !== null` (popup/modal overlay)
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Edit Form in Popup/Modal:**
|
||||
- Dynamic form with editable connection fields only
|
||||
- Field labels with required indicators (asterisk for required fields)
|
||||
- Help text/tooltips from field descriptions
|
||||
- Input validation errors displayed inline
|
||||
- Form pre-populated with current connection values
|
||||
- Save and Cancel buttons in modal footer
|
||||
- On save: Updates connection, closes modal, refreshes table
|
||||
- On cancel: Closes modal without saving
|
||||
|
||||
- **Confirmation Dialogs:**
|
||||
- Delete confirmation dialog
|
||||
|
||||
---
|
||||
|
||||
## User Interactions and Functionality
|
||||
|
||||
### Browsing and Discovery
|
||||
|
||||
**Search Functionality:**
|
||||
- User types in general search box
|
||||
- Frontend debounces input (300-500ms delay)
|
||||
- Search applies across all text fields in connection objects
|
||||
- **Client-side filtering** (connections endpoint returns all user connections)
|
||||
- Results update automatically
|
||||
|
||||
**Field Filtering:**
|
||||
- User selects field to filter by
|
||||
- Frontend shows appropriate filter UI based on field type:
|
||||
- Text fields → Text input with operator selection (contains, equals, startsWith, endsWith)
|
||||
- Select fields → Dropdown with options from backend metadata
|
||||
- Timestamp fields → Date picker with comparison operators or date range picker
|
||||
- User applies filter → Filter appears as active chip/badge
|
||||
- User can remove individual filters
|
||||
- Multiple filters work together (AND logic)
|
||||
- **Client-side filtering** (no backend pagination)
|
||||
|
||||
**Sorting:**
|
||||
- User clicks column header
|
||||
- Sort direction toggles (asc → desc → remove)
|
||||
- Multiple columns can be sorted (multi-level sorting)
|
||||
- Sort indicators show on column headers
|
||||
- All filters and search preserved when sorting
|
||||
- **Client-side sorting** (no backend pagination)
|
||||
|
||||
**View Switching:**
|
||||
- All connection information displayed in table (no detail view navigation needed)
|
||||
|
||||
### Creating Connections
|
||||
|
||||
**Create Interaction:**
|
||||
- User clicks "Connect Google" button → Immediately creates Google connection and initiates OAuth flow
|
||||
- OR user clicks "Connect Microsoft" button → Immediately creates Microsoft connection and initiates OAuth flow
|
||||
- No dialog or selection step needed
|
||||
|
||||
**Connection Creation:**
|
||||
- Connection created with PENDING status
|
||||
- OAuth flow automatically initiated via `/api/connections/{connectionId}/connect`
|
||||
- User redirected to OAuth provider
|
||||
- After OAuth completion, connection status updated to ACTIVE
|
||||
|
||||
**Connection Submission:**
|
||||
- For Google: Connection type sent as `{type: "google"}`
|
||||
- For Microsoft: Connection type sent as `{type: "msft"}`
|
||||
- Success → Connection created, OAuth flow initiated, user redirected
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Editing Connections
|
||||
|
||||
**Edit Interaction:**
|
||||
- User clicks "Edit" button in table row → Opens popup/modal edit form
|
||||
- Form pre-populated with current connection values
|
||||
- User modifies fields (typically externalEmail, status)
|
||||
- Client-side validation shows errors immediately
|
||||
- User clicks Save → Form validates and submits
|
||||
- User clicks Cancel → Closes modal without saving
|
||||
|
||||
**Form Validation:**
|
||||
- Required field validation (shows error if empty)
|
||||
- Type validation (e.g., email fields must be valid email format)
|
||||
- Select field validation (value must be in options array)
|
||||
- Validation errors displayed inline below fields
|
||||
|
||||
**Form Submission:**
|
||||
- Only changed fields sent (or all form data)
|
||||
- Success → Close popup/modal, refresh table, show success message
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error in modal
|
||||
- 400 (validation errors) → Display backend validation errors in modal
|
||||
- Other errors → Show generic error message in modal
|
||||
|
||||
### Refreshing Connection Tokens
|
||||
|
||||
**Refresh Token Action:**
|
||||
- User clicks "Refresh Token" button in table
|
||||
- Frontend shows loading state
|
||||
- Frontend determines connection authority (Google or Microsoft)
|
||||
- Frontend calls appropriate refresh endpoint:
|
||||
- Microsoft: `POST /api/msft/refresh` with `{connectionId: connectionId}`
|
||||
- Google: `POST /api/google/refresh` with `{connectionId: connectionId}`
|
||||
- Success → Token refreshed, connection status updated, table refreshed
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 404 (connection not found) → Show not found error
|
||||
- Other errors → Show generic error message
|
||||
|
||||
**Implementation Notes:**
|
||||
- Refresh button should be disabled or show loading state during refresh
|
||||
- Token status should update immediately after successful refresh
|
||||
- Connection status may change from "expired" to "active" after refresh
|
||||
|
||||
### Deleting Connections
|
||||
|
||||
**Delete Action:**
|
||||
- User clicks delete button in table
|
||||
- Confirmation dialog appears with connection authority/type
|
||||
- Warning about permanent deletion
|
||||
- User confirms → Connection deleted
|
||||
- User cancels → Dialog closes, no action
|
||||
|
||||
**Delete Success Handling:**
|
||||
- Success message displayed
|
||||
- Remove connection from list or refresh table
|
||||
|
||||
---
|
||||
|
||||
## Backend Routes and API Integration
|
||||
|
||||
### Complete Route Reference
|
||||
|
||||
All backend routes used by connection pages:
|
||||
|
||||
| Route | Method | Purpose | When Used | Access Control |
|
||||
|-------|--------|---------|-----------|----------------|
|
||||
| `/api/connections/` | GET | Get all connections for current user | Initial page load | Current user only (returns only user's connections) |
|
||||
| `/api/connections/` | POST | Create new connection | User selects connection type | Current user only |
|
||||
| `/api/connections/{connectionId}` | PUT | Update connection | Edit form submission | Current user only |
|
||||
| `/api/connections/{connectionId}` | DELETE | Delete connection | User confirms deletion | Current user only |
|
||||
| `/api/connections/{connectionId}/connect` | POST | Initiate OAuth flow | After connection creation | Current user only |
|
||||
| `/api/msft/refresh` | POST | Refresh Microsoft token | User clicks refresh token for MSFT connection | Current user only |
|
||||
| `/api/google/refresh` | POST | Refresh Google token | User clicks refresh token for Google connection | Current user only |
|
||||
| `/api/attributes/UserConnection` | GET | Get field definitions | Page load (once per page) - used to generate all UI components | All authenticated users |
|
||||
|
||||
### API Request Patterns
|
||||
|
||||
**Get Connections Request:**
|
||||
```
|
||||
GET /api/connections/
|
||||
```
|
||||
- Returns all connections for the current user (no pagination)
|
||||
- Response includes token status information
|
||||
- Filtering and sorting done client-side
|
||||
|
||||
**Create Connection Request:**
|
||||
```
|
||||
POST /api/connections/
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"type": "google" | "msft"
|
||||
}
|
||||
```
|
||||
- Creates connection with PENDING status
|
||||
- Returns created connection object
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Connect Service Request:**
|
||||
```
|
||||
POST /api/connections/{connectionId}/connect
|
||||
```
|
||||
- Returns `{authUrl: "/api/google/login?state=..."}` or `{authUrl: "/api/msft/login?state=..."}`
|
||||
- Frontend should navigate to the authUrl to initiate OAuth flow
|
||||
- Called automatically after connection creation
|
||||
|
||||
**Update Request:**
|
||||
```
|
||||
PUT /api/connections/{connectionId}
|
||||
Content-Type: application/json
|
||||
Body: {fieldName: value, ...}
|
||||
```
|
||||
- Send only changed fields or all form data
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Refresh Token Request:**
|
||||
```
|
||||
POST /api/msft/refresh
|
||||
Content-Type: application/json
|
||||
Body: {"connectionId": "connection-id"}
|
||||
```
|
||||
OR
|
||||
```
|
||||
POST /api/google/refresh
|
||||
Content-Type: application/json
|
||||
Body: {"connectionId": "connection-id"}
|
||||
```
|
||||
- Returns `{message: "Token refreshed successfully", expires_at: timestamp, expires_in_seconds: number}`
|
||||
- Handle 403 (permission denied) and 404 (connection not found)
|
||||
|
||||
**Delete Requests:**
|
||||
```
|
||||
DELETE /api/connections/{connectionId}
|
||||
```
|
||||
- Delete operation requires user confirmation
|
||||
- Handle 403 (permission denied) and 404 (not found) gracefully
|
||||
|
||||
### Response Handling
|
||||
|
||||
**Get Connections Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "...",
|
||||
"userId": "...",
|
||||
"authority": "google",
|
||||
"externalId": "...",
|
||||
"externalUsername": "...",
|
||||
"externalEmail": "...",
|
||||
"status": "active",
|
||||
"connectedAt": 1234567890,
|
||||
"lastChecked": 1234567890,
|
||||
"expiresAt": 1234567890,
|
||||
"tokenStatus": "active",
|
||||
"tokenExpiresAt": 1234567890
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
- Returns array of connections (no pagination)
|
||||
- Each connection includes token status information
|
||||
|
||||
**Error Responses:**
|
||||
- 403 Forbidden → Show permission error message
|
||||
- 404 Not Found → Show "not found" error message
|
||||
- 400 Bad Request → Display validation errors from response
|
||||
- 500 Internal Server Error → Show generic error message
|
||||
|
||||
---
|
||||
|
||||
## Field and Attribute Reference
|
||||
|
||||
### Complete Field List
|
||||
|
||||
The following is a comprehensive list of all parameters, attributes, and fields that will be displayed for connections. All of these are provided by the backend through the `/api/attributes/UserConnection` endpoint and should never be hardcoded in the frontend.
|
||||
|
||||
#### Core Connection Fields
|
||||
|
||||
**Identification Fields:**
|
||||
- `id` - Unique connection identifier (text, readonly, not required, visible)
|
||||
- `userId` - ID of the user this connection belongs to (text, readonly, not required, visible)
|
||||
|
||||
**Connection Properties:**
|
||||
- `authority` - Authentication authority (select, readonly, not required, visible)
|
||||
- Options: "local", "google", "msft"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `externalId` - User ID in the external system (text, readonly, not required, visible)
|
||||
- `externalUsername` - Username in the external system (text, editable, required, visible)
|
||||
- `externalEmail` - Email in the external system (email, editable, not required, visible)
|
||||
- `status` - Connection status (select, editable, not required, visible)
|
||||
- Options: "active", "inactive", "expired", "pending"
|
||||
- Each option has localized labels (en/fr)
|
||||
|
||||
**Timestamp Fields:**
|
||||
- `connectedAt` - When the connection was established (timestamp, readonly, not required, visible)
|
||||
- `lastChecked` - When the connection was last verified (timestamp, readonly, not required, visible)
|
||||
- `expiresAt` - When the connection expires (timestamp, readonly, not required, visible)
|
||||
|
||||
**Token Status Fields:**
|
||||
- `tokenStatus` - Current token status (select, readonly, not required, visible)
|
||||
- Options: "active", "expired", "none"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `tokenExpiresAt` - When the current token expires (timestamp, readonly, not required, visible)
|
||||
|
||||
### Attribute Definition Structure
|
||||
|
||||
Each field returned from `/api/attributes/UserConnection` contains:
|
||||
|
||||
- `name` - Field name (e.g., "authority", "status", "externalEmail", "id")
|
||||
- `type` - Field data type (e.g., "text", "select", "email", "timestamp", "checkbox")
|
||||
- `label` - Localized field label (object with language keys: {"en": "English Label", "fr": "French Label"})
|
||||
- `description` - Field description text
|
||||
- `required` - Boolean indicating if field is required
|
||||
- `readonly` - Boolean indicating if field is read-only
|
||||
- `editable` - Boolean indicating if field can be edited (inverse of readonly)
|
||||
- `visible` - Boolean indicating if field should be displayed in UI
|
||||
- `options` - Array of options for select fields (each option has `value` and localized `label`)
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Rendering Guidelines
|
||||
|
||||
The frontend must render all UI components dynamically based on backend metadata. No field definitions, labels, validation rules, or UI structure should be hardcoded.
|
||||
|
||||
### Table Column Generation
|
||||
|
||||
When rendering the connection list table:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/UserConnection`
|
||||
2. Filter attributes where `visible: true` to determine which columns to display
|
||||
3. Use `label` property for column headers (select appropriate language based on user preference)
|
||||
4. Use `type` property to determine how to format cell values:
|
||||
- `text` fields → Display as plain text
|
||||
- `email` fields → Display as clickable mailto link
|
||||
- `select` fields → Display value using label from options array (match value to option.value, then display option.label)
|
||||
- `timestamp` fields → Format as relative time ("2 hours ago") or absolute date/time based on user preference
|
||||
- `checkbox` fields → Display as checkmark icon or "Yes"/"No" text
|
||||
5. Use `readonly` property to determine if column should be sortable (readonly fields may still be sortable, but editable fields definitely are)
|
||||
6. Generate click handlers for column headers to update sort parameters (client-side sorting)
|
||||
7. **Add Actions Column:**
|
||||
- Render action buttons in each table row on first load
|
||||
- Include "Edit" button - opens popup/modal edit form
|
||||
- Include "Delete" button - shows confirmation dialog
|
||||
- Include "Refresh Token" button - refreshes OAuth token (only for Google/MSFT connections)
|
||||
- Buttons should be visible and accessible in each row
|
||||
|
||||
### Filter Control Generation
|
||||
|
||||
When rendering filter controls:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/UserConnection`
|
||||
2. Filter attributes where `visible: true` to determine which filters to show
|
||||
3. For each visible field, generate appropriate filter UI based on `type`:
|
||||
- `text` fields → Text input filter (supports "contains", "equals", "startsWith", "endsWith" operators)
|
||||
- `email` fields → Email input filter
|
||||
- `select` fields → Dropdown filter with options from `options` array (use localized labels)
|
||||
- `timestamp` fields → Date range picker or single date picker with comparison operators
|
||||
- `checkbox` fields → Boolean toggle filter (true/false/any)
|
||||
4. Use `label` property for filter labels (localized)
|
||||
5. When user applies filter, update client-side filter state
|
||||
6. Display active filters as chips/badges showing field label and value
|
||||
7. Allow removing individual filters
|
||||
8. **Note:** All filtering is done client-side (connections endpoint returns all user connections)
|
||||
|
||||
### Search Implementation
|
||||
|
||||
For general search functionality:
|
||||
|
||||
1. Display a single search input box (not field-specific)
|
||||
2. When user types, update client-side search state
|
||||
3. Debounce search input (wait 300-500ms after user stops typing before filtering)
|
||||
4. Search applies across all text fields in the connection object
|
||||
5. **Note:** All searching is done client-side (connections endpoint returns all user connections)
|
||||
|
||||
### Form Field Generation
|
||||
|
||||
When rendering edit forms:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/UserConnection`
|
||||
2. Filter attributes where `visible: true` AND `editable: true` to determine which fields to show
|
||||
3. For each editable field, generate appropriate form input based on `type`:
|
||||
- `text` fields → Text input
|
||||
- `email` fields → Email input
|
||||
- `select` fields → Dropdown/select input with options from `options` array (use localized labels)
|
||||
- `timestamp` fields → Date/time picker
|
||||
- `checkbox` fields → Checkbox input
|
||||
4. Use `label` property for field labels (localized)
|
||||
5. Use `required` property to show required indicators (asterisk, etc.)
|
||||
6. Use `description` property to show help text or tooltips
|
||||
7. Validate form before submission:
|
||||
- Check all `required: true` fields have values
|
||||
- Validate types (e.g., email fields must be valid email format)
|
||||
- Validate select fields (value must be in options array)
|
||||
8. On submit, send only changed fields or all form data to update endpoint
|
||||
|
||||
### Display Formatting
|
||||
|
||||
When displaying field values:
|
||||
|
||||
1. Use `type` property to determine formatting:
|
||||
- `text` → Display as-is (may need HTML escaping)
|
||||
- `email` → Display as clickable mailto link
|
||||
- `select` → Look up value in `options` array and display localized label
|
||||
- `timestamp` → Format as relative time or absolute date/time
|
||||
- `checkbox` → Display as checkmark icon or "Yes"/"No" text
|
||||
2. Handle `null` or `undefined` values gracefully (show "-" or "Not set")
|
||||
3. Use `readonly` property to determine if field should show edit indicators
|
||||
4. Special formatting for connection fields:
|
||||
- Authority → Display as badge with icon (Google, Microsoft)
|
||||
- Status → Color-code badges (Active = green, Inactive = gray, Expired = red, Pending = yellow)
|
||||
- Token Status → Color-code badges (Active = green, Expired = red, None = gray)
|
||||
- Timestamps → Show relative time in list view, absolute date in detail view
|
||||
|
||||
### Localization
|
||||
|
||||
All labels and options support multiple languages:
|
||||
|
||||
1. Use user's preferred language (from user settings or browser locale)
|
||||
2. Access localized labels from `label` object: `label[userLanguage]` or `label.en` as fallback
|
||||
3. For select options, use `option.label[userLanguage]` or `option.label.en` as fallback
|
||||
4. If label for current language is missing, fall back to English
|
||||
|
||||
### Key Principles
|
||||
|
||||
- Never hardcode field names, labels, types, or validation rules
|
||||
- Always fetch attribute definitions from backend before rendering UI
|
||||
- Use attribute metadata to determine what to display and how to display it
|
||||
- Support all field types dynamically - if backend adds new types, frontend should handle them
|
||||
- Respect `visible`, `editable`, `readonly`, and `required` flags from backend
|
||||
- Use localized labels from backend metadata
|
||||
- Generate filters, forms, and tables entirely from attribute definitions
|
||||
- When backend adds new fields, frontend should automatically display them without code changes
|
||||
- Handle all error cases gracefully (403, 404, 400, 500)
|
||||
- Provide user feedback for all actions (loading states, success messages, error messages)
|
||||
- **All filtering and sorting is done client-side** (connections endpoint returns all user connections)
|
||||
- **Users can only see and manage their own connections** (backend enforces this)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This document provides complete frontend requirements for all connection management pages and components. All requirements follow the principle of **backend-driven UI generation**—no hardcoding of field definitions, labels, or validation rules. The frontend should dynamically generate all UI components from backend metadata provided through the `/api/attributes/UserConnection` endpoint.
|
||||
|
||||
**Key Architecture Pattern:** The connection management interface is a single page (`/connections`) with different views managed through component state. All interactions happen within the same page component without separate routes.
|
||||
|
||||
**Security Note:** Connections are user-scoped. Users can only see and manage their own connections. The backend enforces this security, and the frontend should never attempt to access other users' connections.
|
||||
|
||||
For generic patterns that apply across all entity types (not just connections), see the [Dynamic Forms and Pagination documentation](./dynamic-forms-and-pagination.md).
|
||||
|
||||
|
|
@ -1,689 +0,0 @@
|
|||
# Dynamic Forms and Pagination - Generic Frontend Patterns
|
||||
|
||||
This document describes the generic, reusable patterns for dynamic form generation and pagination that apply across all entity types in the frontend. These patterns enable completely backend-driven UI generation with no hardcoding.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Backend Metadata System](#backend-metadata-system)
|
||||
3. [Dynamic Form Generation](#dynamic-form-generation)
|
||||
4. [Pagination System](#pagination-system)
|
||||
5. [Search and Filtering](#search-and-filtering)
|
||||
6. [Sorting](#sorting)
|
||||
7. [Localization](#localization)
|
||||
8. [Implementation Patterns](#implementation-patterns)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The frontend uses a **completely backend-driven approach** for generating forms, tables, filters, and pagination controls. All field definitions, labels, validation rules, and UI structure come from the backend through the attribute definition system.
|
||||
|
||||
### Key Principles
|
||||
|
||||
- **No Hardcoding**: Field names, labels, types, validation rules, and options are never hardcoded
|
||||
- **Backend-Driven**: All UI components generated from backend metadata
|
||||
- **Entity-Agnostic**: Same patterns work for workflows, users, mandates, files, prompts, automations, etc.
|
||||
- **Automatic Updates**: When backend adds new fields, frontend automatically displays them without code changes
|
||||
- **Localization**: All labels and options support multiple languages from backend
|
||||
|
||||
### Applicable Entity Types
|
||||
|
||||
These patterns apply to any entity type that has:
|
||||
- An attributes endpoint: `GET /api/attributes/{EntityType}`
|
||||
- A list endpoint supporting pagination: `GET /api/{entities}/?pagination=...`
|
||||
- CRUD operations: Create, Read, Update, Delete
|
||||
|
||||
Examples: `ChatWorkflow`, `User`, `Mandate`, `FileItem`, `Prompt`, `AutomationDefinition`, `ChatMessage`, `ChatLog`, etc.
|
||||
|
||||
---
|
||||
|
||||
## Backend Metadata System
|
||||
|
||||
### Attribute Definition Endpoint
|
||||
|
||||
Every entity type exposes an attributes endpoint that provides complete metadata:
|
||||
|
||||
```
|
||||
GET /api/attributes/{EntityType}
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
- `GET /api/attributes/ChatWorkflow`
|
||||
- `GET /api/attributes/User`
|
||||
- `GET /api/attributes/Mandate`
|
||||
- `GET /api/attributes/FileItem`
|
||||
- `GET /api/attributes/Prompt`
|
||||
|
||||
### Attribute Definition Structure
|
||||
|
||||
Each attribute returned from the endpoint contains:
|
||||
|
||||
- **`name`** - Field name (e.g., "status", "name", "email", "createdAt")
|
||||
- **`type`** - Field data type (e.g., "text", "select", "integer", "timestamp", "checkbox", "email", "url")
|
||||
- **`label`** - Localized field label (object with language keys: `{"en": "English Label", "fr": "French Label"}`)
|
||||
- **`description`** - Field description text (for help text/tooltips)
|
||||
- **`required`** - Boolean indicating if field is required for validation
|
||||
- **`readonly`** - Boolean indicating if field is read-only (cannot be edited)
|
||||
- **`editable`** - Boolean indicating if field can be edited (inverse of readonly)
|
||||
- **`visible`** - Boolean indicating if field should be displayed in UI
|
||||
- **`options`** - Array of options for select fields (each option has `value` and localized `label`)
|
||||
|
||||
### Response Format
|
||||
|
||||
The attributes endpoint returns:
|
||||
|
||||
```json
|
||||
{
|
||||
"entityType": "ChatWorkflow",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "status",
|
||||
"type": "select",
|
||||
"label": {"en": "Status", "fr": "Statut"},
|
||||
"description": "Current status of the workflow",
|
||||
"required": false,
|
||||
"readonly": false,
|
||||
"editable": true,
|
||||
"visible": true,
|
||||
"options": [
|
||||
{"value": "running", "label": {"en": "Running", "fr": "En cours"}},
|
||||
{"value": "completed", "label": {"en": "Completed", "fr": "Terminé"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"label": {"en": "Name", "fr": "Nom"},
|
||||
"description": "Name of the workflow",
|
||||
"required": true,
|
||||
"readonly": false,
|
||||
"editable": true,
|
||||
"visible": true
|
||||
}
|
||||
// ... more attributes
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Form Generation
|
||||
|
||||
Dynamic forms are generated entirely from backend attribute definitions. The same pattern works for create forms, edit forms, and inline editing.
|
||||
|
||||
### Form Generation Process
|
||||
|
||||
1. **Fetch Attribute Definitions**
|
||||
- Call `GET /api/attributes/{EntityType}` when form component mounts
|
||||
- Store attribute definitions in component state
|
||||
|
||||
2. **Filter Attributes**
|
||||
- For edit forms: Filter where `visible: true` AND `editable: true`
|
||||
- For create forms: Filter where `visible: true` AND `editable: true` (may exclude readonly fields)
|
||||
- For display-only: Filter where `visible: true`
|
||||
|
||||
3. **Generate Form Fields**
|
||||
- Iterate through filtered attributes
|
||||
- Generate appropriate input component based on `type`
|
||||
- Use `label` for field labels (localized)
|
||||
- Use `description` for help text/tooltips
|
||||
- Use `required` to show required indicators
|
||||
|
||||
4. **Handle Form Submission**
|
||||
- Validate form using attribute metadata
|
||||
- Send form data to appropriate endpoint (POST for create, PUT for update)
|
||||
- Handle success/error responses
|
||||
|
||||
### Field Type to Input Component Mapping
|
||||
|
||||
Based on the `type` property, generate appropriate input components:
|
||||
|
||||
- **`text`** → Text input (single line)
|
||||
- **`textarea`** → Multi-line text area
|
||||
- **`select`** → Dropdown/select input with options from `options` array
|
||||
- **`integer`** → Number input (integer only)
|
||||
- **`float`** or **`number`** → Number input (decimal allowed)
|
||||
- **`timestamp`** → Date/time picker
|
||||
- **`date`** → Date picker (date only)
|
||||
- **`time`** → Time picker (time only)
|
||||
- **`checkbox`** → Checkbox input (boolean)
|
||||
- **`email`** → Email input (with email validation)
|
||||
- **`url`** → URL input (with URL validation)
|
||||
- **`password`** → Password input (masked)
|
||||
- **`file`** → File upload input
|
||||
|
||||
### Form Field Rendering Rules
|
||||
|
||||
For each form field:
|
||||
|
||||
1. **Field Label**
|
||||
- Use `label[userLanguage]` or `label.en` as fallback
|
||||
- Show required indicator (asterisk, etc.) if `required: true`
|
||||
|
||||
2. **Input Component**
|
||||
- Generate based on `type` property
|
||||
- For `select` fields: Populate options from `options` array, use localized labels
|
||||
- For `integer`/`number` fields: Set appropriate min/max constraints if available
|
||||
- For `timestamp`/`date`/`time` fields: Use appropriate date/time picker component
|
||||
|
||||
3. **Help Text**
|
||||
- Display `description` as help text or tooltip
|
||||
- Show below or next to input field
|
||||
|
||||
4. **Validation**
|
||||
- Mark field as required if `required: true`
|
||||
- Validate type (e.g., integer fields must be numbers)
|
||||
- Validate select fields (value must be in options array)
|
||||
- Validate email/url fields with appropriate regex patterns
|
||||
|
||||
5. **Disabled State**
|
||||
- Disable input if `readonly: true` or `editable: false`
|
||||
- Show read-only indicator if applicable
|
||||
|
||||
### Form Validation
|
||||
|
||||
Before form submission:
|
||||
|
||||
1. **Required Field Validation**
|
||||
- Check all fields where `required: true` have non-empty values
|
||||
- Show error messages for missing required fields
|
||||
|
||||
2. **Type Validation**
|
||||
- Validate integer fields contain valid integers
|
||||
- Validate number fields contain valid numbers
|
||||
- Validate email fields contain valid email addresses
|
||||
- Validate URL fields contain valid URLs
|
||||
- Validate timestamp/date fields contain valid dates
|
||||
|
||||
3. **Option Validation**
|
||||
- For select fields, ensure value exists in `options` array
|
||||
- Show error if invalid option selected
|
||||
|
||||
4. **Custom Validation**
|
||||
- Apply any additional validation rules from backend metadata if available
|
||||
|
||||
### Form Submission
|
||||
|
||||
On form submit:
|
||||
|
||||
1. **Collect Form Data**
|
||||
- Gather values from all form fields
|
||||
- Use attribute `name` as keys in form data object
|
||||
|
||||
2. **Prepare Request**
|
||||
- For create: `POST /api/{entities}` with form data
|
||||
- For update: `PUT /api/{entities}/{id}` with form data
|
||||
- Include only changed fields (for updates) or all form data (for creates)
|
||||
|
||||
3. **Handle Response**
|
||||
- On success: Show success message, navigate or refresh as appropriate
|
||||
- On error: Display validation errors from backend, highlight invalid fields
|
||||
|
||||
---
|
||||
|
||||
## Pagination System
|
||||
|
||||
Pagination is a unified system that works across all list endpoints. It supports pagination, sorting, filtering, and general search in a consistent way.
|
||||
|
||||
### Pagination Endpoint Pattern
|
||||
|
||||
All list endpoints support pagination through a query parameter:
|
||||
|
||||
```
|
||||
GET /api/{entities}/?pagination={JSON_ENCODED_PAGINATION_PARAMS}
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
- `GET /api/workflows/?pagination={"page":1,"pageSize":20,"sort":[],"filters":null}`
|
||||
- `GET /api/users/?pagination={"page":1,"pageSize":20,"sort":[{"field":"name","direction":"asc"}],"filters":{"status":"active"}}`
|
||||
- `GET /api/files/list?pagination={"page":2,"pageSize":50,"sort":[],"filters":{"search":"report"}}`
|
||||
|
||||
### Pagination Request Parameters
|
||||
|
||||
The `pagination` query parameter is a JSON-encoded object with the following structure:
|
||||
|
||||
**`PaginationParams` Structure:**
|
||||
- **`page`** - Current page number (1-based, minimum 1)
|
||||
- **`pageSize`** - Number of items per page (minimum 1, maximum 1000)
|
||||
- **`sort`** - Array of sort field configurations
|
||||
- Each sort field contains:
|
||||
- `field` - Field name to sort by (must match an attribute name)
|
||||
- `direction` - Sort direction: "asc" or "desc"
|
||||
- **`filters`** - Filter criteria dictionary (see [Search and Filtering](#search-and-filtering))
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"page": 1,
|
||||
"pageSize": 20,
|
||||
"sort": [
|
||||
{"field": "lastActivity", "direction": "desc"},
|
||||
{"field": "name", "direction": "asc"}
|
||||
],
|
||||
"filters": {
|
||||
"search": "invoice",
|
||||
"status": "running"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination Response Structure
|
||||
|
||||
All paginated endpoints return data in this format:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
// Array of entity objects
|
||||
],
|
||||
"pagination": {
|
||||
"currentPage": 1,
|
||||
"pageSize": 20,
|
||||
"totalItems": 45,
|
||||
"totalPages": 3,
|
||||
"sort": [
|
||||
{"field": "lastActivity", "direction": "desc"}
|
||||
],
|
||||
"filters": {
|
||||
"search": "invoice",
|
||||
"status": "running"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`PaginationMetadata` Structure:**
|
||||
- **`currentPage`** - Current page number (1-based)
|
||||
- **`pageSize`** - Number of items per page
|
||||
- **`totalItems`** - Total number of items across all pages (after filters applied)
|
||||
- **`totalPages`** - Total number of pages (calculated from totalItems / pageSize)
|
||||
- **`sort`** - Current sort configuration applied (array of SortField objects)
|
||||
- **`filters`** - Current filters applied (mirrors request filters)
|
||||
|
||||
### Pagination UI Components
|
||||
|
||||
Generate pagination controls using the `pagination` metadata from response:
|
||||
|
||||
1. **Page Information Display**
|
||||
- Show "Page X of Y" (using `currentPage` and `totalPages`)
|
||||
- Show "Showing X-Y of Z items" (calculate from `currentPage`, `pageSize`, `totalItems`)
|
||||
|
||||
2. **Page Navigation Controls**
|
||||
- Previous page button (disabled if `currentPage === 1`)
|
||||
- Next page button (disabled if `currentPage === totalPages`)
|
||||
- Page number buttons (show current page and adjacent pages)
|
||||
- First page button (if not on first page)
|
||||
- Last page button (if not on last page)
|
||||
|
||||
3. **Page Size Selector**
|
||||
- Dropdown to change `pageSize` (common options: 10, 20, 50, 100)
|
||||
- When changed, reset to page 1 and refetch
|
||||
|
||||
4. **Page Change Handler**
|
||||
- When user clicks page number or navigation button:
|
||||
- Update `page` in pagination parameters
|
||||
- Preserve all filters and sort settings
|
||||
- Refetch data with updated pagination
|
||||
|
||||
### Non-Paginated Requests
|
||||
|
||||
If user wants all items without pagination:
|
||||
|
||||
- Omit `pagination` parameter entirely: `GET /api/{entities}/`
|
||||
- Backend returns all items (still wrapped in `PaginatedResponse` with `pagination: null`)
|
||||
|
||||
---
|
||||
|
||||
## Search and Filtering
|
||||
|
||||
The pagination system supports both general search and field-specific filtering. Both are combined in the `filters` object.
|
||||
|
||||
### General Search
|
||||
|
||||
General search searches across all text fields in the entity.
|
||||
|
||||
**Implementation:**
|
||||
1. Display a single search input box (not field-specific)
|
||||
2. When user types, update `filters.search` in pagination parameters
|
||||
3. Debounce search input (wait 300-500ms after user stops typing before sending request)
|
||||
4. Reset to page 1 when search query changes
|
||||
5. Combine search with field-specific filters (both are in the `filters` object)
|
||||
|
||||
**Filter Structure:**
|
||||
```json
|
||||
{
|
||||
"filters": {
|
||||
"search": "invoice"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Field-Specific Filtering
|
||||
|
||||
Field-specific filters allow filtering by individual fields with various operators.
|
||||
|
||||
**Filter Structure:**
|
||||
|
||||
Simple equals filter:
|
||||
```json
|
||||
{
|
||||
"filters": {
|
||||
"status": "running"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Filter with operator:
|
||||
```json
|
||||
{
|
||||
"filters": {
|
||||
"name": {
|
||||
"operator": "contains",
|
||||
"value": "workflow"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Supported Operators:**
|
||||
- **`equals`** or **`eq`** - Exact match
|
||||
- **`contains`** - Substring match (case-insensitive for strings)
|
||||
- **`startsWith`** - String starts with (case-insensitive)
|
||||
- **`endsWith`** - String ends with (case-insensitive)
|
||||
- **`gt`** - Greater than (for numbers/dates)
|
||||
- **`gte`** - Greater than or equal
|
||||
- **`lt`** - Less than
|
||||
- **`lte`** - Less than or equal
|
||||
- **`in`** - Value in list (array of values)
|
||||
- **`notIn`** - Value not in list (array of values)
|
||||
|
||||
**Multiple Filters:**
|
||||
All filters are combined with AND logic:
|
||||
```json
|
||||
{
|
||||
"filters": {
|
||||
"search": "invoice",
|
||||
"status": "running",
|
||||
"currentRound": {
|
||||
"operator": "gt",
|
||||
"value": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Filter Control Generation
|
||||
|
||||
Generate filter controls dynamically from attribute definitions:
|
||||
|
||||
1. **Fetch Attribute Definitions**
|
||||
- Call `GET /api/attributes/{EntityType}` when filter component mounts
|
||||
- Filter attributes where `visible: true` to determine which filters to show
|
||||
|
||||
2. **Generate Filter UI by Type**
|
||||
- **`text`** fields → Text input filter (supports "contains", "equals", "startsWith", "endsWith" operators)
|
||||
- **`select`** fields → Dropdown filter with options from `options` array (use localized labels)
|
||||
- **`integer`** or **`number`** fields → Number range filter (min/max inputs) or single number input with comparison operators (gt, gte, lt, lte)
|
||||
- **`timestamp`** or **`date`** fields → Date range picker or single date picker with comparison operators
|
||||
- **`checkbox`** fields → Boolean toggle filter (true/false/any)
|
||||
|
||||
3. **Filter Labels**
|
||||
- Use `label` property for filter labels (localized)
|
||||
- Display filter label next to filter control
|
||||
|
||||
4. **Apply Filters**
|
||||
- When user applies filter, update `filters[fieldName]` in pagination parameters
|
||||
- Support multiple filters simultaneously
|
||||
- Reset to page 1 when filters change
|
||||
- Refetch data with updated filters
|
||||
|
||||
5. **Active Filter Indicators**
|
||||
- Display active filters as chips/badges showing field label and value
|
||||
- Allow removing individual filters by removing them from `filters` object
|
||||
- Show "Clear all filters" button when filters are active
|
||||
|
||||
### Combined Search and Filters
|
||||
|
||||
Search and field filters work together:
|
||||
|
||||
- Both are stored in the `filters` object
|
||||
- All filters are combined with AND logic
|
||||
- When any filter changes, combine all and refetch
|
||||
- Preserve all active filters when sorting or changing pages
|
||||
|
||||
---
|
||||
|
||||
## Sorting
|
||||
|
||||
Sorting allows users to order list items by one or more fields.
|
||||
|
||||
### Sort Configuration
|
||||
|
||||
Sorting is configured in the `sort` array of pagination parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"sort": [
|
||||
{"field": "lastActivity", "direction": "desc"},
|
||||
{"field": "name", "direction": "asc"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- Multiple sort fields create multi-level sorting (sorts by first field, then by second field for ties)
|
||||
- Sort fields are applied in order (first field is primary sort, second is secondary sort, etc.)
|
||||
|
||||
### Sort UI Implementation
|
||||
|
||||
1. **Column Header Sorting**
|
||||
- Make column headers clickable for sortable columns
|
||||
- Show sort indicator (arrow up/down) next to column header when sorted
|
||||
- Clicking a column header:
|
||||
- If not currently sorted by that column: Add sort field with "asc" direction
|
||||
- If sorted ascending: Change to "desc"
|
||||
- If sorted descending: Remove sort field (or change to "asc" if keeping)
|
||||
- Update `sort` array in pagination parameters
|
||||
- Preserve all filters and search
|
||||
- Refetch data with updated sort
|
||||
|
||||
2. **Sort Indicator Display**
|
||||
- Show up arrow (↑) for ascending sort
|
||||
- Show down arrow (↓) for descending sort
|
||||
- Show both arrows when column is sortable but not currently sorted
|
||||
- Hide sort indicators for non-sortable columns
|
||||
|
||||
3. **Multi-Level Sort**
|
||||
- When user clicks a second column header, add it to the `sort` array
|
||||
- Display multiple sort indicators if multiple columns are sorted
|
||||
- Allow removing individual sort fields
|
||||
|
||||
### Sortable Columns
|
||||
|
||||
Determine which columns are sortable:
|
||||
|
||||
- All visible columns can potentially be sortable
|
||||
- Backend handles sorting logic, so frontend can attempt to sort any field
|
||||
- If backend doesn't support sorting a field, it will ignore that sort field
|
||||
- Readonly fields may still be sortable (e.g., timestamps, IDs)
|
||||
|
||||
---
|
||||
|
||||
## Localization
|
||||
|
||||
All labels and options support multiple languages from the backend.
|
||||
|
||||
### Language Detection
|
||||
|
||||
1. **User Language Preference**
|
||||
- Get user's preferred language from user settings
|
||||
- Fall back to browser locale if user setting not available
|
||||
- Default to "en" (English) if no language detected
|
||||
|
||||
2. **Label Access Pattern**
|
||||
- Access localized labels from `label` object: `label[userLanguage]`
|
||||
- Fall back to `label.en` if label for current language is missing
|
||||
- For select options: `option.label[userLanguage]` or `option.label.en` as fallback
|
||||
|
||||
### Localized Components
|
||||
|
||||
Apply localization to:
|
||||
|
||||
1. **Form Fields**
|
||||
- Field labels: `attribute.label[userLanguage]`
|
||||
- Help text/descriptions: `attribute.description` (may be localized if backend provides)
|
||||
- Select options: `option.label[userLanguage]`
|
||||
- Validation error messages: Use localized messages
|
||||
|
||||
2. **Table Columns**
|
||||
- Column headers: `attribute.label[userLanguage]`
|
||||
- Cell values for select fields: `option.label[userLanguage]`
|
||||
|
||||
3. **Filter Controls**
|
||||
- Filter labels: `attribute.label[userLanguage]`
|
||||
- Filter options: `option.label[userLanguage]`
|
||||
|
||||
4. **Pagination Controls**
|
||||
- "Page X of Y" text: Use localized strings
|
||||
- "Showing X-Y of Z items" text: Use localized strings
|
||||
- Button labels (Previous, Next, etc.): Use localized strings
|
||||
|
||||
---
|
||||
|
||||
## Implementation Patterns
|
||||
|
||||
### Pattern 1: Generic List Page Component
|
||||
|
||||
A reusable list page component that works for any entity type:
|
||||
|
||||
**Component Structure:**
|
||||
1. Accept `entityType` as prop (e.g., "ChatWorkflow", "User", "Mandate")
|
||||
2. Fetch attributes: `GET /api/attributes/{entityType}`
|
||||
3. Fetch list data: `GET /api/{entities}/?pagination=...`
|
||||
4. Generate table columns from attributes
|
||||
5. Generate filter controls from attributes
|
||||
6. Handle pagination, search, filtering, and sorting
|
||||
7. Handle row clicks (navigate to detail page)
|
||||
|
||||
**State Management:**
|
||||
- Store attribute definitions
|
||||
- Store pagination parameters (page, pageSize, sort, filters)
|
||||
- Store pagination metadata from response
|
||||
- Store list items
|
||||
- Store loading/error states
|
||||
|
||||
**User Interactions:**
|
||||
- Search input → Update `filters.search`
|
||||
- Filter controls → Update `filters[fieldName]`
|
||||
- Column header clicks → Update `sort` array
|
||||
- Page navigation → Update `page`
|
||||
- Page size change → Update `pageSize`, reset to page 1
|
||||
|
||||
### Pattern 2: Generic Form Component
|
||||
|
||||
A reusable form component that works for any entity type:
|
||||
|
||||
**Component Structure:**
|
||||
1. Accept `entityType` and `mode` as props ("create" or "edit")
|
||||
2. Accept `entityId` as prop (for edit mode)
|
||||
3. Fetch attributes: `GET /api/attributes/{entityType}`
|
||||
4. Fetch entity data (for edit mode): `GET /api/{entities}/{id}`
|
||||
5. Generate form fields from attributes
|
||||
6. Handle form submission
|
||||
7. Handle validation
|
||||
|
||||
**Form Field Generation:**
|
||||
- Filter attributes: `visible: true` AND `editable: true`
|
||||
- Generate input components based on `type`
|
||||
- Apply labels, descriptions, required indicators
|
||||
- Handle validation
|
||||
|
||||
**Form Submission:**
|
||||
- Validate form using attribute metadata
|
||||
- For create: `POST /api/{entities}` with form data
|
||||
- For update: `PUT /api/{entities}/{id}` with form data
|
||||
- Handle success/error responses
|
||||
|
||||
### Pattern 3: Generic Table Component
|
||||
|
||||
A reusable table component that works for any entity type:
|
||||
|
||||
**Component Structure:**
|
||||
1. Accept `attributes` and `items` as props
|
||||
2. Generate columns from attributes (filter `visible: true`)
|
||||
3. Render table rows with formatted cell values
|
||||
4. Handle column header clicks for sorting
|
||||
5. Handle row clicks for navigation
|
||||
|
||||
**Cell Value Formatting:**
|
||||
- Use `type` property to determine formatting
|
||||
- Format select fields using options array
|
||||
- Format timestamps as relative/absolute time
|
||||
- Format numbers with separators
|
||||
- Handle null/undefined values
|
||||
|
||||
### Pattern 4: Generic Filter Component
|
||||
|
||||
A reusable filter component that works for any entity type:
|
||||
|
||||
**Component Structure:**
|
||||
1. Accept `attributes` and `filters` as props
|
||||
2. Generate filter controls from attributes (filter `visible: true`)
|
||||
3. Handle filter changes
|
||||
4. Display active filters as chips/badges
|
||||
5. Allow removing individual filters
|
||||
|
||||
**Filter Control Generation:**
|
||||
- Generate appropriate filter UI based on `type`
|
||||
- Use localized labels
|
||||
- Support multiple operators for text/number fields
|
||||
- Update filters object and trigger refetch
|
||||
|
||||
### Key Implementation Guidelines
|
||||
|
||||
1. **Never Hardcode**
|
||||
- Never hardcode field names, labels, types, or validation rules
|
||||
- Always fetch attribute definitions from backend
|
||||
- Use attribute metadata for all UI generation
|
||||
|
||||
2. **Entity-Agnostic**
|
||||
- Same components work for all entity types
|
||||
- Pass `entityType` as parameter to determine endpoints
|
||||
- Use generic prop names (attributes, items, etc.)
|
||||
|
||||
3. **State Management**
|
||||
- Keep pagination state separate from entity data
|
||||
- Preserve filters and sort when changing pages
|
||||
- Reset to page 1 when filters/search change
|
||||
|
||||
4. **Error Handling**
|
||||
- Handle API errors gracefully
|
||||
- Display validation errors from backend
|
||||
- Show loading states during API calls
|
||||
|
||||
5. **Performance**
|
||||
- Debounce search input (300-500ms)
|
||||
- Cache attribute definitions (don't refetch on every render)
|
||||
- Use pagination to limit data fetched
|
||||
|
||||
6. **Accessibility**
|
||||
- Use semantic HTML for forms and tables
|
||||
- Provide ARIA labels for form fields
|
||||
- Ensure keyboard navigation works
|
||||
- Support screen readers
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This generic system enables:
|
||||
|
||||
- **Complete Backend-Driven UI**: All forms, tables, filters generated from backend metadata
|
||||
- **Zero Hardcoding**: No field definitions, labels, or validation rules in frontend code
|
||||
- **Automatic Updates**: New backend fields automatically appear in frontend
|
||||
- **Consistent UX**: Same patterns across all entity types
|
||||
- **Localization**: Multi-language support from backend metadata
|
||||
- **Reusable Components**: Generic components work for all entity types
|
||||
|
||||
By following these patterns, the frontend becomes a thin presentation layer that dynamically renders UI based on backend metadata, ensuring consistency, maintainability, and automatic adaptation to backend changes.
|
||||
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
# File Routes Frontend Documentation
|
||||
|
||||
This document describes customer journeys for managing files through the frontend, focusing on how users interact with file management and how the backend routes support these experiences. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Customer Journey 1: Discovering and Browsing Files](#customer-journey-1-discovering-and-browsing-files)
|
||||
3. [Customer Journey 2: Uploading Files](#customer-journey-2-uploading-files)
|
||||
4. [Customer Journey 3: Editing File Properties](#customer-journey-3-editing-file-properties)
|
||||
5. [Customer Journey 4: Downloading Files](#customer-journey-4-downloading-files)
|
||||
6. [Customer Journey 5: Adding File to Prompt](#customer-journey-5-adding-file-to-prompt)
|
||||
7. [Customer Journey 6: Deleting Files](#customer-journey-6-deleting-files)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The file routes (`/api/files`) enable users to manage files within their mandate. These routes focus on **file administration** including upload, editing, download, preview, and deletion.
|
||||
|
||||
**Key Principles:**
|
||||
- **User-Centric**: Documentation organized around what users want to accomplish
|
||||
- **Backend-Driven**: All forms, tables, and UI components generated from backend metadata
|
||||
- **No Hardcoding**: Field definitions, labels, validation rules, and options come from the backend
|
||||
- **Permission-Aware**: Backend enforces permissions; frontend handles gracefully
|
||||
- **Mandate-Scoped**: Files are managed within the context of mandates
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 1: Discovering and Browsing Files
|
||||
|
||||
### User Goal
|
||||
"I want to see all files in my mandate and find the one I'm looking for."
|
||||
|
||||
### User Story
|
||||
As a user, I want to browse files, search for specific files, filter by any field, sort them by different criteria, and quickly identify files by their name, size, type, and creation date.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Navigate to /files
|
||||
Frontend->>Backend: GET /api/attributes/FileItem
|
||||
Backend-->>Frontend: Attribute definitions (fields, labels, types)
|
||||
Frontend->>Backend: GET /api/files/list?pagination=...
|
||||
Backend-->>Frontend: Paginated files list
|
||||
Frontend->>Frontend: Generate table columns from attributes
|
||||
Frontend->>Frontend: Generate filter controls from attributes
|
||||
Frontend->>Frontend: Render files table + search + filters
|
||||
Frontend->>Frontend: Render action buttons in table (Edit, Delete, Download, Add to Prompt)
|
||||
Frontend-->>User: Display file list with search/filter UI + action buttons
|
||||
|
||||
alt User performs general search
|
||||
User->>Frontend: Type in search box (e.g., "invoice")
|
||||
Frontend->>Frontend: Update search query
|
||||
Frontend->>Backend: GET /api/files/list?pagination=...filters:{"search":"invoice"}
|
||||
Backend-->>Frontend: Filtered files list
|
||||
Frontend-->>User: Display matching files
|
||||
end
|
||||
|
||||
alt User applies field filter
|
||||
User->>Frontend: Select filter field (e.g., "MIME Type")
|
||||
Frontend->>Frontend: Show filter options from attribute metadata
|
||||
User->>Frontend: Select filter value (e.g., "application/pdf")
|
||||
Frontend->>Frontend: Update filter parameters
|
||||
Frontend->>Backend: GET /api/files/list?pagination=...filters:{"mimeType":"application/pdf"}
|
||||
Backend-->>Frontend: Filtered files list
|
||||
Frontend-->>User: Display filtered files
|
||||
end
|
||||
|
||||
alt User combines search + filter + sort
|
||||
User->>Frontend: Apply search + filter + sort
|
||||
Frontend->>Frontend: Combine all parameters
|
||||
Frontend->>Backend: GET /api/files/list?pagination=...filters:{"search":"invoice","mimeType":"application/pdf"}...sort:desc
|
||||
Backend-->>Frontend: Filtered, sorted files list
|
||||
Frontend-->>User: Display results
|
||||
end
|
||||
|
||||
User->>Frontend: Click column header (e.g., "File Size")
|
||||
Frontend->>Frontend: Update sort parameters
|
||||
Frontend->>Backend: GET /api/files/list?pagination=...sort:desc
|
||||
Backend-->>Frontend: Sorted files list
|
||||
Frontend-->>User: Display sorted files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 2: Uploading Files
|
||||
|
||||
### User Goal
|
||||
"I want to upload new files to my mandate."
|
||||
|
||||
### User Story
|
||||
As a user, I want to upload files by selecting them from my device. The system should validate file size, handle duplicates, and show clear feedback about the upload status.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
alt User clicks "Upload File" button
|
||||
User->>Frontend: Click "Upload File" button
|
||||
Frontend->>Frontend: Show file upload dialog/input
|
||||
Frontend-->>User: Display file picker
|
||||
User->>Frontend: Select file(s) from device
|
||||
else User drags and drops files
|
||||
User->>Frontend: Drag file(s) over upload area
|
||||
Frontend->>Frontend: Highlight drop zone
|
||||
User->>Frontend: Drop file(s) on upload area
|
||||
Frontend->>Frontend: Accept dropped files
|
||||
end
|
||||
|
||||
Frontend->>Frontend: Optimistic update: Show loading state, add file to UI immediately
|
||||
Frontend->>Backend: POST /api/files/upload + file data
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Remove file from UI
|
||||
Frontend-->>User: Show permission error
|
||||
else Success (201)
|
||||
Backend-->>Frontend: Created file object + duplicate info
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: GET /api/files/list?pagination=... (refetch)
|
||||
Backend-->>Frontend: Updated files list
|
||||
Frontend->>Frontend: Update table with fresh data
|
||||
Frontend-->>User: Show success message (with duplicate info if applicable)
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 3: Editing File Properties
|
||||
|
||||
### User Goal
|
||||
"I want to change the file name through an edit form."
|
||||
|
||||
### User Story
|
||||
As a user, I want to edit a file's name through a popup/modal form, with validation and clear error messages.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Edit" button in table row
|
||||
Frontend->>Backend: GET /api/files/fileId
|
||||
Backend-->>Frontend: Current file data
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/FileItem
|
||||
Backend-->>Frontend: Field definitions (editable fields)
|
||||
|
||||
Frontend->>Frontend: Filter editable fields
|
||||
Frontend->>Frontend: Generate form from attributes
|
||||
Frontend->>Frontend: Pre-populate form with file data
|
||||
Frontend->>Frontend: Open popup/modal with edit form
|
||||
Frontend-->>User: Display edit form in popup/modal
|
||||
|
||||
User->>Frontend: Modify file name
|
||||
User->>Frontend: Click "Save"
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types)
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors in modal
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Optimistic update: Apply changes to UI immediately
|
||||
Frontend->>Backend: PUT /api/files/fileId + form data
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error in modal
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show backend validation errors in modal
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Updated file
|
||||
Frontend->>Frontend: Keep optimistic update (or refresh from response)
|
||||
Frontend->>Backend: GET /api/files/list?pagination=... (refetch)
|
||||
Backend-->>Frontend: Updated files list
|
||||
Frontend->>Frontend: Update table with fresh data
|
||||
Frontend->>Frontend: Close popup/modal
|
||||
Frontend-->>User: Show updated file name in table
|
||||
end
|
||||
end
|
||||
|
||||
alt User clicks Cancel
|
||||
User->>Frontend: Click "Cancel" button
|
||||
Frontend->>Frontend: Close popup/modal without saving
|
||||
Frontend-->>User: Modal closed, no changes
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 4: Downloading Files
|
||||
|
||||
### User Goal
|
||||
"I want to download files to my device."
|
||||
|
||||
### User Story
|
||||
As a user, I want to download files by clicking a download button, so I can save them locally on my device.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Download" button in table
|
||||
Frontend->>Frontend: Show loading state
|
||||
Frontend->>Backend: GET /api/files/fileId/download
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend-->>User: Show permission error
|
||||
else File not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: File content with Content-Disposition header
|
||||
Frontend->>Frontend: Trigger browser download
|
||||
Frontend-->>User: File downloads to device
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 5: Adding File to Prompt
|
||||
|
||||
### User Goal
|
||||
"I want to use a file with a prompt in the chat playground."
|
||||
|
||||
### User Story
|
||||
As a user, I want to add a file to the chat playground so I can use it with a prompt to start a workflow.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Add to Prompt" button in table
|
||||
Frontend->>Backend: GET /api/files/fileId
|
||||
Backend-->>Frontend: File object (id, fileName)
|
||||
|
||||
Frontend->>Frontend: Navigate to /chat-playground
|
||||
Frontend->>Frontend: Pre-select file in file picker/attachments
|
||||
Frontend->>Frontend: Show file name as context
|
||||
Frontend-->>User: Display chat playground with file attached
|
||||
|
||||
User->>Frontend: Enter prompt text
|
||||
User->>Frontend: Optionally add more files
|
||||
User->>Frontend: Click "Start" or "Send" button
|
||||
|
||||
Frontend->>Frontend: Validate user input (prompt required)
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Backend: POST /api/chat/playground/start?workflowMode=Dynamic<br/>Body: {prompt: userPrompt, listFileId: [fileId, ...], userLanguage: "en"}
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend-->>User: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Created workflow object
|
||||
Frontend->>Frontend: Update chat playground UI with workflow
|
||||
Frontend-->>User: Show workflow started, display messages/logs
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 6: Deleting Files
|
||||
|
||||
### User Goal
|
||||
"I want to delete files that are no longer needed."
|
||||
|
||||
### User Story
|
||||
As a user, I want to delete files with a clear confirmation to prevent accidental deletion.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Delete" button
|
||||
Frontend->>Backend: GET /api/files/fileId
|
||||
Backend-->>Frontend: File data (for name)
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Delete 'File Name'?"
|
||||
User->>Frontend: Cancel deletion
|
||||
Frontend-->>User: Dialog closed, no action
|
||||
|
||||
User->>Frontend: Click "Delete" button again
|
||||
Frontend->>Backend: GET /api/files/fileId
|
||||
Backend-->>Frontend: File data
|
||||
Frontend->>Frontend: Show confirmation dialog
|
||||
User->>Frontend: Confirm deletion
|
||||
Frontend->>Frontend: Optimistic update: Remove file from UI immediately
|
||||
Frontend->>Backend: DELETE /api/files/fileId
|
||||
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Restore file in UI
|
||||
Frontend-->>User: Show permission error
|
||||
else Not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update: Restore file in UI
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend->>Frontend: Keep optimistic update (file already removed)
|
||||
Frontend->>Frontend: Show success message
|
||||
Frontend->>Frontend: Navigate to /files (if on detail page)
|
||||
Frontend-->>User: Display file list (without deleted file)
|
||||
end
|
||||
```
|
||||
|
||||
|
|
@ -1,574 +0,0 @@
|
|||
# File Page Requirements
|
||||
|
||||
This document contains the complete frontend requirements for all file management pages and components. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Page Structure and Layout](#page-structure-and-layout)
|
||||
3. [User Interactions and Functionality](#user-interactions-and-functionality)
|
||||
4. [Backend Routes and API Integration](#backend-routes-and-api-integration)
|
||||
5. [Field and Attribute Reference](#field-and-attribute-reference)
|
||||
6. [Dynamic Rendering Guidelines](#dynamic-rendering-guidelines)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The file management page enables users to manage files within their mandate. The frontend consists of a single page (`/files`) with different views/states:
|
||||
|
||||
- **List View** - Browse, search, filter, and sort files (all information displayed in table)
|
||||
- **Edit View** - Edit file properties in popup/modal
|
||||
- **Upload View** - Upload new files
|
||||
|
||||
All views use backend-driven UI generation, meaning field definitions, labels, validation rules, and UI structure come entirely from backend metadata through the `/api/attributes/FileItem` endpoint. Views are managed through component state and routing within the same page, not as separate routes.
|
||||
|
||||
---
|
||||
|
||||
## Page Structure and Layout
|
||||
|
||||
### File Management Page (`/files`)
|
||||
|
||||
The file management page uses different views/states to handle different user interactions. Views are managed through component state and routing within the same page component.
|
||||
|
||||
### List View
|
||||
|
||||
**State:** `view === 'list'` or `selectedFileId === null`
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Main Content Area:**
|
||||
- Table or card grid displaying all files in the mandate
|
||||
- Each file row/card shows: file name, MIME type, file size, creation date, mandate ID
|
||||
- **Action buttons column** in table (rendered on first load):
|
||||
- "Edit" button - opens popup/modal edit form for file name
|
||||
- "Delete" button - shows confirmation dialog and deletes file
|
||||
- "Download" button - downloads file to user's device
|
||||
- "Add to Prompt" button - navigates to `/chat-playground` with file attached
|
||||
- All necessary file information displayed in table (no detail view needed)
|
||||
|
||||
- **Search and Filter Controls:**
|
||||
- General search input box (searches across all text fields)
|
||||
- Field-specific filter controls (one filter per visible field)
|
||||
- Active filter indicators (chips/badges showing applied filters)
|
||||
- "Clear all filters" button when filters are active
|
||||
|
||||
- **Sorting Controls:**
|
||||
- Clickable column headers with sort indicators (up/down arrows)
|
||||
- Visual indication of current sort field and direction
|
||||
- Support for multi-level sorting
|
||||
|
||||
- **Pagination Controls:**
|
||||
- Page information display ("Page X of Y", "Showing 1-20 of 45 files")
|
||||
- Previous/Next page buttons
|
||||
- Page number buttons
|
||||
- Page size selector (10, 20, 50, 100 items per page)
|
||||
|
||||
- **Action Buttons:**
|
||||
- "Upload File" button - opens file upload dialog
|
||||
|
||||
### Edit View (Popup/Modal)
|
||||
|
||||
**State:** `view === 'edit'` and `selectedFileId !== null` (popup/modal overlay)
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Edit Form in Popup/Modal:**
|
||||
- Dynamic form with editable file fields only (typically fileName)
|
||||
- Field labels with required indicators (asterisk for required fields)
|
||||
- Help text/tooltips from field descriptions
|
||||
- Input validation errors displayed inline
|
||||
- Form pre-populated with current file values
|
||||
- Save and Cancel buttons in modal footer
|
||||
- On save: Updates file name, closes modal, refreshes table
|
||||
- On cancel: Closes modal without saving
|
||||
|
||||
### Upload View
|
||||
|
||||
**State:** `view === 'upload'` or upload dialog open
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Upload Dialog/Form:**
|
||||
- File input/picker for selecting files
|
||||
- Drag-and-drop area with visual feedback
|
||||
- Selected files list with preview
|
||||
- Upload progress indicator
|
||||
- Duplicate file handling information
|
||||
|
||||
- **Action Buttons:**
|
||||
- Upload button (starts upload)
|
||||
- Cancel button (closes upload dialog)
|
||||
|
||||
- **Confirmation Dialogs:**
|
||||
- Delete confirmation dialog (on file detail page)
|
||||
|
||||
---
|
||||
|
||||
## User Interactions and Functionality
|
||||
|
||||
### Browsing and Discovery
|
||||
|
||||
**Search Functionality:**
|
||||
- User types in general search box
|
||||
- Frontend debounces input (300-500ms delay)
|
||||
- Search applies across all text fields in file objects
|
||||
- Results update automatically
|
||||
- Page resets to page 1 when search changes
|
||||
|
||||
**Field Filtering:**
|
||||
- User selects field to filter by
|
||||
- Frontend shows appropriate filter UI based on field type:
|
||||
- Text fields → Text input with operator selection (contains, equals, startsWith, endsWith)
|
||||
- Integer fields → Number input with comparison operators (gt, gte, lt, lte) or range inputs
|
||||
- Timestamp fields → Date picker with comparison operators or date range picker
|
||||
- Select fields → Dropdown with options from backend metadata
|
||||
- User applies filter → Filter appears as active chip/badge
|
||||
- User can remove individual filters
|
||||
- Multiple filters work together (AND logic)
|
||||
- Page resets to page 1 when filters change
|
||||
|
||||
**Sorting:**
|
||||
- User clicks column header
|
||||
- Sort direction toggles (asc → desc → remove)
|
||||
- Multiple columns can be sorted (multi-level sorting)
|
||||
- Sort indicators show on column headers
|
||||
- All filters and search preserved when sorting
|
||||
|
||||
**Pagination:**
|
||||
- User clicks page number or navigation button
|
||||
- Page updates and data refetches
|
||||
- All filters, search, and sort preserved
|
||||
- User changes page size → Page resets to 1, data refetches
|
||||
|
||||
**View Switching:**
|
||||
- All file information displayed in table (no detail view navigation needed)
|
||||
|
||||
### Uploading Files
|
||||
|
||||
**Upload Interaction:**
|
||||
- User clicks "Upload File" button → Opens upload dialog/form
|
||||
- OR user drags and drops file(s) onto upload area
|
||||
- Drag and drop area highlights when files are dragged over
|
||||
- User selects file(s) from device picker OR drops file(s) on upload area
|
||||
- File upload starts immediately (no size validation)
|
||||
- User clicks Cancel → Closes upload dialog, no action
|
||||
|
||||
**Drag and Drop Support:**
|
||||
- Upload area supports drag and drop functionality
|
||||
- Visual feedback when files are dragged over (highlight drop zone)
|
||||
- Multiple files can be dropped at once
|
||||
- Files are accepted and uploaded immediately upon drop
|
||||
|
||||
**Upload Submission:**
|
||||
- File sent as multipart/form-data
|
||||
- Optional `workflowId` parameter can be included
|
||||
- No file size validation (all file sizes accepted)
|
||||
- Success → File appears in list, show success message with duplicate info if applicable
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
**Duplicate Handling:**
|
||||
- Backend returns duplicate information in response
|
||||
- Frontend displays appropriate message:
|
||||
- Exact duplicate → "File already exists with identical content. Reusing existing file."
|
||||
- Name conflict → "File already exists with different content. Uploaded as '[new name]'."
|
||||
- New file → "File uploaded successfully"
|
||||
|
||||
### Editing Files
|
||||
|
||||
**Edit Interaction:**
|
||||
- User clicks "Edit" button in table row → Opens popup/modal edit form
|
||||
- Form pre-populated with current file values
|
||||
- User modifies file name in form
|
||||
- Client-side validation shows errors immediately
|
||||
- User clicks Save → Form validates and submits
|
||||
- User clicks Cancel → Closes modal without saving
|
||||
|
||||
**Form Validation:**
|
||||
- Required field validation (shows error if empty)
|
||||
- Type validation (e.g., text fields must be valid text)
|
||||
- Validation errors displayed inline below fields
|
||||
|
||||
**Form Submission:**
|
||||
- Only changed fields sent (or all form data)
|
||||
- Success → Close popup/modal, refresh table, show success message
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error in modal
|
||||
- 400 (validation errors) → Display backend validation errors in modal
|
||||
- Other errors → Show generic error message in modal
|
||||
|
||||
### Downloading Files
|
||||
|
||||
**Download Action:**
|
||||
- User clicks "Download" button in table
|
||||
- Frontend shows loading state
|
||||
- Frontend calls `GET /api/files/{fileId}/download`
|
||||
- Browser triggers download with proper filename
|
||||
- Success → File downloads to user's device
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 404 (not found) → Show not found error
|
||||
- Other errors → Show generic error message
|
||||
|
||||
**Implementation Notes:**
|
||||
- Download should use proper Content-Disposition header handling
|
||||
- Filename should be properly encoded for Unicode characters
|
||||
|
||||
### Adding File to Prompt
|
||||
|
||||
**Add to Prompt Action:**
|
||||
- User clicks "Add to Prompt" button in table
|
||||
- Frontend fetches file data to get file ID
|
||||
- Frontend navigates to `/chat-playground` page
|
||||
- Frontend pre-selects the file in the file picker/attachments area
|
||||
- Frontend displays file name as context
|
||||
- User can enter prompt text and optionally add more files
|
||||
- User clicks "Start" or "Send" button
|
||||
- Frontend calls `POST /api/chat/playground/start` with file ID in `listFileId` array
|
||||
|
||||
**Add to Prompt Submission:**
|
||||
- File ID included in `listFileId` array in request body
|
||||
- Prompt text required (user must enter prompt)
|
||||
- Success → Workflow starts with file attached, chat playground updates
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Deleting Files
|
||||
|
||||
**Delete Action:**
|
||||
- User clicks delete button (on detail page or list)
|
||||
- Confirmation dialog appears with file name
|
||||
- Warning about permanent deletion
|
||||
- User confirms → File deleted
|
||||
- User cancels → Dialog closes, no action
|
||||
|
||||
**Delete Success Handling:**
|
||||
- Success message displayed
|
||||
- Remove file from list or refresh table
|
||||
|
||||
---
|
||||
|
||||
## Backend Routes and API Integration
|
||||
|
||||
### Complete Route Reference
|
||||
|
||||
All backend routes used by file pages:
|
||||
|
||||
| Route | Method | Purpose | When Used | Access Control |
|
||||
|-------|--------|---------|-----------|----------------|
|
||||
| `/api/files/list` | GET | Get all files (with pagination) | Initial page load, pagination changes, sort changes, filter changes, search changes | All authenticated users (filtered by mandate) |
|
||||
| `/api/files/{fileId}` | GET | Get file details | Detail page load, edit page load | All authenticated users |
|
||||
| `/api/files/{fileId}` | PUT | Update file | Edit form submission | All authenticated users |
|
||||
| `/api/files/{fileId}` | DELETE | Delete file | User confirms deletion | All authenticated users |
|
||||
| `/api/files/upload` | POST | Upload file | User selects and uploads file | All authenticated users |
|
||||
| `/api/files/{fileId}/download` | GET | Download file | User clicks download button | All authenticated users |
|
||||
| `/api/files/stats` | GET | Get file statistics | Optional: display statistics on page | All authenticated users |
|
||||
| `/api/chat/playground/start` | POST | Start workflow with file | User clicks "Add to Prompt" and starts workflow | All authenticated users |
|
||||
| `/api/attributes/FileItem` | GET | Get field definitions | Page load (once per page) - used to generate all UI components | All authenticated users |
|
||||
|
||||
### API Request Patterns
|
||||
|
||||
**Pagination Request:**
|
||||
```
|
||||
GET /api/files/list?pagination={"page":1,"pageSize":20,"sort":[],"filters":null}
|
||||
```
|
||||
- `pagination` parameter is JSON-encoded string
|
||||
- If user wants all files: omit `pagination` parameter entirely
|
||||
- Filters structure: `{"search":"query","fieldName":"value",...}`
|
||||
- Optional `mandateId` query parameter to filter by mandate
|
||||
|
||||
**Upload Request:**
|
||||
```
|
||||
POST /api/files/upload
|
||||
Content-Type: multipart/form-data
|
||||
Body: {
|
||||
file: <file data>,
|
||||
workflowId: "optional-workflow-id" (optional)
|
||||
}
|
||||
```
|
||||
- File sent as multipart/form-data
|
||||
- `workflowId` is optional form field
|
||||
- No file size limits (all sizes accepted)
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
- Response includes duplicate information:
|
||||
```json
|
||||
{
|
||||
"message": "File uploaded successfully",
|
||||
"file": {...},
|
||||
"duplicateType": "new_file" | "exact_duplicate" | "name_conflict",
|
||||
"originalFileName": "...",
|
||||
"storedFileName": "...",
|
||||
"isDuplicate": false
|
||||
}
|
||||
```
|
||||
|
||||
**Update Request:**
|
||||
```
|
||||
PUT /api/files/{fileId}
|
||||
Content-Type: application/json
|
||||
Body: {fieldName: value, ...}
|
||||
```
|
||||
- Send only changed fields or all form data
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Download Request:**
|
||||
```
|
||||
GET /api/files/{fileId}/download
|
||||
```
|
||||
- Returns file content with Content-Disposition header
|
||||
- Browser automatically triggers download
|
||||
- Filename properly encoded for Unicode characters
|
||||
|
||||
**Add to Prompt Request:**
|
||||
```
|
||||
POST /api/chat/playground/start?workflowMode=Dynamic
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"prompt": "User prompt text here...",
|
||||
"listFileId": ["fileId1", "fileId2"],
|
||||
"userLanguage": "en"
|
||||
}
|
||||
```
|
||||
- `workflowMode` query parameter is required: "Actionplan", "Dynamic", or "Template" (default: "Dynamic")
|
||||
- `listFileId` array contains file IDs to attach (includes the file from "Add to Prompt" button)
|
||||
- `prompt` field contains user's prompt text
|
||||
- `userLanguage` should match user's preferred language
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Delete Requests:**
|
||||
```
|
||||
DELETE /api/files/{fileId}
|
||||
```
|
||||
- Delete operation requires user confirmation
|
||||
- Handle 403 (permission denied) and 404 (not found) gracefully
|
||||
|
||||
### Response Handling
|
||||
|
||||
**Paginated Response:**
|
||||
```json
|
||||
{
|
||||
"items": [...],
|
||||
"pagination": {
|
||||
"currentPage": 1,
|
||||
"pageSize": 20,
|
||||
"totalItems": 45,
|
||||
"totalPages": 3,
|
||||
"sort": [...],
|
||||
"filters": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- 403 Forbidden → Show permission error message
|
||||
- 404 Not Found → Show "not found" error message
|
||||
- 400 Bad Request → Display validation errors from response
|
||||
- 500 Internal Server Error → Show generic error message
|
||||
|
||||
---
|
||||
|
||||
## Field and Attribute Reference
|
||||
|
||||
### Complete Field List
|
||||
|
||||
The following is a comprehensive list of all parameters, attributes, and fields that will be displayed for files. All of these are provided by the backend through the `/api/attributes/FileItem` endpoint and should never be hardcoded in the frontend.
|
||||
|
||||
#### Core File Fields
|
||||
|
||||
**Identification Fields:**
|
||||
- `id` - Unique file identifier (text, readonly, not required, visible)
|
||||
- `mandateId` - ID of the mandate this file belongs to (text, readonly, not required, visible)
|
||||
|
||||
**File Properties:**
|
||||
- `fileName` - Name of the file (text, editable, required, visible)
|
||||
- `mimeType` - MIME type of the file (text, readonly, not required, visible)
|
||||
- `fileHash` - Hash of the file (text, readonly, not required, visible)
|
||||
- `fileSize` - Size of the file in bytes (integer, readonly, not required, visible)
|
||||
- `creationDate` - Date when the file was created (timestamp, readonly, not required, visible)
|
||||
|
||||
### Pagination Parameters
|
||||
|
||||
**Request Parameters (`PaginationParams`):**
|
||||
- `page` - Current page number (1-based, minimum 1)
|
||||
- `pageSize` - Number of items per page (minimum 1, maximum 1000)
|
||||
- `sort` - Array of sort field configurations
|
||||
- Each sort field contains:
|
||||
- `field` - Field name to sort by (must match a file field name)
|
||||
- `direction` - Sort direction: "asc" or "desc"
|
||||
- `filters` - Filter criteria dictionary
|
||||
- `search` - General search term (searches across all text fields, case-insensitive)
|
||||
- Field-specific filters: `{fieldName: value}` or `{fieldName: {operator: "operator", value: value}}`
|
||||
- Supported operators: "equals", "contains", "startsWith", "endsWith", "gt", "gte", "lt", "lte", "in", "notIn"
|
||||
|
||||
**Response Metadata (`PaginationMetadata`):**
|
||||
- `currentPage` - Current page number (1-based)
|
||||
- `pageSize` - Number of items per page
|
||||
- `totalItems` - Total number of items across all pages (after filters applied)
|
||||
- `totalPages` - Total number of pages (calculated from totalItems / pageSize)
|
||||
- `sort` - Current sort configuration applied (array of SortField objects)
|
||||
- `filters` - Current filters applied (mirrors request filters)
|
||||
|
||||
### Attribute Definition Structure
|
||||
|
||||
Each field returned from `/api/attributes/FileItem` contains:
|
||||
|
||||
- `name` - Field name (e.g., "fileName", "mimeType", "fileSize", "id")
|
||||
- `type` - Field data type (e.g., "text", "integer", "timestamp", "checkbox")
|
||||
- `label` - Localized field label (object with language keys: {"en": "English Label", "fr": "French Label"})
|
||||
- `description` - Field description text
|
||||
- `required` - Boolean indicating if field is required
|
||||
- `readonly` - Boolean indicating if field is read-only
|
||||
- `editable` - Boolean indicating if field can be edited (inverse of readonly)
|
||||
- `visible` - Boolean indicating if field should be displayed in UI
|
||||
- `options` - Array of options for select fields (each option has `value` and localized `label`)
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Rendering Guidelines
|
||||
|
||||
The frontend must render all UI components dynamically based on backend metadata. No field definitions, labels, validation rules, or UI structure should be hardcoded.
|
||||
|
||||
### Table Column Generation
|
||||
|
||||
When rendering the file list table:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/FileItem`
|
||||
2. Filter attributes where `visible: true` to determine which columns to display
|
||||
3. Use `label` property for column headers (select appropriate language based on user preference)
|
||||
4. Use `type` property to determine how to format cell values:
|
||||
- `text` fields → Display as plain text
|
||||
- `integer` fields → Display as formatted number (for fileSize, show human-readable format like "1.5 MB")
|
||||
- `timestamp` fields → Format as relative time ("2 hours ago") or absolute date/time based on user preference
|
||||
- `select` fields → Display value using label from options array (match value to option.value, then display option.label)
|
||||
- `checkbox` fields → Display as checkmark icon or "Yes"/"No" text
|
||||
5. Use `readonly` property to determine if column should be sortable (readonly fields may still be sortable, but editable fields definitely are)
|
||||
6. Generate click handlers for column headers to update sort parameters and refetch data
|
||||
7. **Add Actions Column:**
|
||||
- Render action buttons in each table row on first load
|
||||
- Include "Edit" button - opens popup/modal edit form for file name
|
||||
- Include "Delete" button - shows confirmation dialog
|
||||
- Include "Download" button - triggers file download
|
||||
- Include "Add to Prompt" button - navigates to `/chat-playground` with file attached
|
||||
- Buttons should be visible and accessible in each row
|
||||
|
||||
### Filter Control Generation
|
||||
|
||||
When rendering filter controls:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/FileItem`
|
||||
2. Filter attributes where `visible: true` to determine which filters to show
|
||||
3. For each visible field, generate appropriate filter UI based on `type`:
|
||||
- `text` fields → Text input filter (supports "contains", "equals", "startsWith", "endsWith" operators)
|
||||
- `integer` fields → Number range filter (min/max inputs) or single number input with comparison operators (gt, gte, lt, lte)
|
||||
- `timestamp` fields → Date range picker or single date picker with comparison operators
|
||||
- `select` fields → Dropdown filter with options from `options` array (use localized labels)
|
||||
- `checkbox` fields → Boolean toggle filter (true/false/any)
|
||||
4. Use `label` property for filter labels (localized)
|
||||
5. When user applies filter, update `filters` object in pagination parameters and refetch data
|
||||
6. Display active filters as chips/badges showing field label and value
|
||||
7. Allow removing individual filters by removing them from `filters` object
|
||||
|
||||
### Search Implementation
|
||||
|
||||
For general search functionality:
|
||||
|
||||
1. Display a single search input box (not field-specific)
|
||||
2. When user types, update `filters.search` in pagination parameters
|
||||
3. Debounce search input (wait 300-500ms after user stops typing before sending request)
|
||||
4. Search applies across all text fields in the file object
|
||||
5. Reset to page 1 when search query changes
|
||||
6. Combine search with field-specific filters (both are in the `filters` object)
|
||||
|
||||
### Form Field Generation
|
||||
|
||||
When rendering edit forms:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/FileItem`
|
||||
2. Filter attributes where `visible: true` AND `editable: true` to determine which fields to show
|
||||
3. For each editable field, generate appropriate form input based on `type`:
|
||||
- `text` fields → Text input
|
||||
- `integer` fields → Number input with appropriate min/max constraints
|
||||
- `timestamp` fields → Date/time picker
|
||||
- `select` fields → Dropdown/select input with options from `options` array (use localized labels)
|
||||
- `checkbox` fields → Checkbox input
|
||||
4. Use `label` property for field labels (localized)
|
||||
5. Use `required` property to show required indicators (asterisk, etc.)
|
||||
6. Use `description` property to show help text or tooltips
|
||||
7. Validate form before submission:
|
||||
- Check all `required: true` fields have values
|
||||
- Validate types (e.g., integer fields must be numbers)
|
||||
- Validate select fields (value must be in options array)
|
||||
8. On submit, send only changed fields or all form data to update endpoint
|
||||
|
||||
### Display Formatting
|
||||
|
||||
When displaying field values:
|
||||
|
||||
1. Use `type` property to determine formatting:
|
||||
- `text` → Display as-is (may need HTML escaping)
|
||||
- `integer` → Format with thousand separators if needed
|
||||
- For `fileSize` field specifically → Format as human-readable (e.g., "1.5 MB", "500 KB", "2.3 GB")
|
||||
- `timestamp` → Format as relative time or absolute date/time
|
||||
- `select` → Look up value in `options` array and display localized label
|
||||
- `checkbox` → Display as checkmark icon or "Yes"/"No" text
|
||||
2. Handle `null` or `undefined` values gracefully (show "-" or "Not set")
|
||||
3. Use `readonly` property to determine if field should show edit indicators
|
||||
4. Special formatting for file fields:
|
||||
- File Size → Always show in human-readable format (bytes → KB → MB → GB)
|
||||
- MIME Type → Display as badge with icon if possible
|
||||
- File Hash → Display as truncated hash (first 8-12 characters) with copy button
|
||||
- Creation Date → Show relative time in list view, absolute date in detail view
|
||||
|
||||
### File Size Formatting
|
||||
|
||||
For the `fileSize` field (integer in bytes), always format as human-readable:
|
||||
|
||||
```typescript
|
||||
function formatFileSize(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
||||
}
|
||||
```
|
||||
|
||||
### Localization
|
||||
|
||||
All labels and options support multiple languages:
|
||||
|
||||
1. Use user's preferred language (from user settings or browser locale)
|
||||
2. Access localized labels from `label` object: `label[userLanguage]` or `label.en` as fallback
|
||||
3. For select options, use `option.label[userLanguage]` or `option.label.en` as fallback
|
||||
4. If label for current language is missing, fall back to English
|
||||
|
||||
### Key Principles
|
||||
|
||||
- Never hardcode field names, labels, types, or validation rules
|
||||
- Always fetch attribute definitions from backend before rendering UI
|
||||
- Use attribute metadata to determine what to display and how to display it
|
||||
- Support all field types dynamically - if backend adds new types, frontend should handle them
|
||||
- Respect `visible`, `editable`, `readonly`, and `required` flags from backend
|
||||
- Use localized labels from backend metadata
|
||||
- Generate filters, forms, and tables entirely from attribute definitions
|
||||
- When backend adds new fields, frontend should automatically display them without code changes
|
||||
- Handle all error cases gracefully (403, 404, 400, 413, 500)
|
||||
- Provide user feedback for all actions (loading states, success messages, error messages)
|
||||
- Format file sizes in human-readable format
|
||||
- Handle file uploads with proper duplicate detection feedback
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This document provides complete frontend requirements for all file management pages and components. All requirements follow the principle of **backend-driven UI generation**—no hardcoding of field definitions, labels, or validation rules. The frontend should dynamically generate all UI components from backend metadata provided through the `/api/attributes/FileItem` endpoint.
|
||||
|
||||
**Key Architecture Pattern:** The file management interface is a single page (`/files`) with different views managed through component state. All interactions happen within the same page component without separate routes.
|
||||
|
||||
For generic patterns that apply across all entity types (not just files), see the [Dynamic Forms and Pagination documentation](./dynamic-forms-and-pagination.md).
|
||||
|
||||
|
|
@ -1,349 +0,0 @@
|
|||
# Prompt Routes Frontend Documentation
|
||||
|
||||
This document describes customer journeys for managing prompts through the frontend, focusing on how users interact with prompt management and how the backend routes support these experiences. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Customer Journey 1: Discovering and Browsing Prompts](#customer-journey-1-discovering-and-browsing-prompts)
|
||||
3. [Customer Journey 2: Viewing Prompt Details](#customer-journey-2-viewing-prompt-details)
|
||||
4. [Customer Journey 3: Creating New Prompts](#customer-journey-3-creating-new-prompts)
|
||||
5. [Customer Journey 4: Editing Prompt Properties](#customer-journey-4-editing-prompt-properties)
|
||||
6. [Customer Journey 5: Starting a Prompt Workflow](#customer-journey-5-starting-a-prompt-workflow)
|
||||
7. [Customer Journey 6: Deleting Prompts](#customer-journey-6-deleting-prompts)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The prompt routes (`/api/prompts`) enable users to manage prompts within their mandate. These routes focus on **prompt administration** including creation, editing, and deletion.
|
||||
|
||||
**Key Principles:**
|
||||
- **User-Centric**: Documentation organized around what users want to accomplish
|
||||
- **Backend-Driven**: All forms, tables, and UI components generated from backend metadata
|
||||
- **No Hardcoding**: Field definitions, labels, validation rules, and options come from the backend
|
||||
- **Permission-Aware**: Backend enforces permissions; frontend handles gracefully
|
||||
- **Mandate-Scoped**: Prompts are managed within the context of mandates
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 1: Discovering and Browsing Prompts
|
||||
|
||||
### User Goal
|
||||
"I want to see all prompts in my mandate and find the one I'm looking for."
|
||||
|
||||
### User Story
|
||||
As a user, I want to browse prompts, search for specific prompts, filter by any field, sort them by different criteria, and quickly identify prompts by their name and content.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Navigate to /prompts
|
||||
Frontend->>Backend: GET /api/attributes/Prompt
|
||||
Backend-->>Frontend: Attribute definitions (fields, labels, types)
|
||||
Frontend->>Backend: GET /api/prompts/?pagination=...
|
||||
Backend-->>Frontend: Paginated prompts list
|
||||
Frontend->>Frontend: Generate table columns from attributes
|
||||
Frontend->>Frontend: Generate filter controls from attributes
|
||||
Frontend->>Frontend: Render prompts table + search + filters
|
||||
Frontend->>Frontend: Render action buttons in table (Start Prompt, Edit, Delete)
|
||||
Frontend-->>User: Display prompt list with search/filter UI + action buttons
|
||||
|
||||
alt User performs general search
|
||||
User->>Frontend: Type in search box (e.g., "invoice")
|
||||
Frontend->>Frontend: Update search query
|
||||
Frontend->>Backend: GET /api/prompts/?pagination=...filters:{"search":"invoice"}
|
||||
Backend-->>Frontend: Filtered prompts list
|
||||
Frontend-->>User: Display matching prompts
|
||||
end
|
||||
|
||||
alt User applies field filter
|
||||
User->>Frontend: Select filter field (e.g., "Name")
|
||||
Frontend->>Frontend: Show filter options from attribute metadata
|
||||
User->>Frontend: Select filter value (e.g., "Invoice")
|
||||
Frontend->>Frontend: Update filter parameters
|
||||
Frontend->>Backend: GET /api/prompts/?pagination=...filters:{"name":"Invoice"}
|
||||
Backend-->>Frontend: Filtered prompts list
|
||||
Frontend-->>User: Display filtered prompts
|
||||
end
|
||||
|
||||
alt User combines search + filter + sort
|
||||
User->>Frontend: Apply search + filter + sort
|
||||
Frontend->>Frontend: Combine all parameters
|
||||
Frontend->>Backend: GET /api/prompts/?pagination=...filters:{"search":"invoice","name":"Invoice"}...sort:desc
|
||||
Backend-->>Frontend: Filtered, sorted prompts list
|
||||
Frontend-->>User: Display results
|
||||
end
|
||||
|
||||
User->>Frontend: Click column header (e.g., "Name")
|
||||
Frontend->>Frontend: Update sort parameters
|
||||
Frontend->>Backend: GET /api/prompts/?pagination=...sort:desc
|
||||
Backend-->>Frontend: Sorted prompts list
|
||||
Frontend-->>User: Display sorted prompts
|
||||
|
||||
User->>Frontend: Click prompt row
|
||||
Frontend->>Frontend: Navigate to /prompts/promptId
|
||||
Frontend-->>User: Show prompt detail page
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 2: Viewing Prompt Details
|
||||
|
||||
### User Goal
|
||||
"I want to see everything about a specific prompt—its name and full content."
|
||||
|
||||
### User Story
|
||||
As a user, I want to open a prompt and see its complete information, including name, content, and mandate association.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click prompt from list
|
||||
Frontend->>Backend: GET /api/prompts/promptId
|
||||
Backend-->>Frontend: Prompt object
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/Prompt
|
||||
Backend-->>Frontend: Field definitions
|
||||
|
||||
Frontend->>Frontend: Generate UI from metadata
|
||||
Frontend->>Frontend: Render prompt info section
|
||||
Frontend->>Frontend: Separate editable vs read-only fields
|
||||
Frontend-->>User: Display complete prompt view
|
||||
|
||||
alt User has editable fields and permission
|
||||
Frontend->>Frontend: Show "Edit Prompt" button
|
||||
end
|
||||
|
||||
alt User has permission
|
||||
Frontend->>Frontend: Show "Delete Prompt" button
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 3: Creating New Prompts
|
||||
|
||||
### User Goal
|
||||
"I want to create new prompts for my mandate."
|
||||
|
||||
### User Story
|
||||
As a user, I want to create a new prompt by filling out a form with its name and content. The form should validate all required fields and show clear error messages.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Create Prompt" button
|
||||
Frontend->>Frontend: Navigate to /prompts/create
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/Prompt
|
||||
Backend-->>Frontend: Field definitions (editable fields)
|
||||
|
||||
Frontend->>Frontend: Filter editable fields
|
||||
Frontend->>Frontend: Generate form from attributes
|
||||
Frontend-->>User: Display create prompt form
|
||||
|
||||
User->>Frontend: Fill in form fields
|
||||
User->>Frontend: Click "Create"
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types)
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Optimistic update: Show loading state, prepare UI for new prompt
|
||||
Frontend->>Backend: POST /api/prompts + form data
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Show form with error
|
||||
Frontend-->>User: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update: Show form with errors
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Created prompt object
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: GET /api/prompts/promptId (refetch)
|
||||
Backend-->>Frontend: Created prompt object
|
||||
Frontend->>Frontend: Update UI with fresh data
|
||||
Frontend->>Frontend: Navigate to prompt detail page
|
||||
Frontend-->>User: Show created prompt
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 4: Editing Prompt Properties
|
||||
|
||||
### User Goal
|
||||
"I want to change prompt settings like its name or content."
|
||||
|
||||
### User Story
|
||||
As a user, I want to edit a prompt's properties through a form that only shows fields I'm allowed to edit, with validation and clear error messages.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Edit" button
|
||||
Frontend->>Frontend: Navigate to /prompts/id/edit
|
||||
|
||||
Frontend->>Backend: GET /api/prompts/promptId
|
||||
Backend-->>Frontend: Current prompt data
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/Prompt
|
||||
Backend-->>Frontend: Field definitions (editable fields)
|
||||
|
||||
Frontend->>Frontend: Filter editable fields
|
||||
Frontend->>Frontend: Generate form from attributes
|
||||
Frontend->>Frontend: Pre-populate form with prompt data
|
||||
Frontend-->>User: Display edit form
|
||||
|
||||
User->>Frontend: Modify form fields
|
||||
User->>Frontend: Click "Save"
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types)
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Optimistic update: Apply changes to UI immediately
|
||||
Frontend->>Backend: PUT /api/prompts/promptId + form data
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Updated prompt
|
||||
Frontend->>Frontend: Keep optimistic update (or refresh from response)
|
||||
Frontend->>Backend: GET /api/prompts/promptId (refetch)
|
||||
Backend-->>Frontend: Updated prompt object
|
||||
Frontend->>Frontend: Update UI with fresh data
|
||||
Frontend->>Frontend: Navigate to detail page
|
||||
Frontend-->>User: Show updated prompt
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 5: Starting a Prompt Workflow
|
||||
|
||||
### User Goal
|
||||
"I want to start a workflow using a prompt I've created."
|
||||
|
||||
### User Story
|
||||
As a user, I want to start a workflow in the chat playground using a prompt's content, so I can execute the prompt and see the results.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Start Prompt" button in table
|
||||
Frontend->>Backend: GET /api/prompts/promptId
|
||||
Backend-->>Frontend: Prompt object (name, content)
|
||||
|
||||
Frontend->>Frontend: Navigate to /chat-playground
|
||||
Frontend->>Frontend: Pre-fill prompt content in chat input
|
||||
Frontend->>Frontend: Show prompt name as context
|
||||
Frontend-->>User: Display chat playground with prompt loaded
|
||||
|
||||
User->>Frontend: Optionally modify prompt or add files
|
||||
User->>Frontend: Click "Start" or "Send" button
|
||||
|
||||
Frontend->>Frontend: Validate user input
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Backend: POST /api/chat/playground/start?workflowMode=Dynamic<br/>Body: {prompt: promptContent, listFileId: [...], userLanguage: "en"}
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend-->>User: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Created workflow object
|
||||
Frontend->>Frontend: Update chat playground UI with workflow
|
||||
Frontend-->>User: Show workflow started, display messages/logs
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 6: Deleting Prompts
|
||||
|
||||
### User Goal
|
||||
"I want to delete prompts that are no longer needed."
|
||||
|
||||
### User Story
|
||||
As a user, I want to delete prompts with a clear confirmation to prevent accidental deletion.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Delete" button
|
||||
Frontend->>Backend: GET /api/prompts/promptId
|
||||
Backend-->>Frontend: Prompt data (for name)
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Delete 'Prompt Name'?"
|
||||
User->>Frontend: Cancel deletion
|
||||
Frontend-->>User: Dialog closed, no action
|
||||
|
||||
User->>Frontend: Click "Delete" button again
|
||||
Frontend->>Backend: GET /api/prompts/promptId
|
||||
Backend-->>Frontend: Prompt data
|
||||
Frontend->>Frontend: Show confirmation dialog
|
||||
User->>Frontend: Confirm deletion
|
||||
Frontend->>Frontend: Optimistic update: Remove prompt from UI immediately
|
||||
Frontend->>Backend: DELETE /api/prompts/promptId
|
||||
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Restore prompt in UI
|
||||
Frontend-->>User: Show permission error
|
||||
else Not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update: Restore prompt in UI
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend->>Frontend: Keep optimistic update (prompt already removed)
|
||||
Frontend->>Frontend: Show success message
|
||||
Frontend->>Frontend: Navigate to /prompts (if on detail page)
|
||||
Frontend-->>User: Display prompt list (without deleted prompt)
|
||||
end
|
||||
```
|
||||
|
||||
|
|
@ -1,549 +0,0 @@
|
|||
# Prompt Page Requirements
|
||||
|
||||
This document contains the complete frontend requirements for all prompt management pages and components. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Page Structure and Layout](#page-structure-and-layout)
|
||||
3. [User Interactions and Functionality](#user-interactions-and-functionality)
|
||||
4. [Backend Routes and API Integration](#backend-routes-and-api-integration)
|
||||
5. [Field and Attribute Reference](#field-and-attribute-reference)
|
||||
6. [Dynamic Rendering Guidelines](#dynamic-rendering-guidelines)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The prompt management page enables users to manage prompts within their mandate. The frontend consists of a single page (`/prompts`) with different views/states:
|
||||
|
||||
- **List View** - Browse, search, filter, and sort prompts
|
||||
- **Edit View** - Edit prompt properties
|
||||
- **Create View** - Create new prompts
|
||||
|
||||
All views use backend-driven UI generation, meaning field definitions, labels, validation rules, and UI structure come entirely from backend metadata through the `/api/attributes/Prompt` endpoint. Views are managed through component state and routing within the same page, not as separate routes.
|
||||
|
||||
---
|
||||
|
||||
## Page Structure and Layout
|
||||
|
||||
### Prompt Management Page (`/prompts`)
|
||||
|
||||
The prompt management page uses different views/states to handle different user interactions. Views are managed through component state and routing within the same page component.
|
||||
|
||||
### List View
|
||||
|
||||
**State:** `view === 'list'` or `selectedPromptId === null`
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Main Content Area:**
|
||||
- Table or card grid displaying all prompts in the mandate
|
||||
- Each prompt row/card shows: name, content preview, mandate ID
|
||||
- **Action buttons column** in table (rendered on first load):
|
||||
- "Start Prompt" button - navigates to `/chat-playground` and starts workflow with prompt content
|
||||
- "Edit" button - switches to Edit View
|
||||
- "Delete" button - shows confirmation dialog and deletes prompt
|
||||
- Clickable rows/cards that switch to Detail View (set selectedPromptId and view state)
|
||||
|
||||
- **Search and Filter Controls:**
|
||||
- General search input box (searches across all text fields)
|
||||
- Field-specific filter controls (one filter per visible field)
|
||||
- Active filter indicators (chips/badges showing applied filters)
|
||||
- "Clear all filters" button when filters are active
|
||||
|
||||
- **Sorting Controls:**
|
||||
- Clickable column headers with sort indicators (up/down arrows)
|
||||
- Visual indication of current sort field and direction
|
||||
- Support for multi-level sorting
|
||||
|
||||
- **Pagination Controls:**
|
||||
- Page information display ("Page X of Y", "Showing 1-20 of 45 prompts")
|
||||
- Previous/Next page buttons
|
||||
- Page number buttons
|
||||
- Page size selector (10, 20, 50, 100 items per page)
|
||||
|
||||
- **Action Buttons:**
|
||||
- "Create Prompt" button - switches to Create View
|
||||
|
||||
### Detail View
|
||||
|
||||
**State:** `view === 'detail'` and `selectedPromptId !== null`
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Prompt Header Section:**
|
||||
- Prompt name (editable indicator if user has permission)
|
||||
- Mandate ID (read-only)
|
||||
|
||||
- **Prompt Information Section:**
|
||||
- All prompt properties displayed as formatted fields
|
||||
- Read-only fields shown as formatted text
|
||||
- Editable fields shown with edit indicators (if user has permission)
|
||||
- Special formatting:
|
||||
- Content → Display as formatted textarea or code block (if applicable)
|
||||
- Name → Display as heading
|
||||
|
||||
- **Action Buttons:**
|
||||
- Start Prompt button - navigates to `/chat-playground` and starts workflow with prompt content
|
||||
- Edit button (if user has permission and editable fields exist) - switches to Edit View
|
||||
- Delete button (if user has permission)
|
||||
|
||||
### Edit View
|
||||
|
||||
**State:** `view === 'edit'` and `selectedPromptId !== null`
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Form Section:**
|
||||
- Dynamic form with editable prompt fields only
|
||||
- Field labels with required indicators (asterisk for required fields)
|
||||
- Help text/tooltips from field descriptions
|
||||
- Input validation errors displayed inline
|
||||
|
||||
- **Action Buttons:**
|
||||
- Save button (submits form)
|
||||
- Cancel button (switches back to Detail View)
|
||||
|
||||
### Create View
|
||||
|
||||
**State:** `view === 'create'`
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Form Section:**
|
||||
- Dynamic form with all editable prompt fields
|
||||
- Field labels with required indicators
|
||||
- Help text/tooltips from field descriptions
|
||||
- Input validation errors displayed inline
|
||||
|
||||
- **Action Buttons:**
|
||||
- Create button (submits form)
|
||||
- Cancel button (switches back to List View)
|
||||
|
||||
- **Confirmation Dialogs:**
|
||||
- Delete confirmation dialog (on prompt detail page)
|
||||
|
||||
---
|
||||
|
||||
## User Interactions and Functionality
|
||||
|
||||
### Browsing and Discovery
|
||||
|
||||
**Search Functionality:**
|
||||
- User types in general search box
|
||||
- Frontend debounces input (300-500ms delay)
|
||||
- Search applies across all text fields in prompt objects
|
||||
- Results update automatically
|
||||
- Page resets to page 1 when search changes
|
||||
|
||||
**Field Filtering:**
|
||||
- User selects field to filter by
|
||||
- Frontend shows appropriate filter UI based on field type:
|
||||
- Text fields → Text input with operator selection (contains, equals, startsWith, endsWith)
|
||||
- Textarea fields → Text input filter
|
||||
- Select fields → Dropdown with options from backend metadata
|
||||
- Email fields → Email input filter
|
||||
- Checkbox fields → Boolean toggle (true/false/any)
|
||||
- User applies filter → Filter appears as active chip/badge
|
||||
- User can remove individual filters
|
||||
- Multiple filters work together (AND logic)
|
||||
- Page resets to page 1 when filters change
|
||||
|
||||
**Sorting:**
|
||||
- User clicks column header
|
||||
- Sort direction toggles (asc → desc → remove)
|
||||
- Multiple columns can be sorted (multi-level sorting)
|
||||
- Sort indicators show on column headers
|
||||
- All filters and search preserved when sorting
|
||||
|
||||
**Pagination:**
|
||||
- User clicks page number or navigation button
|
||||
- Page updates and data refetches
|
||||
- All filters, search, and sort preserved
|
||||
- User changes page size → Page resets to 1, data refetches
|
||||
|
||||
**View Switching:**
|
||||
- User clicks prompt row/card → Switches to Detail View (set selectedPromptId and view state)
|
||||
- User clicks prompt name → Switches to Detail View (set selectedPromptId and view state)
|
||||
|
||||
### Viewing Prompt Details
|
||||
|
||||
**Information Display:**
|
||||
- All prompt fields displayed using dynamic rendering
|
||||
- Read-only fields shown as formatted text
|
||||
- Editable fields shown with edit indicators
|
||||
- Special fields formatted appropriately (content as formatted text)
|
||||
|
||||
**Action Availability:**
|
||||
- Edit button shown if editable fields exist and user has permission
|
||||
- Delete button shown if user has permission
|
||||
|
||||
### Creating Prompts
|
||||
|
||||
**Form Interaction:**
|
||||
- User clicks "Create Prompt" button → Switches to Create View (set `view: 'create'`)
|
||||
- Form shows all editable fields
|
||||
- User fills in fields
|
||||
- Client-side validation shows errors immediately
|
||||
- User clicks Create → Form validates and submits
|
||||
- User clicks Cancel → Switches back to List View (set `view: 'list'`, `selectedPromptId: null`)
|
||||
|
||||
**Form Validation:**
|
||||
- Required field validation (shows error if empty)
|
||||
- Type validation (e.g., textarea fields must be valid text)
|
||||
- Validation errors displayed inline below fields
|
||||
|
||||
**Form Submission:**
|
||||
- Prompt data sent as Prompt object
|
||||
- Success → Switch to Detail View for created prompt (set `selectedPromptId` to new prompt ID, `view: 'detail'`)
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Editing Prompts
|
||||
|
||||
**Form Interaction:**
|
||||
- User clicks "Edit" button → Switches to Edit View (set `view: 'edit'`)
|
||||
- Form pre-populated with current prompt values
|
||||
- User modifies fields
|
||||
- Client-side validation shows errors immediately
|
||||
- User clicks Save → Form validates and submits
|
||||
- User clicks Cancel → Switches back to Detail View (set `view: 'detail'`)
|
||||
|
||||
**Form Validation:**
|
||||
- Required field validation (shows error if empty)
|
||||
- Type validation (e.g., textarea fields must be valid text)
|
||||
- Validation errors displayed inline below fields
|
||||
|
||||
**Form Submission:**
|
||||
- Only changed fields sent (or all form data)
|
||||
- Success → Switch back to Detail View (set `view: 'detail'`) and show success message
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Starting Prompts
|
||||
|
||||
**Start Prompt Action:**
|
||||
- User clicks "Start Prompt" button in table row or detail page
|
||||
- Frontend fetches prompt data to get prompt content
|
||||
- Frontend navigates to `/chat-playground` page
|
||||
- Frontend pre-fills the chat input with the prompt's content
|
||||
- Frontend displays prompt name as context (optional)
|
||||
- User can optionally modify the prompt or add files before starting
|
||||
- User clicks "Start" or "Send" button in chat playground
|
||||
- Frontend calls `POST /api/chat/playground/start` with:
|
||||
- `workflowMode` query parameter: "Dynamic" (default) or user-selected mode
|
||||
- `userInput` body: `{prompt: promptContent, listFileId: [...], userLanguage: "en"}`
|
||||
|
||||
**Start Prompt Submission:**
|
||||
- Success → Workflow starts, chat playground updates with workflow data, messages/logs appear
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
**Implementation Notes:**
|
||||
- Action buttons (Start Prompt, Edit, Delete) are rendered in the table on first load
|
||||
- Buttons should be visible in each table row
|
||||
- Start Prompt button should be clearly distinguished (e.g., primary button style)
|
||||
- Navigation to chat playground should preserve prompt context
|
||||
|
||||
### Deleting Prompts
|
||||
|
||||
**Delete Action:**
|
||||
- User clicks delete button (on detail page or list)
|
||||
- Confirmation dialog appears with prompt name
|
||||
- Warning about permanent deletion
|
||||
- User confirms → Prompt deleted
|
||||
- User cancels → Dialog closes, no action
|
||||
|
||||
**Delete Success Handling:**
|
||||
- Success message displayed
|
||||
- If in Detail View → Switch back to List View (set `view: 'list'`, `selectedPromptId: null`)
|
||||
- If in List View → Remove prompt from list or refresh
|
||||
|
||||
---
|
||||
|
||||
## Backend Routes and API Integration
|
||||
|
||||
### Complete Route Reference
|
||||
|
||||
All backend routes used by prompt pages:
|
||||
|
||||
| Route | Method | Purpose | When Used | Access Control |
|
||||
|-------|--------|---------|-----------|----------------|
|
||||
| `/api/prompts/` | GET | Get all prompts (with pagination) | Initial page load, pagination changes, sort changes, filter changes, search changes | All authenticated users (filtered by mandate) |
|
||||
| `/api/prompts/{promptId}` | GET | Get prompt details | Detail page load, edit page load | All authenticated users |
|
||||
| `/api/prompts/{promptId}` | PUT | Update prompt | Edit form submission | All authenticated users |
|
||||
| `/api/prompts/{promptId}` | DELETE | Delete prompt | User confirms deletion | All authenticated users |
|
||||
| `/api/prompts` | POST | Create prompt | Create form submission | All authenticated users |
|
||||
| `/api/attributes/Prompt` | GET | Get field definitions | Page load (once per page) - used to generate all UI components | All authenticated users |
|
||||
| `/api/chat/playground/start` | POST | Start workflow with prompt | User clicks "Start Prompt" button | All authenticated users |
|
||||
|
||||
### API Request Patterns
|
||||
|
||||
**Pagination Request:**
|
||||
```
|
||||
GET /api/prompts/?pagination={"page":1,"pageSize":20,"sort":[],"filters":null}
|
||||
```
|
||||
- `pagination` parameter is JSON-encoded string
|
||||
- If user wants all prompts: omit `pagination` parameter entirely
|
||||
- Filters structure: `{"search":"query","fieldName":"value",...}`
|
||||
- Optional `mandateId` query parameter to filter by mandate
|
||||
|
||||
**Create Request:**
|
||||
```
|
||||
POST /api/prompts
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"name": "Invoice Processing",
|
||||
"content": "Process the following invoice..."
|
||||
}
|
||||
```
|
||||
- All Prompt model fields sent as Prompt object
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Update Request:**
|
||||
```
|
||||
PUT /api/prompts/{promptId}
|
||||
Content-Type: application/json
|
||||
Body: {fieldName: value, ...}
|
||||
```
|
||||
- Send only changed fields or all form data
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Start Prompt Request:**
|
||||
```
|
||||
POST /api/chat/playground/start?workflowMode=Dynamic
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"prompt": "Prompt content here...",
|
||||
"listFileId": ["file1", "file2"],
|
||||
"userLanguage": "en"
|
||||
}
|
||||
```
|
||||
- `workflowMode` query parameter is required: "Actionplan", "Dynamic", or "Template" (default: "Dynamic")
|
||||
- `prompt` field contains the prompt's content
|
||||
- `listFileId` is optional array of file IDs to attach
|
||||
- `userLanguage` should match user's preferred language
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Delete Requests:**
|
||||
```
|
||||
DELETE /api/prompts/{promptId}
|
||||
```
|
||||
- Delete operation requires user confirmation
|
||||
- Handle 403 (permission denied) and 404 (not found) gracefully
|
||||
|
||||
### Response Handling
|
||||
|
||||
**Paginated Response:**
|
||||
```json
|
||||
{
|
||||
"items": [...],
|
||||
"pagination": {
|
||||
"currentPage": 1,
|
||||
"pageSize": 20,
|
||||
"totalItems": 45,
|
||||
"totalPages": 3,
|
||||
"sort": [...],
|
||||
"filters": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- 403 Forbidden → Show permission error message
|
||||
- 404 Not Found → Show "not found" error message
|
||||
- 400 Bad Request → Display validation errors from response
|
||||
- 500 Internal Server Error → Show generic error message
|
||||
|
||||
---
|
||||
|
||||
## Field and Attribute Reference
|
||||
|
||||
### Complete Field List
|
||||
|
||||
The following is a comprehensive list of all parameters, attributes, and fields that will be displayed for prompts. All of these are provided by the backend through the `/api/attributes/Prompt` endpoint and should never be hardcoded in the frontend.
|
||||
|
||||
#### Core Prompt Fields
|
||||
|
||||
**Identification Fields:**
|
||||
- `id` - Unique prompt identifier (text, readonly, not required, visible)
|
||||
- `mandateId` - ID of the mandate this prompt belongs to (text, readonly, not required, visible)
|
||||
|
||||
**Content Fields:**
|
||||
- `name` - Name of the prompt (text, editable, required, visible)
|
||||
- `content` - Content of the prompt (textarea, editable, required, visible)
|
||||
|
||||
### Pagination Parameters
|
||||
|
||||
**Request Parameters (`PaginationParams`):**
|
||||
- `page` - Current page number (1-based, minimum 1)
|
||||
- `pageSize` - Number of items per page (minimum 1, maximum 1000)
|
||||
- `sort` - Array of sort field configurations
|
||||
- Each sort field contains:
|
||||
- `field` - Field name to sort by (must match a prompt field name)
|
||||
- `direction` - Sort direction: "asc" or "desc"
|
||||
- `filters` - Filter criteria dictionary
|
||||
- `search` - General search term (searches across all text fields, case-insensitive)
|
||||
- Field-specific filters: `{fieldName: value}` or `{fieldName: {operator: "operator", value: value}}`
|
||||
- Supported operators: "equals", "contains", "startsWith", "endsWith", "in", "notIn"
|
||||
|
||||
**Response Metadata (`PaginationMetadata`):**
|
||||
- `currentPage` - Current page number (1-based)
|
||||
- `pageSize` - Number of items per page
|
||||
- `totalItems` - Total number of items across all pages (after filters applied)
|
||||
- `totalPages` - Total number of pages (calculated from totalItems / pageSize)
|
||||
- `sort` - Current sort configuration applied (array of SortField objects)
|
||||
- `filters` - Current filters applied (mirrors request filters)
|
||||
|
||||
### Attribute Definition Structure
|
||||
|
||||
Each field returned from `/api/attributes/Prompt` contains:
|
||||
|
||||
- `name` - Field name (e.g., "name", "content", "id")
|
||||
- `type` - Field data type (e.g., "text", "textarea", "select", "checkbox")
|
||||
- `label` - Localized field label (object with language keys: {"en": "English Label", "fr": "French Label"})
|
||||
- `description` - Field description text
|
||||
- `required` - Boolean indicating if field is required
|
||||
- `readonly` - Boolean indicating if field is read-only
|
||||
- `editable` - Boolean indicating if field can be edited (inverse of readonly)
|
||||
- `visible` - Boolean indicating if field should be displayed in UI
|
||||
- `options` - Array of options for select fields (each option has `value` and localized `label`)
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Rendering Guidelines
|
||||
|
||||
The frontend must render all UI components dynamically based on backend metadata. No field definitions, labels, validation rules, or UI structure should be hardcoded.
|
||||
|
||||
### Table Column Generation
|
||||
|
||||
When rendering the prompt list table:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/Prompt`
|
||||
2. Filter attributes where `visible: true` to determine which columns to display
|
||||
3. Use `label` property for column headers (select appropriate language based on user preference)
|
||||
4. Use `type` property to determine how to format cell values:
|
||||
- `text` fields → Display as plain text
|
||||
- `textarea` fields → Display as truncated text with "..." or expandable preview
|
||||
- `select` fields → Display value using label from options array (match value to option.value, then display option.label)
|
||||
- `checkbox` fields → Display as checkmark icon or "Yes"/"No" text
|
||||
5. Use `readonly` property to determine if column should be sortable (readonly fields may still be sortable, but editable fields definitely are)
|
||||
6. Generate click handlers for column headers to update sort parameters and refetch data
|
||||
7. **Add Actions Column:**
|
||||
- Render action buttons in each table row on first load
|
||||
- Include "Start Prompt" button (primary style) - navigates to `/chat-playground` with prompt content
|
||||
- Include "Edit" button - switches to Edit View
|
||||
- Include "Delete" button - shows confirmation dialog
|
||||
- Buttons should be visible and accessible in each row
|
||||
|
||||
### Filter Control Generation
|
||||
|
||||
When rendering filter controls:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/Prompt`
|
||||
2. Filter attributes where `visible: true` to determine which filters to show
|
||||
3. For each visible field, generate appropriate filter UI based on `type`:
|
||||
- `text` fields → Text input filter (supports "contains", "equals", "startsWith", "endsWith" operators)
|
||||
- `textarea` fields → Text input filter (supports "contains", "equals", "startsWith", "endsWith" operators)
|
||||
- `email` fields → Email input filter
|
||||
- `select` fields → Dropdown filter with options from `options` array (use localized labels)
|
||||
- `checkbox` fields → Boolean toggle filter (true/false/any)
|
||||
4. Use `label` property for filter labels (localized)
|
||||
5. When user applies filter, update `filters` object in pagination parameters and refetch data
|
||||
6. Display active filters as chips/badges showing field label and value
|
||||
7. Allow removing individual filters by removing them from `filters` object
|
||||
|
||||
### Search Implementation
|
||||
|
||||
For general search functionality:
|
||||
|
||||
1. Display a single search input box (not field-specific)
|
||||
2. When user types, update `filters.search` in pagination parameters
|
||||
3. Debounce search input (wait 300-500ms after user stops typing before sending request)
|
||||
4. Search applies across all text fields in the prompt object
|
||||
5. Reset to page 1 when search query changes
|
||||
6. Combine search with field-specific filters (both are in the `filters` object)
|
||||
|
||||
### Form Field Generation
|
||||
|
||||
When rendering edit forms:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/Prompt`
|
||||
2. Filter attributes where `visible: true` AND `editable: true` to determine which fields to show
|
||||
3. For each editable field, generate appropriate form input based on `type`:
|
||||
- `text` fields → Text input
|
||||
- `textarea` fields → Textarea input (with appropriate rows/height)
|
||||
- `email` fields → Email input
|
||||
- `select` fields → Dropdown/select input with options from `options` array (use localized labels)
|
||||
- `checkbox` fields → Checkbox input
|
||||
4. Use `label` property for field labels (localized)
|
||||
5. Use `required` property to show required indicators (asterisk, etc.)
|
||||
6. Use `description` property to show help text or tooltips
|
||||
7. Validate form before submission:
|
||||
- Check all `required: true` fields have values
|
||||
- Validate types (e.g., textarea fields must be valid text)
|
||||
- Validate select fields (value must be in options array)
|
||||
8. On submit, send only changed fields or all form data to update endpoint
|
||||
|
||||
### Create Form Field Generation
|
||||
|
||||
When rendering create forms:
|
||||
|
||||
1. Follow same pattern as edit forms for Prompt model fields
|
||||
2. On submit, send Prompt object fields in request body
|
||||
|
||||
### Display Formatting
|
||||
|
||||
When displaying field values:
|
||||
|
||||
1. Use `type` property to determine formatting:
|
||||
- `text` → Display as-is (may need HTML escaping)
|
||||
- `textarea` → Display as formatted text (preserve line breaks, may truncate with "Read more" link)
|
||||
- `select` → Look up value in `options` array and display localized label
|
||||
- `checkbox` → Display as checkmark icon or "Yes"/"No" text
|
||||
2. Handle `null` or `undefined` values gracefully (show "-" or "Not set")
|
||||
3. Use `readonly` property to determine if field should show edit indicators
|
||||
4. For content field (textarea), consider:
|
||||
- Truncating long content in list view with "Show more" link
|
||||
- Showing full content in detail view
|
||||
- Preserving formatting (line breaks, whitespace)
|
||||
|
||||
### Localization
|
||||
|
||||
All labels and options support multiple languages:
|
||||
|
||||
1. Use user's preferred language (from user settings or browser locale)
|
||||
2. Access localized labels from `label` object: `label[userLanguage]` or `label.en` as fallback
|
||||
3. For select options, use `option.label[userLanguage]` or `option.label.en` as fallback
|
||||
4. If label for current language is missing, fall back to English
|
||||
|
||||
### Key Principles
|
||||
|
||||
- Never hardcode field names, labels, types, or validation rules
|
||||
- Always fetch attribute definitions from backend before rendering UI
|
||||
- Use attribute metadata to determine what to display and how to display it
|
||||
- Support all field types dynamically - if backend adds new types, frontend should handle them
|
||||
- Respect `visible`, `editable`, `readonly`, and `required` flags from backend
|
||||
- Use localized labels from backend metadata
|
||||
- Generate filters, forms, and tables entirely from attribute definitions
|
||||
- When backend adds new fields, frontend should automatically display them without code changes
|
||||
- Handle all error cases gracefully (403, 404, 400, 500)
|
||||
- Provide user feedback for all actions (loading states, success messages, error messages)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This document provides complete frontend requirements for all prompt management pages and components. All requirements follow the principle of **backend-driven UI generation**—no hardcoding of field definitions, labels, or validation rules. The frontend should dynamically generate all UI components from backend metadata provided through the `/api/attributes/Prompt` endpoint.
|
||||
|
||||
**Key Architecture Pattern:** The prompt management interface is a single page (`/prompts`) with different views managed through component state. All interactions happen within the same page component without separate routes.
|
||||
|
||||
For generic patterns that apply across all entity types (not just prompts), see the [Dynamic Forms and Pagination documentation](./dynamic-forms-and-pagination.md).
|
||||
|
||||
|
|
@ -1,516 +0,0 @@
|
|||
# User Routes Frontend Documentation
|
||||
|
||||
This document describes customer journeys for managing users through the frontend, focusing on how administrators interact with user management and how the backend routes support these experiences. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
**IMPORTANT:** All user management pages are **admin-only**. Non-admin users should not see navigation links or be able to access these pages. The frontend must check user privileges before rendering user management UI.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Customer Journey 1: Discovering and Browsing Users](#customer-journey-1-discovering-and-browsing-users)
|
||||
3. [Customer Journey 2: Viewing User Details](#customer-journey-2-viewing-user-details)
|
||||
4. [Customer Journey 3: Creating New Users](#customer-journey-3-creating-new-users)
|
||||
5. [Customer Journey 4: Editing User Properties](#customer-journey-4-editing-user-properties)
|
||||
6. [Customer Journey 5: Enabling and Disabling Users](#customer-journey-5-enabling-and-disabling-users)
|
||||
7. [Customer Journey 6: Resetting User Passwords](#customer-journey-6-resetting-user-passwords)
|
||||
8. [Customer Journey 7: Revoking User Sessions](#customer-journey-7-revoking-user-sessions)
|
||||
9. [Customer Journey 8: Deleting Users](#customer-journey-8-deleting-users)
|
||||
9. [Backend Metadata System](#backend-metadata-system)
|
||||
10. [Implementation Patterns](#implementation-patterns)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The user routes (`/api/users`) enable administrators to manage users within their mandate. These routes focus on **user administration** including creation, editing, password management, and deletion.
|
||||
|
||||
**Key Principles:**
|
||||
- **Admin-Only**: All user management functionality is restricted to administrators
|
||||
- **User-Centric**: Documentation organized around what administrators want to accomplish
|
||||
- **Backend-Driven**: All forms, tables, and UI components generated from backend metadata
|
||||
- **No Hardcoding**: Field definitions, labels, validation rules, and options come from the backend
|
||||
- **Permission-Aware**: Backend enforces permissions; frontend handles gracefully
|
||||
- **Mandate-Scoped**: Users are managed within the context of mandates
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 1: Discovering and Browsing Users
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to see all users in my mandate and find the one I'm looking for."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to browse users, search for specific users, filter by any field, sort them by different criteria, and quickly identify which users are enabled, their privilege levels, and their authentication methods.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: Navigate to /users (admin only)
|
||||
Frontend->>Frontend: Check if user is admin
|
||||
alt User is not admin
|
||||
Frontend-->>Admin: Redirect to unauthorized page or hide navigation
|
||||
else User is admin
|
||||
Frontend->>Backend: GET /api/attributes/User
|
||||
Backend-->>Frontend: Attribute definitions (fields, labels, types)
|
||||
Frontend->>Backend: GET /api/users/?pagination=...
|
||||
Backend-->>Frontend: Paginated users list
|
||||
Frontend->>Frontend: Generate table columns from attributes
|
||||
Frontend->>Frontend: Generate filter controls from attributes
|
||||
Frontend->>Frontend: Render users table + search + filters
|
||||
Frontend-->>Admin: Display user list with search/filter UI
|
||||
end
|
||||
|
||||
alt Admin performs general search
|
||||
Admin->>Frontend: Type in search box (e.g., "john")
|
||||
Frontend->>Frontend: Update search query
|
||||
Frontend->>Backend: GET /api/users/?pagination=...filters:{"search":"john"}
|
||||
Backend-->>Frontend: Filtered users list
|
||||
Frontend-->>Admin: Display matching users
|
||||
end
|
||||
|
||||
alt Admin applies field filter
|
||||
Admin->>Frontend: Select filter field (e.g., "Privilege")
|
||||
Frontend->>Frontend: Show filter options from attribute metadata
|
||||
Admin->>Frontend: Select filter value (e.g., "admin")
|
||||
Frontend->>Frontend: Update filter parameters
|
||||
Frontend->>Backend: GET /api/users/?pagination=...filters:{"privilege":"admin"}
|
||||
Backend-->>Frontend: Filtered users list
|
||||
Frontend-->>Admin: Display filtered users
|
||||
end
|
||||
|
||||
alt Admin combines search + filter + sort
|
||||
Admin->>Frontend: Apply search + filter + sort
|
||||
Frontend->>Frontend: Combine all parameters
|
||||
Frontend->>Backend: GET /api/users/?pagination=...filters:{"search":"john","privilege":"admin"}...sort:desc
|
||||
Backend-->>Frontend: Filtered, sorted users list
|
||||
Frontend-->>Admin: Display results
|
||||
end
|
||||
|
||||
Admin->>Frontend: Click column header (e.g., "Email")
|
||||
Frontend->>Frontend: Update sort parameters
|
||||
Frontend->>Backend: GET /api/users/?pagination=...sort:desc
|
||||
Backend-->>Frontend: Sorted users list
|
||||
Frontend-->>Admin: Display sorted users
|
||||
|
||||
Admin->>Frontend: Click user row
|
||||
Frontend->>Frontend: Navigate to /users/userId
|
||||
Frontend-->>Admin: Show user detail page
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 2: Viewing User Details
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to see everything about a specific user—their account information, privilege level, and status."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to open a user and see their complete information, including username, email, full name, language preference, enabled status, privilege level, authentication authority, and mandate association.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: Click user from list
|
||||
Frontend->>Backend: GET /api/users/userId
|
||||
Backend-->>Frontend: User object
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/User
|
||||
Backend-->>Frontend: Field definitions
|
||||
|
||||
Frontend->>Frontend: Generate UI from metadata
|
||||
Frontend->>Frontend: Render user info section
|
||||
Frontend->>Frontend: Separate editable vs read-only fields
|
||||
Frontend-->>Admin: Display complete user view
|
||||
|
||||
alt User has editable fields and admin has permission
|
||||
Frontend->>Frontend: Show "Edit User" button
|
||||
end
|
||||
|
||||
alt Admin has permission
|
||||
Frontend->>Frontend: Show "Reset Password" button
|
||||
Frontend->>Frontend: Show "Revoke Sessions" button
|
||||
Frontend->>Frontend: Show "Delete User" button
|
||||
end
|
||||
|
||||
alt Admin clicks "Revoke Sessions"
|
||||
Admin->>Frontend: Click "Revoke Sessions" button
|
||||
Frontend->>Frontend: Show revocation dialog
|
||||
Admin->>Frontend: Select authority filter (optional)
|
||||
Admin->>Frontend: Enter reason (optional)
|
||||
Admin->>Frontend: Click "Revoke"
|
||||
Frontend->>Frontend: Optimistic update: Show loading state
|
||||
Frontend->>Backend: POST /api/admin/tokens/revoke/user
|
||||
alt Success
|
||||
Backend-->>Frontend: {"revoked": count}
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: GET /api/users/userId (refetch)
|
||||
Backend-->>Frontend: Updated user object
|
||||
Frontend->>Frontend: Update UI with fresh data
|
||||
Frontend-->>Admin: Show success message
|
||||
else Error (403/400/500)
|
||||
Backend-->>Frontend: Error response
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>Admin: Show error message
|
||||
end
|
||||
end
|
||||
|
||||
alt Admin clicks "Reset Password"
|
||||
Admin->>Frontend: Click "Reset Password" button
|
||||
Frontend->>Frontend: Show password reset dialog
|
||||
Admin->>Frontend: Enter new password
|
||||
Admin->>Frontend: Confirm password
|
||||
Admin->>Frontend: Click "Reset"
|
||||
Frontend->>Frontend: Optimistic update: Show loading state
|
||||
Frontend->>Backend: POST /api/users/userId/reset-password
|
||||
alt Success
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Backend: GET /api/users/userId (refetch)
|
||||
Backend-->>Frontend: Updated user object
|
||||
Frontend->>Frontend: Update UI with fresh data
|
||||
Frontend-->>Admin: Show success message
|
||||
else Error (403/400/500)
|
||||
Backend-->>Frontend: Error response
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>Admin: Show error message
|
||||
end
|
||||
end
|
||||
|
||||
alt Admin clicks "Delete User"
|
||||
Admin->>Frontend: Click "Delete User" button
|
||||
Frontend->>Backend: GET /api/users/userId
|
||||
Backend-->>Frontend: User data (for name)
|
||||
Frontend->>Frontend: Show confirmation dialog
|
||||
Admin->>Frontend: Confirm deletion
|
||||
Frontend->>Frontend: Optimistic update: Remove user from UI
|
||||
Frontend->>Backend: DELETE /api/users/userId
|
||||
alt Success
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend->>Frontend: Keep optimistic update
|
||||
Frontend->>Frontend: Navigate to /users
|
||||
Frontend-->>Admin: Show success message
|
||||
else Error (403/404/500)
|
||||
Backend-->>Frontend: Error response
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>Admin: Show error message
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
## Customer Journey 3: Creating New Users
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to create new users for my mandate."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to create a new user by filling out a form with their username, email, full name, language preference, privilege level, and initial password. The form should validate all required fields and show clear error messages.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: Click "Create User" button
|
||||
Frontend->>Frontend: Navigate to /users/create
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/User
|
||||
Backend-->>Frontend: Field definitions (editable fields)
|
||||
|
||||
Frontend->>Frontend: Filter editable fields
|
||||
Frontend->>Frontend: Generate form from attributes
|
||||
Frontend->>Frontend: Add password field (special case)
|
||||
Frontend-->>Admin: Display create user form
|
||||
|
||||
Admin->>Frontend: Fill in form fields
|
||||
Admin->>Frontend: Enter password
|
||||
Admin->>Frontend: Click "Create"
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types, email format)
|
||||
alt Validation fails
|
||||
Frontend-->>Admin: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Backend: POST /api/users + form data + password
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend-->>Admin: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>Admin: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Created user object
|
||||
Frontend->>Frontend: Navigate to user detail page
|
||||
Frontend-->>Admin: Show created user
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Customer Journey 4: Editing User Properties
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to change user settings like their name, email, privilege level, or enabled status."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to edit a user's properties through a form that only shows fields I'm allowed to edit, with validation and clear error messages.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: Click "Edit" button
|
||||
Frontend->>Frontend: Navigate to /users/id/edit
|
||||
|
||||
Frontend->>Backend: GET /api/users/userId
|
||||
Backend-->>Frontend: Current user data
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/User
|
||||
Backend-->>Frontend: Field definitions (editable fields)
|
||||
|
||||
Frontend->>Frontend: Filter editable fields
|
||||
Frontend->>Frontend: Generate form from attributes
|
||||
Frontend->>Frontend: Pre-populate form with user data
|
||||
Frontend-->>Admin: Display edit form
|
||||
|
||||
Admin->>Frontend: Modify form fields
|
||||
Admin->>Frontend: Click "Save"
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types)
|
||||
alt Validation fails
|
||||
Frontend-->>Admin: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Optimistic update: Apply changes to UI immediately
|
||||
Frontend->>Backend: PUT /api/users/userId + form data
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>Admin: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>Admin: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Updated user
|
||||
Frontend->>Frontend: Keep optimistic update (or refresh from response)
|
||||
Frontend->>Frontend: Navigate to detail page
|
||||
Frontend-->>Admin: Show updated user
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 5: Enabling and Disabling Users
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to quickly enable or disable users directly from the user list without navigating to their detail page."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to toggle a user's enabled status directly in the user list table. When I click the toggle, it should immediately update the user's status in the backend with an optimistic update, so the UI feels responsive and confident.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: View user list table
|
||||
Frontend->>Backend: GET /api/users/?pagination=...
|
||||
Backend-->>Frontend: Paginated users list
|
||||
Frontend->>Frontend: Render table with enabled toggle column
|
||||
Frontend-->>Admin: Display user list with enabled toggles
|
||||
|
||||
Admin->>Frontend: Click enabled toggle for user
|
||||
Frontend->>Frontend: Optimistic update: Toggle switch immediately<br/>(enabled: true → false or false → true)
|
||||
Frontend->>Backend: PUT /api/users/userId + {enabled: newValue}
|
||||
alt Success (200)
|
||||
Backend->>Backend: Update user enabled status
|
||||
Backend-->>Frontend: Updated user object
|
||||
Frontend->>Frontend: Keep optimistic update (toggle already changed)
|
||||
Frontend->>Backend: GET /api/users/?pagination=... (refetch list)
|
||||
Backend-->>Frontend: Updated users list
|
||||
Frontend->>Frontend: Update table with fresh data
|
||||
Frontend-->>Admin: Show subtle success indicator (optional)
|
||||
else Error (403/400/404/500)
|
||||
Backend-->>Frontend: Error response
|
||||
Frontend->>Frontend: Revert optimistic update: Toggle back to original state
|
||||
Frontend-->>Admin: Show error message
|
||||
end
|
||||
```
|
||||
|
||||
## Customer Journey 6: Resetting User Passwords
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to reset a user's password when they forget it or need a new one."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to reset a user's password by entering a new password. The system should validate password strength and automatically revoke all existing tokens for security.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: Click "Reset Password" button
|
||||
Frontend->>Frontend: Show password reset dialog
|
||||
|
||||
Admin->>Frontend: Enter new password
|
||||
Admin->>Frontend: Confirm new password
|
||||
Admin->>Frontend: Click "Reset"
|
||||
|
||||
Frontend->>Frontend: Validate password (min 8 characters)
|
||||
Frontend->>Frontend: Validate passwords match
|
||||
alt Validation fails
|
||||
Frontend-->>Admin: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Backend: POST /api/users/userId/reset-password + newPassword
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend-->>Admin: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request (password too short)
|
||||
Frontend-->>Admin: Show validation error
|
||||
else Success (200)
|
||||
Backend->>Backend: Reset password
|
||||
Backend->>Backend: Revoke all user tokens
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend-->>Admin: Show success message
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Frontend Requirements
|
||||
|
||||
> **📋 Complete frontend requirements for this journey are documented in [User Page Requirements](./user-page-requirements.md#customer-journey-6-resetting-user-passwords)**
|
||||
|
||||
The frontend must implement a password reset dialog with password strength validation. This is an admin-only action.
|
||||
|
||||
```typescript
|
||||
const handleResetPassword = async (userId: string, newPassword: string) => {
|
||||
// Validate password strength
|
||||
if (newPassword.length < 8) {
|
||||
showError('Password must be at least 8 characters long');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/users/${userId}/reset-password`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ newPassword })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showSuccess('Password reset successfully. All user sessions have been revoked.');
|
||||
} else {
|
||||
const error = await response.json();
|
||||
showError(error.detail || 'Failed to reset password');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 7: Revoking User Sessions
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to revoke all active sessions for a user to force them to log in again."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to revoke all active tokens/sessions for a user, optionally filtered by authentication authority (local, Google, Microsoft). This is useful for security purposes, such as when a user's account may have been compromised or when they need to be logged out of all devices.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: Click "Revoke Sessions" button
|
||||
Frontend->>Frontend: Show session revocation dialog
|
||||
|
||||
Admin->>Frontend: Optionally select authority filter (local/Google/Microsoft)
|
||||
Admin->>Frontend: Optionally enter reason
|
||||
Admin->>Frontend: Click "Revoke Sessions"
|
||||
|
||||
Frontend->>Backend: POST /api/admin/tokens/revoke/user + {userId, authority?, reason?}
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend-->>Admin: Show permission error
|
||||
else Success (200)
|
||||
Backend->>Backend: Revoke all active tokens for user
|
||||
Backend-->>Frontend: {"revoked": count}
|
||||
Frontend-->>Admin: Show success message with count
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 8: Deleting Users
|
||||
|
||||
### User Goal
|
||||
"As an administrator, I want to delete users that are no longer needed."
|
||||
|
||||
### User Story
|
||||
As an administrator, I want to delete users with a clear confirmation to prevent accidental deletion.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
Admin->>Frontend: Click "Delete" button
|
||||
Frontend->>Backend: GET /api/users/userId
|
||||
Backend-->>Frontend: User data (for name)
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Delete 'User Name'?"
|
||||
Admin->>Frontend: Cancel deletion
|
||||
Frontend-->>Admin: Dialog closed, no action
|
||||
|
||||
Admin->>Frontend: Click "Delete" button again
|
||||
Frontend->>Backend: GET /api/users/userId
|
||||
Backend-->>Frontend: User data
|
||||
Frontend->>Frontend: Show confirmation dialog
|
||||
Admin->>Frontend: Confirm deletion
|
||||
Frontend->>Frontend: Optimistic update: Remove user from UI immediately
|
||||
Frontend->>Backend: DELETE /api/users/userId
|
||||
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Restore user in UI
|
||||
Frontend-->>Admin: Show permission error
|
||||
else Not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update: Restore user in UI
|
||||
Frontend-->>Admin: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend->>Frontend: Keep optimistic update (user already removed)
|
||||
Frontend->>Frontend: Show success message
|
||||
Frontend->>Frontend: Navigate to /users (if on detail page)
|
||||
Frontend-->>Admin: Display user list (without deleted user)
|
||||
end
|
||||
```
|
||||
|
|
@ -1,843 +0,0 @@
|
|||
# User Page Requirements
|
||||
|
||||
This document contains the complete frontend requirements for all user management pages and components. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
**IMPORTANT:** All user management pages are **admin-only**. Non-admin users should not see navigation links or be able to access these pages. The frontend must check user privileges before rendering user management UI.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Page Structure and Layout](#page-structure-and-layout)
|
||||
3. [User Interactions and Functionality](#user-interactions-and-functionality)
|
||||
4. [Backend Routes and API Integration](#backend-routes-and-api-integration)
|
||||
5. [Field and Attribute Reference](#field-and-attribute-reference)
|
||||
6. [Dynamic Rendering Guidelines](#dynamic-rendering-guidelines)
|
||||
7. [Admin Access Control](#admin-access-control)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The user management page enables administrators to manage users within their mandate. The frontend consists of a single page (`/users`) with different views/states:
|
||||
|
||||
- **List View** - Browse, search, filter, and sort users (admin-only)
|
||||
- **Detail View** - View complete user information (admin-only)
|
||||
- **Edit View** - Edit user properties (admin-only)
|
||||
- **Create View** - Create new users (admin-only)
|
||||
|
||||
All views use backend-driven UI generation, meaning field definitions, labels, validation rules, and UI structure come entirely from backend metadata through the `/api/attributes/User` endpoint. Views are managed through component state and routing within the same page, not as separate routes.
|
||||
|
||||
---
|
||||
|
||||
## Page Structure and Layout
|
||||
|
||||
### User Management Page (`/users`)
|
||||
|
||||
**Access Control:** Admin-only. Non-admin users should not see this page or navigation links to it.
|
||||
|
||||
The user management page uses different views/states to handle different user interactions. Views are managed through component state and routing within the same page component.
|
||||
|
||||
### List View
|
||||
|
||||
**State:** `view === 'list'` or `selectedUserId === null`
|
||||
|
||||
**What the administrator sees:**
|
||||
|
||||
- **Main Content Area:**
|
||||
- Table or card grid displaying all users in the mandate
|
||||
- Each user row/card shows: username, email, full name, privilege badge, enabled toggle (interactive switch), authentication authority
|
||||
- Enabled toggle is clickable and immediately updates user status with optimistic update
|
||||
- Clickable rows/cards that switch to Detail View (set selectedUserId and view state)
|
||||
|
||||
- **Search and Filter Controls:**
|
||||
- General search input box (searches across all text fields)
|
||||
- Field-specific filter controls (one filter per visible field)
|
||||
- Active filter indicators (chips/badges showing applied filters)
|
||||
- "Clear all filters" button when filters are active
|
||||
|
||||
- **Sorting Controls:**
|
||||
- Clickable column headers with sort indicators (up/down arrows)
|
||||
- Visual indication of current sort field and direction
|
||||
- Support for multi-level sorting
|
||||
|
||||
- **Pagination Controls:**
|
||||
- Page information display ("Page X of Y", "Showing 1-20 of 45 users")
|
||||
- Previous/Next page buttons
|
||||
- Page number buttons
|
||||
- Page size selector (10, 20, 50, 100 items per page)
|
||||
|
||||
- **Action Buttons:**
|
||||
- "Create User" button (admin-only) - switches to Create View
|
||||
|
||||
### Detail View
|
||||
|
||||
**State:** `view === 'detail'` and `selectedUserId !== null`
|
||||
|
||||
**Access Control:** Admin-only.
|
||||
|
||||
**What the administrator sees:**
|
||||
|
||||
- **User Header Section:**
|
||||
- Username (editable indicator if user has permission)
|
||||
- Full name (if available)
|
||||
- Privilege badge with color coding
|
||||
- Enabled status indicator
|
||||
|
||||
- **User Information Section:**
|
||||
- All user properties displayed as formatted fields
|
||||
- Read-only fields shown as formatted text
|
||||
- Editable fields shown with edit indicators (if admin has permission)
|
||||
- Special formatting:
|
||||
- Privilege → Color-coded badge (User/Admin/SysAdmin)
|
||||
- Enabled → Checkmark icon or "Enabled"/"Disabled" text
|
||||
- Authentication Authority → Badge with icon
|
||||
- Email → Clickable mailto link
|
||||
|
||||
- **Action Buttons:**
|
||||
- Edit button (if admin has permission and editable fields exist) - switches to Edit View
|
||||
- Reset Password button (admin-only)
|
||||
- Revoke Sessions button (admin-only)
|
||||
- Delete button (if admin has permission)
|
||||
|
||||
### Edit View
|
||||
|
||||
**State:** `view === 'edit'` and `selectedUserId !== null`
|
||||
|
||||
**Access Control:** Admin-only.
|
||||
|
||||
**What the administrator sees:**
|
||||
|
||||
- **Form Section:**
|
||||
- Dynamic form with editable user fields only
|
||||
- Field labels with required indicators (asterisk for required fields)
|
||||
- Help text/tooltips from field descriptions
|
||||
- Input validation errors displayed inline
|
||||
|
||||
- **Action Buttons:**
|
||||
- Save button (submits form)
|
||||
- Cancel button (switches back to Detail View)
|
||||
|
||||
### Create View
|
||||
|
||||
**State:** `view === 'create'`
|
||||
|
||||
**Access Control:** Admin-only.
|
||||
|
||||
**What the administrator sees:**
|
||||
|
||||
- **Form Section:**
|
||||
- Dynamic form with all editable user fields
|
||||
- Password field (special field, not in User model attributes)
|
||||
- Confirm password field (frontend-only validation)
|
||||
- Field labels with required indicators
|
||||
- Help text/tooltips from field descriptions
|
||||
- Input validation errors displayed inline
|
||||
|
||||
- **Action Buttons:**
|
||||
- Create button (submits form)
|
||||
- Cancel button (switches back to List View)
|
||||
|
||||
- **Confirmation Dialogs:**
|
||||
- Delete confirmation dialog (on user detail page)
|
||||
- Password reset confirmation dialog
|
||||
|
||||
---
|
||||
|
||||
## User Interactions and Functionality
|
||||
|
||||
### Admin Access Control
|
||||
|
||||
**Before Rendering Any User Management UI:**
|
||||
|
||||
1. Check if current user has admin or sysadmin privilege
|
||||
2. If not admin:
|
||||
- Hide navigation links to user management pages
|
||||
- Redirect if user tries to access pages directly
|
||||
- Show unauthorized access message
|
||||
3. If admin:
|
||||
- Render user management UI normally
|
||||
- Handle 403 errors gracefully (may occur if permissions change)
|
||||
|
||||
### Browsing and Discovery
|
||||
|
||||
**Search Functionality:**
|
||||
- Administrator types in general search box
|
||||
- Frontend debounces input (300-500ms delay)
|
||||
- Search applies across all text fields in user objects
|
||||
- Results update automatically
|
||||
- Page resets to page 1 when search changes
|
||||
|
||||
**Field Filtering:**
|
||||
- Administrator selects field to filter by
|
||||
- Frontend shows appropriate filter UI based on field type:
|
||||
- Text fields → Text input with operator selection (contains, equals, startsWith, endsWith)
|
||||
- Select fields → Dropdown with options from backend metadata
|
||||
- Email fields → Email input filter
|
||||
- Checkbox fields → Boolean toggle (true/false/any)
|
||||
- Administrator applies filter → Filter appears as active chip/badge
|
||||
- Administrator can remove individual filters
|
||||
- Multiple filters work together (AND logic)
|
||||
- Page resets to page 1 when filters change
|
||||
|
||||
**Sorting:**
|
||||
- Administrator clicks column header
|
||||
- Sort direction toggles (asc → desc → remove)
|
||||
- Multiple columns can be sorted (multi-level sorting)
|
||||
- Sort indicators show on column headers
|
||||
- All filters and search preserved when sorting
|
||||
|
||||
**Pagination:**
|
||||
- Administrator clicks page number or navigation button
|
||||
- Page updates and data refetches
|
||||
- All filters, search, and sort preserved
|
||||
- Administrator changes page size → Page resets to 1, data refetches
|
||||
|
||||
**View Switching:**
|
||||
- Administrator clicks user row/card → Switches to Detail View (set `selectedUserId` and `view: 'detail'`)
|
||||
- Administrator clicks username → Switches to Detail View (set `selectedUserId` and `view: 'detail'`)
|
||||
|
||||
### Viewing User Details
|
||||
|
||||
**Information Display:**
|
||||
- All user fields displayed using dynamic rendering
|
||||
- Read-only fields shown as formatted text
|
||||
- Editable fields shown with edit indicators
|
||||
- Special fields formatted appropriately (privilege badges, enabled status, email links)
|
||||
|
||||
**Action Availability:**
|
||||
- Edit button shown if editable fields exist and admin has permission
|
||||
- Reset Password button always shown (admin-only action)
|
||||
- Revoke Sessions button always shown (admin-only action)
|
||||
- Delete button shown if admin has permission
|
||||
|
||||
### Creating Users
|
||||
|
||||
**Form Interaction:**
|
||||
- Administrator clicks "Create User" button → Switches to Create View (set `view: 'create'`)
|
||||
- Form shows all editable fields plus password field
|
||||
- Administrator fills in fields
|
||||
- Client-side validation shows errors immediately
|
||||
- Administrator clicks Create → Form validates and submits
|
||||
- Administrator clicks Cancel → Switches back to List View (set `view: 'list'`, `selectedUserId: null`)
|
||||
|
||||
**Form Validation:**
|
||||
- Required field validation (shows error if empty)
|
||||
- Type validation (e.g., email fields must be valid email format)
|
||||
- Select field validation (value must be in options array)
|
||||
- Password validation (minimum 8 characters)
|
||||
- Password confirmation validation (must match password)
|
||||
- Validation errors displayed inline below fields
|
||||
|
||||
**Form Submission:**
|
||||
- User data sent as User object
|
||||
- Password sent as separate field in request body
|
||||
- Success → Switch to Detail View for created user (set `selectedUserId` to new user ID, `view: 'detail'`)
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Editing Users
|
||||
|
||||
**Form Interaction:**
|
||||
- Administrator clicks "Edit" button → Switches to Edit View (set `view: 'edit'`)
|
||||
- Form pre-populated with current user values
|
||||
- Administrator modifies fields
|
||||
- Client-side validation shows errors immediately
|
||||
- Administrator clicks Save → Form validates and submits
|
||||
- Administrator clicks Cancel → Switches back to Detail View (set `view: 'detail'`)
|
||||
|
||||
**Form Validation:**
|
||||
- Required field validation (shows error if empty)
|
||||
- Type validation (e.g., email fields must be valid email format)
|
||||
- Select field validation (value must be in options array)
|
||||
- Validation errors displayed inline below fields
|
||||
|
||||
**Form Submission:**
|
||||
- Only changed fields sent (or all form data)
|
||||
- Success → Switch back to Detail View (set `view: 'detail'`) and show success message
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (validation errors) → Display backend validation errors
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Enabling and Disabling Users
|
||||
|
||||
**Toggle Interaction:**
|
||||
- Administrator views user list table
|
||||
- Table displays enabled/disabled status as toggle switch in "Enabled" column
|
||||
- Administrator clicks toggle switch for a user
|
||||
- Frontend immediately updates toggle state (optimistic update)
|
||||
- Frontend sends PUT request to update user's enabled property
|
||||
- Administrator sees immediate feedback (toggle changes instantly)
|
||||
|
||||
**Toggle Submission:**
|
||||
- Success → Keep optimistic update, refetch user list to ensure consistency
|
||||
- Error → Revert toggle to original state, show error message
|
||||
- No confirmation dialog needed - toggle is immediate and reversible
|
||||
|
||||
**Implementation Notes:**
|
||||
- Toggle should be part of the main table column
|
||||
- Use optimistic updates for instant feedback
|
||||
- Refetch list after successful update
|
||||
- Handle errors gracefully by reverting toggle state
|
||||
|
||||
### Customer Journey 5: Enabling and Disabling Users
|
||||
|
||||
> **📋 Complete customer journey documentation is available in [User Customer Journeys](./user-page-customer-journeys.md#customer-journey-5-enabling-and-disabling-users)**
|
||||
|
||||
### Resetting User Passwords
|
||||
|
||||
**Password Reset Interaction:**
|
||||
- Administrator clicks "Reset Password" button
|
||||
- Password reset dialog appears
|
||||
- Administrator enters new password
|
||||
- Administrator confirms new password
|
||||
- Client-side validation:
|
||||
- Password must be at least 8 characters
|
||||
- Passwords must match
|
||||
- Administrator clicks Reset → Password reset request sent
|
||||
- Administrator clicks Cancel → Dialog closes, no action
|
||||
|
||||
**Password Reset Submission:**
|
||||
- Success → Show success message, close dialog
|
||||
- Backend automatically revokes all user tokens
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (password too short) → Show validation error
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Revoking User Sessions
|
||||
|
||||
**Session Revocation Interaction:**
|
||||
- Administrator clicks "Revoke Sessions" button
|
||||
- Session revocation dialog appears
|
||||
- Administrator optionally selects authority filter:
|
||||
- All authorities (default) - revokes all sessions
|
||||
- Local only - revokes only local authentication sessions
|
||||
- Google only - revokes only Google OAuth sessions
|
||||
- Microsoft only - revokes only Microsoft OAuth sessions
|
||||
- Administrator optionally enters reason (for audit logging)
|
||||
- Administrator clicks "Revoke Sessions" → Session revocation request sent
|
||||
- Administrator clicks Cancel → Dialog closes, no action
|
||||
|
||||
**Session Revocation Submission:**
|
||||
- Success → Show success message with count of revoked sessions, close dialog
|
||||
- User will need to log in again on all affected devices
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (invalid parameters) → Show validation error
|
||||
- Other errors → Show generic error message
|
||||
|
||||
**Use Cases:**
|
||||
- Security incident: User's account may have been compromised
|
||||
- Device management: User lost a device or needs to log out of specific service
|
||||
- Account suspension: Temporarily disable user access
|
||||
- Force re-authentication: Make user log in again without changing password
|
||||
|
||||
**Password Reset Interaction:**
|
||||
- Administrator clicks "Reset Password" button
|
||||
- Password reset dialog appears
|
||||
- Administrator enters new password
|
||||
- Administrator confirms new password
|
||||
- Client-side validation:
|
||||
- Password must be at least 8 characters
|
||||
- Passwords must match
|
||||
- Administrator clicks Reset → Password reset request sent
|
||||
- Administrator clicks Cancel → Dialog closes, no action
|
||||
|
||||
**Password Reset Submission:**
|
||||
- Success → Show success message, close dialog
|
||||
- Backend automatically revokes all user tokens
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error
|
||||
- 400 (password too short) → Show validation error
|
||||
- Other errors → Show generic error message
|
||||
|
||||
### Customer Journey 7: Revoking User Sessions
|
||||
|
||||
> **📋 Complete customer journey documentation is available in [User Customer Journeys](./user-page-customer-journeys.md#customer-journey-7-revoking-user-sessions)**
|
||||
|
||||
### Deleting Users
|
||||
|
||||
**Delete Action:**
|
||||
- Administrator clicks delete button (on detail page or list)
|
||||
- Confirmation dialog appears with user name/username
|
||||
- Warning about permanent deletion
|
||||
- Administrator confirms → User deleted
|
||||
- Administrator cancels → Dialog closes, no action
|
||||
|
||||
**Delete Success Handling:**
|
||||
- Success message displayed
|
||||
- If in Detail View → Switch back to List View (set `view: 'list'`, `selectedUserId: null`)
|
||||
- If in List View → Remove user from list or refresh
|
||||
|
||||
---
|
||||
|
||||
## Backend Routes and API Integration
|
||||
|
||||
### Complete Route Reference
|
||||
|
||||
All backend routes used by user pages:
|
||||
|
||||
| Route | Method | Purpose | When Used | Access Control |
|
||||
|-------|--------|---------|-----------|----------------|
|
||||
| `/api/users/` | GET | Get all users (with pagination) | Initial page load, pagination changes, sort changes, filter changes, search changes | All authenticated users (filtered by mandate) |
|
||||
| `/api/users/{userId}` | GET | Get user details | Detail page load, edit page load | All authenticated users |
|
||||
| `/api/users/{userId}` | PUT | Update user | Edit form submission | All authenticated users |
|
||||
| `/api/users/{userId}` | DELETE | Delete user | User confirms deletion | All authenticated users |
|
||||
| `/api/users` | POST | Create user | Create form submission | All authenticated users |
|
||||
| `/api/users/{userId}/reset-password` | POST | Reset user password | Admin confirms password reset | **Admin-only** |
|
||||
| `/api/admin/tokens/revoke/user` | POST | Revoke all active sessions for a user | Admin confirms session revocation | **Admin-only** |
|
||||
| `/api/users/change-password` | POST | Change current user's password | User changes own password | Current user only (not admin-only) |
|
||||
| `/api/attributes/User` | GET | Get field definitions | Page load (once per page) - used to generate all UI components | All authenticated users |
|
||||
|
||||
### API Request Patterns
|
||||
|
||||
**Pagination Request:**
|
||||
```
|
||||
GET /api/users/?pagination={"page":1,"pageSize":20,"sort":[],"filters":null}
|
||||
```
|
||||
- `pagination` parameter is JSON-encoded string
|
||||
- If user wants all users: omit `pagination` parameter entirely
|
||||
- Filters structure: `{"search":"query","fieldName":"value",...}`
|
||||
- Optional `mandateId` query parameter to filter by mandate
|
||||
|
||||
**Create Request:**
|
||||
```
|
||||
POST /api/users
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"username": "john.doe",
|
||||
"email": "john@example.com",
|
||||
"fullName": "John Doe",
|
||||
"language": "en",
|
||||
"enabled": true,
|
||||
"privilege": "user",
|
||||
"authenticationAuthority": "local",
|
||||
"password": "securepassword123"
|
||||
}
|
||||
```
|
||||
- Password is sent as separate field in body (not part of User model)
|
||||
- All User model fields sent as User object
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Update Request:**
|
||||
```
|
||||
PUT /api/users/{userId}
|
||||
Content-Type: application/json
|
||||
Body: {fieldName: value, ...}
|
||||
```
|
||||
- Send only changed fields or all form data
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Password Reset Request:**
|
||||
```
|
||||
POST /api/users/{userId}/reset-password
|
||||
Content-Type: application/json
|
||||
Body: {"newPassword": "newsecurepassword123"}
|
||||
```
|
||||
- **Admin-only endpoint**
|
||||
- Password must be at least 8 characters
|
||||
- Backend automatically revokes all user tokens
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Session Revocation Request:**
|
||||
```
|
||||
POST /api/admin/tokens/revoke/user
|
||||
Content-Type: application/json
|
||||
Body: {
|
||||
"userId": "user-id-here",
|
||||
"authority": "local", // Optional: "local", "google", "msft", or omit for all
|
||||
"reason": "Security incident" // Optional reason for audit logging
|
||||
}
|
||||
```
|
||||
- **Admin-only endpoint**
|
||||
- `userId` is required
|
||||
- `authority` is optional - if omitted, revokes all sessions regardless of authority
|
||||
- `reason` is optional - defaults to "admin revoke" if not provided
|
||||
- Returns `{"revoked": count}` - number of sessions revoked
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Delete Requests:**
|
||||
```
|
||||
DELETE /api/users/{userId}
|
||||
```
|
||||
- Delete operation requires user confirmation
|
||||
- Handle 403 (permission denied) and 404 (not found) gracefully
|
||||
|
||||
### Response Handling
|
||||
|
||||
**Paginated Response:**
|
||||
```json
|
||||
{
|
||||
"items": [...],
|
||||
"pagination": {
|
||||
"currentPage": 1,
|
||||
"pageSize": 20,
|
||||
"totalItems": 45,
|
||||
"totalPages": 3,
|
||||
"sort": [...],
|
||||
"filters": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- 403 Forbidden → Show permission error message (admin-only actions)
|
||||
- 404 Not Found → Show "not found" error message
|
||||
- 400 Bad Request → Display validation errors from response
|
||||
- 500 Internal Server Error → Show generic error message
|
||||
|
||||
---
|
||||
|
||||
## Field and Attribute Reference
|
||||
|
||||
### Complete Field List
|
||||
|
||||
The following is a comprehensive list of all parameters, attributes, and fields that will be displayed for users. All of these are provided by the backend through the `/api/attributes/User` endpoint and should never be hardcoded in the frontend.
|
||||
|
||||
#### Core User Fields
|
||||
|
||||
**Identification Fields:**
|
||||
- `id` - Unique user identifier (text, readonly, not required, visible)
|
||||
- `mandateId` - ID of the mandate this user belongs to (text, readonly, not required, visible)
|
||||
|
||||
**Account Fields:**
|
||||
- `username` - Username for login (text, editable, required, visible)
|
||||
- `email` - Email address of the user (email, editable, required, visible)
|
||||
- `fullName` - Full name of the user (text, editable, not required, visible)
|
||||
|
||||
**Configuration Fields:**
|
||||
- `language` - Preferred language of the user (select, editable, required, visible)
|
||||
- Options: "de", "en", "fr", "it"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `enabled` - Indicates whether the user is enabled (checkbox, editable, not required, visible)
|
||||
- `privilege` - Permission level (select, editable, required, visible)
|
||||
- Options: "user", "admin", "sysadmin"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `authenticationAuthority` - Primary authentication authority (select, readonly, not required, visible)
|
||||
- Options: "local", "google", "msft"
|
||||
- Each option has localized labels (en/fr)
|
||||
|
||||
**Special Fields (Not in User Model):**
|
||||
- `password` - Password field for create forms (password, not in attributes, required for creation)
|
||||
- `confirmPassword` - Password confirmation (password, frontend-only, required for creation)
|
||||
|
||||
### Pagination Parameters
|
||||
|
||||
**Request Parameters (`PaginationParams`):**
|
||||
- `page` - Current page number (1-based, minimum 1)
|
||||
- `pageSize` - Number of items per page (minimum 1, maximum 1000)
|
||||
- `sort` - Array of sort field configurations
|
||||
- Each sort field contains:
|
||||
- `field` - Field name to sort by (must match a user field name)
|
||||
- `direction` - Sort direction: "asc" or "desc"
|
||||
- `filters` - Filter criteria dictionary
|
||||
- `search` - General search term (searches across all text fields, case-insensitive)
|
||||
- Field-specific filters: `{fieldName: value}` or `{fieldName: {operator: "operator", value: value}}`
|
||||
- Supported operators: "equals", "contains", "startsWith", "endsWith", "in", "notIn"
|
||||
|
||||
**Response Metadata (`PaginationMetadata`):**
|
||||
- `currentPage` - Current page number (1-based)
|
||||
- `pageSize` - Number of items per page
|
||||
- `totalItems` - Total number of items across all pages (after filters applied)
|
||||
- `totalPages` - Total number of pages (calculated from totalItems / pageSize)
|
||||
- `sort` - Current sort configuration applied (array of SortField objects)
|
||||
- `filters` - Current filters applied (mirrors request filters)
|
||||
|
||||
### Attribute Definition Structure
|
||||
|
||||
Each field returned from `/api/attributes/User` contains:
|
||||
|
||||
- `name` - Field name (e.g., "username", "email", "privilege")
|
||||
- `type` - Field data type (e.g., "text", "email", "select", "checkbox")
|
||||
- `label` - Localized field label (object with language keys: {"en": "English Label", "fr": "French Label"})
|
||||
- `description` - Field description text
|
||||
- `required` - Boolean indicating if field is required
|
||||
- `readonly` - Boolean indicating if field is read-only
|
||||
- `editable` - Boolean indicating if field can be edited (inverse of readonly)
|
||||
- `visible` - Boolean indicating if field should be displayed in UI
|
||||
- `options` - Array of options for select fields (each option has `value` and localized `label`)
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Rendering Guidelines
|
||||
|
||||
The frontend must render all UI components dynamically based on backend metadata. No field definitions, labels, validation rules, or UI structure should be hardcoded.
|
||||
|
||||
### Table Column Generation
|
||||
|
||||
When rendering the user list table:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/User`
|
||||
2. Filter attributes where `visible: true` to determine which columns to display
|
||||
3. Use `label` property for column headers (select appropriate language based on user preference)
|
||||
4. Use `type` property to determine how to format cell values:
|
||||
- `text` fields → Display as plain text
|
||||
- `email` fields → Display as clickable mailto link
|
||||
- `select` fields → Display value using label from options array (match value to option.value, then display option.label)
|
||||
- `checkbox` fields → Display as checkmark icon or "Enabled"/"Disabled" text
|
||||
5. Use `readonly` property to determine if column should be sortable (readonly fields may still be sortable, but editable fields definitely are)
|
||||
6. Generate click handlers for column headers to update sort parameters and refetch data
|
||||
|
||||
### Filter Control Generation
|
||||
|
||||
When rendering filter controls:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/User`
|
||||
2. Filter attributes where `visible: true` to determine which filters to show
|
||||
3. For each visible field, generate appropriate filter UI based on `type`:
|
||||
- `text` fields → Text input filter (supports "contains", "equals", "startsWith", "endsWith" operators)
|
||||
- `email` fields → Email input filter
|
||||
- `select` fields → Dropdown filter with options from `options` array (use localized labels)
|
||||
- `checkbox` fields → Boolean toggle filter (true/false/any)
|
||||
4. Use `label` property for filter labels (localized)
|
||||
5. When administrator applies filter, update `filters` object in pagination parameters and refetch data
|
||||
6. Display active filters as chips/badges showing field label and value
|
||||
7. Allow removing individual filters by removing them from `filters` object
|
||||
|
||||
### Search Implementation
|
||||
|
||||
For general search functionality:
|
||||
|
||||
1. Display a single search input box (not field-specific)
|
||||
2. When administrator types, update `filters.search` in pagination parameters
|
||||
3. Debounce search input (wait 300-500ms after user stops typing before sending request)
|
||||
4. Search applies across all text fields in the user object
|
||||
5. Reset to page 1 when search query changes
|
||||
6. Combine search with field-specific filters (both are in the `filters` object)
|
||||
|
||||
### Form Field Generation
|
||||
|
||||
When rendering edit forms:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/User`
|
||||
2. Filter attributes where `visible: true` AND `editable: true` to determine which fields to show
|
||||
3. For each editable field, generate appropriate form input based on `type`:
|
||||
- `text` fields → Text input
|
||||
- `email` fields → Email input
|
||||
- `select` fields → Dropdown/select input with options from `options` array (use localized labels)
|
||||
- `checkbox` fields → Checkbox input
|
||||
4. Use `label` property for field labels (localized)
|
||||
5. Use `required` property to show required indicators (asterisk, etc.)
|
||||
6. Use `description` property to show help text or tooltips
|
||||
7. Validate form before submission:
|
||||
- Check all `required: true` fields have values
|
||||
- Validate types (e.g., email fields must be valid email format)
|
||||
- Validate select fields (value must be in options array)
|
||||
8. On submit, send only changed fields or all form data to update endpoint
|
||||
|
||||
### Create Form Field Generation
|
||||
|
||||
When rendering create forms:
|
||||
|
||||
1. Follow same pattern as edit forms for User model fields
|
||||
2. **Add password field** (not in User model attributes):
|
||||
- Password input field
|
||||
- Confirm password input field (frontend-only validation)
|
||||
- Both fields required for creation
|
||||
- Validate password strength (minimum 8 characters)
|
||||
- Validate passwords match
|
||||
3. On submit, send User object fields plus `password` as separate field in request body
|
||||
|
||||
### Display Formatting
|
||||
|
||||
When displaying field values:
|
||||
|
||||
1. Use `type` property to determine formatting:
|
||||
- `text` → Display as-is (may need HTML escaping)
|
||||
- `email` → Display as clickable mailto link
|
||||
- `select` → Look up value in `options` array and display localized label
|
||||
- `checkbox` → Display as checkmark icon or "Enabled"/"Disabled" text
|
||||
2. Handle `null` or `undefined` values gracefully (show "-" or "Not set")
|
||||
3. Use `readonly` property to determine if field should show edit indicators
|
||||
4. Special formatting for privilege field:
|
||||
- Color-code badges (User = blue, Admin = orange, SysAdmin = red)
|
||||
- Use localized labels from options
|
||||
|
||||
### Localization
|
||||
|
||||
All labels and options support multiple languages:
|
||||
|
||||
1. Use user's preferred language (from user settings or browser locale)
|
||||
2. Access localized labels from `label` object: `label[userLanguage]` or `label.en` as fallback
|
||||
3. For select options, use `option.label[userLanguage]` or `option.label.en` as fallback
|
||||
4. If label for current language is missing, fall back to English
|
||||
|
||||
### Key Principles
|
||||
|
||||
- Never hardcode field names, labels, types, or validation rules
|
||||
- Always fetch attribute definitions from backend before rendering UI
|
||||
- Use attribute metadata to determine what to display and how to display it
|
||||
- Support all field types dynamically - if backend adds new types, frontend should handle them
|
||||
- Respect `visible`, `editable`, `readonly`, and `required` flags from backend
|
||||
- Use localized labels from backend metadata
|
||||
- Generate filters, forms, and tables entirely from attribute definitions
|
||||
- When backend adds new fields, frontend should automatically display them without code changes
|
||||
- Handle all error cases gracefully (403, 404, 400, 500)
|
||||
- Provide user feedback for all actions (loading states, success messages, error messages)
|
||||
- **Always check admin privileges before rendering user management UI**
|
||||
|
||||
---
|
||||
|
||||
## Admin Access Control
|
||||
|
||||
### Frontend Access Control
|
||||
|
||||
The frontend must implement access control to ensure only administrators can access user management pages:
|
||||
|
||||
**1. Navigation Link Visibility:**
|
||||
```typescript
|
||||
// Only show user management link if user is admin
|
||||
{currentUser?.privilege === 'admin' || currentUser?.privilege === 'sysadmin' ? (
|
||||
<NavLink to="/users">Manage Users</NavLink>
|
||||
) : null}
|
||||
```
|
||||
|
||||
**2. Route Protection:**
|
||||
```typescript
|
||||
// Protect route with admin check - single route for all views
|
||||
function UserManagementPage() {
|
||||
const { currentUser } = useAuth();
|
||||
const [view, setView] = useState<'list' | 'detail' | 'edit' | 'create'>('list');
|
||||
const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
|
||||
|
||||
// Check admin status
|
||||
if (currentUser?.privilege !== 'admin' && currentUser?.privilege !== 'sysadmin') {
|
||||
return <UnauthorizedAccess />;
|
||||
}
|
||||
|
||||
// Render appropriate view based on state
|
||||
if (view === 'list') {
|
||||
return <ListView onUserSelect={(id) => { setSelectedUserId(id); setView('detail'); }} onCreate={() => setView('create')} />;
|
||||
} else if (view === 'detail' && selectedUserId) {
|
||||
return <DetailView userId={selectedUserId} onEdit={() => setView('edit')} onBack={() => { setSelectedUserId(null); setView('list'); }} />;
|
||||
} else if (view === 'edit' && selectedUserId) {
|
||||
return <EditView userId={selectedUserId} onSave={() => setView('detail')} onCancel={() => setView('detail')} />;
|
||||
} else if (view === 'create') {
|
||||
return <CreateView onSave={(userId) => { setSelectedUserId(userId); setView('detail'); }} onCancel={() => setView('list')} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
**3. Component-Level Checks:**
|
||||
```typescript
|
||||
// Check admin status before rendering components
|
||||
function ListView({ onUserSelect }) {
|
||||
const { currentUser } = useAuth();
|
||||
|
||||
if (currentUser?.privilege !== 'admin' && currentUser?.privilege !== 'sysadmin') {
|
||||
return <UnauthorizedAccess />;
|
||||
}
|
||||
|
||||
// Render list view UI
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={() => onUserSelect(null)}>Create User</Button>
|
||||
{/* List table */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**4. Error Handling:**
|
||||
```typescript
|
||||
// Handle 403 errors gracefully
|
||||
try {
|
||||
const response = await fetch('/api/users/userId/reset-password', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ newPassword })
|
||||
});
|
||||
|
||||
if (response.status === 403) {
|
||||
showError('You do not have permission to reset passwords');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle other responses
|
||||
} catch (error) {
|
||||
showError('Failed to reset password');
|
||||
}
|
||||
```
|
||||
|
||||
**5. View State Management:**
|
||||
```typescript
|
||||
// Manage views within the same page component
|
||||
function UserManagementPage() {
|
||||
const [view, setView] = useState<'list' | 'detail' | 'edit' | 'create'>('list');
|
||||
const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
|
||||
|
||||
// Switch to detail view
|
||||
const handleUserSelect = (userId: string) => {
|
||||
setSelectedUserId(userId);
|
||||
setView('detail');
|
||||
};
|
||||
|
||||
// Switch to edit view
|
||||
const handleEdit = () => {
|
||||
setView('edit');
|
||||
};
|
||||
|
||||
// Switch to create view
|
||||
const handleCreate = () => {
|
||||
setSelectedUserId(null);
|
||||
setView('create');
|
||||
};
|
||||
|
||||
// Switch back to list view
|
||||
const handleBackToList = () => {
|
||||
setSelectedUserId(null);
|
||||
setView('list');
|
||||
};
|
||||
|
||||
// Render based on view state
|
||||
return (
|
||||
<>
|
||||
{view === 'list' && <ListView onUserSelect={handleUserSelect} onCreate={handleCreate} />}
|
||||
{view === 'detail' && selectedUserId && <DetailView userId={selectedUserId} onEdit={handleEdit} onBack={handleBackToList} />}
|
||||
{view === 'edit' && selectedUserId && <EditView userId={selectedUserId} onSave={() => setView('detail')} onCancel={() => setView('detail')} />}
|
||||
{view === 'create' && <CreateView onSave={(userId) => { setSelectedUserId(userId); setView('detail'); }} onCancel={handleBackToList} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Admin-Only Actions
|
||||
|
||||
The following actions are admin-only and should only be available to administrators:
|
||||
|
||||
- **Reset User Password** (`POST /api/users/{userId}/reset-password`)
|
||||
- Only admins can reset other users' passwords
|
||||
- Backend enforces this with privilege check
|
||||
- Frontend should hide button for non-admins
|
||||
|
||||
- **User Management Page**
|
||||
- The user management page should be admin-only
|
||||
- Navigation links should be hidden for non-admins
|
||||
- Direct URL access should show unauthorized message for non-admins
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
1. **Never trust frontend-only checks** - Backend always enforces permissions
|
||||
2. **Handle 403 errors gracefully** - Show appropriate error messages
|
||||
3. **Hide UI elements** - Don't show admin actions to non-admins
|
||||
4. **Redirect unauthorized access** - Redirect non-admins away from admin pages
|
||||
5. **Log security events** - Backend logs admin actions (handled by backend)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This document provides complete frontend requirements for all user management pages and components. All requirements follow the principle of **backend-driven UI generation**—no hardcoding of field definitions, labels, or validation rules. The frontend should dynamically generate all UI components from backend metadata provided through the `/api/attributes/User` endpoint.
|
||||
|
||||
**Critical Security Requirement:** All user management pages are **admin-only**. The frontend must:
|
||||
- Check user privileges before rendering navigation links
|
||||
- Check user privileges before allowing access to pages
|
||||
- Handle 403 Forbidden errors gracefully
|
||||
- Never expose user management UI to non-admin users
|
||||
|
||||
For generic patterns that apply across all entity types (not just users), see the [Dynamic Forms and Pagination documentation](./dynamic-forms-and-pagination.md).
|
||||
|
||||
|
|
@ -1,474 +0,0 @@
|
|||
# Voice Service Customer Journeys
|
||||
|
||||
This document describes customer journeys for the Google Cloud Voice Services, focusing on how users interact with speech-to-text, translation, text-to-speech, and real-time voice interpretation features.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Customer Journey 1: Converting Speech to Text](#customer-journey-1-converting-speech-to-text)
|
||||
3. [Customer Journey 2: Translating Text](#customer-journey-2-translating-text)
|
||||
4. [Customer Journey 3: Real-time Voice Interpretation](#customer-journey-3-real-time-voice-interpretation)
|
||||
5. [Customer Journey 4: Converting Text to Speech](#customer-journey-4-converting-text-to-speech)
|
||||
6. [Customer Journey 5: Managing Voice Settings](#customer-journey-5-managing-voice-settings)
|
||||
7. [Customer Journey 6: Discovering Available Languages and Voices](#customer-journey-6-discovering-available-languages-and-voices)
|
||||
8. [Customer Journey 7: Real-time Speech-to-Text via WebSocket](#customer-journey-7-real-time-speech-to-text-via-websocket)
|
||||
9. [Customer Journey 8: Real-time Text-to-Speech via WebSocket](#customer-journey-8-real-time-text-to-speech-via-websocket)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The voice service routes (`/api/voice-google`) enable users to interact with Google Cloud voice services including Speech-to-Text, Translation, Text-to-Speech, and real-time voice interpretation. These routes support both HTTP REST endpoints for file-based processing and WebSocket endpoints for real-time streaming.
|
||||
|
||||
**Key Principles:**
|
||||
- **User-Centric**: Documentation organized around what users want to accomplish
|
||||
- **Multi-Modal**: Supports both file upload and real-time streaming
|
||||
- **Language-Aware**: All operations support multiple languages with user-configurable defaults
|
||||
- **Settings-Driven**: User preferences stored and applied automatically
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 1: Converting Speech to Text
|
||||
|
||||
### User Goal
|
||||
"I want to record my speech and convert it into text."
|
||||
|
||||
### User Story
|
||||
As a user, I want to record my speech through the frontend microphone and get the transcribed text with confidence scores and language detection.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Select language (default: de-DE)
|
||||
User->>Frontend: Click "Start Recording"
|
||||
Frontend->>Frontend: Request microphone access
|
||||
alt Microphone access denied
|
||||
Frontend-->>User: Show permission error
|
||||
else Microphone access granted
|
||||
Frontend->>Frontend: Start audio recording
|
||||
Frontend-->>User: Show recording indicator (red dot, timer)
|
||||
|
||||
User->>Frontend: Speak into microphone
|
||||
Frontend->>Frontend: Capture audio stream
|
||||
|
||||
User->>Frontend: Click "Stop Recording"
|
||||
Frontend->>Frontend: Stop audio recording
|
||||
Frontend->>Frontend: Convert audio stream to audio file
|
||||
Frontend->>Frontend: Validate audio file (size, format)
|
||||
|
||||
alt File validation fails
|
||||
Frontend-->>User: Show validation error
|
||||
else File validation passes
|
||||
Frontend->>Frontend: Show loading state
|
||||
Frontend->>Backend: POST /api/voice-google/speech-to-text<br/>(audioFile, language)
|
||||
|
||||
alt Authentication fails (401)
|
||||
Backend-->>Frontend: 401 Unauthorized
|
||||
Frontend-->>User: Show authentication error
|
||||
else Invalid audio format (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show format error message
|
||||
else Speech recognition fails (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show recognition error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: {success: true, text, confidence, language, audio_info}
|
||||
Frontend->>Frontend: Display transcribed text
|
||||
Frontend->>Frontend: Display confidence score
|
||||
Frontend->>Frontend: Display detected language
|
||||
Frontend->>Frontend: Display audio metadata
|
||||
Frontend-->>User: Show transcription result with metadata
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 2: Translating Text
|
||||
|
||||
### User Goal
|
||||
"I want to translate text from one language to another."
|
||||
|
||||
### User Story
|
||||
As a user, I want to enter text and translate it between languages, seeing both the original and translated text.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Enter text to translate
|
||||
User->>Frontend: Select source language (default: de)
|
||||
User->>Frontend: Select target language (default: en)
|
||||
User->>Frontend: Click "Translate"
|
||||
|
||||
Frontend->>Frontend: Validate text is not empty
|
||||
alt Text is empty
|
||||
Frontend-->>User: Show validation error
|
||||
else Text validation passes
|
||||
Frontend->>Frontend: Show loading state
|
||||
Frontend->>Backend: POST /api/voice-google/translate<br/>(text, sourceLanguage, targetLanguage)
|
||||
|
||||
alt Authentication fails (401)
|
||||
Backend-->>Frontend: 401 Unauthorized
|
||||
Frontend-->>User: Show authentication error
|
||||
else Empty text (400)
|
||||
Backend-->>Frontend: 400 Bad Request
|
||||
Frontend-->>User: Show empty text error
|
||||
else Translation fails (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show translation error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: {success: true, original_text, translated_text, source_language, target_language}
|
||||
Frontend->>Frontend: Display original text
|
||||
Frontend->>Frontend: Display translated text
|
||||
Frontend->>Frontend: Display language information
|
||||
Frontend-->>User: Show translation result
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 3: Real-time Voice Interpretation
|
||||
|
||||
### User Goal
|
||||
"I want to speak in one language and get the translated text in another language."
|
||||
|
||||
### User Story
|
||||
As a user, I want to record my speech through the frontend microphone and receive both the transcribed text in the source language and its translation in the target language.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Select source language (default: de-DE)
|
||||
User->>Frontend: Select target language (default: en-US)
|
||||
User->>Frontend: Click "Start Recording"
|
||||
Frontend->>Frontend: Request microphone access
|
||||
alt Microphone access denied
|
||||
Frontend-->>User: Show permission error
|
||||
else Microphone access granted
|
||||
Frontend->>Frontend: Start audio recording
|
||||
Frontend-->>User: Show recording indicator (red dot, timer)
|
||||
|
||||
User->>Frontend: Speak into microphone
|
||||
Frontend->>Frontend: Capture audio stream
|
||||
|
||||
User->>Frontend: Click "Stop Recording"
|
||||
Frontend->>Frontend: Stop audio recording
|
||||
Frontend->>Frontend: Convert audio stream to audio file
|
||||
Frontend->>Frontend: Validate audio file
|
||||
|
||||
alt File validation fails
|
||||
Frontend-->>User: Show validation error
|
||||
else File validation passes
|
||||
Frontend->>Frontend: Show loading state
|
||||
Frontend->>Backend: POST /api/voice-google/realtime-interpreter<br/>(audioFile, fromLanguage, toLanguage, connectionId?)
|
||||
|
||||
alt Authentication fails (401)
|
||||
Backend-->>Frontend: 401 Unauthorized
|
||||
Frontend-->>User: Show authentication error
|
||||
else Invalid audio format (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show format error
|
||||
else Interpretation fails (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show interpretation error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: {success: true, original_text, translated_text, confidence, source_language, target_language, audio_info}
|
||||
Frontend->>Frontend: Display original transcribed text
|
||||
Frontend->>Frontend: Display translated text
|
||||
Frontend->>Frontend: Display confidence score
|
||||
Frontend->>Frontend: Display language information
|
||||
Frontend->>Frontend: Display audio metadata
|
||||
Frontend-->>User: Show interpretation result with both texts
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 4: Converting Text to Speech
|
||||
|
||||
### User Goal
|
||||
"I want to convert written text into spoken audio."
|
||||
|
||||
### User Story
|
||||
As a user, I want to enter text, select a language and voice, and receive an audio file of the spoken text.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Enter text to speak
|
||||
User->>Frontend: Select language (default: de-DE)
|
||||
User->>Frontend: Select voice (optional, default from settings)
|
||||
User->>Frontend: Click "Generate Speech"
|
||||
|
||||
Frontend->>Frontend: Validate text is not empty
|
||||
alt Text is empty
|
||||
Frontend-->>User: Show validation error
|
||||
else Text validation passes
|
||||
Frontend->>Frontend: Show loading state
|
||||
Frontend->>Backend: POST /api/voice-google/text-to-speech<br/>(text, language, voice?)
|
||||
|
||||
alt Authentication fails (401)
|
||||
Backend-->>Frontend: 401 Unauthorized
|
||||
Frontend-->>User: Show authentication error
|
||||
else Empty text (400)
|
||||
Backend-->>Frontend: 400 Bad Request
|
||||
Frontend-->>User: Show empty text error
|
||||
else Text-to-Speech fails (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend-->>User: Show TTS error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Audio file (audio/mpeg)<br/>Headers: X-Voice-Name, X-Language-Code
|
||||
Frontend->>Frontend: Create audio blob from response
|
||||
Frontend->>Frontend: Create download link or audio player
|
||||
Frontend->>Frontend: Display voice name and language
|
||||
Frontend-->>User: Show audio player with download option
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 5: Managing Voice Settings
|
||||
|
||||
### User Goal
|
||||
"I want to configure my default voice settings for speech recognition, translation, and text-to-speech."
|
||||
|
||||
### User Story
|
||||
As a user, I want to view and update my voice settings including default languages, voice preferences, and translation settings.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Navigate to voice settings
|
||||
Frontend->>Backend: GET /api/voice-google/settings
|
||||
Backend-->>Frontend: {success: true, data: {user_settings, default_settings}}
|
||||
Frontend->>Frontend: Pre-populate form with user_settings or default_settings
|
||||
Frontend-->>User: Display settings form
|
||||
|
||||
alt User views settings only
|
||||
User->>Frontend: View current settings
|
||||
Frontend-->>User: Display settings (read-only or editable)
|
||||
else User updates settings
|
||||
User->>Frontend: Modify settings (sttLanguage, ttsLanguage, ttsVoice, translationEnabled, targetLanguage)
|
||||
User->>Frontend: Click "Save Settings"
|
||||
|
||||
Frontend->>Frontend: Validate required fields (sttLanguage, ttsLanguage, ttsVoice)
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Show loading state
|
||||
Frontend->>Backend: POST /api/voice-google/settings<br/>(settings object)
|
||||
|
||||
alt Authentication fails (401)
|
||||
Backend-->>Frontend: 401 Unauthorized
|
||||
Frontend-->>User: Show authentication error
|
||||
else Missing required field (400)
|
||||
Backend-->>Frontend: 400 Bad Request + field name
|
||||
Frontend-->>User: Show missing field error
|
||||
else Save fails (500)
|
||||
Backend-->>Frontend: 500 Internal Server Error
|
||||
Frontend-->>User: Show save error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: {success: true, message, data: settings}
|
||||
Frontend->>Frontend: Update UI with saved settings
|
||||
Frontend->>Frontend: Show success message
|
||||
Frontend-->>User: Display confirmation and updated settings
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 6: Discovering Available Languages and Voices
|
||||
|
||||
### User Goal
|
||||
"I want to see what languages and voices are available for speech and translation services."
|
||||
|
||||
### User Story
|
||||
As a user, I want to browse available languages and filter voices by language to configure my preferences.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
alt User wants to see available languages
|
||||
User->>Frontend: Navigate to language selection
|
||||
Frontend->>Backend: GET /api/voice-google/languages
|
||||
Backend-->>Frontend: {success: true, languages: [...]}
|
||||
Frontend->>Frontend: Display language list
|
||||
Frontend-->>User: Show available languages
|
||||
end
|
||||
|
||||
alt User wants to see available voices
|
||||
User->>Frontend: Navigate to voice selection
|
||||
User->>Frontend: Optionally select language filter
|
||||
Frontend->>Backend: GET /api/voice-google/voices<br/>?language_code=de-DE (optional)
|
||||
Backend-->>Frontend: {success: true, voices: [...], language_filter: "de-DE"}
|
||||
Frontend->>Frontend: Display voice list
|
||||
Frontend->>Frontend: Group voices by language if no filter
|
||||
Frontend-->>User: Show available voices
|
||||
end
|
||||
|
||||
alt User filters voices by language
|
||||
User->>Frontend: Select language in filter
|
||||
Frontend->>Backend: GET /api/voice-google/voices?language_code=selected
|
||||
Backend-->>Frontend: Filtered voices list
|
||||
Frontend->>Frontend: Update voice list
|
||||
Frontend-->>User: Show filtered voices
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 7: Real-time Speech-to-Text via WebSocket
|
||||
|
||||
### User Goal
|
||||
"I want to get real-time transcription as I speak, without uploading a complete audio file."
|
||||
|
||||
### User Story
|
||||
As a user, I want to establish a WebSocket connection and stream audio chunks to receive live transcription results.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Initiate real-time speech-to-text
|
||||
User->>Frontend: Select language (default: de-DE)
|
||||
Frontend->>Frontend: Start audio capture
|
||||
Frontend->>Backend: WebSocket: /api/voice-google/ws/speech-to-text<br/>?userId=user&language=de-DE
|
||||
|
||||
Backend->>Backend: Accept WebSocket connection
|
||||
Backend->>Backend: Initialize voice interface
|
||||
Backend-->>Frontend: {type: "connected", connection_id, message}
|
||||
Frontend-->>User: Show "Connected" status
|
||||
|
||||
loop User speaks
|
||||
User->>Frontend: Speak into microphone
|
||||
Frontend->>Frontend: Capture audio chunk
|
||||
Frontend->>Frontend: Encode audio chunk to base64
|
||||
Frontend->>Backend: {type: "audio_chunk", data: base64_audio, timestamp}
|
||||
|
||||
alt Processing error
|
||||
Backend->>Backend: Log error
|
||||
Backend-->>Frontend: {type: "error", error: "..."}
|
||||
Frontend-->>User: Show error message
|
||||
else Success
|
||||
Backend->>Backend: Process audio chunk (Speech-to-Text)
|
||||
Backend-->>Frontend: {type: "transcription_result", text, confidence, is_final}
|
||||
Frontend->>Frontend: Update transcription display
|
||||
Frontend-->>User: Show live transcription (interim or final)
|
||||
end
|
||||
end
|
||||
|
||||
alt User sends ping
|
||||
User->>Frontend: Keep-alive ping
|
||||
Frontend->>Backend: {type: "ping", timestamp}
|
||||
Backend-->>Frontend: {type: "pong", timestamp}
|
||||
end
|
||||
|
||||
alt User stops or disconnects
|
||||
User->>Frontend: Stop recording
|
||||
Frontend->>Backend: Close WebSocket
|
||||
Backend->>Backend: Cleanup connection
|
||||
Frontend-->>User: Show disconnected status
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 8: Real-time Text-to-Speech via WebSocket
|
||||
|
||||
### User Goal
|
||||
"I want to send text and receive audio streams in real-time without waiting for a complete file."
|
||||
|
||||
### User Story
|
||||
As a user, I want to establish a WebSocket connection, send text messages, and receive audio chunks for playback.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Initiate real-time text-to-speech
|
||||
User->>Frontend: Select language (default: de-DE)
|
||||
User->>Frontend: Select voice (default: de-DE-Wavenet-A)
|
||||
Frontend->>Backend: WebSocket: /api/voice-google/ws/text-to-speech<br/>?userId=user&language=de-DE&voice=de-DE-Wavenet-A
|
||||
|
||||
Backend->>Backend: Accept WebSocket connection
|
||||
Backend-->>Frontend: {type: "connected", connection_id, message}
|
||||
Frontend-->>User: Show "Connected" status
|
||||
|
||||
loop User sends text
|
||||
User->>Frontend: Enter text to speak
|
||||
User->>Frontend: Click "Speak" or send
|
||||
Frontend->>Backend: {type: "text_to_speak", text: "..."}
|
||||
|
||||
alt Processing error
|
||||
Backend->>Backend: Log error
|
||||
Backend-->>Frontend: {type: "error", error: "..."}
|
||||
Frontend-->>User: Show error message
|
||||
else Success
|
||||
Backend->>Backend: Process text-to-speech
|
||||
Backend-->>Frontend: {type: "audio_data", audio: base64_audio, format: "mp3"}
|
||||
Frontend->>Frontend: Decode base64 audio
|
||||
Frontend->>Frontend: Create audio blob
|
||||
Frontend->>Frontend: Play audio or queue for playback
|
||||
Frontend-->>User: Play generated speech audio
|
||||
end
|
||||
end
|
||||
|
||||
alt User sends ping
|
||||
User->>Frontend: Keep-alive ping
|
||||
Frontend->>Backend: {type: "ping", timestamp}
|
||||
Backend-->>Frontend: {type: "pong", timestamp}
|
||||
end
|
||||
|
||||
alt User disconnects
|
||||
User->>Frontend: Close connection
|
||||
Frontend->>Backend: Close WebSocket
|
||||
Backend->>Backend: Cleanup connection
|
||||
Frontend-->>User: Show disconnected status
|
||||
end
|
||||
```
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,768 +0,0 @@
|
|||
# Workflow Routes Frontend Documentation
|
||||
|
||||
This document describes customer journeys for managing workflows through the frontend, focusing on how users interact with workflows and how the backend routes support these experiences. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Customer Journey 1: Discovering and Browsing Workflows](#customer-journey-1-discovering-and-browsing-workflows)
|
||||
3. [Customer Journey 2: Viewing Workflow Details](#customer-journey-2-viewing-workflow-details)
|
||||
4. [Customer Journey 3: Monitoring Workflow Execution](#customer-journey-3-monitoring-workflow-execution)
|
||||
5. [Customer Journey 4: Editing Workflow Properties](#customer-journey-4-editing-workflow-properties)
|
||||
6. [Customer Journey 5: Managing Workflow Messages](#customer-journey-5-managing-workflow-messages)
|
||||
7. [Customer Journey 6: Cleaning Up Workflows](#customer-journey-6-cleaning-up-workflows)
|
||||
8. [Backend Metadata System](#backend-metadata-system)
|
||||
9. [Implementation Patterns](#implementation-patterns)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The workflow routes (`/api/workflows`) enable users to manage and monitor workflows that are already running or completed. These routes focus on **management and monitoring** rather than creation (workflows are created through the chat playground routes).
|
||||
|
||||
**Key Principles:**
|
||||
- **User-Centric**: Documentation organized around what users want to accomplish
|
||||
- **Backend-Driven**: All forms, tables, and UI components generated from backend metadata
|
||||
- **No Hardcoding**: Field definitions, labels, validation rules, and options come from the backend
|
||||
- **Real-Time Updates**: Support for polling and incremental data loading
|
||||
- **Permission-Aware**: Backend enforces permissions; frontend handles gracefully
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 1: Discovering and Browsing Workflows
|
||||
|
||||
### User Goal
|
||||
"I want to see all my workflows and find the one I'm looking for."
|
||||
|
||||
### User Story
|
||||
As a user, I want to browse my workflows, search for specific workflows, filter by any field, sort them by different criteria, and quickly identify which ones are running, completed, or have errors.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Navigate to /workflows
|
||||
Frontend->>Backend: GET /api/attributes/ChatWorkflow
|
||||
Backend-->>Frontend: Attribute definitions (fields, labels, types)
|
||||
Frontend->>Backend: GET /api/workflows/?pagination=...
|
||||
Backend-->>Frontend: Paginated workflows list
|
||||
Frontend->>Frontend: Generate table columns from attributes
|
||||
Frontend->>Frontend: Generate filter controls from attributes
|
||||
Frontend->>Frontend: Render workflows table + search + filters
|
||||
Frontend-->>User: Display workflow list with search/filter UI
|
||||
|
||||
alt User performs general search
|
||||
User->>Frontend: Type in search box (e.g., "invoice")
|
||||
Frontend->>Frontend: Update search query
|
||||
Frontend->>Backend: GET /api/workflows/?pagination=...filters:{"search":"invoice"}
|
||||
Backend-->>Frontend: Filtered workflows list
|
||||
Frontend-->>User: Display matching workflows
|
||||
end
|
||||
|
||||
alt User applies field filter
|
||||
User->>Frontend: Select filter field (e.g., "Status")
|
||||
Frontend->>Frontend: Show filter options from attribute metadata
|
||||
User->>Frontend: Select filter value (e.g., "running")
|
||||
Frontend->>Frontend: Update filter parameters
|
||||
Frontend->>Backend: GET /api/workflows/?pagination=...filters:{"status":"running"}
|
||||
Backend-->>Frontend: Filtered workflows list
|
||||
Frontend-->>User: Display filtered workflows
|
||||
end
|
||||
|
||||
alt User combines search + filter + sort
|
||||
User->>Frontend: Apply search + filter + sort
|
||||
Frontend->>Frontend: Combine all parameters
|
||||
Frontend->>Backend: GET /api/workflows/?pagination=...filters:{"search":"invoice","status":"running"}...sort:desc
|
||||
Backend-->>Frontend: Filtered, sorted workflows list
|
||||
Frontend-->>User: Display results
|
||||
end
|
||||
|
||||
User->>Frontend: Click column header (e.g., "Last Activity")
|
||||
Frontend->>Frontend: Update sort parameters
|
||||
Frontend->>Backend: GET /api/workflows/?pagination=...sort:desc
|
||||
Backend-->>Frontend: Sorted workflows list
|
||||
Frontend-->>User: Display sorted workflows
|
||||
|
||||
User->>Frontend: Click workflow row
|
||||
Frontend->>Frontend: Navigate to /workflows/workflowId
|
||||
Frontend-->>User: Show workflow detail page
|
||||
```
|
||||
|
||||
|
||||
## Customer Journey 2: Viewing Workflow Details
|
||||
|
||||
### User Goal
|
||||
"I want to see everything about a specific workflow—its current state, what it's doing, and what it has accomplished."
|
||||
|
||||
### User Story
|
||||
As a user, I want to open a workflow and see its complete information, including its configuration, current status, messages, and logs, all in one place.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click workflow from list
|
||||
Frontend->>Backend: GET /api/workflows/workflowId
|
||||
Backend-->>Frontend: Workflow object
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/ChatWorkflow
|
||||
Backend-->>Frontend: Field definitions
|
||||
|
||||
Frontend->>Backend: GET /api/workflows/workflowId/messages
|
||||
Backend-->>Frontend: Messages list
|
||||
|
||||
Frontend->>Backend: GET /api/workflows/workflowId/logs
|
||||
Backend-->>Frontend: Logs list
|
||||
|
||||
Frontend->>Frontend: Generate UI from metadata
|
||||
Frontend->>Frontend: Render workflow info section
|
||||
Frontend->>Frontend: Render messages section
|
||||
Frontend->>Frontend: Render logs section
|
||||
Frontend-->>User: Display complete workflow view
|
||||
```
|
||||
|
||||
### Frontend Requirements
|
||||
|
||||
> **📋 Complete frontend requirements for this journey are documented in [Workflow Page Requirements](./workflow-page-requirements.md#customer-journey-2-viewing-workflow-details)**
|
||||
|
||||
The frontend must implement a workflow detail page that displays workflow information, messages, and logs. All UI components are generated from backend metadata—see the requirements document for complete details.
|
||||
|
||||
```typescript
|
||||
// Fetch workflow and attributes
|
||||
const [workflow, attributes] = await Promise.all([
|
||||
fetch(`/api/workflows/${workflowId}`).then(r => r.json()),
|
||||
fetch('/api/attributes/ChatWorkflow').then(r => r.json())
|
||||
]);
|
||||
|
||||
// Separate editable vs read-only fields
|
||||
const readOnlyFields = attributes.attributes.filter(attr =>
|
||||
attr.visible && !attr.editable
|
||||
);
|
||||
const editableFields = attributes.attributes.filter(attr =>
|
||||
attr.visible && attr.editable
|
||||
);
|
||||
|
||||
// Render information section
|
||||
<WorkflowInfoSection>
|
||||
{readOnlyFields.map(attr => (
|
||||
<InfoField
|
||||
key={attr.name}
|
||||
label={attr.label}
|
||||
value={formatValue(workflow[attr.name], attr.type, attr.options)}
|
||||
/>
|
||||
))}
|
||||
</WorkflowInfoSection>
|
||||
|
||||
// Show edit button if user has editable fields and permission
|
||||
{editableFields.length > 0 && (
|
||||
<Button onClick={() => navigate(`/workflows/${workflowId}/edit`)}>
|
||||
Edit Workflow
|
||||
</Button>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Customer Journey 3: Monitoring Workflow Execution
|
||||
|
||||
### User Goal
|
||||
"I want to watch my workflow as it runs and see updates in real-time."
|
||||
|
||||
### User Story
|
||||
As a user, I want to monitor a running workflow and see new messages and logs appear automatically without refreshing the page.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Open workflow detail page
|
||||
Frontend->>Backend: GET /api/workflows/id/status
|
||||
Backend-->>Frontend: Current workflow status
|
||||
Frontend->>Backend: GET /api/workflows/id/messages
|
||||
Backend-->>Frontend: Initial messages
|
||||
Frontend->>Backend: GET /api/workflows/id/logs
|
||||
Backend-->>Frontend: Initial logs
|
||||
Frontend-->>User: Display initial state
|
||||
|
||||
loop Every 5 seconds (if running)
|
||||
Frontend->>Backend: GET /api/workflows/id/status
|
||||
Backend-->>Frontend: Updated status
|
||||
Frontend->>Frontend: Update status badge
|
||||
Frontend-->>User: Show status update
|
||||
end
|
||||
|
||||
loop Every 3 seconds (if running)
|
||||
Frontend->>Backend: GET /api/workflows/id/messages?messageId=lastId
|
||||
Backend-->>Frontend: New messages (if any)
|
||||
alt New messages exist
|
||||
Frontend->>Frontend: Append to messages list
|
||||
Frontend-->>User: Show new messages
|
||||
end
|
||||
end
|
||||
|
||||
loop Every 3 seconds (if running)
|
||||
Frontend->>Backend: GET /api/workflows/id/logs?logId=lastId
|
||||
Backend-->>Frontend: New logs (if any)
|
||||
alt New logs exist
|
||||
Frontend->>Frontend: Prepend to logs list
|
||||
Frontend-->>User: Show new logs
|
||||
end
|
||||
end
|
||||
|
||||
Backend->>Backend: Workflow completes
|
||||
Frontend->>Backend: GET /api/workflows/id/status
|
||||
Backend-->>Frontend: Status: "completed"
|
||||
Frontend->>Frontend: Stop all polling
|
||||
Frontend-->>User: Show completion message
|
||||
```
|
||||
|
||||
## Customer Journey 4: Editing Workflow Properties
|
||||
|
||||
### User Goal
|
||||
"I want to change workflow settings like its name, status, or mode."
|
||||
|
||||
### User Story
|
||||
As a user, I want to edit a workflow's properties through a form that only shows fields I'm allowed to edit, with validation and clear error messages.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Edit" button
|
||||
Frontend->>Frontend: Navigate to /workflows/id/edit
|
||||
|
||||
Frontend->>Backend: GET /api/workflows/workflowId
|
||||
Backend-->>Frontend: Current workflow data
|
||||
|
||||
Frontend->>Backend: GET /api/attributes/ChatWorkflow
|
||||
Backend-->>Frontend: Field definitions (editable fields)
|
||||
|
||||
Frontend->>Frontend: Filter editable fields
|
||||
Frontend->>Frontend: Generate form from attributes
|
||||
Frontend->>Frontend: Pre-populate form with workflow data
|
||||
Frontend-->>User: Display edit form
|
||||
|
||||
User->>Frontend: Modify form fields
|
||||
User->>Frontend: Click "Save"
|
||||
|
||||
Frontend->>Frontend: Validate form (required fields, types)
|
||||
alt Validation fails
|
||||
Frontend-->>User: Show validation errors
|
||||
else Validation passes
|
||||
Frontend->>Frontend: Optimistic update: Apply changes to UI immediately
|
||||
Frontend->>Backend: PUT /api/workflows/workflowId + form data
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show permission error
|
||||
else Validation error (400)
|
||||
Backend-->>Frontend: 400 Bad Request + error details
|
||||
Frontend->>Frontend: Revert optimistic update
|
||||
Frontend-->>User: Show backend validation errors
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Updated workflow
|
||||
Frontend->>Frontend: Keep optimistic update (or refresh from response)
|
||||
Frontend->>Frontend: Navigate to detail page
|
||||
Frontend-->>User: Show updated workflow
|
||||
end
|
||||
end
|
||||
```
|
||||
## Customer Journey 5: Cleaning Up Workflows
|
||||
|
||||
### User Goal
|
||||
"I want to delete workflows I no longer need."
|
||||
|
||||
### User Story
|
||||
As a user, I want to delete workflows that are completed or no longer useful, with a clear confirmation to prevent accidental deletion.
|
||||
|
||||
### User Story Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Backend
|
||||
|
||||
User->>Frontend: Click "Delete" button
|
||||
Frontend->>Backend: GET /api/workflows/workflowId
|
||||
Backend-->>Frontend: Workflow data (for name)
|
||||
Frontend->>Frontend: Show confirmation dialog<br/>"Delete 'Workflow Name'?"
|
||||
User->>Frontend: Cancel deletion
|
||||
Frontend-->>User: Dialog closed, no action
|
||||
|
||||
User->>Frontend: Click "Delete" button again
|
||||
Frontend->>Backend: GET /api/workflows/workflowId
|
||||
Backend-->>Frontend: Workflow data
|
||||
Frontend->>Frontend: Show confirmation dialog
|
||||
User->>Frontend: Confirm deletion
|
||||
Frontend->>Frontend: Optimistic update: Remove workflow from UI immediately
|
||||
Frontend->>Backend: DELETE /api/workflows/workflowId
|
||||
|
||||
alt Permission denied (403)
|
||||
Backend-->>Frontend: 403 Forbidden
|
||||
Frontend->>Frontend: Revert optimistic update: Restore workflow in UI
|
||||
Frontend-->>User: Show permission error
|
||||
else Not found (404)
|
||||
Backend-->>Frontend: 404 Not Found
|
||||
Frontend->>Frontend: Revert optimistic update: Restore workflow in UI
|
||||
Frontend-->>User: Show not found error
|
||||
else Success (200)
|
||||
Backend-->>Frontend: Success response
|
||||
Frontend->>Frontend: Keep optimistic update (workflow already removed)
|
||||
Frontend->>Frontend: Show success message
|
||||
Frontend->>Frontend: Navigate to /workflows (if on detail page)
|
||||
Frontend-->>User: Display workflow list (without deleted workflow)
|
||||
end
|
||||
```
|
||||
|
||||
### Frontend Requirements
|
||||
|
||||
> **📋 Complete frontend requirements for this journey are documented in [Workflow Page Requirements](./workflow-page-requirements.md#customer-journey-6-cleaning-up-workflows)**
|
||||
|
||||
The frontend must implement workflow deletion with confirmation dialogs and proper error handling. See the requirements document for complete details.
|
||||
|
||||
### Backend Routes Used
|
||||
|
||||
| Route | Purpose | When Used |
|
||||
|-------|---------|-----------|
|
||||
| `DELETE /api/workflows/{workflowId}` | Delete workflow | User confirmation |
|
||||
|
||||
### User Navigation Flow
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> WorkflowDetailPage
|
||||
|
||||
WorkflowDetailPage --> ViewingDeleteButton: Hover delete button
|
||||
ViewingDeleteButton --> ClickingDelete: Click delete
|
||||
|
||||
ClickingDelete --> FetchingWorkflowName: Fetch workflow name
|
||||
FetchingWorkflowName --> ShowingConfirmation: Show dialog
|
||||
|
||||
ShowingConfirmation --> WorkflowDetailPage: Cancel
|
||||
ShowingConfirmation --> ConfirmingDeletion: Confirm
|
||||
|
||||
ConfirmingDeletion --> DeletingWorkflow: API call
|
||||
DeletingWorkflow --> WorkflowListPage: Success
|
||||
DeletingWorkflow --> ShowingError: Error (403/404)
|
||||
|
||||
ShowingError --> WorkflowDetailPage: Dismiss error
|
||||
|
||||
note right of ShowingConfirmation
|
||||
ConfirmationDialog Component:
|
||||
- Workflow name
|
||||
- Warning message
|
||||
- Cancel/Confirm buttons
|
||||
end note
|
||||
|
||||
note right of DeletingWorkflow
|
||||
DELETE /api/workflows/{id}
|
||||
- Backend deletes workflow
|
||||
- Deletes all associated data
|
||||
- Returns success response
|
||||
end note
|
||||
|
||||
note right of WorkflowListPage
|
||||
List automatically refreshes
|
||||
Deleted workflow removed
|
||||
Success message shown
|
||||
end note
|
||||
```
|
||||
|
||||
### Example User Flow
|
||||
|
||||
```
|
||||
1. User is on workflow detail page
|
||||
2. User clicks "Delete" button
|
||||
3. Frontend shows confirmation dialog: "Are you sure you want to delete 'Workflow Name'? This action cannot be undone."
|
||||
4. User confirms deletion
|
||||
5. Frontend calls: DELETE /api/workflows/{workflowId}
|
||||
6. Backend deletes workflow and all associated data
|
||||
7. Frontend receives success response
|
||||
8. Frontend shows success message: "Workflow deleted successfully"
|
||||
9. Frontend redirects to /workflows (list page)
|
||||
10. User sees updated workflow list (without deleted workflow)
|
||||
```
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
```typescript
|
||||
function WorkflowDetailPage({ workflowId }) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleDelete = async () => {
|
||||
// Fetch workflow name for confirmation
|
||||
const workflow = await fetch(`/api/workflows/${workflowId}`).then(r => r.json());
|
||||
|
||||
if (!confirm(`Are you sure you want to delete "${workflow.name || 'this workflow'}"? This action cannot be undone.`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/workflows/${workflowId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (response.status === 403) {
|
||||
showError('You do not have permission to delete this workflow');
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 404) {
|
||||
showError('Workflow not found');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
showError(error.detail || 'Failed to delete workflow');
|
||||
return;
|
||||
}
|
||||
|
||||
showSuccess('Workflow deleted successfully');
|
||||
navigate('/workflows');
|
||||
} catch (error) {
|
||||
showError('Failed to delete workflow');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Workflow content */}
|
||||
<Button onClick={handleDelete} variant="danger">
|
||||
Delete Workflow
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backend Metadata System
|
||||
|
||||
### How Metadata Works
|
||||
|
||||
The backend serves all UI metadata through the `/api/attributes/{entityType}` endpoint. This enables completely dynamic frontend generation with no hardcoding.
|
||||
|
||||
#### Attribute Definition Structure
|
||||
|
||||
```typescript
|
||||
interface AttributeDefinition {
|
||||
name: string; // Field name (e.g., "status", "name")
|
||||
type: string; // Frontend type: "text", "select", "integer", "timestamp", "checkbox"
|
||||
label: string; // Display label (localized to user's language)
|
||||
description: string; // Field description
|
||||
required: boolean; // Whether field is required
|
||||
readonly: boolean; // Whether field is read-only
|
||||
editable: boolean; // Whether field can be edited (inverse of readonly)
|
||||
visible: boolean; // Whether field should be displayed
|
||||
order: number; // Display order (lower = earlier)
|
||||
placeholder: string; // Placeholder text for inputs
|
||||
options?: Option[]; // Options for select fields (with localized labels)
|
||||
default?: any; // Default value
|
||||
}
|
||||
|
||||
interface Option {
|
||||
value: string; // Option value (e.g., "running")
|
||||
label: { // Localized labels
|
||||
[language: string]: string; // e.g., {"en": "Running", "fr": "En cours"}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### Fetching Metadata
|
||||
|
||||
```typescript
|
||||
// Fetch attribute definitions for ChatWorkflow
|
||||
const response = await fetch('/api/attributes/ChatWorkflow');
|
||||
const data = await response.json();
|
||||
const attributes = data.attributes; // AttributeDefinition[]
|
||||
```
|
||||
|
||||
#### Using Metadata for Forms
|
||||
|
||||
```typescript
|
||||
// Generate form fields from attributes
|
||||
const formFields = attributes
|
||||
.filter(attr => attr.visible && attr.editable) // Only editable, visible fields
|
||||
.sort((a, b) => a.order - b.order) // Sort by order
|
||||
.map(attr => ({
|
||||
name: attr.name,
|
||||
label: attr.label,
|
||||
type: attr.type,
|
||||
required: attr.required,
|
||||
options: attr.options,
|
||||
placeholder: attr.placeholder,
|
||||
default: attr.default
|
||||
}));
|
||||
```
|
||||
|
||||
#### Using Metadata for Tables
|
||||
|
||||
```typescript
|
||||
// Generate table columns from attributes
|
||||
const tableColumns = attributes
|
||||
.filter(attr => attr.visible) // Only visible fields
|
||||
.sort((a, b) => a.order - b.order) // Sort by order
|
||||
.map(attr => ({
|
||||
key: attr.name,
|
||||
label: attr.label,
|
||||
type: attr.type,
|
||||
sortable: true, // Can be made configurable
|
||||
render: (value) => formatValue(value, attr.type, attr.options)
|
||||
}));
|
||||
```
|
||||
|
||||
### Field Type Handling
|
||||
|
||||
Different field types require different UI components:
|
||||
|
||||
| Type | UI Component | Example Values |
|
||||
|------|--------------|----------------|
|
||||
| `text` | Text input | "My Workflow" |
|
||||
| `textarea` | Textarea | Long descriptions |
|
||||
| `integer` | Number input | 5, 10, 100 |
|
||||
| `select` | Dropdown | Options from `options` array |
|
||||
| `checkbox` | Checkbox | true/false |
|
||||
| `timestamp` | Date/time picker | Unix timestamp |
|
||||
|
||||
### Localization
|
||||
|
||||
All labels and option labels support multiple languages. The backend serves labels in the user's language (determined by authentication context). The frontend should:
|
||||
|
||||
1. Use `label` directly (already localized)
|
||||
2. For select options, use `option.label[userLanguage]` or fallback to `option.value`
|
||||
|
||||
---
|
||||
|
||||
## Implementation Patterns
|
||||
|
||||
### Pattern 1: Dynamic Table Component
|
||||
|
||||
```typescript
|
||||
function DynamicTable({ entityType, data, pagination, onSort, onPageChange, onRowClick }) {
|
||||
const [attributes, setAttributes] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`/api/attributes/${entityType}`)
|
||||
.then(r => r.json())
|
||||
.then(data => setAttributes(data.attributes.filter(attr => attr.visible)));
|
||||
}, [entityType]);
|
||||
|
||||
const columns = attributes
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map(attr => ({
|
||||
key: attr.name,
|
||||
label: attr.label,
|
||||
type: attr.type,
|
||||
render: (value) => formatValue(value, attr.type, attr.options)
|
||||
}));
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map(col => (
|
||||
<th key={col.key} onClick={() => handleSort(col.key)}>
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map(row => (
|
||||
<tr key={row.id} onClick={() => onRowClick?.(row)}>
|
||||
{columns.map(col => (
|
||||
<td key={col.key}>{col.render(row[col.key])}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Dynamic Form Component
|
||||
|
||||
```typescript
|
||||
function DynamicForm({ entityType, initialValues, onSubmit, onCancel }) {
|
||||
const [attributes, setAttributes] = useState([]);
|
||||
const [formData, setFormData] = useState(initialValues || {});
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`/api/attributes/${entityType}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const editable = data.attributes.filter(attr => attr.visible && attr.editable);
|
||||
setAttributes(editable);
|
||||
});
|
||||
}, [entityType]);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Validate
|
||||
const newErrors = {};
|
||||
attributes.forEach(attr => {
|
||||
if (attr.required && !formData[attr.name]) {
|
||||
newErrors[attr.name] = `${attr.label} is required`;
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(newErrors).length > 0) {
|
||||
setErrors(newErrors);
|
||||
return;
|
||||
}
|
||||
|
||||
onSubmit(formData);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
{attributes
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map(attr => (
|
||||
<FormField
|
||||
key={attr.name}
|
||||
field={attr}
|
||||
value={formData[attr.name] ?? attr.default}
|
||||
onChange={(value) => setFormData(prev => ({ ...prev, [attr.name]: value }))}
|
||||
error={errors[attr.name]}
|
||||
/>
|
||||
))}
|
||||
<button type="submit">Save</button>
|
||||
{onCancel && <button type="button" onClick={onCancel}>Cancel</button>}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: Polling Hook
|
||||
|
||||
```typescript
|
||||
function usePolling(url, interval, condition = true) {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!condition) return;
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
setData(result);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Polling error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData(); // Initial fetch
|
||||
const intervalId = setInterval(fetchData, interval);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}, [url, interval, condition]);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Usage
|
||||
const workflow = usePolling(
|
||||
`/api/workflows/${workflowId}/status`,
|
||||
5000,
|
||||
workflow?.status === 'running'
|
||||
);
|
||||
```
|
||||
|
||||
### Pattern 4: Incremental Data Loading
|
||||
|
||||
```typescript
|
||||
function useIncrementalData(baseUrl, lastIdKey) {
|
||||
const [items, setItems] = useState([]);
|
||||
const [lastId, setLastId] = useState(null);
|
||||
|
||||
const loadInitial = async () => {
|
||||
const response = await fetch(baseUrl).then(r => r.json());
|
||||
const newItems = response.items || response;
|
||||
setItems(newItems);
|
||||
if (newItems.length > 0) {
|
||||
setLastId(newItems[newItems.length - 1][lastIdKey]);
|
||||
}
|
||||
};
|
||||
|
||||
const loadNew = async () => {
|
||||
if (!lastId) return;
|
||||
|
||||
const url = `${baseUrl}?${lastIdKey}=${lastId}`;
|
||||
const response = await fetch(url).then(r => r.json());
|
||||
const newItems = response.items || response;
|
||||
|
||||
if (newItems.length > 0) {
|
||||
setItems(prev => [...prev, ...newItems]);
|
||||
setLastId(newItems[newItems.length - 1][lastIdKey]);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadInitial();
|
||||
}, [baseUrl]);
|
||||
|
||||
return { items, loadNew };
|
||||
}
|
||||
|
||||
// Usage for messages
|
||||
const { items: messages, loadNew: loadNewMessages } = useIncrementalData(
|
||||
`/api/workflows/${workflowId}/messages`,
|
||||
'messageId'
|
||||
);
|
||||
|
||||
// Poll for new messages
|
||||
useEffect(() => {
|
||||
if (workflow?.status !== 'running') return;
|
||||
|
||||
const interval = setInterval(loadNewMessages, 3000);
|
||||
return () => clearInterval(interval);
|
||||
}, [workflow?.status, loadNewMessages]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This documentation focuses on **customer journeys**—what users want to accomplish with workflows. Each journey describes:
|
||||
|
||||
1. **User Goal**: What the user is trying to achieve
|
||||
2. **User Story**: The scenario from the user's perspective
|
||||
3. **Frontend Requirements**: What pages/components are needed
|
||||
4. **Backend Routes**: Which API endpoints support the journey
|
||||
5. **Example Flows**: Step-by-step user interactions
|
||||
6. **Implementation Patterns**: Code examples for common patterns
|
||||
|
||||
The key principle throughout: **Everything is dynamic and driven by backend metadata**. The frontend should never hardcode field definitions, validation rules, labels, or UI structure. All of this comes from the backend's attribute definition system.
|
||||
|
||||
By following these customer journeys and using the backend metadata system, you can build a frontend that:
|
||||
- Adapts automatically to backend changes
|
||||
- Supports multiple languages
|
||||
- Enforces permissions correctly
|
||||
- Provides real-time updates
|
||||
- Requires minimal maintenance
|
||||
|
|
@ -1,555 +0,0 @@
|
|||
# Workflow Page Requirements
|
||||
|
||||
This document contains the complete frontend requirements for the workflow management page and its views/components. All UI components are dynamically generated from backend metadata—no hardcoding required.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Page Structure and Layout](#page-structure-and-layout)
|
||||
3. [User Interactions and Functionality](#user-interactions-and-functionality)
|
||||
4. [Backend Routes and API Integration](#backend-routes-and-api-integration)
|
||||
5. [Field and Attribute Reference](#field-and-attribute-reference)
|
||||
6. [Dynamic Rendering Guidelines](#dynamic-rendering-guidelines)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The workflow management page enables users to manage and monitor workflows that are already running or completed. The frontend consists of a single page (`/workflows`) with different views/states:
|
||||
|
||||
- **List View** - Browse, search, filter, and sort workflows
|
||||
- **Detail View** - View complete workflow information, messages, and logs (expandable sections, roll down to see more)
|
||||
- **Edit View** - Edit workflow properties (can be shown as popup/modal or inline edit)
|
||||
|
||||
All views use backend-driven UI generation, meaning field definitions, labels, validation rules, and UI structure come entirely from backend metadata through the `/api/attributes/ChatWorkflow` endpoint. Views are managed through component state and UI extensions (expandable sections, popups, modals) within the same page, not as separate routes.
|
||||
|
||||
---
|
||||
|
||||
## Page Structure and Layout
|
||||
|
||||
### Workflow Management Page (`/workflows`)
|
||||
|
||||
The workflow management page uses different views/states and UI extensions to handle different user interactions. Views are managed through component state and UI extensions (expandable sections, popups, modals) within the same page component.
|
||||
|
||||
### List View
|
||||
|
||||
**State:** `view === 'list'` or `selectedWorkflowId === null`
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Main Content Area:**
|
||||
- Table or card grid displaying all workflows
|
||||
- Each workflow row/card shows: name, status badge, last activity timestamp, progress indicators
|
||||
- Clickable rows/cards that expand to show Detail View (expandable section or roll down)
|
||||
|
||||
- **Search and Filter Controls:**
|
||||
- General search input box (searches across all text fields)
|
||||
- Field-specific filter controls (one filter per visible field)
|
||||
- Active filter indicators (chips/badges showing applied filters)
|
||||
- "Clear all filters" button when filters are active
|
||||
|
||||
- **Sorting Controls:**
|
||||
- Clickable column headers with sort indicators (up/down arrows)
|
||||
- Visual indication of current sort field and direction
|
||||
- Support for multi-level sorting
|
||||
|
||||
- **Pagination Controls:**
|
||||
- Page information display ("Page X of Y", "Showing 1-20 of 45 workflows")
|
||||
- Previous/Next page buttons
|
||||
- Page number buttons
|
||||
- Page size selector (10, 20, 50, 100 items per page)
|
||||
|
||||
### Detail View (Expandable Section)
|
||||
|
||||
**State:** `view === 'detail'` and `selectedWorkflowId !== null`
|
||||
|
||||
**UI Pattern:** Expandable section that rolls down from the selected workflow row, or inline expansion within the list
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Workflow Header Section:**
|
||||
- Workflow name (editable indicator if user has permission)
|
||||
- Status badge with color coding
|
||||
- Key metrics (progress indicators, timestamps)
|
||||
|
||||
- **Workflow Information Section:**
|
||||
- All workflow properties displayed as formatted fields
|
||||
- Read-only fields shown as formatted text
|
||||
- Editable fields shown with edit indicators (if user has permission)
|
||||
- Special formatting:
|
||||
- Status → Color-coded badge
|
||||
- Timestamps → Relative time ("2 hours ago") or absolute date/time
|
||||
- Progress fields → Progress bars
|
||||
- Select fields → Display localized labels (not enum values)
|
||||
|
||||
- **Messages Section:**
|
||||
- List of all messages in chronological order
|
||||
- Each message shows: role (user/assistant/system), content, timestamp, attached files
|
||||
- Message threading support (if `parentMessageId` exists)
|
||||
- Pagination controls if many messages
|
||||
- Delete button for each message (if user has permission)
|
||||
|
||||
- **Logs Section:**
|
||||
- List of execution logs in reverse chronological order (newest first)
|
||||
- Color-coded by type (info/warning/error)
|
||||
- Progress indicators if `progress` field exists
|
||||
- Filter controls by log type
|
||||
- Pagination controls if many logs
|
||||
|
||||
- **Action Buttons:**
|
||||
- Edit button (if user has permission and editable fields exist)
|
||||
- Delete button (if user has permission)
|
||||
|
||||
- **Real-Time Updates** (when workflow is running):
|
||||
- Status indicator that updates automatically
|
||||
- New messages appearing in the messages list
|
||||
- New logs appearing in the logs viewer
|
||||
- Progress indicators updating
|
||||
- Activity indicators showing workflow is active
|
||||
|
||||
### Workflow Edit Page (`/workflows/{workflowId}/edit`)
|
||||
|
||||
**What the user sees:**
|
||||
|
||||
- **Form Section:**
|
||||
- Dynamic form with editable workflow fields only
|
||||
- Field labels with required indicators (asterisk for required fields)
|
||||
- Help text/tooltips from field descriptions
|
||||
- Input validation errors displayed inline
|
||||
|
||||
- **Action Buttons:**
|
||||
- Save button (submits form)
|
||||
- Cancel button (navigates back to detail page)
|
||||
|
||||
- **Confirmation Dialogs:**
|
||||
- Delete confirmation dialog (on workflow detail page)
|
||||
- Message deletion confirmation dialog
|
||||
- File deletion confirmation dialog
|
||||
|
||||
---
|
||||
|
||||
## User Interactions and Functionality
|
||||
|
||||
### Browsing and Discovery
|
||||
|
||||
**Search Functionality:**
|
||||
- User types in general search box
|
||||
- Frontend debounces input (300-500ms delay)
|
||||
- Search applies across all text fields in workflow objects
|
||||
- Results update automatically
|
||||
- Page resets to page 1 when search changes
|
||||
|
||||
**Field Filtering:**
|
||||
- User selects field to filter by
|
||||
- Frontend shows appropriate filter UI based on field type:
|
||||
- Text fields → Text input with operator selection (contains, equals, startsWith, endsWith)
|
||||
- Select fields → Dropdown with options from backend metadata
|
||||
- Integer fields → Number input with comparison operators (gt, gte, lt, lte) or range inputs
|
||||
- Timestamp fields → Date picker with comparison operators or date range picker
|
||||
- Checkbox fields → Boolean toggle (true/false/any)
|
||||
- User applies filter → Filter appears as active chip/badge
|
||||
- User can remove individual filters
|
||||
- Multiple filters work together (AND logic)
|
||||
- Page resets to page 1 when filters change
|
||||
|
||||
**Sorting:**
|
||||
- User clicks column header
|
||||
- Sort direction toggles (asc → desc → remove)
|
||||
- Multiple columns can be sorted (multi-level sorting)
|
||||
- Sort indicators show on column headers
|
||||
- All filters and search preserved when sorting
|
||||
|
||||
**Pagination:**
|
||||
- User clicks page number or navigation button
|
||||
- Page updates and data refetches
|
||||
- All filters, search, and sort preserved
|
||||
- User changes page size → Page resets to 1, data refetches
|
||||
|
||||
**View Switching:**
|
||||
- User clicks workflow row/card → Expands Detail View (expandable section rolls down, or switches to Detail View state)
|
||||
- User clicks workflow name → Expands Detail View (expandable section rolls down, or switches to Detail View state)
|
||||
- User clicks collapse/back button → Collapses Detail View, returns to List View
|
||||
|
||||
### Viewing Workflow Details
|
||||
|
||||
**Information Display:**
|
||||
- All workflow fields displayed using dynamic rendering
|
||||
- Read-only fields shown as formatted text
|
||||
- Editable fields shown with edit indicators
|
||||
- Special fields formatted appropriately (status badges, timestamps, progress bars)
|
||||
|
||||
**Messages Display:**
|
||||
- Messages shown in chronological order (`publishedAt`)
|
||||
- Role-based styling (user/assistant/system)
|
||||
- Attached files displayed with download/view options
|
||||
- Threading support for parent-child message relationships
|
||||
- Pagination if many messages
|
||||
|
||||
**Logs Display:**
|
||||
- Logs shown in reverse chronological order (newest first)
|
||||
- Color-coded by type (info/warning/error)
|
||||
- Progress indicators displayed if available
|
||||
- Filtering by log type supported
|
||||
|
||||
**Real-Time Monitoring** (when workflow is running):
|
||||
- Status polling every 2-5 seconds
|
||||
- New messages polling every 2-3 seconds (incremental loading)
|
||||
- New logs polling every 2-3 seconds (incremental loading)
|
||||
- All polling stops when workflow status changes to "completed", "stopped", or "error"
|
||||
- Completion message shown when workflow finishes
|
||||
|
||||
### Editing Workflows
|
||||
|
||||
**Form Interaction:**
|
||||
- User clicks "Edit" button → Opens Edit View as popup/modal overlay
|
||||
- Form pre-populated with current workflow values
|
||||
- User modifies fields
|
||||
- Client-side validation shows errors immediately
|
||||
- User clicks Save → Form validates and submits
|
||||
- User clicks Cancel → Closes popup/modal, returns to Detail View
|
||||
|
||||
**Form Validation:**
|
||||
- Required field validation (shows error if empty)
|
||||
- Type validation (e.g., integer fields must be numbers)
|
||||
- Select field validation (value must be in options array)
|
||||
- Validation errors displayed inline below fields
|
||||
|
||||
**Form Submission:**
|
||||
- Only changed fields sent (or all form data)
|
||||
- Success → Close popup/modal, refresh Detail View data, show success message
|
||||
- Error handling:
|
||||
- 403 (permission denied) → Show permission error in popup
|
||||
- 400 (validation errors) → Display backend validation errors in popup
|
||||
- Other errors → Show generic error message in popup
|
||||
|
||||
### Managing Messages
|
||||
|
||||
**Message Actions:**
|
||||
- User clicks delete button on message
|
||||
- Confirmation dialog appears
|
||||
- User confirms → Message deleted, list updates
|
||||
- User cancels → Dialog closes, no action
|
||||
|
||||
**File Actions:**
|
||||
- User clicks delete button on file attachment
|
||||
- Confirmation dialog appears
|
||||
- User confirms → File removed from message, display updates
|
||||
- User cancels → Dialog closes, no action
|
||||
|
||||
### Deleting Workflows
|
||||
|
||||
**Delete Action:**
|
||||
- User clicks delete button (on detail page or list)
|
||||
- Confirmation dialog appears with workflow name
|
||||
- Warning about permanent deletion
|
||||
- User confirms → Workflow deleted
|
||||
- User cancels → Dialog closes, no action
|
||||
|
||||
**Delete Success Handling:**
|
||||
- Success message displayed
|
||||
- If Detail View is expanded → Collapse Detail View, return to List View
|
||||
- If in List View → Remove workflow from list or refresh
|
||||
|
||||
---
|
||||
|
||||
## Backend Routes and API Integration
|
||||
|
||||
### Complete Route Reference
|
||||
|
||||
All backend routes used by workflow pages:
|
||||
|
||||
| Route | Method | Purpose | When Used |
|
||||
|-------|--------|---------|-----------|
|
||||
| `/api/workflows/` | GET | Get all workflows (with pagination) | Initial page load, pagination changes, sort changes, filter changes, search changes |
|
||||
| `/api/workflows/{workflowId}` | GET | Get workflow details | Detail View expansion, Edit View popup open |
|
||||
| `/api/workflows/{workflowId}` | PUT | Update workflow | Edit form submission in popup |
|
||||
| `/api/workflows/{workflowId}` | DELETE | Delete workflow | User confirms deletion in popup |
|
||||
| `/api/workflows/{workflowId}/status` | GET | Get current workflow status | Real-time polling (every 2-5s when running, Detail View expanded) |
|
||||
| `/api/workflows/{workflowId}/messages` | GET | Get workflow messages | Detail View expansion, refresh, real-time polling (every 2-3s when running) |
|
||||
| `/api/workflows/{workflowId}/messages` | GET | Get new messages (incremental) | Real-time polling with `messageId` parameter (Detail View expanded) |
|
||||
| `/api/workflows/{workflowId}/messages/{messageId}` | DELETE | Delete message | User confirms message deletion in popup |
|
||||
| `/api/workflows/{workflowId}/messages/{messageId}/files/{fileId}` | DELETE | Delete file from message | User confirms file deletion in popup |
|
||||
| `/api/workflows/{workflowId}/logs` | GET | Get workflow logs | Detail View expansion, refresh, real-time polling (every 2-3s when running) |
|
||||
| `/api/workflows/{workflowId}/logs` | GET | Get new logs (incremental) | Real-time polling with `logId` parameter (Detail View expanded) |
|
||||
| `/api/attributes/ChatWorkflow` | GET | Get field definitions | Page load (once per page) - used to generate all UI components |
|
||||
|
||||
### API Request Patterns
|
||||
|
||||
**Pagination Request:**
|
||||
```
|
||||
GET /api/workflows/?pagination={"page":1,"pageSize":20,"sort":[],"filters":null}
|
||||
```
|
||||
- `pagination` parameter is JSON-encoded string
|
||||
- If user wants all workflows: omit `pagination` parameter entirely
|
||||
- Filters structure: `{"search":"query","fieldName":"value",...}`
|
||||
|
||||
**Incremental Loading (Polling):**
|
||||
```
|
||||
GET /api/workflows/{workflowId}/messages?messageId={lastMessageId}
|
||||
GET /api/workflows/{workflowId}/logs?logId={lastLogId}
|
||||
```
|
||||
- Used for real-time updates
|
||||
- Only fetch new items after the specified ID
|
||||
- Prevents reloading all data
|
||||
|
||||
**Update Request:**
|
||||
```
|
||||
PUT /api/workflows/{workflowId}
|
||||
Content-Type: application/json
|
||||
Body: {fieldName: value, ...}
|
||||
```
|
||||
- Send only changed fields or all form data
|
||||
- Handle 403 (permission denied) and 400 (validation errors)
|
||||
|
||||
**Delete Requests:**
|
||||
```
|
||||
DELETE /api/workflows/{workflowId}
|
||||
DELETE /api/workflows/{workflowId}/messages/{messageId}
|
||||
DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
```
|
||||
- All delete operations require user confirmation
|
||||
- Handle 403 (permission denied) and 404 (not found) gracefully
|
||||
|
||||
### Response Handling
|
||||
|
||||
**Paginated Response:**
|
||||
```json
|
||||
{
|
||||
"items": [...],
|
||||
"pagination": {
|
||||
"currentPage": 1,
|
||||
"pageSize": 20,
|
||||
"totalItems": 45,
|
||||
"totalPages": 3,
|
||||
"sort": [...],
|
||||
"filters": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- 403 Forbidden → Show permission error message
|
||||
- 404 Not Found → Show "not found" error message
|
||||
- 400 Bad Request → Display validation errors from response
|
||||
- 500 Internal Server Error → Show generic error message
|
||||
|
||||
---
|
||||
|
||||
## Field and Attribute Reference
|
||||
|
||||
### Complete Field List
|
||||
|
||||
The following is a comprehensive list of all parameters, attributes, and fields that will be displayed for workflows. All of these are provided by the backend through the `/api/attributes/ChatWorkflow` endpoint and should never be hardcoded in the frontend.
|
||||
|
||||
#### Core Workflow Fields
|
||||
|
||||
**Identification Fields:**
|
||||
- `id` - Unique workflow identifier (text, readonly, not required, visible)
|
||||
- `mandateId` - ID of the mandate this workflow belongs to (text, readonly, not required, visible)
|
||||
|
||||
**Status and Configuration Fields:**
|
||||
- `status` - Current workflow status (select, editable, not required, visible)
|
||||
- Options: "running", "completed", "stopped", "error"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `name` - Workflow name (text, editable, required, visible)
|
||||
- `workflowMode` - Workflow execution mode (select, editable, not required, visible)
|
||||
- Options: "Dynamic", "Automation", "Actionplan"
|
||||
- Each option has localized labels (en/fr)
|
||||
- `maxSteps` - Maximum number of iterations in react mode (integer, editable, not required, visible)
|
||||
|
||||
**Progress Tracking Fields:**
|
||||
- `currentRound` - Current round number in workflow execution (integer, readonly, not required, visible)
|
||||
- `currentTask` - Current task number within the current round (integer, readonly, not required, visible)
|
||||
- `currentAction` - Current action number within the current task (integer, readonly, not required, visible)
|
||||
- `totalTasks` - Total number of tasks in the workflow (integer, readonly, not required, visible)
|
||||
- `totalActions` - Total number of actions in the workflow (integer, readonly, not required, visible)
|
||||
|
||||
**Timestamp Fields:**
|
||||
- `startedAt` - When the workflow started (timestamp, readonly, not required, visible)
|
||||
- `lastActivity` - Timestamp of last activity (timestamp, readonly, not required, visible)
|
||||
|
||||
**Related Data Fields (Lists):**
|
||||
- `logs` - List of workflow log entries (text/list, readonly, not required, visible)
|
||||
- `messages` - List of workflow messages (text/list, readonly, not required, visible)
|
||||
- `stats` - List of workflow statistics (text/list, readonly, not required, visible)
|
||||
- `tasks` - List of tasks in the workflow (text/list, readonly, not required, visible)
|
||||
- `expectedFormats` - List of expected file format extensions (text/list, readonly, not required, visible)
|
||||
|
||||
### Pagination Parameters
|
||||
|
||||
**Request Parameters (`PaginationParams`):**
|
||||
- `page` - Current page number (1-based, minimum 1)
|
||||
- `pageSize` - Number of items per page (minimum 1, maximum 1000)
|
||||
- `sort` - Array of sort field configurations
|
||||
- Each sort field contains:
|
||||
- `field` - Field name to sort by (must match a workflow field name)
|
||||
- `direction` - Sort direction: "asc" or "desc"
|
||||
- `filters` - Filter criteria dictionary
|
||||
- `search` - General search term (searches across all text fields, case-insensitive)
|
||||
- Field-specific filters: `{fieldName: value}` or `{fieldName: {operator: "operator", value: value}}`
|
||||
- Supported operators: "equals", "contains", "startsWith", "endsWith", "gt", "gte", "lt", "lte", "in", "notIn"
|
||||
|
||||
**Response Metadata (`PaginationMetadata`):**
|
||||
- `currentPage` - Current page number (1-based)
|
||||
- `pageSize` - Number of items per page
|
||||
- `totalItems` - Total number of items across all pages (after filters applied)
|
||||
- `totalPages` - Total number of pages (calculated from totalItems / pageSize)
|
||||
- `sort` - Current sort configuration applied (array of SortField objects)
|
||||
- `filters` - Current filters applied (mirrors request filters)
|
||||
|
||||
### Attribute Definition Structure
|
||||
|
||||
Each field returned from `/api/attributes/ChatWorkflow` contains:
|
||||
|
||||
- `name` - Field name (e.g., "status", "name", "lastActivity")
|
||||
- `type` - Field data type (e.g., "text", "select", "integer", "timestamp", "checkbox")
|
||||
- `label` - Localized field label (object with language keys: {"en": "English Label", "fr": "French Label"})
|
||||
- `description` - Field description text
|
||||
- `required` - Boolean indicating if field is required
|
||||
- `readonly` - Boolean indicating if field is read-only
|
||||
- `editable` - Boolean indicating if field can be edited (inverse of readonly)
|
||||
- `visible` - Boolean indicating if field should be displayed in UI
|
||||
- `options` - Array of options for select fields (each option has `value` and localized `label`)
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Rendering Guidelines
|
||||
|
||||
The frontend must render all UI components dynamically based on backend metadata. No field definitions, labels, validation rules, or UI structure should be hardcoded.
|
||||
|
||||
### Table Column Generation
|
||||
|
||||
When rendering the workflow list table:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/ChatWorkflow`
|
||||
2. Filter attributes where `visible: true` to determine which columns to display
|
||||
3. Use `label` property for column headers (select appropriate language based on user preference)
|
||||
4. Use `type` property to determine how to format cell values:
|
||||
- `text` fields → Display as plain text
|
||||
- `select` fields → Display value using label from options array (match value to option.value, then display option.label)
|
||||
- `integer` fields → Display as formatted number
|
||||
- `timestamp` fields → Format as relative time ("2 hours ago") or absolute date/time based on user preference
|
||||
- `checkbox` fields → Display as checkmark icon or boolean indicator
|
||||
5. Use `readonly` property to determine if column should be sortable (readonly fields may still be sortable, but editable fields definitely are)
|
||||
6. Generate click handlers for column headers to update sort parameters and refetch data
|
||||
|
||||
### Filter Control Generation
|
||||
|
||||
When rendering filter controls:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/ChatWorkflow`
|
||||
2. Filter attributes where `visible: true` to determine which filters to show
|
||||
3. For each visible field, generate appropriate filter UI based on `type`:
|
||||
- `text` fields → Text input filter (supports "contains", "equals", "startsWith", "endsWith" operators)
|
||||
- `select` fields → Dropdown filter with options from `options` array (use localized labels)
|
||||
- `integer` fields → Number range filter (min/max inputs) or single number input with comparison operators (gt, gte, lt, lte)
|
||||
- `timestamp` fields → Date range picker or single date picker with comparison operators
|
||||
- `checkbox` fields → Boolean toggle filter (true/false/any)
|
||||
4. Use `label` property for filter labels (localized)
|
||||
5. When user applies filter, update `filters` object in pagination parameters and refetch data
|
||||
6. Display active filters as chips/badges showing field label and value
|
||||
7. Allow removing individual filters by removing them from `filters` object
|
||||
|
||||
### Search Implementation
|
||||
|
||||
For general search functionality:
|
||||
|
||||
1. Display a single search input box (not field-specific)
|
||||
2. When user types, update `filters.search` in pagination parameters
|
||||
3. Debounce search input (wait 300-500ms after user stops typing before sending request)
|
||||
4. Search applies across all text fields in the workflow object
|
||||
5. Reset to page 1 when search query changes
|
||||
6. Combine search with field-specific filters (both are in the `filters` object)
|
||||
|
||||
### Form Field Generation
|
||||
|
||||
When rendering edit forms:
|
||||
|
||||
1. Fetch attribute definitions from `/api/attributes/ChatWorkflow`
|
||||
2. Filter attributes where `visible: true` AND `editable: true` to determine which fields to show
|
||||
3. For each editable field, generate appropriate form input based on `type`:
|
||||
- `text` fields → Text input
|
||||
- `select` fields → Dropdown/select input with options from `options` array (use localized labels)
|
||||
- `integer` fields → Number input with appropriate min/max constraints
|
||||
- `timestamp` fields → Date/time picker
|
||||
- `checkbox` fields → Checkbox input
|
||||
4. Use `label` property for field labels (localized)
|
||||
5. Use `required` property to show required indicators (asterisk, etc.)
|
||||
6. Use `description` property to show help text or tooltips
|
||||
7. Validate form before submission:
|
||||
- Check all `required: true` fields have values
|
||||
- Validate types (e.g., integer fields must be numbers)
|
||||
- Validate select fields (value must be in options array)
|
||||
8. On submit, send only changed fields or all form data to update endpoint
|
||||
|
||||
### Display Formatting
|
||||
|
||||
When displaying field values:
|
||||
|
||||
1. Use `type` property to determine formatting:
|
||||
- `text` → Display as-is (may need HTML escaping)
|
||||
- `select` → Look up value in `options` array and display localized label
|
||||
- `integer` → Format with thousand separators if needed
|
||||
- `timestamp` → Format as relative time or absolute date/time
|
||||
- `checkbox` → Display as checkmark icon or "Yes"/"No" text
|
||||
2. Handle `null` or `undefined` values gracefully (show "-" or "Not set")
|
||||
3. For list fields (logs, messages, stats), render as expandable sections or separate tables
|
||||
4. Use `readonly` property to determine if field should show edit indicators
|
||||
|
||||
### Real-Time Polling
|
||||
|
||||
When monitoring running workflows:
|
||||
|
||||
1. **Status Polling:**
|
||||
- Poll every 2-5 seconds if workflow status is "running"
|
||||
- Stop polling if status is "completed", "stopped", or "error"
|
||||
- Update status badge and workflow information on each poll
|
||||
|
||||
2. **Message Polling (Incremental Loading):**
|
||||
- Store ID of last displayed message
|
||||
- Poll every 2-3 seconds for messages after last message ID
|
||||
- Append new messages to list (don't reload all)
|
||||
- Only poll if workflow is "running"
|
||||
|
||||
3. **Log Polling (Incremental Loading):**
|
||||
- Store ID of last displayed log
|
||||
- Poll every 2-3 seconds for logs after last log ID
|
||||
- Prepend new logs to list (newest first)
|
||||
- Only poll if workflow is "running"
|
||||
|
||||
4. **Polling Management:**
|
||||
- Stop all polling when workflow completes
|
||||
- Show completion message
|
||||
- Update UI to indicate workflow is finished
|
||||
|
||||
### Localization
|
||||
|
||||
All labels and options support multiple languages:
|
||||
|
||||
1. Use user's preferred language (from user settings or browser locale)
|
||||
2. Access localized labels from `label` object: `label[userLanguage]` or `label.en` as fallback
|
||||
3. For select options, use `option.label[userLanguage]` or `option.label.en` as fallback
|
||||
4. If label for current language is missing, fall back to English
|
||||
|
||||
### Key Principles
|
||||
|
||||
- Never hardcode field names, labels, types, or validation rules
|
||||
- Always fetch attribute definitions from backend before rendering UI
|
||||
- Use attribute metadata to determine what to display and how to display it
|
||||
- Support all field types dynamically - if backend adds new types, frontend should handle them
|
||||
- Respect `visible`, `editable`, `readonly`, and `required` flags from backend
|
||||
- Use localized labels from backend metadata
|
||||
- Generate filters, forms, and tables entirely from attribute definitions
|
||||
- When backend adds new fields, frontend should automatically display them without code changes
|
||||
- Handle all error cases gracefully (403, 404, 400, 500)
|
||||
- Provide user feedback for all actions (loading states, success messages, error messages)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This document provides complete frontend requirements for the workflow management page and its views/components. All requirements follow the principle of **backend-driven UI generation**—no hardcoding of field definitions, labels, or validation rules. The frontend should dynamically generate all UI components from backend metadata provided through the `/api/attributes/ChatWorkflow` endpoint.
|
||||
|
||||
**Key Architecture Pattern:** The workflow management interface is a single page (`/workflows`) with different views managed through component state. Detail View is shown as an expandable section (roll down from selected workflow row), and Edit View is shown as a popup/modal overlay. All interactions happen within the same page component without separate routes.
|
||||
|
||||
For generic patterns that apply across all entity types (not just workflows), see the [Dynamic Forms and Pagination documentation](./dynamic-forms-and-pagination.md).
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
# Überblick und Projektstruktur
|
||||
|
||||
[← Zurück zum Inhaltsverzeichnis](README.md) | [Weiter: Datenmodell erstellen →](02-datamodels.md)
|
||||
|
||||
## Überblick
|
||||
|
||||
Das Feature "realEstate" bietet eine **stateless API** für Real Estate-Datenbankoperationen mit AI-basierter natürlicher Sprachverarbeitung:
|
||||
|
||||
### Hauptfunktionalität
|
||||
|
||||
- **Natürliche Sprache → CRUD-Operationen**: User-Input wird mit AI analysiert und in entsprechende Datenbankoperationen übersetzt
|
||||
- **Direkte Datenbankabfragen**: SQL-Queries können direkt ausgeführt werden
|
||||
- **CRUD-Operationen**: Erstellen, Lesen, Aktualisieren und Löschen von Real Estate-Entitäten
|
||||
- **PostgreSQL-Datenbankzugriff**: Über den bestehenden DatabaseConnector
|
||||
- **RESTful API-Endpunkte**: Einfache, stateless Endpunkte ohne Session-Management
|
||||
- **Benutzerauthentifizierung und Zugriffskontrolle**: Integriert in bestehende UAM-Struktur
|
||||
|
||||
### Architektur-Ansatz
|
||||
|
||||
**Stateless Design:**
|
||||
- Keine Chat-Sessions notwendig (optional für zukünftige Erweiterungen)
|
||||
- Jeder Request ist unabhängig
|
||||
- Direkter Flow: User-Input → AI-Analyse → CRUD-Operation → Ergebnis
|
||||
- Keine Query-History (kann optional später hinzugefügt werden)
|
||||
|
||||
**AI-Integration:**
|
||||
- Nutzt `serviceAi` für Intent-Erkennung
|
||||
- Übersetzt natürliche Sprache in CRUD-Operationen
|
||||
- Unterstützt CREATE, READ, UPDATE, DELETE, QUERY-Intents
|
||||
|
||||
**Real Estate-Datenmodell-Entitäten:**
|
||||
- **Kernentitäten**: Projekt, Parzelle
|
||||
- **Unterstützend**: Dokument, Kontext
|
||||
- **Geografisch**: GeoPolylinie, GeoPunkt
|
||||
- **Administrativ**: Land, Kanton, Gemeinde
|
||||
|
||||
### Architektur-Komponenten
|
||||
|
||||
Die Architektur folgt dem Muster bestehender Features:
|
||||
- **Routes** (`modules/routes/`) - API-Endpunkte (stateless)
|
||||
- **Features** (`modules/features/`) - Geschäftslogik mit AI-Integration
|
||||
- **Interfaces** (`modules/interfaces/`) - Datenbankzugriff (CRUD-Operationen)
|
||||
- **DataModels** (`modules/datamodels/`) - Pydantic-Modelle für Real Estate-Entitäten
|
||||
- **Services** (`modules/services/`) - AI-Service für Intent-Analyse
|
||||
|
||||
---
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
gateway/
|
||||
├── modules/
|
||||
│ ├── routes/
|
||||
│ │ └── routeRealEstate.py # NEU: Stateless API-Endpunkte
|
||||
│ │ ├── POST /api/realestate/command # Natürliche Sprache → CRUD
|
||||
│ │ └── POST /api/realestate/query # Direkte SQL-Query
|
||||
│ │
|
||||
│ ├── features/
|
||||
│ │ └── realEstate/
|
||||
│ │ └── mainRealEstate.py # NEU: Feature-Logik mit AI-Integration
|
||||
│ │ ├── processNaturalLanguageCommand() # Hauptfunktion
|
||||
│ │ ├── analyzeUserIntent() # AI-basierte Intent-Analyse
|
||||
│ │ └── executeIntentBasedOperation() # CRUD-Ausführung
|
||||
│ │
|
||||
│ ├── interfaces/
|
||||
│ │ ├── interfaceDbRealEstateAccess.py # NEU: Zugriffskontrolle
|
||||
│ │ ├── interfaceDbRealEstateObjects.py # NEU: CRUD-Interface
|
||||
│ │ └── interfaceDbRealEstateChatObjects.py # OPTIONAL: Für Session-Support
|
||||
│ │
|
||||
│ ├── datamodels/
|
||||
│ │ ├── datamodelRealEstate.py # NEU: Real Estate Datenmodelle
|
||||
│ │ │ ├── Projekt, Parzelle, Dokument, etc.
|
||||
│ │ └── datamodelRealEstateChat.py # OPTIONAL: Für Session-Support
|
||||
│ │
|
||||
│ ├── services/
|
||||
│ │ └── serviceAi/ # BEREITS VORHANDEN
|
||||
│ │ └── mainServiceAi.py # Wird für Intent-Analyse genutzt
|
||||
│ │
|
||||
│ └── connectors/
|
||||
│ └── connectorDbPostgre.py # BEREITS VORHANDEN
|
||||
│
|
||||
├── app.py # Router-Registrierung
|
||||
├── env_dev.env # Environment-Konfiguration
|
||||
└── modules/features/featuresLifecycle.py # Feature-Lifecycle
|
||||
```
|
||||
|
||||
### Wichtige Dateien
|
||||
|
||||
**Erforderlich:**
|
||||
- `routeRealEstate.py` - API-Endpunkte (stateless)
|
||||
- `mainRealEstate.py` - Feature-Logik mit AI-Integration
|
||||
- `interfaceDbRealEstateAccess.py` - Zugriffskontrolle
|
||||
- `interfaceDbRealEstateObjects.py` - CRUD-Interface
|
||||
- `datamodelRealEstate.py` - Datenmodelle
|
||||
|
||||
**Optional (für zukünftige Session-Unterstützung):**
|
||||
- `interfaceDbRealEstateChatObjects.py` - Session-Management
|
||||
- `datamodelRealEstateChat.py` - Session-Modelle
|
||||
|
||||
---
|
||||
|
||||
## Datenfluss: User-Input → Ergebnis
|
||||
|
||||
### Flow: Natürliche Sprache ohne Session
|
||||
|
||||
```
|
||||
1. User sendet Request
|
||||
POST /api/realestate/command
|
||||
Body: { "userInput": "Erstelle ein neues Projekt namens 'Hauptstrasse 42'" }
|
||||
|
||||
2. Route empfängt Request
|
||||
routeRealEstate.process_command()
|
||||
→ Auth: getCurrentUser()
|
||||
|
||||
3. Feature-Logik verarbeitet
|
||||
mainRealEstate.processNaturalLanguageCommand()
|
||||
→ Services initialisieren: getServices(currentUser, workflow=None)
|
||||
|
||||
4. AI analysiert Intent
|
||||
analyzeUserIntent(services.ai, userInput)
|
||||
→ services.ai.callAiPlanning(intentPrompt)
|
||||
→ AI gibt zurück: { "intent": "CREATE", "entity": "Projekt", "parameters": {...} }
|
||||
|
||||
5. CRUD-Operation ausführen
|
||||
executeIntentBasedOperation(intent, entity, parameters)
|
||||
→ getRealEstateInterface(currentUser)
|
||||
→ RealEstateObjects.createProjekt(projekt)
|
||||
→ DatabaseConnector.recordCreate(Projekt)
|
||||
|
||||
6. Ergebnis zurückgeben
|
||||
HTTP 200 OK
|
||||
{ "success": true, "intent": "CREATE", "result": {...} }
|
||||
```
|
||||
|
||||
### Flow: Direkte SQL-Query
|
||||
|
||||
```
|
||||
1. User sendet Request
|
||||
POST /api/realestate/query
|
||||
Body: { "queryText": "SELECT * FROM Projekt WHERE plz = '8000'" }
|
||||
|
||||
2. Route empfängt Request
|
||||
routeRealEstate.execute_direct_query()
|
||||
→ Auth: getCurrentUser()
|
||||
|
||||
3. Query ausführen
|
||||
getChatInterface(currentUser)
|
||||
→ RealEstateChatObjects.executeQuery(queryText)
|
||||
→ DatabaseConnector.executeQuery(sql)
|
||||
|
||||
4. Ergebnis zurückgeben
|
||||
HTTP 200 OK
|
||||
{ "rows": [...], "columns": [...], "rowCount": 15 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vorteile des stateless Ansatzes
|
||||
|
||||
- **Einfachheit**: Kein Session-Management notwendig
|
||||
- **Performance**: Weniger Datenbank-Operationen pro Request
|
||||
- **Skalierbarkeit**: Stateless Requests sind einfacher zu skalieren
|
||||
- **Flexibilität**: Jeder Request ist unabhängig
|
||||
- **Erweiterbarkeit**: Session-Support kann später optional hinzugefügt werden
|
||||
|
||||
---
|
||||
|
||||
**Nächster Schritt:** [02-datamodels.md](02-datamodels.md)
|
||||
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
# Schritt 2: Interface erstellen
|
||||
|
||||
[← Zurück: Datenmodell erstellen](02-datamodels.md) | [Weiter: Feature-Logik implementieren →](04-feature-logic.md)
|
||||
|
||||
## Übersicht: Was sind Interfaces?
|
||||
|
||||
**Interfaces** sind aktive Klassen, die den **Datenbankzugriff** implementieren. Sie unterscheiden sich von **Datamodels** (die nur die Datenstruktur definieren):
|
||||
|
||||
| **Aspekt** | **Datamodels** | **Interfaces** |
|
||||
|------------|----------------|----------------|
|
||||
| **Zweck** | Definiert **WAS** (Datenstruktur) | Implementiert **WIE** (Datenzugriff) |
|
||||
| **Inhalt** | Pydantic-Modelle mit Feldern und Validierung | Klassen mit CRUD-Methoden (`create`, `get`, `update`, `delete`) |
|
||||
| **Beispiel** | `class Projekt(BaseModel): ...` | `def createProjekt(...) -> Projekt: ...` |
|
||||
| **Aktivität** | Passiv (nur Struktur) | Aktiv (führt Operationen aus) |
|
||||
|
||||
**Analogie:**
|
||||
- **Datamodel** = Bauplan (beschreibt das Haus)
|
||||
- **Interface** = Bauunternehmer (baut das Haus)
|
||||
|
||||
---
|
||||
|
||||
## Struktur: Real Estate CRUD-Interface
|
||||
|
||||
Da das Feature **stateless** arbeitet, benötigen wir nur **ein Interface** für CRUD-Operationen auf Real Estate-Entitäten:
|
||||
|
||||
### Real Estate-Datenmodelle → Real Estate CRUD-Interface
|
||||
|
||||
**Datamodel:** `datamodelRealEstate.py`
|
||||
- `Projekt`
|
||||
- `Parzelle`
|
||||
- `Dokument`
|
||||
- `Kanton`, `Gemeinde`, `Land`
|
||||
- `GeoPolylinie`, `GeoPunkt`
|
||||
- `Kontext`
|
||||
- etc.
|
||||
|
||||
**Interface:** `interfaceDbRealEstateObjects.py`
|
||||
- `RealEstateObjects` (Haupt-Interface)
|
||||
- `RealEstateAccess` (Zugriffskontrolle)
|
||||
- Methoden: `createProjekt()`, `getParzelle()`, `updateDokument()`, etc.
|
||||
|
||||
**Hinweis:** Das Haupt-Interface enthält auch `executeQuery()` für direkte SQL-Queries (stateless)
|
||||
|
||||
---
|
||||
|
||||
## Warum nur ein Haupt-Interface?
|
||||
|
||||
1. **Stateless Design**:
|
||||
- Keine Session-Verwaltung notwendig
|
||||
- Direkte CRUD-Operationen auf Real Estate-Modellen
|
||||
|
||||
2. **Einfache Architektur**:
|
||||
- Ein Interface für alle CRUD-Operationen
|
||||
- Weniger Komplexität, bessere Wartbarkeit
|
||||
|
||||
3. **Query-Funktionalität**:
|
||||
- `executeQuery()` ist direkt im Haupt-Interface verfügbar
|
||||
- Für direkte SQL-Queries (stateless)
|
||||
- Keine Session-Management-Funktionen
|
||||
|
||||
---
|
||||
|
||||
## Zu erstellende Dateien
|
||||
|
||||
### Schritt 2a: Real Estate CRUD-Interface (ERFORDERLICH)
|
||||
|
||||
**Zwei separate Dateien** (wie bei anderen Features):
|
||||
|
||||
#### Datei 1: `modules/interfaces/interfaceDbRealEstateAccess.py`
|
||||
|
||||
**Enthält:**
|
||||
- `RealEstateAccess` - Zugriffskontrolle für Real Estate-Entitäten
|
||||
- Methoden: `uam()`, `canModify()`
|
||||
|
||||
**Zweck:** Prüft Zugriffsrechte und filtert Daten basierend auf Benutzerprivilegien
|
||||
|
||||
#### Datei 2: `modules/interfaces/interfaceDbRealEstateObjects.py`
|
||||
|
||||
**Enthält:**
|
||||
- `RealEstateObjects` - Haupt-Interface für CRUD-Operationen
|
||||
- `getInterface()` - Factory-Funktion
|
||||
- Nutzt `RealEstateAccess` aus der Access-Datei
|
||||
|
||||
**Zweck:** Verwaltet Real Estate-Entitäten (Projekt, Parzelle, Dokument, etc.)
|
||||
|
||||
**Nutzt:**
|
||||
- `datamodelRealEstate.py` (Projekt, Parzelle, Dokument, etc.)
|
||||
- `interfaceDbRealEstateAccess.py` (für Zugriffskontrolle)
|
||||
|
||||
**Wann benötigt:** Für alle CRUD-Operationen auf Real Estate-Entitäten (z.B. Projekte erstellen/bearbeiten, Parzellen verwalten). Dies ist das Haupt-Interface für das Feature.
|
||||
|
||||
---
|
||||
|
||||
## Übersicht: Dateien und ihre Beziehungen
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ DATAMODELS (Struktur) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ datamodelRealEstate.py │
|
||||
│ ├── Projekt │
|
||||
│ ├── Parzelle │
|
||||
│ ├── Dokument │
|
||||
│ ├── Kanton, Gemeinde, Land │
|
||||
│ ├── GeoPolylinie, GeoPunkt │
|
||||
│ ├── Kontext │
|
||||
│ └── ... │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ nutzt
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ INTERFACES (Zugriff) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ REAL ESTATE CRUD-INTERFACE (ERFORDERLICH) │
|
||||
│ │
|
||||
│ interfaceDbRealEstateAccess.py │
|
||||
│ └── RealEstateAccess │
|
||||
│ ├── uam() │
|
||||
│ └── canModify() │
|
||||
│ │
|
||||
│ interfaceDbRealEstateObjects.py │
|
||||
│ ├── RealEstateObjects │
|
||||
│ │ ├── createProjekt() │
|
||||
│ │ ├── getProjekt() │
|
||||
│ │ ├── updateProjekt() │
|
||||
│ │ ├── deleteProjekt() │
|
||||
│ │ ├── createParzelle() │
|
||||
│ │ ├── getParzelle() │
|
||||
│ │ └── ... (CRUD für alle Entitäten) │
|
||||
│ └── getInterface() │
|
||||
│ └── nutzt RealEstateAccess │
|
||||
│ │
|
||||
│ │
|
||||
│ HINWEIS: executeQuery() ist im Haupt-Interface verfügbar │
|
||||
│ (kein separates Chat-Interface notwendig) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Interface-Struktur: Access vs. Objects
|
||||
|
||||
Jedes Interface besteht aus **zwei Klassen**:
|
||||
|
||||
### 1. `*Access` Klasse (Zugriffskontrolle)
|
||||
|
||||
**Zweck:** Prüft, wer was sehen/dürfen darf
|
||||
|
||||
**Methoden:**
|
||||
- `uam()` - Filtert Daten basierend auf Benutzerprivilegien
|
||||
- `canModify()` - Prüft, ob Benutzer ändern darf
|
||||
|
||||
**Beispiel:** `RealEstateAccess`
|
||||
|
||||
### 2. `*Objects` Klasse (Haupt-Interface)
|
||||
|
||||
**Zweck:** Führt CRUD-Operationen aus
|
||||
|
||||
**Methoden:**
|
||||
- `create*()` - Erstellt neue Einträge
|
||||
- `get*()` - Lädt einzelne Einträge nach ID
|
||||
- `get*()` (Plural) - Lädt Listen von Einträgen mit optionalen Filtern
|
||||
- `update*()` - Aktualisiert Einträge
|
||||
- `delete*()` - Löscht Einträge
|
||||
- `executeQuery()` - Führt direkte SQL-Queries aus (stateless)
|
||||
|
||||
**Nutzt:** `*Access` für Zugriffskontrolle
|
||||
|
||||
**Beispiel:** `RealEstateObjects`
|
||||
|
||||
**Warum getrennt?**
|
||||
- Separation of Concerns: Zugriffskontrolle ist separate Verantwortlichkeit
|
||||
- Wiederverwendbarkeit: Access-Klasse kann von mehreren Interfaces genutzt werden
|
||||
- Testbarkeit: Zugriffskontrolle kann unabhängig getestet werden
|
||||
|
||||
---
|
||||
|
||||
## Implementierung: Real Estate CRUD-Interface
|
||||
|
||||
Das Real Estate CRUD-Interface besteht aus **zwei separaten Dateien**, genau wie bei anderen Features (`interfaceDbAppObjects.py` + `interfaceDbAppAccess.py`).
|
||||
|
||||
### Datei 1: Access-Implementierung
|
||||
|
||||
**Datei:** `modules/interfaces/interfaceDbRealEstateAccess.py`
|
||||
|
||||
**Enthält:**
|
||||
- `RealEstateAccess` Klasse
|
||||
- Methoden: `uam()`, `canModify()`
|
||||
|
||||
**Funktionalität:**
|
||||
- **`uam()`**: Filtert Datensätze basierend auf Benutzerprivilegien (SYSADMIN sieht alles, ADMIN sieht Mandat, User sieht nur eigene)
|
||||
- **`canModify()`**: Prüft, ob Benutzer Datensätze ändern/löschen darf
|
||||
- Fügt Zugriffskontroll-Attribute hinzu: `_hideView`, `_hideEdit`, `_hideDelete`
|
||||
|
||||
---
|
||||
|
||||
### Datei 2: Objects-Implementierung
|
||||
|
||||
**Datei:** `modules/interfaces/interfaceDbRealEstateObjects.py`
|
||||
|
||||
**Enthält:**
|
||||
- `RealEstateObjects` Klasse (Haupt-Interface)
|
||||
- `getInterface()` Factory-Funktion (Singleton-Pattern)
|
||||
|
||||
**Datenbank-Konfiguration:**
|
||||
- Verwendet `DB_REALESTATE_*` Umgebungsvariablen (nicht `DB_APP_*`)
|
||||
- Variablen: `DB_REALESTATE_HOST`, `DB_REALESTATE_DATABASE`, `DB_REALESTATE_USER`, `DB_REALESTATE_PASSWORD_SECRET`, `DB_REALESTATE_PORT`
|
||||
|
||||
**CRUD-Methoden für alle Entitäten:**
|
||||
- **Projekt**: `createProjekt()`, `getProjekt()`, `getProjekte()`, `updateProjekt()`, `deleteProjekt()`
|
||||
- **Parzelle**: `createParzelle()`, `getParzelle()`, `getParzellen()`, `updateParzelle()`, `deleteParzelle()`
|
||||
- **Dokument**: `createDokument()`, `getDokument()`, `getDokumente()`, `updateDokument()`, `deleteDokument()`
|
||||
- **Gemeinde**: `createGemeinde()`, `getGemeinde()`, `getGemeinden()`, `updateGemeinde()`, `deleteGemeinde()`
|
||||
- **Kanton**: `createKanton()`, `getKanton()`, `getKantone()`, `updateKanton()`, `deleteKanton()`
|
||||
- **Land**: `createLand()`, `getLand()`, `getLaender()`, `updateLand()`, `deleteLand()`
|
||||
|
||||
**Zusätzliche Funktionalität:**
|
||||
- **List-Methoden**: Alle Entitäten haben `get*()` (Plural) Methoden für Listen mit optionalen Filtern
|
||||
- **Location-Resolution**: `getParzellen()` löst automatisch Gemeinde-Namen zu IDs auf
|
||||
- **Query-Ausführung**: `executeQuery()` für direkte SQL-Queries (stateless)
|
||||
- **Supporting Tables**: Automatische Erstellung von Land, Kanton, Gemeinde, Dokument Tabellen bei Initialisierung
|
||||
|
||||
## Wichtige Punkte:
|
||||
|
||||
1. **DatabaseConnector**: Nutzt `connectorDbPostgre.DatabaseConnector` für Datenbankzugriff
|
||||
2. **Access Control**: `RealEstateAccess` implementiert Benutzer- und Mandaten-Filterung
|
||||
3. **Singleton Pattern**: `getInterface()` erstellt pro User eine Instanz
|
||||
4. **CRUD-Operationen**: `recordCreate`, `recordModify`, `recordDelete`, `getRecordset` vom Connector
|
||||
5. **MandateId**: Wird automatisch gesetzt, wenn nicht vorhanden
|
||||
6. **List-Methoden**: Alle Entitäten haben `get*()` (Plural) Methoden für Listen mit optionalen Filtern
|
||||
7. **Location-Resolution**: Parzelle-Filter können Gemeinde-Namen enthalten, die automatisch zu IDs aufgelöst werden
|
||||
8. **Query-Ausführung**: `executeQuery()` ist direkt im Haupt-Interface verfügbar (kein separates Chat-Interface notwendig)
|
||||
9. **Datenbank-Initialisierung**: Unterstützende Tabellen (Land, Kanton, Gemeinde, Dokument) werden automatisch erstellt
|
||||
|
||||
---
|
||||
|
||||
## Query-Ausführung: executeQuery()
|
||||
|
||||
Das Haupt-Interface `RealEstateObjects` enthält die Methode `executeQuery()` für direkte SQL-Queries. **Kein separates Chat-Interface ist notwendig.**
|
||||
|
||||
### Verwendung
|
||||
|
||||
**Für CRUD-Operationen (EMPFOHLEN):**
|
||||
- Verwenden Sie die strukturierten CRUD-Methoden (`createProjekt()`, `getProjekte()`, etc.)
|
||||
- Vorteile: Validierung, Zugriffskontrolle, Typsicherheit, keine SQL-Injection-Risiken
|
||||
|
||||
**Für komplexe SELECT-Queries (OPTIONAL):**
|
||||
- Verwenden Sie `executeQuery()` direkt im Haupt-Interface
|
||||
- **Warnung**: Nur für SELECT-Queries, immer Parameterisierung verwenden, Queries validieren
|
||||
|
||||
### Hinweise zur Query-Ausführung
|
||||
|
||||
1. **Stateless**: Keine Session-Management-Funktionen
|
||||
2. **Nur für Queries**: Primär für SELECT-Queries gedacht
|
||||
3. **Sicherheit**: Immer Parameterisierung verwenden
|
||||
4. **Validierung**: Queries sollten validiert werden (z.B. nur SELECT erlauben)
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung: Benötigte Dateien
|
||||
|
||||
### Erforderlich (für CRUD-Operationen):
|
||||
|
||||
1. ✅ `modules/datamodels/datamodelRealEstate.py`
|
||||
- Real Estate-Datenmodelle (Projekt, Parzelle, Dokument, etc.)
|
||||
|
||||
2. ✅ `modules/interfaces/interfaceDbRealEstateAccess.py`
|
||||
- Zugriffskontrolle für Real Estate-Entitäten (RealEstateAccess)
|
||||
|
||||
3. ✅ `modules/interfaces/interfaceDbRealEstateObjects.py`
|
||||
- Real Estate CRUD-Interface (RealEstateObjects)
|
||||
- Nutzt `interfaceDbRealEstateAccess.py`
|
||||
- Haupt-Interface für alle CRUD-Operationen
|
||||
|
||||
### Hinweis zu Query-Ausführung:
|
||||
|
||||
4. ✅ `executeQuery()` ist bereits im Haupt-Interface verfügbar
|
||||
- Kein separates Chat-Interface notwendig
|
||||
- Direkt in `RealEstateObjects` verfügbar
|
||||
- Stateless, keine Session-Management
|
||||
|
||||
---
|
||||
|
||||
[← Zurück: Datenmodell erstellen](02-datamodels.md) | [Weiter: Feature-Logik implementieren →](04-feature-logic.md)
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,236 +0,0 @@
|
|||
# Schritt 3: Feature-Logik implementieren
|
||||
|
||||
[← Zurück: Interface erstellen](03-interfaces.md) | [Weiter: Routen erstellen →](05-routes.md)
|
||||
|
||||
**Datei:** `modules/features/realEstate/mainRealEstate.py`
|
||||
|
||||
Die Feature-Logik enthält die Geschäftslogik für das Feature. Sie wird von den Routen aufgerufen und arbeitet **stateless** ohne Session-Management.
|
||||
|
||||
## Übersicht: Stateless Feature-Logik mit AI-Integration
|
||||
|
||||
Die Feature-Logik verwendet **AI**, um natürliche Sprache direkt in CRUD-Operationen zu übersetzen - ohne Session-Management:
|
||||
|
||||
```
|
||||
User Input (natürliche Sprache)
|
||||
↓
|
||||
AI-Analyse (Intent-Erkennung)
|
||||
↓
|
||||
CRUD-Operation identifizieren
|
||||
↓
|
||||
Parameter extrahieren
|
||||
↓
|
||||
Interface CRUD-Methode aufrufen
|
||||
↓
|
||||
Datenbank-Operation ausführen
|
||||
↓
|
||||
Ergebnis zurückgeben (keine Session-Speicherung)
|
||||
```
|
||||
|
||||
**Beispiel:**
|
||||
- User: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
- AI analysiert → Intent: CREATE, Entity: Projekt, Parameter: {label: "Hauptstrasse 42"}
|
||||
- Feature-Logik ruft auf → Interface-Methode `createProjekt()` mit extrahierten Parametern
|
||||
- Ergebnis wird direkt zurückgegeben (keine Session, keine History)
|
||||
|
||||
## AI-Integration: Services initialisieren
|
||||
|
||||
Um AI zu verwenden, müssen Sie die **Services** initialisieren. Services sind eine zentrale Schnittstelle zu verschiedenen Systemkomponenten (AI, Chat, Database, etc.).
|
||||
|
||||
### Services-Initialisierung
|
||||
|
||||
**Wichtig:**
|
||||
- Services werden normalerweise im Feature-Logik-Modul initialisiert und an Funktionen weitergegeben
|
||||
- Für Query-Ausführung wird `getRealEstateInterface()` verwendet, nicht `getChatInterface()`
|
||||
|
||||
---
|
||||
|
||||
## AI-basierte Intent-Erkennung und CRUD-Operationen
|
||||
|
||||
### Schritt 1: Intent-Analyse mit AI
|
||||
|
||||
Die AI analysiert User-Input und identifiziert:
|
||||
- **Intent**: CREATE, READ, UPDATE, DELETE, QUERY
|
||||
- **Entity**: Projekt, Parzelle, Dokument, etc.
|
||||
- **Parameter**: Extrahierte Werte aus dem User-Input
|
||||
|
||||
### Schritt 2: CRUD-Operation ausführen
|
||||
|
||||
Basierend auf der AI-Analyse wird die entsprechende Interface-Methode aufgerufen.
|
||||
|
||||
## Wichtige Punkte:
|
||||
|
||||
### 1. Services-Initialisierung
|
||||
|
||||
- **`getServices(currentUser, workflow=None)`** - Initialisiert Services für AI-Zugriff
|
||||
- **`services.ai`** - Zugriff auf AI-Service für AI-Aufrufe
|
||||
|
||||
### 2. AI-Aufrufe
|
||||
|
||||
- **`callAiPlanning()`** - Für strukturierte JSON-Antworten (Intent-Analyse, SQL-Übersetzung)
|
||||
- **`callAiText()`** - Für einfache Text-Generierung
|
||||
- **`callAiDocuments()`** - Für Dokumenten-Verarbeitung
|
||||
|
||||
### 3. Intent-Analyse
|
||||
|
||||
Die AI analysiert User-Input und gibt zurück:
|
||||
- **Intent**: CREATE, READ, UPDATE, DELETE, QUERY
|
||||
- **Entity**: Projekt, Parzelle, Dokument, etc.
|
||||
- **Parameters**: Extrahierte Werte aus dem Input
|
||||
|
||||
### 4. CRUD-Operationen
|
||||
|
||||
Basierend auf der Intent-Analyse werden folgende Operationen unterstützt:
|
||||
|
||||
**Unterstützte Entities:**
|
||||
- Projekt, Parzelle, Gemeinde, Kanton, Land, Dokument
|
||||
|
||||
**CREATE** → `interface.createProjekt()`, `interface.createParzelle()`, `interface.createGemeinde()`, `interface.createKanton()`, `interface.createLand()`, `interface.createDokument()`
|
||||
|
||||
**READ** →
|
||||
- Einzelne Entität: `interface.getProjekt(id)`, `interface.getParzelle(id)`, etc.
|
||||
- Liste mit Filtern: `interface.getProjekte(recordFilter)`, `interface.getParzellen(recordFilter)`, etc.
|
||||
- **Wichtig:** READ-Operationen validieren Filter-Felder gegen das Datenmodell
|
||||
|
||||
**UPDATE** → `interface.updateProjekt(id, updateData)`, `interface.updateParzelle(id, updateData)`, etc.
|
||||
|
||||
**DELETE** → `interface.deleteProjekt(id)`, `interface.deleteParzelle(id)`, etc.
|
||||
|
||||
**QUERY** → `interface.executeQuery(queryText, parameters)` für direkte SQL-Ausführung
|
||||
|
||||
### 5. Field Validation
|
||||
|
||||
- **READ-Operationen** validieren Filter-Felder gegen das Datenmodell
|
||||
- Ungültige Felder werden ignoriert und geloggt
|
||||
- **Wichtig:** Location-Queries sollten Parzelle-Entity verwenden, nicht Projekt direkt
|
||||
|
||||
### 6. Error Handling
|
||||
|
||||
- Umfassendes Error Handling für AI-Aufrufe
|
||||
- JSON-Parsing mit Fallback (extrahiert JSON aus Markdown-Code-Blöcken)
|
||||
- Validierung der AI-Response-Struktur
|
||||
- Logging für Debugging mit `exc_info=True` für vollständige Stack-Traces
|
||||
- Entity-Validierung: Prüft ob Entity existiert vor Update/Delete
|
||||
|
||||
---
|
||||
|
||||
## Vollständiger Flow: User-Input → CRUD-Operation (stateless)
|
||||
|
||||
### Beispiel: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
|
||||
```
|
||||
1. User sendet HTTP POST Request
|
||||
POST /api/realestate/command
|
||||
Body: {"userInput": "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"}
|
||||
|
||||
2. Route ruft Feature-Logik auf
|
||||
→ processNaturalLanguageCommand() wird aufgerufen
|
||||
→ Keine Session-ID notwendig!
|
||||
|
||||
3. Feature-Logik initialisiert Services
|
||||
→ Services werden für den aktuellen User initialisiert
|
||||
→ AI-Service wird aus Services abgerufen
|
||||
|
||||
4. AI analysiert User-Input
|
||||
→ analyzeUserIntent() wird aufgerufen
|
||||
→ AI gibt zurück: Intent "CREATE", Entity "Projekt", Parameter {"label": "Hauptstrasse 42"}
|
||||
|
||||
5. Feature-Logik führt CRUD-Operation aus
|
||||
→ executeIntentBasedOperation() wird mit Intent und Parametern aufgerufen
|
||||
→ Real Estate Interface wird initialisiert
|
||||
→ Projekt-Objekt wird aus Parametern erstellt
|
||||
→ createProjekt() wird aufgerufen
|
||||
|
||||
6. Interface speichert in Datenbank
|
||||
→ Datenbank-Operation wird über Interface ausgeführt
|
||||
→ PostgreSQL INSERT wird durchgeführt
|
||||
|
||||
7. Ergebnis wird direkt zurückgegeben
|
||||
→ Route gibt HTTP Response zurück
|
||||
→ Keine Session-Speicherung, keine History
|
||||
→ Frontend zeigt Erfolg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI-Service Methoden im Detail
|
||||
|
||||
### `callAiPlanning()` - Für strukturierte Antworten
|
||||
|
||||
**Verwendung:** Intent-Analyse, SQL-Übersetzung, strukturierte Daten-Extraktion
|
||||
|
||||
**Vorteile:**
|
||||
- Optimiert für strukturierte JSON-Antworten
|
||||
- Verwendet beste Modelle für Planungs-Aufgaben
|
||||
- Automatisches Debug-File-Writing
|
||||
|
||||
### `callAiText()` - Für einfache Text-Generierung
|
||||
|
||||
**Verwendung:** Text-Generierung, Zusammenfassungen, Erklärungen
|
||||
|
||||
**Hinweis:** Response ist direkt ein Text-String (kein JSON-Parsing nötig)
|
||||
|
||||
### `callAiDocuments()` - Für Dokumenten-Verarbeitung
|
||||
|
||||
**Verwendung:** Dokumenten-Analyse, Extraktion, Generierung mit Dokumenten-Kontext
|
||||
|
||||
**Hinweis:** Unterstützt optionales `outputFormat` Parameter für strukturierte Ausgaben (z.B. "json")
|
||||
|
||||
---
|
||||
|
||||
## Best Practices für AI-Integration
|
||||
|
||||
### 1. Prompt-Engineering
|
||||
|
||||
- **Klare Struktur**: Definieren Sie genau, welche Antwort Sie erwarten
|
||||
- **Beispiele**: Geben Sie Beispiele für bessere Ergebnisse
|
||||
- **Format**: Spezifizieren Sie das erwartete Format (JSON, SQL, etc.)
|
||||
|
||||
### 2. Error Handling
|
||||
|
||||
- **JSON-Parsing**: Immer try/except für JSON-Parsing
|
||||
- **Fallback**: Planen Sie Fallback-Strategien bei AI-Fehlern
|
||||
- **Validierung**: Validieren Sie AI-Antworten vor Verwendung
|
||||
|
||||
### 3. Sicherheit
|
||||
|
||||
- **Query-Validierung**: Validieren Sie SQL-Queries vor Ausführung
|
||||
- **Parameter-Sanitization**: Sanitizen Sie alle Parameter
|
||||
- **MandateId-Filter**: Stellen Sie sicher, dass MandateId immer gefiltert wird
|
||||
|
||||
### 4. Performance
|
||||
|
||||
- **Caching**: Cache häufige AI-Antworten wenn möglich
|
||||
- **Model-Auswahl**: Lassen Sie das System automatisch das beste Modell wählen
|
||||
- **Async**: Nutzen Sie async/await für nicht-blockierende Operationen
|
||||
|
||||
### 5. Debugging
|
||||
|
||||
- **Debug-Files**: Nutzen Sie `debugType` Parameter für Debug-Dateien
|
||||
- **Logging**: Loggen Sie alle AI-Aufrufe und Antworten
|
||||
- **Confidence-Scores**: Nutzen Sie Confidence-Scores für Fehlerbehandlung
|
||||
|
||||
---
|
||||
|
||||
## Erweiterte Features
|
||||
|
||||
### Schema-Aware Prompting
|
||||
|
||||
Sie können das Datenbank-Schema in Prompts einbinden, um der AI besseren Kontext zu geben. Laden Sie Schema-Informationen dynamisch und fügen Sie diese in den Prompt ein.
|
||||
|
||||
### Context-Aware Operations (Optional)
|
||||
|
||||
Falls Sie später Kontext zwischen Queries benötigen, können Sie optional eine Session verwenden. Für stateless Operationen ist dies normalerweise nicht notwendig. Falls gewünscht, können Sie vorherige Queries aus einer Session laden und als Kontext in den Prompt einbinden.
|
||||
|
||||
### Multi-Step Operations
|
||||
|
||||
Für komplexe Operationen können Sie mehrere AI-Calls machen:
|
||||
1. **Intent-Analyse**: Zuerst den User-Intent analysieren
|
||||
2. **Parameter-Validierung**: Optional Parameter mit AI validieren
|
||||
3. **CRUD-Operation**: Die eigentliche Operation ausführen
|
||||
|
||||
---
|
||||
|
||||
[← Zurück: Interface erstellen](03-interfaces.md) | [Weiter: Routen erstellen →](05-routes.md)
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
# Schritt 4: Routen erstellen
|
||||
|
||||
[← Zurück: Feature-Logik implementieren](04-feature-logic.md) | [Weiter: Router registrieren →](06-router-registration.md)
|
||||
|
||||
**Datei:** `modules/routes/routeRealEstate.py`
|
||||
|
||||
Die Routen definieren die REST-API-Endpunkte für das Feature. Das Feature arbeitet **stateless** ohne Session-Management.
|
||||
|
||||
## Route-Struktur
|
||||
|
||||
```
|
||||
/api/realestate/
|
||||
├── POST /command → Natürliche Sprache → CRUD-Operation
|
||||
├── POST /query → Direkte SQL-Query
|
||||
├── GET /tables → Liste aller verfügbaren Tabellen
|
||||
├── GET /table/{table} → Daten aus einer Tabelle (mit optionaler Pagination)
|
||||
└── POST /table/{table} → Neuen Datensatz in einer Tabelle erstellen
|
||||
```
|
||||
|
||||
## Wichtige Punkte:
|
||||
|
||||
### 1. Stateless Design
|
||||
|
||||
- **Keine Session-Management**: Alle Endpunkte arbeiten stateless
|
||||
- **Direkte Verarbeitung**: User-Input wird direkt verarbeitet und Ergebnis zurückgegeben
|
||||
- **Keine History**: Queries werden nicht gespeichert (kann optional später hinzugefügt werden)
|
||||
|
||||
### 2. API-Endpunkte
|
||||
|
||||
**`POST /api/realestate/command`**
|
||||
- Verarbeitet natürliche Sprache
|
||||
- Nutzt AI für Intent-Analyse
|
||||
- Führt CRUD-Operationen aus
|
||||
- Gibt Ergebnis direkt zurück
|
||||
- **CSRF-Token erforderlich** (X-CSRF-Token Header)
|
||||
|
||||
**`POST /api/realestate/query`**
|
||||
- Führt direkte SQL-Queries aus
|
||||
- Request Body: `{"queryText": "...", "parameters": {...}}`
|
||||
- Keine Session notwendig
|
||||
- Gibt Query-Ergebnis direkt zurück
|
||||
- **CSRF-Token erforderlich** (X-CSRF-Token Header)
|
||||
|
||||
**`GET /api/realestate/tables`**
|
||||
- Gibt Liste aller verfügbaren Tabellen zurück
|
||||
- Enthält Tabellennamen, Beschreibungen und Model-Namen
|
||||
- **CSRF-Token erforderlich** (X-CSRF-Token Header)
|
||||
|
||||
**`GET /api/realestate/table/{table}`**
|
||||
- Gibt alle Daten aus einer spezifischen Tabelle zurück
|
||||
- Unterstützt optionale Pagination über Query-Parameter
|
||||
- Sortierung und Filterung möglich
|
||||
- Leere Tabellen geben ein leeres Modell-Instanz zurück (für Schema-Extraktion)
|
||||
- **CSRF-Token erforderlich** (X-CSRF-Token Header)
|
||||
|
||||
**`POST /api/realestate/table/{table}`**
|
||||
- Erstellt einen neuen Datensatz in einer spezifischen Tabelle
|
||||
- Request Body enthält die Datensatz-Daten
|
||||
- mandateId wird automatisch gesetzt falls nicht vorhanden
|
||||
- **CSRF-Token erforderlich** (X-CSRF-Token Header)
|
||||
|
||||
### 3. Sicherheit
|
||||
|
||||
- **Rate Limiting**: 120 Requests pro Minute für alle Endpunkte
|
||||
- **Authentication**: Alle Endpunkte erfordern authentifizierte User
|
||||
- **CSRF-Token-Validierung**: Alle Endpunkte validieren CSRF-Token im X-CSRF-Token Header
|
||||
- Token muss hexadezimaler String sein
|
||||
- Token-Länge: 16-64 Zeichen
|
||||
- Fehlende oder ungültige Token führen zu 403 Forbidden
|
||||
- **Query-Validierung**: WICHTIG - Validieren Sie SQL-Queries vor Ausführung
|
||||
- **MandateId-Filter**: Wird automatisch durch Interfaces angewendet
|
||||
|
||||
### 4. Error Handling
|
||||
|
||||
- Umfassendes Error Handling mit HTTPException
|
||||
- Unterschiedliche Status-Codes:
|
||||
- **400 Bad Request**: Validierungsfehler (z.B. fehlende Parameter, ungültige Daten)
|
||||
- **403 Forbidden**: CSRF-Token fehlt oder ist ungültig
|
||||
- **404 Not Found**: Ressource nicht gefunden
|
||||
- **500 Internal Server Error**: Server-Fehler
|
||||
- Detaillierte Fehlermeldungen für Debugging
|
||||
- Logging mit vollständigen Stack-Traces (`exc_info=True`)
|
||||
|
||||
### 5. Response-Struktur
|
||||
|
||||
**Command-Endpunkt:**
|
||||
- Erfolgreiche Response enthält: `success`, `intent`, `entity`, `result`
|
||||
- Result enthält die Operation-Details und das erstellte/aktualisierte/gelesene Objekt
|
||||
|
||||
**Query-Endpunkt:**
|
||||
- Response enthält: `status`, `rows`, `columns`, `rowCount`, `executionTime`
|
||||
|
||||
**Tables-Endpunkt:**
|
||||
- Response enthält: `tables` (Array mit Tabellen-Informationen), `count`
|
||||
|
||||
**Table GET-Endpunkt:**
|
||||
- Response ist eine `PaginatedResponse` mit `items` und `pagination` Metadata
|
||||
- Ohne Pagination: Alle Items werden zurückgegeben
|
||||
- Mit Pagination: Enthält `currentPage`, `pageSize`, `totalItems`, `totalPages`, `sort`, `filters`
|
||||
|
||||
**Table POST-Endpunkt:**
|
||||
- Response ist das erstellte Objekt als Dictionary (model_dump())
|
||||
|
||||
---
|
||||
|
||||
## Flow: Route → Feature-Logik
|
||||
|
||||
### Command-Endpunkt Flow
|
||||
|
||||
```
|
||||
POST /api/realestate/command
|
||||
↓
|
||||
CSRF-Token-Validierung
|
||||
↓
|
||||
routeRealEstate.process_command()
|
||||
↓
|
||||
getCurrentUser() # Auth
|
||||
↓
|
||||
processNaturalLanguageCommand(currentUser, userInput)
|
||||
↓
|
||||
mainRealEstate.processNaturalLanguageCommand()
|
||||
↓
|
||||
analyzeUserIntent() → executeIntentBasedOperation()
|
||||
↓
|
||||
return Dict mit Ergebnis
|
||||
```
|
||||
|
||||
### Query-Endpunkt Flow
|
||||
|
||||
```
|
||||
POST /api/realestate/query
|
||||
↓
|
||||
CSRF-Token-Validierung
|
||||
↓
|
||||
routeRealEstate.execute_query()
|
||||
↓
|
||||
getCurrentUser() # Auth
|
||||
↓
|
||||
Body-Parsing (queryText, parameters)
|
||||
↓
|
||||
executeDirectQuery(currentUser, queryText, parameters)
|
||||
↓
|
||||
mainRealEstate.executeDirectQuery()
|
||||
↓
|
||||
getRealEstateInterface(currentUser)
|
||||
↓
|
||||
Interface.executeQuery(queryText, parameters)
|
||||
↓
|
||||
return Dict mit rows, columns, rowCount
|
||||
```
|
||||
|
||||
### Table-Endpunkte Flow
|
||||
|
||||
**GET /api/realestate/table/{table}:**
|
||||
```
|
||||
GET /api/realestate/table/{table}
|
||||
↓
|
||||
CSRF-Token-Validierung
|
||||
↓
|
||||
getCurrentUser() # Auth
|
||||
↓
|
||||
Tabellen-Name validieren
|
||||
↓
|
||||
getRealEstateInterface(currentUser)
|
||||
↓
|
||||
Interface.getProjekte() / getParzellen() / etc.
|
||||
↓
|
||||
Pagination anwenden (falls angegeben)
|
||||
↓
|
||||
return PaginatedResponse
|
||||
```
|
||||
|
||||
**POST /api/realestate/table/{table}:**
|
||||
```
|
||||
POST /api/realestate/table/{table}
|
||||
↓
|
||||
CSRF-Token-Validierung
|
||||
↓
|
||||
getCurrentUser() # Auth
|
||||
↓
|
||||
Tabellen-Name validieren
|
||||
↓
|
||||
Model-Instanz aus Body-Daten erstellen
|
||||
↓
|
||||
getRealEstateInterface(currentUser)
|
||||
↓
|
||||
Interface.createProjekt() / createParzelle() / etc.
|
||||
↓
|
||||
return erstelltes Objekt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verfügbare Tabellen
|
||||
|
||||
Die folgenden Tabellen sind verfügbar:
|
||||
- **Projekt**: Real estate projects
|
||||
- **Parzelle**: Plots/parcels
|
||||
- **Dokument**: Documents
|
||||
- **Gemeinde**: Municipalities
|
||||
- **Kanton**: Cantons
|
||||
- **Land**: Countries
|
||||
|
||||
## Pagination
|
||||
|
||||
Der `GET /api/realestate/table/{table}` Endpunkt unterstützt Pagination über einen Query-Parameter:
|
||||
|
||||
- **Parameter**: `pagination` (JSON-encoded string)
|
||||
- **Format**: `{"page": 1, "pageSize": 10, "sort": [{"field": "label", "direction": "asc"}], "filters": []}`
|
||||
- **Ohne Pagination**: Alle Datensätze werden zurückgegeben
|
||||
|
||||
## Vorteile des stateless Ansatzes
|
||||
|
||||
- **Einfachheit**: Kein Session-Management notwendig
|
||||
- **Performance**: Weniger Datenbank-Operationen pro Request
|
||||
- **Skalierbarkeit**: Stateless Requests sind einfacher zu skalieren
|
||||
- **Flexibilität**: Jeder Request ist unabhängig
|
||||
- **Schnell**: Direkte Verarbeitung ohne Overhead
|
||||
|
||||
---
|
||||
|
||||
[← Zurück: Feature-Logik implementieren](04-feature-logic.md) | [Weiter: Router registrieren →](06-router-registration.md)
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
# Schritt 6: Environment-Konfiguration
|
||||
|
||||
[← Zurück: Router registrieren](06-router-registration.md) | [Weiter: Feature Lifecycle →](08-lifecycle.md)
|
||||
|
||||
**Datei:** `env_dev.env`
|
||||
|
||||
Für das realEstate-Feature benötigen wir keine zusätzlichen Environment-Variablen, da es die bereits vorhandenen PostgreSQL-Konfigurationen nutzt:
|
||||
|
||||
```env
|
||||
# PostgreSQL Storage (bereits vorhanden)
|
||||
DB_APP_HOST=localhost
|
||||
DB_APP_DATABASE=poweron_app
|
||||
DB_APP_USER=poweron_dev
|
||||
DB_APP_PASSWORD_SECRET = DEV_ENC:...
|
||||
DB_APP_PORT=5432
|
||||
```
|
||||
|
||||
**Optional**: Falls Sie eine separate Datenbank für Real Estate verwenden möchten, können Sie zusätzliche Variablen hinzufügen:
|
||||
|
||||
```env
|
||||
# Optional: Separate Real Estate Database
|
||||
DB_REALESTATE_HOST=localhost
|
||||
DB_REALESTATE_DATABASE=poweron_realestate
|
||||
DB_REALESTATE_USER=poweron_dev
|
||||
DB_REALESTATE_PASSWORD_SECRET = DEV_ENC:...
|
||||
DB_REALESTATE_PORT=5432
|
||||
```
|
||||
|
||||
In diesem Fall müssten Sie die `_initializeDatabase()` Methode im Interface anpassen:
|
||||
|
||||
```python
|
||||
def _initializeDatabase(self):
|
||||
"""Initialize PostgreSQL database connection."""
|
||||
try:
|
||||
# Use Real Estate specific config if available, otherwise fall back to APP config
|
||||
dbHost = APP_CONFIG.get("DB_REALESTATE_HOST") or APP_CONFIG.get("DB_APP_HOST", "localhost")
|
||||
dbDatabase = APP_CONFIG.get("DB_REALESTATE_DATABASE") or APP_CONFIG.get("DB_APP_DATABASE", "poweron_app")
|
||||
dbUser = APP_CONFIG.get("DB_REALESTATE_USER") or APP_CONFIG.get("DB_APP_USER")
|
||||
dbPassword = APP_CONFIG.get("DB_REALESTATE_PASSWORD_SECRET") or APP_CONFIG.get("DB_APP_PASSWORD_SECRET")
|
||||
dbPort = int(APP_CONFIG.get("DB_REALESTATE_PORT") or APP_CONFIG.get("DB_APP_PORT", 5432))
|
||||
|
||||
# ... rest of initialization ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[← Zurück: Router registrieren](06-router-registration.md) | [Weiter: Feature Lifecycle →](08-lifecycle.md)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,491 +0,0 @@
|
|||
# Swiss Topo STAC API Integration Guide
|
||||
|
||||
This document provides comprehensive conceptual documentation for integrating the Swiss Federal Office of Topography (Swisstopo) STAC (SpatioTemporal Asset Catalog) API into the Real Estate feature. This integration enables two primary user workflows: creating projects with enriched parcel data, and browsing maps to explore parcels and their project associations.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Architecture](#architecture)
|
||||
3. [User Story 1: Create Project and Connect Parcels](#user-story-1-create-project-and-connect-parcels)
|
||||
4. [User Story 2: Browse Map and Explore Parcels](#user-story-2-browse-map-and-explore-parcels)
|
||||
5. [API Routes and Endpoints](#api-routes-and-endpoints)
|
||||
6. [Implementation Components](#implementation-components)
|
||||
7. [Data Flow Summary](#data-flow-summary)
|
||||
8. [Error Handling and Security](#error-handling-and-security)
|
||||
9. [Testing and Configuration](#testing-and-configuration)
|
||||
10. [Summary](#summary)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
### Purpose
|
||||
|
||||
The STAC API integration enables the Real Estate feature to support two distinct user workflows:
|
||||
|
||||
**User Story 1**: Create a new project, select parcels on a map, automatically enrich them with geospatial data, and store everything in the database for project management.
|
||||
|
||||
**User Story 2**: Browse a map, click on parcels to fetch their data from STAC, and see whether each parcel is already connected to an existing project or available for new projects.
|
||||
|
||||
### STAC API Overview
|
||||
|
||||
The Swiss Federal Office of Topography provides a STAC-compliant API that follows open standards for describing geospatial data. The API provides:
|
||||
|
||||
- **Collections**: Groups of related geospatial data (e.g., cadastral parcels, zoning maps)
|
||||
- **Items**: Individual geospatial features (e.g., a specific parcel, a zoning polygon)
|
||||
- **Assets**: Downloadable files associated with items (e.g., GeoPackage files, PDFs)
|
||||
|
||||
### Key STAC Collections for Real Estate
|
||||
|
||||
| Collection ID | Description | Use Case |
|
||||
|--------------|-------------|----------|
|
||||
| `ch.swisstopo.amtliche-vermessung` | Official cadastral survey | Identify parcel boundaries |
|
||||
| `ch.swisstopo.swissboundaries3d-gemeinde-flaeche` | Municipality boundaries | Resolve Gemeinde from coordinates |
|
||||
| `ch.swisstopo.swissboundaries3d-kanton-flaeche` | Canton boundaries | Resolve Kanton from coordinates |
|
||||
| `ch.are.nutzungsplanung` | Land use planning (zoning) | Extract bauzone, baulinie |
|
||||
| `ch.bafu.laermbelastung` | Noise pollution zones | Set laermschutzzone |
|
||||
| `ch.bafu.hochwassergefahrenkarte` | Flood hazard map | Set hochwasserschutzzone |
|
||||
| `ch.bafu.grundwasserschutz` | Groundwater protection zones | Set grundwasserschutzzone |
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
The integration follows the existing Gateway architecture pattern with clear separation of concerns:
|
||||
|
||||
**Frontend Layer**: User interaction components including map widgets, project forms, and parcel exploration views that communicate with the backend via HTTP/REST.
|
||||
|
||||
**API Routes Layer**: HTTP endpoint definitions that handle request validation, authentication, and response formatting. New domain-focused endpoints will replace generic table-based routes.
|
||||
|
||||
**Feature Logic Layer**: Business rules and orchestration logic that coordinates STAC queries, data transformation, and enrichment processes. This layer contains the core intelligence for processing location selections, fetching parcel data, and checking parcel-project associations.
|
||||
|
||||
**Service Layer**: Specialized services including AI-powered document extraction and text processing capabilities that can analyze zoning regulations from PDF documents.
|
||||
|
||||
**Interface Layer**: Normalized data access layer providing CRUD operations on Real Estate entities (Projekt, Parzelle, Gemeinde, etc.) with proper access control and mandate filtering.
|
||||
|
||||
**Connector Layer**: External API communication layer that handles STAC API interactions, including query construction, response parsing, error handling, and retry logic.
|
||||
|
||||
**Data Storage**: PostgreSQL database for persistent storage of project data, and the Swiss Topo STAC API as the external geospatial data source.
|
||||
|
||||
---
|
||||
|
||||
## User Story 1: Create Project and Connect Parcels
|
||||
|
||||
**User Goal**: Create a new construction project, select parcels on a map, automatically enrich them with geospatial data, and store everything in the database for project management.
|
||||
|
||||
### Workflow Steps
|
||||
|
||||
1. **Create New Project**: User provides project name and mandate identifier. Backend creates Projekt object with initial status "Analyse" and empty collections. ✅ Already implemented
|
||||
|
||||
2. **Select Location on Map**: User clicks on map at location of interest. Frontend sends coordinates (LV95/EPSG:2056) to backend. ❌ New endpoint needed
|
||||
|
||||
3. **Identify Parcel from STAC**: Backend queries STAC cadastral collection for parcels intersecting the clicked point. Returns parcel geometry, identifier, properties, and assets. ❌ New functionality needed
|
||||
|
||||
4. **Create Parzelle and Connect**: Backend converts STAC geometry to internal GeoPolylinie format, creates Parzelle object, and attaches it to Projekt. Parcel becomes part of project data model for enrichment, annotation, and offline access. ❌ New functionality needed
|
||||
|
||||
5. **Enrich Administrative Context**: Backend searches STAC for Gemeinde and Kanton boundaries, creates/updates entities, and establishes hierarchy: Parzelle → Gemeinde → Kanton → Land. ❌ New functionality needed
|
||||
|
||||
6. **Extract Zoning Information**: Backend searches zoning collection, calculates intersection areas, selects dominant zone by largest overlap, and extracts bauzone and baulinie. ❌ New functionality needed
|
||||
|
||||
7. **Identify Protection Zones**: Backend searches noise, flood, and groundwater collections, finds zones with largest overlap, and extracts zone designations (laermschutzzone, hochwasserschutzzone, grundwasserschutzzone). ❌ New functionality needed
|
||||
|
||||
8. **Link Source Documents**: Backend extracts assets from STAC items (GeoPackages, PDFs), creates Dokument objects with metadata, and links them to Parzelle for audit trail. ❌ New functionality needed
|
||||
|
||||
9. **AI Extraction (Optional)**: Backend filters PDF documents, calls AI service to extract building parameters (AZ, BZ, vollgeschossZahl, gebaeudehoeheMax, regulations), and updates Parzelle with extracted values. ⚠️ Partial - AI service exists, extraction method needs implementation
|
||||
|
||||
10. **Save to Database**: All entities (Projekt, Parzelle, Gemeinde, Kanton, Land, Dokument) are saved to PostgreSQL. Frontend receives enriched Parzelle with enrichment status. ❌ New functionality needed
|
||||
|
||||
11. **User Continues Planning**: Users can edit project status, add more parcels, upload documents, add notes, override AI values, and update parcel properties. ⚠️ Partial - Update endpoints exist but may need enhancement
|
||||
|
||||
---
|
||||
|
||||
## User Story 2: Browse Map and Explore Parcels
|
||||
|
||||
**User Goal**: Browse a map, click on parcels to fetch their data from STAC, and see whether each parcel is already connected to an existing project or available for new projects.
|
||||
|
||||
### Workflow Steps
|
||||
|
||||
1. **Open Map Browser**: User navigates to map browsing interface displaying Switzerland map. ❌ New interface needed
|
||||
|
||||
2. **Click on Map**: User clicks at any location. Frontend sends GET request with coordinates and optional mandate ID. ❌ New endpoint needed
|
||||
|
||||
3. **Query STAC for Parcel**: Backend queries STAC cadastral collection for parcels at coordinates. Returns parcel data if found. ❌ New functionality needed
|
||||
|
||||
4. **Check Project Associations**: Backend searches database for Parzelle records matching STAC parcel identifier, retrieves associated Projekte, filters by mandate if provided, and determines status: Connected (list projects) or Available (not connected). ❌ New functionality needed
|
||||
|
||||
5. **Fetch Basic Context (Optional)**: Backend performs lightweight enrichment: administrative boundaries (Gemeinde, Kanton), basic zoning, and protection zones. Uses caching for performance. ❌ New functionality needed
|
||||
|
||||
6. **Display Parcel Information**: Frontend shows parcel details, geometry on map, project association status with action buttons (View Project or Create New Project), protection zones, and STAC source indication. ❌ New interface needed
|
||||
|
||||
7. **Explore Multiple Parcels**: User can continue clicking to explore multiple parcels. Map shows multiple parcels with color-coded association status. ❌ New functionality needed
|
||||
|
||||
8. **Connect Parcel to Project (Optional)**: If parcel is available, user can create new project or add to existing project. System validates permissions, fetches full parcel data, creates Parzelle, triggers full enrichment (User Story 1 Steps 5-9), and saves to database. ❌ New functionality needed
|
||||
|
||||
### Key Differences from User Story 1
|
||||
|
||||
**User Story 2** focuses on **exploration and discovery**:
|
||||
- No project creation required upfront
|
||||
- Lightweight data fetching (can be cached)
|
||||
- Read-only exploration mode
|
||||
- Project association checking
|
||||
- Optional connection to projects
|
||||
|
||||
**User Story 1** focuses on **project creation and data persistence**:
|
||||
- Project must exist first
|
||||
- Full data enrichment and storage
|
||||
- All data saved to database
|
||||
- Complete administrative, zoning, and protection zone context
|
||||
- AI extraction of building rules
|
||||
|
||||
---
|
||||
|
||||
## API Routes and Endpoints
|
||||
|
||||
### Route Migration Overview
|
||||
|
||||
With STAC API integration, the route structure is refactored from generic table-based endpoints to domain-focused endpoints that better align with user workflows and business logic.
|
||||
|
||||
### Routes to Deprecate
|
||||
|
||||
**POST /api/realestate/query**: Direct SQL queries are a security risk. Use `/command` endpoint for natural language queries or specific GET endpoints for structured data retrieval. ⚠️ Deprecate
|
||||
|
||||
**POST /api/realestate/table/{table}**: Generic table creation doesn't reflect domain model. Replace with dedicated project and parcel endpoints. ⚠️ Deprecate
|
||||
|
||||
### Routes to Keep
|
||||
|
||||
**POST /api/realestate/command**: Keep for natural language commands. May need AI prompt updates for STAC workflows. ✅ Keep
|
||||
|
||||
**GET /api/realestate/tables**: Keep for frontend schema discovery. ✅ Keep
|
||||
|
||||
**GET /api/realestate/table/{table}**: Keep for admin/debugging. Consider adding domain-specific alternatives. ✅ Keep
|
||||
|
||||
### New Domain-Focused Routes
|
||||
|
||||
#### Project Management
|
||||
|
||||
- **POST /api/realestate/projects**: Create new project (replaces `POST /api/realestate/table/Projekt`)
|
||||
- **GET /api/realestate/projects**: List projects with filtering and pagination
|
||||
- **GET /api/realestate/projects/{projekt_id}**: Get single project with full details
|
||||
- **PATCH /api/realestate/projects/{projekt_id}**: Update project properties
|
||||
- **DELETE /api/realestate/projects/{projekt_id}**: Delete project with access control
|
||||
|
||||
#### Parcel Management (User Story 1)
|
||||
|
||||
- **POST /api/realestate/projects/{projekt_id}/locations**: Select location and create Parzelle with full enrichment
|
||||
- **GET /api/realestate/projects/{projekt_id}/parcels**: List all parcels for a project
|
||||
- **GET /api/realestate/projects/{projekt_id}/parcels/{parzelle_id}**: Get single parcel with full details
|
||||
- **PATCH /api/realestate/projects/{projekt_id}/parcels/{parzelle_id}**: Update parcel properties
|
||||
- **DELETE /api/realestate/projects/{projekt_id}/parcels/{parzelle_id}**: Remove parcel from project
|
||||
- **POST /api/realestate/projects/{projekt_id}/parcels/{parzelle_id}/refresh-context**: Re-enrich parcel with updated STAC data
|
||||
|
||||
#### Map Browsing (User Story 2)
|
||||
|
||||
- **GET /api/realestate/parcels/explore**: Browse map and fetch parcel data from STAC with project association check
|
||||
- **POST /api/realestate/projects/{projekt_id}/parcels/connect**: Connect explored parcel to project (triggers full enrichment)
|
||||
|
||||
### Migration Strategy
|
||||
|
||||
1. **Phase 1**: Add new domain-focused endpoints alongside existing routes
|
||||
2. **Phase 2**: Update frontend to use new endpoints
|
||||
3. **Phase 3**: Mark old endpoints as deprecated with warnings
|
||||
4. **Phase 4**: Remove deprecated endpoints after migration period
|
||||
|
||||
---
|
||||
|
||||
## Implementation Components
|
||||
|
||||
### STAC Connector
|
||||
|
||||
**Purpose**: Encapsulate all STAC API interactions following Gateway connector pattern.
|
||||
|
||||
**Key Methods**:
|
||||
- Search parcels by point (cadastral collection)
|
||||
- Search administrative boundaries (Gemeinde, Kanton)
|
||||
- Search zoning (land use planning)
|
||||
- Search protection zones (noise, flood, groundwater)
|
||||
- Get item assets (downloadable files)
|
||||
|
||||
**Features**: Retry logic with exponential backoff, coordinate system transformations, error handling, response transformation to internal data structures.
|
||||
|
||||
**Caching Strategy**: Cache administrative boundaries (rarely change), cache map browsing lookups (short TTL), don't cache project creation searches (user-specific), cache collection metadata.
|
||||
|
||||
### Feature Logic Extensions
|
||||
|
||||
**For User Story 1**:
|
||||
- Process location selection and create Parzelle
|
||||
- Enrich parcel with STAC data (orchestrates full enrichment)
|
||||
- Extract zoning by calculating intersection areas
|
||||
- Extract protection zones by overlap calculation
|
||||
- Geometry conversion helpers (STAC GeoJSON ↔ internal GeoPolylinie)
|
||||
|
||||
**For User Story 2**:
|
||||
- Fetch parcel data from STAC (lightweight)
|
||||
- Check parcel-project associations (database queries)
|
||||
- Get basic parcel context (quick administrative/zoning lookup)
|
||||
- Connect parcel to project (transition to User Story 1)
|
||||
|
||||
### AI Service Extension
|
||||
|
||||
**Purpose**: Extract building rules from zoning PDF documents.
|
||||
|
||||
**Process**: Download PDFs, extract text (OCR if needed), use LLM with structured schema to extract AZ, BZ, vollgeschossZahl, gebaeudehoeheMax, and regulation descriptions.
|
||||
|
||||
**Status**: ⚠️ Partial - AI service exists, but zoning rule extraction method needs implementation
|
||||
|
||||
---
|
||||
|
||||
## Data Flow Summary
|
||||
|
||||
### User Story 1: Location Selection Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Route
|
||||
participant FeatureLogic
|
||||
participant STACConnector
|
||||
participant STACAPI
|
||||
participant Database
|
||||
participant AIService
|
||||
|
||||
User->>Frontend: Click on map
|
||||
Frontend->>Route: POST /projects/{id}/locations<br/>(coordinates, project_id)
|
||||
Route->>Route: Validate CSRF token<br/>Validate coordinates<br/>Check project access
|
||||
|
||||
Route->>FeatureLogic: processLocationSelection()
|
||||
FeatureLogic->>STACConnector: search_parcels_by_point(x, y)
|
||||
STACConnector->>STACAPI: Query cadastral collection<br/>(point geometry)
|
||||
STACAPI-->>STACConnector: Parcel items (geometry, properties, assets)
|
||||
STACConnector-->>FeatureLogic: Parcel data
|
||||
|
||||
FeatureLogic->>FeatureLogic: Convert STAC geometry<br/>to GeoPolylinie
|
||||
FeatureLogic->>FeatureLogic: Create Parzelle object
|
||||
FeatureLogic->>Database: Attach Parzelle to Projekt
|
||||
|
||||
FeatureLogic->>FeatureLogic: Trigger enrichment
|
||||
|
||||
FeatureLogic->>STACConnector: search_administrative_boundaries()
|
||||
STACConnector->>STACAPI: Query Gemeinde/Kanton collections
|
||||
STACAPI-->>STACConnector: Boundary data
|
||||
STACConnector-->>FeatureLogic: Administrative boundaries
|
||||
FeatureLogic->>Database: Create/update Gemeinde, Kanton, Land
|
||||
|
||||
FeatureLogic->>STACConnector: search_zoning()
|
||||
STACConnector->>STACAPI: Query zoning collection
|
||||
STACAPI-->>STACConnector: Zoning polygons
|
||||
STACConnector-->>FeatureLogic: Zoning data
|
||||
FeatureLogic->>FeatureLogic: Calculate intersection areas<br/>Select dominant zone
|
||||
FeatureLogic->>FeatureLogic: Extract bauzone, baulinie
|
||||
|
||||
FeatureLogic->>STACConnector: search_protection_zones()
|
||||
STACConnector->>STACAPI: Query noise/flood/groundwater collections
|
||||
STACAPI-->>STACConnector: Protection zone data
|
||||
STACConnector-->>FeatureLogic: Protection zones
|
||||
FeatureLogic->>FeatureLogic: Extract zone designations
|
||||
|
||||
FeatureLogic->>FeatureLogic: Extract assets from STAC items
|
||||
FeatureLogic->>Database: Create Dokument objects
|
||||
|
||||
opt AI Extraction Enabled
|
||||
FeatureLogic->>FeatureLogic: Filter PDF documents
|
||||
FeatureLogic->>AIService: extractZoningRules(documents, bauzone)
|
||||
AIService->>AIService: Download PDFs<br/>Extract text (OCR if needed)
|
||||
AIService->>AIService: LLM analysis with structured schema
|
||||
AIService-->>FeatureLogic: Extracted values (AZ, BZ, etc.)
|
||||
FeatureLogic->>FeatureLogic: Update Parzelle with AI results
|
||||
FeatureLogic->>Database: Add Kontext entry
|
||||
end
|
||||
|
||||
FeatureLogic->>Database: Save all entities<br/>(Projekt, Parzelle, Gemeinde, etc.)
|
||||
FeatureLogic-->>Route: Enriched Parzelle + status
|
||||
Route-->>Frontend: Response with enriched Parzelle
|
||||
Frontend->>User: Display enriched parcel data
|
||||
```
|
||||
|
||||
### User Story 2: Map Browse Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Frontend
|
||||
participant Route
|
||||
participant FeatureLogic
|
||||
participant STACConnector
|
||||
participant STACAPI
|
||||
participant Database
|
||||
|
||||
User->>Frontend: Click on map (browse mode)
|
||||
Frontend->>Route: GET /parcels/explore<br/>(coordinates, mandate_id?)
|
||||
Route->>Route: Validate coordinates<br/>Validate authentication
|
||||
|
||||
Route->>FeatureLogic: fetchParcelData()
|
||||
FeatureLogic->>STACConnector: search_parcels_by_point(x, y)
|
||||
STACConnector->>STACAPI: Query cadastral collection<br/>(point geometry)
|
||||
STACAPI-->>STACConnector: Parcel items (geometry, properties)
|
||||
STACConnector-->>FeatureLogic: Parcel data
|
||||
|
||||
FeatureLogic->>Database: Check parcel-project associations<br/>(search Parzelle by STAC identifier)
|
||||
Database-->>FeatureLogic: Matching Parzelle records<br/>with Projekt IDs
|
||||
|
||||
alt Parcels Found in Database
|
||||
FeatureLogic->>Database: Get Projekt details
|
||||
Database-->>FeatureLogic: Projekt information
|
||||
FeatureLogic->>FeatureLogic: Status: Connected<br/>(list projects)
|
||||
else No Parcels Found
|
||||
FeatureLogic->>FeatureLogic: Status: Available
|
||||
end
|
||||
|
||||
opt Basic Context Requested
|
||||
FeatureLogic->>STACConnector: search_administrative_boundaries()<br/>search_zoning()<br/>search_protection_zones()
|
||||
STACConnector->>STACAPI: Query collections (cached if possible)
|
||||
STACAPI-->>STACConnector: Context data
|
||||
STACConnector-->>FeatureLogic: Basic context
|
||||
end
|
||||
|
||||
FeatureLogic-->>Route: Parcel data + association status + context
|
||||
Route-->>Frontend: Response with parcel info
|
||||
Frontend->>User: Display parcel details<br/>Show project association status<br/>Show action buttons
|
||||
|
||||
opt User Chooses to Connect
|
||||
User->>Frontend: Click "Create Project" or "Add to Project"
|
||||
Frontend->>Route: POST /projects/{id}/parcels/connect<br/>(parcel_id, project_id)
|
||||
Route->>FeatureLogic: connectParcelToProject()
|
||||
Note over FeatureLogic: Triggers User Story 1<br/>enrichment process
|
||||
FeatureLogic->>Database: Create Parzelle + enrichment
|
||||
FeatureLogic-->>Route: Created Parzelle
|
||||
Route-->>Frontend: Success response
|
||||
end
|
||||
```
|
||||
|
||||
### AI Extraction Flow (User Story 1)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant FeatureLogic
|
||||
participant Parzelle
|
||||
participant AIService
|
||||
participant PDFSource
|
||||
participant LLM
|
||||
participant Database
|
||||
|
||||
Note over FeatureLogic: After STAC enrichment completes
|
||||
|
||||
FeatureLogic->>Parzelle: Get dokumente collection
|
||||
Parzelle-->>FeatureLogic: List of Dokument objects
|
||||
|
||||
FeatureLogic->>FeatureLogic: Filter documents:<br/>- mimeType == "application/pdf"<br/>- dokumentTyp in [BZO, Bauverordnung]
|
||||
|
||||
FeatureLogic->>AIService: extractZoningRules(<br/>parzelleId,<br/>documents[],<br/>bauzone)
|
||||
|
||||
loop For each PDF document
|
||||
AIService->>PDFSource: Download PDF from dokumentReferenz
|
||||
PDFSource-->>AIService: PDF file
|
||||
|
||||
AIService->>AIService: Extract text content<br/>(OCR if scanned PDF)
|
||||
AIService->>AIService: Preprocess text
|
||||
end
|
||||
|
||||
AIService->>LLM: Analyze text with structured schema<br/>(prompt: extract building parameters)
|
||||
LLM->>LLM: Parse zoning regulations
|
||||
LLM-->>AIService: Structured extraction:<br/>- az (float)<br/>- bz (float)<br/>- vollgeschossZahl (int)<br/>- gebaeudehoeheMax (float)<br/>- regelnGrenzabstand (array)<br/>- regelnMehrlaengenzuschlag (array)<br/>- regelnMehrhoehenzuschlag (array)
|
||||
|
||||
AIService-->>FeatureLogic: Extraction results
|
||||
|
||||
FeatureLogic->>FeatureLogic: Parse and validate results
|
||||
|
||||
FeatureLogic->>Parzelle: Update fields:<br/>parzelle.az = result.az<br/>parzelle.bz = result.bz<br/>parzelle.vollgeschossZahl = result.vollgeschossZahl<br/>parzelle.gebaeudehoeheMax = result.gebaeudehoeheMax<br/>parzelle.regelnGrenzabstand = result.regelnGrenzabstand<br/>parzelle.regelnMehrlaengenzuschlag = result.regelnMehrlaengenzuschlag<br/>parzelle.regelnMehrhoehenzuschlag = result.regelnMehrhoehenzuschlag
|
||||
|
||||
FeatureLogic->>Database: Create Kontext entry:<br/>thema: "Automatisch extrahierte Baukennzahlen"<br/>inhalt: "AZ/BZ etc. wurden aus BZO [Gemeinde] Stand [Date] extrahiert."
|
||||
|
||||
FeatureLogic->>Database: Save updated Parzelle
|
||||
|
||||
Database-->>FeatureLogic: Confirmation
|
||||
|
||||
FeatureLogic-->>FeatureLogic: Return enrichment results<br/>including AI extraction status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling and Security
|
||||
|
||||
### Error Handling
|
||||
|
||||
**STAC API Errors**: Retry with exponential backoff (3 attempts), validate coordinates (Switzerland bounds), handle no results found (404 for Story 1, informative response for Story 2), continue with available collections if some fail.
|
||||
|
||||
**Database Errors**: Transaction management with rollback, validate relationships before saving, handle parcel already connected scenarios.
|
||||
|
||||
**AI Extraction Errors**: Skip failed documents, return partial results, don't fail entire enrichment process.
|
||||
|
||||
### Security
|
||||
|
||||
**CSRF Protection**: All POST/PATCH endpoints require CSRF tokens. GET endpoints validate authentication.
|
||||
|
||||
**Input Validation**: Validate coordinate ranges (Switzerland bounds), whitelist coordinate systems (EPSG:2056, EPSG:4326), validate UUID formats.
|
||||
|
||||
**Rate Limiting**: 60 requests/minute for location selection, 120 requests/minute for map browsing, 30 requests/minute for context refresh.
|
||||
|
||||
**Data Privacy**: STAC queries are read-only, parcel data stored per mandate with access control, mandate filtering for project associations.
|
||||
|
||||
**Access Control**: Users must have permission to create/modify projects, project association information filtered by mandate access, parcel connection requires project modification permissions.
|
||||
|
||||
---
|
||||
|
||||
## Testing and Configuration
|
||||
|
||||
### Testing Approach
|
||||
|
||||
**Unit Tests**: STAC connector methods with mock responses, feature logic with mocked connector, project association checking with various database states.
|
||||
|
||||
**Integration Tests**: End-to-end flows for both user stories, enrichment pipeline, project association scenarios (not connected, one project, multiple projects, different mandates).
|
||||
|
||||
**Mock STAC Responses**: Create realistic mock responses for development and testing without external API dependency.
|
||||
|
||||
### Configuration Requirements
|
||||
|
||||
**Environment Variables**: STAC base URL, timeout settings, retry configuration, caching settings (different for User Story 1 vs User Story 2), map browse cache TTL.
|
||||
|
||||
**Configuration Files**: API endpoint URLs, timeout and retry parameters, caching behavior, collection identifiers.
|
||||
|
||||
### Dependencies
|
||||
|
||||
**Python Packages**: STAC API client, geospatial operations library, GeoJSON support library.
|
||||
|
||||
**System Requirements**: Python 3.10+, internet connection for STAC API, PostgreSQL database, optional PostGIS extension.
|
||||
|
||||
**External Services**: Swiss Topo STAC API (public, no authentication), AI service infrastructure (existing).
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This integration guide provides a comprehensive conceptual blueprint for integrating the Swiss Topo STAC API into the Real Estate feature. The implementation enables two distinct user workflows:
|
||||
|
||||
**User Story 1: Create Project and Connect Parcels**
|
||||
- Create new projects and select parcels on a map
|
||||
- Automatic enrichment with administrative, zoning, and protection zone data
|
||||
- Full data persistence in database
|
||||
- AI-powered extraction of building rules from documents
|
||||
- Complete project management workflow
|
||||
|
||||
**User Story 2: Browse Map and Explore Parcels**
|
||||
- Explore parcels on a map without project creation
|
||||
- Lightweight data fetching from STAC
|
||||
- Check if parcels are connected to existing projects
|
||||
- Optional connection to projects with full enrichment
|
||||
|
||||
The architecture follows the existing Gateway patterns:
|
||||
- **Connectors** for external API communication
|
||||
- **Interfaces** for normalized data access
|
||||
- **Features** for business logic
|
||||
- **Routes** for API endpoints
|
||||
|
||||
Both user stories share the STAC connector and enrichment logic, but serve different purposes: User Story 1 focuses on project creation and data persistence, while User Story 2 focuses on exploration and discovery before committing to project creation.
|
||||
|
||||
The route structure is refactored from generic table-based endpoints to domain-focused endpoints that better align with user workflows, with a phased migration strategy to ensure backward compatibility.
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 2.1
|
||||
**Last Updated**: 2025-01-28
|
||||
**Author**: Gateway Development Team
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
# Feature Integration Guide: realEstate
|
||||
|
||||
Diese Dokumentation erklärt Schritt für Schritt, wie Sie ein neues Feature "realEstate" in das Gateway-Projekt integrieren. Das Feature ermöglicht es, über ein Chat-Interface Datenbankabfragen auf Real Estate-Daten (Architektur-Planungs-App) durchzuführen.
|
||||
|
||||
**Referenz:** Das zugrundeliegende Datenmodell ist in `../PEK_datamodel_desc.md` beschrieben (PEK ist ein Beispiel für eine Real Estate-Firma, das Modell ist aber allgemein verwendbar).
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Überblick und Projektstruktur](01-overview.md)
|
||||
2. [Schritt 1: Datenmodell erstellen](02-datamodels.md)
|
||||
3. [Schritt 2: Interface erstellen](03-interfaces.md)
|
||||
4. [Schritt 3: Feature-Logik implementieren](04-feature-logic.md)
|
||||
5. [Schritt 4: Routen erstellen](05-routes.md)
|
||||
6. [Schritt 5: Router registrieren](06-router-registration.md)
|
||||
7. [Schritt 6: Environment-Konfiguration](07-environment.md)
|
||||
8. [Schritt 7: Feature Lifecycle (optional)](08-lifecycle.md)
|
||||
9. [Datenbank-Schema und Tabellenerstellung](09-database-schema.md)
|
||||
10. [Sicherheitshinweise](10-security.md)
|
||||
11. [Testing](11-testing.md)
|
||||
12. [Troubleshooting](12-troubleshooting.md)
|
||||
13. [Zusammenfassung](13-summary.md)
|
||||
14. [Swiss Topo STAC API Integration](14-stac-api-integration.md)
|
||||
|
||||
---
|
||||
|
||||
## Schnellstart
|
||||
|
||||
Für eine schnelle Übersicht über alle zu erstellenden Dateien, siehe [Zusammenfassung](13-summary.md).
|
||||
|
||||
## Architektur-Überblick
|
||||
|
||||
Die Architektur folgt dem Muster bestehender Features wie `chatPlayground`:
|
||||
- **Routes** (`modules/routes/`) - API-Endpunkte
|
||||
- **Features** (`modules/features/`) - Geschäftslogik
|
||||
- **Interfaces** (`modules/interfaces/`) - Datenbankzugriff
|
||||
- **DataModels** (`modules/datamodels/`) - Pydantic-Modelle
|
||||
|
||||
---
|
||||
|
||||
**Nächster Schritt:** [01-overview.md](01-overview.md)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "wie viele projekte in zürich gibt es?"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"kontextGemeinde": "Zürich"}, "confidence": 0.9}
|
||||
Note: Location queries should query Parzelle, not Projekt directly
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "READ",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"kontextGemeinde": "Zürich"
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
**Reasoning:**
|
||||
- The user is asking "how many projects in Zürich are there?" which is a query/read operation
|
||||
- Since location information (city/municipality) is stored in `Parzelle` via the `kontextGemeinde` field, not directly in `Projekt`, we need to query `Parzelle` entities
|
||||
- The parameter `kontextGemeinde` is set to "Zürich" to filter parcels by municipality
|
||||
- To get projects in Zürich, the system would need to find all Parzellen with kontextGemeinde="Zürich" and then retrieve the associated Projekte
|
||||
- Confidence is 0.9 because the intent is clear, though the user asks about "projects" while we're querying "Parzelle" (which is the correct approach given the data model)
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "wie viele Projekte in Zürich gibt es?"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "READ",
|
||||
"entity": "Projekt",
|
||||
"parameters": {
|
||||
"location_filter": "Zürich"
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
- **Intent**: READ - The user wants to query/count existing projects ("wie viele ... gibt es?" = "how many ... are there?")
|
||||
- **Entity**: Projekt - The user is asking about "Projekte" (projects)
|
||||
- **Parameters**:
|
||||
- `location_filter: "Zürich"` - The user wants to filter projects by location (Zürich). Since Projekt doesn't have direct location fields, this will need to be resolved through the associated Parzellen's kontextGemeinde relationship
|
||||
- **Confidence**: 0.9 - High confidence as the intent and entity are clear, though the query involves a relationship traversal (Projekt → Parzelle → Gemeinde)
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "Erstelle ein neues Projekt in Zürich"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "CREATE",
|
||||
"entity": "Projekt",
|
||||
"parameters": {
|
||||
"location_filter": "Zürich"
|
||||
},
|
||||
"confidence": 0.85
|
||||
}
|
||||
```
|
||||
|
||||
**Explanation:**
|
||||
- **Intent**: CREATE - The user wants to create a new project ("Erstelle ein neues Projekt")
|
||||
- **Entity**: Projekt - The entity type is clearly specified
|
||||
- **Parameters**:
|
||||
- `location_filter`: "Zürich" - The user mentions "in Zürich" as the location. Since Projekt doesn't have direct location fields (location is stored in associated Parzellen), I'm using `location_filter` to indicate the intended location context. This will need to be handled by creating associated Parzellen with the appropriate `kontextGemeinde` value.
|
||||
- **Confidence**: 0.85 - High confidence in intent and entity, slightly lower because the location reference for a new project is indirect (projects get location through their parcels) and no project name/label was provided.
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "wie viele Projekte in zürich gibt es jetzt?"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "READ",
|
||||
"entity": "Projekt",
|
||||
"parameters": {
|
||||
"location_filter": "zürich"
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
- **Intent**: READ - The user wants to query/count existing projects ("wie viele Projekte ... gibt es")
|
||||
- **Entity**: Projekt - The query is about projects ("Projekte")
|
||||
- **Parameters**:
|
||||
- `location_filter`: "zürich" - The user is asking about projects in Zürich. Since Projekt doesn't have direct location fields but location is stored in associated Parzellen (via kontextGemeinde), we use the `location_filter` parameter to indicate this geographic filter needs to be resolved through the Parzelle relationship.
|
||||
- **Confidence**: 0.9 - High confidence. The intent is clear (counting/querying projects), the location is explicitly mentioned, and the temporal indicator "jetzt" (now) suggests current/active projects.
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "wie viele parzellen in zürich gibt es?"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "READ",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"kontextGemeinde": "Zürich"
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
**Reasoning:**
|
||||
- The user is asking "wie viele parzellen in zürich gibt es?" (how many plots are there in Zürich?)
|
||||
- **Intent**: READ - The user wants to query/read information about existing entities
|
||||
- **Entity**: Parzelle - The user is specifically asking about "parzellen" (plots/parcels)
|
||||
- **Parameters**: The location "Zürich" refers to a municipality (Gemeinde). Since Parzelle has the field `kontextGemeinde` which links to the municipality, this is the appropriate field to use for filtering by location
|
||||
- **Confidence**: 0.9 - High confidence as the intent and entity are clearly stated, though the exact count operation is implicit
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "wie viele parzellen in zürich gibt es?"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "READ",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"kontextGemeinde": "Zürich"
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
**Reasoning:**
|
||||
- The user is asking "wie viele parzellen in zürich gibt es?" (how many plots are there in Zürich?)
|
||||
- **Intent**: READ - The user wants to query/read information about existing entities
|
||||
- **Entity**: Parzelle - The user is specifically asking about "parzellen" (plots/parcels)
|
||||
- **Parameters**: The location "Zürich" refers to a municipality (Gemeinde). Since Parzelle has the field `kontextGemeinde` which links to the municipality, this is the appropriate field to use for filtering by location
|
||||
- **Confidence**: 0.9 - High confidence as the intent and entity are clearly stated, though the exact count operation is implicit
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "add a new parzelle with these information into the database: Parzellen-Informationen
|
||||
Ausgewählte Parzelle
|
||||
ID:AA1704
|
||||
Nummer:AA1704
|
||||
Name:AA1704
|
||||
EGRID:CH887199917793
|
||||
IdentND:ZH0200000261
|
||||
Kanton:ZH
|
||||
Gemeinde:Zürich
|
||||
Gemeinde-Code:261
|
||||
Fläche:6514.99 m²
|
||||
Zentrum (LV95):2682951.44, 1247622.91
|
||||
Geoportal:
|
||||
Link öffnen
|
||||
Kartenansicht
|
||||
Zentrum:2682951.44, 1247622.91
|
||||
Bounds Min:2682914.00, 1247582.00
|
||||
Bounds Max:2683024.30, 1247667.50"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"id": "AA1704",
|
||||
"label": "AA1704",
|
||||
"kontextGemeinde": "Zürich"
|
||||
},
|
||||
"confidence": 0.75
|
||||
}
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
|
||||
The user wants to add a new "Parzelle" (plot/parcel) to the database. The command provides extensive information about the parcel, but only a subset can be mapped to the available fields in the **Parzelle** entity definition:
|
||||
|
||||
**Mapped fields:**
|
||||
- `id`: "AA1704" (from ID/Nummer)
|
||||
- `label`: "AA1704" (from Name)
|
||||
- `kontextGemeinde`: "Zürich" (from Gemeinde - will need to be resolved to municipality ID)
|
||||
|
||||
**Unmapped information** (not available in the Parzelle entity schema):
|
||||
- EGRID: CH887199917793
|
||||
- IdentND: ZH0200000261
|
||||
- Kanton: ZH
|
||||
- Gemeinde-Code: 261
|
||||
- Fläche: 6514.99 m²
|
||||
- Zentrum coordinates (LV95): 2682951.44, 1247622.91
|
||||
- Bounds coordinates
|
||||
|
||||
**Confidence reasoning:**
|
||||
The confidence is set to 0.75 because:
|
||||
- The intent (CREATE) is clear
|
||||
- The entity (Parzelle) is explicitly mentioned
|
||||
- However, most of the provided information cannot be stored in the current Parzelle schema
|
||||
- The user may expect all this data to be stored, but the system can only capture id, label, and kontextGemeinde from the provided information
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "erstelle einen neuen parzelleneintrag mit diesen informationen: ID:AA4198
|
||||
Nummer:AA4198
|
||||
Name:AA4198
|
||||
EGRID:CH879177719964
|
||||
IdentND:ZH0200000261
|
||||
Adresse:Steinmühleplatz 3, 8001 Zürich
|
||||
Kanton:ZH
|
||||
Gemeinde:Zürich
|
||||
Gemeinde-Code:261
|
||||
Fläche:1972.83 m²
|
||||
Zentrum (LV95):2682910.35, 1247566.80"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Erstelle eine Parzelle mit Label 123, PLZ 8000, Gemeinde Zürich, Bauzone W3"
|
||||
Output: {"intent": "CREATE", "entity": "Parzelle", "parameters": {"label": "123", "plz": "8000", "kontextGemeinde": "Zürich", "bauzone": "W3"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Parzellen-Informationen: ID:AA1704, Nummer:AA1704, EGRID:CH887199917793, Kanton:ZH, Gemeinde:Zürich, Gemeinde-Code:261, Fläche:6514.99 m², Zentrum:2682951.44,1247622.91"
|
||||
Output: {
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"label": "AA1704",
|
||||
"parzellenAliasTags": ["AA1704"],
|
||||
"kontextGemeinde": "Zürich",
|
||||
"kontextInformationen": [
|
||||
{"kategorie": "Swiss Topo", "schluessel": "EGRID", "wert": "CH887199917793"},
|
||||
{"kategorie": "Kanton", "schluessel": "Abkürzung", "wert": "ZH"},
|
||||
{"kategorie": "Gemeinde", "schluessel": "BFS-Nummer", "wert": "261"},
|
||||
{"kategorie": "Parzelle", "schluessel": "Fläche", "wert": "6514.99", "einheit": "m²"},
|
||||
{"kategorie": "Parzelle", "schluessel": "Zentrum_X", "wert": "2682951.44", "crs": "EPSG:2056"},
|
||||
{"kategorie": "Parzelle", "schluessel": "Zentrum_Y", "wert": "1247622.91", "crs": "EPSG:2056"}
|
||||
]
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
Note: Extract structured data from detailed input. Use kontextInformationen for metadata like EGRID, coordinates, area, etc.
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
||||
IMPORTANT EXTRACTION RULES:
|
||||
1. For CREATE operations, extract ALL mentioned data fields from the user input
|
||||
2. Use kontextInformationen array for metadata that doesn't have dedicated fields (EGRID, BFS numbers, area, coordinates, etc.)
|
||||
3. Match field names EXACTLY to the entity definition above
|
||||
4. Convert data types correctly (strings for text, numbers for numeric values)
|
||||
5. Extract coordinates, areas, and other numeric values from text
|
||||
6. When multiple values are mentioned for the same concept (ID, Nummer, Name), use the most relevant one for 'label' and put alternatives in parzellenAliasTags
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"label": "AA4198",
|
||||
"strasseNr": "Steinmühleplatz 3",
|
||||
"plz": "8001",
|
||||
"kontextGemeinde": "Zürich",
|
||||
"kontextInformationen": [
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "ID",
|
||||
"wert": "AA4198"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Nummer",
|
||||
"wert": "AA4198"
|
||||
},
|
||||
{
|
||||
"kategorie": "Swiss Topo",
|
||||
"schluessel": "EGRID",
|
||||
"wert": "CH879177719964"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "IdentND",
|
||||
"wert": "ZH0200000261"
|
||||
},
|
||||
{
|
||||
"kategorie": "Kanton",
|
||||
"schluessel": "Abkürzung",
|
||||
"wert": "ZH"
|
||||
},
|
||||
{
|
||||
"kategorie": "Gemeinde",
|
||||
"schluessel": "BFS-Nummer",
|
||||
"wert": "261"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Fläche",
|
||||
"wert": "1972.83",
|
||||
"einheit": "m²"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Zentrum_X",
|
||||
"wert": "2682910.35",
|
||||
"crs": "EPSG:2056"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Zentrum_Y",
|
||||
"wert": "1247566.80",
|
||||
"crs": "EPSG:2056"
|
||||
}
|
||||
]
|
||||
},
|
||||
"confidence": 0.95
|
||||
}
|
||||
```
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "erstelle einen neuen parzelleneintrag mit diesen informationen: ID:AA4198
|
||||
Nummer:AA4198
|
||||
Name:AA4198
|
||||
EGRID:CH879177719964
|
||||
IdentND:ZH0200000261
|
||||
Adresse:Steinmühleplatz 3, 8001 Zürich
|
||||
Kanton:ZH
|
||||
Gemeinde:Zürich
|
||||
Gemeinde-Code:261
|
||||
Fläche:1972.83 m²
|
||||
Zentrum (LV95):2682910.35, 1247566.80"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Erstelle eine Parzelle mit Label 123, PLZ 8000, Gemeinde Zürich, Bauzone W3"
|
||||
Output: {"intent": "CREATE", "entity": "Parzelle", "parameters": {"label": "123", "plz": "8000", "kontextGemeinde": "Zürich", "bauzone": "W3"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Parzellen-Informationen: ID:AA1704, Nummer:AA1704, EGRID:CH887199917793, Kanton:ZH, Gemeinde:Zürich, Gemeinde-Code:261, Fläche:6514.99 m², Zentrum:2682951.44,1247622.91"
|
||||
Output: {
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"label": "AA1704",
|
||||
"parzellenAliasTags": ["AA1704"],
|
||||
"kontextGemeinde": "Zürich",
|
||||
"kontextInformationen": [
|
||||
{"kategorie": "Swiss Topo", "schluessel": "EGRID", "wert": "CH887199917793"},
|
||||
{"kategorie": "Kanton", "schluessel": "Abkürzung", "wert": "ZH"},
|
||||
{"kategorie": "Gemeinde", "schluessel": "BFS-Nummer", "wert": "261"},
|
||||
{"kategorie": "Parzelle", "schluessel": "Fläche", "wert": "6514.99", "einheit": "m²"},
|
||||
{"kategorie": "Parzelle", "schluessel": "Zentrum_X", "wert": "2682951.44", "crs": "EPSG:2056"},
|
||||
{"kategorie": "Parzelle", "schluessel": "Zentrum_Y", "wert": "1247622.91", "crs": "EPSG:2056"}
|
||||
]
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
Note: Extract structured data from detailed input. Use kontextInformationen for metadata like EGRID, coordinates, area, etc.
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
||||
IMPORTANT EXTRACTION RULES:
|
||||
1. For CREATE operations, extract ALL mentioned data fields from the user input
|
||||
2. Use kontextInformationen array for metadata that doesn't have dedicated fields (EGRID, BFS numbers, area, coordinates, etc.)
|
||||
3. Match field names EXACTLY to the entity definition above
|
||||
4. Convert data types correctly (strings for text, numbers for numeric values)
|
||||
5. Extract coordinates, areas, and other numeric values from text
|
||||
6. When multiple values are mentioned for the same concept (ID, Nummer, Name), use the most relevant one for 'label' and put alternatives in parzellenAliasTags
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"label": "AA4198",
|
||||
"strasseNr": "Steinmühleplatz 3",
|
||||
"plz": "8001",
|
||||
"kontextGemeinde": "Zürich",
|
||||
"kontextInformationen": [
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "ID",
|
||||
"wert": "AA4198"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Nummer",
|
||||
"wert": "AA4198"
|
||||
},
|
||||
{
|
||||
"kategorie": "Swiss Topo",
|
||||
"schluessel": "EGRID",
|
||||
"wert": "CH879177719964"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "IdentND",
|
||||
"wert": "ZH0200000261"
|
||||
},
|
||||
{
|
||||
"kategorie": "Kanton",
|
||||
"schluessel": "Abkürzung",
|
||||
"wert": "ZH"
|
||||
},
|
||||
{
|
||||
"kategorie": "Gemeinde",
|
||||
"schluessel": "BFS-Nummer",
|
||||
"wert": "261"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Fläche",
|
||||
"wert": "1972.83",
|
||||
"einheit": "m²"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Zentrum_X",
|
||||
"wert": "2682910.35",
|
||||
"crs": "EPSG:2056"
|
||||
},
|
||||
{
|
||||
"kategorie": "Parzelle",
|
||||
"schluessel": "Zentrum_Y",
|
||||
"wert": "1247566.80",
|
||||
"crs": "EPSG:2056"
|
||||
}
|
||||
]
|
||||
},
|
||||
"confidence": 0.95
|
||||
}
|
||||
```
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "erstelle einen neuen parzelleneintrag mit diesen informationen: ID:AA4198
|
||||
Nummer:AA4198
|
||||
Name:AA4198
|
||||
EGRID:CH879177719964
|
||||
IdentND:ZH0200000261
|
||||
Adresse:Steinmühleplatz 3, 8001 Zürich
|
||||
Kanton:ZH
|
||||
Gemeinde:Zürich
|
||||
Gemeinde-Code:261
|
||||
Fläche:1972.83 m²
|
||||
Zentrum (LV95):2682910.35, 1247566.80"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
- kontextInformationen: List[Kontext] (metadata - each item has 'thema' and 'inhalt' fields only)
|
||||
|
||||
**Kontext** (Context information for metadata):
|
||||
- thema: string (topic/subject, e.g. "EGRID", "Fläche", "Zentrum")
|
||||
- inhalt: string (content as text, e.g. "CH887199917793", "6514.99 m²", "X: 123, Y: 456")
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Erstelle eine Parzelle mit Label 123, PLZ 8000, Gemeinde Zürich, Bauzone W3"
|
||||
Output: {"intent": "CREATE", "entity": "Parzelle", "parameters": {"label": "123", "plz": "8000", "kontextGemeinde": "Zürich", "bauzone": "W3"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Parzellen-Informationen: ID:AA1704, Nummer:AA1704, EGRID:CH887199917793, Kanton:ZH, Gemeinde:Zürich, Gemeinde-Code:261, Fläche:6514.99 m², Zentrum:2682951.44,1247622.91"
|
||||
Output: {
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"label": "AA1704",
|
||||
"parzellenAliasTags": ["AA1704"],
|
||||
"kontextGemeinde": "Zürich",
|
||||
"kontextInformationen": [
|
||||
{"thema": "EGRID", "inhalt": "CH887199917793"},
|
||||
{"thema": "Kanton", "inhalt": "ZH"},
|
||||
{"thema": "BFS-Nummer", "inhalt": "261"},
|
||||
{"thema": "Fläche", "inhalt": "6514.99 m²"},
|
||||
{"thema": "Zentrum (LV95)", "inhalt": "X: 2682951.44 m, Y: 1247622.91 m (EPSG:2056)"}
|
||||
]
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
Note: Extract structured data from detailed input. Use kontextInformationen for metadata. Each item has 'thema' (topic) and 'inhalt' (content as text).
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich" or "Wie viele Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {"location_filter": "Zürich"}, "confidence": 0.9}
|
||||
Note: For project location queries, use Projekt entity with location_filter parameter
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
||||
IMPORTANT EXTRACTION RULES:
|
||||
1. For CREATE operations, extract ALL mentioned data fields from the user input
|
||||
2. Use kontextInformationen array for metadata that doesn't have dedicated fields (EGRID, BFS numbers, area, coordinates, etc.)
|
||||
3. Each kontextInformationen item MUST have exactly two fields: 'thema' (topic/subject) and 'inhalt' (content as text string)
|
||||
4. Format kontextInformationen values as readable text strings, including units (e.g., "6514.99 m²", "X: 123, Y: 456")
|
||||
5. Match field names EXACTLY to the entity definition above
|
||||
6. Convert data types correctly (strings for text, numbers for numeric values)
|
||||
7. Extract coordinates, areas, and other numeric values from text
|
||||
8. When multiple values are mentioned for the same concept (ID, Nummer, Name), use the most relevant one for 'label' and put alternatives in parzellenAliasTags
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"label": "AA4198",
|
||||
"strasseNr": "Steinmühleplatz 3",
|
||||
"plz": "8001",
|
||||
"kontextGemeinde": "Zürich",
|
||||
"kontextInformationen": [
|
||||
{
|
||||
"thema": "ID",
|
||||
"inhalt": "AA4198"
|
||||
},
|
||||
{
|
||||
"thema": "Nummer",
|
||||
"inhalt": "AA4198"
|
||||
},
|
||||
{
|
||||
"thema": "EGRID",
|
||||
"inhalt": "CH879177719964"
|
||||
},
|
||||
{
|
||||
"thema": "IdentND",
|
||||
"inhalt": "ZH0200000261"
|
||||
},
|
||||
{
|
||||
"thema": "Kanton",
|
||||
"inhalt": "ZH"
|
||||
},
|
||||
{
|
||||
"thema": "BFS-Nummer",
|
||||
"inhalt": "261"
|
||||
},
|
||||
{
|
||||
"thema": "Fläche",
|
||||
"inhalt": "1972.83 m²"
|
||||
},
|
||||
{
|
||||
"thema": "Zentrum (LV95)",
|
||||
"inhalt": "X: 2682910.35 m, Y: 1247566.80 m (EPSG:2056)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"confidence": 0.95
|
||||
}
|
||||
```
|
||||
Loading…
Reference in a new issue