feat: Improve file display, timezone handling, and LOT management
Changes: - Fix datetime serialization with UTC 'Z' suffix for correct timezone display - Add PDF upload support with extension fallback for MIME detection - Fix LOT add/remove by creating new list for SQLAlchemy JSON change detection - Add file message components (FileMessage, ImageLightbox, UploadPreview) - Add multi-file upload support with progress tracking - Link uploaded files to chat messages via message_id - Include file attachments in AI report generation - Update specs for file-storage, realtime-messaging, and ai-report-generation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -50,7 +50,7 @@ The system SHALL maintain a permanent `users` table to store user display names
|
||||
|
||||
### Requirement: Report Data Collection
|
||||
|
||||
The system SHALL collect all relevant room data for AI processing, including messages, members, files, and room metadata.
|
||||
The system SHALL collect all relevant room data for AI processing, including messages, members, files with their conversation context, and room metadata.
|
||||
|
||||
#### Scenario: Collect complete room data for report generation
|
||||
|
||||
@@ -62,9 +62,29 @@ The system SHALL collect all relevant room data for AI processing, including mes
|
||||
- Room metadata (title, incident_type, severity, status, location, description, timestamps)
|
||||
- All 50 messages sorted by created_at ascending
|
||||
- All 5 members with their roles (owner, editor, viewer)
|
||||
- All 3 files with metadata (filename, type, uploader, upload time)
|
||||
- All 3 files with metadata (filename, type, uploader, upload time) AND their associated message context
|
||||
- **AND** messages SHALL include sender display name (not just user_id)
|
||||
- **AND** file references in messages SHALL be annotated as "[附件: filename.ext]"
|
||||
- **AND** file references in messages SHALL be annotated with surrounding context
|
||||
|
||||
#### Scenario: Include file context in report data
|
||||
- **GIVEN** a file "defect_photo.jpg" was uploaded with the message "發現產品表面瑕疵"
|
||||
- **AND** the previous message was "Line 3 溫度異常升高中"
|
||||
- **AND** the next message was "已通知維修人員處理"
|
||||
- **WHEN** report data is collected
|
||||
- **THEN** the file entry SHALL include:
|
||||
```json
|
||||
{
|
||||
"file_id": "...",
|
||||
"filename": "defect_photo.jpg",
|
||||
"uploader_display_name": "陳工程師",
|
||||
"uploaded_at": "2025-12-08T14:30:00+08:00",
|
||||
"caption": "發現產品表面瑕疵",
|
||||
"context_before": "Line 3 溫度異常升高中",
|
||||
"context_after": "已通知維修人員處理"
|
||||
}
|
||||
```
|
||||
- **AND** the AI prompt SHALL format files as:
|
||||
`[附件: defect_photo.jpg] - 上傳者: 陳工程師 (14:30), 說明: "發現產品表面瑕疵" (前文: "Line 3 溫度異常升高中")`
|
||||
|
||||
#### Scenario: Handle room with no messages
|
||||
|
||||
@@ -82,8 +102,6 @@ The system SHALL collect all relevant room data for AI processing, including mes
|
||||
- **AND** summarize older messages by day (e.g., "2025-12-01: 45 則訊息討論設備檢修")
|
||||
- **AND** the total formatted content SHALL stay within token limits
|
||||
|
||||
---
|
||||
|
||||
### Requirement: DIFY AI Integration
|
||||
|
||||
The system SHALL integrate with DIFY Chat API to generate structured report content from collected room data.
|
||||
@@ -126,7 +144,7 @@ The system SHALL integrate with DIFY Chat API to generate structured report cont
|
||||
|
||||
### Requirement: Document Assembly
|
||||
|
||||
The system SHALL assemble professional .docx documents from AI-generated content with embedded images from MinIO.
|
||||
The system SHALL assemble professional .docx documents from AI-generated content with embedded images from MinIO and file context from conversations.
|
||||
|
||||
#### Scenario: Generate complete report document
|
||||
|
||||
@@ -142,8 +160,9 @@ The system SHALL assemble professional .docx documents from AI-generated content
|
||||
- Section 4: 處理過程 (from AI resolution_process.content)
|
||||
- Section 5: 目前狀態 (from AI current_status)
|
||||
- Section 6: 最終處置結果 (from AI final_resolution, if has_resolution=true)
|
||||
- Section 7: 附件 (embedded images + file list)
|
||||
- Section 7: 附件 (embedded images with captions + file list with context)
|
||||
- **AND** images SHALL be embedded at appropriate size (max width 15cm)
|
||||
- **AND** each image SHALL include its caption from the upload message
|
||||
- **AND** document SHALL use professional formatting (標楷體 or similar)
|
||||
|
||||
#### Scenario: Handle missing images during assembly
|
||||
@@ -163,8 +182,6 @@ The system SHALL assemble professional .docx documents from AI-generated content
|
||||
- **THEN** the system SHALL create a complete document without the embedded images section
|
||||
- **AND** the attachments section SHALL show "本事件無附件檔案" if no files exist
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Report Generation API
|
||||
|
||||
The system SHALL provide REST API endpoints for triggering report generation and downloading generated reports.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
TBD - created by archiving change add-file-upload-minio. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: File Upload with Validation
|
||||
The system SHALL accept multipart file uploads to incident rooms, validate file type and size, and persist files to MinIO object storage with metadata tracking in PostgreSQL.
|
||||
The system SHALL accept multipart file uploads to incident rooms, validate file type and size, persist files to MinIO object storage with metadata tracking in PostgreSQL, AND create an associated message in the chat for context.
|
||||
|
||||
#### Scenario: Upload image to incident room
|
||||
- **WHEN** a user with OWNER or EDITOR role uploads an image file via `POST /api/rooms/{room_id}/files`
|
||||
@@ -20,11 +20,19 @@ The system SHALL accept multipart file uploads to incident rooms, validate file
|
||||
- Validate JWT token and extract user_id
|
||||
- Verify user is member of room-123 with OWNER or EDITOR role
|
||||
- Validate file MIME type is image/jpeg, image/png, or image/gif
|
||||
- Validate file size ≤ 10MB
|
||||
- Validate file size <= 10MB
|
||||
- Generate unique file_id (UUID)
|
||||
- Upload file to MinIO bucket `task-reporter-files` at path `room-123/images/{file_id}.jpg`
|
||||
- Create database record in `room_files` table
|
||||
- Return file metadata with presigned download URL (1-hour expiry)
|
||||
- Create a message with `message_type=image_ref` containing file metadata
|
||||
- Create database record in `room_files` table with `message_id` reference
|
||||
- Return file metadata with presigned download URL (1-hour expiry) and message_id
|
||||
|
||||
#### Scenario: Upload document to incident room
|
||||
- **WHEN** a user uploads a non-image file (PDF, log, etc.)
|
||||
- **THEN** the system SHALL:
|
||||
- Create a message with `message_type=file_ref`
|
||||
- Store message_id in the file record
|
||||
- Return file metadata with message_id
|
||||
|
||||
#### Scenario: Reject oversized file upload
|
||||
- **WHEN** a user attempts to upload a 15MB PDF file
|
||||
@@ -34,6 +42,7 @@ The system SHALL accept multipart file uploads to incident rooms, validate file
|
||||
- Include error message: "File size exceeds limit: 15MB > 20MB"
|
||||
- NOT upload file to MinIO
|
||||
- NOT create database record
|
||||
- NOT create chat message
|
||||
|
||||
#### Scenario: Reject unauthorized file type
|
||||
- **WHEN** a user attempts to upload an executable file (e.g., .exe, .sh, .bat)
|
||||
@@ -57,7 +66,8 @@ The system SHALL accept multipart file uploads to incident rooms, validate file
|
||||
- Validate MIME type is text/plain
|
||||
- Upload to MinIO at `room-456/logs/{file_id}.log`
|
||||
- Store metadata with file_type='log'
|
||||
- Return success response with file_id
|
||||
- Create a message with message_type='file_ref'
|
||||
- Return success response with file_id and message_id
|
||||
|
||||
### Requirement: File Download with Access Control
|
||||
The system SHALL generate time-limited presigned download URLs for files, enforcing room membership-based access control.
|
||||
@@ -264,3 +274,39 @@ The system SHALL validate file types using MIME type detection (not just file ex
|
||||
- NOT upload to MinIO
|
||||
- Log security event
|
||||
|
||||
### Requirement: File-Message Association
|
||||
The system SHALL maintain a foreign key relationship between uploaded files and their associated chat messages, enabling contextual display of files in conversations.
|
||||
|
||||
#### Scenario: Query file with associated message
|
||||
- **WHEN** a client requests file metadata via `GET /api/rooms/{room_id}/files/{file_id}`
|
||||
- **THEN** the response SHALL include:
|
||||
```json
|
||||
{
|
||||
"file_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"message_id": "msg-789",
|
||||
"filename": "defect.jpg",
|
||||
"file_type": "image",
|
||||
"download_url": "...",
|
||||
"uploaded_at": "2025-12-08T10:30:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: Delete file cascades to message
|
||||
- **WHEN** a file is soft-deleted via `DELETE /api/rooms/{room_id}/files/{file_id}`
|
||||
- **THEN** the system SHALL also soft-delete the associated message
|
||||
- **AND** broadcast both `file_deleted` and `message_deleted` events
|
||||
|
||||
### Requirement: Image Thumbnail URLs
|
||||
The system SHALL generate presigned URLs suitable for thumbnail display, allowing frontends to efficiently render image previews.
|
||||
|
||||
#### Scenario: File metadata includes thumbnail URL
|
||||
- **WHEN** file metadata is returned for an image file
|
||||
- **THEN** the response SHALL include a `thumbnail_url` field
|
||||
- **AND** the URL SHALL be a presigned MinIO URL valid for 1 hour
|
||||
- **AND** the frontend SHALL use CSS to constrain thumbnail display size
|
||||
|
||||
#### Scenario: Non-image files have no thumbnail
|
||||
- **WHEN** file metadata is returned for a non-image file (PDF, log, etc.)
|
||||
- **THEN** the response SHALL NOT include a `thumbnail_url` field
|
||||
- **AND** the frontend SHALL display a file-type icon instead
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ The system SHALL persist all messages to database for audit trail, report genera
|
||||
- **AND** maintain user's access control (only rooms they're members of)
|
||||
|
||||
### Requirement: Message Types and Formatting
|
||||
The system SHALL support various message types including text, image references, file references, and structured data for production incidents.
|
||||
The system SHALL support various message types including text, image references, file references, and structured data for production incidents, with inline display of file attachments in the chat view.
|
||||
|
||||
#### Scenario: Text message with mentions
|
||||
- **WHEN** a user sends a message with @mentions
|
||||
@@ -103,19 +103,24 @@ The system SHALL support various message types including text, image references,
|
||||
- **THEN** the system SHALL parse and store mentions
|
||||
- **AND** potentially trigger notifications to mentioned users
|
||||
|
||||
#### Scenario: Image reference message
|
||||
- **WHEN** a user uploads an image and sends reference
|
||||
```json
|
||||
{
|
||||
"type": "message",
|
||||
"message_type": "image_ref",
|
||||
"content": "Defect found on product",
|
||||
"file_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"file_url": "http://localhost:9000/bucket/room-123/image.jpg"
|
||||
}
|
||||
```
|
||||
- **THEN** the system SHALL store the file reference
|
||||
- **AND** clients SHALL display image preview inline
|
||||
#### Scenario: Image reference message display
|
||||
- **WHEN** a message with `message_type=image_ref` is rendered in the chat
|
||||
- **THEN** the client SHALL display:
|
||||
- A thumbnail of the image (max 300px width)
|
||||
- The message content/caption below the image
|
||||
- Sender name and timestamp
|
||||
- A click-to-expand functionality
|
||||
- **AND** clicking the thumbnail SHALL open a full-size preview lightbox
|
||||
|
||||
#### Scenario: File reference message display
|
||||
- **WHEN** a message with `message_type=file_ref` is rendered in the chat
|
||||
- **THEN** the client SHALL display:
|
||||
- A file type icon (PDF, document, log, etc.)
|
||||
- The filename
|
||||
- File size in human-readable format
|
||||
- A download button/link
|
||||
- The message content/caption
|
||||
- Sender name and timestamp
|
||||
|
||||
#### Scenario: Structured incident data
|
||||
- **WHEN** reporting specific incident metrics
|
||||
@@ -265,7 +270,7 @@ The system SHALL include the sender's display name in message responses and broa
|
||||
|
||||
### Requirement: GMT+8 Timezone Display
|
||||
|
||||
The frontend SHALL display all timestamps in GMT+8 (Asia/Taipei) timezone for consistent user experience across all browsers.
|
||||
The frontend SHALL display all timestamps in GMT+8 (Asia/Taipei) timezone for consistent user experience across all browsers and all parts of the application.
|
||||
|
||||
#### Scenario: Message timestamp in GMT+8
|
||||
- **WHEN** a message is displayed in the chat room
|
||||
@@ -277,6 +282,14 @@ The frontend SHALL display all timestamps in GMT+8 (Asia/Taipei) timezone for co
|
||||
- **WHEN** the room list is displayed
|
||||
- **THEN** the "last updated" time SHALL be formatted in GMT+8 timezone
|
||||
|
||||
#### Scenario: File upload timestamp in GMT+8
|
||||
- **WHEN** a file is displayed in chat or file drawer
|
||||
- **THEN** the upload timestamp SHALL be formatted in GMT+8 timezone
|
||||
|
||||
#### Scenario: Report generation timestamp in GMT+8
|
||||
- **WHEN** report metadata is displayed
|
||||
- **THEN** the "generated at" timestamp SHALL be formatted in GMT+8 timezone
|
||||
|
||||
### Requirement: @Mention Support
|
||||
The messaging system SHALL support @mention functionality to tag specific users in messages.
|
||||
|
||||
@@ -337,3 +350,61 @@ Messages with @mentions SHALL store the mention metadata for querying.
|
||||
- **WHEN** fetching messages that mention a specific user
|
||||
- **THEN** messages with that user_id in `mentions` array are returned
|
||||
|
||||
### Requirement: Image Preview Lightbox
|
||||
The frontend SHALL provide a lightbox component for viewing full-size images from chat messages.
|
||||
|
||||
#### Scenario: Open image lightbox
|
||||
- **WHEN** user clicks on an image thumbnail in the chat
|
||||
- **THEN** a modal overlay SHALL appear
|
||||
- **AND** the full-size image SHALL be displayed centered
|
||||
- **AND** a loading indicator SHALL show while image loads
|
||||
- **AND** the image SHALL be constrained to fit the viewport
|
||||
|
||||
#### Scenario: Close image lightbox
|
||||
- **WHEN** the lightbox is open
|
||||
- **THEN** user can close it by:
|
||||
- Clicking the X button
|
||||
- Pressing the ESC key
|
||||
- Clicking outside the image
|
||||
- **AND** focus SHALL return to the chat
|
||||
|
||||
#### Scenario: Image lightbox with download
|
||||
- **WHEN** the lightbox is open
|
||||
- **THEN** a download button SHALL be visible
|
||||
- **AND** clicking it SHALL download the original file
|
||||
|
||||
### Requirement: File Type Icons
|
||||
The frontend SHALL display appropriate icons for different file types in chat messages and file drawer.
|
||||
|
||||
#### Scenario: PDF file icon
|
||||
- **WHEN** a PDF file is displayed
|
||||
- **THEN** a PDF icon (red/document style) SHALL be shown
|
||||
|
||||
#### Scenario: Log/text file icon
|
||||
- **WHEN** a .log or .txt file is displayed
|
||||
- **THEN** a text file icon SHALL be shown
|
||||
|
||||
#### Scenario: Excel file icon
|
||||
- **WHEN** an Excel file (.xlsx, .xls) is displayed
|
||||
- **THEN** a spreadsheet icon (green) SHALL be shown
|
||||
|
||||
#### Scenario: Generic file icon
|
||||
- **WHEN** a file with unknown type is displayed
|
||||
- **THEN** a generic document icon SHALL be shown
|
||||
|
||||
### Requirement: Upload Preview
|
||||
The frontend SHALL show a preview of the file being uploaded before the message is sent.
|
||||
|
||||
#### Scenario: Image upload preview
|
||||
- **WHEN** user selects an image file for upload
|
||||
- **THEN** a preview thumbnail SHALL be displayed in the input area
|
||||
- **AND** user can add a caption/description
|
||||
- **AND** user can cancel the upload before sending
|
||||
- **AND** a send button confirms the upload
|
||||
|
||||
#### Scenario: File upload preview
|
||||
- **WHEN** user selects a non-image file for upload
|
||||
- **THEN** file info (name, size, type icon) SHALL be displayed
|
||||
- **AND** user can add a description
|
||||
- **AND** user can cancel or confirm
|
||||
|
||||
|
||||
Reference in New Issue
Block a user