1import { type FC, useMemo, useState } from "react";
2import { Button, DataTableViewOptions } from "@marzneshin/components";
3import { useTranslation } from "react-i18next";
4import { useQuery } from "@tanstack/react-query";
5import { EntityTableContext } from "./entity-table-provider";
6import { EntityDataTable } from "./table";
7import { DataTablePagination } from "./table-pagination";
8import { TableFiltering } from "./table-filtering";
9import {
10 type UseRowSelectionReturn,
11 type UseDialogProps,
12 useFiltering,
13 usePagination,
14 type FetchEntityReturn,
15 useEntityTable,
16 useVisibility,
17 useSorting,
18 type SortableQueryKey,
19 type QueryKey,
20 type EntityQueryKeyType,
21} from "./hooks";
22import { useDialog } from "@marzneshin/hooks";
23
24interface EntityTableProps<T> {
25 fetchEntity: ({ queryKey }: EntityQueryKeyType) => FetchEntityReturn<T>;
26 MutationDialog: FC<UseDialogProps<T>>;
27 DeleteConfirmationDialog: FC<UseDialogProps<T>>;
28 SettingsDialog: FC<UseDialogProps<T | any>>;
29 columnsFn: any;
30 filteredColumn: string;
31 entityKey: string;
32 rowSelection?: UseRowSelectionReturn;
33 manualSorting?: boolean;
34}
35
36export function EntityTable<T>({
37 fetchEntity,
38 MutationDialog,
39 DeleteConfirmationDialog,
40 SettingsDialog,
41 columnsFn,
42 filteredColumn,
43 rowSelection,
44 entityKey,
45 manualSorting = false,
46}: EntityTableProps<T>) {
47 const [mutationDialogOpen, setMutationDialogOpen] = useDialog();
48 const [deleteDialogOpen, setDeleteDialogOpen] = useDialog();
49 const [settingsDialogOpen, setSettingsDialogOpen] = useDialog();
50 const [selectedEntity, selectEntity] = useState<T | null>(null);
51
52 const onEdit = (entity: T) => {
53 selectEntity(entity);
54 setMutationDialogOpen(true);
55 };
56
57 const onDelete = (entity: T) => {
58 selectEntity(entity);
59 setDeleteDialogOpen(true);
60 };
61
62 const onCreate = () => {
63 selectEntity(null);
64 setMutationDialogOpen(true);
65 };
66
67 const onOpen = (entity: T) => {
68 selectEntity(entity);
69 setSettingsDialogOpen(true);
70 };
71
72 const { t } = useTranslation();
73 const filtering = useFiltering({ column: filteredColumn });
74 const sorting = useSorting();
75 const visibility = useVisibility();
76 const { onPaginationChange, pageIndex, pageSize } = usePagination();
77 const sortedQuery: SortableQueryKey = [
78 entityKey,
79 pageIndex,
80 pageSize,
81 filtering.columnFilters,
82 sorting.sorting[0]?.id ? sorting.sorting[0].id : "created_at",
83 sorting.sorting[0]?.desc,
84 ];
85 const query: QueryKey = [
86 entityKey,
87 pageIndex,
88 pageSize,
89 filtering.columnFilters,
90 ];
91
92 const { data, isLoading } = useQuery({
93 queryFn: fetchEntity,
94 queryKey: manualSorting ? sortedQuery : query,
95 initialData: { entity: [], pageCount: 1 },
96 });
97
98 const columns = columnsFn({ onEdit, onDelete, onOpen });
99 const table = useEntityTable({
100 data,
101 columns,
102 pageSize,
103 pageIndex,
104 rowSelection,
105 visibility,
106 sorting,
107 onPaginationChange,
108 });
109
110 const contextValue = useMemo(() => (
111 { table, data: data.entity, filtering, isLoading }
112 ), [table, data.entity, filtering, isLoading])
113
114 return (
115 <EntityTableContext.Provider
116 value={contextValue}
117 >
118 <SettingsDialog
119 open={settingsDialogOpen}
120 onOpenChange={setSettingsDialogOpen}
121 entity={selectedEntity}
122 />
123 <DeleteConfirmationDialog
124 open={deleteDialogOpen}
125 onOpenChange={setDeleteDialogOpen}
126 entity={selectedEntity}
127 />
128 <MutationDialog
129 open={mutationDialogOpen}
130 onOpenChange={setMutationDialogOpen}
131 entity={selectedEntity}
132 />
133 <div className="flex flex-col">
134 <div className="flex items-center py-4">
135 <TableFiltering />
136 <DataTableViewOptions table={table} />
137 {onCreate && (
138 <Button aria-label={`create-${entityKey}`} onClick={onCreate}>
139 {t("create")}
140 </Button>
141 )}
142 </div>
143 <div className="w-full rounded-md border">
144 <EntityDataTable columns={columns} onRowClick={onOpen} />
145 <DataTablePagination />
146 </div>
147 </div>
148 </EntityTableContext.Provider>
149 );
150}
151
152export * from "./hooks";