@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.
- package/README.md +46 -0
- package/package.json +68 -0
- package/src/app-shell.js +106 -0
- package/src/baselines/app-shell/vite/_gitignore +30 -0
- package/src/baselines/app-shell/vite/_oxlintrc.json +5 -0
- package/src/baselines/app-shell/vite/_package.json +55 -0
- package/src/baselines/app-shell/vite/public/locales/en/example.json +8 -0
- package/src/baselines/app-shell/vite/src/lib/data/config.ts +15 -0
- package/src/baselines/app-shell/vite/src/lib/i18n.ts +44 -0
- package/src/baselines/app-shell/vite/src/pages/Example/index.tsx +25 -0
- package/src/baselines/app-shell/vite/src/providers/Provider.tsx +31 -0
- package/src/baselines/app-shell/vite/src/tests/mocks.ts +1 -0
- package/src/baselines/app-shell/vite/src/tests/providers.tsx +13 -0
- package/src/baselines/app-shell/vite/src/tests/setupTests.ts +24 -0
- package/src/baselines/app-shell/vite/src/types/theme.d.ts +8 -0
- package/src/baselines/app-shell/vite/src/types/vite-env.d.ts +1 -0
- package/src/baselines/app-shell/vite/tsconfig.json +10 -0
- package/src/baselines/app-shell/vite/tsconfig.node.json +9 -0
- package/src/baselines/app-shell/vite/uno.config.ts +6 -0
- package/src/baselines/app-shell/vite/vite.config.ts +45 -0
- package/src/baselines/vite/_gitignore +30 -0
- package/src/baselines/vite/_oxlintrc.json +5 -0
- package/src/baselines/vite/_package.json +53 -0
- package/src/baselines/vite/index.html +18 -0
- package/src/baselines/vite/public/favicon.ico +0 -0
- package/src/baselines/vite/public/locales/en/common.json +16 -0
- package/src/baselines/vite/public/locales/en/home.json +4 -0
- package/src/baselines/vite/public/logo192.png +0 -0
- package/src/baselines/vite/src/App.tsx +31 -0
- package/src/baselines/vite/src/assets/HitachiLogo.tsx +27 -0
- package/src/baselines/vite/src/components/common/Loading/Loading.test.tsx +18 -0
- package/src/baselines/vite/src/components/common/Loading/Loading.tsx +15 -0
- package/src/baselines/vite/src/components/common/Loading/index.ts +1 -0
- package/src/baselines/vite/src/context/NavigationContext.tsx +67 -0
- package/src/baselines/vite/src/lib/i18n.ts +29 -0
- package/src/baselines/vite/src/main.tsx +12 -0
- package/src/baselines/vite/src/pages/Home/index.tsx +13 -0
- package/src/baselines/vite/src/pages/NotFound/NotFound.tsx +39 -0
- package/src/baselines/vite/src/pages/NotFound/index.tsx +1 -0
- package/src/baselines/vite/src/pages/layout/navigation.tsx +82 -0
- package/src/baselines/vite/src/routes.tsx +14 -0
- package/src/baselines/vite/src/tests/mocks.ts +1 -0
- package/src/baselines/vite/src/tests/providers.tsx +13 -0
- package/src/baselines/vite/src/tests/setupTests.ts +24 -0
- package/src/baselines/vite/src/types/theme.d.ts +8 -0
- package/src/baselines/vite/src/vite-env.d.ts +1 -0
- package/src/baselines/vite/tsconfig.json +10 -0
- package/src/baselines/vite/tsconfig.node.json +9 -0
- package/src/baselines/vite/uno.config.ts +6 -0
- package/src/baselines/vite/vite.config.ts +31 -0
- package/src/contents.js +63 -0
- package/src/create.js +172 -0
- package/src/index.js +22 -0
- package/src/navigation.js +21 -0
- package/src/package.js +37 -0
- package/src/plop-templates/README.md.hbs +10 -0
- package/src/plop-templates/app-shell/app-shell.config.ts.hbs +54 -0
- package/src/plop-templates/app-shell/index.html.hbs +15 -0
- package/src/plopfile.js +61 -0
- package/src/templates/AssetInventory/CardView.tsx +167 -0
- package/src/templates/AssetInventory/ListView.tsx +56 -0
- package/src/templates/AssetInventory/data.tsx +255 -0
- package/src/templates/AssetInventory/index.tsx +198 -0
- package/src/templates/AssetInventory/usePaginationData.ts +158 -0
- package/src/templates/Canvas/Context.tsx +49 -0
- package/src/templates/Canvas/ListView.tsx +189 -0
- package/src/templates/Canvas/Node.tsx +203 -0
- package/src/templates/Canvas/Sidebar.tsx +51 -0
- package/src/templates/Canvas/StatusEdge.tsx +75 -0
- package/src/templates/Canvas/StickyNode.tsx +475 -0
- package/src/templates/Canvas/Table.tsx +202 -0
- package/src/templates/Canvas/TreeView.tsx +211 -0
- package/src/templates/Canvas/dependencies.json +7 -0
- package/src/templates/Canvas/index.tsx +363 -0
- package/src/templates/Canvas/styles.tsx +41 -0
- package/src/templates/Canvas/utils.tsx +70 -0
- package/src/templates/Dashboard/GridPanel.tsx +33 -0
- package/src/templates/Dashboard/Kpi.tsx +107 -0
- package/src/templates/Dashboard/Map.styles.ts +681 -0
- package/src/templates/Dashboard/Map.tsx +71 -0
- package/src/templates/Dashboard/data.ts +67 -0
- package/src/templates/Dashboard/dependencies.json +11 -0
- package/src/templates/Dashboard/index.tsx +173 -0
- package/src/templates/DetailsView/KPIs.tsx +70 -0
- package/src/templates/DetailsView/MetadataItem.tsx +35 -0
- package/src/templates/DetailsView/Properties.tsx +127 -0
- package/src/templates/DetailsView/Table.tsx +104 -0
- package/src/templates/DetailsView/data.ts +67 -0
- package/src/templates/DetailsView/index.tsx +102 -0
- package/src/templates/DetailsView/usePaginationData.ts +155 -0
- package/src/templates/DetailsView/utils.ts +51 -0
- package/src/templates/Form/index.tsx +107 -0
- package/src/templates/KanbanBoard/ColumnContainer.tsx +89 -0
- package/src/templates/KanbanBoard/TaskCard.tsx +130 -0
- package/src/templates/KanbanBoard/data.tsx +140 -0
- package/src/templates/KanbanBoard/dependencies.json +6 -0
- package/src/templates/KanbanBoard/index.tsx +179 -0
- package/src/templates/KanbanBoard/styles.tsx +76 -0
- package/src/templates/KanbanBoard/types.ts +21 -0
- package/src/templates/ListView/Indicator.tsx +42 -0
- package/src/templates/ListView/Kpi.tsx +120 -0
- package/src/templates/ListView/Table.tsx +55 -0
- package/src/templates/ListView/data.tsx +179 -0
- package/src/templates/ListView/dependencies.json +5 -0
- package/src/templates/ListView/index.tsx +245 -0
- package/src/templates/ListView/usePaginationData.ts +158 -0
- package/src/templates/Welcome/index.tsx +101 -0
- package/src/templates/package.json +30 -0
- package/src/utils.js +37 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import useSWR from "swr";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
delay,
|
|
5
|
+
ServerPaginationProps,
|
|
6
|
+
useServerPagination,
|
|
7
|
+
} from "./usePaginationData";
|
|
8
|
+
import { createEntry, DetailsViewEntry } from "./utils";
|
|
9
|
+
|
|
10
|
+
// --- Data ---
|
|
11
|
+
|
|
12
|
+
const TOTAL = 10;
|
|
13
|
+
|
|
14
|
+
const db = [...Array(TOTAL).keys()].map(createEntry);
|
|
15
|
+
|
|
16
|
+
const model = {
|
|
17
|
+
description: "Model created from the example Jupyter Notebook",
|
|
18
|
+
status: "Critical",
|
|
19
|
+
severity: "Medium",
|
|
20
|
+
tags: ["test", "note"],
|
|
21
|
+
project: "Wine Quality",
|
|
22
|
+
progress: 0.87,
|
|
23
|
+
risk: 0.2,
|
|
24
|
+
asc: "Failure Prediction",
|
|
25
|
+
createdAt: "2022-05-24 14:32:50",
|
|
26
|
+
modifiedAt: "2022-05-24 14:32:50",
|
|
27
|
+
imageUrl: "https://i.imgur.com/5EW6x5r.jpg",
|
|
28
|
+
deploys: {
|
|
29
|
+
summary: [
|
|
30
|
+
{ id: 1, title: "Success Requests", count: 4, diff: 2.02 },
|
|
31
|
+
{ id: 2, title: "Error Requests", count: 2, diff: -1.63 },
|
|
32
|
+
{ id: 3, title: "Open Requests", count: 12, diff: 1.84 },
|
|
33
|
+
],
|
|
34
|
+
data: [
|
|
35
|
+
{ id: 2, title: "Update Build", value: "2020/12/1" },
|
|
36
|
+
{ id: 3, title: "Clean Data Logs", value: "2018/5/3" },
|
|
37
|
+
{ id: 6, title: "Build", value: "46uYmU" },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type ModelDetails = typeof model;
|
|
43
|
+
|
|
44
|
+
// #region Endpoints
|
|
45
|
+
export interface PaginationDataProps
|
|
46
|
+
extends Omit<ServerPaginationProps<DetailsViewEntry>, "endpoint" | "db"> {
|
|
47
|
+
id: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const usePaginationData = (props: PaginationDataProps) => {
|
|
51
|
+
const endpoint = `/model/${props.id}/events`;
|
|
52
|
+
return useServerPagination({ endpoint, db, ...props });
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const useModelData = () => {
|
|
56
|
+
return useSWR(
|
|
57
|
+
"model",
|
|
58
|
+
async () => {
|
|
59
|
+
// Loading
|
|
60
|
+
await delay(800);
|
|
61
|
+
|
|
62
|
+
return model;
|
|
63
|
+
},
|
|
64
|
+
{ suspense: true },
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
// #endregion
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ElementType, ReactNode, Suspense, useState } from "react";
|
|
2
|
+
import { css } from "@emotion/css";
|
|
3
|
+
import {
|
|
4
|
+
HvButton,
|
|
5
|
+
HvGlobalActions,
|
|
6
|
+
HvGlobalActionsProps,
|
|
7
|
+
HvGrid,
|
|
8
|
+
HvGridProps,
|
|
9
|
+
HvLoading,
|
|
10
|
+
theme,
|
|
11
|
+
} from "@hitachivantara/uikit-react-core";
|
|
12
|
+
|
|
13
|
+
import { KPIs } from "./KPIs";
|
|
14
|
+
import { Properties } from "./Properties";
|
|
15
|
+
import { Table } from "./Table";
|
|
16
|
+
|
|
17
|
+
const MODEL_ID = "123";
|
|
18
|
+
|
|
19
|
+
interface SectionProps extends HvGridProps {
|
|
20
|
+
variant?: HvGlobalActionsProps["variant"];
|
|
21
|
+
actions?: ReactNode;
|
|
22
|
+
component?: ElementType;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const Section = ({
|
|
26
|
+
title,
|
|
27
|
+
variant = "section",
|
|
28
|
+
component = "section",
|
|
29
|
+
actions,
|
|
30
|
+
...others
|
|
31
|
+
}: SectionProps) => (
|
|
32
|
+
<>
|
|
33
|
+
{title && (
|
|
34
|
+
<HvGlobalActions variant={variant} title={title}>
|
|
35
|
+
{actions}
|
|
36
|
+
</HvGlobalActions>
|
|
37
|
+
)}
|
|
38
|
+
<Suspense fallback={<HvLoading className={css({ minHeight: 200 })} />}>
|
|
39
|
+
<HvGrid
|
|
40
|
+
container
|
|
41
|
+
component={component}
|
|
42
|
+
className={css({
|
|
43
|
+
marginTop: 0, // avoid collapse
|
|
44
|
+
marginBottom: theme.space.md,
|
|
45
|
+
})}
|
|
46
|
+
{...others}
|
|
47
|
+
/>
|
|
48
|
+
</Suspense>
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const DetailsView = () => {
|
|
53
|
+
const [editing, setEditing] = useState(false);
|
|
54
|
+
|
|
55
|
+
const actions = editing ? (
|
|
56
|
+
<span>
|
|
57
|
+
<HvButton type="submit" form="properties" variant="primaryGhost">
|
|
58
|
+
Save
|
|
59
|
+
</HvButton>
|
|
60
|
+
<HvButton variant="secondaryGhost" onClick={() => setEditing(false)}>
|
|
61
|
+
Cancel
|
|
62
|
+
</HvButton>
|
|
63
|
+
</span>
|
|
64
|
+
) : (
|
|
65
|
+
<HvButton variant="primaryGhost" onClick={() => setEditing(true)}>
|
|
66
|
+
Edit
|
|
67
|
+
</HvButton>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
<Section title="Asset Details" variant="global">
|
|
73
|
+
<KPIs />
|
|
74
|
+
</Section>
|
|
75
|
+
|
|
76
|
+
<Section
|
|
77
|
+
id="properties"
|
|
78
|
+
title="Asset Properties"
|
|
79
|
+
actions={actions}
|
|
80
|
+
component={editing ? "form" : "section"}
|
|
81
|
+
onSubmit={(evt) => {
|
|
82
|
+
evt.preventDefault();
|
|
83
|
+
const formData = new FormData(evt.currentTarget as any);
|
|
84
|
+
console.log(Object.fromEntries(formData.entries()));
|
|
85
|
+
setEditing(false);
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<Properties editMode={editing} />
|
|
89
|
+
</Section>
|
|
90
|
+
|
|
91
|
+
<Section title="Events">
|
|
92
|
+
<HvGrid item xs={12}>
|
|
93
|
+
<Table modelId={MODEL_ID} />
|
|
94
|
+
</HvGrid>
|
|
95
|
+
</Section>
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export { DetailsView as Component };
|
|
101
|
+
|
|
102
|
+
export default DetailsView;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import useSWR from "swr";
|
|
3
|
+
|
|
4
|
+
// --- Types ---
|
|
5
|
+
|
|
6
|
+
type DataObject = Record<string, string | number | undefined>;
|
|
7
|
+
|
|
8
|
+
export interface ServerPaginationProps<T extends DataObject = DataObject> {
|
|
9
|
+
endpoint: string;
|
|
10
|
+
db: T[];
|
|
11
|
+
limit: number;
|
|
12
|
+
skip: number;
|
|
13
|
+
sort?: string;
|
|
14
|
+
order?: "desc" | "asc";
|
|
15
|
+
filter?: { id: string; value: string | number };
|
|
16
|
+
search?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PaginationData<T extends DataObject = DataObject> {
|
|
20
|
+
data: T[];
|
|
21
|
+
total: number;
|
|
22
|
+
pages: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// --- Utils ---
|
|
26
|
+
|
|
27
|
+
export const delay = (ms = 500) =>
|
|
28
|
+
new Promise((resolve) => {
|
|
29
|
+
setTimeout(() => resolve("Time passed"), ms);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const searchObj = (entry: DataObject, search: string) => {
|
|
33
|
+
return Object.keys(entry)
|
|
34
|
+
.filter((key) => key !== "id" && key !== "statusColor")
|
|
35
|
+
.find(
|
|
36
|
+
(key) =>
|
|
37
|
+
entry[key]?.toString().toLowerCase().search(search.toLowerCase()) !==
|
|
38
|
+
-1,
|
|
39
|
+
)?.length;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// --- Pagination hook ---
|
|
43
|
+
|
|
44
|
+
export function useServerPagination<T extends DataObject = DataObject>({
|
|
45
|
+
endpoint,
|
|
46
|
+
db,
|
|
47
|
+
limit,
|
|
48
|
+
skip,
|
|
49
|
+
sort,
|
|
50
|
+
order = "desc",
|
|
51
|
+
filter,
|
|
52
|
+
search,
|
|
53
|
+
}: ServerPaginationProps<T>) {
|
|
54
|
+
const initialState: PaginationData<T> = {
|
|
55
|
+
data: [],
|
|
56
|
+
pages: 0,
|
|
57
|
+
total: 0,
|
|
58
|
+
};
|
|
59
|
+
const [currData, setCurrData] = useState(initialState);
|
|
60
|
+
const [deleting, setDeleting] = useState(false);
|
|
61
|
+
const [allData, setAllData] = useState<T[]>(db);
|
|
62
|
+
|
|
63
|
+
const fetcher = useCallback(async () => {
|
|
64
|
+
// Loading
|
|
65
|
+
await delay(800);
|
|
66
|
+
|
|
67
|
+
const items =
|
|
68
|
+
search || filter
|
|
69
|
+
? allData.filter((entry) => {
|
|
70
|
+
let keep = true;
|
|
71
|
+
|
|
72
|
+
if (search) {
|
|
73
|
+
keep = !!searchObj(entry, search);
|
|
74
|
+
}
|
|
75
|
+
if (filter && keep) {
|
|
76
|
+
keep =
|
|
77
|
+
entry[filter.id]?.toString().toLowerCase() ===
|
|
78
|
+
filter.value.toString().toLowerCase();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return keep;
|
|
82
|
+
})
|
|
83
|
+
: [...allData];
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
data: items
|
|
87
|
+
.sort(
|
|
88
|
+
sort
|
|
89
|
+
? (a, b) => {
|
|
90
|
+
return order === "desc"
|
|
91
|
+
? (b[sort] || "")
|
|
92
|
+
.toString()
|
|
93
|
+
.localeCompare((a[sort] || "").toString(), undefined, {
|
|
94
|
+
numeric: true,
|
|
95
|
+
sensitivity: "base",
|
|
96
|
+
})
|
|
97
|
+
: (a[sort] || "")
|
|
98
|
+
.toString()
|
|
99
|
+
.localeCompare((b[sort] || "").toString(), undefined, {
|
|
100
|
+
numeric: true,
|
|
101
|
+
sensitivity: "base",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
: undefined,
|
|
105
|
+
)
|
|
106
|
+
.slice(skip, skip + limit),
|
|
107
|
+
total: items.length,
|
|
108
|
+
pages: Math.ceil(items.length / limit),
|
|
109
|
+
};
|
|
110
|
+
}, [allData, filter, limit, order, search, skip, sort]);
|
|
111
|
+
|
|
112
|
+
const { data, isLoading, mutate } = useSWR<PaginationData<T>>(() => {
|
|
113
|
+
const params = new URLSearchParams({
|
|
114
|
+
skip: skip.toString(),
|
|
115
|
+
limit: limit.toString(),
|
|
116
|
+
...(sort && { sort, order }),
|
|
117
|
+
...(search && { search }),
|
|
118
|
+
...(filter && { [filter.id]: filter.value }),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return `${endpoint}?${params.toString()}`;
|
|
122
|
+
}, fetcher);
|
|
123
|
+
|
|
124
|
+
// Prevents data from updating to undefined when loading (this crashes useHvData)
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!isLoading && data) {
|
|
127
|
+
setCurrData(data);
|
|
128
|
+
}
|
|
129
|
+
}, [isLoading, data]);
|
|
130
|
+
|
|
131
|
+
const deleteEntries = useCallback(
|
|
132
|
+
async (ids: string[]) => {
|
|
133
|
+
setDeleting(true);
|
|
134
|
+
|
|
135
|
+
setAllData((prev) =>
|
|
136
|
+
prev.filter((entry: any) => entry.id && !ids.includes(entry.id)),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const refreshedData = await fetcher();
|
|
140
|
+
|
|
141
|
+
mutate(refreshedData);
|
|
142
|
+
|
|
143
|
+
await delay(800);
|
|
144
|
+
|
|
145
|
+
setDeleting(false);
|
|
146
|
+
},
|
|
147
|
+
[fetcher, mutate],
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
deleteEntries,
|
|
152
|
+
data: currData,
|
|
153
|
+
loading: isLoading || deleting,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { HvTableColumnConfig } from "@hitachivantara/uikit-react-core";
|
|
2
|
+
|
|
3
|
+
// --- Table data utils ---
|
|
4
|
+
|
|
5
|
+
export type DetailsViewEntry = {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
eventType: string;
|
|
9
|
+
status: string;
|
|
10
|
+
severity: string;
|
|
11
|
+
priority: string;
|
|
12
|
+
time: string;
|
|
13
|
+
temperature: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getColumns = (): HvTableColumnConfig<
|
|
17
|
+
DetailsViewEntry,
|
|
18
|
+
string
|
|
19
|
+
>[] => [
|
|
20
|
+
{ Header: "Title", accessor: "name", style: { minWidth: 120 } },
|
|
21
|
+
{ Header: "Event Type", accessor: "eventType", style: { minWidth: 100 } },
|
|
22
|
+
{ Header: "Status", accessor: "status", style: { width: 120 } },
|
|
23
|
+
{ Header: "Severity", accessor: "severity" },
|
|
24
|
+
{ Header: "Priority", accessor: "priority" },
|
|
25
|
+
{ Header: "Time", accessor: "time" },
|
|
26
|
+
{ Header: "Temperature", accessor: "temperature" },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const getOption = (opts: string[], i: number) => opts[i % opts.length];
|
|
30
|
+
|
|
31
|
+
const getTime = (priority: string, index: number) => {
|
|
32
|
+
let i = priority === "High" ? index + 4 : index + 3;
|
|
33
|
+
i = priority === "Medium" ? i + 30 : index + 20;
|
|
34
|
+
return `${i % 12}:${i % 60}:${i % 60}`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getPriority = (i: number) =>
|
|
38
|
+
(i % 2 > 0 && "High") || (i % 2 < 0 && "Medium") || "Low";
|
|
39
|
+
|
|
40
|
+
export const createEntry = (i: number): DetailsViewEntry => {
|
|
41
|
+
return {
|
|
42
|
+
id: `${i + 1}`,
|
|
43
|
+
name: `Event ${i + 1}`,
|
|
44
|
+
eventType: `Anomaly detection ${i % 4}`,
|
|
45
|
+
status: getOption(["Closed", "Open"], i),
|
|
46
|
+
severity: getOption(["Critical", "Major", "Average", "Minor"], i),
|
|
47
|
+
priority: getPriority(i),
|
|
48
|
+
time: getTime(getPriority(i), i),
|
|
49
|
+
temperature: `${i + 35}`,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HvButton,
|
|
3
|
+
HvCheckBox,
|
|
4
|
+
HvDatePicker,
|
|
5
|
+
HvGrid,
|
|
6
|
+
HvInput,
|
|
7
|
+
HvOption,
|
|
8
|
+
HvRadio,
|
|
9
|
+
HvRadioGroup,
|
|
10
|
+
HvSelect,
|
|
11
|
+
HvTextArea,
|
|
12
|
+
HvTimePicker,
|
|
13
|
+
} from "@hitachivantara/uikit-react-core";
|
|
14
|
+
import { Phone } from "@hitachivantara/uikit-react-icons";
|
|
15
|
+
|
|
16
|
+
const countries = [
|
|
17
|
+
{ id: "pt", label: "Portugal" },
|
|
18
|
+
{ id: "sp", label: "Spain" },
|
|
19
|
+
{ id: "fr", label: "France" },
|
|
20
|
+
{ id: "de", label: "Germany" },
|
|
21
|
+
{ id: "us", label: "United States" },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const Form = () => (
|
|
25
|
+
<form
|
|
26
|
+
autoComplete="on"
|
|
27
|
+
onSubmit={(event) => {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
const formData = new FormData(event.currentTarget);
|
|
30
|
+
alert(JSON.stringify(Object.fromEntries(formData), null, 2));
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
<HvGrid container maxWidth="md" rowSpacing="xs">
|
|
34
|
+
<HvGrid item xs={12} sm={6}>
|
|
35
|
+
<HvInput required name="name" label="Full Name" />
|
|
36
|
+
</HvGrid>
|
|
37
|
+
<HvGrid item xs={12} sm={6}>
|
|
38
|
+
<HvInput required type="email" name="email" label="Email" />
|
|
39
|
+
</HvGrid>
|
|
40
|
+
<HvGrid item xs={12} sm={6}>
|
|
41
|
+
<HvInput name="street-address" label="Address" />
|
|
42
|
+
</HvGrid>
|
|
43
|
+
<HvGrid item xs={12} sm={6}>
|
|
44
|
+
<HvSelect
|
|
45
|
+
required
|
|
46
|
+
name="country"
|
|
47
|
+
label="Country"
|
|
48
|
+
statusMessage="Country is required"
|
|
49
|
+
>
|
|
50
|
+
{countries.map(({ id, label }) => (
|
|
51
|
+
<HvOption key={id} value={id}>
|
|
52
|
+
{label}
|
|
53
|
+
</HvOption>
|
|
54
|
+
))}
|
|
55
|
+
</HvSelect>
|
|
56
|
+
</HvGrid>
|
|
57
|
+
<HvGrid item xs={12} sm={6}>
|
|
58
|
+
<HvInput name="tel" label="Phone number" endAdornment={<Phone />} />
|
|
59
|
+
</HvGrid>
|
|
60
|
+
<HvGrid item xs={12} sm={6}>
|
|
61
|
+
<HvInput type="password" name="password" label="Password" />
|
|
62
|
+
</HvGrid>
|
|
63
|
+
<HvGrid item xs={12} component="hr" />
|
|
64
|
+
<HvGrid item xs={12} sm={6}>
|
|
65
|
+
<HvDatePicker name="bday" label="Birthday" placeholder="Select date" />
|
|
66
|
+
</HvGrid>
|
|
67
|
+
<HvGrid item xs={12} sm={6}>
|
|
68
|
+
<HvTimePicker name="startTime" label="Start time" />
|
|
69
|
+
</HvGrid>
|
|
70
|
+
<HvGrid item xs={12} component="hr" />
|
|
71
|
+
<HvGrid item xs={12}>
|
|
72
|
+
<HvTextArea
|
|
73
|
+
required
|
|
74
|
+
name="description"
|
|
75
|
+
description="Write a short description"
|
|
76
|
+
label="Description"
|
|
77
|
+
rows={3}
|
|
78
|
+
inputProps={{ minLength: 16, maxLength: 128 }}
|
|
79
|
+
minCharQuantity={16}
|
|
80
|
+
maxCharQuantity={128}
|
|
81
|
+
/>
|
|
82
|
+
</HvGrid>
|
|
83
|
+
<HvGrid item xs={12}>
|
|
84
|
+
<HvRadioGroup required name="sex" label="Sex" orientation="horizontal">
|
|
85
|
+
<HvRadio value="male" label="Male" />
|
|
86
|
+
<HvRadio value="female" label="Female" />
|
|
87
|
+
<HvRadio value="other" label="Other" />
|
|
88
|
+
</HvRadioGroup>
|
|
89
|
+
</HvGrid>
|
|
90
|
+
<HvGrid item xs={12}>
|
|
91
|
+
<HvCheckBox
|
|
92
|
+
defaultChecked
|
|
93
|
+
name="subscribe"
|
|
94
|
+
label="Subscribe to newsletter"
|
|
95
|
+
value="yes"
|
|
96
|
+
/>
|
|
97
|
+
</HvGrid>
|
|
98
|
+
<HvGrid item xs={12}>
|
|
99
|
+
<HvButton type="submit">Submit</HvButton>
|
|
100
|
+
</HvGrid>
|
|
101
|
+
</HvGrid>
|
|
102
|
+
</form>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
export { Form as Component };
|
|
106
|
+
|
|
107
|
+
export default Form;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { SortableContext, useSortable } from "@dnd-kit/sortable";
|
|
3
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
4
|
+
import {
|
|
5
|
+
HvIconButton,
|
|
6
|
+
HvStack,
|
|
7
|
+
HvTypography,
|
|
8
|
+
} from "@hitachivantara/uikit-react-core";
|
|
9
|
+
import { Add } from "@hitachivantara/uikit-react-icons";
|
|
10
|
+
|
|
11
|
+
import classes from "./styles";
|
|
12
|
+
import { TaskCard } from "./TaskCard";
|
|
13
|
+
import { Column, Task } from "./types";
|
|
14
|
+
|
|
15
|
+
interface ColumnProps {
|
|
16
|
+
column: Column;
|
|
17
|
+
tasks?: Task[];
|
|
18
|
+
addTask?: (columnId: string) => void;
|
|
19
|
+
deleteTask?: (taskId: string) => void;
|
|
20
|
+
addEnabled?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const ColumnContainer = ({
|
|
24
|
+
column,
|
|
25
|
+
tasks,
|
|
26
|
+
addTask,
|
|
27
|
+
deleteTask,
|
|
28
|
+
addEnabled = true,
|
|
29
|
+
}: ColumnProps) => {
|
|
30
|
+
const tasksIds = useMemo(() => tasks?.map((task) => task.id), [tasks]) || [];
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
setNodeRef,
|
|
34
|
+
attributes,
|
|
35
|
+
listeners,
|
|
36
|
+
transform,
|
|
37
|
+
transition,
|
|
38
|
+
isDragging,
|
|
39
|
+
} = useSortable({ id: column.id, data: { type: "Column", column } });
|
|
40
|
+
|
|
41
|
+
const style = {
|
|
42
|
+
transition,
|
|
43
|
+
transform: CSS.Transform.toString(transform),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
ref={setNodeRef}
|
|
49
|
+
style={{
|
|
50
|
+
...style,
|
|
51
|
+
...(isDragging && {
|
|
52
|
+
opacity: 0.2,
|
|
53
|
+
}),
|
|
54
|
+
}}
|
|
55
|
+
className={classes.columnContainer}
|
|
56
|
+
>
|
|
57
|
+
<div className={classes.column}>
|
|
58
|
+
<div className={classes.columnHeader} {...attributes} {...listeners}>
|
|
59
|
+
<div className={classes.columnTitle}>
|
|
60
|
+
{column.icon}
|
|
61
|
+
<HvTypography variant="title3">{column.title}</HvTypography>
|
|
62
|
+
<div className={classes.count}>{tasks?.length}</div>
|
|
63
|
+
</div>
|
|
64
|
+
<HvIconButton
|
|
65
|
+
title="Add"
|
|
66
|
+
variant="primarySubtle"
|
|
67
|
+
onClick={() => addTask?.(column.id)}
|
|
68
|
+
disabled={!addEnabled}
|
|
69
|
+
>
|
|
70
|
+
<Add />
|
|
71
|
+
</HvIconButton>
|
|
72
|
+
</div>
|
|
73
|
+
<HvStack direction="column" spacing="md">
|
|
74
|
+
<SortableContext items={tasksIds}>
|
|
75
|
+
{tasks &&
|
|
76
|
+
tasks.map((task) => (
|
|
77
|
+
<TaskCard
|
|
78
|
+
key={task.id}
|
|
79
|
+
task={task}
|
|
80
|
+
color={column.color}
|
|
81
|
+
deleteTask={deleteTask}
|
|
82
|
+
/>
|
|
83
|
+
))}
|
|
84
|
+
</SortableContext>
|
|
85
|
+
</HvStack>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useSortable } from "@dnd-kit/sortable";
|
|
2
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
3
|
+
import {
|
|
4
|
+
HvActionBar,
|
|
5
|
+
HvAvatar,
|
|
6
|
+
HvAvatarGroup,
|
|
7
|
+
HvButton,
|
|
8
|
+
HvCard,
|
|
9
|
+
HvCardContent,
|
|
10
|
+
HvCardHeader,
|
|
11
|
+
HvOverflowTooltip,
|
|
12
|
+
HvTypography,
|
|
13
|
+
} from "@hitachivantara/uikit-react-core";
|
|
14
|
+
import {
|
|
15
|
+
Delete,
|
|
16
|
+
Level0Good,
|
|
17
|
+
Level1,
|
|
18
|
+
Level3Bad,
|
|
19
|
+
Level4,
|
|
20
|
+
Level5,
|
|
21
|
+
} from "@hitachivantara/uikit-react-icons";
|
|
22
|
+
|
|
23
|
+
import classes from "./styles";
|
|
24
|
+
import { Column, Task } from "./types";
|
|
25
|
+
|
|
26
|
+
interface TaskProps {
|
|
27
|
+
task: Task;
|
|
28
|
+
color?: Column["color"];
|
|
29
|
+
deleteTask?: (taskId: string) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const getStatusIcon = (statusLevel?: number) => {
|
|
33
|
+
switch (statusLevel) {
|
|
34
|
+
case 1:
|
|
35
|
+
return <Level1 color="info" />;
|
|
36
|
+
case 2:
|
|
37
|
+
return <Level3Bad color="warningStrong" />;
|
|
38
|
+
case 3:
|
|
39
|
+
return <Level4 color="negativeStrong" />;
|
|
40
|
+
case 4:
|
|
41
|
+
return <Level5 color="negativeDeep" />;
|
|
42
|
+
case 5:
|
|
43
|
+
return <Level0Good color="positive" />;
|
|
44
|
+
default:
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const TaskCard = ({ task, deleteTask }: TaskProps) => {
|
|
50
|
+
const {
|
|
51
|
+
setNodeRef,
|
|
52
|
+
attributes,
|
|
53
|
+
listeners,
|
|
54
|
+
transform,
|
|
55
|
+
transition,
|
|
56
|
+
isDragging,
|
|
57
|
+
} = useSortable({ id: task.id, data: { type: "Task", task } });
|
|
58
|
+
|
|
59
|
+
const style = {
|
|
60
|
+
transition,
|
|
61
|
+
transform: CSS.Transform.toString(transform),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
ref={setNodeRef}
|
|
67
|
+
style={{
|
|
68
|
+
...style,
|
|
69
|
+
...(isDragging && {
|
|
70
|
+
opacity: 0.2,
|
|
71
|
+
}),
|
|
72
|
+
}}
|
|
73
|
+
{...attributes}
|
|
74
|
+
{...listeners}
|
|
75
|
+
>
|
|
76
|
+
<HvCard
|
|
77
|
+
bgcolor="bgContainer"
|
|
78
|
+
classes={{ root: classes.card, semanticBar: classes.cardSemanticBar }}
|
|
79
|
+
>
|
|
80
|
+
<HvCardHeader
|
|
81
|
+
title={
|
|
82
|
+
<>
|
|
83
|
+
<HvTypography variant="caption2">Some time ago</HvTypography>
|
|
84
|
+
<HvTypography variant="title3">{task.title}</HvTypography>
|
|
85
|
+
</>
|
|
86
|
+
}
|
|
87
|
+
className={classes.cardHeader}
|
|
88
|
+
icon={getStatusIcon(task.statusLevel)}
|
|
89
|
+
/>
|
|
90
|
+
<HvCardContent>
|
|
91
|
+
<HvOverflowTooltip
|
|
92
|
+
paragraphOverflow
|
|
93
|
+
data={
|
|
94
|
+
<HvTypography>
|
|
95
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris
|
|
96
|
+
vitae ex sem. Ut posuere nibh risus, vel pellentesque elit
|
|
97
|
+
varius sed. Etiam eleifend consequat tortor, ac ultrices nunc
|
|
98
|
+
viverra pretium. Praesent rutrum congue justo fermentum varius.
|
|
99
|
+
Aenean malesuada magna ut purus tincidunt tempor. Quisque urna
|
|
100
|
+
erat, finibus quis mi vitae, vehicula gravida dui. In vitae
|
|
101
|
+
elementum felis, sed interdum nunc. Integer varius nibh ac
|
|
102
|
+
congue rutrum. Pellentesque vel auctor risus. Cras quis
|
|
103
|
+
sollicitudin odio.
|
|
104
|
+
</HvTypography>
|
|
105
|
+
}
|
|
106
|
+
/>
|
|
107
|
+
<HvButton
|
|
108
|
+
icon
|
|
109
|
+
onClick={() => deleteTask?.(task.id)}
|
|
110
|
+
classes={{ root: classes.cardDeleteIcon }}
|
|
111
|
+
>
|
|
112
|
+
<Delete />
|
|
113
|
+
</HvButton>
|
|
114
|
+
</HvCardContent>
|
|
115
|
+
<HvActionBar classes={{ root: classes.cardActions }}>
|
|
116
|
+
{task.users ? (
|
|
117
|
+
<HvAvatarGroup size="xs" maxVisible={2}>
|
|
118
|
+
{task.users.map((user) => (
|
|
119
|
+
<HvAvatar key={user.name} alt={user.name} src={user.avatar} />
|
|
120
|
+
))}
|
|
121
|
+
</HvAvatarGroup>
|
|
122
|
+
) : (
|
|
123
|
+
<HvButton variant="primaryGhost">Assign</HvButton>
|
|
124
|
+
)}
|
|
125
|
+
<div style={{ flex: 1 }} />
|
|
126
|
+
</HvActionBar>
|
|
127
|
+
</HvCard>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
};
|