- Add preprocessMarkdown to fix concatenated table rows from AI responses
- Map sources field in loadRoomMessages so Sources footer survives page refresh
- Move ChatSource interface before RoomMessage and add sources field to RoomMessage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nginx requires an explicit resolver when proxy_pass uses a variable.
Also enable proxy_ssl_server_name so TLS SNI works correctly when
proxying to the HTTPS backend on HuggingFace Spaces.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
fix: use nginx reverse proxy to forward /api/* to backend at runtime
HuggingFace Spaces secrets are runtime env vars, not build-time args,
so Vite cannot bake VITE_API_BASE_URL into the bundle from HF secrets.
Instead, nginx proxies /api/* to ${VITE_API_BASE_URL}/api/* using
envsubst at container startup, keeping the frontend using relative paths.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
fix: inject VITE_API_BASE_URL build arg in Dockerfile
Vite bakes env vars at build time. Without passing the HuggingFace
secret as a Docker build arg, VITE_API_BASE_URL compiled to an empty
string, causing all API requests to hit the frontend's own origin.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## API Integration
- Align LoginResponse type with full backend schema (id, fullname, company, role, etc.)
- Fix Login.tsx field mapping: res.data.user_id → res.data.id, res.data.name → res.data.fullname
- Add RoomMessage and RoomDetail interfaces for GET /api/v1/room/{room_id}
- Add getRoom() to fetch room detail with full chat history
- Add deleteRoom() for soft-delete via DELETE /api/v1/room/{room_id}?user_id=...
## Chat History Loading
- Add messagesLoaded flag to ChatRoom interface to track fetch state
- Add useEffect watching currentChatId to auto-load messages when a room is selected
- Covers both auto-selected room on page load and manual sidebar clicks
- Newly created rooms are marked messagesLoaded: true (no fetch needed)
## Delete Room
- deleteChat() now calls deleteRoom API before removing from local state
- If API fails (403/404), deletion is cancelled — room stays in sidebar
- deleteAllChats() calls deleteRoom in parallel via Promise.allSettled
- Deleted rooms disappear…