diff --git a/packages/admin/src/api/repairs.ts b/packages/admin/src/api/repairs.ts index a2d9b71..7a5329a 100644 --- a/packages/admin/src/api/repairs.ts +++ b/packages/admin/src/api/repairs.ts @@ -1,6 +1,6 @@ import { queryOptions } from '@tanstack/react-query' import { api } from '@/lib/api-client' -import type { RepairTicket, RepairLineItem, RepairBatch, RepairServiceTemplate } from '@/types/repair' +import type { RepairTicket, RepairLineItem, RepairBatch, RepairNote, RepairServiceTemplate } from '@/types/repair' import type { PaginatedResponse, PaginationInput } from '@forte/shared/schemas' // --- Repair Tickets --- @@ -89,6 +89,27 @@ export function repairBatchTicketsOptions(batchId: string, params: PaginationInp }) } +// --- Repair Notes --- + +export const repairNoteKeys = { + all: (ticketId: string) => ['repair-tickets', ticketId, 'notes'] as const, +} + +export function repairNoteListOptions(ticketId: string) { + return queryOptions({ + queryKey: repairNoteKeys.all(ticketId), + queryFn: () => api.get<{ data: RepairNote[] }>(`/v1/repair-tickets/${ticketId}/notes`), + enabled: !!ticketId, + }) +} + +export const repairNoteMutations = { + create: (ticketId: string, data: Record) => + api.post(`/v1/repair-tickets/${ticketId}/notes`, data), + delete: (id: string) => + api.del(`/v1/repair-notes/${id}`), +} + // --- Repair Service Templates --- export const repairServiceTemplateKeys = { diff --git a/packages/admin/src/components/repairs/ticket-notes.tsx b/packages/admin/src/components/repairs/ticket-notes.tsx new file mode 100644 index 0000000..db80e19 --- /dev/null +++ b/packages/admin/src/components/repairs/ticket-notes.tsx @@ -0,0 +1,167 @@ +import { useState } from 'react' +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { repairNoteListOptions, repairNoteMutations, repairNoteKeys } from '@/api/repairs' +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 } 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 TicketNotesProps { + ticketId: string +} + +export function TicketNotes({ ticketId }: TicketNotesProps) { + const queryClient = useQueryClient() + const hasPermission = useAuthStore((s) => s.hasPermission) + const [content, setContent] = useState('') + const [visibility, setVisibility] = useState<'internal' | 'customer'>('internal') + + const { data } = useQuery(repairNoteListOptions(ticketId)) + const notes = data?.data ?? [] + + const createMutation = useMutation({ + mutationFn: (data: Record) => repairNoteMutations.create(ticketId, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: repairNoteKeys.all(ticketId) }) + setContent('') + toast.success('Note added') + }, + onError: (err) => toast.error(err.message), + }) + + const deleteMutation = useMutation({ + mutationFn: repairNoteMutations.delete, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: repairNoteKeys.all(ticketId) }) + toast.success('Note removed') + }, + onError: (err) => toast.error(err.message), + }) + + function handleSubmit(e: React.FormEvent) { + e.preventDefault() + if (!content.trim()) return + createMutation.mutate({ content: content.trim(), visibility }) + } + + 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') && ( +
+