From 8614e3f0f197bc64894dbce3cf8b90c23a104068 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Tue, 3 Mar 2026 07:03:27 -0800 Subject: [PATCH] fix(feedback): address 6 bugs/gaps from systematic review - Fix search icon positioning (add relative container) - Fix toast variant from 'destructive' to 'error' - Remove non-existent screenshotUrl fields from delete endpoint - Fix misleading blob path comment (blob stays at initial location) - Add download screenshot function and button - Add pointer-events-none to search icon --- .../src/app/(dashboard)/feedback/page.tsx | 40 +++++++++++++++---- .../src/modules/feedback/routes.ts | 11 ++--- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/dashboards/admin-web/src/app/(dashboard)/feedback/page.tsx b/dashboards/admin-web/src/app/(dashboard)/feedback/page.tsx index 0fdfdea8..8b4e49b1 100644 --- a/dashboards/admin-web/src/app/(dashboard)/feedback/page.tsx +++ b/dashboards/admin-web/src/app/(dashboard)/feedback/page.tsx @@ -12,6 +12,7 @@ import { Trash2, ImageIcon, Loader2, + Download, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; @@ -104,7 +105,7 @@ export default function FeedbackPage() { const data = await res.json(); setFeedback(data.items || []); } catch (err) { - toast({ title: 'Error', description: 'Failed to load feedback', variant: 'destructive' }); + toast({ title: 'Error', description: 'Failed to load feedback', variant: 'error' }); } finally { setLoading(false); } @@ -118,7 +119,7 @@ export default function FeedbackPage() { setScreenshotUrl(data.url); setLightboxOpen(true); } catch (err) { - toast({ title: 'Error', description: 'Failed to load screenshot', variant: 'destructive' }); + toast({ title: 'Error', description: 'Failed to load screenshot', variant: 'error' }); } } @@ -130,7 +131,25 @@ export default function FeedbackPage() { toast({ title: 'Success', description: 'Screenshot deleted' }); fetchFeedback(); } catch (err) { - toast({ title: 'Error', description: 'Failed to delete screenshot', variant: 'destructive' }); + toast({ title: 'Error', description: 'Failed to delete screenshot', variant: 'error' }); + } + } + + async function downloadScreenshot(feedbackId: string, contentType?: string) { + try { + const res = await fetch(`/api/feedback/${feedbackId}/screenshot`); + if (!res.ok) throw new Error('Failed to fetch screenshot'); + const data = await res.json(); + + // Create temporary link to download + const link = document.createElement('a'); + link.href = data.url; + link.download = `screenshot-${feedbackId}.${contentType?.split('/')[1] || 'png'}`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } catch (err) { + toast({ title: 'Error', description: 'Failed to download screenshot', variant: 'error' }); } } @@ -157,8 +176,8 @@ export default function FeedbackPage() {
-
- +
+ viewScreenshot(selectedFeedback.id)} > - View Screenshot + View + +
diff --git a/services/platform-service/src/modules/feedback/routes.ts b/services/platform-service/src/modules/feedback/routes.ts index bd908077..aa1235eb 100644 --- a/services/platform-service/src/modules/feedback/routes.ts +++ b/services/platform-service/src/modules/feedback/routes.ts @@ -95,10 +95,9 @@ export async function feedbackRoutes(app: FastifyInstance): Promise { throw new BadRequestError('Invalid contentType. Must be image/png, image/jpeg, or image/webp'); } - // Generate blob path (feedbackId will be assigned after creation) - // For pre-upload, we use a temp path that gets moved on feedback submission - const tempFeedbackId = `temp_${userId}_${Date.now()}`; - const blobPath = generateScreenshotBlobPath(productId, tempFeedbackId, body.contentType); + // Generate blob path with user-specific prefix for organization + // Note: Blob stays at this location - we don't move it after feedback creation + const blobPath = generateScreenshotBlobPath(productId, userId, body.contentType); // Generate SAS URL for upload (5 minutes expiry) const uploadUrl = await generateSasUrl( @@ -155,10 +154,8 @@ export async function feedbackRoutes(app: FastifyInstance): Promise { } // Update feedback to remove screenshot reference - const updated = await updateFeedback(req.params.id, productId, { + await updateFeedback(req.params.id, productId, { screenshotBlobPath: undefined, - screenshotUrl: undefined, - screenshotUrlExpiresAt: undefined, screenshotContentType: undefined, screenshotSizeBytes: undefined, } as unknown as import('./types.js').UpdateFeedbackInput);