@evenicanpm/portal-table-ui 1.6.0 → 1.8.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 +4 -4
- package/src/auth/auth-context.tsx +15 -9
- package/src/features/table/components/dashboard/table/bulk-action-button.tsx +76 -0
- package/src/features/table/components/dashboard/table/data-mask-map.ts +1 -1
- package/src/features/table/components/dashboard/table/details/details.tsx +63 -35
- package/src/features/table/components/dashboard/table/details/rows/downloads.tsx +0 -1
- package/src/features/table/components/dashboard/table/details/rows/primitive.tsx +0 -1
- package/src/features/table/components/dashboard/table/details/rows/table.tsx +0 -1
- package/src/features/table/components/dashboard/table/details/templates/invoice-details.tsx +440 -0
- package/src/features/table/components/dashboard/table/details/templates/template-registry.ts +39 -0
- package/src/features/table/components/dashboard/table/filter-render-map.tsx +2 -1
- package/src/features/table/components/dashboard/table/filters/date-filter.tsx +5 -5
- package/src/features/table/components/dashboard/table/filters/status-filter.tsx +2 -2
- package/src/features/table/components/dashboard/table/no-results.tsx +1 -1
- package/src/features/table/components/dashboard/table/table-header.tsx +41 -5
- package/src/features/table/components/dashboard/table/table-row/action-button.tsx +32 -24
- package/src/features/table/components/dashboard/table/table-row/table-row.tsx +60 -13
- package/src/features/table/components/dashboard/table/table-row-skeleton.tsx +0 -1
- package/src/features/table/components/dashboard/table/table.tsx +143 -30
- package/src/features/table/components/dashboard/table/use-select-rows.ts +53 -0
- package/src/features/table/components/dashboard/table-dashboard.tsx +2 -2
- package/src/features/table/logic/resolvers.ts +43 -0
- package/src/features/table/types/schemas.ts +39 -0
- package/src/index.ts +1 -2
- package/dist/auth/auth-context.d.ts +0 -36
- package/dist/features/settings/index.d.ts +0 -2
- package/dist/features/table/components/dashboard/index.d.ts +0 -1
- package/dist/features/table/components/dashboard/table/actions.d.ts +0 -14
- package/dist/features/table/components/dashboard/table/data-mask-map.d.ts +0 -2
- package/dist/features/table/components/dashboard/table/details/details.d.ts +0 -27
- package/dist/features/table/components/dashboard/table/details/index.d.ts +0 -1
- package/dist/features/table/components/dashboard/table/details/label.d.ts +0 -5
- package/dist/features/table/components/dashboard/table/details/rows/downloads.d.ts +0 -6
- package/dist/features/table/components/dashboard/table/details/rows/index.d.ts +0 -3
- package/dist/features/table/components/dashboard/table/details/rows/primitive.d.ts +0 -6
- package/dist/features/table/components/dashboard/table/details/rows/table.d.ts +0 -17
- package/dist/features/table/components/dashboard/table/details/styles.d.ts +0 -10
- package/dist/features/table/components/dashboard/table/filter-render-map.d.ts +0 -2
- package/dist/features/table/components/dashboard/table/filters/date-filter.d.ts +0 -2
- package/dist/features/table/components/dashboard/table/filters/status-filter.d.ts +0 -2
- package/dist/features/table/components/dashboard/table/index.d.ts +0 -2
- package/dist/features/table/components/dashboard/table/no-results.d.ts +0 -2
- package/dist/features/table/components/dashboard/table/status-pill.d.ts +0 -8
- package/dist/features/table/components/dashboard/table/table-header.d.ts +0 -16
- package/dist/features/table/components/dashboard/table/table-input.d.ts +0 -11
- package/dist/features/table/components/dashboard/table/table-row/action-button.d.ts +0 -8
- package/dist/features/table/components/dashboard/table/table-row/table-row.d.ts +0 -32
- package/dist/features/table/components/dashboard/table/table-row-skeleton.d.ts +0 -6
- package/dist/features/table/components/dashboard/table/table.d.ts +0 -18
- package/dist/features/table/components/dashboard/table-dashboard.d.ts +0 -22
- package/dist/features/table/index.d.ts +0 -8
- package/dist/features/table/logic/index.d.ts +0 -23
- package/dist/features/table/logic/resolvers.d.ts +0 -21
- package/dist/features/table/logic/transformers.d.ts +0 -35
- package/dist/features/table/logic/types.d.ts +0 -8
- package/dist/features/table/types/handlers.d.ts +0 -53
- package/dist/features/table/types/index.d.ts +0 -69
- package/dist/features/table/types/queries.d.ts +0 -27
- package/dist/features/table/types/schemas.d.ts +0 -31
- package/dist/index.d.ts +0 -63
- package/dist/portal.d.ts +0 -38
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/features/table/components/dashboard/table/actions.ts +0 -59
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evenicanpm/portal-table-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@arrows/multimethod": "^2.1.0",
|
|
44
|
-
"@evenicanpm/portal-types-schemas": "^1.
|
|
45
|
-
"@evenicanpm/ui": "^1.
|
|
44
|
+
"@evenicanpm/portal-types-schemas": "^1.8.0",
|
|
45
|
+
"@evenicanpm/ui": "^1.8.0",
|
|
46
46
|
"@mui/lab": "^7.0.1-beta.19",
|
|
47
47
|
"@tanstack/react-query": "^5.90.11",
|
|
48
48
|
"date-fns": "^4.1.0"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "485f77c5cce7c36849fb9d416d1e503d0f9299a3"
|
|
51
51
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
type PropsWithChildren,
|
|
4
|
+
useContext,
|
|
5
|
+
useMemo,
|
|
6
|
+
} from "react";
|
|
2
7
|
|
|
3
8
|
// --- 1. Types --- //
|
|
4
9
|
/**
|
|
@@ -64,16 +69,17 @@ const PortalAuthProvider = ({
|
|
|
64
69
|
}: PropsWithChildren<AuthProviderProps>) => {
|
|
65
70
|
const isAuthenticated = !!session?.email;
|
|
66
71
|
|
|
67
|
-
const authValue =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
const authValue = useMemo(
|
|
73
|
+
() => ({
|
|
74
|
+
isAuthenticated,
|
|
75
|
+
session,
|
|
76
|
+
logout,
|
|
77
|
+
}),
|
|
78
|
+
[isAuthenticated, session, logout],
|
|
79
|
+
);
|
|
72
80
|
|
|
73
81
|
return (
|
|
74
|
-
<AuthContext.Provider value={
|
|
75
|
-
{children}
|
|
76
|
-
</AuthContext.Provider>
|
|
82
|
+
<AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>
|
|
77
83
|
);
|
|
78
84
|
};
|
|
79
85
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Badge, Button, CircularProgress, IconButton } from "@mui/material";
|
|
2
|
+
import { useMutation } from "@tanstack/react-query";
|
|
3
|
+
import type * as Types from "../../../types";
|
|
4
|
+
|
|
5
|
+
interface BulkActionButtonProps {
|
|
6
|
+
action: Types.Schemas.UIBulkAction;
|
|
7
|
+
resourceName: string;
|
|
8
|
+
selectedIds: (string | number)[];
|
|
9
|
+
clearSelection: () => void;
|
|
10
|
+
selectedRows: Types.Queries.Row[];
|
|
11
|
+
}
|
|
12
|
+
export function BulkActionButton({
|
|
13
|
+
action,
|
|
14
|
+
resourceName,
|
|
15
|
+
selectedIds,
|
|
16
|
+
clearSelection,
|
|
17
|
+
selectedRows,
|
|
18
|
+
}: BulkActionButtonProps) {
|
|
19
|
+
const mutationOptions = action.mutation({
|
|
20
|
+
resourceName,
|
|
21
|
+
selectedIds,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const allowed = !action.isAllowed || action.isAllowed(selectedRows);
|
|
25
|
+
const { mutate, isPending } = useMutation(mutationOptions);
|
|
26
|
+
|
|
27
|
+
const handleClick = () => {
|
|
28
|
+
mutate(undefined, {
|
|
29
|
+
onSuccess: () => {
|
|
30
|
+
clearSelection();
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const Icon = action.icon;
|
|
36
|
+
|
|
37
|
+
if (Icon) {
|
|
38
|
+
return (
|
|
39
|
+
<IconButton
|
|
40
|
+
size="small"
|
|
41
|
+
onClick={handleClick}
|
|
42
|
+
disabled={isPending || selectedIds.length === 0 || !allowed}
|
|
43
|
+
sx={{
|
|
44
|
+
border: 1,
|
|
45
|
+
borderRadius: 1,
|
|
46
|
+
paddingInline: 2,
|
|
47
|
+
minHeight: "fit-content",
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
{isPending ? (
|
|
51
|
+
<CircularProgress size={26} />
|
|
52
|
+
) : (
|
|
53
|
+
<Badge badgeContent={selectedIds.length} color="primary">
|
|
54
|
+
<Icon />
|
|
55
|
+
</Badge>
|
|
56
|
+
)}
|
|
57
|
+
</IconButton>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Button
|
|
63
|
+
variant="contained"
|
|
64
|
+
onClick={handleClick}
|
|
65
|
+
size="large"
|
|
66
|
+
sx={{ textWrap: "nowrap", minHeight: "fit-content" }}
|
|
67
|
+
disabled={isPending || selectedIds.length === 0 || !allowed}
|
|
68
|
+
>
|
|
69
|
+
{isPending ? (
|
|
70
|
+
<CircularProgress size={22} />
|
|
71
|
+
) : (
|
|
72
|
+
`${action.label} (${selectedIds.length})`
|
|
73
|
+
)}
|
|
74
|
+
</Button>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -11,6 +11,7 @@ import type { StatusColorMap, StatusTextMap } from "../../../../types/schemas";
|
|
|
11
11
|
import { StatusPill } from "../status-pill";
|
|
12
12
|
import { DetailsRowLabel } from "./label";
|
|
13
13
|
import { DetailsDownloads, DetailsPrimitive, DetailsTable } from "./rows";
|
|
14
|
+
import { detailsTemplateRegistry } from "./templates/template-registry";
|
|
14
15
|
|
|
15
16
|
interface DetailsProps {
|
|
16
17
|
/** What do we do when use clicks back button? */
|
|
@@ -18,7 +19,7 @@ interface DetailsProps {
|
|
|
18
19
|
/** What is the id field name */
|
|
19
20
|
id: string | number;
|
|
20
21
|
/** The actual pojo of data from the API */
|
|
21
|
-
documentData: Types.Queries.Row;
|
|
22
|
+
documentData: Types.Queries.Row | unknown;
|
|
22
23
|
/* the label of the document type ie 'INVOICES' */
|
|
23
24
|
documentTypeLabel: string;
|
|
24
25
|
/** Special instructions for how to render certain properties */
|
|
@@ -31,6 +32,11 @@ interface DetailsProps {
|
|
|
31
32
|
statusColorMap: StatusColorMap;
|
|
32
33
|
/** */
|
|
33
34
|
statusTextMap: StatusTextMap;
|
|
35
|
+
template?: string;
|
|
36
|
+
loading?: boolean;
|
|
37
|
+
|
|
38
|
+
actions?: Types.Schemas.UIAction[];
|
|
39
|
+
resourceName?: string;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
/**
|
|
@@ -45,61 +51,83 @@ const Details = ({
|
|
|
45
51
|
dataMaskMap,
|
|
46
52
|
statusColorMap,
|
|
47
53
|
statusTextMap,
|
|
54
|
+
template,
|
|
55
|
+
actions,
|
|
56
|
+
resourceName,
|
|
57
|
+
loading = false,
|
|
48
58
|
}: DetailsProps) => {
|
|
59
|
+
// Resolve the template component once — undefined when the key is absent or
|
|
60
|
+
// not found in the registry, which gracefully falls back to the generic view.
|
|
61
|
+
const TemplateComponent = template
|
|
62
|
+
? detailsTemplateRegistry[template]
|
|
63
|
+
: undefined;
|
|
64
|
+
|
|
49
65
|
const visibleDetails = transformers.getVisibleDetails(columns);
|
|
50
66
|
|
|
51
|
-
//
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
67
|
+
// Pre-compute primitive items only when the generic grid is needed
|
|
68
|
+
const primitiveItems = TemplateComponent
|
|
69
|
+
? undefined
|
|
70
|
+
: visibleDetails?.map((col) => ({
|
|
71
|
+
key: col.Data,
|
|
72
|
+
value: (documentData as Types.Queries.Row)?.[col.Data],
|
|
73
|
+
maskFn: resolvers.resolveDataMask(col, dataMaskMap),
|
|
74
|
+
mask: col.DataMask,
|
|
75
|
+
col,
|
|
76
|
+
}));
|
|
59
77
|
|
|
60
78
|
return (
|
|
61
79
|
<Box>
|
|
62
|
-
{/* Header */}
|
|
63
80
|
<Box height={48} sx={{ borderBottom: 1, borderColor: "divider" }}>
|
|
64
81
|
<FlexBox
|
|
65
82
|
alignItems="center"
|
|
66
83
|
justifyContent="space-between"
|
|
67
84
|
mb={2}
|
|
68
|
-
height=
|
|
85
|
+
height="100%"
|
|
69
86
|
>
|
|
70
87
|
<Button onClick={handleBack} startIcon={<ArrowBackIcon />}>
|
|
71
88
|
{documentTypeLabel}
|
|
72
89
|
</Button>
|
|
73
|
-
<H6 color=
|
|
90
|
+
<H6 color="grey.800">
|
|
74
91
|
{documentTypeLabel}-{id}
|
|
75
92
|
</H6>
|
|
76
93
|
<Subtitle>DETAILS</Subtitle>
|
|
77
94
|
</FlexBox>
|
|
78
95
|
</Box>
|
|
79
96
|
|
|
80
|
-
<
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
<Box mt={2}>
|
|
98
|
+
{TemplateComponent ? (
|
|
99
|
+
<TemplateComponent
|
|
100
|
+
data={documentData}
|
|
101
|
+
statusColorMap={statusColorMap}
|
|
102
|
+
statusTextMap={statusTextMap}
|
|
103
|
+
loading={loading}
|
|
104
|
+
actions={actions}
|
|
105
|
+
resourceName={resourceName}
|
|
106
|
+
/>
|
|
107
|
+
) : (
|
|
108
|
+
<Grid container spacing={2}>
|
|
109
|
+
{primitiveItems?.map((col) => {
|
|
110
|
+
const maskedValue = col.maskFn(col.value, col.mask);
|
|
111
|
+
const Component = getRowComponent(maskedValue, col.key);
|
|
112
|
+
const sx = getRowLayout(maskedValue, col.key);
|
|
113
|
+
return (
|
|
114
|
+
<Grid key={col.key} {...sx}>
|
|
115
|
+
<DetailsRowLabel label={col.col.Label} />
|
|
116
|
+
<Component
|
|
117
|
+
value={maskedValue}
|
|
118
|
+
label={col.key}
|
|
119
|
+
column={col.col}
|
|
120
|
+
dataMaskMap={dataMaskMap}
|
|
121
|
+
statusColorMap={statusColorMap}
|
|
122
|
+
statusTextMap={statusTextMap}
|
|
123
|
+
status={col.value}
|
|
124
|
+
/>
|
|
125
|
+
</Grid>
|
|
126
|
+
);
|
|
127
|
+
})}
|
|
128
|
+
</Grid>
|
|
129
|
+
)}
|
|
130
|
+
</Box>
|
|
103
131
|
</Box>
|
|
104
132
|
);
|
|
105
133
|
};
|