@evenicanpm/portal-table-ui 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/features/table/components/dashboard/table/bulk-action-button.tsx +10 -4
- package/src/features/table/components/dashboard/table/data-mask-map.ts +2 -2
- package/src/features/table/components/dashboard/table/details/details.tsx +6 -2
- package/src/features/table/components/dashboard/table/details/templates/invoice-details.tsx +64 -15
- package/src/features/table/components/dashboard/table/details/templates/template-registry.ts +1 -0
- package/src/features/table/components/dashboard/table/table-header.tsx +1 -2
- package/src/features/table/components/dashboard/table/table-input.tsx +12 -5
- package/src/features/table/components/dashboard/table/table-row/action-button.tsx +4 -1
- package/src/features/table/components/dashboard/table/table-row/table-row.tsx +2 -9
- package/src/features/table/components/dashboard/table/table.tsx +34 -10
- package/src/features/table/components/dashboard/table-dashboard.tsx +15 -0
- package/src/features/table/logic/resolvers.ts +1 -1
- package/src/features/table/types/schemas.ts +19 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evenicanpm/portal-table-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"@tanstack/react-query": "^5.90.11",
|
|
48
48
|
"date-fns": "^4.1.0"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "8227589295d3d63586f9b26fff8d643b070989b3"
|
|
51
51
|
}
|
|
@@ -25,11 +25,17 @@ export function BulkActionButton({
|
|
|
25
25
|
const { mutate, isPending } = useMutation(mutationOptions);
|
|
26
26
|
|
|
27
27
|
const handleClick = () => {
|
|
28
|
-
mutate(
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
mutate(
|
|
29
|
+
{
|
|
30
|
+
resourceName,
|
|
31
|
+
selectedIds,
|
|
31
32
|
},
|
|
32
|
-
|
|
33
|
+
{
|
|
34
|
+
onSuccess: () => {
|
|
35
|
+
clearSelection();
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
);
|
|
33
39
|
};
|
|
34
40
|
|
|
35
41
|
const Icon = action.icon;
|
|
@@ -9,7 +9,7 @@ export const dataMaskMap: Table.Handlers.DataMaskMap = {
|
|
|
9
9
|
|
|
10
10
|
const date = new Date(value);
|
|
11
11
|
|
|
12
|
-
if (isNaN(date.getTime())) {
|
|
12
|
+
if (Number.isNaN(date.getTime())) {
|
|
13
13
|
console.warn("Invalid datetime value:", value);
|
|
14
14
|
return "";
|
|
15
15
|
}
|
|
@@ -21,7 +21,7 @@ export const dataMaskMap: Table.Handlers.DataMaskMap = {
|
|
|
21
21
|
|
|
22
22
|
const date = new Date(value);
|
|
23
23
|
|
|
24
|
-
if (isNaN(date.getTime())) {
|
|
24
|
+
if (Number.isNaN(date.getTime())) {
|
|
25
25
|
console.warn("Invalid date value:", value);
|
|
26
26
|
return "";
|
|
27
27
|
}
|
|
@@ -19,7 +19,9 @@ interface DetailsProps {
|
|
|
19
19
|
/** What is the id field name */
|
|
20
20
|
id: string | number;
|
|
21
21
|
/** The actual pojo of data from the API */
|
|
22
|
-
documentData
|
|
22
|
+
documentData?: unknown;
|
|
23
|
+
/** The row data for the document */
|
|
24
|
+
row: Types.Queries.Row;
|
|
23
25
|
/* the label of the document type ie 'INVOICES' */
|
|
24
26
|
documentTypeLabel: string;
|
|
25
27
|
/** Special instructions for how to render certain properties */
|
|
@@ -46,6 +48,7 @@ const Details = ({
|
|
|
46
48
|
id,
|
|
47
49
|
documentTypeLabel,
|
|
48
50
|
documentData,
|
|
51
|
+
row,
|
|
49
52
|
handleBack,
|
|
50
53
|
columns,
|
|
51
54
|
dataMaskMap,
|
|
@@ -69,7 +72,7 @@ const Details = ({
|
|
|
69
72
|
? undefined
|
|
70
73
|
: visibleDetails?.map((col) => ({
|
|
71
74
|
key: col.Data,
|
|
72
|
-
value:
|
|
75
|
+
value: row?.[col.Data], // use row instead of documentData
|
|
73
76
|
maskFn: resolvers.resolveDataMask(col, dataMaskMap),
|
|
74
77
|
mask: col.DataMask,
|
|
75
78
|
col,
|
|
@@ -98,6 +101,7 @@ const Details = ({
|
|
|
98
101
|
{TemplateComponent ? (
|
|
99
102
|
<TemplateComponent
|
|
100
103
|
data={documentData}
|
|
104
|
+
row={row}
|
|
101
105
|
statusColorMap={statusColorMap}
|
|
102
106
|
statusTextMap={statusTextMap}
|
|
103
107
|
loading={loading}
|
|
@@ -76,8 +76,13 @@ interface InvoiceData {
|
|
|
76
76
|
DueDate?: string;
|
|
77
77
|
Notes?: string;
|
|
78
78
|
Id: string;
|
|
79
|
+
OrderedByCustomerName?: string;
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
const detailSkeletonKeys = ["from", "bill-to", "invoice-date", "due-date"];
|
|
83
|
+
const tableSkeletonRowKeys = ["line-1", "line-2", "line-3", "line-4"];
|
|
84
|
+
const totalSkeletonKeys = ["subtotal", "tax", "total"];
|
|
85
|
+
|
|
81
86
|
const isDataEmpty = (data: InvoiceData | null | undefined): boolean => {
|
|
82
87
|
if (!data) return true;
|
|
83
88
|
const hasNoLineItems =
|
|
@@ -90,6 +95,7 @@ const isDataEmpty = (data: InvoiceData | null | undefined): boolean => {
|
|
|
90
95
|
export const InvoiceDetailsTemplate = ({
|
|
91
96
|
data,
|
|
92
97
|
statusColorMap,
|
|
98
|
+
row,
|
|
93
99
|
statusTextMap,
|
|
94
100
|
loading = false,
|
|
95
101
|
actions,
|
|
@@ -107,6 +113,7 @@ export const InvoiceDetailsTemplate = ({
|
|
|
107
113
|
return (
|
|
108
114
|
<Box sx={{ p: { xs: 2, sm: 3 }, width: containerWidth, mx: "auto" }}>
|
|
109
115
|
<Card
|
|
116
|
+
variant="outlined"
|
|
110
117
|
sx={{ p: { xs: 3, sm: 6 }, borderRadius: 3, textAlign: "center" }}
|
|
111
118
|
>
|
|
112
119
|
<Typography variant="h6" color="text.secondary" gutterBottom>
|
|
@@ -147,10 +154,10 @@ export const InvoiceDetailsTemplate = ({
|
|
|
147
154
|
<Skeleton variant="rectangular" width={100} height={32} />
|
|
148
155
|
<Skeleton variant="rectangular" width={120} height={40} />
|
|
149
156
|
</Box>
|
|
150
|
-
<Card sx={{ p: { xs: 2, sm: 4 }, borderRadius: 3 }}>
|
|
157
|
+
<Card variant="outlined" sx={{ p: { xs: 2, sm: 4 }, borderRadius: 3 }}>
|
|
151
158
|
<Grid2 container spacing={{ xs: 2, sm: 4 }}>
|
|
152
|
-
{
|
|
153
|
-
<Grid2 size={{ xs: 12, sm: 6 }} key={
|
|
159
|
+
{detailSkeletonKeys.map((key) => (
|
|
160
|
+
<Grid2 size={{ xs: 12, sm: 6 }} key={key}>
|
|
154
161
|
<Skeleton width="40%" height={20} />
|
|
155
162
|
<Skeleton width="80%" />
|
|
156
163
|
<Skeleton width="60%" />
|
|
@@ -178,8 +185,8 @@ export const InvoiceDetailsTemplate = ({
|
|
|
178
185
|
</TableRow>
|
|
179
186
|
</TableHead>
|
|
180
187
|
<TableBody>
|
|
181
|
-
{
|
|
182
|
-
<TableRow key={
|
|
188
|
+
{tableSkeletonRowKeys.map((key) => (
|
|
189
|
+
<TableRow key={key}>
|
|
183
190
|
{columns.map((col) => (
|
|
184
191
|
<TableCell
|
|
185
192
|
key={col.label}
|
|
@@ -196,9 +203,9 @@ export const InvoiceDetailsTemplate = ({
|
|
|
196
203
|
|
|
197
204
|
<Box mt={3} display="flex" justifyContent="flex-end">
|
|
198
205
|
<Box width={{ xs: "100%", sm: 300 }}>
|
|
199
|
-
{
|
|
206
|
+
{totalSkeletonKeys.map((key) => (
|
|
200
207
|
<Box
|
|
201
|
-
key={
|
|
208
|
+
key={key}
|
|
202
209
|
display="flex"
|
|
203
210
|
justifyContent="space-between"
|
|
204
211
|
mb={1}
|
|
@@ -234,8 +241,7 @@ export const InvoiceDetailsTemplate = ({
|
|
|
234
241
|
)}
|
|
235
242
|
<Box display="flex" gap={2} alignItems="center">
|
|
236
243
|
{actions?.map((action) => {
|
|
237
|
-
const
|
|
238
|
-
const enabled = !isPaid && action.isEnabled;
|
|
244
|
+
const enabled = !action.isEnabled || action.isEnabled(row);
|
|
239
245
|
if (!enabled) {
|
|
240
246
|
return (
|
|
241
247
|
<span
|
|
@@ -262,7 +268,10 @@ export const InvoiceDetailsTemplate = ({
|
|
|
262
268
|
|
|
263
269
|
{/* Notes Section */}
|
|
264
270
|
{invoice?.Notes && (
|
|
265
|
-
<Card
|
|
271
|
+
<Card
|
|
272
|
+
variant="outlined"
|
|
273
|
+
sx={{ p: { xs: 2, sm: 3 }, borderRadius: 3, mb: 3 }}
|
|
274
|
+
>
|
|
266
275
|
<Typography variant="subtitle1" color="text.secondary" mb={1}>
|
|
267
276
|
Notes
|
|
268
277
|
</Typography>
|
|
@@ -277,16 +286,49 @@ export const InvoiceDetailsTemplate = ({
|
|
|
277
286
|
)}
|
|
278
287
|
|
|
279
288
|
{/* Main Invoice Card */}
|
|
280
|
-
<Card sx={{ p: { xs: 2, sm: 3 }, borderRadius: 3 }}>
|
|
289
|
+
<Card variant="outlined" sx={{ p: { xs: 2, sm: 3 }, borderRadius: 3 }}>
|
|
281
290
|
{/* Details Section */}
|
|
282
291
|
{(invoice?.CompanyName ||
|
|
283
292
|
invoice?.Name ||
|
|
284
293
|
invoice?.InvoiceDate ||
|
|
285
294
|
invoice?.DueDate) && (
|
|
286
295
|
<>
|
|
287
|
-
<
|
|
288
|
-
|
|
289
|
-
|
|
296
|
+
<Box
|
|
297
|
+
sx={{
|
|
298
|
+
display: "flex",
|
|
299
|
+
flexDirection: { xs: "column", sm: "row" },
|
|
300
|
+
gap: 1,
|
|
301
|
+
justifyContent: "space-between",
|
|
302
|
+
alignItems: { xs: "flex-start", sm: "center" },
|
|
303
|
+
marginBottom: 2,
|
|
304
|
+
}}
|
|
305
|
+
>
|
|
306
|
+
<Typography variant="subtitle1" color="text.secondary">
|
|
307
|
+
Details
|
|
308
|
+
</Typography>
|
|
309
|
+
|
|
310
|
+
{invoice.OrderedByCustomerName && (
|
|
311
|
+
<Box
|
|
312
|
+
sx={{
|
|
313
|
+
display: "flex",
|
|
314
|
+
gap: 1,
|
|
315
|
+
alignItems: "center",
|
|
316
|
+
flexWrap: "wrap",
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
<Typography variant="body2" color="text.secondary">
|
|
320
|
+
Order Placed By:
|
|
321
|
+
</Typography>
|
|
322
|
+
<Typography
|
|
323
|
+
variant="body2"
|
|
324
|
+
sx={{ wordBreak: "break-all" }}
|
|
325
|
+
fontWeight={600}
|
|
326
|
+
>
|
|
327
|
+
{invoice.OrderedByCustomerName}
|
|
328
|
+
</Typography>
|
|
329
|
+
</Box>
|
|
330
|
+
)}
|
|
331
|
+
</Box>
|
|
290
332
|
<Grid2 container spacing={{ xs: 2, sm: 4 }}>
|
|
291
333
|
{invoice?.CompanyName && (
|
|
292
334
|
<Grid2 size={{ xs: 12, sm: 6 }}>
|
|
@@ -384,9 +426,16 @@ export const InvoiceDetailsTemplate = ({
|
|
|
384
426
|
const unitPrice = item.Price ?? 0;
|
|
385
427
|
const quantity = item.Quantity ?? 0;
|
|
386
428
|
const lineTotal = item.NetAmount ?? unitPrice * quantity;
|
|
429
|
+
const itemKey = [
|
|
430
|
+
item.ItemId ?? "item",
|
|
431
|
+
productName,
|
|
432
|
+
quantity,
|
|
433
|
+
unitPrice,
|
|
434
|
+
lineTotal,
|
|
435
|
+
].join("-");
|
|
387
436
|
|
|
388
437
|
return (
|
|
389
|
-
<TableRow key={
|
|
438
|
+
<TableRow key={itemKey}>
|
|
390
439
|
<TableCell sx={{ width: 60, minWidth: 60 }}>
|
|
391
440
|
<Typography variant="body2">
|
|
392
441
|
{item?.ItemId || idx + 1}
|
package/src/features/table/components/dashboard/table/details/templates/template-registry.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { InvoiceDetailsTemplate } from "./invoice-details";
|
|
|
8
8
|
*/
|
|
9
9
|
export interface DetailsTemplateProps {
|
|
10
10
|
data: unknown;
|
|
11
|
+
row: Types.Queries.Row;
|
|
11
12
|
statusColorMap: Types.Schemas.StatusColorMap;
|
|
12
13
|
statusTextMap: Types.Schemas.StatusTextMap;
|
|
13
14
|
loading?: boolean;
|
|
@@ -5,7 +5,6 @@ import { styled } from "@mui/material/styles";
|
|
|
5
5
|
import TableCell from "@mui/material/TableCell";
|
|
6
6
|
import TableHead from "@mui/material/TableHead";
|
|
7
7
|
import TableRow from "@mui/material/TableRow";
|
|
8
|
-
import type React from "react";
|
|
9
8
|
|
|
10
9
|
// STYLED COMPONENTS
|
|
11
10
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
|
@@ -85,7 +84,7 @@ export default function TableHeader({
|
|
|
85
84
|
key={column.Label + String(i)}
|
|
86
85
|
sx={{
|
|
87
86
|
whiteSpace: "normal",
|
|
88
|
-
wordBreak: "
|
|
87
|
+
wordBreak: "normal",
|
|
89
88
|
}}
|
|
90
89
|
>
|
|
91
90
|
{column.Label}
|
|
@@ -6,8 +6,8 @@ import React from "react";
|
|
|
6
6
|
interface SearchProps {
|
|
7
7
|
searchTerm: string;
|
|
8
8
|
setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
|
|
9
|
-
setColumn: React.Dispatch<React.SetStateAction<
|
|
10
|
-
selectedColumn:
|
|
9
|
+
setColumn: React.Dispatch<React.SetStateAction<Column>>;
|
|
10
|
+
selectedColumn: Column;
|
|
11
11
|
searchCols: Column[];
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -78,12 +78,19 @@ const SearchField = React.memo(
|
|
|
78
78
|
onChange={(e) => {
|
|
79
79
|
setSearchTerm(e.target.value);
|
|
80
80
|
}}
|
|
81
|
-
placeholder={
|
|
81
|
+
placeholder={
|
|
82
|
+
selectedColumn?.Label
|
|
83
|
+
? `Search by ${selectedColumn.Label}`
|
|
84
|
+
: "Select a column to search"
|
|
85
|
+
}
|
|
82
86
|
endAdornment={
|
|
83
87
|
<ColumnSelect
|
|
84
|
-
selected={selectedColumn}
|
|
88
|
+
selected={selectedColumn?.Data || ""}
|
|
85
89
|
searchCols={searchCols}
|
|
86
|
-
handleChange={
|
|
90
|
+
handleChange={(value) => {
|
|
91
|
+
const col = searchCols.find((c) => c.Data === value);
|
|
92
|
+
if (col) setColumn(col);
|
|
93
|
+
}}
|
|
87
94
|
/>
|
|
88
95
|
}
|
|
89
96
|
/>
|
|
@@ -14,15 +14,8 @@ type ColumnLogic =
|
|
|
14
14
|
| {
|
|
15
15
|
id: string;
|
|
16
16
|
mask: string | undefined;
|
|
17
|
-
maskFn:
|
|
18
|
-
cellFn:
|
|
19
|
-
| ((
|
|
20
|
-
value: string | number | string[],
|
|
21
|
-
) => React.ReactElement<
|
|
22
|
-
any,
|
|
23
|
-
string | React.JSXElementConstructor<any>
|
|
24
|
-
>)
|
|
25
|
-
| ((value: string | number) => string | number);
|
|
17
|
+
maskFn: (value: unknown, mask?: string) => string | number | string[];
|
|
18
|
+
cellFn: (value: string | number | string[]) => React.ReactNode;
|
|
26
19
|
}[]
|
|
27
20
|
| undefined;
|
|
28
21
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Column } from "@evenicanpm/portal-types-schemas";
|
|
1
2
|
import { FlexBox, ResponsiveTablePagination } from "@evenicanpm/ui";
|
|
2
3
|
import Table from "@mui/material/Table";
|
|
3
4
|
import TableBody from "@mui/material/TableBody";
|
|
@@ -150,14 +151,28 @@ export default function DocumentTable({
|
|
|
150
151
|
const visibleCols = transformers.getVisibleColumns(columns);
|
|
151
152
|
|
|
152
153
|
// Depends on searchableCols, must be declared after transformer
|
|
153
|
-
const [searchColumn, setSearchColumn] = useState(
|
|
154
|
-
() => searchableCols?.[0]
|
|
154
|
+
const [searchColumn, setSearchColumn] = useState<Column>(
|
|
155
|
+
() => searchableCols?.[0] as Column,
|
|
155
156
|
);
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
// always derive a safe usable column
|
|
159
|
+
const effectiveSearchColumn = searchColumn ?? searchableCols?.[0];
|
|
160
|
+
|
|
161
|
+
const { data: fetchedRows, isPending: rowsPending } = useQuery(
|
|
162
|
+
resource.rowsQueryFn({
|
|
163
|
+
page,
|
|
164
|
+
pageSize: pageSize + 1, // Fetch one extra to check if there's a next page
|
|
165
|
+
filters,
|
|
166
|
+
searchColumn: effectiveSearchColumn?.Data || "",
|
|
167
|
+
searchTerm,
|
|
168
|
+
}),
|
|
159
169
|
);
|
|
160
170
|
|
|
171
|
+
const hasNextPage = fetchedRows ? fetchedRows.data.length > pageSize : false;
|
|
172
|
+
const rows: Types.Queries.TableRows | undefined = fetchedRows
|
|
173
|
+
? { ...fetchedRows, data: fetchedRows.data.slice(0, pageSize) }
|
|
174
|
+
: undefined;
|
|
175
|
+
|
|
161
176
|
// --- Resolver --- //
|
|
162
177
|
const filterLogic = filterReqs?.map((req) => ({
|
|
163
178
|
req,
|
|
@@ -194,8 +209,8 @@ export default function DocumentTable({
|
|
|
194
209
|
!!resource.detailsQueryFn && !!resource.detailsTemplate;
|
|
195
210
|
|
|
196
211
|
const { data: detailsData, isPending: detailsPending } = useQuery(
|
|
197
|
-
hasTemplateSupport && selectedDocument
|
|
198
|
-
? resource.detailsQueryFn
|
|
212
|
+
hasTemplateSupport && selectedDocument && resource.detailsQueryFn
|
|
213
|
+
? resource.detailsQueryFn(String(selectedDocument))
|
|
199
214
|
: {
|
|
200
215
|
queryKey: [],
|
|
201
216
|
queryFn: async () => undefined,
|
|
@@ -226,14 +241,16 @@ export default function DocumentTable({
|
|
|
226
241
|
const rowSnapshot =
|
|
227
242
|
rows?.data.find((doc) => doc?.[idField] === selectedDocument) ?? {};
|
|
228
243
|
|
|
229
|
-
const
|
|
230
|
-
hasTemplateSupport && detailsData ? detailsData : rowSnapshot;
|
|
244
|
+
const shouldUseDetailsData = hasTemplateSupport;
|
|
231
245
|
|
|
232
246
|
return (
|
|
233
247
|
<Details
|
|
234
248
|
documentTypeLabel={label}
|
|
235
249
|
id={selectedDocument}
|
|
236
|
-
documentData={
|
|
250
|
+
documentData={
|
|
251
|
+
shouldUseDetailsData ? (detailsData ?? rowSnapshot) : undefined
|
|
252
|
+
}
|
|
253
|
+
row={rowSnapshot}
|
|
237
254
|
handleBack={handleBack}
|
|
238
255
|
columns={columns}
|
|
239
256
|
dataMaskMap={resolvedDataMaskMap}
|
|
@@ -267,7 +284,7 @@ export default function DocumentTable({
|
|
|
267
284
|
setSearchTerm={setSearchTerm}
|
|
268
285
|
setColumn={setSearchColumn}
|
|
269
286
|
searchCols={searchableCols || []}
|
|
270
|
-
selectedColumn={
|
|
287
|
+
selectedColumn={effectiveSearchColumn}
|
|
271
288
|
/>
|
|
272
289
|
{filterLogic?.map((filter) => (
|
|
273
290
|
<filter.FilterComponent
|
|
@@ -357,6 +374,13 @@ export default function DocumentTable({
|
|
|
357
374
|
rowsPerPageOptions={[5, 10]}
|
|
358
375
|
onPageChange={handleChangePage}
|
|
359
376
|
onRowsPerPageChange={handleChangePageSize}
|
|
377
|
+
slotProps={{
|
|
378
|
+
actions: {
|
|
379
|
+
nextButton: {
|
|
380
|
+
disabled: !hasNextPage,
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
}}
|
|
360
384
|
/>
|
|
361
385
|
</>
|
|
362
386
|
);
|
|
@@ -62,6 +62,21 @@ export const TableDashboard = ({ resources, viewConfigQuery }: TableProps) => {
|
|
|
62
62
|
pageSize={pageSize}
|
|
63
63
|
setPageSize={setPageSize}
|
|
64
64
|
/>
|
|
65
|
+
{/* CHILD TABLE */}
|
|
66
|
+
{resource.children?.length ? (
|
|
67
|
+
<Box mt={6}>
|
|
68
|
+
{resource.children.map((child, idx) => (
|
|
69
|
+
<Box key={child.name + String(idx)} mt={4}>
|
|
70
|
+
<Table
|
|
71
|
+
viewConfigQuery={viewConfigQuery}
|
|
72
|
+
resource={child}
|
|
73
|
+
pageSize={pageSize}
|
|
74
|
+
setPageSize={setPageSize}
|
|
75
|
+
/>
|
|
76
|
+
</Box>
|
|
77
|
+
))}
|
|
78
|
+
</Box>
|
|
79
|
+
) : null}
|
|
65
80
|
</TabPanel>
|
|
66
81
|
</Box>
|
|
67
82
|
))}
|
|
@@ -66,7 +66,7 @@ export const resolveCellUI = (
|
|
|
66
66
|
) => {
|
|
67
67
|
if (Object.hasOwn(handlers, col.Data)) return handlers?.[col?.Data];
|
|
68
68
|
|
|
69
|
-
return (value: string | number) => value;
|
|
69
|
+
return (value: string | number | string[]) => value;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
export function resolveActions(
|
|
@@ -30,20 +30,28 @@ type BaseAction = {
|
|
|
30
30
|
icon?: SvgIconComponent;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
type MutationArgs = {
|
|
34
|
+
resourceName: string;
|
|
35
|
+
docId: string | number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type BulkMutationArgs = {
|
|
39
|
+
resourceName: string;
|
|
40
|
+
selectedIds: (string | number)[];
|
|
41
|
+
};
|
|
42
|
+
|
|
33
43
|
export type UIAction = BaseAction & {
|
|
34
44
|
isEnabled?: (row: Record<string, unknown>) => boolean;
|
|
35
|
-
mutation: (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}) => UseMutationOptions<any, any, any, any>;
|
|
45
|
+
mutation: (
|
|
46
|
+
args: MutationArgs,
|
|
47
|
+
) => UseMutationOptions<unknown, unknown, MutationArgs, unknown>;
|
|
39
48
|
};
|
|
40
49
|
|
|
41
50
|
export type UIBulkAction = BaseAction & {
|
|
42
51
|
isAllowed?: (rows: Record<string, unknown>[]) => boolean;
|
|
43
|
-
mutation: (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}) => UseMutationOptions<any, any, any, any>;
|
|
52
|
+
mutation: (
|
|
53
|
+
args: BulkMutationArgs,
|
|
54
|
+
) => UseMutationOptions<unknown, unknown, BulkMutationArgs, unknown>;
|
|
47
55
|
};
|
|
48
56
|
|
|
49
57
|
/**
|
|
@@ -86,4 +94,7 @@ export interface Resource {
|
|
|
86
94
|
// Actions and Bulk Actions
|
|
87
95
|
actions?: Record<string, UIAction>;
|
|
88
96
|
bulkActions?: Record<string, UIBulkAction>;
|
|
97
|
+
|
|
98
|
+
// Children resources for nested tables
|
|
99
|
+
children?: Resource[];
|
|
89
100
|
}
|