extended PEK routes and included swisstopo connector

This commit is contained in:
Ida Dittrich 2025-12-01 17:03:39 +01:00
parent 7fd29a4ee5
commit 28774f419c
180 changed files with 10905 additions and 8458 deletions

View file

@ -34,4 +34,11 @@ Web_Crawl_RETRY_DELAY = 2
# Web Research configuration
Web_Research_MAX_DEPTH = 2
Web_Research_MAX_LINKS_PER_DOMAIN = 4
Web_Research_CRAWL_TIMEOUT_MINUTES = 10
Web_Research_CRAWL_TIMEOUT_MINUTES = 10
# STAC API Connector configuration (Swiss Topo)
Connector_StacSwisstopo_BASE_URL = https://data.geo.admin.ch/api/stac/v1
Connector_StacSwisstopo_TIMEOUT = 30
Connector_StacSwisstopo_MAX_RETRIES = 3
Connector_StacSwisstopo_RETRY_DELAY = 1.0
Connector_StacSwisstopo_ENABLE_CACHE = True

View file

@ -0,0 +1,365 @@
# 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.

View file

@ -506,3 +506,4 @@ async def get_sensitive_data(
- [Architecture Overview](./architecture-overview.md) - Overall system architecture

View file

@ -0,0 +1,786 @@
# 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).

View file

@ -0,0 +1,542 @@
# 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
```

View file

@ -0,0 +1,293 @@
# 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
```

View file

@ -0,0 +1,491 @@
# 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).

View file

@ -0,0 +1,689 @@
# 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.

View file

@ -0,0 +1,335 @@
# 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
```

View file

@ -0,0 +1,574 @@
# 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).

View file

@ -0,0 +1,349 @@
# 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
```

View file

@ -0,0 +1,549 @@
# 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).

View file

@ -0,0 +1,516 @@
# 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
```

View file

@ -0,0 +1,843 @@
# 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).

View file

@ -0,0 +1,474 @@
# 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

View file

@ -0,0 +1,768 @@
# 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

View file

@ -0,0 +1,555 @@
# 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).

File diff suppressed because it is too large Load diff

View file

@ -50,3 +50,4 @@ def _initializeDatabase(self):

View file

@ -0,0 +1,491 @@
# 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

View file

@ -19,6 +19,7 @@ Diese Dokumentation erklärt Schritt für Schritt, wie Sie ein neues Feature "re
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)
---
@ -43,3 +44,4 @@ Die Architektur folgt dem Muster bestehender Features wie `chatPlayground`:

View file

@ -1,19 +0,0 @@
{
"id": "msg_d93206d0-bce6-48bb-aa24-df4ee6b52335",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "test",
"role": "user",
"status": "first",
"sequenceNr": 1,
"publishedAt": 1764243585.056298,
"roundNumber": 1,
"taskNumber": 0,
"actionNumber": 0,
"documentsLabel": "round1_usercontext",
"actionId": null,
"actionMethod": null,
"actionName": null,
"success": null,
"documents": []
}

View file

@ -1,19 +0,0 @@
{
"id": "msg_e290a3f2-1d32-4eaf-96c1-522c6e3c50cf",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "📋 **Task Plan**\n\nWe have created a task plan to accomplish your main business objective efficiently.\n\n💬 This task will accomplish the entire business objective you have requested.\n\n",
"role": "assistant",
"status": "step",
"sequenceNr": 2,
"publishedAt": 1764243597.686788,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 0,
"documentsLabel": "task_plan",
"actionId": null,
"actionMethod": null,
"actionName": null,
"success": null,
"documents": []
}

View file

@ -1,6 +0,0 @@
📋 **Task Plan**
We have created a task plan to accomplish your main business objective efficiently.
💬 This task will accomplish the entire business objective you have requested.

View file

@ -1,19 +0,0 @@
{
"id": "msg_2a59133b-7b54-43e5-a42a-a26574dc6431",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "🚀 **Task 1/1**\n\n💬 This task will accomplish the entire business objective you have requested.",
"role": "assistant",
"status": "step",
"sequenceNr": 3,
"publishedAt": 1764243598.8484213,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 0,
"documentsLabel": "task_1_start",
"actionId": null,
"actionMethod": null,
"actionName": null,
"success": null,
"documents": []
}

View file

@ -1,3 +0,0 @@
🚀 **Task 1/1**
💬 This task will accomplish the entire business objective you have requested.

View file

@ -1,19 +0,0 @@
{
"id": "msg_b5e92d78-7260-46b2-a95a-b64b8d329e50",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "**Action 1 (ai.generateDocument)**\n\n✅ This task will accomplish the entire business objective you have requested.\n\n",
"role": "assistant",
"status": "step",
"sequenceNr": 4,
"publishedAt": 1764243623.799448,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 1,
"documentsLabel": "round1_task1_action1_results",
"actionId": "action_3679cfd2-c66d-4ed5-a125-7c8ea9d93b6c",
"actionMethod": "ai",
"actionName": "generateDocument",
"success": null,
"documents": []
}

View file

@ -1,4 +0,0 @@
**Action 1 (ai.generateDocument)**
✅ This task will accomplish the entire business objective you have requested.

View file

@ -1,12 +0,0 @@
{
"id": "08c7d2e7-e3ac-44c6-b305-2696f7de627a",
"messageId": "msg_b5e92d78-7260-46b2-a95a-b64b8d329e50",
"fileId": "16a9c803-a59b-43d3-9fc9-7ae9d6242e17",
"fileName": "test_document_memo.docx",
"fileSize": 36938,
"mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 1,
"actionId": "action_3679cfd2-c66d-4ed5-a125-7c8ea9d93b6c"
}

View file

@ -1,12 +0,0 @@
{
"id": "da1fa51f-68d2-41f1-bc25-aaff26beaab7",
"messageId": "msg_b5e92d78-7260-46b2-a95a-b64b8d329e50",
"fileId": "c6cb75d8-5a4f-4774-aa32-4c769507d8d4",
"fileName": "structured_content.json",
"fileSize": 2612,
"mimeType": "application/json",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 1,
"actionId": "action_3679cfd2-c66d-4ed5-a125-7c8ea9d93b6c"
}

View file

@ -1,106 +0,0 @@
{
"metadata": {
"split_strategy": "single_document",
"source_documents": [],
"extraction_method": "ai_generation",
"title": "Test Document"
},
"documents": [
{
"id": "doc_1",
"title": "Test Document",
"filename": "test_document_memo.docx",
"sections": [
{
"id": "section_heading_1",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Memo"
}
],
"order": 0
},
{
"id": "section_heading_2",
"content_type": "heading",
"elements": [
{
"level": 2,
"text": "To: All Employees"
}
],
"order": 1
},
{
"id": "section_heading_3",
"content_type": "heading",
"elements": [
{
"level": 2,
"text": "From: Management"
}
],
"order": 2
},
{
"id": "section_heading_4",
"content_type": "heading",
"elements": [
{
"level": 2,
"text": "Date: October 10, 2023"
}
],
"order": 3
},
{
"id": "section_heading_5",
"content_type": "heading",
"elements": [
{
"level": 2,
"text": "Subject: Placeholder Content"
}
],
"order": 4
},
{
"id": "section_paragraph_1",
"content_type": "paragraph",
"elements": [
{
"text": "This memo serves as a placeholder for future content. Please review the structure and format to ensure it meets your needs."
}
],
"order": 5
},
{
"id": "section_bullet_list_1",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Introduction to the topic",
"Detailed analysis",
"Conclusion and recommendations"
]
}
],
"order": 6
},
{
"id": "section_paragraph_2",
"content_type": "paragraph",
"elements": [
{
"text": "For further information, please contact the management team. We appreciate your attention to this matter and look forward to your feedback."
}
],
"order": 7
}
]
}
]
}

View file

@ -1,19 +0,0 @@
{
"id": "msg_fd67f067-be0d-45fe-9a53-40507b83be9b",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "**Action 2 (ai.analyzeDocuments)**\n\n✅ This task will accomplish the entire business objective you have requested.\n\n",
"role": "assistant",
"status": "step",
"sequenceNr": 5,
"publishedAt": 1764243670.5022125,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 2,
"documentsLabel": "round1_task1_action2_results",
"actionId": "action_65fdae54-0864-45f7-921e-4872ec5849d6",
"actionMethod": "ai",
"actionName": "analyzeDocuments",
"success": null,
"documents": []
}

View file

@ -1,4 +0,0 @@
**Action 2 (ai.analyzeDocuments)**
✅ This task will accomplish the entire business objective you have requested.

View file

@ -1,24 +0,0 @@
Memo Analysis Report
====================
Overview
========
The document is a memo intended for all employees from the management team, dated October 10, 2023. It serves as a placeholder for future content, providing a structure and format for communication within the organization.
Key Insights
============
Trends and Patterns
===================
The document highlights the importance of having a well-defined structure in official communications, suggesting that the organization values clarity and consistency. The use of placeholders indicates a proactive approach to document preparation, allowing for flexibility and adaptability in content creation.
Important Findings
==================
Actionable Insights
===================
Generated: 2025-11-27 11:41:09 UTC

View file

@ -1,12 +0,0 @@
{
"id": "937cbc15-3df6-45b7-8d24-80964746a4be",
"messageId": "msg_fd67f067-be0d-45fe-9a53-40507b83be9b",
"fileId": "0ff8956e-2a0c-4251-bb85-06713416e004",
"fileName": "memo_analysis_report.txt",
"fileSize": 780,
"mimeType": "text/plain",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 2,
"actionId": "action_65fdae54-0864-45f7-921e-4872ec5849d6"
}

View file

@ -1,12 +0,0 @@
{
"id": "b129c6dc-f9ac-45de-9789-7ca73002d314",
"messageId": "msg_fd67f067-be0d-45fe-9a53-40507b83be9b",
"fileId": "c8b9d523-ed09-4670-80b7-a40d64688344",
"fileName": "structured_content_1.json",
"fileSize": 4399,
"mimeType": "application/json",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 2,
"actionId": "action_65fdae54-0864-45f7-921e-4872ec5849d6"
}

View file

@ -1,134 +0,0 @@
{
"metadata": {
"split_strategy": "single_document",
"source_documents": [],
"extraction_method": "ai_generation",
"title": "Memo Analysis Report"
},
"documents": [
{
"id": "doc_1",
"title": "Memo Analysis Report",
"filename": "memo_analysis_report.json",
"sections": [
{
"id": "section_overview",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Overview"
}
],
"order": 0
},
{
"id": "section_overview_paragraph",
"content_type": "paragraph",
"elements": [
{
"text": "The document is a memo intended for all employees from the management team, dated October 10, 2023. It serves as a placeholder for future content, providing a structure and format for communication within the organization."
}
],
"order": 1
},
{
"id": "section_key_insights",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Key Insights"
}
],
"order": 2
},
{
"id": "section_key_insights_bullet_list",
"content_type": "bullet_list",
"elements": [
{
"items": [
"The memo follows a conventional structure with clear sections for introduction, detailed analysis, and conclusion.",
"It is explicitly labeled as a placeholder, indicating it is not the final content but a template for future use.",
"The memo encourages feedback from employees, suggesting an open line of communication between management and staff."
]
}
],
"order": 3
},
{
"id": "section_trends_patterns",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Trends and Patterns"
}
],
"order": 4
},
{
"id": "section_trends_patterns_paragraph",
"content_type": "paragraph",
"elements": [
{
"text": "The document highlights the importance of having a well-defined structure in official communications, suggesting that the organization values clarity and consistency. The use of placeholders indicates a proactive approach to document preparation, allowing for flexibility and adaptability in content creation."
}
],
"order": 5
},
{
"id": "section_important_findings",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Important Findings"
}
],
"order": 6
},
{
"id": "section_important_findings_bullet_list",
"content_type": "bullet_list",
"elements": [
{
"items": [
"The memo is issued by management, underscoring their role in overseeing communication standards.",
"By soliciting feedback, the document reflects a collaborative environment where employee input is valued."
]
}
],
"order": 7
},
{
"id": "section_actionable_insights",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Actionable Insights"
}
],
"order": 8
},
{
"id": "section_actionable_insights_bullet_list",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Review and standardize document templates to maintain consistency across the organization.",
"Establish regular channels for employees to provide feedback on communication tools and practices.",
"Provide training sessions for employees on how to utilize document templates effectively.",
"Regularly review and update document templates to reflect any changes in organizational needs."
]
}
],
"order": 9
}
]
}
]
}

View file

@ -1,19 +0,0 @@
{
"id": "msg_23fba067-7b15-4079-bc8e-6d82f00c38c0",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "**Action 3 (ai.summarizeDocument)**\n\n✅ This task will accomplish the entire business objective you have requested.\n\n",
"role": "assistant",
"status": "step",
"sequenceNr": 6,
"publishedAt": 1764243707.10735,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 3,
"documentsLabel": "round1_task1_action3_results",
"actionId": "action_d585b2e1-8682-4b46-bc30-94d802161981",
"actionMethod": "ai",
"actionName": "summarizeDocument",
"success": null,
"documents": []
}

View file

@ -1,4 +0,0 @@
**Action 3 (ai.summarizeDocument)**
✅ This task will accomplish the entire business objective you have requested.

View file

@ -1,10 +0,0 @@
Memo Analysis Report Summary
============================
Key Insights
============
The memo analysis report, dated October 10, 2023, emphasizes the importance of a structured approach to official communications within the organization. It serves as a template for future content, highlighting the organization's dedication to clarity and consistency.
Generated: 2025-11-27 11:41:46 UTC

View file

@ -1,12 +0,0 @@
{
"id": "04546e10-67f0-4e89-aecf-67e6261ab919",
"messageId": "msg_23fba067-7b15-4079-bc8e-6d82f00c38c0",
"fileId": "5d220ab1-9b07-490f-91ca-b06317bc3971",
"fileName": "memo_analysis_summary.txt",
"fileSize": 390,
"mimeType": "text/plain",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 3,
"actionId": "action_d585b2e1-8682-4b46-bc30-94d802161981"
}

View file

@ -1,12 +0,0 @@
{
"id": "05e4eb1c-451f-4a88-b96b-3767ba13a8b5",
"messageId": "msg_23fba067-7b15-4079-bc8e-6d82f00c38c0",
"fileId": "a109b999-23b8-4e48-8821-dc49c3e55f21",
"fileName": "structured_content_2.json",
"fileSize": 1803,
"mimeType": "application/json",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 3,
"actionId": "action_d585b2e1-8682-4b46-bc30-94d802161981"
}

View file

@ -1,52 +0,0 @@
{
"metadata": {
"split_strategy": "single_document",
"source_documents": [],
"extraction_method": "ai_generation",
"title": "Memo Analysis Report Summary"
},
"documents": [
{
"id": "doc_1",
"title": "Memo Analysis Report Summary",
"filename": "memo_analysis_summary.json",
"sections": [
{
"id": "section_heading_key_insights",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Key Insights"
}
],
"order": 0
},
{
"id": "section_paragraph_summary",
"content_type": "paragraph",
"elements": [
{
"text": "The memo analysis report, dated October 10, 2023, emphasizes the importance of a structured approach to official communications within the organization. It serves as a template for future content, highlighting the organization's dedication to clarity and consistency."
}
],
"order": 1
},
{
"id": "section_bullet_list_insights",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Conventional Structure: The memo follows a traditional format with sections for introduction, detailed analysis, and conclusion, ensuring clarity and consistency.",
"Placeholder Nature: The document is labeled as a placeholder, indicating flexibility and adaptability in content creation.",
"Encouragement of Feedback: The memo invites feedback from employees, promoting open communication and a collaborative environment."
]
}
],
"order": 2
}
]
}
]
}

View file

@ -1,19 +0,0 @@
{
"id": "msg_a18f6389-b07f-40b9-9c9f-3d4c889201bf",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "**Action 4 (ai.analyzeDocuments)**\n\n✅ This task will accomplish the entire business objective you have requested.\n\n",
"role": "assistant",
"status": "step",
"sequenceNr": 7,
"publishedAt": 1764243749.4128509,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 4,
"documentsLabel": "round1_task1_action4_results",
"actionId": "action_67646482-4149-485b-9075-0f404d6d7d55",
"actionMethod": "ai",
"actionName": "analyzeDocuments",
"success": null,
"documents": []
}

View file

@ -1,4 +0,0 @@
**Action 4 (ai.analyzeDocuments)**
✅ This task will accomplish the entire business objective you have requested.

View file

@ -1,22 +0,0 @@
Memo Analysis Report
====================
Overview
========
The document is a memo intended for all employees from the management team, dated October 10, 2023. It serves as a placeholder for future content, providing a structure and format for communication within the organization.
Key Insights
============
Trends and Patterns
===================
Important Findings
==================
Actionable Insights
===================
Generated: 2025-11-27 11:42:28 UTC

View file

@ -1,12 +0,0 @@
{
"id": "84940888-ac72-4433-af74-00b9ad866f0c",
"messageId": "msg_a18f6389-b07f-40b9-9c9f-3d4c889201bf",
"fileId": "f066d5f7-8793-4258-9667-febf5b3b47b4",
"fileName": "memo_analysis_report_1.txt",
"fileSize": 469,
"mimeType": "text/plain",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 4,
"actionId": "action_67646482-4149-485b-9075-0f404d6d7d55"
}

View file

@ -1,12 +0,0 @@
{
"id": "d5d8e687-129e-44f3-aea3-09c665c3d54d",
"messageId": "msg_a18f6389-b07f-40b9-9c9f-3d4c889201bf",
"fileId": "d48e3112-bd6c-478b-ae2c-d409922ac210",
"fileName": "structured_content_3.json",
"fileSize": 4815,
"mimeType": "application/json",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 4,
"actionId": "action_67646482-4149-485b-9075-0f404d6d7d55"
}

View file

@ -1,136 +0,0 @@
{
"metadata": {
"split_strategy": "single_document",
"source_documents": [],
"extraction_method": "ai_generation",
"title": "Memo Analysis Report"
},
"documents": [
{
"id": "doc_1",
"title": "Memo Analysis Report",
"filename": "memo_analysis_report.json",
"sections": [
{
"id": "section_overview",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Overview"
}
],
"order": 0
},
{
"id": "section_overview_paragraph",
"content_type": "paragraph",
"elements": [
{
"text": "The document is a memo intended for all employees from the management team, dated October 10, 2023. It serves as a placeholder for future content, providing a structure and format for communication within the organization."
}
],
"order": 1
},
{
"id": "section_key_insights",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Key Insights"
}
],
"order": 2
},
{
"id": "section_key_insights_bullet_list",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Structured Communication: The memo emphasizes the importance of having a well-defined structure in official communications, suggesting that the organization prioritizes clarity and consistency.",
"Proactive Document Preparation: The use of placeholders indicates a proactive approach to document preparation, allowing flexibility and adaptability in content creation."
]
}
],
"order": 3
},
{
"id": "section_trends_patterns",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Trends and Patterns"
}
],
"order": 4
},
{
"id": "section_trends_patterns_bullet_list",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Emphasis on Clarity and Consistency: The structured format reflects a trend towards minimizing misunderstandings and ensuring alignment in organizational updates.",
"Adaptability in Communication: The placeholder nature suggests a trend towards adaptability, enabling quick responses to changing circumstances."
]
}
],
"order": 5
},
{
"id": "section_important_findings",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Important Findings"
}
],
"order": 6
},
{
"id": "section_important_findings_bullet_list",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Organizational Values: The memo's structure indicates values of clarity, consistency, and adaptability, likely reflected in other organizational operations.",
"Potential for Improved Efficiency: Using placeholders and structured formats can improve communication efficiency by reducing the time and effort needed for drafting new communications."
]
}
],
"order": 7
},
{
"id": "section_actionable_insights",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Actionable Insights"
}
],
"order": 8
},
{
"id": "section_actionable_insights_bullet_list",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Standardize Communication Templates: Develop standardized templates to enhance clarity and consistency across communications.",
"Training on Effective Communication: Provide training for employees on using structured templates effectively and adapting them to various contexts.",
"Regular Review and Update of Templates: Regularly review and update communication templates to ensure they meet evolving organizational needs.",
"Feedback Mechanism: Implement a feedback mechanism for employees to provide input on communication practices, ensuring alignment with employee needs and organizational goals."
]
}
],
"order": 9
}
]
}
]
}

View file

@ -1,19 +0,0 @@
{
"id": "msg_e945a952-5733-476f-90d0-b2bfa125d9be",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "**Action 5 (ai.summarizeDocument)**\n\n✅ This task will accomplish the entire business objective you have requested.\n\n",
"role": "assistant",
"status": "step",
"sequenceNr": 8,
"publishedAt": 1764243780.9853323,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 5,
"documentsLabel": "round1_task1_action5_results",
"actionId": "action_9608f071-b799-419a-b396-8f8ffe9ee4f3",
"actionMethod": "ai",
"actionName": "summarizeDocument",
"success": null,
"documents": []
}

View file

@ -1,4 +0,0 @@
**Action 5 (ai.summarizeDocument)**
✅ This task will accomplish the entire business objective you have requested.

View file

@ -1,12 +0,0 @@
Memo Summary
============
Memo Summary
============
The memo, dated October 10, 2023, is addressed to all employees from management and serves as a placeholder for future content. It outlines the structure and format for upcoming communications and emphasizes the importance of reviewing this structure to meet organizational needs.
The memo highlights the importance of employee input and collaboration in refining communication tools within the organization.
Generated: 2025-11-27 11:42:59 UTC

View file

@ -1,12 +0,0 @@
{
"id": "f420bacd-393f-4e84-8087-584a22738532",
"messageId": "msg_e945a952-5733-476f-90d0-b2bfa125d9be",
"fileId": "9aed6bd3-0d00-404d-bc59-7ec9f818b950",
"fileName": "memo_summary.txt",
"fileSize": 500,
"mimeType": "text/plain",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 5,
"actionId": "action_9608f071-b799-419a-b396-8f8ffe9ee4f3"
}

View file

@ -1,12 +0,0 @@
{
"id": "84ca0709-e5ce-490c-b56c-3860d9825289",
"messageId": "msg_e945a952-5733-476f-90d0-b2bfa125d9be",
"fileId": "6e4c958c-adea-4ce6-aab7-aea912bf19ec",
"fileName": "structured_content_4.json",
"fileSize": 1983,
"mimeType": "application/json",
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 5,
"actionId": "action_9608f071-b799-419a-b396-8f8ffe9ee4f3"
}

View file

@ -1,62 +0,0 @@
{
"metadata": {
"split_strategy": "single_document",
"source_documents": [],
"extraction_method": "ai_generation",
"title": "Memo Summary"
},
"documents": [
{
"id": "doc_1",
"title": "Memo Summary",
"filename": "memo_summary.json",
"sections": [
{
"id": "section_heading_1",
"content_type": "heading",
"elements": [
{
"level": 1,
"text": "Memo Summary"
}
],
"order": 0
},
{
"id": "section_paragraph_1",
"content_type": "paragraph",
"elements": [
{
"text": "The memo, dated October 10, 2023, is addressed to all employees from management and serves as a placeholder for future content. It outlines the structure and format for upcoming communications and emphasizes the importance of reviewing this structure to meet organizational needs."
}
],
"order": 1
},
{
"id": "section_bullet_list_1",
"content_type": "bullet_list",
"elements": [
{
"items": [
"Purpose: The memo is a template for future communications, seeking feedback on its structure and format.",
"Content Structure: Includes an introduction, detailed analysis, and conclusion with recommendations.",
"Call to Action: Employees are encouraged to contact management for more information and provide feedback."
]
}
],
"order": 2
},
{
"id": "section_paragraph_2",
"content_type": "paragraph",
"elements": [
{
"text": "The memo highlights the importance of employee input and collaboration in refining communication tools within the organization."
}
],
"order": 3
}
]
}
]
}

View file

@ -1,19 +0,0 @@
{
"id": "msg_c7cd6b0f-1811-4e5b-a8b9-5899a2c816a9",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "🎯 **Task 1**\n\n✅ The content validation indicates that the objective is not fully met due to incomplete summaries and inadequate document sizes, requiring further work to ensure comprehensiveness and quality.\n📊 Score 5.0/10",
"role": "assistant",
"status": "step",
"sequenceNr": 9,
"publishedAt": 1764243789.1994853,
"roundNumber": 1,
"taskNumber": 1,
"actionNumber": 0,
"documentsLabel": "task_1_completion",
"actionId": null,
"actionMethod": null,
"actionName": null,
"success": null,
"documents": []
}

View file

@ -1,4 +0,0 @@
🎯 **Task 1**
✅ The content validation indicates that the objective is not fully met due to incomplete summaries and inadequate document sizes, requiring further work to ensure comprehensiveness and quality.
📊 Score 5.0/10

View file

@ -1,19 +0,0 @@
{
"id": "msg_abbfe2bb-4fef-46d4-9927-f01282d03673",
"workflowId": "c6d18dcc-937a-4112-9596-9eedc5a45d6a",
"parentMessageId": null,
"message": "Workflow completed.\n\nProcessed 1 user inputs and generated 8 responses.\nAll tasks completed successfully.",
"role": "assistant",
"status": "last",
"sequenceNr": 10,
"publishedAt": 1764243790.4648964,
"roundNumber": 1,
"taskNumber": 0,
"actionNumber": 0,
"documentsLabel": "workflow_feedback",
"actionId": null,
"actionMethod": null,
"actionName": null,
"success": null,
"documents": []
}

View file

@ -1,4 +0,0 @@
Workflow completed.
Processed 1 user inputs and generated 8 responses.
All tasks completed successfully.

View file

@ -1,48 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
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:
- Projekt: Real estate project
- Parzelle: Plot/parcel
- Dokument: Document
- Kanton: Canton
- Gemeinde: Municipality
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 (label, statusProzess, etc.)
// For READ: include filter criteria (id, label, plz, etc.)
// For DELETE: include entity ID if mentioned
// For QUERY: include queryText if SQL is detected
},
"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: "Aktualisiere Projekt XYZ mit Status 'Planung'"
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
- Input: "SELECT * FROM Projekt WHERE plz = '8000'"
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE plz = '8000'", "queryType": "sql"}, "confidence": 1.0}
- Input: "Lösche Parzelle ABC"
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}

View file

@ -1,10 +0,0 @@
```json
{
"intent": "CREATE",
"entity": "Projekt",
"parameters": {
"label": "Hauptstrasse 42"
},
"confidence": 0.95
}
```

View file

@ -1,48 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Zeige mir alle Projekte 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:
- Projekt: Real estate project
- Parzelle: Plot/parcel
- Dokument: Document
- Kanton: Canton
- Gemeinde: Municipality
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 (label, statusProzess, etc.)
// For READ: include filter criteria (id, label, plz, etc.)
// For DELETE: include entity ID if mentioned
// For QUERY: include queryText if SQL is detected
},
"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: "Aktualisiere Projekt XYZ mit Status 'Planung'"
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
- Input: "SELECT * FROM Projekt WHERE plz = '8000'"
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE plz = '8000'", "queryType": "sql"}, "confidence": 1.0}
- Input: "Lösche Parzelle ABC"
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}

View file

@ -1,16 +0,0 @@
```json
{
"intent": "READ",
"entity": "Projekt",
"parameters": {
"location": "Zürich"
},
"confidence": 0.95
}
```
**Analysis:**
- **Intent**: READ - The command "Zeige mir" (Show me) clearly indicates a query/read operation
- **Entity**: Projekt - The user explicitly asks for "Projekte" (projects)
- **Parameters**: The location filter "in Zürich" is extracted as a search criterion. This could refer to a municipality (Gemeinde) named Zürich or projects located in the Zürich area
- **Confidence**: 0.95 - High confidence as the intent and entity are clearly stated, with an explicit location filter

View file

@ -1,48 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Zeige mir alle Projekte 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:
- Projekt: Real estate project
- Parzelle: Plot/parcel
- Dokument: Document
- Kanton: Canton
- Gemeinde: Municipality
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 (label, statusProzess, etc.)
// For READ: include filter criteria (id, label, plz, etc.)
// For DELETE: include entity ID if mentioned
// For QUERY: include queryText if SQL is detected
},
"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: "Aktualisiere Projekt XYZ mit Status 'Planung'"
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
- Input: "SELECT * FROM Projekt WHERE plz = '8000'"
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE plz = '8000'", "queryType": "sql"}, "confidence": 1.0}
- Input: "Lösche Parzelle ABC"
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}

View file

@ -1,16 +0,0 @@
```json
{
"intent": "READ",
"entity": "Projekt",
"parameters": {
"location": "Zürich"
},
"confidence": 0.95
}
```
**Analysis:**
- **Intent**: READ - The command "Zeige mir" (Show me) clearly indicates a query/read operation
- **Entity**: Projekt - The user explicitly asks for "Projekte" (projects)
- **Parameters**: The location filter "in Zürich" is extracted as a search criterion. This could refer to a municipality (Gemeinde) named Zürich or projects located in the Zürich area
- **Confidence**: 0.95 - High confidence as the intent and entity are clearly stated in the command

View file

@ -1,88 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Zeige mir alle Projekte 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)
- kontextKanton: string (canton ID, Foreign Key)
- kontextLand: string (country ID, Foreign Key)
- 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)
- Location queries (city, postal code) should use Parzelle fields (plz, kontextGemeinde), NOT Projekt fields
- 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}

View file

@ -1,18 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.9
}
```
**Reasoning:**
- The user wants to see/display ("Zeige mir") projects, which indicates a READ intent
- The location filter "in Zürich" refers to a city/municipality
- According to the schema, **Projekt does NOT have location fields directly** - location information is stored in the associated **Parzelle** entity through the `kontextGemeinde` field
- Therefore, to find projects in Zürich, we need to query **Parzelle** with the filter `kontextGemeinde: "Zürich"`
- The system can then retrieve the associated projects through the relationship (Projekte contain Parzellen)
- Confidence is 0.9 because the intent is clear, though the user said "Projekte" while we're technically querying "Parzelle" first to filter by location

View file

@ -1,88 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Zeige mir alle Projekte 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)
- kontextKanton: string (canton ID, Foreign Key)
- kontextLand: string (country ID, Foreign Key)
- 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)
- Location queries (city, postal code) should use Parzelle fields (plz, kontextGemeinde), NOT Projekt fields
- 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}

