feat: Add Chat UX improvements with notifications and @mention support
- Add ActionBar component with expandable toolbar for mobile - Add @mention functionality with autocomplete dropdown - Add browser notification system (push, sound, vibration) - Add NotificationSettings modal for user preferences - Add mention badges on room list cards - Add ReportPreview with Markdown rendering and copy/download - Add message copy functionality with hover actions - Add backend mentions field to messages with Alembic migration - Add lots field to rooms, remove templates - Optimize WebSocket database session handling - Various UX polish (animations, accessibility) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -262,3 +262,91 @@ The system SHALL track report generation status and notify users of completion v
|
||||
```
|
||||
- **AND** the error message SHALL be user-friendly (no technical details)
|
||||
|
||||
### Requirement: DIFY Service Health Check
|
||||
|
||||
The system SHALL provide a health check mechanism to verify DIFY AI service connectivity and configuration.
|
||||
|
||||
#### Scenario: Check DIFY configuration on startup
|
||||
- **WHEN** the application starts
|
||||
- **AND** `DIFY_API_KEY` is not configured
|
||||
- **THEN** the system SHALL log a warning message: "DIFY_API_KEY not configured - AI report generation will be unavailable"
|
||||
|
||||
#### Scenario: DIFY health check endpoint
|
||||
- **WHEN** a user sends `GET /api/reports/health`
|
||||
- **AND** `DIFY_API_KEY` is not configured
|
||||
- **THEN** the system SHALL return:
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "DIFY_API_KEY 未設定,請聯繫系統管理員"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: DIFY service unreachable
|
||||
- **WHEN** a user sends `GET /api/reports/health`
|
||||
- **AND** `DIFY_API_KEY` is configured
|
||||
- **BUT** the DIFY service cannot be reached
|
||||
- **THEN** the system SHALL return:
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "無法連接 AI 服務,請稍後再試"
|
||||
}
|
||||
```
|
||||
|
||||
### Requirement: Report Generation Status Polling
|
||||
|
||||
The frontend SHALL implement polling mechanism to ensure report status updates are received even if WebSocket connection is unstable.
|
||||
|
||||
#### Scenario: Poll report status after generation trigger
|
||||
- **WHEN** a user triggers report generation
|
||||
- **AND** receives the initial `report_id`
|
||||
- **THEN** the frontend SHALL poll `GET /api/rooms/{room_id}/reports/{report_id}` every 2 seconds
|
||||
- **AND** continue polling until status is "completed" or "failed"
|
||||
- **AND** timeout after 120 seconds with user-friendly error message
|
||||
|
||||
#### Scenario: Display generation progress
|
||||
- **WHEN** polling returns status "collecting_data"
|
||||
- **THEN** the UI SHALL display "正在收集聊天室資料..."
|
||||
- **WHEN** polling returns status "generating_content"
|
||||
- **THEN** the UI SHALL display "AI 正在分析並生成報告內容..."
|
||||
- **WHEN** polling returns status "assembling_document"
|
||||
- **THEN** the UI SHALL display "正在組裝報告文件..."
|
||||
|
||||
#### Scenario: Display generation error
|
||||
- **WHEN** polling returns status "failed"
|
||||
- **THEN** the UI SHALL display the `error_message` from the response
|
||||
- **AND** provide option to retry generation
|
||||
|
||||
### Requirement: Markdown Report Output
|
||||
The report generation system SHALL provide reports in Markdown format for in-page preview.
|
||||
|
||||
#### Scenario: Get report as Markdown
|
||||
- **WHEN** user requests `GET /api/rooms/{room_id}/reports/{report_id}/markdown`
|
||||
- **AND** the report status is `completed`
|
||||
- **THEN** the system returns the report content in Markdown format
|
||||
- **AND** the Markdown includes all report sections (summary, timeline, participants, etc.)
|
||||
|
||||
#### Scenario: Markdown includes metadata
|
||||
- **WHEN** generating Markdown output
|
||||
- **THEN** the output includes a metadata header with room info, LOT numbers, dates
|
||||
- **AND** the format is suitable for copy-paste to other platforms
|
||||
|
||||
### Requirement: In-Page Report Preview
|
||||
The frontend SHALL display a preview of the generated report within the chat room interface.
|
||||
|
||||
#### Scenario: Display report preview
|
||||
- **WHEN** user clicks on a completed report
|
||||
- **THEN** a modal or drawer opens showing the Markdown-rendered report
|
||||
- **AND** the preview includes proper formatting (headers, tables, lists)
|
||||
|
||||
#### Scenario: Copy Markdown content
|
||||
- **WHEN** user clicks "Copy Markdown" in the preview
|
||||
- **THEN** the raw Markdown text is copied to clipboard
|
||||
- **AND** a success toast notification is shown
|
||||
|
||||
#### Scenario: Download Word from preview
|
||||
- **WHEN** user clicks "Download Word" in the preview
|
||||
- **THEN** the .docx file is downloaded
|
||||
- **AND** the filename uses the report title
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ The system SHALL recognize ymirliu@panjit.com.tw as a system administrator with
|
||||
- **AND** record the admin override in audit log
|
||||
|
||||
### Requirement: Create Incident Room
|
||||
The system SHALL allow authenticated users to create a new incident room with metadata including title, incident type, severity level, location, and description. Each room SHALL be assigned a unique identifier and timestamp upon creation.
|
||||
The system SHALL allow authenticated users to create a new incident room with metadata including title, incident type, severity level, location, description, and optional LOT batch numbers. Each room SHALL be assigned a unique identifier and timestamp upon creation.
|
||||
|
||||
#### Scenario: Create room for equipment failure incident
|
||||
#### Scenario: Create room with LOT numbers
|
||||
- **WHEN** an authenticated user sends `POST /api/rooms` with body:
|
||||
```json
|
||||
{
|
||||
@@ -31,25 +31,18 @@ The system SHALL allow authenticated users to create a new incident room with me
|
||||
"incident_type": "equipment_failure",
|
||||
"severity": "high",
|
||||
"location": "Building A, Line 3",
|
||||
"description": "Conveyor belt motor overheating, production halted"
|
||||
"description": "Conveyor belt motor overheating, production halted",
|
||||
"lots": ["LOT-2024-001"]
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL create a new incident_rooms record with:
|
||||
- Unique room_id (UUID)
|
||||
- Provided metadata fields
|
||||
- Provided metadata fields including lots
|
||||
- Status set to "active"
|
||||
- created_by set to current user's ID
|
||||
- created_at timestamp
|
||||
- **AND** automatically add the creator as a room member with "owner" role
|
||||
- **AND** return status 201 with the room details including room_id
|
||||
|
||||
#### Scenario: Create room with missing required fields
|
||||
- **WHEN** a user attempts to create a room without required fields (title, incident_type)
|
||||
- **THEN** the system SHALL return status 400 with validation error details
|
||||
|
||||
#### Scenario: Create room without authentication
|
||||
- **WHEN** an unauthenticated request is sent to `POST /api/rooms`
|
||||
- **THEN** the system SHALL return status 401 with "Authentication required"
|
||||
- **AND** return status 201 with the room details including room_id and lots
|
||||
|
||||
### Requirement: List and Filter Incident Rooms
|
||||
The system SHALL provide endpoints to list incident rooms with filtering capabilities by status, incident type, severity, date range, and user membership. The system SHALL automatically exclude rooms with ARCHIVED status from listing results for non-admin users, ensuring archived rooms are only visible to system administrators.
|
||||
@@ -121,44 +114,21 @@ The system SHALL allow room owners and members with appropriate permissions to a
|
||||
- **AND** record the admin action in audit log
|
||||
|
||||
### Requirement: Update Room Status and Metadata
|
||||
The system SHALL allow room owners to update room metadata and transition room status through its lifecycle (active → resolved → archived).
|
||||
The system SHALL allow room owners and editors to update room metadata including LOT batch numbers, and allow owners to transition room status through its lifecycle (active -> resolved -> archived).
|
||||
|
||||
#### Scenario: Mark room as resolved
|
||||
- **WHEN** a room owner sends `PATCH /api/rooms/{room_id}` with:
|
||||
```json
|
||||
{
|
||||
"status": "resolved",
|
||||
"resolution_notes": "Replaced motor, production resumed"
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL update status to "resolved"
|
||||
- **AND** set resolved_at timestamp
|
||||
- **AND** store resolution_notes
|
||||
- **AND** keep room accessible in read-only mode for members
|
||||
|
||||
#### Scenario: Update room metadata
|
||||
#### Scenario: Update room metadata with LOT
|
||||
- **WHEN** a room owner sends `PATCH /api/rooms/{room_id}` with:
|
||||
```json
|
||||
{
|
||||
"severity": "critical",
|
||||
"description": "Updated: Fire hazard detected"
|
||||
"description": "Updated: Fire hazard detected",
|
||||
"lots": ["LOT-2024-001", "LOT-2024-002"]
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL update only the provided fields
|
||||
- **THEN** the system SHALL update only the provided fields including lots
|
||||
- **AND** record the update in room_activity log
|
||||
- **AND** set last_updated_at timestamp
|
||||
|
||||
#### Scenario: Archive resolved room
|
||||
- **WHEN** a room owner sends `PATCH /api/rooms/{room_id}` with status "archived"
|
||||
- **AND** the room is already in "resolved" status
|
||||
- **THEN** the system SHALL update status to "archived"
|
||||
- **AND** set archived_at timestamp
|
||||
- **AND** make room read-only for all members
|
||||
|
||||
#### Scenario: Invalid status transition
|
||||
- **WHEN** attempting to change status from "active" directly to "archived"
|
||||
- **THEN** the system SHALL return status 400 with "Invalid status transition"
|
||||
|
||||
### Requirement: Room Access Control
|
||||
The system SHALL enforce role-based access control for all room operations based on user membership and assigned roles. System administrators SHALL have full access to all rooms regardless of membership status.
|
||||
|
||||
@@ -188,31 +158,6 @@ The system SHALL enforce role-based access control for all room operations based
|
||||
- **THEN** the system SHALL return ALL rooms in the system, not just rooms where admin is a member
|
||||
- **AND** include an "is_admin_view" flag in the response
|
||||
|
||||
### Requirement: Room Templates
|
||||
The system SHALL support predefined room templates for common incident types to streamline room creation with preset metadata and initial members.
|
||||
|
||||
#### Scenario: Create room from template
|
||||
- **WHEN** a user sends `POST /api/rooms` with:
|
||||
```json
|
||||
{
|
||||
"template": "equipment_failure",
|
||||
"title": "Molding Machine #5 Down",
|
||||
"location": "Building B"
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL create room with:
|
||||
- Preset incident_type from template
|
||||
- Default severity level from template
|
||||
- Auto-assigned team members based on template configuration
|
||||
- Template-specific metadata fields
|
||||
|
||||
#### Scenario: List available templates
|
||||
- **WHEN** a user sends `GET /api/room-templates`
|
||||
- **THEN** the system SHALL return available templates with:
|
||||
- Template name and description
|
||||
- Default values for each template
|
||||
- Required additional fields
|
||||
|
||||
### Requirement: Admin Permanent Room Deletion
|
||||
The system SHALL provide system administrators with the ability to permanently delete rooms, including all associated data (members, messages, files, reports). This operation is irreversible and restricted to system administrators only.
|
||||
|
||||
@@ -260,3 +205,95 @@ The system SHALL hide rooms with ARCHIVED status from non-admin users in all lis
|
||||
- **THEN** the system SHALL return all rooms regardless of status
|
||||
- **AND** include archived rooms in the response
|
||||
|
||||
### Requirement: LOT Batch Number Tracking
|
||||
The system SHALL support tracking multiple LOT (batch numbers) associated with each incident room. LOT information helps identify affected product batches for quality traceability and recall purposes.
|
||||
|
||||
#### Scenario: Create room with LOT numbers
|
||||
- **WHEN** an authenticated user sends `POST /api/rooms` with body:
|
||||
```json
|
||||
{
|
||||
"title": "Quality Issue on Line 3",
|
||||
"incident_type": "quality_issue",
|
||||
"severity": "high",
|
||||
"location": "Building A",
|
||||
"lots": ["LOT-2024-001", "LOT-2024-002"]
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL create the room with the provided LOT numbers
|
||||
- **AND** store lots as a JSON array in the database
|
||||
- **AND** return the room details including the lots array
|
||||
|
||||
#### Scenario: Create room without LOT numbers
|
||||
- **WHEN** an authenticated user creates a room without specifying lots
|
||||
- **THEN** the system SHALL create the room with an empty lots array
|
||||
- **AND** allow lots to be added later via update
|
||||
|
||||
#### Scenario: Update room LOT numbers
|
||||
- **WHEN** an authenticated user with editor or owner role sends `PATCH /api/rooms/{room_id}` with:
|
||||
```json
|
||||
{
|
||||
"lots": ["LOT-2024-001", "LOT-2024-002", "LOT-2024-003"]
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL replace the existing lots with the new array
|
||||
- **AND** update last_updated_at timestamp
|
||||
- **AND** return the updated room details
|
||||
|
||||
#### Scenario: Add single LOT to existing room
|
||||
- **WHEN** an authenticated user with editor or owner role sends `POST /api/rooms/{room_id}/lots` with:
|
||||
```json
|
||||
{
|
||||
"lot": "LOT-2024-004"
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL append the LOT to the existing lots array
|
||||
- **AND** prevent duplicate LOT entries
|
||||
- **AND** return the updated lots array
|
||||
|
||||
#### Scenario: Remove single LOT from room
|
||||
- **WHEN** an authenticated user with editor or owner role sends `DELETE /api/rooms/{room_id}/lots/{lot_id}`
|
||||
- **THEN** the system SHALL remove the specified LOT from the array
|
||||
- **AND** return the updated lots array
|
||||
|
||||
#### Scenario: Viewer cannot modify LOT numbers
|
||||
- **WHEN** a user with viewer role attempts to update LOT numbers
|
||||
- **THEN** the system SHALL return status 403 with "Insufficient permissions"
|
||||
|
||||
#### Scenario: LOT numbers displayed in room details
|
||||
- **WHEN** a user sends `GET /api/rooms/{room_id}`
|
||||
- **THEN** the response SHALL include the lots array
|
||||
- **AND** lots SHALL be returned in the order they were added
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Action Bar Layout
|
||||
The chat room interface SHALL provide an action bar near the message input area for quick access to common operations.
|
||||
|
||||
#### Scenario: Action bar displays common actions
|
||||
- **WHEN** user views a chat room
|
||||
- **THEN** an action bar is displayed above or beside the message input
|
||||
- **AND** the action bar includes: upload file, generate report, add member buttons
|
||||
|
||||
#### Scenario: Action bar toggle on mobile
|
||||
- **WHEN** user is on a mobile device
|
||||
- **THEN** the action bar buttons can be collapsed/expanded
|
||||
- **AND** a single toggle button reveals the full action options
|
||||
|
||||
### Requirement: Message Copy Action
|
||||
Users SHALL be able to copy individual message content to clipboard.
|
||||
|
||||
#### Scenario: Copy message content
|
||||
- **WHEN** user hovers over a message
|
||||
- **THEN** a copy button appears
|
||||
- **AND** clicking the button copies the message content to clipboard
|
||||
- **AND** a success toast notification is shown
|
||||
|
||||
### Requirement: Notification Settings
|
||||
Users SHALL be able to configure notification preferences for the chat room.
|
||||
|
||||
#### Scenario: Configure notification settings
|
||||
- **WHEN** user accesses notification settings
|
||||
- **THEN** user can enable/disable sound notifications
|
||||
- **AND** user can enable/disable browser push notifications
|
||||
- **AND** user can enable/disable vibration (on supported devices)
|
||||
|
||||
|
||||
@@ -192,3 +192,148 @@ The system SHALL support message editing and deletion with proper audit trail an
|
||||
- **AND** broadcast to all connected clients
|
||||
- **AND** aggregate reaction counts for display
|
||||
|
||||
### Requirement: Short-lived Database Sessions for WebSocket
|
||||
The system SHALL process WebSocket messages using short-lived database sessions that are acquired and released for each individual operation, rather than holding a session for the entire WebSocket connection lifetime.
|
||||
|
||||
#### Scenario: Message creation with short session
|
||||
- **WHEN** a user sends a message via WebSocket
|
||||
- **THEN** the system acquires a database session
|
||||
- **AND** creates the message with proper sequence number
|
||||
- **AND** commits the transaction
|
||||
- **AND** releases the session immediately
|
||||
- **AND** broadcasts the message to room members
|
||||
|
||||
#### Scenario: Concurrent message handling
|
||||
- **WHEN** multiple users send messages simultaneously
|
||||
- **THEN** each message operation uses an independent database session
|
||||
- **AND** sequence numbers are correctly assigned without duplicates
|
||||
- **AND** no connection pool exhaustion occurs
|
||||
|
||||
### Requirement: Message Sequence Number Integrity
|
||||
The system SHALL guarantee unique, monotonically increasing sequence numbers per room using database-level locking to prevent race conditions during concurrent message creation.
|
||||
|
||||
#### Scenario: Concurrent sequence assignment
|
||||
- **WHEN** two users send messages to the same room at the exact same time
|
||||
- **THEN** each message receives a unique sequence number
|
||||
- **AND** the sequence numbers are consecutive without gaps or duplicates
|
||||
|
||||
#### Scenario: High concurrency sequence safety
|
||||
- **WHEN** 50+ users send messages to the same room simultaneously
|
||||
- **THEN** all messages receive correct unique sequence numbers
|
||||
- **AND** the operation does not cause deadlocks
|
||||
|
||||
### Requirement: Configurable Database Connection Pool
|
||||
The system SHALL support environment variable configuration for database connection pool parameters to optimize for different deployment scales.
|
||||
|
||||
#### Scenario: Custom pool size configuration
|
||||
- **WHEN** the application starts with `DB_POOL_SIZE=20` environment variable
|
||||
- **THEN** the connection pool maintains 20 persistent connections
|
||||
|
||||
#### Scenario: Pool overflow configuration
|
||||
- **WHEN** the application starts with `DB_MAX_OVERFLOW=30` environment variable
|
||||
- **THEN** the connection pool can expand up to 30 additional connections beyond the pool size
|
||||
|
||||
#### Scenario: Pool timeout configuration
|
||||
- **WHEN** all connections are in use and a new request arrives
|
||||
- **AND** `DB_POOL_TIMEOUT=10` is configured
|
||||
- **THEN** the request waits up to 10 seconds for an available connection
|
||||
- **AND** raises an error if no connection becomes available
|
||||
|
||||
#### Scenario: Default configuration
|
||||
- **WHEN** no database pool environment variables are set
|
||||
- **THEN** the system uses production-ready defaults (pool_size=20, max_overflow=30, timeout=10, recycle=1800)
|
||||
|
||||
### Requirement: Message Sender Display Name
|
||||
|
||||
The system SHALL include the sender's display name in message responses and broadcasts, enabling the UI to show user-friendly names instead of email addresses.
|
||||
|
||||
#### Scenario: Message response includes display name
|
||||
- **WHEN** a message is retrieved via REST API or WebSocket
|
||||
- **THEN** the response SHALL include `sender_display_name` field
|
||||
- **AND** the display name SHALL be obtained by joining with the `tr_users` table
|
||||
- **AND** if the sender does not exist in `tr_users`, the field SHALL fallback to `sender_id`
|
||||
|
||||
#### Scenario: WebSocket broadcast includes display name
|
||||
- **WHEN** a new message is broadcast via WebSocket
|
||||
- **THEN** the broadcast SHALL include `sender_display_name` field
|
||||
- **AND** the value SHALL be the sender's display name from `tr_users` table
|
||||
|
||||
#### Scenario: Historical messages include display name
|
||||
- **WHEN** a client requests message history via `GET /api/rooms/{room_id}/messages`
|
||||
- **THEN** each message in the response SHALL include `sender_display_name`
|
||||
- **AND** messages from unknown users SHALL show their `sender_id` as fallback
|
||||
|
||||
### Requirement: GMT+8 Timezone Display
|
||||
|
||||
The frontend SHALL display all timestamps in GMT+8 (Asia/Taipei) timezone for consistent user experience across all browsers.
|
||||
|
||||
#### Scenario: Message timestamp in GMT+8
|
||||
- **WHEN** a message is displayed in the chat room
|
||||
- **THEN** the timestamp SHALL be formatted in GMT+8 timezone
|
||||
- **AND** use format "HH:mm" for today's messages
|
||||
- **AND** use format "MM/DD HH:mm" for older messages
|
||||
|
||||
#### Scenario: Room list timestamps in GMT+8
|
||||
- **WHEN** the room list is displayed
|
||||
- **THEN** the "last updated" time SHALL be formatted in GMT+8 timezone
|
||||
|
||||
### Requirement: @Mention Support
|
||||
The messaging system SHALL support @mention functionality to tag specific users in messages.
|
||||
|
||||
#### Scenario: Trigger mention autocomplete
|
||||
- **WHEN** user types `@` in the message input
|
||||
- **THEN** a dropdown menu appears showing room members
|
||||
- **AND** the list filters as user continues typing
|
||||
- **AND** user can select a member using keyboard or mouse
|
||||
|
||||
#### Scenario: Insert mention into message
|
||||
- **WHEN** user selects a member from the mention dropdown
|
||||
- **THEN** the mention is inserted as `@display_name`
|
||||
- **AND** the mention is stored with the user_id reference
|
||||
- **AND** the mention is visually highlighted in the message
|
||||
|
||||
#### Scenario: Mention notification
|
||||
- **WHEN** a message containing @mention is sent
|
||||
- **THEN** the mentioned user receives a highlighted notification
|
||||
- **AND** the notification indicates they were mentioned
|
||||
|
||||
### Requirement: Browser Push Notifications
|
||||
The system SHALL support browser push notifications for new messages.
|
||||
|
||||
#### Scenario: Request notification permission
|
||||
- **WHEN** user first visits the chat room
|
||||
- **THEN** the system prompts for notification permission
|
||||
- **AND** the permission state is stored locally
|
||||
|
||||
#### Scenario: Send push notification
|
||||
- **WHEN** a new message arrives while the tab is not focused
|
||||
- **AND** user has granted notification permission
|
||||
- **THEN** a browser push notification is displayed
|
||||
- **AND** clicking the notification focuses the chat room
|
||||
|
||||
### Requirement: Sound and Vibration Alerts
|
||||
The system SHALL support audio and haptic feedback for new messages.
|
||||
|
||||
#### Scenario: Play notification sound
|
||||
- **WHEN** a new message arrives
|
||||
- **AND** sound notifications are enabled
|
||||
- **THEN** a notification sound is played
|
||||
|
||||
#### Scenario: Vibrate on mobile
|
||||
- **WHEN** a new message arrives on a mobile device
|
||||
- **AND** vibration is enabled
|
||||
- **AND** the device supports Vibration API
|
||||
- **THEN** the device vibrates briefly
|
||||
|
||||
### Requirement: Mention Data Storage
|
||||
Messages with @mentions SHALL store the mention metadata for querying.
|
||||
|
||||
#### Scenario: Store mention references
|
||||
- **WHEN** a message with @mentions is created
|
||||
- **THEN** the `mentions` field stores an array of mentioned user_ids
|
||||
- **AND** the message content preserves the @display_name format
|
||||
|
||||
#### Scenario: Query messages mentioning user
|
||||
- **WHEN** fetching messages that mention a specific user
|
||||
- **THEN** messages with that user_id in `mentions` array are returned
|
||||
|
||||
|
||||
Reference in New Issue
Block a user