@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.
Files changed (63) hide show
  1. package/package.json +4 -4
  2. package/src/auth/auth-context.tsx +15 -9
  3. package/src/features/table/components/dashboard/table/bulk-action-button.tsx +76 -0
  4. package/src/features/table/components/dashboard/table/data-mask-map.ts +1 -1
  5. package/src/features/table/components/dashboard/table/details/details.tsx +63 -35
  6. package/src/features/table/components/dashboard/table/details/rows/downloads.tsx +0 -1
  7. package/src/features/table/components/dashboard/table/details/rows/primitive.tsx +0 -1
  8. package/src/features/table/components/dashboard/table/details/rows/table.tsx +0 -1
  9. package/src/features/table/components/dashboard/table/details/templates/invoice-details.tsx +440 -0
  10. package/src/features/table/components/dashboard/table/details/templates/template-registry.ts +39 -0
  11. package/src/features/table/components/dashboard/table/filter-render-map.tsx +2 -1
  12. package/src/features/table/components/dashboard/table/filters/date-filter.tsx +5 -5
  13. package/src/features/table/components/dashboard/table/filters/status-filter.tsx +2 -2
  14. package/src/features/table/components/dashboard/table/no-results.tsx +1 -1
  15. package/src/features/table/components/dashboard/table/table-header.tsx +41 -5
  16. package/src/features/table/components/dashboard/table/table-row/action-button.tsx +32 -24
  17. package/src/features/table/components/dashboard/table/table-row/table-row.tsx +60 -13
  18. package/src/features/table/components/dashboard/table/table-row-skeleton.tsx +0 -1
  19. package/src/features/table/components/dashboard/table/table.tsx +143 -30
  20. package/src/features/table/components/dashboard/table/use-select-rows.ts +53 -0
  21. package/src/features/table/components/dashboard/table-dashboard.tsx +2 -2
  22. package/src/features/table/logic/resolvers.ts +43 -0
  23. package/src/features/table/types/schemas.ts +39 -0
  24. package/src/index.ts +1 -2
  25. package/dist/auth/auth-context.d.ts +0 -36
  26. package/dist/features/settings/index.d.ts +0 -2
  27. package/dist/features/table/components/dashboard/index.d.ts +0 -1
  28. package/dist/features/table/components/dashboard/table/actions.d.ts +0 -14
  29. package/dist/features/table/components/dashboard/table/data-mask-map.d.ts +0 -2
  30. package/dist/features/table/components/dashboard/table/details/details.d.ts +0 -27
  31. package/dist/features/table/components/dashboard/table/details/index.d.ts +0 -1
  32. package/dist/features/table/components/dashboard/table/details/label.d.ts +0 -5
  33. package/dist/features/table/components/dashboard/table/details/rows/downloads.d.ts +0 -6
  34. package/dist/features/table/components/dashboard/table/details/rows/index.d.ts +0 -3
  35. package/dist/features/table/components/dashboard/table/details/rows/primitive.d.ts +0 -6
  36. package/dist/features/table/components/dashboard/table/details/rows/table.d.ts +0 -17
  37. package/dist/features/table/components/dashboard/table/details/styles.d.ts +0 -10
  38. package/dist/features/table/components/dashboard/table/filter-render-map.d.ts +0 -2
  39. package/dist/features/table/components/dashboard/table/filters/date-filter.d.ts +0 -2
  40. package/dist/features/table/components/dashboard/table/filters/status-filter.d.ts +0 -2
  41. package/dist/features/table/components/dashboard/table/index.d.ts +0 -2
  42. package/dist/features/table/components/dashboard/table/no-results.d.ts +0 -2
  43. package/dist/features/table/components/dashboard/table/status-pill.d.ts +0 -8
  44. package/dist/features/table/components/dashboard/table/table-header.d.ts +0 -16
  45. package/dist/features/table/components/dashboard/table/table-input.d.ts +0 -11
  46. package/dist/features/table/components/dashboard/table/table-row/action-button.d.ts +0 -8
  47. package/dist/features/table/components/dashboard/table/table-row/table-row.d.ts +0 -32
  48. package/dist/features/table/components/dashboard/table/table-row-skeleton.d.ts +0 -6
  49. package/dist/features/table/components/dashboard/table/table.d.ts +0 -18
  50. package/dist/features/table/components/dashboard/table-dashboard.d.ts +0 -22
  51. package/dist/features/table/index.d.ts +0 -8
  52. package/dist/features/table/logic/index.d.ts +0 -23
  53. package/dist/features/table/logic/resolvers.d.ts +0 -21
  54. package/dist/features/table/logic/transformers.d.ts +0 -35
  55. package/dist/features/table/logic/types.d.ts +0 -8
  56. package/dist/features/table/types/handlers.d.ts +0 -53
  57. package/dist/features/table/types/index.d.ts +0 -69
  58. package/dist/features/table/types/queries.d.ts +0 -27
  59. package/dist/features/table/types/schemas.d.ts +0 -31
  60. package/dist/index.d.ts +0 -63
  61. package/dist/portal.d.ts +0 -38
  62. package/dist/tsconfig.tsbuildinfo +0 -1
  63. 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.6.0",
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.6.0",
45
- "@evenicanpm/ui": "^1.6.0",
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": "9b18fe6294cc0e57332ec8703825a609ac7b73b2"
50
+ "gitHead": "485f77c5cce7c36849fb9d416d1e503d0f9299a3"
51
51
  }