View file

@ -1,17 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.9
}
```
**Reasoning:**
- The user wants to see/display ("Zeige mir") projects, which indicates a READ intent
- The location filter "in Zürich" refers to a city/municipality
- According to the schema, **Projekt does NOT have location fields directly** - location information is stored in the associated **Parzelle** entity through the `kontextGemeinde` field
- Therefore, to find projects in Zürich, we need to query **Parzelle** entities with `kontextGemeinde: "Zürich"`, which will give us the plots (and their associated projects) in that municipality
- The confidence is 0.9 because the intent and location are clear, though the user said "Projekte" but we need to query via "Parzelle" due to the data model structure

View file

@ -1,88 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Erstell eine neue Parzelle 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)
- kontextKanton: string (canton ID, Foreign Key)
- kontextLand: string (country ID, Foreign Key)
- 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)
- Location queries (city, postal code) should use Parzelle fields (plz, kontextGemeinde), NOT Projekt fields
- 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}

View file

@ -1,17 +0,0 @@
```json
{
"intent": "CREATE",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.9
}
```
**Analysis:**
- **Intent**: CREATE - The user explicitly wants to create ("Erstell") a new entity
- **Entity**: Parzelle - The user specifies "eine neue Parzelle" (a new plot/parcel)
- **Parameters**:
- `kontextGemeinde`: "Zürich" - The location (municipality) where the plot should be created
- **Confidence**: 0.9 - High confidence as the intent and entity are clearly stated, though only minimal information (location) is provided for the creation

View file

@ -1,88 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Zeige mir alle Parzellen 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)
- kontextKanton: string (canton ID, Foreign Key)
- kontextLand: string (country ID, Foreign Key)
- 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)
- Location queries (city, postal code) should use Parzelle fields (plz, kontextGemeinde), NOT Projekt fields
- 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}

View file

@ -1,17 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.95
}
```
**Reasoning:**
- The user command "Zeige mir alle Parzellen in Zürich" translates to "Show me all plots in Zurich"
- **Intent**: READ - The user wants to query/retrieve existing entities
- **Entity**: Parzelle - The user explicitly asks for "Parzellen" (plots/parcels)
- **Parameters**: The location filter "in Zürich" maps to the `kontextGemeinde` field of the Parzelle entity, which stores the municipality information
- **Confidence**: 0.95 - High confidence because the intent is clear (show/display), the entity is explicitly mentioned (Parzellen), and the location parameter (Zürich) clearly maps to the kontextGemeinde field

