@openshift-migration-advisor/shared-components 0.25.0-fce5293f01c1 → 0.27.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/report/ErrorTable.d.ts +18 -0
- package/dist/report/ErrorTable.d.ts.map +1 -0
- package/dist/report/ErrorTable.js +20 -0
- package/dist/report/HostsOverview.d.ts +13 -0
- package/dist/report/HostsOverview.d.ts.map +1 -0
- package/dist/report/HostsOverview.js +84 -0
- package/dist/report/ReportTable.d.ts +14 -0
- package/dist/report/ReportTable.d.ts.map +1 -0
- package/dist/report/ReportTable.js +23 -0
- package/dist/report/WarningsTable.d.ts +9 -0
- package/dist/report/WarningsTable.d.ts.map +1 -0
- package/dist/report/WarningsTable.js +20 -0
- package/dist/report/constants.d.ts +2 -0
- package/dist/report/constants.d.ts.map +1 -1
- package/dist/report/constants.js +2 -0
- package/dist/report/index.d.ts +4 -0
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +4 -0
- package/package.json +1 -1
- package/src/report/ErrorTable.tsx +85 -0
- package/src/report/HostsOverview.tsx +185 -0
- package/src/report/ReportTable.tsx +81 -0
- package/src/report/WarningsTable.tsx +75 -0
- package/src/report/__tests__/HostsOverview.test.tsx +145 -0
- package/src/report/constants.ts +2 -0
- package/src/report/index.ts +15 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal shape of a migration issue consumed by {@link ErrorTable}. Declared
|
|
4
|
+
* locally so this package does not depend on a specific SDK (`agent-sdk` or
|
|
5
|
+
* `planner-sdk`); callers pass the subset of `MigrationIssue` fields read here.
|
|
6
|
+
*/
|
|
7
|
+
export interface MigrationIssueLike {
|
|
8
|
+
label: string;
|
|
9
|
+
assessment: string;
|
|
10
|
+
count: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ErrorTableProps {
|
|
13
|
+
errors: MigrationIssueLike[];
|
|
14
|
+
/** Optional drill-down callback invoked with an issue's `label`. */
|
|
15
|
+
onConcernClick?: (concernLabel: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const ErrorTable: React.FC<ErrorTableProps>;
|
|
18
|
+
//# sourceMappingURL=ErrorTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorTable.d.ts","sourceRoot":"","sources":["../../src/report/ErrorTable.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,oEAAoE;IACpE,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAgDhD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Card, CardBody, CardTitle, Flex, FlexItem, Icon, } from "@patternfly/react-core";
|
|
3
|
+
import { ExclamationCircleIcon } from "@patternfly/react-icons";
|
|
4
|
+
import { CardEmptyState } from "./CardEmptyState.js";
|
|
5
|
+
import { ChartHeaderActions } from "./ChartDownloadButton.js";
|
|
6
|
+
import { ChartExportSurface } from "./ChartExportSurface.js";
|
|
7
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
8
|
+
import { dashboardStyles } from "./dashboardStyles.js";
|
|
9
|
+
import { ReportTable } from "./ReportTable.js";
|
|
10
|
+
export const ErrorTable = ({ errors, onConcernClick, }) => {
|
|
11
|
+
const handleRowClick = (issue) => {
|
|
12
|
+
if (issue.label && onConcernClick) {
|
|
13
|
+
onConcernClick(issue.label);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const chartId = "errors-table";
|
|
17
|
+
const chartTitle = "Errors";
|
|
18
|
+
return (_jsx(ChartExportSurface, { id: chartId, title: chartTitle, children: _jsxs(Card, { className: dashboardStyles.card, children: [_jsx(CardTitle, { children: _jsxs(Flex, { justifyContent: { default: "justifyContentSpaceBetween" }, alignItems: { default: "alignItemsCenter" }, children: [_jsxs(FlexItem, { children: [_jsx(Icon, { status: "danger", children: _jsx(ExclamationCircleIcon, {}) }), " ", "Errors"] }), _jsx(ChartHeaderActions, { chartId: chartId, title: chartTitle })] }) }), _jsx(CardBody, { className: dashboardStyles.cardBodyScrollable, children: errors.length === 0 ? (_jsx(CardEmptyState, { title: REPORT_CARD_EMPTY_STATE_TITLES.errors })) : (_jsx("div", { children: _jsx(ReportTable, { data: errors, columns: ["Description", "Total VMs"], fields: ["assessment", "count"], onRowClick: onConcernClick ? handleRowClick : undefined, clickableFields: onConcernClick ? ["assessment"] : [] }) })) })] }) }));
|
|
19
|
+
};
|
|
20
|
+
ErrorTable.displayName = "ErrorTable";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FC } from "react";
|
|
2
|
+
import { type MigrationDonutChartLegendVariant } from "../charts/MigrationDonutChart.js";
|
|
3
|
+
/** Smallest host shape this card reads. Apps pass SDK hosts structurally. */
|
|
4
|
+
export interface HostLike {
|
|
5
|
+
model?: string | number;
|
|
6
|
+
}
|
|
7
|
+
export interface HostsOverviewProps {
|
|
8
|
+
hosts?: HostLike[];
|
|
9
|
+
/** `"html"` in agent-ui; ui-app passes `"chart"`. */
|
|
10
|
+
legendVariant?: MigrationDonutChartLegendVariant;
|
|
11
|
+
}
|
|
12
|
+
export declare const HostsOverview: FC<HostsOverviewProps>;
|
|
13
|
+
//# sourceMappingURL=HostsOverview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HostsOverview.d.ts","sourceRoot":"","sources":["../../src/report/HostsOverview.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAGL,KAAK,gCAAgC,EACtC,MAAM,kCAAkC,CAAC;AAO1C,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,qDAAqD;IACrD,aAAa,CAAC,EAAE,gCAAgC,CAAC;CAClD;AAuFD,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAwDhD,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { css } from "@emotion/css";
|
|
3
|
+
import { Card, CardBody, CardTitle, Flex, FlexItem, } from "@patternfly/react-core";
|
|
4
|
+
import { ServerIcon } from "@patternfly/react-icons";
|
|
5
|
+
import { chart_color_blue_300, chart_color_green_300, chart_color_purple_100, chart_color_purple_300, chart_color_teal_200, chart_color_yellow_400, } from "@patternfly/react-tokens";
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
import { MigrationDonutChart, } from "../charts/MigrationDonutChart.js";
|
|
8
|
+
import { CardEmptyState } from "./CardEmptyState.js";
|
|
9
|
+
import { ChartHeaderActions } from "./ChartDownloadButton.js";
|
|
10
|
+
import { ChartExportSurface } from "./ChartExportSurface.js";
|
|
11
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
12
|
+
import { dashboardStyles } from "./dashboardStyles.js";
|
|
13
|
+
const TOP_HOST_MODELS = 5;
|
|
14
|
+
const OTHER_MODELS_LABEL = "Other models";
|
|
15
|
+
const UNKNOWN_MODEL_LABEL = "Unknown model";
|
|
16
|
+
const CHART_ID = "hosts-overview";
|
|
17
|
+
const CHART_TITLE = "Host distribution by model";
|
|
18
|
+
/**
|
|
19
|
+
* Same categorical order as the ui-app host-model donut, mapped onto
|
|
20
|
+
* PatternFly `chart_color_*` tokens (nearest token where the old hex
|
|
21
|
+
* was not itself a token).
|
|
22
|
+
*/
|
|
23
|
+
const HOST_MODEL_COLORS = [
|
|
24
|
+
chart_color_blue_300.value,
|
|
25
|
+
chart_color_purple_300.value,
|
|
26
|
+
chart_color_purple_100.value,
|
|
27
|
+
chart_color_teal_200.value,
|
|
28
|
+
chart_color_yellow_400.value,
|
|
29
|
+
chart_color_green_300.value,
|
|
30
|
+
];
|
|
31
|
+
const cardSubtitle = css `
|
|
32
|
+
color: var(--pf-t--global--text--color--subtle);
|
|
33
|
+
font-size: 0.85rem;
|
|
34
|
+
`;
|
|
35
|
+
const hostModelLabel = (model) => {
|
|
36
|
+
const raw = typeof model === "string"
|
|
37
|
+
? model
|
|
38
|
+
: typeof model === "number"
|
|
39
|
+
? String(model)
|
|
40
|
+
: "";
|
|
41
|
+
const trimmed = raw.trim();
|
|
42
|
+
return trimmed !== "" ? trimmed : UNKNOWN_MODEL_LABEL;
|
|
43
|
+
};
|
|
44
|
+
const buildHostModelDonut = (hosts) => {
|
|
45
|
+
const counts = new Map();
|
|
46
|
+
for (const host of hosts) {
|
|
47
|
+
const label = hostModelLabel(host?.model);
|
|
48
|
+
counts.set(label, (counts.get(label) ?? 0) + 1);
|
|
49
|
+
}
|
|
50
|
+
const entries = [...counts.entries()]
|
|
51
|
+
.map(([model, count]) => ({ model, count }))
|
|
52
|
+
.sort((a, b) => b.count - a.count);
|
|
53
|
+
const top = entries.slice(0, TOP_HOST_MODELS);
|
|
54
|
+
const restSum = entries
|
|
55
|
+
.slice(TOP_HOST_MODELS)
|
|
56
|
+
.reduce((sum, entry) => sum + entry.count, 0);
|
|
57
|
+
const slices = top.map((entry) => ({
|
|
58
|
+
name: entry.model,
|
|
59
|
+
count: entry.count,
|
|
60
|
+
countDisplay: `${entry.count} hosts`,
|
|
61
|
+
legendCategory: entry.model,
|
|
62
|
+
}));
|
|
63
|
+
if (restSum > 0) {
|
|
64
|
+
slices.push({
|
|
65
|
+
name: OTHER_MODELS_LABEL,
|
|
66
|
+
count: restSum,
|
|
67
|
+
countDisplay: `${restSum} hosts`,
|
|
68
|
+
legendCategory: OTHER_MODELS_LABEL,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const legend = {};
|
|
72
|
+
slices.forEach((slice, index) => {
|
|
73
|
+
const color = HOST_MODEL_COLORS[index % HOST_MODEL_COLORS.length];
|
|
74
|
+
if (color) {
|
|
75
|
+
legend[slice.legendCategory] = color;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return { slices, legend, totalHosts: hosts.length };
|
|
79
|
+
};
|
|
80
|
+
export const HostsOverview = ({ hosts, legendVariant = "html", }) => {
|
|
81
|
+
const { slices, legend, totalHosts } = useMemo(() => buildHostModelDonut(Array.isArray(hosts) ? hosts : []), [hosts]);
|
|
82
|
+
return (_jsx(ChartExportSurface, { id: CHART_ID, title: CHART_TITLE, children: _jsxs(Card, { className: dashboardStyles.card, children: [_jsx(CardTitle, { children: _jsxs(Flex, { justifyContent: { default: "justifyContentSpaceBetween" }, alignItems: { default: "alignItemsCenter" }, children: [_jsx(FlexItem, { children: _jsxs("div", { children: [_jsxs("div", { children: [_jsx(ServerIcon, {}), " ", CHART_TITLE] }), _jsx("div", { className: cardSubtitle, children: "Top 5 models" })] }) }), _jsx(ChartHeaderActions, { chartId: CHART_ID, title: CHART_TITLE })] }) }), _jsx(CardBody, { className: dashboardStyles.cardBodyScrollable, children: slices.length === 0 ? (_jsx(CardEmptyState, { title: REPORT_CARD_EMPTY_STATE_TITLES.hosts })) : (_jsx(MigrationDonutChart, { legendVariant: legendVariant, data: slices, height: 300, width: 420, donutThickness: 18, titleFontSize: 34, legend: legend, legendWidth: 680, title: `${totalHosts}`, subTitle: "Hosts", subTitleColor: "var(--pf-t--global--text--color--subtle)", itemsPerRow: 2, labelFontSize: 16, marginLeft: "0%", tooltipLabelFormatter: ({ datum, percent }) => `${datum.countDisplay}\n${percent.toFixed(1)}%` })) })] }) }));
|
|
83
|
+
};
|
|
84
|
+
HostsOverview.displayName = "HostsOverview";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
export interface ReportTableProps<DataItem> {
|
|
3
|
+
columns: string[];
|
|
4
|
+
data: DataItem[];
|
|
5
|
+
fields: Array<keyof DataItem>;
|
|
6
|
+
caption?: string;
|
|
7
|
+
onRowClick?: (item: DataItem) => void;
|
|
8
|
+
clickableFields?: Array<keyof DataItem>;
|
|
9
|
+
}
|
|
10
|
+
export declare function ReportTable<DataItem>(props: ReportTableProps<DataItem>): React.ReactNode;
|
|
11
|
+
export declare namespace ReportTable {
|
|
12
|
+
var displayName: string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=ReportTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReportTable.d.ts","sourceRoot":"","sources":["../../src/report/ReportTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,gBAAgB,CAAC,QAAQ;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,MAAM,EAAE,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IACtC,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;CACzC;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAClC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GAChC,KAAK,CAAC,SAAS,CA8DjB;yBAhEe,WAAW"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "@patternfly/react-core";
|
|
3
|
+
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";
|
|
4
|
+
import { tableFullWidthStyle } from "./dashboardStyles.js";
|
|
5
|
+
export function ReportTable(props) {
|
|
6
|
+
const { columns, data, fields, caption, onRowClick, clickableFields = [], } = props;
|
|
7
|
+
const renderCell = (item, field) => {
|
|
8
|
+
const value = item[field];
|
|
9
|
+
if (value === "" || value == null) {
|
|
10
|
+
return "-";
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === "boolean") {
|
|
13
|
+
return value ? "True" : "False";
|
|
14
|
+
}
|
|
15
|
+
// Make field clickable if specified
|
|
16
|
+
if (clickableFields.includes(field) && onRowClick) {
|
|
17
|
+
return (_jsx(Button, { variant: "link", isInline: true, onClick: () => onRowClick(item), style: { padding: 0, fontSize: "inherit" }, children: value }));
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
return (_jsxs(Table, { variant: "compact", borders: false, className: tableFullWidthStyle, children: [caption && _jsx("caption", { children: caption }), _jsx(Thead, { children: _jsx(Tr, { children: columns.map((name) => (_jsx(Th, { children: name }, name))) }) }), _jsx(Tbody, { children: data.map((item) => (_jsx(Tr, { children: fields.map((f) => (_jsx(Td, { children: renderCell(item, f) }, String(f)))) }, `row-${String(item[fields[0]])}`))) })] }));
|
|
22
|
+
}
|
|
23
|
+
ReportTable.displayName = "ReportTable";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type { MigrationIssueLike } from "./ErrorTable.js";
|
|
3
|
+
export interface WarningsTableProps {
|
|
4
|
+
warnings: MigrationIssueLike[];
|
|
5
|
+
/** Optional drill-down callback invoked with an issue's `label`. */
|
|
6
|
+
onConcernClick?: (concernLabel: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const WarningsTable: React.FC<WarningsTableProps>;
|
|
9
|
+
//# sourceMappingURL=WarningsTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WarningsTable.d.ts","sourceRoot":"","sources":["../../src/report/WarningsTable.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,oEAAoE;IACpE,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAgDtD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Card, CardBody, CardTitle, Flex, FlexItem, Icon, } from "@patternfly/react-core";
|
|
3
|
+
import { ExclamationTriangleIcon } from "@patternfly/react-icons";
|
|
4
|
+
import { CardEmptyState } from "./CardEmptyState.js";
|
|
5
|
+
import { ChartHeaderActions } from "./ChartDownloadButton.js";
|
|
6
|
+
import { ChartExportSurface } from "./ChartExportSurface.js";
|
|
7
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
8
|
+
import { dashboardStyles } from "./dashboardStyles.js";
|
|
9
|
+
import { ReportTable } from "./ReportTable.js";
|
|
10
|
+
export const WarningsTable = ({ warnings, onConcernClick, }) => {
|
|
11
|
+
const handleRowClick = (issue) => {
|
|
12
|
+
if (issue.label && onConcernClick) {
|
|
13
|
+
onConcernClick(issue.label);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const chartId = "warnings-table";
|
|
17
|
+
const chartTitle = "Warnings";
|
|
18
|
+
return (_jsx(ChartExportSurface, { id: chartId, title: chartTitle, children: _jsxs(Card, { className: dashboardStyles.card, children: [_jsx(CardTitle, { children: _jsxs(Flex, { justifyContent: { default: "justifyContentSpaceBetween" }, alignItems: { default: "alignItemsCenter" }, children: [_jsxs(FlexItem, { children: [_jsx(Icon, { status: "warning", children: _jsx(ExclamationTriangleIcon, {}) }), " ", "Warnings"] }), _jsx(ChartHeaderActions, { chartId: chartId, title: chartTitle })] }) }), _jsx(CardBody, { className: dashboardStyles.cardBodyScrollable, children: warnings.length === 0 ? (_jsx(CardEmptyState, { title: REPORT_CARD_EMPTY_STATE_TITLES.warnings })) : (_jsx("div", { children: _jsx(ReportTable, { data: warnings, columns: ["Description", "Total VMs"], fields: ["assessment", "count"], onRowClick: onConcernClick ? handleRowClick : undefined, clickableFields: onConcernClick ? ["assessment"] : [] }) })) })] }) }));
|
|
19
|
+
};
|
|
20
|
+
WarningsTable.displayName = "WarningsTable";
|
|
@@ -19,6 +19,8 @@ export declare const REPORT_CARD_EMPTY_STATE_TITLES: {
|
|
|
19
19
|
readonly operatingSystems: "Operating system data not collected";
|
|
20
20
|
readonly migrationStatus: "Migration status data not collected";
|
|
21
21
|
readonly issuesBreakdown: "Issues breakdown data not collected";
|
|
22
|
+
readonly errors: "Error data not collected";
|
|
23
|
+
readonly warnings: "Warning data not collected";
|
|
22
24
|
readonly hostPowerStates: "Host power state data not collected";
|
|
23
25
|
readonly vmPowerStates: "VM power state data not collected";
|
|
24
26
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/report/constants.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,WAA6B,CAAC;AAC5D,eAAO,MAAM,iBAAiB,WAA+B,CAAC;AAE9D,eAAO,MAAM,8BAA8B
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/report/constants.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,WAA6B,CAAC;AAC5D,eAAO,MAAM,iBAAiB,WAA+B,CAAC;AAE9D,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;CAkBjC,CAAC"}
|
package/dist/report/constants.js
CHANGED
|
@@ -20,6 +20,8 @@ export const REPORT_CARD_EMPTY_STATE_TITLES = {
|
|
|
20
20
|
operatingSystems: "Operating system data not collected",
|
|
21
21
|
migrationStatus: "Migration status data not collected",
|
|
22
22
|
issuesBreakdown: "Issues breakdown data not collected",
|
|
23
|
+
errors: "Error data not collected",
|
|
24
|
+
warnings: "Warning data not collected",
|
|
23
25
|
hostPowerStates: "Host power state data not collected",
|
|
24
26
|
vmPowerStates: "VM power state data not collected",
|
|
25
27
|
};
|
package/dist/report/index.d.ts
CHANGED
|
@@ -10,8 +10,10 @@ export { chartColorFailure, chartColorSuccess, REPORT_CARD_EMPTY_STATE_TITLES, }
|
|
|
10
10
|
export { DashboardEmptyStateBase, type DashboardEmptyStateBaseProps, } from "./DashboardEmptyStateBase.js";
|
|
11
11
|
export { dashboardStyles, tableFullWidthStyle } from "./dashboardStyles.js";
|
|
12
12
|
export { EmptySearchResults, type EmptySearchResultsProps, } from "./EmptySearchResults.js";
|
|
13
|
+
export { ErrorTable, type ErrorTableProps, type MigrationIssueLike, } from "./ErrorTable.js";
|
|
13
14
|
export { FeatureStatusBadge, type FeatureStatusBadgeProps, } from "./FeatureStatusBadge.js";
|
|
14
15
|
export { HostPowerStates, type HostPowerStatesProps, } from "./HostPowerStates.js";
|
|
16
|
+
export { type HostLike, HostsOverview, type HostsOverviewProps, } from "./HostsOverview.js";
|
|
15
17
|
export { buildHtmlFromCharts, buildHtmlReport, escapeHtml, type HtmlReportImage, } from "./htmlExport.js";
|
|
16
18
|
export { InfrastructureSummary, type InfrastructureSummaryProps, } from "./InfrastructureSummary.js";
|
|
17
19
|
export type { ClusterDetailRow, ClusterDetailsModel, FeatureStatus, HostCapability, InfrastructureSummaryModel, InventoryCluster, InventoryDatastore, InventoryHost, InventoryInfra, InventoryNetwork, InventoryVms, NetworkLabel, } from "./infrastructureSummaryModel.js";
|
|
@@ -30,10 +32,12 @@ export { type PdfExportSegment, sliceCanvas, splitSegmentForPageHeight, } from "
|
|
|
30
32
|
export type { PowerStateChartModel } from "./powerStates.js";
|
|
31
33
|
export { buildHostPowerStateChart, buildVmPowerStateChart, HOST_POWER_COLORS, VM_POWER_COLORS, } from "./powerStates.js";
|
|
32
34
|
export { ExportReportButton, type ExportReportButtonProps, ReportExportMenu, type ReportExportMenuProps, } from "./ReportExportMenu.js";
|
|
35
|
+
export { ReportTable, type ReportTableProps } from "./ReportTable.js";
|
|
33
36
|
export { type ReportExportOption, type StandardReportExportHandlers, standardReportExportOptions, } from "./reportExportOptions.js";
|
|
34
37
|
export { SupportTierBadge } from "./SupportTierBadge.js";
|
|
35
38
|
export { ALL_TIERS_FILTER, type OsBarChartViewModel, type OsTableRow, useOsBarChartViewModel, } from "./useOsBarChartViewModel.js";
|
|
36
39
|
export { VCenterClusterDetails, type VCenterClusterDetailsProps, } from "./VCenterClusterDetails.js";
|
|
37
40
|
export { VmPowerStates, type VmPowerStatesProps, } from "./VmPowerStates.js";
|
|
41
|
+
export { WarningsTable, type WarningsTableProps, } from "./WarningsTable.js";
|
|
38
42
|
export { zipChartPngs } from "./zipChartPngs.js";
|
|
39
43
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"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"}
|
package/dist/report/index.js
CHANGED
|
@@ -10,8 +10,10 @@ export { chartColorFailure, chartColorSuccess, REPORT_CARD_EMPTY_STATE_TITLES, }
|
|
|
10
10
|
export { DashboardEmptyStateBase, } from "./DashboardEmptyStateBase.js";
|
|
11
11
|
export { dashboardStyles, tableFullWidthStyle } from "./dashboardStyles.js";
|
|
12
12
|
export { EmptySearchResults, } from "./EmptySearchResults.js";
|
|
13
|
+
export { ErrorTable, } from "./ErrorTable.js";
|
|
13
14
|
export { FeatureStatusBadge, } from "./FeatureStatusBadge.js";
|
|
14
15
|
export { HostPowerStates, } from "./HostPowerStates.js";
|
|
16
|
+
export { HostsOverview, } from "./HostsOverview.js";
|
|
15
17
|
export { buildHtmlFromCharts, buildHtmlReport, escapeHtml, } from "./htmlExport.js";
|
|
16
18
|
export { InfrastructureSummary, } from "./InfrastructureSummary.js";
|
|
17
19
|
export { booleanToFeatureStatus, buildClusterDetailRows, buildClusterDetails, buildInfrastructureSummary, countVCenters, formatVSphereVersion, hostCapabilityStatus, visibleNetworks, vsanStatus, } from "./infrastructureSummaryModel.js";
|
|
@@ -27,9 +29,11 @@ export { buildPdfFromCharts } from "./pdfExport.js";
|
|
|
27
29
|
export { sliceCanvas, splitSegmentForPageHeight, } from "./pdfPage.js";
|
|
28
30
|
export { buildHostPowerStateChart, buildVmPowerStateChart, HOST_POWER_COLORS, VM_POWER_COLORS, } from "./powerStates.js";
|
|
29
31
|
export { ExportReportButton, ReportExportMenu, } from "./ReportExportMenu.js";
|
|
32
|
+
export { ReportTable } from "./ReportTable.js";
|
|
30
33
|
export { standardReportExportOptions, } from "./reportExportOptions.js";
|
|
31
34
|
export { SupportTierBadge } from "./SupportTierBadge.js";
|
|
32
35
|
export { ALL_TIERS_FILTER, useOsBarChartViewModel, } from "./useOsBarChartViewModel.js";
|
|
33
36
|
export { VCenterClusterDetails, } from "./VCenterClusterDetails.js";
|
|
34
37
|
export { VmPowerStates, } from "./VmPowerStates.js";
|
|
38
|
+
export { WarningsTable, } from "./WarningsTable.js";
|
|
35
39
|
export { zipChartPngs } from "./zipChartPngs.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openshift-migration-advisor/shared-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Shared React UI components for Migration Advisor apps (report charts, forms, OS support tiers)",
|
|
5
5
|
"author": "Montse Ortega <mortegag@redhat.com>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Card,
|
|
3
|
+
CardBody,
|
|
4
|
+
CardTitle,
|
|
5
|
+
Flex,
|
|
6
|
+
FlexItem,
|
|
7
|
+
Icon,
|
|
8
|
+
} from "@patternfly/react-core";
|
|
9
|
+
import { ExclamationCircleIcon } from "@patternfly/react-icons";
|
|
10
|
+
import type React from "react";
|
|
11
|
+
import { CardEmptyState } from "./CardEmptyState.js";
|
|
12
|
+
import { ChartHeaderActions } from "./ChartDownloadButton.js";
|
|
13
|
+
import { ChartExportSurface } from "./ChartExportSurface.js";
|
|
14
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
15
|
+
import { dashboardStyles } from "./dashboardStyles.js";
|
|
16
|
+
import { ReportTable } from "./ReportTable.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Minimal shape of a migration issue consumed by {@link ErrorTable}. Declared
|
|
20
|
+
* locally so this package does not depend on a specific SDK (`agent-sdk` or
|
|
21
|
+
* `planner-sdk`); callers pass the subset of `MigrationIssue` fields read here.
|
|
22
|
+
*/
|
|
23
|
+
export interface MigrationIssueLike {
|
|
24
|
+
label: string;
|
|
25
|
+
assessment: string;
|
|
26
|
+
count: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ErrorTableProps {
|
|
30
|
+
errors: MigrationIssueLike[];
|
|
31
|
+
/** Optional drill-down callback invoked with an issue's `label`. */
|
|
32
|
+
onConcernClick?: (concernLabel: string) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const ErrorTable: React.FC<ErrorTableProps> = ({
|
|
36
|
+
errors,
|
|
37
|
+
onConcernClick,
|
|
38
|
+
}) => {
|
|
39
|
+
const handleRowClick = (issue: MigrationIssueLike) => {
|
|
40
|
+
if (issue.label && onConcernClick) {
|
|
41
|
+
onConcernClick(issue.label);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const chartId = "errors-table";
|
|
46
|
+
const chartTitle = "Errors";
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<ChartExportSurface id={chartId} title={chartTitle}>
|
|
50
|
+
<Card className={dashboardStyles.card}>
|
|
51
|
+
<CardTitle>
|
|
52
|
+
<Flex
|
|
53
|
+
justifyContent={{ default: "justifyContentSpaceBetween" }}
|
|
54
|
+
alignItems={{ default: "alignItemsCenter" }}
|
|
55
|
+
>
|
|
56
|
+
<FlexItem>
|
|
57
|
+
<Icon status="danger">
|
|
58
|
+
<ExclamationCircleIcon />
|
|
59
|
+
</Icon>{" "}
|
|
60
|
+
Errors
|
|
61
|
+
</FlexItem>
|
|
62
|
+
<ChartHeaderActions chartId={chartId} title={chartTitle} />
|
|
63
|
+
</Flex>
|
|
64
|
+
</CardTitle>
|
|
65
|
+
<CardBody className={dashboardStyles.cardBodyScrollable}>
|
|
66
|
+
{errors.length === 0 ? (
|
|
67
|
+
<CardEmptyState title={REPORT_CARD_EMPTY_STATE_TITLES.errors} />
|
|
68
|
+
) : (
|
|
69
|
+
<div>
|
|
70
|
+
<ReportTable<MigrationIssueLike>
|
|
71
|
+
data={errors}
|
|
72
|
+
columns={["Description", "Total VMs"]}
|
|
73
|
+
fields={["assessment", "count"]}
|
|
74
|
+
onRowClick={onConcernClick ? handleRowClick : undefined}
|
|
75
|
+
clickableFields={onConcernClick ? ["assessment"] : []}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
</CardBody>
|
|
80
|
+
</Card>
|
|
81
|
+
</ChartExportSurface>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
ErrorTable.displayName = "ErrorTable";
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { css } from "@emotion/css";
|
|
2
|
+
import {
|
|
3
|
+
Card,
|
|
4
|
+
CardBody,
|
|
5
|
+
CardTitle,
|
|
6
|
+
Flex,
|
|
7
|
+
FlexItem,
|
|
8
|
+
} from "@patternfly/react-core";
|
|
9
|
+
import { ServerIcon } from "@patternfly/react-icons";
|
|
10
|
+
import {
|
|
11
|
+
chart_color_blue_300,
|
|
12
|
+
chart_color_green_300,
|
|
13
|
+
chart_color_purple_100,
|
|
14
|
+
chart_color_purple_300,
|
|
15
|
+
chart_color_teal_200,
|
|
16
|
+
chart_color_yellow_400,
|
|
17
|
+
} from "@patternfly/react-tokens";
|
|
18
|
+
import type { FC } from "react";
|
|
19
|
+
import { useMemo } from "react";
|
|
20
|
+
import {
|
|
21
|
+
MigrationDonutChart,
|
|
22
|
+
type MigrationDonutChartDatum,
|
|
23
|
+
type MigrationDonutChartLegendVariant,
|
|
24
|
+
} from "../charts/MigrationDonutChart.js";
|
|
25
|
+
import { CardEmptyState } from "./CardEmptyState.js";
|
|
26
|
+
import { ChartHeaderActions } from "./ChartDownloadButton.js";
|
|
27
|
+
import { ChartExportSurface } from "./ChartExportSurface.js";
|
|
28
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
29
|
+
import { dashboardStyles } from "./dashboardStyles.js";
|
|
30
|
+
|
|
31
|
+
/** Smallest host shape this card reads. Apps pass SDK hosts structurally. */
|
|
32
|
+
export interface HostLike {
|
|
33
|
+
model?: string | number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface HostsOverviewProps {
|
|
37
|
+
hosts?: HostLike[];
|
|
38
|
+
/** `"html"` in agent-ui; ui-app passes `"chart"`. */
|
|
39
|
+
legendVariant?: MigrationDonutChartLegendVariant;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const TOP_HOST_MODELS = 5;
|
|
43
|
+
const OTHER_MODELS_LABEL = "Other models";
|
|
44
|
+
const UNKNOWN_MODEL_LABEL = "Unknown model";
|
|
45
|
+
const CHART_ID = "hosts-overview";
|
|
46
|
+
const CHART_TITLE = "Host distribution by model";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Same categorical order as the ui-app host-model donut, mapped onto
|
|
50
|
+
* PatternFly `chart_color_*` tokens (nearest token where the old hex
|
|
51
|
+
* was not itself a token).
|
|
52
|
+
*/
|
|
53
|
+
const HOST_MODEL_COLORS = [
|
|
54
|
+
chart_color_blue_300.value,
|
|
55
|
+
chart_color_purple_300.value,
|
|
56
|
+
chart_color_purple_100.value,
|
|
57
|
+
chart_color_teal_200.value,
|
|
58
|
+
chart_color_yellow_400.value,
|
|
59
|
+
chart_color_green_300.value,
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const cardSubtitle = css`
|
|
63
|
+
color: var(--pf-t--global--text--color--subtle);
|
|
64
|
+
font-size: 0.85rem;
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const hostModelLabel = (model: HostLike["model"]): string => {
|
|
68
|
+
const raw =
|
|
69
|
+
typeof model === "string"
|
|
70
|
+
? model
|
|
71
|
+
: typeof model === "number"
|
|
72
|
+
? String(model)
|
|
73
|
+
: "";
|
|
74
|
+
const trimmed = raw.trim();
|
|
75
|
+
return trimmed !== "" ? trimmed : UNKNOWN_MODEL_LABEL;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const buildHostModelDonut = (
|
|
79
|
+
hosts: HostLike[],
|
|
80
|
+
): {
|
|
81
|
+
slices: MigrationDonutChartDatum[];
|
|
82
|
+
legend: Record<string, string>;
|
|
83
|
+
totalHosts: number;
|
|
84
|
+
} => {
|
|
85
|
+
const counts = new Map<string, number>();
|
|
86
|
+
for (const host of hosts) {
|
|
87
|
+
const label = hostModelLabel(host?.model);
|
|
88
|
+
counts.set(label, (counts.get(label) ?? 0) + 1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const entries = [...counts.entries()]
|
|
92
|
+
.map(([model, count]) => ({ model, count }))
|
|
93
|
+
.sort((a, b) => b.count - a.count);
|
|
94
|
+
|
|
95
|
+
const top = entries.slice(0, TOP_HOST_MODELS);
|
|
96
|
+
const restSum = entries
|
|
97
|
+
.slice(TOP_HOST_MODELS)
|
|
98
|
+
.reduce((sum, entry) => sum + entry.count, 0);
|
|
99
|
+
|
|
100
|
+
const slices: MigrationDonutChartDatum[] = top.map((entry) => ({
|
|
101
|
+
name: entry.model,
|
|
102
|
+
count: entry.count,
|
|
103
|
+
countDisplay: `${entry.count} hosts`,
|
|
104
|
+
legendCategory: entry.model,
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
if (restSum > 0) {
|
|
108
|
+
slices.push({
|
|
109
|
+
name: OTHER_MODELS_LABEL,
|
|
110
|
+
count: restSum,
|
|
111
|
+
countDisplay: `${restSum} hosts`,
|
|
112
|
+
legendCategory: OTHER_MODELS_LABEL,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const legend: Record<string, string> = {};
|
|
117
|
+
slices.forEach((slice, index) => {
|
|
118
|
+
const color = HOST_MODEL_COLORS[index % HOST_MODEL_COLORS.length];
|
|
119
|
+
if (color) {
|
|
120
|
+
legend[slice.legendCategory] = color;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return { slices, legend, totalHosts: hosts.length };
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const HostsOverview: FC<HostsOverviewProps> = ({
|
|
128
|
+
hosts,
|
|
129
|
+
legendVariant = "html",
|
|
130
|
+
}) => {
|
|
131
|
+
const { slices, legend, totalHosts } = useMemo(
|
|
132
|
+
() => buildHostModelDonut(Array.isArray(hosts) ? hosts : []),
|
|
133
|
+
[hosts],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<ChartExportSurface id={CHART_ID} title={CHART_TITLE}>
|
|
138
|
+
<Card className={dashboardStyles.card}>
|
|
139
|
+
<CardTitle>
|
|
140
|
+
<Flex
|
|
141
|
+
justifyContent={{ default: "justifyContentSpaceBetween" }}
|
|
142
|
+
alignItems={{ default: "alignItemsCenter" }}
|
|
143
|
+
>
|
|
144
|
+
<FlexItem>
|
|
145
|
+
<div>
|
|
146
|
+
<div>
|
|
147
|
+
<ServerIcon /> {CHART_TITLE}
|
|
148
|
+
</div>
|
|
149
|
+
<div className={cardSubtitle}>Top 5 models</div>
|
|
150
|
+
</div>
|
|
151
|
+
</FlexItem>
|
|
152
|
+
<ChartHeaderActions chartId={CHART_ID} title={CHART_TITLE} />
|
|
153
|
+
</Flex>
|
|
154
|
+
</CardTitle>
|
|
155
|
+
<CardBody className={dashboardStyles.cardBodyScrollable}>
|
|
156
|
+
{slices.length === 0 ? (
|
|
157
|
+
<CardEmptyState title={REPORT_CARD_EMPTY_STATE_TITLES.hosts} />
|
|
158
|
+
) : (
|
|
159
|
+
<MigrationDonutChart
|
|
160
|
+
legendVariant={legendVariant}
|
|
161
|
+
data={slices}
|
|
162
|
+
height={300}
|
|
163
|
+
width={420}
|
|
164
|
+
donutThickness={18}
|
|
165
|
+
titleFontSize={34}
|
|
166
|
+
legend={legend}
|
|
167
|
+
legendWidth={680}
|
|
168
|
+
title={`${totalHosts}`}
|
|
169
|
+
subTitle="Hosts"
|
|
170
|
+
subTitleColor="var(--pf-t--global--text--color--subtle)"
|
|
171
|
+
itemsPerRow={2}
|
|
172
|
+
labelFontSize={16}
|
|
173
|
+
marginLeft="0%"
|
|
174
|
+
tooltipLabelFormatter={({ datum, percent }) =>
|
|
175
|
+
`${datum.countDisplay}\n${percent.toFixed(1)}%`
|
|
176
|
+
}
|
|
177
|
+
/>
|
|
178
|
+
)}
|
|
179
|
+
</CardBody>
|
|
180
|
+
</Card>
|
|
181
|
+
</ChartExportSurface>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
HostsOverview.displayName = "HostsOverview";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Button } from "@patternfly/react-core";
|
|
2
|
+
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";
|
|
3
|
+
import type React from "react";
|
|
4
|
+
import { tableFullWidthStyle } from "./dashboardStyles.js";
|
|
5
|
+
|
|
6
|
+
export interface ReportTableProps<DataItem> {
|
|
7
|
+
columns: string[];
|
|
8
|
+
data: DataItem[];
|
|
9
|
+
fields: Array<keyof DataItem>;
|
|
10
|
+
caption?: string;
|
|
11
|
+
onRowClick?: (item: DataItem) => void;
|
|
12
|
+
clickableFields?: Array<keyof DataItem>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function ReportTable<DataItem>(
|
|
16
|
+
props: ReportTableProps<DataItem>,
|
|
17
|
+
): React.ReactNode {
|
|
18
|
+
const {
|
|
19
|
+
columns,
|
|
20
|
+
data,
|
|
21
|
+
fields,
|
|
22
|
+
caption,
|
|
23
|
+
onRowClick,
|
|
24
|
+
clickableFields = [],
|
|
25
|
+
} = props;
|
|
26
|
+
|
|
27
|
+
const renderCell = (
|
|
28
|
+
item: DataItem,
|
|
29
|
+
field: keyof DataItem,
|
|
30
|
+
): React.ReactNode => {
|
|
31
|
+
const value = item[field];
|
|
32
|
+
|
|
33
|
+
if (value === "" || value == null) {
|
|
34
|
+
return "-";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof value === "boolean") {
|
|
38
|
+
return value ? "True" : "False";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Make field clickable if specified
|
|
42
|
+
if (clickableFields.includes(field) && onRowClick) {
|
|
43
|
+
return (
|
|
44
|
+
<Button
|
|
45
|
+
variant="link"
|
|
46
|
+
isInline
|
|
47
|
+
onClick={() => onRowClick(item)}
|
|
48
|
+
style={{ padding: 0, fontSize: "inherit" }}
|
|
49
|
+
>
|
|
50
|
+
{value as React.ReactNode}
|
|
51
|
+
</Button>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return value as React.ReactNode;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Table variant="compact" borders={false} className={tableFullWidthStyle}>
|
|
60
|
+
{caption && <caption>{caption}</caption>}
|
|
61
|
+
<Thead>
|
|
62
|
+
<Tr>
|
|
63
|
+
{columns.map((name) => (
|
|
64
|
+
<Th key={name}>{name}</Th>
|
|
65
|
+
))}
|
|
66
|
+
</Tr>
|
|
67
|
+
</Thead>
|
|
68
|
+
<Tbody>
|
|
69
|
+
{data.map((item) => (
|
|
70
|
+
<Tr key={`row-${String(item[fields[0]])}`}>
|
|
71
|
+
{fields.map((f) => (
|
|
72
|
+
<Td key={String(f)}>{renderCell(item, f)}</Td>
|
|
73
|
+
))}
|
|
74
|
+
</Tr>
|
|
75
|
+
))}
|
|
76
|
+
</Tbody>
|
|
77
|
+
</Table>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ReportTable.displayName = "ReportTable";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Card,
|
|
3
|
+
CardBody,
|
|
4
|
+
CardTitle,
|
|
5
|
+
Flex,
|
|
6
|
+
FlexItem,
|
|
7
|
+
Icon,
|
|
8
|
+
} from "@patternfly/react-core";
|
|
9
|
+
import { ExclamationTriangleIcon } from "@patternfly/react-icons";
|
|
10
|
+
import type React from "react";
|
|
11
|
+
import { CardEmptyState } from "./CardEmptyState.js";
|
|
12
|
+
import { ChartHeaderActions } from "./ChartDownloadButton.js";
|
|
13
|
+
import { ChartExportSurface } from "./ChartExportSurface.js";
|
|
14
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
15
|
+
import { dashboardStyles } from "./dashboardStyles.js";
|
|
16
|
+
import type { MigrationIssueLike } from "./ErrorTable.js";
|
|
17
|
+
import { ReportTable } from "./ReportTable.js";
|
|
18
|
+
|
|
19
|
+
export interface WarningsTableProps {
|
|
20
|
+
warnings: MigrationIssueLike[];
|
|
21
|
+
/** Optional drill-down callback invoked with an issue's `label`. */
|
|
22
|
+
onConcernClick?: (concernLabel: string) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const WarningsTable: React.FC<WarningsTableProps> = ({
|
|
26
|
+
warnings,
|
|
27
|
+
onConcernClick,
|
|
28
|
+
}) => {
|
|
29
|
+
const handleRowClick = (issue: MigrationIssueLike) => {
|
|
30
|
+
if (issue.label && onConcernClick) {
|
|
31
|
+
onConcernClick(issue.label);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const chartId = "warnings-table";
|
|
36
|
+
const chartTitle = "Warnings";
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<ChartExportSurface id={chartId} title={chartTitle}>
|
|
40
|
+
<Card className={dashboardStyles.card}>
|
|
41
|
+
<CardTitle>
|
|
42
|
+
<Flex
|
|
43
|
+
justifyContent={{ default: "justifyContentSpaceBetween" }}
|
|
44
|
+
alignItems={{ default: "alignItemsCenter" }}
|
|
45
|
+
>
|
|
46
|
+
<FlexItem>
|
|
47
|
+
<Icon status="warning">
|
|
48
|
+
<ExclamationTriangleIcon />
|
|
49
|
+
</Icon>{" "}
|
|
50
|
+
Warnings
|
|
51
|
+
</FlexItem>
|
|
52
|
+
<ChartHeaderActions chartId={chartId} title={chartTitle} />
|
|
53
|
+
</Flex>
|
|
54
|
+
</CardTitle>
|
|
55
|
+
<CardBody className={dashboardStyles.cardBodyScrollable}>
|
|
56
|
+
{warnings.length === 0 ? (
|
|
57
|
+
<CardEmptyState title={REPORT_CARD_EMPTY_STATE_TITLES.warnings} />
|
|
58
|
+
) : (
|
|
59
|
+
<div>
|
|
60
|
+
<ReportTable<MigrationIssueLike>
|
|
61
|
+
data={warnings}
|
|
62
|
+
columns={["Description", "Total VMs"]}
|
|
63
|
+
fields={["assessment", "count"]}
|
|
64
|
+
onRowClick={onConcernClick ? handleRowClick : undefined}
|
|
65
|
+
clickableFields={onConcernClick ? ["assessment"] : []}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
</CardBody>
|
|
70
|
+
</Card>
|
|
71
|
+
</ChartExportSurface>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
WarningsTable.displayName = "WarningsTable";
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import {
|
|
3
|
+
chart_color_blue_300,
|
|
4
|
+
chart_color_green_300,
|
|
5
|
+
chart_color_purple_100,
|
|
6
|
+
chart_color_purple_300,
|
|
7
|
+
chart_color_teal_200,
|
|
8
|
+
chart_color_yellow_400,
|
|
9
|
+
} from "@patternfly/react-tokens";
|
|
10
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
11
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
12
|
+
import type { MigrationDonutChartProps } from "../../charts/MigrationDonutChart.js";
|
|
13
|
+
import { HostsOverview } from "../HostsOverview.js";
|
|
14
|
+
|
|
15
|
+
const donutProps = vi.hoisted(() => ({
|
|
16
|
+
current: null as MigrationDonutChartProps | null,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock("../../charts/MigrationDonutChart.js", () => ({
|
|
20
|
+
MigrationDonutChart: (props: MigrationDonutChartProps): JSX.Element => {
|
|
21
|
+
donutProps.current = props;
|
|
22
|
+
return (
|
|
23
|
+
<div data-testid="hosts-donut">
|
|
24
|
+
{props.title} {props.subTitle}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
donutProps.current = null;
|
|
32
|
+
cleanup();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const renderedDonut = (): MigrationDonutChartProps => {
|
|
36
|
+
const props = donutProps.current;
|
|
37
|
+
if (!props) {
|
|
38
|
+
throw new Error("expected the host model donut to render");
|
|
39
|
+
}
|
|
40
|
+
return props;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
describe("HostsOverview", () => {
|
|
44
|
+
it("shows the shared empty state when no hosts are available", () => {
|
|
45
|
+
render(<HostsOverview />);
|
|
46
|
+
|
|
47
|
+
expect(screen.getByText("Host distribution by model")).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByText("Top 5 models")).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByText("Host data not collected")).toBeInTheDocument();
|
|
50
|
+
expect(screen.queryByTestId("hosts-donut")).not.toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("buckets host counts into the top 5 models plus Other models", () => {
|
|
54
|
+
render(
|
|
55
|
+
<HostsOverview
|
|
56
|
+
hosts={[
|
|
57
|
+
{ model: "M1" },
|
|
58
|
+
{ model: "M1" },
|
|
59
|
+
{ model: "M1" },
|
|
60
|
+
{ model: "M1" },
|
|
61
|
+
{ model: "M1" },
|
|
62
|
+
{ model: "M1" },
|
|
63
|
+
{ model: "M2" },
|
|
64
|
+
{ model: "M2" },
|
|
65
|
+
{ model: "M2" },
|
|
66
|
+
{ model: "M2" },
|
|
67
|
+
{ model: "M2" },
|
|
68
|
+
{ model: "M3" },
|
|
69
|
+
{ model: "M3" },
|
|
70
|
+
{ model: "M3" },
|
|
71
|
+
{ model: "M3" },
|
|
72
|
+
{ model: "M4" },
|
|
73
|
+
{ model: "M4" },
|
|
74
|
+
{ model: "M4" },
|
|
75
|
+
{ model: "M5" },
|
|
76
|
+
{ model: "M5" },
|
|
77
|
+
{ model: "M6" },
|
|
78
|
+
{ model: "M7" },
|
|
79
|
+
]}
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(screen.getByTestId("hosts-donut")).toHaveTextContent("22 Hosts");
|
|
84
|
+
|
|
85
|
+
const props = renderedDonut();
|
|
86
|
+
expect(props.data.map((slice) => [slice.name, slice.count])).toEqual([
|
|
87
|
+
["M1", 6],
|
|
88
|
+
["M2", 5],
|
|
89
|
+
["M3", 4],
|
|
90
|
+
["M4", 3],
|
|
91
|
+
["M5", 2],
|
|
92
|
+
["Other models", 2],
|
|
93
|
+
]);
|
|
94
|
+
expect(props.data[5]?.countDisplay).toBe("2 hosts");
|
|
95
|
+
expect(props.legend).toEqual({
|
|
96
|
+
M1: chart_color_blue_300.value,
|
|
97
|
+
M2: chart_color_purple_300.value,
|
|
98
|
+
M3: chart_color_purple_100.value,
|
|
99
|
+
M4: chart_color_teal_200.value,
|
|
100
|
+
M5: chart_color_yellow_400.value,
|
|
101
|
+
"Other models": chart_color_green_300.value,
|
|
102
|
+
});
|
|
103
|
+
expect(props.legendVariant).toBe("html");
|
|
104
|
+
expect(props.height).toBe(300);
|
|
105
|
+
expect(props.width).toBe(420);
|
|
106
|
+
expect(props.donutThickness).toBe(18);
|
|
107
|
+
expect(props.titleFontSize).toBe(34);
|
|
108
|
+
expect(props.legendWidth).toBe(680);
|
|
109
|
+
expect(props.itemsPerRow).toBe(2);
|
|
110
|
+
expect(props.labelFontSize).toBe(16);
|
|
111
|
+
expect(props.marginLeft).toBe("0%");
|
|
112
|
+
expect(props.subTitleColor).toBe(
|
|
113
|
+
"var(--pf-t--global--text--color--subtle)",
|
|
114
|
+
);
|
|
115
|
+
expect(
|
|
116
|
+
props.tooltipLabelFormatter?.({
|
|
117
|
+
datum: {
|
|
118
|
+
x: "M1",
|
|
119
|
+
y: 6,
|
|
120
|
+
countDisplay: "6 hosts",
|
|
121
|
+
legendCategory: "M1",
|
|
122
|
+
},
|
|
123
|
+
percent: 27.2727,
|
|
124
|
+
total: 22,
|
|
125
|
+
}),
|
|
126
|
+
).toBe("6 hosts\n27.3%");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("treats numeric models as labels and blank models as unknown", () => {
|
|
130
|
+
render(
|
|
131
|
+
<HostsOverview
|
|
132
|
+
legendVariant="chart"
|
|
133
|
+
hosts={[{ model: 42 }, { model: " " }, { model: " R640 " }]}
|
|
134
|
+
/>,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(renderedDonut().data.map((slice) => slice.name)).toEqual([
|
|
138
|
+
"42",
|
|
139
|
+
"Unknown model",
|
|
140
|
+
"R640",
|
|
141
|
+
]);
|
|
142
|
+
expect(renderedDonut().legendVariant).toBe("chart");
|
|
143
|
+
expect(renderedDonut().title).toBe("3");
|
|
144
|
+
});
|
|
145
|
+
});
|
package/src/report/constants.ts
CHANGED
|
@@ -25,6 +25,8 @@ export const REPORT_CARD_EMPTY_STATE_TITLES = {
|
|
|
25
25
|
operatingSystems: "Operating system data not collected",
|
|
26
26
|
migrationStatus: "Migration status data not collected",
|
|
27
27
|
issuesBreakdown: "Issues breakdown data not collected",
|
|
28
|
+
errors: "Error data not collected",
|
|
29
|
+
warnings: "Warning data not collected",
|
|
28
30
|
hostPowerStates: "Host power state data not collected",
|
|
29
31
|
vmPowerStates: "VM power state data not collected",
|
|
30
32
|
} as const;
|
package/src/report/index.ts
CHANGED
|
@@ -65,6 +65,11 @@ export {
|
|
|
65
65
|
EmptySearchResults,
|
|
66
66
|
type EmptySearchResultsProps,
|
|
67
67
|
} from "./EmptySearchResults.js";
|
|
68
|
+
export {
|
|
69
|
+
ErrorTable,
|
|
70
|
+
type ErrorTableProps,
|
|
71
|
+
type MigrationIssueLike,
|
|
72
|
+
} from "./ErrorTable.js";
|
|
68
73
|
export {
|
|
69
74
|
FeatureStatusBadge,
|
|
70
75
|
type FeatureStatusBadgeProps,
|
|
@@ -73,6 +78,11 @@ export {
|
|
|
73
78
|
HostPowerStates,
|
|
74
79
|
type HostPowerStatesProps,
|
|
75
80
|
} from "./HostPowerStates.js";
|
|
81
|
+
export {
|
|
82
|
+
type HostLike,
|
|
83
|
+
HostsOverview,
|
|
84
|
+
type HostsOverviewProps,
|
|
85
|
+
} from "./HostsOverview.js";
|
|
76
86
|
export {
|
|
77
87
|
buildHtmlFromCharts,
|
|
78
88
|
buildHtmlReport,
|
|
@@ -167,6 +177,7 @@ export {
|
|
|
167
177
|
ReportExportMenu,
|
|
168
178
|
type ReportExportMenuProps,
|
|
169
179
|
} from "./ReportExportMenu.js";
|
|
180
|
+
export { ReportTable, type ReportTableProps } from "./ReportTable.js";
|
|
170
181
|
export {
|
|
171
182
|
type ReportExportOption,
|
|
172
183
|
type StandardReportExportHandlers,
|
|
@@ -187,4 +198,8 @@ export {
|
|
|
187
198
|
VmPowerStates,
|
|
188
199
|
type VmPowerStatesProps,
|
|
189
200
|
} from "./VmPowerStates.js";
|
|
201
|
+
export {
|
|
202
|
+
WarningsTable,
|
|
203
|
+
type WarningsTableProps,
|
|
204
|
+
} from "./WarningsTable.js";
|
|
190
205
|
export { zipChartPngs } from "./zipChartPngs.js";
|