@@ -1,4 +1,9 @@
1
- import { createContext, type PropsWithChildren, useContext } from "react";
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
- isAuthenticated,
69
- session,
70
- logout,
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={{ ...authValue }}>
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
+ }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import * as Table from "../../../types/";
3
2
  import { format } from "date-fns";
3
+ import type * as Table from "../../../types/";
4
4
 
5
5
  export const dataMaskMap: Table.Handlers.DataMaskMap = {
6
6
  // DATE TIME HAS SPECIFIC MASK HANDLING
@@ -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
- // Build Primtive Values (string or number)
52
- const primtiveItems = visibleDetails?.map((col) => ({
53
- key: col?.Data,
54
- value: documentData?.[col?.Data],
55
- maskFn: resolvers.resolveDataMask(col, dataMaskMap),
56
- mask: col?.DataMask,
57
- col: col,
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={"100%"}
85
+ height="100%"
69
86
  >
70
87
  <Button onClick={handleBack} startIcon={<ArrowBackIcon />}>
71
88
  {documentTypeLabel}
72
89
  </Button>
73
- <H6 color={"grey.800"}>
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
- <Grid container spacing={2} sx={{ mt: 2 }}>
81
- {primtiveItems?.map((col) => {
82
- const maskedValue = col.maskFn(col?.value, col?.mask);
83
- const Component = getRowComponent(maskedValue, col?.key);
84
- const sx = getRowLayout(maskedValue, col?.key);
85
-
86
- return (
87
- <Grid key={col?.key} {...sx}>
88
- <DetailsRowLabel label={col?.col?.Label} />
89
- <Component
90
- key={col?.key}
91
- value={maskedValue}
92
- label={col?.key}
93
- column={col?.col}
94
- dataMaskMap={dataMaskMap}
95
- statusColorMap={statusColorMap}
96
- statusTextMap={statusTextMap}
97
- status={col?.value}
98
- />
99
- </Grid>
100
- );
101
- })}
102
- </Grid>
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
  };
@@ -2,7 +2,6 @@ import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
2
2
  import { Button, Stack } from "@mui/material";
3
3
 
4
4
  interface Props {
5
- label: string;
6
5
  value: string[];
7
6
  }
8
7
 
@@ -3,7 +3,6 @@
3
3
  import { Box } from "@mui/material";
4
4
 
5
5
  interface Props {
6
- label: string;
7
6
  value: string | number;
8
7
  }
9
8
 
@@ -15,7 +15,6 @@ import type { DataMaskMap } from "../../../../../types/handlers";
15
15
  interface Props {
16
16
  column: Column;
17
17
  value: Record<string, string>[];
18
- label: string;
19
18
  dataMaskMap: DataMaskMap;
20
19
  }
21
20