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
This commit is contained in:
saravanakumardb1 2026-03-03 07:03:27 -08:00
parent 439610cbe5
commit 8614e3f0f1
2 changed files with 37 additions and 14 deletions

View File

@ -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() {
<Card className="mb-6">
<CardContent className="pt-6">
<div className="flex flex-wrap gap-4">
<div className="flex-1 min-w-[200px]">
<Search className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<div className="flex-1 min-w-[200px] relative">
<Search className="absolute left-3 top-3 h-4 w-4 text-muted-foreground pointer-events-none" />
<Input
placeholder="Search feedback..."
value={search}
@ -313,14 +332,21 @@ export default function FeedbackPage() {
onClick={() => viewScreenshot(selectedFeedback.id)}
>
<Eye className="h-4 w-4 mr-2" />
View Screenshot
View
</Button>
<Button
variant="outline"
onClick={() => downloadScreenshot(selectedFeedback.id, selectedFeedback.screenshotContentType)}
>
<Download className="h-4 w-4 mr-2" />
Download
</Button>
<Button
variant="outline"
onClick={() => deleteScreenshot(selectedFeedback.id)}
>
<Trash2 className="h-4 w-4 mr-2 text-red-500" />
Delete Screenshot
Delete
</Button>
</div>
</div>

View File

@ -95,10 +95,9 @@ export async function feedbackRoutes(app: FastifyInstance): Promise<void> {
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<void> {
}
// 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);