@evenicanpm/portal-table-ui 1.4.0 → 1.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/dist/auth/auth-context.d.ts +36 -0
- package/dist/features/settings/index.d.ts +2 -0
- package/dist/features/table/components/dashboard/table/actions.d.ts +14 -0
- package/dist/features/table/components/dashboard/table/data-mask-map.d.ts +2 -0
- package/dist/features/table/components/dashboard/table/details/details.d.ts +27 -0
- package/dist/features/table/components/dashboard/table/details/label.d.ts +5 -0
- package/dist/features/table/components/dashboard/table/details/rows/downloads.d.ts +6 -0
- package/dist/features/table/components/dashboard/table/details/rows/primitive.d.ts +6 -0
- package/dist/features/table/components/dashboard/table/details/rows/table.d.ts +17 -0
- package/{src/features/table/components/dashboard/table/details/styles.ts → dist/features/table/components/dashboard/table/details/styles.d.ts} +2 -10
- package/dist/features/table/components/dashboard/table/filter-render-map.d.ts +2 -0
- package/dist/features/table/components/dashboard/table/filters/date-filter.d.ts +2 -0
- package/dist/features/table/components/dashboard/table/filters/status-filter.d.ts +2 -0
- package/dist/features/table/components/dashboard/table/no-results.d.ts +2 -0
- package/dist/features/table/components/dashboard/table/status-pill.d.ts +8 -0
- package/dist/features/table/components/dashboard/table/table-header.d.ts +16 -0
- package/dist/features/table/components/dashboard/table/table-input.d.ts +11 -0
- package/dist/features/table/components/dashboard/table/table-row/action-button.d.ts +8 -0
- package/dist/features/table/components/dashboard/table/table-row/table-row.d.ts +32 -0
- package/dist/features/table/components/dashboard/table/table-row-skeleton.d.ts +6 -0
- package/dist/features/table/components/dashboard/table/table.d.ts +18 -0
- package/dist/features/table/components/dashboard/table-dashboard.d.ts +22 -0
- package/{src/features/table/index.ts → dist/features/table/index.d.ts} +1 -7
- package/dist/features/table/logic/resolvers.d.ts +21 -0
- package/dist/features/table/logic/transformers.d.ts +35 -0
- package/dist/features/table/logic/types.d.ts +8 -0
- package/{src/features/table/types/handlers.ts → dist/features/table/types/handlers.d.ts} +12 -30
- package/{src/features/table/types/index.ts → dist/features/table/types/index.d.ts} +0 -1
- package/{src/features/table/types/queries.ts → dist/features/table/types/queries.d.ts} +3 -10
- package/dist/features/table/types/schemas.d.ts +31 -0
- package/{src/index.ts → dist/index.d.ts} +0 -2
- package/dist/portal.d.ts +38 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +6 -3
- package/src/auth/auth-context.tsx +0 -86
- package/src/features/table/components/dashboard/table/actions.ts +0 -59
- package/src/features/table/components/dashboard/table/data-mask-map.ts +0 -26
- package/src/features/table/components/dashboard/table/details/details.tsx +0 -164
- package/src/features/table/components/dashboard/table/details/label.tsx +0 -26
- package/src/features/table/components/dashboard/table/details/rows/downloads.tsx +0 -26
- package/src/features/table/components/dashboard/table/details/rows/primitive.tsx +0 -26
- package/src/features/table/components/dashboard/table/details/rows/table.tsx +0 -74
- package/src/features/table/components/dashboard/table/filter-render-map.tsx +0 -9
- package/src/features/table/components/dashboard/table/filters/date-filter.tsx +0 -31
- package/src/features/table/components/dashboard/table/filters/status-filter.tsx +0 -36
- package/src/features/table/components/dashboard/table/no-results.tsx +0 -26
- package/src/features/table/components/dashboard/table/status-pill.tsx +0 -18
- package/src/features/table/components/dashboard/table/table-header.tsx +0 -62
- package/src/features/table/components/dashboard/table/table-input.tsx +0 -94
- package/src/features/table/components/dashboard/table/table-row/action-button.tsx +0 -39
- package/src/features/table/components/dashboard/table/table-row/table-row.tsx +0 -96
- package/src/features/table/components/dashboard/table/table-row-skeleton.tsx +0 -36
- package/src/features/table/components/dashboard/table/table.tsx +0 -250
- package/src/features/table/components/dashboard/table-dashboard.tsx +0 -71
- package/src/features/table/logic/resolvers.ts +0 -68
- package/src/features/table/logic/transformers.ts +0 -56
- package/src/features/table/logic/types.ts +0 -8
- package/src/features/table/types/schemas.ts +0 -50
- package/src/portal.tsx +0 -68
- package/tsconfig.json +0 -28
- package/typedoc.json +0 -10
- /package/{src/features/table/components/dashboard/index.tsx → dist/features/table/components/dashboard/index.d.ts} +0 -0
- /package/{src/features/table/components/dashboard/table/details/index.ts → dist/features/table/components/dashboard/table/details/index.d.ts} +0 -0
- /package/{src/features/table/components/dashboard/table/details/rows/index.ts → dist/features/table/components/dashboard/table/details/rows/index.d.ts} +0 -0
- /package/{src/features/table/components/dashboard/table/index.ts → dist/features/table/components/dashboard/table/index.d.ts} +0 -0
- /package/{src/features/table/logic/index.ts → dist/features/table/logic/index.d.ts} +0 -0
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { Skeleton } from "@mui/material";
|
|
2
|
-
import { styled } from "@mui/material/styles";
|
|
3
|
-
import TableCell from "@mui/material/TableCell";
|
|
4
|
-
import TableHead from "@mui/material/TableHead";
|
|
5
|
-
import TableRow from "@mui/material/TableRow";
|
|
6
|
-
import type React from "react";
|
|
7
|
-
import { Column } from "@evenicanpm/portal-types-schemas";
|
|
8
|
-
|
|
9
|
-
// STYLED COMPONENTS
|
|
10
|
-
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
|
11
|
-
fontWeight: 600,
|
|
12
|
-
padding: "16px 20px",
|
|
13
|
-
color: theme.palette.grey[900],
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
interface TableHeaderProps {
|
|
17
|
-
hasActions: boolean;
|
|
18
|
-
columns: (Column & { ColumnOrder: number })[] | undefined;
|
|
19
|
-
loading: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
* Table handles display and caches it's own data.
|
|
25
|
-
*
|
|
26
|
-
*/
|
|
27
|
-
export default function TableHeader({
|
|
28
|
-
hasActions,
|
|
29
|
-
columns,
|
|
30
|
-
loading,
|
|
31
|
-
}: Readonly<TableHeaderProps>) {
|
|
32
|
-
if (loading) {
|
|
33
|
-
return (
|
|
34
|
-
<TableHead>
|
|
35
|
-
<TableRow>
|
|
36
|
-
{Array.from({ length: 4 })?.map((_col, i) => (
|
|
37
|
-
<StyledTableCell key={String(`skeleton-${i + 1}`)}>
|
|
38
|
-
<Skeleton
|
|
39
|
-
variant="rounded"
|
|
40
|
-
width={100}
|
|
41
|
-
height={25}
|
|
42
|
-
sx={{ bgcolor: "rgba(255,255,255,0.1)" }}
|
|
43
|
-
/>
|
|
44
|
-
</StyledTableCell>
|
|
45
|
-
))}
|
|
46
|
-
</TableRow>
|
|
47
|
-
</TableHead>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
return (
|
|
51
|
-
<TableHead>
|
|
52
|
-
<TableRow>
|
|
53
|
-
{columns?.map((column, i) => (
|
|
54
|
-
<StyledTableCell key={column.Label + String(i)}>
|
|
55
|
-
{column.Label}
|
|
56
|
-
</StyledTableCell>
|
|
57
|
-
))}
|
|
58
|
-
{hasActions && <StyledTableCell>Actions</StyledTableCell>}
|
|
59
|
-
</TableRow>
|
|
60
|
-
</TableHead>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { Column } from "@evenicanpm/portal-types-schemas";
|
|
2
|
-
import { SearchInput } from "@evenicanpm/ui";
|
|
3
|
-
import { MenuItem, Select } from "@mui/material";
|
|
4
|
-
import React from "react";
|
|
5
|
-
|
|
6
|
-
interface SearchProps {
|
|
7
|
-
searchTerm: string;
|
|
8
|
-
setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
|
|
9
|
-
setColumn: React.Dispatch<React.SetStateAction<string>>;
|
|
10
|
-
selectedColumn: string;
|
|
11
|
-
searchCols: Column[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const ColumnSelect = ({
|
|
15
|
-
selected,
|
|
16
|
-
handleChange,
|
|
17
|
-
searchCols,
|
|
18
|
-
}: {
|
|
19
|
-
selected: string;
|
|
20
|
-
handleChange: React.Dispatch<React.SetStateAction<string>>;
|
|
21
|
-
searchCols: Column[];
|
|
22
|
-
}) => {
|
|
23
|
-
return (
|
|
24
|
-
<Select
|
|
25
|
-
id="demo-simple-select"
|
|
26
|
-
value={selected}
|
|
27
|
-
sx={{
|
|
28
|
-
maxWidth: "150px",
|
|
29
|
-
"& .MuiOutlinedInput-notchedOutline": { border: "none" },
|
|
30
|
-
"& .MuiSelect-select": {
|
|
31
|
-
paddingTop: 0,
|
|
32
|
-
paddingBottom: 0,
|
|
33
|
-
display: "flex",
|
|
34
|
-
alignItems: "center",
|
|
35
|
-
},
|
|
36
|
-
"&:hover": { bgcolor: "rgba(0, 0, 0, 0.04)" }, // Subtle hover state
|
|
37
|
-
}}
|
|
38
|
-
onChange={(e) => handleChange(e.target.value)}
|
|
39
|
-
>
|
|
40
|
-
{searchCols?.map((col) => (
|
|
41
|
-
<MenuItem key={col?.Data} value={col?.Data}>
|
|
42
|
-
{col?.Label}
|
|
43
|
-
</MenuItem>
|
|
44
|
-
))}
|
|
45
|
-
</Select>
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const SearchField = React.memo(
|
|
50
|
-
({
|
|
51
|
-
searchTerm,
|
|
52
|
-
setSearchTerm,
|
|
53
|
-
searchCols,
|
|
54
|
-
selectedColumn,
|
|
55
|
-
setColumn,
|
|
56
|
-
}: SearchProps) => {
|
|
57
|
-
return (
|
|
58
|
-
<SearchInput
|
|
59
|
-
sx={{
|
|
60
|
-
width: "100%",
|
|
61
|
-
pr: 0,
|
|
62
|
-
boderRight: "none",
|
|
63
|
-
"& .MuiOutlinedInput-notchedOutline": {
|
|
64
|
-
borderRight: "none",
|
|
65
|
-
borderTopRightRadius: 0,
|
|
66
|
-
borderBottomRightRadius: 0,
|
|
67
|
-
},
|
|
68
|
-
// Target the hover state too, otherwise the thick line reappears on hover
|
|
69
|
-
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
70
|
-
borderRight: "none",
|
|
71
|
-
},
|
|
72
|
-
// Target focus state
|
|
73
|
-
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
|
74
|
-
borderRight: "none",
|
|
75
|
-
},
|
|
76
|
-
}}
|
|
77
|
-
value={searchTerm}
|
|
78
|
-
onChange={(e) => {
|
|
79
|
-
setSearchTerm(e.target.value);
|
|
80
|
-
}}
|
|
81
|
-
placeholder={`Seach by ${selectedColumn}`}
|
|
82
|
-
endAdornment={
|
|
83
|
-
<ColumnSelect
|
|
84
|
-
selected={selectedColumn}
|
|
85
|
-
searchCols={searchCols}
|
|
86
|
-
handleChange={setColumn}
|
|
87
|
-
/>
|
|
88
|
-
}
|
|
89
|
-
/>
|
|
90
|
-
);
|
|
91
|
-
},
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
export { SearchField };
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { actionIconMap, actionMutationMap } from "../actions";
|
|
2
|
-
import { FlexBox } from "@evenicanpm/ui";
|
|
3
|
-
import { IconButton, CircularProgress } from "@mui/material";
|
|
4
|
-
import { Action } from "@evenicanpm/portal-types-schemas";
|
|
5
|
-
import { useMutation } from "@tanstack/react-query";
|
|
6
|
-
|
|
7
|
-
interface ActionButtonProps {
|
|
8
|
-
docId: string;
|
|
9
|
-
action: Action;
|
|
10
|
-
resourceName: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function ActionButton({
|
|
14
|
-
action,
|
|
15
|
-
resourceName,
|
|
16
|
-
docId,
|
|
17
|
-
}: Readonly<ActionButtonProps>) {
|
|
18
|
-
// Maybe move to a resolver??
|
|
19
|
-
// currently we only use the action type to resolve
|
|
20
|
-
const Icon = actionIconMap?.[action.Type];
|
|
21
|
-
const mutation = actionMutationMap?.[action.Type](resourceName, docId);
|
|
22
|
-
const { mutate, isPending } = useMutation(mutation);
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<FlexBox justifyContent="flex-start" gap={1}>
|
|
26
|
-
<span key={action.Name}>
|
|
27
|
-
<IconButton
|
|
28
|
-
size="small"
|
|
29
|
-
onClick={(e) => {
|
|
30
|
-
e.stopPropagation();
|
|
31
|
-
mutate({ docId });
|
|
32
|
-
}}
|
|
33
|
-
>
|
|
34
|
-
{isPending ? <CircularProgress size={26} /> : <Icon />}
|
|
35
|
-
</IconButton>
|
|
36
|
-
</span>
|
|
37
|
-
</FlexBox>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import TableCell from "@mui/material/TableCell";
|
|
2
|
-
import TableRow from "@mui/material/TableRow";
|
|
3
|
-
import type React from "react";
|
|
4
|
-
import type * as Types from "../../../../types";
|
|
5
|
-
import { styled } from "@mui/system";
|
|
6
|
-
import { Action } from "@evenicanpm/portal-types-schemas";
|
|
7
|
-
import { FlexBox } from "@evenicanpm/ui";
|
|
8
|
-
import { ActionButton } from "./action-button";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* This is what we transorm the column data into for convenient rendering.
|
|
12
|
-
*/
|
|
13
|
-
type ColumnLogic =
|
|
14
|
-
| {
|
|
15
|
-
id: string;
|
|
16
|
-
mask: string | undefined;
|
|
17
|
-
maskFn: any;
|
|
18
|
-
cellFn:
|
|
19
|
-
| ((
|
|
20
|
-
value: string | number | string[],
|
|
21
|
-
) => React.ReactElement<
|
|
22
|
-
any,
|
|
23
|
-
string | React.JSXElementConstructor<any>
|
|
24
|
-
>)
|
|
25
|
-
| ((value: string | number) => string | number);
|
|
26
|
-
}[]
|
|
27
|
-
| undefined;
|
|
28
|
-
|
|
29
|
-
const StyledTableCell = styled(TableCell)({
|
|
30
|
-
height: "75px",
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
export type DocumentTableProps = {
|
|
34
|
-
/** The row to render */
|
|
35
|
-
row: Types.Queries.Row;
|
|
36
|
-
/** The render logic for each column ie data mask functions, cell maps*/
|
|
37
|
-
columnLogic: ColumnLogic;
|
|
38
|
-
/** */
|
|
39
|
-
handleClick: React.Dispatch<React.SetStateAction<string | number | null>>;
|
|
40
|
-
/** Actions renderer */
|
|
41
|
-
actions: Action[];
|
|
42
|
-
/** Does this view config have actions? */
|
|
43
|
-
/** The id field for the document (used for knowing what to set selected id to) */
|
|
44
|
-
idField: string;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* A single row (document) in the table. Takes the pre-constructed column logic
|
|
49
|
-
* and executes all of the cell renders and data map functions. Also handles
|
|
50
|
-
* rendering the action icons.
|
|
51
|
-
*/
|
|
52
|
-
export default function DataTableRow({
|
|
53
|
-
row,
|
|
54
|
-
columnLogic,
|
|
55
|
-
handleClick: handleSelectDocument,
|
|
56
|
-
actions,
|
|
57
|
-
idField,
|
|
58
|
-
}: Readonly<DocumentTableProps>) {
|
|
59
|
-
const id = row?.[idField] as string;
|
|
60
|
-
return (
|
|
61
|
-
<TableRow
|
|
62
|
-
hover
|
|
63
|
-
sx={{ cursor: "pointer" }}
|
|
64
|
-
onClick={() => {
|
|
65
|
-
// type narrowing to make sure we don't use a complex object
|
|
66
|
-
// as the id in state
|
|
67
|
-
if (typeof id === "string" || typeof id === "number") {
|
|
68
|
-
handleSelectDocument(id);
|
|
69
|
-
}
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
{columnLogic?.map((col, i) => {
|
|
73
|
-
// --- Cell Render --- //
|
|
74
|
-
const rawValue = row[col.id];
|
|
75
|
-
const maskedValue = col.maskFn(rawValue, col?.mask);
|
|
76
|
-
const cellValue = col.cellFn(maskedValue);
|
|
77
|
-
return (
|
|
78
|
-
<StyledTableCell key={rawValue + String(i)}>
|
|
79
|
-
{cellValue}
|
|
80
|
-
</StyledTableCell>
|
|
81
|
-
);
|
|
82
|
-
})}
|
|
83
|
-
{actions?.map((action) => (
|
|
84
|
-
<TableCell
|
|
85
|
-
key={action.Name}
|
|
86
|
-
onClick={(e) => e.stopPropagation()}
|
|
87
|
-
sx={{ whiteSpace: "nowrap" }}
|
|
88
|
-
>
|
|
89
|
-
<FlexBox justifyContent="flex-start" gap={1}>
|
|
90
|
-
<ActionButton docId={id} action={action} resourceName={"invoice"} />
|
|
91
|
-
</FlexBox>
|
|
92
|
-
</TableCell>
|
|
93
|
-
))}
|
|
94
|
-
</TableRow>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { Skeleton } from "@mui/material";
|
|
2
|
-
import TableCell from "@mui/material/TableCell";
|
|
3
|
-
import TableRow from "@mui/material/TableRow";
|
|
4
|
-
import type React from "react";
|
|
5
|
-
import { styled } from "@mui/system";
|
|
6
|
-
|
|
7
|
-
const StyledTableCell = styled(TableCell)({
|
|
8
|
-
height: "75px",
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
export type TableRowSkeletonProps = {
|
|
12
|
-
pageSize: number;
|
|
13
|
-
colSpan: number;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default function TableRowSkeleton({
|
|
17
|
-
pageSize,
|
|
18
|
-
colSpan,
|
|
19
|
-
}: Readonly<TableRowSkeletonProps>) {
|
|
20
|
-
const rowKeys = Array.from({ length: pageSize }, (_, i) => `row-${i}`);
|
|
21
|
-
const cellKeys = Array.from({ length: colSpan }, (_, i) => `cell-${i}`);
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<>
|
|
25
|
-
{rowKeys.map((rowKey) => (
|
|
26
|
-
<TableRow key={rowKey}>
|
|
27
|
-
{cellKeys.map((cellKey) => (
|
|
28
|
-
<StyledTableCell key={`${rowKey}-${cellKey}`}>
|
|
29
|
-
<Skeleton />
|
|
30
|
-
</StyledTableCell>
|
|
31
|
-
))}
|
|
32
|
-
</TableRow>
|
|
33
|
-
))}
|
|
34
|
-
</>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import { FlexBox, ResponsiveTablePagination } from "@evenicanpm/ui";
|
|
2
|
-
import Table from "@mui/material/Table";
|
|
3
|
-
import TableBody from "@mui/material/TableBody";
|
|
4
|
-
import TableCell from "@mui/material/TableCell";
|
|
5
|
-
import TableContainer from "@mui/material/TableContainer";
|
|
6
|
-
import TableRow from "@mui/material/TableRow";
|
|
7
|
-
import { type QueryKey, useQuery } from "@tanstack/react-query";
|
|
8
|
-
import type React from "react";
|
|
9
|
-
import { useState } from "react";
|
|
10
|
-
import { resolvers, transformers } from "../../../logic";
|
|
11
|
-
import type * as Types from "../../../types";
|
|
12
|
-
import { dataMaskMap as dataMaskMapLocal } from "./data-mask-map";
|
|
13
|
-
import { Details } from "./details";
|
|
14
|
-
import { filterRenderMapFallback } from "./filter-render-map";
|
|
15
|
-
import { NoResultsComponent as NoResultsComponentLocal } from "./no-results";
|
|
16
|
-
import { StatusPill } from "./status-pill";
|
|
17
|
-
import TableHeader from "./table-header";
|
|
18
|
-
import { SearchField } from "./table-input";
|
|
19
|
-
import DataTableRow from "./table-row/table-row";
|
|
20
|
-
import TableRowSkeleton from "./table-row-skeleton";
|
|
21
|
-
|
|
22
|
-
export type DocumentTableProps = {
|
|
23
|
-
resource: Types.Schemas.Resource;
|
|
24
|
-
pageSize: number;
|
|
25
|
-
setPageSize: React.Dispatch<React.SetStateAction<number>>;
|
|
26
|
-
viewConfigQuery: (resourceName: string) => {
|
|
27
|
-
queryKey: QueryKey;
|
|
28
|
-
queryFn: () => Promise<Types.Queries.TableConfig>;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
34
|
-
* Table handles display and caches it's own data.
|
|
35
|
-
*
|
|
36
|
-
*/
|
|
37
|
-
export default function DocumentTable({
|
|
38
|
-
resource,
|
|
39
|
-
pageSize,
|
|
40
|
-
setPageSize,
|
|
41
|
-
viewConfigQuery,
|
|
42
|
-
}: Readonly<DocumentTableProps>) {
|
|
43
|
-
const [page, setPage] = useState(0);
|
|
44
|
-
const [selectedDocument, setSelectedDocument] = useState<
|
|
45
|
-
string | number | null
|
|
46
|
-
>(null);
|
|
47
|
-
const [filters, setFilters] = useState<Record<string, string>>({});
|
|
48
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
49
|
-
const [searchColumn, setSearchColumn] = useState("Title");
|
|
50
|
-
|
|
51
|
-
const {
|
|
52
|
-
dataMaskMap,
|
|
53
|
-
cellRenderMap: columnRenderMap,
|
|
54
|
-
filterRenderMap,
|
|
55
|
-
detailsRenderMap,
|
|
56
|
-
idField,
|
|
57
|
-
label,
|
|
58
|
-
noResultsComponent,
|
|
59
|
-
statusColorMap,
|
|
60
|
-
statusTextMap,
|
|
61
|
-
} = resource;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
*
|
|
65
|
-
* This is not simply a default map because it passes special props
|
|
66
|
-
* specific to status... Once we have more of these types of
|
|
67
|
-
* functions that return special renders that are not general
|
|
68
|
-
* resolvers we can create a new file.
|
|
69
|
-
*
|
|
70
|
-
* Render would be something like... If no column resolver -> try
|
|
71
|
-
* resolving these as opposed to just overwriting column renders
|
|
72
|
-
*/
|
|
73
|
-
const resolvedFilterRenderMap = {
|
|
74
|
-
...filterRenderMapFallback,
|
|
75
|
-
...(filterRenderMap ?? {}),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const cellRenderMapDefaults = {
|
|
79
|
-
...columnRenderMap,
|
|
80
|
-
Status: (value: string | number | string[]) => (
|
|
81
|
-
<StatusPill
|
|
82
|
-
status={value}
|
|
83
|
-
statusColorMap={statusColorMap}
|
|
84
|
-
statusTextMap={statusTextMap}
|
|
85
|
-
/>
|
|
86
|
-
),
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// --- State Handlers --- //
|
|
90
|
-
const handleChangePageSize = (
|
|
91
|
-
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
|
92
|
-
) => {
|
|
93
|
-
setPageSize(Number.parseInt(event.target.value, 10));
|
|
94
|
-
setPage(0);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const handleChangePage = (
|
|
98
|
-
_event: React.MouseEvent<HTMLButtonElement> | null,
|
|
99
|
-
newPage: number,
|
|
100
|
-
) => {
|
|
101
|
-
setPage(newPage);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const handleBack = () => {
|
|
105
|
-
setSelectedDocument(null);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const resolvedNoResults = (noResultsComponent ?? NoResultsComponentLocal)({
|
|
109
|
-
label,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const resolvedDataMaskMap = dataMaskMap ?? dataMaskMapLocal;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Data pipeline:
|
|
116
|
-
* FETCH -> TRANFORMER -> RESOLVER
|
|
117
|
-
* Keep the JSX dumb.
|
|
118
|
-
*
|
|
119
|
-
* Tested performance with console.time,
|
|
120
|
-
* calculation rendering around 0.02,
|
|
121
|
-
* no need to memoize yet.
|
|
122
|
-
*/
|
|
123
|
-
|
|
124
|
-
// --- Fetch --- //
|
|
125
|
-
const { data: config, isPending: configPending } = useQuery(
|
|
126
|
-
viewConfigQuery(resource.name),
|
|
127
|
-
);
|
|
128
|
-
const { data: rows, isPending: rowsPending } = useQuery(
|
|
129
|
-
resource.rowsQueryFn({ page, pageSize, filters, searchColumn, searchTerm }),
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
const columns = config?.Columns ?? [];
|
|
133
|
-
const actions = config?.Actions ?? [];
|
|
134
|
-
const hasActions = actions.length > 0;
|
|
135
|
-
|
|
136
|
-
// --- Transformer --- //
|
|
137
|
-
const searchableCols = transformers.getSearchableColumns(columns);
|
|
138
|
-
const filterReqs = transformers.getActiveFilters(columns);
|
|
139
|
-
const visibleCols = transformers.getVisibleColumns(columns);
|
|
140
|
-
|
|
141
|
-
// --- Resolver --- //
|
|
142
|
-
const filterLogic = filterReqs?.map((req) => ({
|
|
143
|
-
req,
|
|
144
|
-
FilterComponent: resolvers.resolveFilterUI(req, resolvedFilterRenderMap),
|
|
145
|
-
}));
|
|
146
|
-
const columnLogic = visibleCols?.map((col) => ({
|
|
147
|
-
id: col.Data,
|
|
148
|
-
mask: col?.DataMask,
|
|
149
|
-
maskFn: resolvers.resolveDataMask(col, resolvedDataMaskMap),
|
|
150
|
-
cellFn: resolvers.resolveCellUI(col, cellRenderMapDefaults),
|
|
151
|
-
}));
|
|
152
|
-
|
|
153
|
-
// --- Render --- //
|
|
154
|
-
if (selectedDocument) {
|
|
155
|
-
const selectedDocDetails =
|
|
156
|
-
rows?.data.find((doc) => doc?.[idField] === selectedDocument) || {};
|
|
157
|
-
// Build the logic necessary for rendering
|
|
158
|
-
return (
|
|
159
|
-
<Details
|
|
160
|
-
documentTypeLabel={label}
|
|
161
|
-
id={selectedDocument}
|
|
162
|
-
documentData={selectedDocDetails}
|
|
163
|
-
handleBack={handleBack}
|
|
164
|
-
columns={columns || []}
|
|
165
|
-
dataMaskMap={resolvedDataMaskMap}
|
|
166
|
-
statusColorMap={statusColorMap}
|
|
167
|
-
statusTextMap={statusTextMap}
|
|
168
|
-
detailsRenderMap={detailsRenderMap}
|
|
169
|
-
/>
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Table body data calculations
|
|
174
|
-
const colSpan = visibleCols?.length ? visibleCols.length + 1 : 4;
|
|
175
|
-
const noResults = !rowsPending && !configPending && rows?.data?.length === 0;
|
|
176
|
-
|
|
177
|
-
return (
|
|
178
|
-
<>
|
|
179
|
-
<h1>{label}</h1>
|
|
180
|
-
<FlexBox
|
|
181
|
-
justifyContent={"flex-end"}
|
|
182
|
-
width="100%"
|
|
183
|
-
height={65}
|
|
184
|
-
gap={2}
|
|
185
|
-
pt={2}
|
|
186
|
-
>
|
|
187
|
-
<SearchField
|
|
188
|
-
searchTerm={searchTerm}
|
|
189
|
-
setSearchTerm={setSearchTerm}
|
|
190
|
-
setColumn={setSearchColumn}
|
|
191
|
-
searchCols={searchableCols || []}
|
|
192
|
-
selectedColumn={searchColumn}
|
|
193
|
-
/>
|
|
194
|
-
{filterLogic?.map((filter) => (
|
|
195
|
-
<filter.FilterComponent
|
|
196
|
-
key={filter.req?.filterName}
|
|
197
|
-
value={filters}
|
|
198
|
-
setValue={setFilters}
|
|
199
|
-
resetPagination={() => setPage(0)}
|
|
200
|
-
{...filter.req}
|
|
201
|
-
/>
|
|
202
|
-
))}
|
|
203
|
-
</FlexBox>
|
|
204
|
-
<TableContainer>
|
|
205
|
-
<Table aria-label="simple table" sx={{ tableLayout: { md: "fixed" } }}>
|
|
206
|
-
<TableHeader
|
|
207
|
-
columns={visibleCols}
|
|
208
|
-
loading={configPending}
|
|
209
|
-
hasActions={hasActions}
|
|
210
|
-
/>
|
|
211
|
-
<TableBody>
|
|
212
|
-
{noResults ? (
|
|
213
|
-
<TableRow>
|
|
214
|
-
<TableCell
|
|
215
|
-
colSpan={colSpan || 1}
|
|
216
|
-
align="center"
|
|
217
|
-
sx={{ py: 6, color: "text.secondary" }}
|
|
218
|
-
>
|
|
219
|
-
{resolvedNoResults}
|
|
220
|
-
</TableCell>
|
|
221
|
-
</TableRow>
|
|
222
|
-
) : rowsPending ? (
|
|
223
|
-
<TableRowSkeleton pageSize={pageSize} colSpan={colSpan} />
|
|
224
|
-
) : (
|
|
225
|
-
rows?.data?.map((row, i) => (
|
|
226
|
-
<DataTableRow
|
|
227
|
-
key={`data-table-row-${i + 1}`}
|
|
228
|
-
columnLogic={columnLogic}
|
|
229
|
-
row={row}
|
|
230
|
-
handleClick={setSelectedDocument}
|
|
231
|
-
idField={idField}
|
|
232
|
-
actions={actions}
|
|
233
|
-
/>
|
|
234
|
-
))
|
|
235
|
-
)}
|
|
236
|
-
</TableBody>
|
|
237
|
-
</Table>
|
|
238
|
-
</TableContainer>
|
|
239
|
-
<ResponsiveTablePagination
|
|
240
|
-
count={rows?.items || 0}
|
|
241
|
-
page={page}
|
|
242
|
-
component="div"
|
|
243
|
-
rowsPerPage={pageSize}
|
|
244
|
-
rowsPerPageOptions={[5, 10]}
|
|
245
|
-
onPageChange={handleChangePage}
|
|
246
|
-
onRowsPerPageChange={handleChangePageSize}
|
|
247
|
-
/>
|
|
248
|
-
</>
|
|
249
|
-
);
|
|
250
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import TabContext from "@mui/lab/TabContext";
|
|
2
|
-
import TabList from "@mui/lab/TabList";
|
|
3
|
-
import TabPanel from "@mui/lab/TabPanel";
|
|
4
|
-
import Box from "@mui/material/Box";
|
|
5
|
-
import Tab from "@mui/material/Tab";
|
|
6
|
-
import { type SyntheticEvent, useState } from "react";
|
|
7
|
-
import type * as Types from "../../types";
|
|
8
|
-
import { Table } from "./table";
|
|
9
|
-
import { QueryKey } from "@tanstack/react-query";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The main props for top-level table entry-point
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*/
|
|
16
|
-
export interface TableProps {
|
|
17
|
-
viewConfigQuery: (resourceName: string) => {
|
|
18
|
-
queryKey: QueryKey;
|
|
19
|
-
queryFn: () => Promise<Types.Queries.TableConfig>;
|
|
20
|
-
};
|
|
21
|
-
resources: Types.Schemas.Resource[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
* The TableDashboard is the main view component for the table feature.
|
|
27
|
-
* Holds state for the tabs and global pagination for all tables.
|
|
28
|
-
*
|
|
29
|
-
* @params props
|
|
30
|
-
*/
|
|
31
|
-
export const TableDashboard = ({ resources, viewConfigQuery }: TableProps) => {
|
|
32
|
-
const [value, setValue] = useState(resources[0]?.name);
|
|
33
|
-
const [pageSize, setPageSize] = useState(5); // better if this is global to not reset when switching tables
|
|
34
|
-
|
|
35
|
-
const handleChange = (_event: SyntheticEvent, newValue: string) => {
|
|
36
|
-
setValue(newValue);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<Box sx={{ width: "100%", minHeight: "70vh" }}>
|
|
41
|
-
<TabContext value={value}>
|
|
42
|
-
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
|
43
|
-
<TabList onChange={handleChange}>
|
|
44
|
-
{resources?.map((resource, i) => (
|
|
45
|
-
<Tab
|
|
46
|
-
key={resource?.name + String(i)}
|
|
47
|
-
label={resource?.label}
|
|
48
|
-
value={resource?.name}
|
|
49
|
-
/>
|
|
50
|
-
))}
|
|
51
|
-
</TabList>
|
|
52
|
-
</Box>
|
|
53
|
-
{resources?.map((resource, i) => (
|
|
54
|
-
<Box key={resource.name + String(i)}>
|
|
55
|
-
<TabPanel
|
|
56
|
-
value={resource?.name}
|
|
57
|
-
sx={{ padding: "25px 0", margin: 0 }}
|
|
58
|
-
>
|
|
59
|
-
<Table
|
|
60
|
-
viewConfigQuery={viewConfigQuery}
|
|
61
|
-
resource={resource}
|
|
62
|
-
pageSize={pageSize}
|
|
63
|
-
setPageSize={setPageSize}
|
|
64
|
-
/>
|
|
65
|
-
</TabPanel>
|
|
66
|
-
</Box>
|
|
67
|
-
))}
|
|
68
|
-
</TabContext>
|
|
69
|
-
</Box>
|
|
70
|
-
);
|
|
71
|
-
};
|