From e224b535ce8df6b39fd176e52ddfaac4d5dd9524 Mon Sep 17 00:00:00 2001 From: Ryan Moon Date: Sun, 29 Mar 2026 11:19:39 -0500 Subject: [PATCH] Fix auth for all file thumbnails, add upload error logging All image thumbnails in ticket photos and note attachments now use AuthImage component that fetches via Bearer token. Fixed 401 errors on Photos & Docs tab. Added error logging for note photo uploads to surface upload failures. --- .../src/components/repairs/ticket-notes.tsx | 7 ++++- .../src/components/repairs/ticket-photos.tsx | 30 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/admin/src/components/repairs/ticket-notes.tsx b/packages/admin/src/components/repairs/ticket-notes.tsx index 3358e1f..b53f25b 100644 --- a/packages/admin/src/components/repairs/ticket-notes.tsx +++ b/packages/admin/src/components/repairs/ticket-notes.tsx @@ -116,11 +116,16 @@ export function TicketNotes({ ticketId }: TicketNotesProps) { formData.append('entityType', 'repair_note') formData.append('entityId', note.id) formData.append('category', 'attachment') - await fetch('/v1/files', { + const uploadRes = await fetch('/v1/files', { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData, }) + if (!uploadRes.ok) { + const err = await uploadRes.json().catch(() => ({})) + console.error('Photo upload failed:', err) + toast.error(`Photo upload failed: ${(err as any).error?.message ?? 'Unknown error'}`) + } } queryClient.invalidateQueries({ queryKey: repairNoteKeys.all(ticketId) }) diff --git a/packages/admin/src/components/repairs/ticket-photos.tsx b/packages/admin/src/components/repairs/ticket-photos.tsx index 91f78f4..53c5081 100644 --- a/packages/admin/src/components/repairs/ticket-photos.tsx +++ b/packages/admin/src/components/repairs/ticket-photos.tsx @@ -1,4 +1,4 @@ -import { useRef } from 'react' +import { useRef, useState, useEffect } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { queryOptions } from '@tanstack/react-query' import { api } from '@/lib/api-client' @@ -7,6 +7,30 @@ import { Button } from '@/components/ui/button' import { FileText, ImageIcon, Plus, Trash2 } from 'lucide-react' import { toast } from 'sonner' +function AuthImage({ path, alt, className, onClick }: { path: string; alt: string; className?: string; onClick?: () => void }) { + const token = useAuthStore((s) => s.token) + const [src, setSrc] = useState(null) + + useEffect(() => { + let cancelled = false + async function load() { + try { + const res = await fetch(`/v1/files/serve/${path}`, { + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }) + if (!res.ok || cancelled) return + const blob = await res.blob() + if (!cancelled) setSrc(URL.createObjectURL(blob)) + } catch { /* ignore */ } + } + load() + return () => { cancelled = true } + }, [path, token]) + + if (!src) return
+ return {alt} +} + async function openSignedFile(fileId: string) { try { const res = await api.get<{ url: string }>(`/v1/files/signed-url/${fileId}`) @@ -164,8 +188,8 @@ function PhotoSection({ ) : (