@hitachivantara/uikit-cli 6.0.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.
Files changed (109) hide show
  1. package/README.md +46 -0
  2. package/package.json +68 -0
  3. package/src/app-shell.js +106 -0
  4. package/src/baselines/app-shell/vite/_gitignore +30 -0
  5. package/src/baselines/app-shell/vite/_oxlintrc.json +5 -0
  6. package/src/baselines/app-shell/vite/_package.json +55 -0
  7. package/src/baselines/app-shell/vite/public/locales/en/example.json +8 -0
  8. package/src/baselines/app-shell/vite/src/lib/data/config.ts +15 -0
  9. package/src/baselines/app-shell/vite/src/lib/i18n.ts +44 -0
  10. package/src/baselines/app-shell/vite/src/pages/Example/index.tsx +25 -0
  11. package/src/baselines/app-shell/vite/src/providers/Provider.tsx +31 -0
  12. package/src/baselines/app-shell/vite/src/tests/mocks.ts +1 -0
  13. package/src/baselines/app-shell/vite/src/tests/providers.tsx +13 -0
  14. package/src/baselines/app-shell/vite/src/tests/setupTests.ts +24 -0
  15. package/src/baselines/app-shell/vite/src/types/theme.d.ts +8 -0
  16. package/src/baselines/app-shell/vite/src/types/vite-env.d.ts +1 -0
  17. package/src/baselines/app-shell/vite/tsconfig.json +10 -0
  18. package/src/baselines/app-shell/vite/tsconfig.node.json +9 -0
  19. package/src/baselines/app-shell/vite/uno.config.ts +6 -0
  20. package/src/baselines/app-shell/vite/vite.config.ts +45 -0
  21. package/src/baselines/vite/_gitignore +30 -0
  22. package/src/baselines/vite/_oxlintrc.json +5 -0
  23. package/src/baselines/vite/_package.json +53 -0
  24. package/src/baselines/vite/index.html +18 -0
  25. package/src/baselines/vite/public/favicon.ico +0 -0
  26. package/src/baselines/vite/public/locales/en/common.json +16 -0
  27. package/src/baselines/vite/public/locales/en/home.json +4 -0
  28. package/src/baselines/vite/public/logo192.png +0 -0
  29. package/src/baselines/vite/src/App.tsx +31 -0
  30. package/src/baselines/vite/src/assets/HitachiLogo.tsx +27 -0
  31. package/src/baselines/vite/src/components/common/Loading/Loading.test.tsx +18 -0
  32. package/src/baselines/vite/src/components/common/Loading/Loading.tsx +15 -0
  33. package/src/baselines/vite/src/components/common/Loading/index.ts +1 -0
  34. package/src/baselines/vite/src/context/NavigationContext.tsx +67 -0
  35. package/src/baselines/vite/src/lib/i18n.ts +29 -0
  36. package/src/baselines/vite/src/main.tsx +12 -0
  37. package/src/baselines/vite/src/pages/Home/index.tsx +13 -0
  38. package/src/baselines/vite/src/pages/NotFound/NotFound.tsx +39 -0
  39. package/src/baselines/vite/src/pages/NotFound/index.tsx +1 -0
  40. package/src/baselines/vite/src/pages/layout/navigation.tsx +82 -0
  41. package/src/baselines/vite/src/routes.tsx +14 -0
  42. package/src/baselines/vite/src/tests/mocks.ts +1 -0
  43. package/src/baselines/vite/src/tests/providers.tsx +13 -0
  44. package/src/baselines/vite/src/tests/setupTests.ts +24 -0
  45. package/src/baselines/vite/src/types/theme.d.ts +8 -0
  46. package/src/baselines/vite/src/vite-env.d.ts +1 -0
  47. package/src/baselines/vite/tsconfig.json +10 -0
  48. package/src/baselines/vite/tsconfig.node.json +9 -0
  49. package/src/baselines/vite/uno.config.ts +6 -0
  50. package/src/baselines/vite/vite.config.ts +31 -0
  51. package/src/contents.js +63 -0
  52. package/src/create.js +172 -0
  53. package/src/index.js +22 -0
  54. package/src/navigation.js +21 -0
  55. package/src/package.js +37 -0
  56. package/src/plop-templates/README.md.hbs +10 -0
  57. package/src/plop-templates/app-shell/app-shell.config.ts.hbs +54 -0
  58. package/src/plop-templates/app-shell/index.html.hbs +15 -0
  59. package/src/plopfile.js +61 -0
  60. package/src/templates/AssetInventory/CardView.tsx +167 -0
  61. package/src/templates/AssetInventory/ListView.tsx +56 -0
  62. package/src/templates/AssetInventory/data.tsx +255 -0
  63. package/src/templates/AssetInventory/index.tsx +198 -0
  64. package/src/templates/AssetInventory/usePaginationData.ts +158 -0
  65. package/src/templates/Canvas/Context.tsx +49 -0
  66. package/src/templates/Canvas/ListView.tsx +189 -0
  67. package/src/templates/Canvas/Node.tsx +203 -0
  68. package/src/templates/Canvas/Sidebar.tsx +51 -0
  69. package/src/templates/Canvas/StatusEdge.tsx +75 -0
  70. package/src/templates/Canvas/StickyNode.tsx +475 -0
  71. package/src/templates/Canvas/Table.tsx +202 -0
  72. package/src/templates/Canvas/TreeView.tsx +211 -0
  73. package/src/templates/Canvas/dependencies.json +7 -0
  74. package/src/templates/Canvas/index.tsx +363 -0
  75. package/src/templates/Canvas/styles.tsx +41 -0
  76. package/src/templates/Canvas/utils.tsx +70 -0
  77. package/src/templates/Dashboard/GridPanel.tsx +33 -0
  78. package/src/templates/Dashboard/Kpi.tsx +107 -0
  79. package/src/templates/Dashboard/Map.styles.ts +681 -0
  80. package/src/templates/Dashboard/Map.tsx +71 -0
  81. package/src/templates/Dashboard/data.ts +67 -0
  82. package/src/templates/Dashboard/dependencies.json +11 -0
  83. package/src/templates/Dashboard/index.tsx +173 -0
  84. package/src/templates/DetailsView/KPIs.tsx +70 -0
  85. package/src/templates/DetailsView/MetadataItem.tsx +35 -0
  86. package/src/templates/DetailsView/Properties.tsx +127 -0
  87. package/src/templates/DetailsView/Table.tsx +104 -0
  88. package/src/templates/DetailsView/data.ts +67 -0
  89. package/src/templates/DetailsView/index.tsx +102 -0
  90. package/src/templates/DetailsView/usePaginationData.ts +155 -0
  91. package/src/templates/DetailsView/utils.ts +51 -0
  92. package/src/templates/Form/index.tsx +107 -0
  93. package/src/templates/KanbanBoard/ColumnContainer.tsx +89 -0
  94. package/src/templates/KanbanBoard/TaskCard.tsx +130 -0
  95. package/src/templates/KanbanBoard/data.tsx +140 -0
  96. package/src/templates/KanbanBoard/dependencies.json +6 -0
  97. package/src/templates/KanbanBoard/index.tsx +179 -0
  98. package/src/templates/KanbanBoard/styles.tsx +76 -0
  99. package/src/templates/KanbanBoard/types.ts +21 -0
  100. package/src/templates/ListView/Indicator.tsx +42 -0
  101. package/src/templates/ListView/Kpi.tsx +120 -0
  102. package/src/templates/ListView/Table.tsx +55 -0
  103. package/src/templates/ListView/data.tsx +179 -0
  104. package/src/templates/ListView/dependencies.json +5 -0
  105. package/src/templates/ListView/index.tsx +245 -0
  106. package/src/templates/ListView/usePaginationData.ts +158 -0
  107. package/src/templates/Welcome/index.tsx +101 -0
  108. package/src/templates/package.json +30 -0
  109. package/src/utils.js +37 -0