View file

@ -1,17 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.95
}
```
**Explanation:**
- **Intent**: READ - The user wants to query/view existing data ("Zeige mir" = "Show me")
- **Entity**: Parzelle - The user explicitly asks for "Parzellen" (plots/parcels)
- **Parameters**:
- `kontextGemeinde`: "Zürich" - The location filter is specified as Zürich, which is a municipality (Gemeinde). According to the schema, location information for Parzellen is stored in the `kontextGemeinde` field
- **Confidence**: 0.95 - High confidence as the command is clear and unambiguous, directly requesting plots in a specific location

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Füge die Gemeinde Zürich in das System hinzu."
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}

View file

@ -1,16 +0,0 @@
```json
{
"intent": "CREATE",
"entity": "Gemeinde",
"parameters": {
"label": "Zürich"
},
"confidence": 0.95
}
```
**Analysis:**
- **Intent**: CREATE - The verb "Füge...hinzu" (add) clearly indicates the user wants to create a new entity
- **Entity**: Gemeinde - The user explicitly mentions "die Gemeinde Zürich" (the municipality Zürich)
- **Parameters**: The municipality name "Zürich" is extracted as the label/identifier
- **Confidence**: 0.95 - High confidence due to clear intent and explicit entity mention

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Füge die Gemeinde Zürich in das System hinzu."
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}

View file

@ -1,16 +0,0 @@
```json
{
"intent": "CREATE",
"entity": "Gemeinde",
"parameters": {
"label": "Zürich"
},
"confidence": 0.95
}
```
**Analysis:**
- **Intent**: CREATE - The user explicitly wants to add ("Füge...hinzu") a new entity to the system
- **Entity**: Gemeinde - The user specifically mentions "die Gemeinde Zürich" (the municipality Zürich)
- **Parameters**: The municipality name "Zürich" is extracted as the label/identifier
- **Confidence**: 0.95 - High confidence as the command is clear and unambiguous with explicit CREATE intent and entity type

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Füge ein neues Projekt mit Parzelle in Zürich hinzu."
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}

View file

@ -1,20 +0,0 @@
```json
{
"intent": "CREATE",
"entity": "Projekt",
"parameters": {
"parzellen": [
{
"kontextGemeinde": "Zürich"
}
]
},
"confidence": 0.85
}
```
**Reasoning:**
- The user wants to create ("Füge hinzu") a new project ("neues Projekt")
- The project should include a plot ("mit Parzelle") in Zürich
- Since Projekt contains Parzellen as a nested list, and the location information (Zürich) belongs to Parzelle (via kontextGemeinde field), I've structured the parameters to reflect this relationship
- The confidence is 0.85 because while the intent is clear, the user hasn't provided other typical project details like a label/name for the project itself

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Füge die Gemeinde Winterthur hinzu"
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}

View file

@ -1,16 +0,0 @@
```json
{
"intent": "CREATE",
"entity": "Gemeinde",
"parameters": {
"label": "Winterthur"
},
"confidence": 0.95
}
```
**Analysis:**
- **Intent**: CREATE - The verb "Füge...hinzu" (add) clearly indicates the user wants to create a new entity
- **Entity**: Gemeinde - The user explicitly mentions "die Gemeinde Winterthur" (the municipality Winterthur)
- **Parameters**: The name/label of the municipality is "Winterthur"
- **Confidence**: 0.95 - High confidence as the command is clear and unambiguous

View file

@ -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}

View file

@ -1,17 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.9
}
```
**Reasoning:**
- The user asks "How many projects in Zürich are there?" which is a READ/query operation
- Since location information (city) is stored in Parzelle via `kontextGemeinde`, not directly in Projekt, we need to query Parzelle entities
- The parameter `kontextGemeinde` is set to "Zürich" to filter plots by municipality
- To get projects in Zürich, the system will need to find all Parzellen with kontextGemeinde="Zürich" and then identify the associated projects
- 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)

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "welche 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}

