extended PEK routes and included swisstopo connector
This commit is contained in:
parent
cfeaccdf02
commit
e0c4193e36
180 changed files with 10905 additions and 8458 deletions
|
|
@ -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
|
||||
365
docs/code-documentation/date-time-handling.md
Normal file
365
docs/code-documentation/date-time-handling.md
Normal 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.
|
||||
|
||||
|
|
@ -506,3 +506,4 @@ async def get_sensitive_data(
|
|||
- [Architecture Overview](./architecture-overview.md) - Overall system architecture
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
786
docs/frontend-documentation/chat-playground-page-requirements.md
Normal file
786
docs/frontend-documentation/chat-playground-page-requirements.md
Normal 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).
|
||||
|
||||
542
docs/frontend-documentation/chat-playground-workflow-journeys.md
Normal file
542
docs/frontend-documentation/chat-playground-workflow-journeys.md
Normal 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
|
||||
```
|
||||
|
||||
293
docs/frontend-documentation/connection-page-customer-journeys.md
Normal file
293
docs/frontend-documentation/connection-page-customer-journeys.md
Normal 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
|
||||
```
|
||||
|
||||
491
docs/frontend-documentation/connection-page-requirements.md
Normal file
491
docs/frontend-documentation/connection-page-requirements.md
Normal 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).
|
||||
|
||||
689
docs/frontend-documentation/dynamic-forms-and-pagination.md
Normal file
689
docs/frontend-documentation/dynamic-forms-and-pagination.md
Normal 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.
|
||||
|
||||
335
docs/frontend-documentation/file-page-customer-journeys.md
Normal file
335
docs/frontend-documentation/file-page-customer-journeys.md
Normal 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
|
||||
```
|
||||
|
||||
574
docs/frontend-documentation/file-page-requirements.md
Normal file
574
docs/frontend-documentation/file-page-requirements.md
Normal 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).
|
||||
|
||||
349
docs/frontend-documentation/prompt-page-customer-journeys.md
Normal file
349
docs/frontend-documentation/prompt-page-customer-journeys.md
Normal 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
|
||||
```
|
||||
|
||||
549
docs/frontend-documentation/prompt-page-requirements.md
Normal file
549
docs/frontend-documentation/prompt-page-requirements.md
Normal 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).
|
||||
|
||||
516
docs/frontend-documentation/user-page-customer-journeys.md
Normal file
516
docs/frontend-documentation/user-page-customer-journeys.md
Normal 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
|
||||
```
|
||||
843
docs/frontend-documentation/user-page-requirements.md
Normal file
843
docs/frontend-documentation/user-page-requirements.md
Normal 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).
|
||||
|
||||
474
docs/frontend-documentation/voice-service-customer-journeys.md
Normal file
474
docs/frontend-documentation/voice-service-customer-journeys.md
Normal 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
|
||||
```
|
||||
|
||||
1081
docs/frontend-documentation/voice-service-page-requirements.md
Normal file
1081
docs/frontend-documentation/voice-service-page-requirements.md
Normal file
File diff suppressed because it is too large
Load diff
768
docs/frontend-documentation/workflow-page-customer-journeys.md
Normal file
768
docs/frontend-documentation/workflow-page-customer-journeys.md
Normal 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
|
||||
555
docs/frontend-documentation/workflow-page-requirements.md
Normal file
555
docs/frontend-documentation/workflow-page-requirements.md
Normal 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
|
|
@ -50,3 +50,4 @@ def _initializeDatabase(self):
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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`:
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
test
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
🚀 **Task 1/1**
|
||||
|
||||
💬 This task will accomplish the entire business objective you have requested.
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
**Action 1 (ai.generateDocument)**
|
||||
|
||||
✅ This task will accomplish the entire business objective you have requested.
|
||||
|
||||
|
|
@ -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"
|
||||
}
|
||||
Binary file not shown.
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
**Action 2 (ai.analyzeDocuments)**
|
||||
|
||||
✅ This task will accomplish the entire business objective you have requested.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
**Action 3 (ai.summarizeDocument)**
|
||||
|
||||
✅ This task will accomplish the entire business objective you have requested.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
**Action 4 (ai.analyzeDocuments)**
|
||||
|
||||
✅ This task will accomplish the entire business objective you have requested.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
**Action 5 (ai.summarizeDocument)**
|
||||
|
||||
✅ This task will accomplish the entire business objective you have requested.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Workflow completed.
|
||||
|
||||
Processed 1 user inputs and generated 8 responses.
|
||||
All tasks completed successfully.
|
||||
|
|
@ -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}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "CREATE",
|
||||
"entity": "Projekt",
|
||||
"parameters": {
|
||||
"label": "Hauptstrasse 42"
|
||||
},
|
||||
"confidence": 0.95
|
||||
}
|
||||
```
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "Wie viele Projekte in Zürich gibt es?"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"kontextGemeinde": "Zürich"}, "confidence": 0.9}
|
||||
Note: Location queries should query Parzelle, not Projekt directly
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "READ",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"kontextGemeinde": "Zürich"
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
**Reasoning:**
|
||||
- The user 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)
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "CREATE",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"kontextGemeinde": "Zürich",
|
||||
"strasseNr": "Ueberlandstrasse 11",
|
||||
"plz": "8050"
|
||||
},
|
||||
"confidence": 0.95
|
||||
}
|
||||
```
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
Analyze the following user command and extract the intent, entity, and parameters.
|
||||
|
||||
User Command: "Wie viele Projekte in Zürich gibt es?"
|
||||
|
||||
Available intents:
|
||||
- CREATE: User wants to create a new entity
|
||||
- READ: User wants to read/query entities
|
||||
- UPDATE: User wants to update an existing entity
|
||||
- DELETE: User wants to delete an entity
|
||||
- QUERY: User wants to execute a database query (SQL statements)
|
||||
|
||||
Available entities and their fields:
|
||||
|
||||
**Projekt** (Real estate project):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (project designation/name)
|
||||
- statusProzess: string enum (Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv)
|
||||
- perimeter: GeoPolylinie (geographic boundary, JSONB)
|
||||
- baulinie: GeoPolylinie (building line, JSONB)
|
||||
- parzellen: List[Parzelle] (plots belonging to project, JSONB)
|
||||
- dokumente: List[Dokument] (documents, JSONB)
|
||||
- kontextInformationen: List[Kontext] (context info, JSONB)
|
||||
|
||||
**Parzelle** (Plot/parcel):
|
||||
- id: string (primary key)
|
||||
- mandateId: string (mandate ID)
|
||||
- label: string (plot designation)
|
||||
- strasseNr: string (street and house number)
|
||||
- plz: string (postal code)
|
||||
- kontextGemeinde: string (municipality ID, Foreign Key to Gemeinde table)
|
||||
- bauzone: string (building zone, e.g. W3, WG2)
|
||||
- az: float (Ausnützungsziffer)
|
||||
- bz: float (Bebauungsziffer)
|
||||
- vollgeschossZahl: int (number of allowed full floors)
|
||||
- gebaeudehoeheMax: float (maximum building height in meters)
|
||||
- laermschutzzone: string (noise protection zone)
|
||||
- hochwasserschutzzone: string (flood protection zone)
|
||||
- grundwasserschutzzone: string (groundwater protection zone)
|
||||
- parzelleBebaut: JaNein enum (is plot built)
|
||||
- parzelleErschlossen: JaNein enum (is plot developed)
|
||||
- parzelleHanglage: JaNein enum (is plot on slope)
|
||||
|
||||
**Important relationships:**
|
||||
- Projekte contain Parzellen (projects have plots)
|
||||
- Parzelle links to Gemeinde (via kontextGemeinde)
|
||||
- Gemeinde links to Kanton (via id_kanton)
|
||||
- Kanton links to Land (via id_land)
|
||||
- Location queries (city, postal code) should use Parzelle.kontextGemeinde (municipality name will be resolved to ID)
|
||||
- Projekt does NOT have location fields directly - location is stored in associated Parzellen
|
||||
|
||||
Return a JSON object with the following structure:
|
||||
{
|
||||
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
||||
"entity": "Projekt|Parzelle|Dokument|Kanton|Gemeinde|null",
|
||||
"parameters": {
|
||||
// Extracted parameters from user input
|
||||
// For CREATE/UPDATE: include all relevant fields using EXACT field names from above
|
||||
// For READ: include filter criteria using EXACT field names (id, label, plz, kontextGemeinde, etc.)
|
||||
// For DELETE: include entity ID if mentioned
|
||||
// For QUERY: include queryText if SQL is detected
|
||||
// IMPORTANT: Use only field names that exist in the entity definition above
|
||||
},
|
||||
"confidence": 0.0-1.0 // Confidence score for the analysis
|
||||
}
|
||||
|
||||
Examples:
|
||||
- Input: "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
||||
Output: {"intent": "CREATE", "entity": "Projekt", "parameters": {"label": "Hauptstrasse 42"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Zeige mir alle Projekte"
|
||||
Output: {"intent": "READ", "entity": "Projekt", "parameters": {}, "confidence": 0.9}
|
||||
|
||||
- Input: "Zeige mir Projekte in Zürich"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"kontextGemeinde": "Zürich"}, "confidence": 0.9}
|
||||
Note: Location queries should query Parzelle, not Projekt directly
|
||||
|
||||
- Input: "Zeige mir Parzellen mit PLZ 8000"
|
||||
Output: {"intent": "READ", "entity": "Parzelle", "parameters": {"plz": "8000"}, "confidence": 0.95}
|
||||
|
||||
- Input: "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
||||
Output: {"intent": "UPDATE", "entity": "Projekt", "parameters": {"id": "XYZ", "statusProzess": "Planung"}, "confidence": 0.85}
|
||||
|
||||
- Input: "SELECT * FROM Projekt WHERE label = 'Test'"
|
||||
Output: {"intent": "QUERY", "entity": null, "parameters": {"queryText": "SELECT * FROM Projekt WHERE label = 'Test'", "queryType": "sql"}, "confidence": 1.0}
|
||||
|
||||
- Input: "Lösche Parzelle ABC"
|
||||
Output: {"intent": "DELETE", "entity": "Parzelle", "parameters": {"id": "ABC"}, "confidence": 0.9}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
```json
|
||||
{
|
||||
"intent": "READ",
|
||||
"entity": "Parzelle",
|
||||
"parameters": {
|
||||
"kontextGemeinde": "Zürich"
|
||||
},
|
||||
"confidence": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
**Reasoning:**
|
||||
- The user 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)
|
||||
|
|
@ -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
Loading…
Reference in a new issue