# Project Thumbnail URL Fix ## Issue Project thumbnails were not displaying correctly due to URL routing mismatches between the frontend and backend. ## Root Cause 1. **Vite proxy configuration** strips the `/api` prefix before forwarding requests to the backend 2. The **backend was returning** thumbnail URLs with `/api` prefix: `/api/files/projects/1/thumbnail` 3. The **frontend was constructing** full URLs like `http://localhost:8000/api/files/projects/1/thumbnail`, bypassing the Vite proxy 4. This caused 404 errors because the backend routes don't have an `/api` prefix ## Solution ### Backend Changes (`backend/routers/projects.py`) Changed all thumbnail URL responses from `/api/files/projects/{id}/thumbnail` to `/files/projects/{id}/thumbnail` in 4 locations: - `list_projects()` endpoint (line ~95) - `get_project()` endpoint (line ~175) - `update_project()` endpoint (line ~282) - `upload_project_thumbnail()` endpoint (line ~1193) This matches the actual backend route structure in `backend/routers/files.py`. ### Frontend Changes #### 1. ProjectThumbnailUpload Component **File**: `frontend/src/components/project/ProjectThumbnailUpload.vue` **Pattern**: Following the task attachment implementation pattern using blob URLs **Changes**: - Added `apiClient` import - Added `thumbnailBlobUrl` ref to store the blob URL - Implemented `loadThumbnail()` function that: - Fetches the thumbnail via `apiClient.get()` with `responseType: 'blob'` - Creates a blob URL with `URL.createObjectURL()` - Properly handles authentication headers through apiClient - Updated `getThumbnailUrl()` to return the blob URL - Added lifecycle hooks: - `onMounted()` to load thumbnail on component mount - `watch()` to reload when `currentThumbnailUrl` prop changes - `onUnmounted()` to clean up blob URL and prevent memory leaks **Benefits**: - Proper authentication handling through apiClient interceptors - Memory-efficient with proper cleanup - Consistent with task attachment implementation - Better error handling #### 2. ProjectsView Component **File**: `frontend/src/views/ProjectsView.vue` **Pattern**: Blob URLs for multiple thumbnails with authentication **Changes**: - Added `apiClient` import - Added `thumbnailBlobUrls` Map to store blob URLs by project ID - Implemented `loadThumbnail()` function to fetch individual thumbnails - Implemented `loadAllThumbnails()` to load thumbnails for all visible projects - Updated `getThumbnailUrl()` to return blob URL from the Map - Added `watch()` to reload thumbnails when projects change - Added `onUnmounted()` to clean up all blob URLs - Removed `loading="lazy"` since we're managing loading explicitly **Why blob URLs?**: - The thumbnail endpoint requires authentication - `` tags don't send auth headers - Blob URLs allow us to fetch with auth via apiClient, then display without auth - Consistent with task attachment implementation ## How It Works Now ### Upload Flow 1. User uploads thumbnail → Backend saves file and returns: `/files/projects/1/thumbnail` 2. Frontend receives URL and stores it 3. Component loads thumbnail via `apiClient.get('/files/projects/1/thumbnail')` with blob response 4. Creates blob URL and displays image ### Display Flow (ProjectsView) 1. Backend returns: `/files/projects/1/thumbnail` 2. Frontend adds `/api` prefix: `/api/files/projects/1/thumbnail` 3. Browser requests: `/api/files/projects/1/thumbnail` 4. Vite proxy strips `/api` and forwards: `/files/projects/1/thumbnail` → `http://localhost:8000/files/projects/1/thumbnail` 5. Backend serves the file correctly ✅ ## Reference Implementation This implementation follows the same pattern as task attachments: - `frontend/src/components/task/AttachmentCard.vue` - `frontend/src/components/task/SubmissionCard.vue` Both use blob URLs with proper lifecycle management for displaying thumbnails with authentication. ## Testing 1. Upload a project thumbnail in project settings 2. Verify it displays in the upload component 3. Navigate to projects list and verify thumbnail displays 4. Refresh page and verify thumbnail persists 5. Remove thumbnail and verify placeholder shows 6. Check browser console for no 404 errors ## Files Modified - `backend/routers/projects.py` - Fixed 4 thumbnail URL responses - `frontend/src/components/project/ProjectThumbnailUpload.vue` - Implemented blob URL pattern - `frontend/src/views/ProjectsView.vue` - Fixed URL transformation