import { useState, useRef } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { queryOptions } from '@tanstack/react-query' import { repairNoteListOptions, repairNoteMutations, repairNoteKeys } from '@/api/repairs' import { api } from '@/lib/api-client' import { useAuthStore } from '@/stores/auth.store' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Badge } from '@/components/ui/badge' import { Send, Trash2, Eye, Lock, ImageIcon, X } from 'lucide-react' import { toast } from 'sonner' import type { RepairNote } from '@/types/repair' const STATUS_LABELS: Record = { in_transit: 'In Transit', intake: 'Intake', diagnosing: 'Diagnosing', pending_approval: 'Pending Approval', approved: 'Approved', in_progress: 'In Progress', pending_parts: 'Pending Parts', ready: 'Ready', picked_up: 'Picked Up', delivered: 'Delivered', cancelled: 'Cancelled', } interface FileRecord { id: string path: string filename: string } function noteFilesOptions(noteId: string) { return queryOptions({ queryKey: ['files', 'repair_note', noteId], queryFn: () => api.get<{ data: FileRecord[] }>('/v1/files', { entityType: 'repair_note', entityId: noteId }), enabled: !!noteId, }) } async function openSignedFile(fileId: string) { try { const res = await api.get<{ url: string }>(`/v1/files/signed-url/${fileId}`) window.open(res.url, '_blank') } catch { toast.error('Failed to open file') } } interface TicketNotesProps { ticketId: string } export function TicketNotes({ ticketId }: TicketNotesProps) { const queryClient = useQueryClient() const hasPermission = useAuthStore((s) => s.hasPermission) const token = useAuthStore((s) => s.token) const [content, setContent] = useState('') const [visibility, setVisibility] = useState<'internal' | 'customer'>('internal') const [photos, setPhotos] = useState([]) const [posting, setPosting] = useState(false) const photoInputRef = useRef(null) const { data } = useQuery(repairNoteListOptions(ticketId)) const notes = data?.data ?? [] const deleteMutation = useMutation({ mutationFn: repairNoteMutations.delete, onSuccess: () => { queryClient.invalidateQueries({ queryKey: repairNoteKeys.all(ticketId) }) toast.success('Note removed') }, onError: (err) => toast.error(err.message), }) async function handleSubmit(e: React.FormEvent) { e.preventDefault() if (!content.trim()) return setPosting(true) try { // Create the note const note = await repairNoteMutations.create(ticketId, { content: content.trim(), visibility }) // Upload attached photos to the note for (const photo of photos) { const formData = new FormData() formData.append('file', photo) formData.append('entityType', 'repair_note') formData.append('entityId', note.id) formData.append('category', 'attachment') await fetch('/v1/files', { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData, }) } queryClient.invalidateQueries({ queryKey: repairNoteKeys.all(ticketId) }) setContent('') setPhotos([]) toast.success('Note added') } catch (err) { toast.error(err instanceof Error ? err.message : 'Failed to post note') } finally { setPosting(false) } } function addPhotos(e: React.ChangeEvent) { const files = Array.from(e.target.files ?? []) setPhotos((prev) => [...prev, ...files]) e.target.value = '' } function removePhoto(index: number) { setPhotos((prev) => prev.filter((_, i) => i !== index)) } function formatDate(dateStr: string) { const d = new Date(dateStr) return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + d.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }) } return (
{/* Add note form */} {hasPermission('repairs.edit') && (