@@ -0,0 +1,71 @@
1
+ import { ReactNode } from "react";
2
+ import {
3
+ MapContainer,
4
+ MapContainerProps,
5
+ Marker,
6
+ MarkerProps,
7
+ Popup,
8
+ PopupProps,
9
+ TileLayer,
10
+ } from "react-leaflet";
11
+ import { css, cx } from "@emotion/css";
12
+ import { Global } from "@emotion/react";
13
+ import { Icon } from "leaflet";
14
+
15
+ import { mapStyles } from "./Map.styles";
16
+
17
+ const IconDefault = new Icon({
18
+ iconUrl: "https://unpkg.com/leaflet@1.9.3/dist/images/marker-icon.png",
19
+ });
20
+
21
+ const LevelIcons = [
22
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 16" fill="#478B1A"><path d="M3.37 11.4a6 6 0 1 1 5.26 0L6 16Zm0 0" /><path fill="#f0f0f0" d="M5.02 8.98 3.15 7.1l.7-.7 1.13 1.12 3.14-3.6.76.66Zm0 0"/></svg>`,
23
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 16" fill="#4D8AC0"><path d="M3.71 12H0l1.71-6L0 0h12l-1.71 6L12 12H8.29L6 16Zm0 0"/><path fill="#f0f0f0" d="M5.02 8.98 3.15 7.1l.7-.7 1.13 1.12 3.14-3.6.76.66Zm0 0"/></svg>`,
24
+ ``, // NO LEVEL 2
25
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 16" fill="#F9C846"><path d="M3.37 11.4a6 6 0 1 1 5.26 0L6 16Zm0 0"/><path fill="#f0f0f0" d="M5.54 8h1v1h-1ZM5.5 3h1v4h-1Zm0 0"/></svg>`,
26
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#D43136"><path d="M5.36 11.4a6 6 0 1 1 5.27 0L8 16Zm0 0"/><path fill="#f0f0f0" d="M6.53 8h1v1h-1ZM6.5 3h1v4h-1Zm2.03 5h1v1h-1ZM8.5 3h1v4h-1Zm0 0"/></svg>`,
27
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 15" fill="#930A80"><path d="M3.75 11.19A5.76 5.76 0 0 1 .11 4.7C.66 1.98 3.13 0 6 0s5.34 1.98 5.88 4.7a5.77 5.77 0 0 1-3.64 6.49L6 14.99Zm0 0"/><path fill="#f0f0f0" d="M6.54 7.74v.97h-1v-.97ZM5.5 2.91h1v3.86h-1Zm3.04 4.83v.97h-1v-.97ZM7.5 2.91h1v3.86h-1ZM4.55 7.74v.97h-1v-.97ZM3.5 2.91h1v3.86h-1Zm0 0"/></svg>`,
28
+ ].map((svg) => {
29
+ return new Icon({
30
+ iconUrl: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`,
31
+ iconSize: [32, 32],
32
+ });
33
+ });
34
+
35
+ interface MapProps extends MapContainerProps {
36
+ children?: ReactNode;
37
+ markers?: {
38
+ position: MarkerProps["position"];
39
+ label: PopupProps["children"];
40
+ level?: 0 | 1 | 3 | 4 | 5;
41
+ }[];
42
+ }
43
+
44
+ export const Map = ({ markers, ...props }: MapProps) => {
45
+ return (
46
+ <>
47
+ <Global styles={mapStyles} />
48
+ <MapContainer
49
+ className={cx(
50
+ css({
51
+ ".leaflet-tile-container img": {
52
+ filter: "grayscale(100%)",
53
+ },
54
+ }),
55
+ )}
56
+ {...props}
57
+ >
58
+ <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
59
+ {markers?.map(({ label, position, level = -1 }) => (
60
+ <Marker
61
+ key={String(position)}
62
+ icon={LevelIcons[level] || IconDefault}
63
+ position={position}
64
+ >
65
+ <Popup>{label}</Popup>
66
+ </Marker>
67
+ ))}
68
+ </MapContainer>
69
+ </>
70
+ );
71
+ };
@@ -0,0 +1,67 @@
1
+ import useSWR from "swr";
2
+
3
+ const delay = (ms: number) =>
4
+ new Promise((resolve) => {
5
+ setTimeout(resolve, ms);
6
+ });
7
+
8
+ const shuffleData = (key: number) => (value: number) => value * key;
9
+
10
+ const makeValues = (index: number, shuffleKey: number) => {
11
+ const baseData = [
12
+ [2300, 1000, 8500, 2300, 1000, 8500],
13
+ [6000, 1000, 1000, 6000, 1000, 1000],
14
+ [3700, 7500, 1100, 3700, 7500, 1100],
15
+ [2100, 8500, 3000, 2100, 8500, 3000],
16
+ [500, 8000, 9500, 500, 8000, 9500],
17
+ ];
18
+
19
+ return baseData[index].map(shuffleData(shuffleKey));
20
+ };
21
+
22
+ const fetchData = async (key = 1) => {
23
+ const barData = {
24
+ Group: ["Appliances", "Chairs", "Binders", "Storage", "Hardware", "Tables"],
25
+ "Sales Target": makeValues(0, key),
26
+ "Sales Per Rep": makeValues(1, key),
27
+ "Monthly Sales": makeValues(2, key),
28
+ Target: makeValues(3, key),
29
+ };
30
+
31
+ const lineData = {
32
+ Group: ["8:30", "9:00", "9:30", "10:00", "10:30", "11:00"],
33
+ "Sales Target": makeValues(0, key),
34
+ "Sales Per Rep": makeValues(1, key),
35
+ "Monthly Sales": makeValues(2, key),
36
+ Target: makeValues(3, key),
37
+ Cash: makeValues(4, key),
38
+ };
39
+
40
+ const barData2 = {
41
+ Group: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
42
+ "Sales Target": makeValues(0, key),
43
+ "Sales Per Rep": makeValues(1, key),
44
+ "Monthly Sales": makeValues(2, key),
45
+ Target: makeValues(3, key),
46
+ };
47
+
48
+ const donutData = {
49
+ Category: ["Server Sales", "Sales Per Rep", "Monthly Sales"],
50
+ Total: [61239, 26829, 71902].map(shuffleData(key)),
51
+ };
52
+
53
+ await delay(800);
54
+
55
+ return {
56
+ barData,
57
+ lineData,
58
+ barData2,
59
+ donutData,
60
+ };
61
+ };
62
+
63
+ export const useServerData = (index: number) => {
64
+ return useSWR(`/data/${index}`, async () => fetchData(index + 1), {
65
+ suspense: true,
66
+ });
67
+ };
@@ -0,0 +1,11 @@
1
+ {
2
+ "dependencies": {
3
+ "@hitachivantara/uikit-react-viz": "^5.6.0",
4
+ "leaflet": "^1.9.4",
5
+ "react-leaflet": "^4.2.1",
6
+ "usehooks-ts": "^2.16.0"
7
+ },
8
+ "devDependencies": {
9
+ "@types/leaflet": "^1.9.8"
10
+ }
11
+ }
@@ -0,0 +1,173 @@
1
+ import { Suspense, useState, useTransition } from "react";
2
+ import { css } from "@emotion/css";
3
+ import { useDebounce } from "usehooks-ts";
4
+ import {
5
+ HvButton,
6
+ HvGrid,
7
+ HvLoading,
8
+ HvMultiButton,
9
+ HvMultiButtonProps,
10
+ HvTypography,
11
+ theme,
12
+ } from "@hitachivantara/uikit-react-core";
13
+ import { Add, User } from "@hitachivantara/uikit-react-icons";
14
+ import {
15
+ HvBarChart,
16
+ HvDonutChart,
17
+ HvLineChart,
18
+ HvVizProvider,
19
+ } from "@hitachivantara/uikit-react-viz";
20
+
21
+ import { useServerData } from "./data";
22
+ import { GridPanel } from "./GridPanel";
23
+ import { Kpi } from "./Kpi";
24
+ import { Map as MapComponent } from "./Map";
25
+
26
+ interface ButtonsProps extends HvMultiButtonProps {
27
+ labels: string[];
28
+ onButtonChange?: (index: number) => void;
29
+ }
30
+
31
+ const formatter = (value?: number | string) => `${Number(value) / 1000}k`;
32
+
33
+ const Buttons = ({ labels, onButtonChange, ...others }: ButtonsProps) => {
34
+ const [selected, setSelected] = useState(0);
35
+
36
+ const handleClick = (index: number) => {
37
+ if (index === selected) return;
38
+ setSelected(index);
39
+ onButtonChange?.(index);
40
+ };
41
+
42
+ return (
43
+ <HvMultiButton {...others}>
44
+ {labels.map((label, index) => (
45
+ <HvButton
46
+ key={label}
47
+ style={{ width: 90 }}
48
+ selected={selected === index}
49
+ onClick={() => handleClick(index)}
50
+ >
51
+ {label}
52
+ </HvButton>
53
+ ))}
54
+ </HvMultiButton>
55
+ );
56
+ };
57
+
58
+ const Dashboard = () => {
59
+ const [time, setTime] = useState(0);
60
+ const [isPending, startTransition] = useTransition();
61
+ const isLoading = useDebounce(isPending, 50);
62
+ const { data } = useServerData(time);
63
+
64
+ return (
65
+ <HvGrid container>
66
+ <HvGrid item xs={12} display="flex" justifyContent="space-between">
67
+ <HvTypography component="h1" variant="title3">
68
+ Dashboard
69
+ </HvTypography>
70
+ <div className={css({ display: "flex", gap: theme.space.xs })}>
71
+ <Buttons
72
+ labels={["Today", "Week", "Month"]}
73
+ onButtonChange={(index) => {
74
+ startTransition(() => {
75
+ setTime(index);
76
+ });
77
+ }}
78
+ />
79
+ <HvButton variant="secondaryGhost" startIcon={<Add />}>
80
+ Add Portlet
81
+ </HvButton>
82
+ </div>
83
+ </HvGrid>
84
+ <GridPanel xs={12} md={8} isLoading={isLoading}>
85
+ <HvBarChart
86
+ horizontal
87
+ stack="Group"
88
+ data={data.barData}
89
+ measures={[
90
+ "Sales Target",
91
+ "Sales Per Rep",
92
+ "Monthly Sales",
93
+ "Target",
94
+ ]}
95
+ groupBy="Group"
96
+ />
97
+ </GridPanel>
98
+ <GridPanel xs={12} md={4} isLoading={isLoading}>
99
+ <div className={css({ position: "relative", height: "100%" })}>
100
+ <HvDonutChart
101
+ data={data.donutData}
102
+ groupBy="Category"
103
+ measure={{ field: "Total", valueFormatter: (v) => `${v} €` }}
104
+ />
105
+ <div
106
+ className={css({
107
+ position: "absolute",
108
+ top: "50%",
109
+ left: "50%",
110
+ transform: "translate(-50%, -50%)",
111
+ display: "flex",
112
+ flexFlow: "column wrap",
113
+ alignItems: "center",
114
+ justifyContent: "center",
115
+ })}
116
+ >
117
+ <User iconSize="M" />
118
+ <HvTypography variant="title3">
119
+ {data.donutData.Total.reduce((acc, value) => acc + value, 0)}
120
+ </HvTypography>
121
+ </div>
122
+ </div>
123
+ </GridPanel>
124
+ {[1, 2, 3, 4].map((key) => (
125
+ <HvGrid key={key} item xs={6} md={3}>
126
+ <Kpi color="positive" title="KPI description" value={3340} />
127
+ </HvGrid>
128
+ ))}
129
+ <GridPanel xs={12} isLoading={isLoading}>
130
+ <HvLineChart
131
+ area
132
+ stack="total"
133
+ data={data.lineData}
134
+ groupBy="Group"
135
+ measures={Object.keys(data.lineData).slice(1)}
136
+ yAxis={{ labelFormatter: formatter }}
137
+ tooltip={{ valueFormatter: formatter }}
138
+ />
139
+ </GridPanel>
140
+ <GridPanel xs={12} md={6} width="100%" height={400} isLoading={isLoading}>
141
+ <MapComponent
142
+ center={[38.7356, -9.2997]}
143
+ zoom={18}
144
+ style={{ height: "100%" }}
145
+ markers={[
146
+ { position: [38.7356, -9.2997], label: "HV Office", level: 0 },
147
+ { position: [38.735, -9.299], label: "Bus Stop", level: 3 },
148
+ { position: [38.735, -9.2993], label: "Fire", level: 4 },
149
+ ]}
150
+ />
151
+ </GridPanel>
152
+ <GridPanel xs={12} md={6} height={400} isLoading={isLoading}>
153
+ <HvBarChart
154
+ height={400}
155
+ data={data.barData2}
156
+ groupBy="Group"
157
+ measures={Object.keys(data.barData2).slice(1)}
158
+ stack="total"
159
+ />
160
+ </GridPanel>
161
+ </HvGrid>
162
+ );
163
+ };
164
+
165
+ export const Component = () => (
166
+ <HvVizProvider>
167
+ <Suspense fallback={<HvLoading style={{ minHeight: 400 }} />}>
168
+ <Dashboard />
169
+ </Suspense>
170
+ </HvVizProvider>
171
+ );
172
+
173
+ export default Component;
@@ -0,0 +1,70 @@
1
+ import { css } from "@emotion/css";
2
+ import {
3
+ HvAvatar,
4
+ HvGrid,
5
+ HvTypography,
6
+ theme,
7
+ } from "@hitachivantara/uikit-react-core";
8
+ import { Bottom, Top } from "@hitachivantara/uikit-react-icons";
9
+
10
+ import { useModelData } from "./data";
11
+ import { MetadataItem } from "./MetadataItem";
12
+
13
+ const Kpi = ({
14
+ title,
15
+ count,
16
+ diff,
17
+ }: {
18
+ title: string;
19
+ count: number;
20
+ diff: number;
21
+ }) => {
22
+ const Icon = diff > 0 ? Top : Bottom;
23
+
24
+ return (
25
+ <MetadataItem title={title}>
26
+ <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
27
+ <HvTypography variant="title2">{count}</HvTypography>
28
+ <Icon color={theme.colors[diff > 0 ? "positive" : "warning"]} />
29
+ <HvTypography variant="caption1">
30
+ {`${Math.abs(diff).toFixed(2)} ${diff > 0 ? "more" : "less"}`}
31
+ </HvTypography>
32
+ </div>
33
+ </MetadataItem>
34
+ );
35
+ };
36
+
37
+ export const KPIs = () => {
38
+ const { data } = useModelData();
39
+ const { deploys, imageUrl } = data;
40
+
41
+ return (
42
+ <>
43
+ <HvGrid item xs={12} md={2} lg={2}>
44
+ <HvAvatar
45
+ size="xl"
46
+ status="border"
47
+ classes={{ container: css({ margin: "auto", width: "fit-content" }) }}
48
+ src={imageUrl}
49
+ alt="Asset image"
50
+ />
51
+ </HvGrid>
52
+ <HvGrid item xs={12} md={10} lg={10}>
53
+ <HvGrid container direction="row">
54
+ {deploys.summary.map((el) => (
55
+ <HvGrid key={el.id} item xs={12} sm={4}>
56
+ <Kpi title={el.title} count={el.count} diff={el.diff} />
57
+ </HvGrid>
58
+ ))}
59
+ {deploys.data.map((el) => (
60
+ <HvGrid key={el.id} item xs={12} sm={4}>
61
+ <MetadataItem title={el.title}>
62
+ <HvTypography variant="caption1">{el.value}</HvTypography>
63
+ </MetadataItem>
64
+ </HvGrid>
65
+ ))}
66
+ </HvGrid>
67
+ </HvGrid>
68
+ </>
69
+ );
70
+ };
@@ -0,0 +1,35 @@
1
+ import { ReactNode } from "react";
2
+ import { css } from "@emotion/css";
3
+ import {
4
+ HvGrid,
5
+ HvGridProps,
6
+ HvTypography,
7
+ theme,
8
+ } from "@hitachivantara/uikit-react-core";
9
+
10
+ export interface MetadataItemProps extends Omit<HvGridProps, "title"> {
11
+ title?: ReactNode;
12
+ children?: ReactNode;
13
+ }
14
+
15
+ export const MetadataItem = ({
16
+ title,
17
+ children,
18
+ ...others
19
+ }: MetadataItemProps) => (
20
+ <HvGrid item {...others}>
21
+ <div
22
+ role="group"
23
+ className={css({
24
+ paddingTop: theme.space.xs,
25
+ borderTop: `1px solid ${theme.colors.border}`,
26
+ display: "flex",
27
+ flexFlow: "column wrap",
28
+ gap: theme.space.xs,
29
+ })}
30
+ >
31
+ {title && <HvTypography variant="label">{title}</HvTypography>}
32
+ {children}
33
+ </div>
34
+ </HvGrid>
35
+ );
@@ -0,0 +1,127 @@
1
+ import { ComponentType } from "react";
2
+ import { css } from "@emotion/css";
3
+ import {
4
+ HvColor,
5
+ HvDropdown,
6
+ HvGridProps,
7
+ HvProgressBar,
8
+ HvTag,
9
+ HvTagsInput,
10
+ HvTextArea,
11
+ theme,
12
+ } from "@hitachivantara/uikit-react-core";
13
+ import { Caution, Level4Alt } from "@hitachivantara/uikit-react-icons";
14
+
15
+ import { ModelDetails, useModelData } from "./data";
16
+ import { MetadataItem } from "./MetadataItem";
17
+
18
+ const ProgressBar = ({
19
+ color = "text",
20
+ value,
21
+ }: {
22
+ color?: HvColor;
23
+ value: number;
24
+ }) => {
25
+ return (
26
+ <HvProgressBar
27
+ classes={{
28
+ progressBarContainer: css({ height: 12 }),
29
+ progressBar: css({ backgroundColor: theme.colors[color] || color }),
30
+ }}
31
+ value={value}
32
+ />
33
+ );
34
+ };
35
+
36
+ const entries: Partial<
37
+ Record<
38
+ keyof ModelDetails,
39
+ HvGridProps & {
40
+ label: string;
41
+ Component?: ComponentType<any>;
42
+ EditComponent?: ComponentType<any>;
43
+ }
44
+ >
45
+ > = {
46
+ description: {
47
+ label: "Description",
48
+ sm: 6,
49
+ EditComponent: ({ value }) => (
50
+ <HvTextArea name="description" rows={3} defaultValue={value} />
51
+ ),
52
+ },
53
+ status: {
54
+ label: "Status",
55
+ Component: ({ value }) => (
56
+ <div className={css({ display: "flex", alignItems: "center" })}>
57
+ <Level4Alt color="negative" /> {value}
58
+ </div>
59
+ ),
60
+ EditComponent: () => (
61
+ <HvDropdown
62
+ name="status"
63
+ values={[
64
+ { id: "critical", label: "Critical", selected: true },
65
+ { id: "error", label: "Error" },
66
+ { id: "warning", label: "Warning" },
67
+ ]}
68
+ />
69
+ ),
70
+ },
71
+ tags: {
72
+ label: "Tags",
73
+ Component: ({ value }) => (
74
+ <div className={css({ display: "flex", gap: theme.space.xs })}>
75
+ {value.map((tag: string) => (
76
+ <HvTag key={tag} label={tag} />
77
+ ))}
78
+ </div>
79
+ ),
80
+ EditComponent: ({ value }) => (
81
+ <HvTagsInput name="tags" defaultValue={value} />
82
+ ),
83
+ },
84
+ progress: {
85
+ label: "Progress",
86
+ Component: ({ value }) => <ProgressBar value={value * 100} />,
87
+ },
88
+ severity: {
89
+ label: "Severity",
90
+ Component: ({ value }) => (
91
+ <div className={css({ display: "flex", alignItems: "center" })}>
92
+ <Caution color="warningStrong" /> {value}
93
+ </div>
94
+ ),
95
+ },
96
+ modifiedAt: {
97
+ label: "Last Updated",
98
+ },
99
+ createdAt: {
100
+ label: "Created",
101
+ },
102
+ risk: {
103
+ label: "Risk",
104
+ Component: ({ value }) => (
105
+ <ProgressBar color="negative" value={value * 100} />
106
+ ),
107
+ },
108
+ };
109
+
110
+ export const Properties = ({ editMode }: { editMode?: boolean }) => {
111
+ const { data } = useModelData();
112
+
113
+ const elements = Object.entries(entries).map(([key, entry]) => {
114
+ const { label, Component, EditComponent, ...others } = entry;
115
+ const value = data[key as keyof ModelDetails];
116
+ const ContentComponent = (editMode && EditComponent) || Component;
117
+
118
+ return (
119
+ <MetadataItem key={key} xs={12} sm={3} title={label} {...others}>
120
+ {(ContentComponent && <ContentComponent value={value} />) ||
121
+ (value as any)}
122
+ </MetadataItem>
123
+ );
124
+ });
125
+
126
+ return <>{elements}</>;
127
+ };
@@ -0,0 +1,104 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ import {
3
+ HvLoadingContainer,
4
+ HvPagination,
5
+ HvTable,
6
+ HvTableBody,
7
+ HvTableCell,
8
+ HvTableContainer,
9
+ HvTableHead,
10
+ HvTableHeader,
11
+ HvTableRow,
12
+ HvTableSection,
13
+ useHvData,
14
+ useHvPagination,
15
+ } from "@hitachivantara/uikit-react-core";
16
+
17
+ import { PaginationDataProps, usePaginationData } from "./data";
18
+ import { DetailsViewEntry, getColumns } from "./utils";
19
+
20
+ const PAGE_OPTIONS = [8, 16, 32];
21
+
22
+ interface TableProps {
23
+ modelId: string;
24
+ }
25
+
26
+ export const Table = ({ modelId }: TableProps) => {
27
+ const [params, setParams] = useState<PaginationDataProps>({
28
+ id: modelId,
29
+ limit: PAGE_OPTIONS[0],
30
+ skip: 0,
31
+ });
32
+
33
+ const {
34
+ data: { pages, data },
35
+ loading,
36
+ } = usePaginationData(params);
37
+
38
+ const columns = useMemo(() => getColumns(), []);
39
+
40
+ const instance = useHvData<DetailsViewEntry, string>(
41
+ {
42
+ data,
43
+ columns,
44
+ manualPagination: true,
45
+ autoResetPage: false,
46
+ pageCount: pages,
47
+ initialState: { pageSize: PAGE_OPTIONS[0] },
48
+ },
49
+ useHvPagination,
50
+ );
51
+
52
+ useEffect(() => {
53
+ const { pageSize = PAGE_OPTIONS[0], pageIndex = 0 } = instance.state;
54
+
55
+ setParams((prev) => ({
56
+ ...prev,
57
+ limit: pageSize,
58
+ skip: pageSize * pageIndex,
59
+ }));
60
+ }, [instance.state]);
61
+
62
+ return (
63
+ <HvTableSection>
64
+ <HvLoadingContainer hidden={!loading}>
65
+ <HvTableContainer>
66
+ <HvTable {...instance.getTableProps()}>
67
+ <HvTableHead>
68
+ <HvTableRow>
69
+ {columns.map((col) => (
70
+ <HvTableHeader key={col.Header}>{col.Header}</HvTableHeader>
71
+ ))}
72
+ </HvTableRow>
73
+ </HvTableHead>
74
+ <HvTableBody {...instance.getTableBodyProps()}>
75
+ {instance.page.map((row) => {
76
+ instance.prepareRow(row);
77
+ const { key, ...rowProps } = row.getRowProps();
78
+ return (
79
+ <HvTableRow key={key} {...rowProps}>
80
+ {row.cells.map((cell) => {
81
+ const { key: cellKey, ...cellProps } =
82
+ cell.getCellProps();
83
+ return (
84
+ <HvTableCell key={cellKey} {...cellProps}>
85
+ {cell.render("Cell")}
86
+ </HvTableCell>
87
+ );
88
+ })}
89
+ </HvTableRow>
90
+ );
91
+ })}
92
+ </HvTableBody>
93
+ </HvTable>
94
+ </HvTableContainer>
95
+ {instance.page?.length > 0 && (
96
+ <HvPagination
97
+ {...instance.getHvPaginationProps?.()}
98
+ pageSizeOptions={PAGE_OPTIONS}
99
+ />
100
+ )}
101
+ </HvLoadingContainer>
102
+ </HvTableSection>
103
+ );
104
+ };