View file

@ -1,17 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.9
}
```
**Reasoning:**
- The user is asking "which projects in Zürich exist?" which is a READ/query operation
- Since location information (city/municipality) is stored in Parzelle (via `kontextGemeinde`), not directly in Projekt, we need to query Parzelle entities
- "Zürich" is the municipality name that maps to the `kontextGemeinde` field
- The system will need to find all Parzellen in Zürich and then retrieve their associated Projekte
- High confidence (0.9) as the intent and location are clearly stated

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Welche 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}

View file

@ -1,18 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.9
}
```
**Reasoning:**
- The user is asking "Which projects in Zürich exist?" (Welche Projekte in Zürich gibt es?)
- This is a READ operation (querying for existing data)
- 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 plots by municipality
- To get projects in Zürich, the system will need to find Parzellen with kontextGemeinde="Zürich" and then retrieve their associated Projekte
- Confidence is 0.9 because the intent is clear, though the user asked about "Projekte" while we're technically querying via "Parzelle" due to the data model structure

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Welche Gemeinden haben wir bereits hinzugefügt?"
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}

View file

@ -1,14 +0,0 @@
```json
{
"intent": "READ",
"entity": "Gemeinde",
"parameters": {},
"confidence": 0.95
}
```
**Reasoning:**
- The user is asking "Which municipalities have we already added?" (Welche Gemeinden haben wir bereits hinzugefügt?)
- This is clearly a READ operation to query existing Gemeinde entities
- No specific filters are mentioned, so the user wants to see all Gemeinde records
- The confidence is high (0.95) because the intent and entity are explicitly stated in the question

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Füge eine neue Parzelle hinzu die in Zürich liegt und die Adresse Ueberlandstrasse 11 8050 hat."
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}

