@evenicanpm/portal-table-ui 1.6.0 → 1.7.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 +3 -3
  2. package/src/auth/auth-context.tsx +15 -9
  3. package/src/features/table/components/dashboard/table/bulk-action-button.tsx +77 -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 +56 -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 +407 -0
  10. package/src/features/table/components/dashboard/table/details/templates/template-registry.ts +36 -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 +61 -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 +141 -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.7.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",
44
+ "@evenicanpm/portal-types-schemas": "^1.7.0",
45
45
  "@evenicanpm/ui": "^1.6.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": "2e25d32705901a4ff2dd43aa420fde931568ad93"
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,77 @@
1
+ import { Badge, Button, CircularProgress, IconButton } from "@mui/material";
2
+ import { useMutation } from "@tanstack/react-query";
3
+ import type { Row } from "src/features/table/types/queries";
4
+ import type { UIBulkAction } from "src/features/table/types/schemas";
5
+
6
+ interface BulkActionButtonProps {
7
+ action: UIBulkAction;
8
+ resourceName: string;
9
+ selectedIds: (string | number)[];
10
+ clearSelection: () => void;
11
+ selectedRows: Row[];
12
+ }
13
+ export function BulkActionButton({
14
+ action,
15
+ resourceName,
16
+ selectedIds,
17
+ clearSelection,
18
+ selectedRows,
19
+ }: BulkActionButtonProps) {
20
+ const mutationOptions = action.mutation({
21
+ resourceName,
22
+ selectedIds,
23
+ });
24
+
25
+ const allowed = !action.isAllowed || action.isAllowed(selectedRows);
26
+ const { mutate, isPending } = useMutation(mutationOptions);
27
+
28
+ const handleClick = () => {
29
+ mutate(undefined, {
30
+ onSuccess: () => {
31
+ clearSelection();
32
+ },
33
+ });
34
+ };
35
+
36
+ const Icon = action.icon;
37
+
38
+ if (Icon) {
39
+ return (
40
+ <IconButton
41
+ size="small"
42
+ onClick={handleClick}
43
+ disabled={isPending || selectedIds.length === 0 || !allowed}
44
+ sx={{
45
+ border: 1,
46
+ borderRadius: 1,
47
+ paddingInline: 2,
48
+ minHeight: "fit-content",
49
+ }}
50
+ >
51
+ {isPending ? (
52
+ <CircularProgress size={26} />
53
+ ) : (
54
+ <Badge badgeContent={selectedIds.length} color="primary">
55
+ <Icon />
56
+ </Badge>
57
+ )}
58
+ </IconButton>
59
+ );
60
+ }
61
+
62
+ return (
63
+ <Button
64
+ variant="contained"
65
+ onClick={handleClick}
66
+ size="large"
67
+ sx={{ textWrap: "nowrap", minHeight: "fit-content" }}
68
+ disabled={isPending || selectedIds.length === 0 || !allowed}
69
+ >
70
+ {isPending ? (
71
+ <CircularProgress size={22} />
72
+ ) : (
73
+ `${action.label} (${selectedIds.length})`
74
+ )}
75
+ </Button>
76
+ );
77
+ }
@@ -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,8 @@ interface DetailsProps {
31
32
  statusColorMap: StatusColorMap;
32
33
  /** */
33
34
  statusTextMap: StatusTextMap;
35
+ template?: string;
36
+ loading?: boolean;
34
37
  }
35
38
 
36
39
  /**
@@ -45,61 +48,79 @@ const Details = ({
45
48
  dataMaskMap,
46
49
  statusColorMap,
47
50
  statusTextMap,
51
+ template,
52
+ loading = false,
48
53
  }: DetailsProps) => {
54
+ // Resolve the template component once — undefined when the key is absent or
55
+ // not found in the registry, which gracefully falls back to the generic view.
56
+ const TemplateComponent = template
57
+ ? detailsTemplateRegistry[template]
58
+ : undefined;
59
+
49
60
  const visibleDetails = transformers.getVisibleDetails(columns);
50
61
 
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
- }));
62
+ // Pre-compute primitive items only when the generic grid is needed
63
+ const primitiveItems = TemplateComponent
64
+ ? undefined
65
+ : visibleDetails?.map((col) => ({
66
+ key: col.Data,
67
+ value: (documentData as Types.Queries.Row)?.[col.Data],
68
+ maskFn: resolvers.resolveDataMask(col, dataMaskMap),
69
+ mask: col.DataMask,
70
+ col,
71
+ }));
59
72
 
60
73
  return (
61
74
  <Box>
62
- {/* Header */}
63
75
  <Box height={48} sx={{ borderBottom: 1, borderColor: "divider" }}>
64
76
  <FlexBox
65
77
  alignItems="center"
66
78
  justifyContent="space-between"
67
79
  mb={2}
68
- height={"100%"}
80
+ height="100%"
69
81
  >
70
82
  <Button onClick={handleBack} startIcon={<ArrowBackIcon />}>
71
83
  {documentTypeLabel}
72
84
  </Button>
73
- <H6 color={"grey.800"}>
85
+ <H6 color="grey.800">
74
86
  {documentTypeLabel}-{id}
75
87
  </H6>
76
88
  <Subtitle>DETAILS</Subtitle>
77
89
  </FlexBox>
78
90
  </Box>
79
91
 
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>
92
+ <Box mt={2}>
93
+ {TemplateComponent ? (
94
+ <TemplateComponent
95
+ data={documentData}
96
+ statusColorMap={statusColorMap}
97
+ statusTextMap={statusTextMap}
98
+ loading={loading}
99
+ />
100
+ ) : (
101
+ <Grid container spacing={2}>
102
+ {primitiveItems?.map((col) => {
103
+ const maskedValue = col.maskFn(col.value, col.mask);
104
+ const Component = getRowComponent(maskedValue, col.key);
105
+ const sx = getRowLayout(maskedValue, col.key);
106
+ return (
107
+ <Grid key={col.key} {...sx}>
108
+ <DetailsRowLabel label={col.col.Label} />
109
+ <Component
110
+ value={maskedValue}
111
+ label={col.key}
112
+ column={col.col}
113
+ dataMaskMap={dataMaskMap}
114
+ statusColorMap={statusColorMap}
115
+ statusTextMap={statusTextMap}
116
+ status={col.value}
117
+ />
118
+ </Grid>
119
+ );
120
+ })}
121
+ </Grid>
122
+ )}
123
+ </Box>
103
124
  </Box>
104
125
  );
105
126
  };
@@ -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