@openshift-migration-advisor/shared-components 0.25.0-fce5293f01c1 → 0.27.0-5d0cc20d809e
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/ChartExportProvider.d.ts +3 -2
- package/dist/report/ChartExportProvider.d.ts.map +1 -1
- package/dist/report/ChartExportProvider.js +3 -3
- package/dist/report/CpuAndMemoryOverview.d.ts +19 -0
- package/dist/report/CpuAndMemoryOverview.d.ts.map +1 -0
- package/dist/report/CpuAndMemoryOverview.js +112 -0
- 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/captureChartElement.d.ts.map +1 -1
- package/dist/report/captureChartElement.js +28 -8
- package/dist/report/chartExport.d.ts +23 -0
- package/dist/report/chartExport.d.ts.map +1 -1
- package/dist/report/chartExport.js +20 -0
- package/dist/report/chartExportContext.d.ts +6 -2
- package/dist/report/chartExportContext.d.ts.map +1 -1
- 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 +7 -2
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +5 -0
- package/dist/report/pdfExport.d.ts +2 -2
- package/dist/report/pdfExport.d.ts.map +1 -1
- package/dist/report/pdfExport.js +52 -3
- package/package.json +2 -2
- package/src/report/ChartExportProvider.tsx +13 -5
- package/src/report/CpuAndMemoryOverview.tsx +277 -0
- 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__/ChartExportProvider.test.tsx +110 -2
- package/src/report/__tests__/CpuAndMemoryOverview.test.tsx +200 -0
- package/src/report/__tests__/HostsOverview.test.tsx +145 -0
- package/src/report/__tests__/captureChartElement.test.ts +73 -0
- package/src/report/__tests__/chartExportPackagers.test.ts +75 -0
- package/src/report/captureChartElement.ts +37 -7
- package/src/report/chartExport.ts +40 -0
- package/src/report/chartExportContext.tsx +9 -1
- package/src/report/constants.ts +2 -0
- package/src/report/index.ts +22 -0
- package/src/report/pdfExport.ts +81 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type FC, type ReactNode } from "react";
|
|
2
|
-
import { type ChartCaptureSource, type ChartExportFile } from "./chartExport.js";
|
|
2
|
+
import { type ChartCaptureSource, type ChartExportFile, type PdfTextPage } from "./chartExport.js";
|
|
3
3
|
export type CaptureChartElement = (element: HTMLElement) => Promise<HTMLCanvasElement>;
|
|
4
4
|
export type ZipChartFiles = (files: ChartExportFile[]) => Promise<Blob>;
|
|
5
5
|
export type EncodeChartPng = (canvas: HTMLCanvasElement) => Promise<Blob>;
|
|
6
6
|
export type BuildChartDocument = (charts: ChartCaptureSource[], documentTitle: string) => Promise<Blob>;
|
|
7
|
+
export type BuildPdfDocument = (charts: ChartCaptureSource[], documentTitle: string, extraPages?: PdfTextPage[]) => Promise<Blob>;
|
|
7
8
|
export type { ChartExportApi, ChartExportFormat, } from "./chartExportContext.js";
|
|
8
9
|
export { useChartExport, useRegisterChart } from "./chartExportContext.js";
|
|
9
10
|
export interface ChartExportProviderProps {
|
|
@@ -11,7 +12,7 @@ export interface ChartExportProviderProps {
|
|
|
11
12
|
capture?: CaptureChartElement;
|
|
12
13
|
zipFiles?: ZipChartFiles;
|
|
13
14
|
encodePng?: EncodeChartPng;
|
|
14
|
-
buildPdf?:
|
|
15
|
+
buildPdf?: BuildPdfDocument;
|
|
15
16
|
buildHtml?: BuildChartDocument;
|
|
16
17
|
downloadFile?: (blob: Blob, filename: string) => void;
|
|
17
18
|
getZipFilename?: () => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartExportProvider.d.ts","sourceRoot":"","sources":["../../src/report/ChartExportProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAAE,EACP,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"ChartExportProvider.d.ts","sourceRoot":"","sources":["../../src/report/ChartExportProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAAE,EACP,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGpB,KAAK,WAAW,EAMjB,MAAM,kBAAkB,CAAC;AAU1B,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAChC,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AACxE,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAC1E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,MAAM,EAAE,kBAAkB,EAAE,EAC5B,aAAa,EAAE,MAAM,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,MAAM,gBAAgB,GAAG,CAC7B,MAAM,EAAE,kBAAkB,EAAE,EAC5B,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,WAAW,EAAE,KACvB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,YAAY,EACV,cAAc,EACd,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;CAChC;AAsED,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAiQ5D,CAAC"}
|
|
@@ -156,17 +156,17 @@ export const ChartExportProvider = ({ children, capture, zipFiles, encodePng = c
|
|
|
156
156
|
runCapture,
|
|
157
157
|
zipFiles,
|
|
158
158
|
]);
|
|
159
|
-
const downloadPdf = useCallback(async (documentTitle) => {
|
|
159
|
+
const downloadPdf = useCallback(async (documentTitle, extraPages) => {
|
|
160
160
|
await runCapture(async () => {
|
|
161
161
|
const charts = listCharts();
|
|
162
|
-
if (charts.length === 0) {
|
|
162
|
+
if (charts.length === 0 && (extraPages?.length ?? 0) === 0) {
|
|
163
163
|
throw new Error("The report is not ready to export yet.");
|
|
164
164
|
}
|
|
165
165
|
const captureFn = await resolveCapture(capture);
|
|
166
166
|
const prepareElement = await resolvePrepareElement(capture);
|
|
167
167
|
const build = await resolvePdf(buildPdf);
|
|
168
168
|
try {
|
|
169
|
-
downloadFile(await build(toCaptureSources(charts, captureFn, prepareElement), documentTitle), getPdfFilename());
|
|
169
|
+
downloadFile(await build(toCaptureSources(charts, captureFn, prepareElement), documentTitle, extraPages), getPdfFilename());
|
|
170
170
|
}
|
|
171
171
|
finally {
|
|
172
172
|
await restoreChartExportViews(charts);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FC } from "react";
|
|
2
|
+
import { type MigrationDonutChartLegendVariant } from "../charts/MigrationDonutChart.js";
|
|
3
|
+
export interface CpuAndMemoryOverviewProps {
|
|
4
|
+
cpuTierDistribution?: Record<string, number>;
|
|
5
|
+
memoryTierDistribution?: Record<string, number>;
|
|
6
|
+
memoryTotalGB?: number;
|
|
7
|
+
cpuTotalCores?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Memory-tier slice click. The argument is the distribution key.
|
|
10
|
+
* Agent-ui maps it to a VM memory filter; ui-app omits it.
|
|
11
|
+
*/
|
|
12
|
+
onTierSelect?: (tierLabel: string) => void;
|
|
13
|
+
/** Center-title click. Agent-ui opens the VM list with no extra filters. */
|
|
14
|
+
onTitleClick?: () => void;
|
|
15
|
+
/** `"html"` in agent-ui; ui-app passes `"chart"`. */
|
|
16
|
+
legendVariant?: MigrationDonutChartLegendVariant;
|
|
17
|
+
}
|
|
18
|
+
export declare const CpuAndMemoryOverview: FC<CpuAndMemoryOverviewProps>;
|
|
19
|
+
//# sourceMappingURL=CpuAndMemoryOverview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CpuAndMemoryOverview.d.ts","sourceRoot":"","sources":["../../src/report/CpuAndMemoryOverview.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,EAAE,EAAO,MAAM,OAAO,CAAC;AAErC,OAAO,EAGL,KAAK,gCAAgC,EACtC,MAAM,kCAAkC,CAAC;AAQ1C,MAAM,WAAW,yBAAyB;IACxC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,qDAAqD;IACrD,aAAa,CAAC,EAAE,gCAAgC,CAAC;CAClD;AAuFD,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,yBAAyB,CAiJ9D,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { css } from "@emotion/css";
|
|
3
|
+
import { Card, CardBody, CardTitle, Dropdown, DropdownItem, DropdownList, Flex, FlexItem, MenuToggle, } from "@patternfly/react-core";
|
|
4
|
+
import { DataProcessorIcon } from "@patternfly/react-icons";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { MigrationDonutChart, } from "../charts/MigrationDonutChart.js";
|
|
7
|
+
import { CardEmptyState } from "./CardEmptyState.js";
|
|
8
|
+
import { ChartHeaderActions } from "./ChartDownloadButton.js";
|
|
9
|
+
import { ChartExportSurface } from "./ChartExportSurface.js";
|
|
10
|
+
import { chartExportViewsFromLabels } from "./chartExport.js";
|
|
11
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
12
|
+
import { dashboardStyles } from "./dashboardStyles.js";
|
|
13
|
+
const VIEW_MODE_LABELS = {
|
|
14
|
+
memoryTiers: "VM distribution by memory size tier",
|
|
15
|
+
vcpuTiers: "VM distribution by vCPU count tier",
|
|
16
|
+
};
|
|
17
|
+
const VIEW_SUBTITLES = {
|
|
18
|
+
memoryTiers: "Memory size tiers",
|
|
19
|
+
vcpuTiers: "vCPU count tiers",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Categorical order shared by the agent-ui and ui-app CPU/memory donuts.
|
|
23
|
+
*/
|
|
24
|
+
const TIER_COLORS = [
|
|
25
|
+
"#0066cc",
|
|
26
|
+
"#5e40be",
|
|
27
|
+
"#b6a6e9",
|
|
28
|
+
"#73c5c5",
|
|
29
|
+
"#b98412",
|
|
30
|
+
"#28a745",
|
|
31
|
+
"#f0ad4e",
|
|
32
|
+
"#d9534f",
|
|
33
|
+
"#009596",
|
|
34
|
+
"#6a6e73",
|
|
35
|
+
];
|
|
36
|
+
const CHART_ID = "cpu-memory-overview";
|
|
37
|
+
const CHART_TITLE = "CPU & memory";
|
|
38
|
+
const cardSubtitle = css `
|
|
39
|
+
color: var(--pf-t--global--text--color--subtle);
|
|
40
|
+
font-size: 0.85rem;
|
|
41
|
+
`;
|
|
42
|
+
const menuToggleMinWidth = css `
|
|
43
|
+
min-width: 290px;
|
|
44
|
+
`;
|
|
45
|
+
const titleRow = css `
|
|
46
|
+
width: 100%;
|
|
47
|
+
`;
|
|
48
|
+
const isViewMode = (value) => value === "memoryTiers" || value === "vcpuTiers";
|
|
49
|
+
const tierSortKey = (label) => {
|
|
50
|
+
const value = Number.parseInt(label.trim(), 10);
|
|
51
|
+
return Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER;
|
|
52
|
+
};
|
|
53
|
+
const hasUnit = (label, unit) => unit === "GB" ? /gb$/i.test(label) : /cores?$/i.test(label);
|
|
54
|
+
const slicesFromDistribution = (distribution, unit) => Object.entries(distribution ?? {})
|
|
55
|
+
.filter(([, count]) => Number(count) > 0)
|
|
56
|
+
.sort(([left], [right]) => tierSortKey(left) - tierSortKey(right))
|
|
57
|
+
.map(([tier, count]) => {
|
|
58
|
+
const trimmed = tier.trim();
|
|
59
|
+
const name = hasUnit(trimmed, unit) ? trimmed : `${trimmed} ${unit}`;
|
|
60
|
+
const vmCount = Number(count);
|
|
61
|
+
return {
|
|
62
|
+
name,
|
|
63
|
+
count: vmCount,
|
|
64
|
+
countDisplay: `${vmCount} VMs`,
|
|
65
|
+
legendCategory: tier,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
const legendFor = (categories) => {
|
|
69
|
+
const legend = {};
|
|
70
|
+
categories.forEach((category, index) => {
|
|
71
|
+
const color = TIER_COLORS[index % TIER_COLORS.length];
|
|
72
|
+
if (color) {
|
|
73
|
+
legend[category] = color;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return legend;
|
|
77
|
+
};
|
|
78
|
+
export const CpuAndMemoryOverview = ({ cpuTierDistribution, memoryTierDistribution, memoryTotalGB, cpuTotalCores, onTierSelect, onTitleClick, legendVariant = "html", }) => {
|
|
79
|
+
const [viewMode, setViewMode] = useState("memoryTiers");
|
|
80
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
81
|
+
const memorySlices = useMemo(() => slicesFromDistribution(memoryTierDistribution, "GB"), [memoryTierDistribution]);
|
|
82
|
+
const vcpuSlices = useMemo(() => slicesFromDistribution(cpuTierDistribution, "cores"), [cpuTierDistribution]);
|
|
83
|
+
const activeSlices = viewMode === "memoryTiers" ? memorySlices : vcpuSlices;
|
|
84
|
+
const legend = useMemo(() => legendFor(activeSlices.map((slice) => slice.legendCategory)), [activeSlices]);
|
|
85
|
+
const totalVMs = useMemo(() => activeSlices.reduce((sum, slice) => sum + slice.count, 0), [activeSlices]);
|
|
86
|
+
const chartTitle = `${CHART_TITLE} — ${VIEW_MODE_LABELS[viewMode]}`;
|
|
87
|
+
const subTitle = viewMode === "memoryTiers"
|
|
88
|
+
? typeof memoryTotalGB === "number"
|
|
89
|
+
? `${memoryTotalGB.toLocaleString()} GB`
|
|
90
|
+
: undefined
|
|
91
|
+
: typeof cpuTotalCores === "number"
|
|
92
|
+
? `${cpuTotalCores.toLocaleString()} Cores`
|
|
93
|
+
: undefined;
|
|
94
|
+
const emptyTitle = viewMode === "memoryTiers"
|
|
95
|
+
? REPORT_CARD_EMPTY_STATE_TITLES.memory
|
|
96
|
+
: REPORT_CARD_EMPTY_STATE_TITLES.cpu;
|
|
97
|
+
return (_jsx(ChartExportSurface, { id: CHART_ID, title: chartTitle, exportViews: chartExportViewsFromLabels(CHART_TITLE, VIEW_MODE_LABELS), activeExportViewId: viewMode, onExportViewChange: (viewId) => {
|
|
98
|
+
if (isViewMode(viewId)) {
|
|
99
|
+
setViewMode(viewId);
|
|
100
|
+
}
|
|
101
|
+
}, children: _jsxs(Card, { className: dashboardStyles.card, children: [_jsx(CardTitle, { children: _jsxs(Flex, { className: titleRow, justifyContent: { default: "justifyContentSpaceBetween" }, alignItems: { default: "alignItemsCenter" }, children: [_jsx(FlexItem, { children: _jsxs("div", { children: [_jsxs("div", { children: [_jsx(DataProcessorIcon, {}), " ", CHART_TITLE] }), _jsx("div", { className: cardSubtitle, children: VIEW_SUBTITLES[viewMode] })] }) }), _jsx(ChartHeaderActions, { chartId: CHART_ID, title: chartTitle, children: _jsx(Dropdown, { isOpen: isDropdownOpen, onSelect: (_event, value) => {
|
|
102
|
+
if (isViewMode(value)) {
|
|
103
|
+
setViewMode(value);
|
|
104
|
+
}
|
|
105
|
+
setIsDropdownOpen(false);
|
|
106
|
+
}, onOpenChange: setIsDropdownOpen, toggle: (toggleRef) => (_jsx(MenuToggle, { ref: toggleRef, onClick: () => setIsDropdownOpen((open) => !open), isExpanded: isDropdownOpen, className: menuToggleMinWidth, children: VIEW_MODE_LABELS[viewMode] })), children: _jsxs(DropdownList, { children: [_jsx(DropdownItem, { value: "memoryTiers", children: VIEW_MODE_LABELS.memoryTiers }, "memoryTiers"), _jsx(DropdownItem, { value: "vcpuTiers", children: VIEW_MODE_LABELS.vcpuTiers }, "vcpuTiers")] }) }) })] }) }), _jsx(CardBody, { className: dashboardStyles.cardBodyScrollable, children: activeSlices.length === 0 ? (_jsx(CardEmptyState, { title: emptyTitle })) : (_jsx(MigrationDonutChart, { legendVariant: legendVariant, data: activeSlices, legend: legend, height: 300, width: 420, donutThickness: 18, titleFontSize: 34, title: `${totalVMs} VMs`, subTitle: subTitle, subTitleColor: "var(--pf-t--global--text--color--subtle)", legendLabelFormatter: ({ x, countDisplay }) => `${x} (${countDisplay})`, tooltipLabelFormatter: ({ datum, percent }) => `${datum.countDisplay}\n${percent.toFixed(1)}%`, itemsPerRow: legendVariant === "chart"
|
|
107
|
+
? Math.ceil(activeSlices.length / 2)
|
|
108
|
+
: undefined, labelFontSize: legendVariant === "chart" ? 18 : undefined, marginLeft: legendVariant === "chart" ? "52%" : undefined, onItemClick: viewMode === "memoryTiers" && onTierSelect
|
|
109
|
+
? (item) => onTierSelect(item.legendCategory)
|
|
110
|
+
: undefined, onTitleClick: onTitleClick })) })] }) }));
|
|
111
|
+
};
|
|
112
|
+
CpuAndMemoryOverview.displayName = "CpuAndMemoryOverview";
|
|
@@ -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";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"captureChartElement.d.ts","sourceRoot":"","sources":["../../src/report/captureChartElement.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"captureChartElement.d.ts","sourceRoot":"","sources":["../../src/report/captureChartElement.ts"],"names":[],"mappings":"AAsCA;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI,CAqD3E;AAuQD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,iBAAiB,CAAC,CAoC5B"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { t_color_gray_20, t_color_white, t_global_text_color_regular, } from "@patternfly/react-tokens";
|
|
1
2
|
import html2canvas from "html2canvas-pro";
|
|
2
|
-
import { CHART_EXPORT_CAPTURING_ATTR, CHART_EXPORT_SCROLL_ATTR, shouldIgnoreChartExportElement, } from "./chartExport.js";
|
|
3
|
+
import { applyLightThemeToDocument, CHART_EXPORT_CAPTURING_ATTR, CHART_EXPORT_SCROLL_ATTR, shouldIgnoreChartExportElement, } from "./chartExport.js";
|
|
3
4
|
/** CSS-pixel scale; 2x made zip-all of ~15 live cards too slow. */
|
|
4
5
|
const CHART_CAPTURE_SCALE = 1;
|
|
5
6
|
const HIDDEN_LAYOUT_STYLE_KEYS = [
|
|
@@ -71,6 +72,10 @@ export function revealHiddenChartAncestors(element) {
|
|
|
71
72
|
const PADDED_CAPTURE_SELECTOR = ".pf-v6-c-card__title, .pf-v6-c-card__header, .pf-v6-c-card__body";
|
|
72
73
|
const CARD_CAPTURE_SELECTOR = ".pf-v6-c-card";
|
|
73
74
|
const TRANSPARENT_FILLS = new Set(["", "transparent", "rgba(0, 0, 0, 0)"]);
|
|
75
|
+
/** PatternFly light-theme chrome. html2canvas inlines live computed SVG styles. */
|
|
76
|
+
const LIGHT_EXPORT_BACKGROUND = t_color_white.value;
|
|
77
|
+
const LIGHT_EXPORT_BORDER = t_color_gray_20.value;
|
|
78
|
+
const LIGHT_EXPORT_TEXT = t_global_text_color_regular.value;
|
|
74
79
|
function queryCards(root) {
|
|
75
80
|
const cards = Array.from(root.querySelectorAll(CARD_CAPTURE_SELECTOR));
|
|
76
81
|
if (root.matches(CARD_CAPTURE_SELECTOR)) {
|
|
@@ -112,16 +117,29 @@ function readCardBorder(source) {
|
|
|
112
117
|
}
|
|
113
118
|
return { width, style, color };
|
|
114
119
|
}
|
|
115
|
-
function applyCardChrome(source, dest) {
|
|
120
|
+
function applyCardChrome(source, dest, colors) {
|
|
116
121
|
const computed = window.getComputedStyle(source);
|
|
117
122
|
dest.style.borderRadius = computed.borderRadius;
|
|
118
|
-
dest.style.backgroundColor = resolvedFill(computed.backgroundColor);
|
|
119
123
|
dest.style.overflow = "hidden";
|
|
124
|
+
dest.style.backgroundColor =
|
|
125
|
+
colors === "light"
|
|
126
|
+
? LIGHT_EXPORT_BACKGROUND
|
|
127
|
+
: resolvedFill(computed.backgroundColor);
|
|
120
128
|
const border = readCardBorder(source);
|
|
121
129
|
if (border) {
|
|
122
130
|
dest.style.borderWidth = border.width;
|
|
123
131
|
dest.style.borderStyle = border.style;
|
|
124
|
-
dest.style.borderColor =
|
|
132
|
+
dest.style.borderColor =
|
|
133
|
+
colors === "light" ? LIGHT_EXPORT_BORDER : border.color;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function applyLightThemeSvgText(root) {
|
|
137
|
+
for (const node of root.querySelectorAll("text, tspan")) {
|
|
138
|
+
if (!(node instanceof SVGElement)) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
node.style.fill = LIGHT_EXPORT_TEXT;
|
|
142
|
+
node.style.color = LIGHT_EXPORT_TEXT;
|
|
125
143
|
}
|
|
126
144
|
}
|
|
127
145
|
function hideCardBeforePseudo() {
|
|
@@ -162,7 +180,7 @@ function copyCardChrome(node) {
|
|
|
162
180
|
height: node.style.height,
|
|
163
181
|
maxHeight: node.style.maxHeight,
|
|
164
182
|
};
|
|
165
|
-
applyCardChrome(node, node);
|
|
183
|
+
applyCardChrome(node, node, "computed");
|
|
166
184
|
node.style.minHeight = "0px";
|
|
167
185
|
node.style.height = "auto";
|
|
168
186
|
node.style.maxHeight = "none";
|
|
@@ -174,7 +192,7 @@ function applyComputedStylesToClone(sourceRoot, clonedRoot) {
|
|
|
174
192
|
sourceCards.forEach((source, index) => {
|
|
175
193
|
const dest = cloneCards[index];
|
|
176
194
|
if (dest) {
|
|
177
|
-
applyCardChrome(source, dest);
|
|
195
|
+
applyCardChrome(source, dest, "light");
|
|
178
196
|
}
|
|
179
197
|
});
|
|
180
198
|
const sources = sourceRoot.querySelectorAll(PADDED_CAPTURE_SELECTOR);
|
|
@@ -264,14 +282,16 @@ export async function captureChartElement(element) {
|
|
|
264
282
|
}
|
|
265
283
|
return html2canvas(element, {
|
|
266
284
|
useCORS: true,
|
|
267
|
-
backgroundColor:
|
|
285
|
+
backgroundColor: LIGHT_EXPORT_BACKGROUND,
|
|
268
286
|
logging: false,
|
|
269
287
|
scale: CHART_CAPTURE_SCALE,
|
|
270
288
|
imageTimeout: 0,
|
|
271
289
|
ignoreElements: (node) => node instanceof Element &&
|
|
272
290
|
shouldIgnoreChartExportElement(node, element),
|
|
273
|
-
onclone: (
|
|
291
|
+
onclone: (clonedDoc, clonedElement) => {
|
|
292
|
+
applyLightThemeToDocument(clonedDoc);
|
|
274
293
|
applyComputedStylesToClone(element, clonedElement);
|
|
294
|
+
applyLightThemeSvgText(clonedElement);
|
|
275
295
|
},
|
|
276
296
|
});
|
|
277
297
|
}
|
|
@@ -38,8 +38,31 @@ export type ChartCaptureSource = {
|
|
|
38
38
|
filename: string;
|
|
39
39
|
capture: () => Promise<HTMLCanvasElement>;
|
|
40
40
|
};
|
|
41
|
+
/** One label/value row rendered on a {@link PdfTextPage}. */
|
|
42
|
+
export type PdfTextPageItem = {
|
|
43
|
+
label: string;
|
|
44
|
+
value: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* A native-text PDF page appended after the captured chart images.
|
|
48
|
+
*
|
|
49
|
+
* Used for content that is not a chart/card (e.g. calculated recommendations)
|
|
50
|
+
* but still needs to be part of the exported PDF.
|
|
51
|
+
*/
|
|
52
|
+
export type PdfTextPage = {
|
|
53
|
+
title: string;
|
|
54
|
+
items: PdfTextPageItem[];
|
|
55
|
+
footer?: string;
|
|
56
|
+
};
|
|
41
57
|
export declare function releaseCanvas(canvas: HTMLCanvasElement): void;
|
|
42
58
|
export declare function waitForChartExportPaint(): Promise<void>;
|
|
59
|
+
/** PatternFly applies dark tokens when this class is on `:root`. */
|
|
60
|
+
export declare const PATTERNFLY_DARK_THEME_CLASSES: readonly ["pf-v6-theme-dark", "pf-v5-theme-dark"];
|
|
61
|
+
/**
|
|
62
|
+
* Force PatternFly light tokens on a document (the html2canvas clone).
|
|
63
|
+
* Does not touch the live page.
|
|
64
|
+
*/
|
|
65
|
+
export declare function applyLightThemeToDocument(root: Document): void;
|
|
43
66
|
export declare function chartExportViewsFromLabels(titlePrefix: string, labels: Record<string, string>): ChartExportView[];
|
|
44
67
|
export declare function toBulkChartCaptureSources(charts: RegisteredChart[], capture: (element: HTMLElement) => Promise<HTMLCanvasElement>, prepareElement?: (element: HTMLElement) => () => void): ChartCaptureSource[];
|
|
45
68
|
export declare function restoreChartExportViews(charts: RegisteredChart[]): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chartExport.d.ts","sourceRoot":"","sources":["../../src/report/chartExport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,eAAO,MAAM,sBAAsB,2BAA2B,CAAC;AAC/D,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AACnE,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AAElE,eAAO,MAAM,oBAAoB;;CAEvB,CAAC;AAEX,eAAO,MAAM,sBAAsB;;CAEzB,CAAC;AAEX,eAAO,MAAM,oBAAoB,EAAE,aAGlC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC3C,CAAC;AAEF,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAG7D;AAED,wBAAgB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CASvD;AAED,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,eAAe,EAAE,CAKnB;AAqBD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,eAAe,EAAE,EACzB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,iBAAiB,CAAC,EAC7D,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,IAAI,GACpD,kBAAkB,EAAE,CAyBtB;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,eAAe,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,eAAe,EAAE,GACxB,eAAe,EAAE,CAcnB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAcrE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAU,GAAG,MAAM,CAEpE;AAWD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,OAAO,EACb,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,GAC3B,OAAO,CAeT;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAS/D"}
|
|
1
|
+
{"version":3,"file":"chartExport.d.ts","sourceRoot":"","sources":["../../src/report/chartExport.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,6 +25,26 @@ export function waitForChartExportPaint() {
|
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
+
/** PatternFly applies dark tokens when this class is on `:root`. */
|
|
29
|
+
export const PATTERNFLY_DARK_THEME_CLASSES = [
|
|
30
|
+
"pf-v6-theme-dark",
|
|
31
|
+
"pf-v5-theme-dark",
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Force PatternFly light tokens on a document (the html2canvas clone).
|
|
35
|
+
* Does not touch the live page.
|
|
36
|
+
*/
|
|
37
|
+
export function applyLightThemeToDocument(root) {
|
|
38
|
+
for (const node of [root.documentElement, root.body]) {
|
|
39
|
+
if (!node) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
for (const className of PATTERNFLY_DARK_THEME_CLASSES) {
|
|
43
|
+
node.classList.remove(className);
|
|
44
|
+
}
|
|
45
|
+
node.style.colorScheme = "light";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
28
48
|
export function chartExportViewsFromLabels(titlePrefix, labels) {
|
|
29
49
|
return Object.entries(labels).map(([id, label]) => ({
|
|
30
50
|
id,
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { type RefCallback } from "react";
|
|
2
|
-
import { type ChartExportMeta, type ChartExportView, type RegisteredChart } from "./chartExport.js";
|
|
2
|
+
import { type ChartExportMeta, type ChartExportView, type PdfTextPage, type RegisteredChart } from "./chartExport.js";
|
|
3
3
|
export type ChartExportFormat = "pdf" | "png" | "html";
|
|
4
4
|
export type ChartExportApi = {
|
|
5
5
|
downloadChart: (id: string) => Promise<void>;
|
|
6
6
|
downloadAll: () => Promise<void>;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* @param extraPages - Native-text pages appended after the captured chart
|
|
9
|
+
* images (e.g. calculated recommendations that aren't rendered as charts).
|
|
10
|
+
*/
|
|
11
|
+
downloadPdf: (documentTitle: string, extraPages?: PdfTextPage[]) => Promise<void>;
|
|
8
12
|
downloadHtml: (documentTitle: string) => Promise<void>;
|
|
9
13
|
downloadingChartId: string | null;
|
|
10
14
|
exportingFormat: ChartExportFormat | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chartExportContext.d.ts","sourceRoot":"","sources":["../../src/report/chartExportContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAIjB,MAAM,OAAO,CAAC;AACf,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"chartExportContext.d.ts","sourceRoot":"","sources":["../../src/report/chartExportContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAIjB,MAAM,OAAO,CAAC;AACf,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EAEpB,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC;;;OAGG;IACH,WAAW,EAAE,CACX,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,WAAW,EAAE,KACvB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,YAAY,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,IAAI,CAAC;CAClD,CAAC;AAEF,eAAO,MAAM,0BAA0B,qDACU,CAAC;AAClD,eAAO,MAAM,qBAAqB,gDAA6C,CAAC;AAEhF,wBAAgB,cAAc,IAAI,cAAc,GAAG,IAAI,CAEtD;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,EAAE,EACF,KAAK,EACL,QAAQ,EACR,WAAW,EACX,kBAAkB,EAClB,aAAa,GACd,EAAE,eAAe,GAAG;IACnB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,GAAG,WAAW,CAAC,WAAW,CAAC,CA8B3B"}
|
|
@@ -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"}
|