View file

@ -1,12 +0,0 @@
```json
{
"intent": "CREATE",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich",
"strasseNr": "Ueberlandstrasse 11",
"plz": "8050"
},
"confidence": 0.95
}
```

View file

@ -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"
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}

View file

@ -1,18 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich",
"aggregation": "count"
},
"confidence": 0.95
}
```
**Explanation:**
- The user is asking "How many plots are there in Zürich?" which is a READ/query operation
- The entity is "Parzelle" (plot/parcel) since the user explicitly asks about "Parzellen"
- The location "Zürich" maps to the `kontextGemeinde` field (municipality)
- The phrase "Wie viele" (how many) indicates a count aggregation is needed
- High confidence (0.95) because the intent and entity are clearly stated in German

View file

@ -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}

View file

@ -1,17 +0,0 @@
```json
{
"intent": "READ",
"entity": "Parzelle",
"parameters": {
"kontextGemeinde": "Zürich"
},
"confidence": 0.9
}
```
**Reasoning:**
- The user asks "How many projects in Zürich are there?" which is a READ/query operation
- Since location information (city/municipality) is stored in **Parzelle** via the `kontextGemeinde` field, not directly in Projekt, we need to query Parzelle entities
- "Zürich" maps to the `kontextGemeinde` parameter
- The system will need to find all Parzellen in Zürich and then count the associated unique projects
- Confidence is 0.9 because the intent is clear, though the user asks about "Projekte" while we're querying via "Parzelle" (which is the correct approach based on the data model)

View file

@ -1,89 +0,0 @@
Analyze the following user command and extract the intent, entity, and parameters.
User Command: "Welche Gemeinden gibt es in der datenbank?"
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}

Some files were not shown because too many files have changed in this diff Show more