"use client" import { useState, useRef } from "react" import { useMutation, useQueryClient } from "@tanstack/react-query" import { Paperclip, Plus, Trash2, FileIcon, ImageIcon, FileTextIcon } from "lucide-react" import { toast } from "sonner" import { ResourcePage } from "@/shared/data-view/resource-page" import { ColumnHeader } from "@/shared/data-view/table-view" import FormDialog from "@/shared/components/form-dialog" import { InventoryAdjustmentForm } from "@/modules/inventory-adjustments/inventory-adjustment-form" import { Button } from "@/shared/components/ui/button" import { Card, CardContent } from "@/shared/components/ui/card" import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/shared/components/ui/dialog" import { confirm } from "@/shared/components/confirm-dialog" import { useAuthApi } from "@/shared/useApi" import { INVENTORY_ADJUSTMENT_ROUTES } from "@garage/api" import type { InventoryAdjustmentsClient } from "@garage/api" // ── Attachment helpers ── type AttachmentFile = { id: number original_name?: string attachment_path?: string created_at?: string } function getFileIcon(path?: string) { if (!path) return FileIcon const lower = path.toLowerCase() if (/\.(jpg|jpeg|png|gif|webp|svg)$/.test(lower)) return ImageIcon if (/\.pdf$/.test(lower)) return FileTextIcon return FileIcon } // ── Attachments Dialog ── function AttachmentsDialog({ open, adjustmentId, adjustmentRef, onClose, }: { open: boolean adjustmentId: string adjustmentRef: string onClose: () => void }) { const api = useAuthApi() const queryClient = useQueryClient() const fileInputRef = useRef(null) const [isUploading, setIsUploading] = useState(false) const [sessionFiles, setSessionFiles] = useState([]) const queryKey = [INVENTORY_ADJUSTMENT_ROUTES.INDEX, adjustmentId, "attachments"] const deleteMutation = useMutation({ mutationFn: (attachmentId: number) => api.inventoryAdjustments.deleteAttachment(adjustmentId, attachmentId), onSuccess: (_, attachmentId) => { toast.success("Attachment deleted.") setSessionFiles((prev) => prev.filter((f) => f.id !== attachmentId)) queryClient.invalidateQueries({ queryKey }) }, onError: () => toast.error("Failed to delete attachment."), }) const handleDelete = async (attachment: AttachmentFile) => { const confirmed = await confirm({ title: "Delete Attachment", description: `Are you sure you want to delete "${attachment.original_name ?? "this file"}"?`, confirmLabel: "Delete", variant: "destructive", }) if (confirmed) deleteMutation.mutate(attachment.id) } const handleUpload = async (e: React.ChangeEvent) => { const files = e.target.files if (!files || files.length === 0) return setIsUploading(true) const fileArray = Array.from(files) try { const result = await toast.promise( api.inventoryAdjustments.addAttachment(adjustmentId, fileArray), { loading: "Uploading attachment(s)...", success: "Attachment(s) uploaded successfully", error: "Failed to upload attachment(s)", }, ) // Track uploaded files locally for display within this session const now = new Date().toISOString() const uploaded: AttachmentFile[] = fileArray.map((file, i) => ({ id: Date.now() + i, original_name: file.name, attachment_path: file.name, created_at: now, })) setSessionFiles((prev) => [...prev, ...uploaded]) queryClient.invalidateQueries({ queryKey }) } finally { setIsUploading(false) if (fileInputRef.current) fileInputRef.current.value = "" } } const handleClose = () => { setSessionFiles([]) onClose() } return ( !v && handleClose()}> Attachments — {adjustmentRef}
{sessionFiles.length === 0 ? ( No attachments uploaded in this session. Click "Upload Attachment" to add files. ) : (
{sessionFiles.map((attachment) => { const Icon = getFileIcon(attachment.attachment_path) return (
{attachment.original_name} {attachment.created_at && ( {new Date(attachment.created_at).toLocaleDateString()} )}
) })}
)}
) } // ── Page ── export default function InventoryAdjustmentsPage() { const [attachmentTarget, setAttachmentTarget] = useState<{ id: string ref: string } | null>(null) return ( <> pageTitle="Inventory Adjustments" routeKey={INVENTORY_ADJUSTMENT_ROUTES.INDEX} getClient={(api) => api.inventoryAdjustments} headerProps={({ selectedItem, invalidateQuery }) => ({ actions: ( {(resourceId) => ( )} ), })} columns={({ actionsColumn }) => [ { accessorKey: "reference_number", header: ({ column }) => , cell: ({ row }) => (row.original as any).reference_number || "—", }, { accessorKey: "date", header: ({ column }) => , cell: ({ row }) => { const val = (row.original as any).date return val ? new Date(val).toLocaleDateString() : "—" }, }, { accessorKey: "chart_of_account", header: ({ column }) => , cell: ({ row }) => (row.original as any).chart_of_account || "—", }, { accessorKey: "notes", header: ({ column }) => , cell: ({ row }) => { const notes = (row.original as any).notes return notes ? ( {notes} ) : "—" }, }, { accessorKey: "created_at", header: ({ column }) => , cell: ({ row }) => { const val = (row.original as any).created_at return val ? new Date(val).toLocaleDateString() : "—" }, }, { id: "attachments", header: () => null, cell: ({ row }) => { const item = row.original as any return ( ) }, }, actionsColumn(), ]} /> {attachmentTarget && ( setAttachmentTarget(null)} /> )} ) }