@evenicanpm/portal-table-ui 2.4.2 → 2.5.1
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 +2 -2
- 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 +296 -98
- 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.1",
|
|
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.1"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -45,7 +45,7 @@ export function BulkActionButton({
|
|
|
45
45
|
<IconButton
|
|
46
46
|
size="small"
|
|
47
47
|
onClick={handleClick}
|
|
48
|
-
disabled={isPending ||
|
|
48
|
+
disabled={isPending || !allowed}
|
|
49
49
|
sx={{
|
|
50
50
|
border: 1,
|
|
51
51
|
borderRadius: 1,
|
|
@@ -70,7 +70,7 @@ export function BulkActionButton({
|
|
|
70
70
|
onClick={handleClick}
|
|
71
71
|
size="large"
|
|
72
72
|
sx={{ textWrap: "nowrap", minHeight: "fit-content" }}
|
|
73
|
-
disabled={isPending ||
|
|
73
|
+
disabled={isPending || !allowed}
|
|
74
74
|
>
|
|
75
75
|
{isPending ? (
|
|
76
76
|
<CircularProgress size={22} />
|
|
@@ -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,254 @@ 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
|
+
};
|
|
237
|
+
|
|
238
|
+
function BulkActionButtons({
|
|
239
|
+
hasBulkActions,
|
|
240
|
+
resolvedBulkActions,
|
|
241
|
+
rows,
|
|
242
|
+
idField,
|
|
243
|
+
selectedIds,
|
|
244
|
+
resourceName,
|
|
245
|
+
clearSelection,
|
|
246
|
+
}: Readonly<BulkActionButtonsProps>) {
|
|
247
|
+
if (!hasBulkActions) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<>
|
|
253
|
+
{resolvedBulkActions.map((action) => {
|
|
254
|
+
const selectedRows =
|
|
255
|
+
rows?.data?.filter((row) => {
|
|
256
|
+
const id = getRowId(row, idField);
|
|
257
|
+
return id !== null && selectedIds.has(id);
|
|
258
|
+
}) ?? [];
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<BulkActionButton
|
|
262
|
+
key={action.label}
|
|
263
|
+
action={action}
|
|
264
|
+
resourceName={resourceName}
|
|
265
|
+
selectedIds={Array.from(selectedIds)}
|
|
266
|
+
selectedRows={selectedRows}
|
|
267
|
+
clearSelection={clearSelection}
|
|
268
|
+
/>
|
|
269
|
+
);
|
|
270
|
+
})}
|
|
271
|
+
</>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
27
275
|
export type DocumentTableProps = {
|
|
28
276
|
resource: Types.Schemas.Resource;
|
|
29
277
|
pageSize: number;
|
|
@@ -58,9 +306,9 @@ export default function DocumentTable({
|
|
|
58
306
|
>(null);
|
|
59
307
|
|
|
60
308
|
const selectedDocument =
|
|
61
|
-
selectedDocumentProp
|
|
62
|
-
?
|
|
63
|
-
:
|
|
309
|
+
selectedDocumentProp === undefined
|
|
310
|
+
? selectedDocumentLocal
|
|
311
|
+
: selectedDocumentProp;
|
|
64
312
|
const setSelectedDocument =
|
|
65
313
|
setSelectedDocumentProp ?? setSelectedDocumentLocal;
|
|
66
314
|
const [filters, setFilters] = useState<Record<string, string>>({});
|
|
@@ -98,13 +346,7 @@ export default function DocumentTable({
|
|
|
98
346
|
|
|
99
347
|
const cellRenderMapDefaults = {
|
|
100
348
|
...columnRenderMap,
|
|
101
|
-
Status: (
|
|
102
|
-
<StatusPill
|
|
103
|
-
status={value}
|
|
104
|
-
statusColorMap={statusColorMap}
|
|
105
|
-
statusTextMap={statusTextMap}
|
|
106
|
-
/>
|
|
107
|
-
),
|
|
349
|
+
Status: createStatusCellRenderer(statusColorMap, statusTextMap),
|
|
108
350
|
};
|
|
109
351
|
|
|
110
352
|
// --- State Handlers --- //
|
|
@@ -153,9 +395,9 @@ export default function DocumentTable({
|
|
|
153
395
|
enabled: !!viewConfigQuery && !viewConfig,
|
|
154
396
|
});
|
|
155
397
|
|
|
156
|
-
const config = viewConfig ?? fetchedConfig;
|
|
398
|
+
const config: Types.ViewConfig | undefined = viewConfig ?? fetchedConfig;
|
|
157
399
|
|
|
158
|
-
const configPending =
|
|
400
|
+
const configPending = viewConfig ? false : fetchedConfigPending;
|
|
159
401
|
|
|
160
402
|
const columns = config?.Columns ?? [];
|
|
161
403
|
|
|
@@ -165,8 +407,8 @@ export default function DocumentTable({
|
|
|
165
407
|
const visibleCols = transformers.getVisibleColumns(columns);
|
|
166
408
|
|
|
167
409
|
// Depends on searchableCols, must be declared after transformer
|
|
168
|
-
const [searchColumn, setSearchColumn] = useState<Column>(
|
|
169
|
-
() => searchableCols?.[0] as Column,
|
|
410
|
+
const [searchColumn, setSearchColumn] = useState<Types.Column>(
|
|
411
|
+
() => searchableCols?.[0] as Types.Column,
|
|
170
412
|
);
|
|
171
413
|
|
|
172
414
|
// always derive a safe usable column
|
|
@@ -199,16 +441,10 @@ export default function DocumentTable({
|
|
|
199
441
|
cellFn: resolvers.resolveCellUI(col, cellRenderMapDefaults),
|
|
200
442
|
}));
|
|
201
443
|
|
|
202
|
-
const
|
|
203
|
-
const v = row?.[field];
|
|
204
|
-
if (typeof v === "string" || typeof v === "number") return v;
|
|
205
|
-
return null;
|
|
206
|
-
};
|
|
444
|
+
const getRowIdForField = (row: Types.Queries.Row) => getRowId(row, idField);
|
|
207
445
|
|
|
208
446
|
const currentPageIds: Id[] =
|
|
209
|
-
rows?.data
|
|
210
|
-
?.map((r) => getRowId(r, idField))
|
|
211
|
-
.filter((v): v is Id => v !== null) ?? [];
|
|
447
|
+
rows?.data?.map(getRowIdForField).filter((v): v is Id => v !== null) ?? [];
|
|
212
448
|
|
|
213
449
|
const {
|
|
214
450
|
selectedIds,
|
|
@@ -223,13 +459,7 @@ export default function DocumentTable({
|
|
|
223
459
|
!!resource.detailsQueryFn && !!resource.detailsTemplate;
|
|
224
460
|
|
|
225
461
|
const { data: detailsData, isPending: detailsPending } = useQuery(
|
|
226
|
-
hasTemplateSupport
|
|
227
|
-
? resource.detailsQueryFn(String(selectedDocument))
|
|
228
|
-
: {
|
|
229
|
-
queryKey: [],
|
|
230
|
-
queryFn: async () => undefined,
|
|
231
|
-
enabled: false,
|
|
232
|
-
},
|
|
462
|
+
resolveDetailsQueryOptions(resource, hasTemplateSupport, selectedDocument),
|
|
233
463
|
);
|
|
234
464
|
|
|
235
465
|
const resolvedActions = resolvers.resolveActions(
|
|
@@ -255,29 +485,24 @@ export default function DocumentTable({
|
|
|
255
485
|
|
|
256
486
|
// --- Render --- //
|
|
257
487
|
if (selectedDocument) {
|
|
258
|
-
const rowSnapshot =
|
|
259
|
-
rows?.data.find((doc) => doc?.[idField] === selectedDocument) ?? {};
|
|
260
|
-
|
|
261
|
-
const shouldUseDetailsData = hasTemplateSupport;
|
|
262
|
-
|
|
263
488
|
return (
|
|
264
|
-
<
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
489
|
+
<DocumentTableDetailsView
|
|
490
|
+
selectedDocument={selectedDocument}
|
|
491
|
+
rows={rows}
|
|
492
|
+
idField={idField}
|
|
493
|
+
hasTemplateSupport={hasTemplateSupport}
|
|
494
|
+
detailsData={detailsData}
|
|
495
|
+
detailsPending={detailsPending}
|
|
496
|
+
label={label}
|
|
272
497
|
columns={columns}
|
|
273
|
-
|
|
498
|
+
resolvedDataMaskMap={resolvedDataMaskMap}
|
|
274
499
|
statusColorMap={statusColorMap}
|
|
275
500
|
statusTextMap={statusTextMap}
|
|
276
501
|
detailsRenderMap={detailsRenderMap}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
actions={resolvedActions}
|
|
502
|
+
detailsTemplate={resource.detailsTemplate}
|
|
503
|
+
resolvedActions={resolvedActions}
|
|
280
504
|
resourceName={resource.name}
|
|
505
|
+
onBack={handleBack}
|
|
281
506
|
/>
|
|
282
507
|
);
|
|
283
508
|
}
|
|
@@ -316,25 +541,15 @@ export default function DocumentTable({
|
|
|
316
541
|
/>
|
|
317
542
|
))}
|
|
318
543
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
<BulkActionButton
|
|
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
|
-
})}
|
|
544
|
+
<BulkActionButtons
|
|
545
|
+
hasBulkActions={hasBulkActions}
|
|
546
|
+
resolvedBulkActions={resolvedBulkActions}
|
|
547
|
+
rows={rows}
|
|
548
|
+
idField={idField}
|
|
549
|
+
selectedIds={selectedIds}
|
|
550
|
+
resourceName={resource.name}
|
|
551
|
+
clearSelection={clearSelection}
|
|
552
|
+
/>
|
|
338
553
|
</FlexBox>
|
|
339
554
|
<TableContainer className="invoice-list-table-container">
|
|
340
555
|
<Table
|
|
@@ -352,38 +567,21 @@ export default function DocumentTable({
|
|
|
352
567
|
/>
|
|
353
568
|
|
|
354
569
|
<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
|
-
)}
|
|
570
|
+
<DocumentTableBody
|
|
571
|
+
noResults={noResults}
|
|
572
|
+
rowsPending={rowsPending}
|
|
573
|
+
colSpan={colSpan}
|
|
574
|
+
resolvedNoResults={resolvedNoResults}
|
|
575
|
+
pageSize={pageSize}
|
|
576
|
+
rows={rows}
|
|
577
|
+
idField={idField}
|
|
578
|
+
columnLogic={columnLogic}
|
|
579
|
+
resolvedActions={resolvedActions}
|
|
580
|
+
selectedIds={selectedIds}
|
|
581
|
+
onSelectDocument={setSelectedDocument}
|
|
582
|
+
onToggleSelected={toggleOne}
|
|
583
|
+
resourceName={resource.name}
|
|
584
|
+
/>
|
|
387
585
|
</TableBody>
|
|
388
586
|
</Table>
|
|
389
587
|
</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
|
*
|