"use client"; import { useState } from "react"; import { DataTable } from "@/components/common/data-table"; import { ColumnDef } from "@tanstack/react-table"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Plus, Pencil, Trash2, RefreshCw } from "lucide-react"; import { toast } from "sonner"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Skeleton } from "@/components/ui/skeleton"; interface FieldConfig { name: string; label: string; type: "text" | "textarea" | "checkbox" | "select"; required?: boolean; options?: { label: string; value: any }[]; } interface GenericCrudTableProps { entityName: string; queryKey: string[]; fetchFn: () => Promise; createFn: (data: any) => Promise; updateFn: (id: number, data: any) => Promise; deleteFn: (id: number) => Promise; columns: ColumnDef[]; fields: FieldConfig[]; title?: string; description?: string; filters?: React.ReactNode; } export function GenericCrudTable({ entityName, queryKey, fetchFn, createFn, updateFn, deleteFn, columns, fields, title, description, filters, }: GenericCrudTableProps) { const queryClient = useQueryClient(); const [isOpen, setIsOpen] = useState(false); const [editingItem, setEditingItem] = useState(null); const [formData, setFormData] = useState({}); // Delete Dialog State const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [itemToDelete, setItemToDelete] = useState(null); const { data, isLoading, refetch } = useQuery({ queryKey, queryFn: fetchFn, }); const createMutation = useMutation({ mutationFn: createFn, onSuccess: () => { toast.success(`${entityName} created successfully`); queryClient.invalidateQueries({ queryKey }); handleClose(); }, onError: () => toast.error(`Failed to create ${entityName}`), }); const updateMutation = useMutation({ mutationFn: ({ id, data }: { id: number; data: any }) => updateFn(id, data), onSuccess: () => { toast.success(`${entityName} updated successfully`); queryClient.invalidateQueries({ queryKey }); handleClose(); }, onError: () => toast.error(`Failed to update ${entityName}`), }); const deleteMutation = useMutation({ mutationFn: deleteFn, onSuccess: () => { toast.success(`${entityName} deleted successfully`); queryClient.invalidateQueries({ queryKey }); setDeleteDialogOpen(false); setItemToDelete(null); }, onError: () => toast.error(`Failed to delete ${entityName}`), }); const handleCreate = () => { setEditingItem(null); setFormData({}); setIsOpen(true); }; const handleEdit = (item: any) => { setEditingItem(item); setFormData({ ...item }); setIsOpen(true); }; const handleDeleteClick = (id: number) => { setItemToDelete(id); setDeleteDialogOpen(true); }; const confirmDelete = () => { if (itemToDelete) { deleteMutation.mutate(itemToDelete); } }; const handleClose = () => { setIsOpen(false); setEditingItem(null); setFormData({}); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (editingItem) { updateMutation.mutate({ id: editingItem.id, data: formData }); } else { createMutation.mutate(formData); } }; const handleChange = (field: string, value: any) => { setFormData((prev: any) => ({ ...prev, [field]: value })); }; // Add default Actions column if not present const tableColumns = [ ...columns, { id: "actions", header: "Actions", cell: ({ row }: { row: any }) => (
), }, ]; return (
{title &&

{title}

} {description && (

{description}

)}
{filters}
{isLoading ? (
{[1, 2, 3, 4, 5].map((i) => (
))}
) : ( )} {editingItem ? `Edit ${entityName}` : `New ${entityName}`}
{fields.map((field) => (
{field.type === "textarea" ? (