155 lines
4.4 KiB
TypeScript
155 lines
4.4 KiB
TypeScript
"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<any>
|
|
create(payload: any): Promise<any>
|
|
update(id: string, payload: any): Promise<any>
|
|
destroy(id: string): Promise<any>
|
|
}
|
|
|
|
export type UseCrudDialogOptions<TClient extends CrudDialogClient> = {
|
|
queryKey: string[]
|
|
getClient: () => TClient
|
|
resourceLabel?: string
|
|
}
|
|
|
|
// ── Hook ──
|
|
|
|
export function useCrudDialog<TClient extends CrudDialogClient>({
|
|
queryKey,
|
|
getClient,
|
|
resourceLabel = "item",
|
|
}: UseCrudDialogOptions<TClient>) {
|
|
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<string | null>(null)
|
|
const [sortOrder, setSortOrder] = useState<"asc" | "desc" | null>(null)
|
|
|
|
// ── Form dialog state ──
|
|
const [isFormOpen, setIsFormOpen] = useState(false)
|
|
const [editingId, setEditingId] = useState<string | null>(null)
|
|
const [editingItem, setEditingItem] = useState<any>(null)
|
|
|
|
const fullQueryKey = [...queryKey, { page, pageSize, sortBy, sortOrder }]
|
|
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: fullQueryKey,
|
|
queryFn: () => {
|
|
const params: Record<string, unknown> = { 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,
|
|
}
|
|
}
|