@evenicanpm/portal-table-ui 2.4.1 → 2.5.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 +4 -1
- package/src/features/table/components/dashboard/table/details/details.tsx +2 -3
- package/src/features/table/components/dashboard/table/details/templates/invoice-details.tsx +4 -4
- package/src/features/table/components/dashboard/table/table-header.tsx +12 -3
- package/src/features/table/components/dashboard/table/table-row/action-button.tsx +4 -2
- package/src/features/table/components/dashboard/table/table-row/table-row.tsx +3 -7
- package/src/features/table/components/dashboard/table/table.tsx +299 -97
- package/src/features/table/types/queries.ts +3 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evenicanpm/portal-table-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@mui/lab": "^7.0.1-beta.19",
|
|
40
40
|
"date-fns": "^4.1.0",
|
|
41
41
|
"zod": "^4.4.3",
|
|
42
|
-
"@evenicanpm/ui": "2.
|
|
42
|
+
"@evenicanpm/ui": "2.5.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -8,6 +8,7 @@ interface BulkActionButtonProps {
|
|
|
8
8
|
selectedIds: (string | number)[];
|
|
9
9
|
clearSelection: () => void;
|
|
10
10
|
selectedRows: Types.Queries.Row[];
|
|
11
|
+
selectionRequired: boolean;
|
|
11
12
|
}
|
|
12
13
|
export function BulkActionButton({
|
|
13
14
|
action,
|
|
@@ -15,13 +16,15 @@ export function BulkActionButton({
|
|
|
15
16
|
selectedIds,
|
|
16
17
|
clearSelection,
|
|
17
18
|
selectedRows,
|
|
19
|
+
selectionRequired,
|
|
18
20
|
}: BulkActionButtonProps) {
|
|
19
21
|
const mutationOptions = action.mutation({
|
|
20
22
|
resourceName,
|
|
21
23
|
selectedIds,
|
|
22
24
|
});
|
|
23
25
|
|
|
24
|
-
const
|
|
26
|
+
const isAllowed = action.isAllowed ? action.isAllowed(selectedRows) : true;
|
|
27
|
+
const allowed = !selectionRequired || isAllowed;
|
|
25
28
|
const { mutate, isPending } = useMutation(mutationOptions);
|
|
26
29
|
|
|
27
30
|
const handleClick = () => {
|
|
@@ -6,7 +6,6 @@ import { Box, Button } from "@mui/material";
|
|
|
6
6
|
import Grid from "@mui/material/Grid2";
|
|
7
7
|
import { resolvers, transformers } from "../../../../logic";
|
|
8
8
|
import type * as Types from "../../../../types";
|
|
9
|
-
import type { Column } from "../../../../types";
|
|
10
9
|
import type { StatusColorMap, StatusTextMap } from "../../../../types/schemas";
|
|
11
10
|
import { StatusPill } from "../status-pill";
|
|
12
11
|
import { DetailsRowLabel } from "./label";
|
|
@@ -29,7 +28,7 @@ interface DetailsProps {
|
|
|
29
28
|
/** Data mask map */
|
|
30
29
|
dataMaskMap: Types.Handlers.DataMaskMap;
|
|
31
30
|
/** Table columns **/
|
|
32
|
-
columns: Column[];
|
|
31
|
+
columns: Types.Column[];
|
|
33
32
|
/** */
|
|
34
33
|
statusColorMap: StatusColorMap;
|
|
35
34
|
/** */
|
|
@@ -104,7 +103,7 @@ const Details = ({
|
|
|
104
103
|
"@media print": { display: "inline-block" },
|
|
105
104
|
}}
|
|
106
105
|
>
|
|
107
|
-
{documentTypeLabel.slice(0,
|
|
106
|
+
{documentTypeLabel.slice(0, -1)} #{id}
|
|
108
107
|
</H6>
|
|
109
108
|
<Subtitle sx={{ "@media print": { display: "none" } }}>
|
|
110
109
|
DETAILS
|
|
@@ -50,6 +50,10 @@ const formatDate = (dateString?: string): string => {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
function handleOnClick() {
|
|
54
|
+
globalThis.print();
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
/**
|
|
54
58
|
* InvoiceType enum type.
|
|
55
59
|
*/
|
|
@@ -269,10 +273,6 @@ export const InvoiceDetailsTemplate = ({
|
|
|
269
273
|
);
|
|
270
274
|
}
|
|
271
275
|
|
|
272
|
-
function handleOnClick() {
|
|
273
|
-
window.print();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
276
|
return (
|
|
277
277
|
<Box
|
|
278
278
|
sx={{
|
|
@@ -14,6 +14,15 @@ const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
|
|
14
14
|
minWidth: 140,
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
|
+
const headerSkeletonKeys = [
|
|
18
|
+
"header-skeleton-1",
|
|
19
|
+
"header-skeleton-2",
|
|
20
|
+
"header-skeleton-3",
|
|
21
|
+
"header-skeleton-4",
|
|
22
|
+
"header-skeleton-5",
|
|
23
|
+
"header-skeleton-6",
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
17
26
|
interface TableHeaderProps {
|
|
18
27
|
hasActions: boolean;
|
|
19
28
|
columns: (Column & { ColumnOrder: number })[] | undefined;
|
|
@@ -35,7 +44,7 @@ export default function TableHeader({
|
|
|
35
44
|
selectAllChecked,
|
|
36
45
|
selectAllIndeterminate,
|
|
37
46
|
onToggleAll,
|
|
38
|
-
}: TableHeaderProps) {
|
|
47
|
+
}: Readonly<TableHeaderProps>) {
|
|
39
48
|
if (loading) {
|
|
40
49
|
return (
|
|
41
50
|
<TableHead>
|
|
@@ -48,8 +57,8 @@ export default function TableHeader({
|
|
|
48
57
|
sx={{ bgcolor: "rgba(255,255,255,0.1)" }}
|
|
49
58
|
/>
|
|
50
59
|
</StyledTableCell>
|
|
51
|
-
{
|
|
52
|
-
<StyledTableCell key={
|
|
60
|
+
{headerSkeletonKeys.map((key) => (
|
|
61
|
+
<StyledTableCell key={key}>
|
|
53
62
|
<Skeleton
|
|
54
63
|
variant="rounded"
|
|
55
64
|
width={100}
|
|
@@ -12,7 +12,7 @@ export function ActionButton({
|
|
|
12
12
|
action,
|
|
13
13
|
resourceName,
|
|
14
14
|
docId,
|
|
15
|
-
}: ActionButtonProps) {
|
|
15
|
+
}: Readonly<ActionButtonProps>) {
|
|
16
16
|
const { mutate, isPending } = useMutation(
|
|
17
17
|
action.mutation({
|
|
18
18
|
resourceName,
|
|
@@ -30,9 +30,11 @@ export function ActionButton({
|
|
|
30
30
|
|
|
31
31
|
if (action.icon) {
|
|
32
32
|
const Icon = action.icon;
|
|
33
|
+
const iconContent = isPending ? <CircularProgress size={26} /> : <Icon />;
|
|
34
|
+
|
|
33
35
|
return (
|
|
34
36
|
<IconButton size="small" onClick={handleClick} disabled={isPending}>
|
|
35
|
-
{
|
|
37
|
+
{iconContent}
|
|
36
38
|
</IconButton>
|
|
37
39
|
);
|
|
38
40
|
}
|
|
@@ -54,7 +54,7 @@ export default function DataTableRow({
|
|
|
54
54
|
isSelected,
|
|
55
55
|
onToggleSelected,
|
|
56
56
|
resourceName,
|
|
57
|
-
}: DocumentTableProps) {
|
|
57
|
+
}: Readonly<DocumentTableProps>) {
|
|
58
58
|
const visibleActions = actions.filter(
|
|
59
59
|
(a) => !a.isEnabled || a.isEnabled(row),
|
|
60
60
|
);
|
|
@@ -80,16 +80,12 @@ export default function DataTableRow({
|
|
|
80
80
|
/>
|
|
81
81
|
</TableCell>
|
|
82
82
|
|
|
83
|
-
{columnLogic?.map((col
|
|
83
|
+
{columnLogic?.map((col) => {
|
|
84
84
|
// --- Cell Render --- //
|
|
85
85
|
const rawValue = row[col.id];
|
|
86
86
|
const maskedValue = col.maskFn(rawValue, col?.mask);
|
|
87
87
|
const cellValue = col.cellFn(maskedValue);
|
|
88
|
-
return
|
|
89
|
-
<StyledTableCell key={rawValue + String(i)}>
|
|
90
|
-
{cellValue}
|
|
91
|
-
</StyledTableCell>
|
|
92
|
-
);
|
|
88
|
+
return <StyledTableCell key={col?.id}>{cellValue}</StyledTableCell>;
|
|
93
89
|
})}
|
|
94
90
|
{visibleActions.length > 0 && (
|
|
95
91
|
<TableCell
|
|
@@ -9,7 +9,7 @@ import type React from "react";
|
|
|
9
9
|
import { useState } from "react";
|
|
10
10
|
import { resolvers, transformers } from "../../../logic";
|
|
11
11
|
import type * as Types from "../../../types";
|
|
12
|
-
import type {
|
|
12
|
+
import type { StatusColorMap, StatusTextMap } from "../../../types/schemas";
|
|
13
13
|
import { BulkActionButton } from "./bulk-action-button";
|
|
14
14
|
import { dataMaskMap as dataMaskMapLocal } from "./data-mask-map";
|
|
15
15
|
import { Details } from "./details";
|
|
@@ -24,6 +24,257 @@ import { useSelectRows } from "./use-select-rows";
|
|
|
24
24
|
|
|
25
25
|
type Id = string | number;
|
|
26
26
|
|
|
27
|
+
type ColumnLogic =
|
|
28
|
+
| {
|
|
29
|
+
id: string;
|
|
30
|
+
mask: string | undefined;
|
|
31
|
+
maskFn: (value: unknown, mask?: string) => string | number | string[];
|
|
32
|
+
cellFn: (value: string | number | string[]) => React.ReactNode;
|
|
33
|
+
}[]
|
|
34
|
+
| undefined;
|
|
35
|
+
|
|
36
|
+
type StatusCellProps = {
|
|
37
|
+
value: string | number | string[];
|
|
38
|
+
statusColorMap: StatusColorMap;
|
|
39
|
+
statusTextMap: StatusTextMap;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function StatusCell({
|
|
43
|
+
value,
|
|
44
|
+
statusColorMap,
|
|
45
|
+
statusTextMap,
|
|
46
|
+
}: Readonly<StatusCellProps>) {
|
|
47
|
+
return (
|
|
48
|
+
<StatusPill
|
|
49
|
+
status={value}
|
|
50
|
+
statusColorMap={statusColorMap}
|
|
51
|
+
statusTextMap={statusTextMap}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createStatusCellRenderer(
|
|
57
|
+
statusColorMap: StatusColorMap,
|
|
58
|
+
statusTextMap: StatusTextMap,
|
|
59
|
+
) {
|
|
60
|
+
return (value: string | number | string[]) => (
|
|
61
|
+
<StatusCell
|
|
62
|
+
value={value}
|
|
63
|
+
statusColorMap={statusColorMap}
|
|
64
|
+
statusTextMap={statusTextMap}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getRowId(row: Types.Queries.Row, field: string): Id | null {
|
|
70
|
+
const v = row?.[field];
|
|
71
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type DocumentTableBodyProps = {
|
|
76
|
+
noResults: boolean;
|
|
77
|
+
rowsPending: boolean;
|
|
78
|
+
colSpan: number;
|
|
79
|
+
resolvedNoResults: React.ReactNode;
|
|
80
|
+
pageSize: number;
|
|
81
|
+
rows: Types.Queries.TableRows | undefined;
|
|
82
|
+
idField: string;
|
|
83
|
+
columnLogic: ColumnLogic;
|
|
84
|
+
resolvedActions: Types.Schemas.UIAction[];
|
|
85
|
+
selectedIds: Set<Id>;
|
|
86
|
+
onSelectDocument: React.Dispatch<
|
|
87
|
+
React.SetStateAction<string | number | null>
|
|
88
|
+
>;
|
|
89
|
+
onToggleSelected: (id: Id) => void;
|
|
90
|
+
resourceName: string;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function DocumentTableBody({
|
|
94
|
+
noResults,
|
|
95
|
+
rowsPending,
|
|
96
|
+
colSpan,
|
|
97
|
+
resolvedNoResults,
|
|
98
|
+
pageSize,
|
|
99
|
+
rows,
|
|
100
|
+
idField,
|
|
101
|
+
columnLogic,
|
|
102
|
+
resolvedActions,
|
|
103
|
+
selectedIds,
|
|
104
|
+
onSelectDocument,
|
|
105
|
+
onToggleSelected,
|
|
106
|
+
resourceName,
|
|
107
|
+
}: Readonly<DocumentTableBodyProps>) {
|
|
108
|
+
if (noResults) {
|
|
109
|
+
return (
|
|
110
|
+
<TableRow>
|
|
111
|
+
<TableCell
|
|
112
|
+
colSpan={colSpan || 1}
|
|
113
|
+
align="center"
|
|
114
|
+
sx={{ py: 6, color: "text.secondary" }}
|
|
115
|
+
>
|
|
116
|
+
{resolvedNoResults}
|
|
117
|
+
</TableCell>
|
|
118
|
+
</TableRow>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (rowsPending) {
|
|
123
|
+
return <TableRowSkeleton pageSize={pageSize} colSpan={colSpan} />;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<>
|
|
128
|
+
{rows?.data?.map((row) => {
|
|
129
|
+
const id = getRowId(row, idField);
|
|
130
|
+
if (id === null) return null;
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<DataTableRow
|
|
134
|
+
key={id}
|
|
135
|
+
columnLogic={columnLogic}
|
|
136
|
+
row={row}
|
|
137
|
+
handleClick={onSelectDocument}
|
|
138
|
+
id={id}
|
|
139
|
+
actions={resolvedActions}
|
|
140
|
+
isSelected={selectedIds.has(id)}
|
|
141
|
+
onToggleSelected={onToggleSelected}
|
|
142
|
+
resourceName={resourceName}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
})}
|
|
146
|
+
</>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resolveDetailsQueryOptions(
|
|
151
|
+
resource: Types.Schemas.Resource,
|
|
152
|
+
hasTemplateSupport: boolean,
|
|
153
|
+
selectedDocument: string | number | null,
|
|
154
|
+
) {
|
|
155
|
+
if (hasTemplateSupport && selectedDocument && resource.detailsQueryFn) {
|
|
156
|
+
return resource.detailsQueryFn(String(selectedDocument));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
queryKey: [],
|
|
161
|
+
queryFn: async () => undefined,
|
|
162
|
+
enabled: false,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type DocumentTableDetailsViewProps = {
|
|
167
|
+
selectedDocument: string | number;
|
|
168
|
+
rows: Types.Queries.TableRows | undefined;
|
|
169
|
+
idField: string;
|
|
170
|
+
hasTemplateSupport: boolean;
|
|
171
|
+
detailsData: unknown;
|
|
172
|
+
detailsPending: boolean;
|
|
173
|
+
label: string;
|
|
174
|
+
columns: Types.Column[];
|
|
175
|
+
resolvedDataMaskMap: Types.Handlers.DataMaskMap;
|
|
176
|
+
statusColorMap: StatusColorMap;
|
|
177
|
+
statusTextMap: StatusTextMap;
|
|
178
|
+
detailsRenderMap: Types.Handlers.DetailsRenderMap | undefined;
|
|
179
|
+
detailsTemplate: Types.Schemas.Resource["detailsTemplate"];
|
|
180
|
+
resolvedActions: Types.Schemas.UIAction[];
|
|
181
|
+
resourceName: string;
|
|
182
|
+
onBack: () => void;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
function DocumentTableDetailsView({
|
|
186
|
+
selectedDocument,
|
|
187
|
+
rows,
|
|
188
|
+
idField,
|
|
189
|
+
hasTemplateSupport,
|
|
190
|
+
detailsData,
|
|
191
|
+
detailsPending,
|
|
192
|
+
label,
|
|
193
|
+
columns,
|
|
194
|
+
resolvedDataMaskMap,
|
|
195
|
+
statusColorMap,
|
|
196
|
+
statusTextMap,
|
|
197
|
+
detailsRenderMap,
|
|
198
|
+
detailsTemplate,
|
|
199
|
+
resolvedActions,
|
|
200
|
+
resourceName,
|
|
201
|
+
onBack,
|
|
202
|
+
}: Readonly<DocumentTableDetailsViewProps>) {
|
|
203
|
+
const rowSnapshot =
|
|
204
|
+
rows?.data.find((doc) => doc?.[idField] === selectedDocument) ?? {};
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<Details
|
|
208
|
+
documentTypeLabel={label}
|
|
209
|
+
id={selectedDocument}
|
|
210
|
+
documentData={
|
|
211
|
+
hasTemplateSupport ? (detailsData ?? rowSnapshot) : undefined
|
|
212
|
+
}
|
|
213
|
+
row={rowSnapshot}
|
|
214
|
+
handleBack={onBack}
|
|
215
|
+
columns={columns}
|
|
216
|
+
dataMaskMap={resolvedDataMaskMap}
|
|
217
|
+
statusColorMap={statusColorMap}
|
|
218
|
+
statusTextMap={statusTextMap}
|
|
219
|
+
detailsRenderMap={detailsRenderMap}
|
|
220
|
+
template={hasTemplateSupport ? detailsTemplate : undefined}
|
|
221
|
+
loading={hasTemplateSupport ? detailsPending : false}
|
|
222
|
+
actions={resolvedActions}
|
|
223
|
+
resourceName={resourceName}
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
type BulkActionButtonsProps = {
|
|
229
|
+
hasBulkActions: boolean;
|
|
230
|
+
resolvedBulkActions: Types.Schemas.UIBulkAction[];
|
|
231
|
+
rows: Types.Queries.TableRows | undefined;
|
|
232
|
+
idField: string;
|
|
233
|
+
selectedIds: Set<Id>;
|
|
234
|
+
resourceName: string;
|
|
235
|
+
clearSelection: () => void;
|
|
236
|
+
selectionRequired: boolean;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
function BulkActionButtons({
|
|
240
|
+
hasBulkActions,
|
|
241
|
+
resolvedBulkActions,
|
|
242
|
+
rows,
|
|
243
|
+
idField,
|
|
244
|
+
selectedIds,
|
|
245
|
+
resourceName,
|
|
246
|
+
clearSelection,
|
|
247
|
+
selectionRequired,
|
|
248
|
+
}: Readonly<BulkActionButtonsProps>) {
|
|
249
|
+
if (!hasBulkActions) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<>
|
|
255
|
+
{resolvedBulkActions.map((action) => {
|
|
256
|
+
const selectedRows =
|
|
257
|
+
rows?.data?.filter((row) => {
|
|
258
|
+
const id = getRowId(row, idField);
|
|
259
|
+
return id !== null && selectedIds.has(id);
|
|
260
|
+
}) ?? [];
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<BulkActionButton
|
|
264
|
+
key={action.label}
|
|
265
|
+
action={action}
|
|
266
|
+
resourceName={resourceName}
|
|
267
|
+
selectionRequired={selectionRequired}
|
|
268
|
+
selectedIds={Array.from(selectedIds)}
|
|
269
|
+
selectedRows={selectedRows}
|
|
270
|
+
clearSelection={clearSelection}
|
|
271
|
+
/>
|
|
272
|
+
);
|
|
273
|
+
})}
|
|
274
|
+
</>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
27
278
|
export type DocumentTableProps = {
|
|
28
279
|
resource: Types.Schemas.Resource;
|
|
29
280
|
pageSize: number;
|
|
@@ -58,9 +309,9 @@ export default function DocumentTable({
|
|
|
58
309
|
>(null);
|
|
59
310
|
|
|
60
311
|
const selectedDocument =
|
|
61
|
-
selectedDocumentProp
|
|
62
|
-
?
|
|
63
|
-
:
|
|
312
|
+
selectedDocumentProp === undefined
|
|
313
|
+
? selectedDocumentLocal
|
|
314
|
+
: selectedDocumentProp;
|
|
64
315
|
const setSelectedDocument =
|
|
65
316
|
setSelectedDocumentProp ?? setSelectedDocumentLocal;
|
|
66
317
|
const [filters, setFilters] = useState<Record<string, string>>({});
|
|
@@ -98,13 +349,7 @@ export default function DocumentTable({
|
|
|
98
349
|
|
|
99
350
|
const cellRenderMapDefaults = {
|
|
100
351
|
...columnRenderMap,
|
|
101
|
-
Status: (
|
|
102
|
-
<StatusPill
|
|
103
|
-
status={value}
|
|
104
|
-
statusColorMap={statusColorMap}
|
|
105
|
-
statusTextMap={statusTextMap}
|
|
106
|
-
/>
|
|
107
|
-
),
|
|
352
|
+
Status: createStatusCellRenderer(statusColorMap, statusTextMap),
|
|
108
353
|
};
|
|
109
354
|
|
|
110
355
|
// --- State Handlers --- //
|
|
@@ -155,7 +400,7 @@ export default function DocumentTable({
|
|
|
155
400
|
|
|
156
401
|
const config = viewConfig ?? fetchedConfig;
|
|
157
402
|
|
|
158
|
-
const configPending =
|
|
403
|
+
const configPending = viewConfig ? false : fetchedConfigPending;
|
|
159
404
|
|
|
160
405
|
const columns = config?.Columns ?? [];
|
|
161
406
|
|
|
@@ -165,8 +410,8 @@ export default function DocumentTable({
|
|
|
165
410
|
const visibleCols = transformers.getVisibleColumns(columns);
|
|
166
411
|
|
|
167
412
|
// Depends on searchableCols, must be declared after transformer
|
|
168
|
-
const [searchColumn, setSearchColumn] = useState<Column>(
|
|
169
|
-
() => searchableCols?.[0] as Column,
|
|
413
|
+
const [searchColumn, setSearchColumn] = useState<Types.Column>(
|
|
414
|
+
() => searchableCols?.[0] as Types.Column,
|
|
170
415
|
);
|
|
171
416
|
|
|
172
417
|
// always derive a safe usable column
|
|
@@ -199,16 +444,10 @@ export default function DocumentTable({
|
|
|
199
444
|
cellFn: resolvers.resolveCellUI(col, cellRenderMapDefaults),
|
|
200
445
|
}));
|
|
201
446
|
|
|
202
|
-
const
|
|
203
|
-
const v = row?.[field];
|
|
204
|
-
if (typeof v === "string" || typeof v === "number") return v;
|
|
205
|
-
return null;
|
|
206
|
-
};
|
|
447
|
+
const getRowIdForField = (row: Types.Queries.Row) => getRowId(row, idField);
|
|
207
448
|
|
|
208
449
|
const currentPageIds: Id[] =
|
|
209
|
-
rows?.data
|
|
210
|
-
?.map((r) => getRowId(r, idField))
|
|
211
|
-
.filter((v): v is Id => v !== null) ?? [];
|
|
450
|
+
rows?.data?.map(getRowIdForField).filter((v): v is Id => v !== null) ?? [];
|
|
212
451
|
|
|
213
452
|
const {
|
|
214
453
|
selectedIds,
|
|
@@ -223,13 +462,7 @@ export default function DocumentTable({
|
|
|
223
462
|
!!resource.detailsQueryFn && !!resource.detailsTemplate;
|
|
224
463
|
|
|
225
464
|
const { data: detailsData, isPending: detailsPending } = useQuery(
|
|
226
|
-
hasTemplateSupport
|
|
227
|
-
? resource.detailsQueryFn(String(selectedDocument))
|
|
228
|
-
: {
|
|
229
|
-
queryKey: [],
|
|
230
|
-
queryFn: async () => undefined,
|
|
231
|
-
enabled: false,
|
|
232
|
-
},
|
|
465
|
+
resolveDetailsQueryOptions(resource, hasTemplateSupport, selectedDocument),
|
|
233
466
|
);
|
|
234
467
|
|
|
235
468
|
const resolvedActions = resolvers.resolveActions(
|
|
@@ -255,29 +488,24 @@ export default function DocumentTable({
|
|
|
255
488
|
|
|
256
489
|
// --- Render --- //
|
|
257
490
|
if (selectedDocument) {
|
|
258
|
-
const rowSnapshot =
|
|
259
|
-
rows?.data.find((doc) => doc?.[idField] === selectedDocument) ?? {};
|
|
260
|
-
|
|
261
|
-
const shouldUseDetailsData = hasTemplateSupport;
|
|
262
|
-
|
|
263
491
|
return (
|
|
264
|
-
<
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
492
|
+
<DocumentTableDetailsView
|
|
493
|
+
selectedDocument={selectedDocument}
|
|
494
|
+
rows={rows}
|
|
495
|
+
idField={idField}
|
|
496
|
+
hasTemplateSupport={hasTemplateSupport}
|
|
497
|
+
detailsData={detailsData}
|
|
498
|
+
detailsPending={detailsPending}
|
|
499
|
+
label={label}
|
|
272
500
|
columns={columns}
|
|
273
|
-
|
|
501
|
+
resolvedDataMaskMap={resolvedDataMaskMap}
|
|
274
502
|
statusColorMap={statusColorMap}
|
|
275
503
|
statusTextMap={statusTextMap}
|
|
276
504
|
detailsRenderMap={detailsRenderMap}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
actions={resolvedActions}
|
|
505
|
+
detailsTemplate={resource.detailsTemplate}
|
|
506
|
+
resolvedActions={resolvedActions}
|
|
280
507
|
resourceName={resource.name}
|
|
508
|
+
onBack={handleBack}
|
|
281
509
|
/>
|
|
282
510
|
);
|
|
283
511
|
}
|
|
@@ -316,25 +544,16 @@ export default function DocumentTable({
|
|
|
316
544
|
/>
|
|
317
545
|
))}
|
|
318
546
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
key={action.label}
|
|
330
|
-
action={action}
|
|
331
|
-
resourceName={resource.name}
|
|
332
|
-
selectedIds={Array.from(selectedIds)}
|
|
333
|
-
selectedRows={selectedRows}
|
|
334
|
-
clearSelection={clearSelection}
|
|
335
|
-
/>
|
|
336
|
-
);
|
|
337
|
-
})}
|
|
547
|
+
<BulkActionButtons
|
|
548
|
+
hasBulkActions={hasBulkActions}
|
|
549
|
+
selectionRequired={config?.SelectionRequired ?? true}
|
|
550
|
+
resolvedBulkActions={resolvedBulkActions}
|
|
551
|
+
rows={rows}
|
|
552
|
+
idField={idField}
|
|
553
|
+
selectedIds={selectedIds}
|
|
554
|
+
resourceName={resource.name}
|
|
555
|
+
clearSelection={clearSelection}
|
|
556
|
+
/>
|
|
338
557
|
</FlexBox>
|
|
339
558
|
<TableContainer className="invoice-list-table-container">
|
|
340
559
|
<Table
|
|
@@ -352,38 +571,21 @@ export default function DocumentTable({
|
|
|
352
571
|
/>
|
|
353
572
|
|
|
354
573
|
<TableBody>
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (id === null) return null;
|
|
371
|
-
|
|
372
|
-
return (
|
|
373
|
-
<DataTableRow
|
|
374
|
-
key={id}
|
|
375
|
-
columnLogic={columnLogic}
|
|
376
|
-
row={row}
|
|
377
|
-
handleClick={setSelectedDocument}
|
|
378
|
-
id={id}
|
|
379
|
-
actions={resolvedActions}
|
|
380
|
-
isSelected={selectedIds.has(id)}
|
|
381
|
-
onToggleSelected={(docId) => toggleOne(docId)}
|
|
382
|
-
resourceName={resource.name}
|
|
383
|
-
/>
|
|
384
|
-
);
|
|
385
|
-
})
|
|
386
|
-
)}
|
|
574
|
+
<DocumentTableBody
|
|
575
|
+
noResults={noResults}
|
|
576
|
+
rowsPending={rowsPending}
|
|
577
|
+
colSpan={colSpan}
|
|
578
|
+
resolvedNoResults={resolvedNoResults}
|
|
579
|
+
pageSize={pageSize}
|
|
580
|
+
rows={rows}
|
|
581
|
+
idField={idField}
|
|
582
|
+
columnLogic={columnLogic}
|
|
583
|
+
resolvedActions={resolvedActions}
|
|
584
|
+
selectedIds={selectedIds}
|
|
585
|
+
onSelectDocument={setSelectedDocument}
|
|
586
|
+
onToggleSelected={toggleOne}
|
|
587
|
+
resourceName={resource.name}
|
|
588
|
+
/>
|
|
387
589
|
</TableBody>
|
|
388
590
|
</Table>
|
|
389
591
|
</TableContainer>
|
|
@@ -6,15 +6,14 @@ import type { ViewConfig } from "../types";
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
export type RowValue = string | number | string[] | Record<string, string>[];
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
*
|
|
11
13
|
* A single row in the data table response object.
|
|
12
14
|
*
|
|
13
15
|
*/
|
|
14
|
-
export type Row = Record<
|
|
15
|
-
string,
|
|
16
|
-
string | number | string[] | Record<string, string>[] | unknown
|
|
17
|
-
>;
|
|
16
|
+
export type Row = Record<string, RowValue>;
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
*
|