## Overview
Add support for file attachments in chat messages. Users should be able to attach up to 10 files per message (with or without text content), with inline image previews displayed in the chat UI.
## Current Architecture
### Backend
- `ChatMessage` model at [app/models/chat_message.rb](app/models/chat_message.rb) - text-only, has `metadata` JSONB column
- `Chats::SendMessageService` at [app/services/chats/send_message_service.rb](app/services/chats/send_message_service.rb) - handles message creation
- `ChatMessagesController` at [app/controllers/chat_messages_controller.rb](app/controllers/chat_messages_controller.rb) - API endpoints
- Real-time broadcast via NATS (messages include `to_nats_payload` method)
### Frontend
- `ChatRoom.jsx` at [app/javascript/components/chat/ChatRoom.jsx](app/javascript/components/chat/ChatRoom.jsx) - React chat UI via turbo-mount
- `chat_service.js` at [app/javascript/services/chat_service.js](app/javascript/services/chat_service.js) - NATS subscription and message sending
### Reference Implementations
- `Contacts::AttachFilesService` at [app/services/contacts/attach_files_service.rb](app/services/contacts/attach_files_service.rb) - file validation pattern
- `Contact` model uses `has_many_attached :files` - ActiveStorage pattern
- `TenantFileService` at [app/services/tenant_file_service.rb](app/services/tenant_file_service.rb) - tenant-aware uploads
## Implementation Requirements
### Backend Changes
1. **Model Update** - Add ActiveStorage attachment to `ChatMessage`:
```ruby
has_many_attached :files
```
2. **Migration** - No schema change needed (ActiveStorage handles attachments table)
3. **Service Update** - Modify `Chats::SendMessageService`:
- Accept optional `files` parameter (array, max 10)
- Validate files using same pattern as `Contacts::AttachFilesService`:
- Max 500MB per file
- Blocked extensions: `.exe, .js, .py, .bat, .cmd, .sh, .dll, .jar, .vbs, .msi`
- Blocked content types for executables
- Use `TenantFileService.upload` for tenant-aware storage
- Allow content to be blank if files are present
4. **Controller Update** - Modify `ChatMessagesController#create`:
- Accept multipart form data with files
- Pass files to service
5. **Payload Update** - Extend `ChatMessage#to_nats_payload`:
```ruby
{
# ... existing fields ...
attachments: files.map do |file|
{
id: file.id,
filename: file.filename.to_s,
content_type: file.content_type,
byte_size: file.byte_size,
url: url_for(file),
is_image: file.content_type&.start_with?('image/')
}
end
}
```
### Frontend Changes
1. **ChatRoom.jsx Updates**:
- Add file input (hidden) triggered by attachment button
- Add drag-and-drop zone over chat area
- Show file previews before sending (thumbnails for images, file icons for others)
- Display attachment count/limit indicator
- Update `handleSendMessage` to use FormData for multipart upload
2. **Message Component Updates**:
- Render attachments in message bubbles
- Image attachments: inline thumbnail preview (clickable to open full size)
- Other files: file icon + filename + size (clickable to download)
3. **chat_service.js Updates**:
- Modify `sendMessage` to accept optional files array
- Use FormData instead of JSON when files present
### UI/UX Requirements
- Attachment button (paperclip icon) next to send button
- Drag-and-drop visual feedback (highlight drop zone)
- File preview area between message input and file selector
- Remove individual files from selection before sending
- Loading indicator during upload
- Error handling for failed uploads
## Validation Rules
- Max 10 files per message
- Max 500MB per file
- Blocked dangerous file types (see blocklist above)
- Message can have: text only, files only, or both
## Overview
Add support for file attachments in chat messages. Users should be able to attach up to 10 files per message (with or without text content), with inline image previews displayed in the chat UI.
## Current Architecture
### Backend
- `ChatMessage` model at [app/models/chat_message.rb](app/models/chat_message.rb) - text-only, has `metadata` JSONB column
- `Chats::SendMessageService` at [app/services/chats/send_message_service.rb](app/services/chats/send_message_service.rb) - handles message creation
- `ChatMessagesController` at [app/controllers/chat_messages_controller.rb](app/controllers/chat_messages_controller.rb) - API endpoints
- Real-time broadcast via NATS (messages include `to_nats_payload` method)
### Frontend
- `ChatRoom.jsx` at [app/javascript/components/chat/ChatRoom.jsx](app/javascript/components/chat/ChatRoom.jsx) - React chat UI via turbo-mount
- `chat_service.js` at [app/javascript/services/chat_service.js](app/javascript/services/chat_service.js) - NATS subscription and message sending
### Reference Implementations
- `Contacts::AttachFilesService` at [app/services/contacts/attach_files_service.rb](app/services/contacts/attach_files_service.rb) - file validation pattern
- `Contact` model uses `has_many_attached :files` - ActiveStorage pattern
- `TenantFileService` at [app/services/tenant_file_service.rb](app/services/tenant_file_service.rb) - tenant-aware uploads
## Implementation Requirements
### Backend Changes
1. **Model Update** - Add ActiveStorage attachment to `ChatMessage`:
```ruby
has_many_attached :files
```
2. **Migration** - No schema change needed (ActiveStorage handles attachments table)
3. **Service Update** - Modify `Chats::SendMessageService`:
- Accept optional `files` parameter (array, max 10)
- Validate files using same pattern as `Contacts::AttachFilesService`:
- Max 500MB per file
- Blocked extensions: `.exe, .js, .py, .bat, .cmd, .sh, .dll, .jar, .vbs, .msi`
- Blocked content types for executables
- Use `TenantFileService.upload` for tenant-aware storage
- Allow content to be blank if files are present
4. **Controller Update** - Modify `ChatMessagesController#create`:
- Accept multipart form data with files
- Pass files to service
5. **Payload Update** - Extend `ChatMessage#to_nats_payload`:
```ruby
{
# ... existing fields ...
attachments: files.map do |file|
{
id: file.id,
filename: file.filename.to_s,
content_type: file.content_type,
byte_size: file.byte_size,
url: url_for(file),
is_image: file.content_type&.start_with?('image/')
}
end
}
```
### Frontend Changes
1. **ChatRoom.jsx Updates**:
- Add file input (hidden) triggered by attachment button
- Add drag-and-drop zone over chat area
- Show file previews before sending (thumbnails for images, file icons for others)
- Display attachment count/limit indicator
- Update `handleSendMessage` to use FormData for multipart upload
2. **Message Component Updates**:
- Render attachments in message bubbles
- Image attachments: inline thumbnail preview (clickable to open full size)
- Other files: file icon + filename + size (clickable to download)
3. **chat_service.js Updates**:
- Modify `sendMessage` to accept optional files array
- Use FormData instead of JSON when files present
### UI/UX Requirements
- Attachment button (paperclip icon) next to send button
- Drag-and-drop visual feedback (highlight drop zone)
- File preview area between message input and file selector
- Remove individual files from selection before sending
- Loading indicator during upload
- Error handling for failed uploads
## Validation Rules
- Max 10 files per message
- Max 500MB per file
- Blocked dangerous file types (see blocklist above)
- Message can have: text only, files only, or both