"use client" import { useState } from "react" import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" import { toast } from "sonner" import { confirm } from "@/shared/components/confirm-dialog" import type { DataViewChangeEvent, DataViewPaginationState, DataViewSorting } from "@/shared/data-view/table-view" // ── Types ── export type CrudDialogClient = { list(query?: any): Promise create(payload: any): Promise update(id: string, payload: any): Promise destroy(id: string): Promise } export type UseCrudDialogOptions = { queryKey: string[] getClient: () => TClient resourceLabel?: string } // ── Hook ── export function useCrudDialog({ queryKey, getClient, resourceLabel = "item", }: UseCrudDialogOptions) { const client = getClient() const queryClient = useQueryClient() // ── Local pagination state (no URL pollution) ── const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(10) const [sortBy, setSortBy] = useState(null) const [sortOrder, setSortOrder] = useState<"asc" | "desc" | null>(null) // ── Form dialog state ── const [isFormOpen, setIsFormOpen] = useState(false) const [editingId, setEditingId] = useState(null) const [editingItem, setEditingItem] = useState(null) const fullQueryKey = [...queryKey, { page, pageSize, sortBy, sortOrder }] const { data, isLoading } = useQuery({ queryKey: fullQueryKey, queryFn: () => { const params: Record = { page, per_page: pageSize } if (sortBy) params.sort_by = sortBy if (sortOrder) params.sort_order = sortOrder return client.list(params) }, }) const responseData = (data as any)?.data ?? [] const items = Array.isArray(responseData) ? responseData : [] const meta = (data as any)?.meta const pagination: DataViewPaginationState = { page, pageSize, pageCount: meta?.last_page ?? 1, total: meta?.total ?? 0, } const sorting: DataViewSorting = sortBy ? [{ id: sortBy, desc: sortOrder === "desc" }] : [] const handleChange = (event: DataViewChangeEvent) => { switch (event.type) { case "pagination": setPage(event.pagination.page) setPageSize(event.pagination.pageSize) break case "sorting": { const sort = event.sorting[0] setSortBy(sort?.id ?? null) setSortOrder(sort ? (sort.desc ? "desc" : "asc") : null) setPage(1) break } } } const invalidateQuery = () => { queryClient.invalidateQueries({ queryKey }) } const { mutateAsync: deleteItem } = useMutation({ mutationFn: (id: string) => { const promise = client.destroy(id) toast.promise(promise, { loading: `Deleting ${resourceLabel}...`, success: `${resourceLabel} deleted`, error: `Failed to delete ${resourceLabel}`, }) return promise }, onSuccess: invalidateQuery, }) const openCreate = () => { setEditingId(null) setEditingItem(null) setIsFormOpen(true) } const openEdit = (row: any) => { setEditingId(String(row.id)) setEditingItem(row) setIsFormOpen(true) } const closeForm = () => { setIsFormOpen(false) setEditingId(null) setEditingItem(null) } const handleDelete = async (row: any) => { const confirmed = await confirm({ title: `Delete this ${resourceLabel}?`, description: "This action cannot be undone.", confirmLabel: "Delete", variant: "destructive", }) if (confirmed) await deleteItem(String(row.id)) } const handleFormSuccess = () => { invalidateQuery() closeForm() } return { items, isLoading, pagination, sorting, handleChange, isFormOpen, editingId, editingItem, openCreate, openEdit, closeForm, handleDelete, handleFormSuccess, invalidateQuery, } }