"use client" import { useState } from "react" import { useFormContext, useController, type FieldValues, type FieldPath, } from "react-hook-form" import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query" import { Check, ChevronFirst, ChevronLast, ChevronLeft, ChevronRight, Edit2, MoreHorizontal, Plus, RefreshCcw, Search, Trash2, X, } from "lucide-react" import { useAuthApi } from "@/shared/useApi" import { QUICK_REMARK_ROUTES } from "@garage/api" import { Sheet, SheetContent, SheetHeader, SheetTitle, } from "@/shared/components/ui/sheet" import { Button } from "@/shared/components/ui/button" import { Input } from "@/shared/components/ui/input" import { Field, FieldLabel, FieldError, } from "@/shared/components/ui/field" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/shared/components/ui/dropdown-menu" import { Tooltip, TooltipContent, TooltipTrigger, } from "@/shared/components/ui/tooltip" import { cn } from "@/shared/lib/utils" // ── Types ── type QuickRemark = { id: number description: string } type QuickRemarksPage = { data: QuickRemark[] meta: { current_page: number last_page: number per_page: number total: number } } type RhfCustomerRemarksFieldProps< TValues extends FieldValues, TName extends FieldPath, > = { name: TName label?: string description?: string required?: boolean disabled?: boolean placeholder?: string } // ── Helpers ── function extractPage(response: unknown): QuickRemarksPage { const r = response as any return { data: Array.isArray(r?.data?.data) ? r.data.data : Array.isArray(r?.data) ? r.data : [], meta: r?.data?.meta ?? r?.meta ?? { current_page: 1, last_page: 1, per_page: 15, total: 0, }, } } // ── QuickRemarksSheet ── function QuickRemarksSheet({ open, onOpenChange, selected, onToggle, }: { open: boolean onOpenChange: (v: boolean) => void selected: string[] onToggle: (description: string) => void }) { const api = useAuthApi() const queryClient = useQueryClient() const [page, setPage] = useState(1) const [search, setSearch] = useState("") const [creating, setCreating] = useState(false) const [newDescription, setNewDescription] = useState("") const [editingId, setEditingId] = useState(null) const [editingText, setEditingText] = useState("") const queryKey = [QUICK_REMARK_ROUTES.INDEX, { page, search }] const { data, isLoading } = useQuery({ queryKey, queryFn: async () => { const res = await api.quickRemarks.list({ page, ...(search ? { search } : {}), }) return extractPage(res) }, enabled: open, staleTime: 30_000, }) const remarks = data?.data ?? [] const meta = data?.meta const invalidate = () => queryClient.invalidateQueries({ queryKey: [QUICK_REMARK_ROUTES.INDEX] }) const createMutation = useMutation({ mutationFn: (description: string) => api.quickRemarks.create({ description }), onSuccess: () => { invalidate() setCreating(false) setNewDescription("") }, }) const updateMutation = useMutation({ mutationFn: ({ id, description }: { id: number; description: string }) => api.quickRemarks.update(String(id), { description }), onSuccess: () => { invalidate() setEditingId(null) setEditingText("") }, }) const deleteMutation = useMutation({ mutationFn: (id: number) => api.quickRemarks.destroy(String(id)), onSuccess: () => invalidate(), }) function handleCreate() { const text = newDescription.trim() if (!text) return createMutation.mutate(text) } function handleUpdate(id: number) { const text = editingText.trim() if (!text) return updateMutation.mutate({ id, description: text }) } function startEdit(remark: QuickRemark) { setEditingId(remark.id) setEditingText(remark.description) } const totalPages = meta?.last_page ?? 1 const from = meta ? (meta.current_page - 1) * meta.per_page + 1 : 0 const to = meta ? Math.min(meta.current_page * meta.per_page, meta.total) : 0 return ( {/* Header */} Quick Remarks {/* Search */}
{ setSearch(e.target.value) setPage(1) }} className="pl-9 h-9" />
{/* Inline create form */} {creating && (
setNewDescription(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); handleCreate() } if (e.key === "Escape") { setCreating(false); setNewDescription("") } }} className="h-8 text-sm" />
)} {/* List */}
{isLoading ? (
Loading...
) : remarks.length === 0 ? (
No quick remarks found.
) : ( remarks.map((remark) => { const isSelected = selected.includes(remark.description) const isEditing = editingId === remark.id if (isEditing) { return (
setEditingText(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); handleUpdate(remark.id) } if (e.key === "Escape") { setEditingId(null); setEditingText("") } }} className="h-8 flex-1 text-sm" />
) } return (
onToggle(remark.description)} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault() onToggle(remark.description) } }} >
{isSelected && ( )}
{remark.description}
e.stopPropagation()}> { e.stopPropagation() startEdit(remark) }} > Edit { e.stopPropagation() deleteMutation.mutate(remark.id) }} > Delete
) }) )}
{/* Pagination */} {meta && meta.total > 0 && (
{from}–{to} of {meta.total}
)}
) } // ── Main Component ── export function RhfCustomerRemarksField< TValues extends FieldValues, TName extends FieldPath, >({ name, label = "Customer Remark", description, required, disabled, placeholder = "Enter remark...", }: RhfCustomerRemarksFieldProps) { const { control } = useFormContext() const { field, fieldState: { error } } = useController({ name, control, disabled }) const [sheetOpen, setSheetOpen] = useState(false) const remarks: string[] = Array.isArray(field.value) ? field.value : [] function updateAt(index: number, value: string) { const next = [...remarks] next[index] = value field.onChange(next) } function addLine() { field.onChange([...remarks, ""]) } function removeLine(index: number) { field.onChange(remarks.filter((_, i) => i !== index)) } function toggleQuickRemark(description: string) { const exists = remarks.includes(description) if (exists) { field.onChange(remarks.filter((r) => r !== description)) } else { field.onChange([...remarks, description]) } } return ( {label && ( {label} {required && ( * )} )} {/* Repeater rows */}
{remarks.map((remark, index) => (
updateAt(index, e.target.value)} placeholder={placeholder} disabled={disabled} className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground" />
))} {/* Empty state */} {remarks.length === 0 && (
No remarks added yet.
)} {/* Footer row */}
Quick Remarks
{error && {error.message}} {description && !error && (

{description}

)}
) }