@openshift-migration-advisor/shared-components 0.24.0 → 0.25.0-0ad1e70d1fa5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +31 -0
  2. package/dist/charts/index.d.ts +1 -1
  3. package/dist/charts/index.d.ts.map +1 -1
  4. package/dist/forms/RadioButtonFormGroup.d.ts.map +1 -1
  5. package/dist/forms/RadioButtonFormGroup.js +2 -2
  6. package/dist/report/ChartDownloadButton.d.ts +11 -0
  7. package/dist/report/ChartDownloadButton.d.ts.map +1 -0
  8. package/dist/report/ChartDownloadButton.js +24 -0
  9. package/dist/report/ChartExportProvider.d.ts +22 -0
  10. package/dist/report/ChartExportProvider.d.ts.map +1 -0
  11. package/dist/report/ChartExportProvider.js +222 -0
  12. package/dist/report/ChartExportSurface.d.ts +12 -0
  13. package/dist/report/ChartExportSurface.d.ts.map +1 -0
  14. package/dist/report/ChartExportSurface.js +32 -0
  15. package/dist/report/FeatureStatusBadge.d.ts +7 -0
  16. package/dist/report/FeatureStatusBadge.d.ts.map +1 -0
  17. package/dist/report/FeatureStatusBadge.js +13 -0
  18. package/dist/report/HostPowerStates.d.ts +8 -0
  19. package/dist/report/HostPowerStates.d.ts.map +1 -0
  20. package/dist/report/HostPowerStates.js +11 -0
  21. package/dist/report/InfrastructureSummary.d.ts +7 -0
  22. package/dist/report/InfrastructureSummary.d.ts.map +1 -0
  23. package/dist/report/InfrastructureSummary.js +9 -0
  24. package/dist/report/OSDistribution.d.ts +0 -2
  25. package/dist/report/OSDistribution.d.ts.map +1 -1
  26. package/dist/report/OSDistribution.js +10 -15
  27. package/dist/report/OsSupportTiersHelpPopover.d.ts.map +1 -1
  28. package/dist/report/OsSupportTiersHelpPopover.js +2 -31
  29. package/dist/report/OsUpgradeNotice.d.ts.map +1 -1
  30. package/dist/report/OsUpgradeNotice.js +1 -5
  31. package/dist/report/OsUpgradeRecommendationPopover.d.ts +0 -1
  32. package/dist/report/OsUpgradeRecommendationPopover.d.ts.map +1 -1
  33. package/dist/report/OsUpgradeRecommendationPopover.js +4 -3
  34. package/dist/report/PowerStateCard.d.ts +16 -0
  35. package/dist/report/PowerStateCard.d.ts.map +1 -0
  36. package/dist/report/PowerStateCard.js +9 -0
  37. package/dist/report/ReportExportMenu.d.ts +19 -0
  38. package/dist/report/ReportExportMenu.d.ts.map +1 -0
  39. package/dist/report/ReportExportMenu.js +31 -0
  40. package/dist/report/SupportTierBadge.d.ts +0 -1
  41. package/dist/report/SupportTierBadge.d.ts.map +1 -1
  42. package/dist/report/SupportTierBadge.js +1 -19
  43. package/dist/report/VCenterClusterDetails.d.ts +9 -0
  44. package/dist/report/VCenterClusterDetails.d.ts.map +1 -0
  45. package/dist/report/VCenterClusterDetails.js +31 -0
  46. package/dist/report/VmPowerStates.d.ts +8 -0
  47. package/dist/report/VmPowerStates.d.ts.map +1 -0
  48. package/dist/report/VmPowerStates.js +11 -0
  49. package/dist/report/captureChartElement.d.ts +8 -0
  50. package/dist/report/captureChartElement.d.ts.map +1 -0
  51. package/dist/report/captureChartElement.js +284 -0
  52. package/dist/report/chartExport.d.ts +57 -0
  53. package/dist/report/chartExport.d.ts.map +1 -0
  54. package/dist/report/chartExport.js +162 -0
  55. package/dist/report/chartExportContext.d.ts +28 -0
  56. package/dist/report/chartExportContext.d.ts.map +1 -0
  57. package/dist/report/chartExportContext.js +36 -0
  58. package/dist/report/chartExportFilenames.d.ts +4 -0
  59. package/dist/report/chartExportFilenames.d.ts.map +1 -0
  60. package/dist/report/chartExportFilenames.js +16 -0
  61. package/dist/report/constants.d.ts +2 -0
  62. package/dist/report/constants.d.ts.map +1 -1
  63. package/dist/report/constants.js +2 -0
  64. package/dist/report/dashboardStyles.d.ts +0 -4
  65. package/dist/report/dashboardStyles.d.ts.map +1 -1
  66. package/dist/report/dashboardStyles.js +0 -19
  67. package/dist/report/htmlExport.d.ts +9 -0
  68. package/dist/report/htmlExport.d.ts.map +1 -0
  69. package/dist/report/htmlExport.js +65 -0
  70. package/dist/report/index.d.ts +23 -0
  71. package/dist/report/index.d.ts.map +1 -1
  72. package/dist/report/index.js +21 -0
  73. package/dist/report/infrastructureSummaryModel.d.ts +82 -0
  74. package/dist/report/infrastructureSummaryModel.d.ts.map +1 -0
  75. package/dist/report/infrastructureSummaryModel.js +139 -0
  76. package/dist/report/pdfExport.d.ts +3 -0
  77. package/dist/report/pdfExport.d.ts.map +1 -0
  78. package/dist/report/pdfExport.js +82 -0
  79. package/dist/report/pdfPage.d.ts +15 -0
  80. package/dist/report/pdfPage.d.ts.map +1 -0
  81. package/dist/report/pdfPage.js +44 -0
  82. package/dist/report/powerStates.d.ts +11 -0
  83. package/dist/report/powerStates.d.ts.map +1 -0
  84. package/dist/report/powerStates.js +113 -0
  85. package/dist/report/reportExportOptions.d.ts +17 -0
  86. package/dist/report/reportExportOptions.d.ts.map +1 -0
  87. package/dist/report/reportExportOptions.js +31 -0
  88. package/dist/report/zipChartPngs.d.ts +3 -0
  89. package/dist/report/zipChartPngs.d.ts.map +1 -0
  90. package/dist/report/zipChartPngs.js +8 -0
  91. package/package.json +7 -2
  92. package/src/charts/index.ts +1 -0
  93. package/src/forms/RadioButtonFormGroup.tsx +10 -3
  94. package/src/report/ChartDownloadButton.tsx +59 -0
  95. package/src/report/ChartExportProvider.tsx +384 -0
  96. package/src/report/ChartExportSurface.tsx +60 -0
  97. package/src/report/FeatureStatusBadge.tsx +30 -0
  98. package/src/report/HostPowerStates.tsx +38 -0
  99. package/src/report/InfrastructureSummary.tsx +73 -0
  100. package/src/report/OSDistribution.tsx +96 -99
  101. package/src/report/OsSupportTiersHelpPopover.tsx +32 -62
  102. package/src/report/OsUpgradeNotice.tsx +1 -7
  103. package/src/report/OsUpgradeRecommendationPopover.tsx +3 -4
  104. package/src/report/PowerStateCard.tsx +86 -0
  105. package/src/report/ReportExportMenu.tsx +119 -0
  106. package/src/report/SupportTierBadge.tsx +12 -38
  107. package/src/report/VCenterClusterDetails.tsx +233 -0
  108. package/src/report/VmPowerStates.tsx +38 -0
  109. package/src/report/__tests__/ChartExportProvider.test.tsx +474 -0
  110. package/src/report/__tests__/HostPowerStates.test.tsx +47 -0
  111. package/src/report/__tests__/InfrastructureSummary.test.tsx +126 -0
  112. package/src/report/__tests__/OSDistribution.test.tsx +10 -11
  113. package/src/report/__tests__/captureChartElement.test.ts +132 -0
  114. package/src/report/__tests__/chartExportPackagers.test.ts +224 -0
  115. package/src/report/__tests__/infrastructureSummaryModel.test.ts +224 -0
  116. package/src/report/__tests__/powerStates.test.ts +70 -0
  117. package/src/report/captureChartElement.ts +368 -0
  118. package/src/report/chartExport.ts +237 -0
  119. package/src/report/chartExportContext.tsx +84 -0
  120. package/src/report/chartExportFilenames.ts +21 -0
  121. package/src/report/constants.ts +2 -0
  122. package/src/report/dashboardStyles.ts +0 -23
  123. package/src/report/htmlExport.ts +83 -0
  124. package/src/report/index.ts +128 -0
  125. package/src/report/infrastructureSummaryModel.ts +281 -0
  126. package/src/report/pdfExport.ts +154 -0
  127. package/src/report/pdfPage.ts +83 -0
  128. package/src/report/powerStates.ts +169 -0
  129. package/src/report/reportExportOptions.ts +53 -0
  130. package/src/report/zipChartPngs.ts +10 -0
@@ -0,0 +1,384 @@
1
+ import {
2
+ type FC,
3
+ type ReactNode,
4
+ useCallback,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from "react";
9
+ import {
10
+ type ChartCaptureSource,
11
+ type ChartExportFile,
12
+ canvasToBlob,
13
+ downloadBlob,
14
+ type RegisteredChart,
15
+ releaseCanvas,
16
+ restoreChartExportViews,
17
+ sortRegisteredChartsByDocumentOrder,
18
+ toBulkChartCaptureSources,
19
+ } from "./chartExport.js";
20
+ import {
21
+ type ChartExportApi,
22
+ ChartExportApiContext,
23
+ type ChartExportFormat,
24
+ type ChartExportRegistry,
25
+ ChartExportRegistryContext,
26
+ } from "./chartExportContext.js";
27
+ import { getChartExportFilename } from "./chartExportFilenames.js";
28
+
29
+ export type CaptureChartElement = (
30
+ element: HTMLElement,
31
+ ) => Promise<HTMLCanvasElement>;
32
+ export type ZipChartFiles = (files: ChartExportFile[]) => Promise<Blob>;
33
+ export type EncodeChartPng = (canvas: HTMLCanvasElement) => Promise<Blob>;
34
+ export type BuildChartDocument = (
35
+ charts: ChartCaptureSource[],
36
+ documentTitle: string,
37
+ ) => Promise<Blob>;
38
+
39
+ export type {
40
+ ChartExportApi,
41
+ ChartExportFormat,
42
+ } from "./chartExportContext.js";
43
+ export { useChartExport, useRegisterChart } from "./chartExportContext.js";
44
+
45
+ export interface ChartExportProviderProps {
46
+ children: ReactNode;
47
+ capture?: CaptureChartElement;
48
+ zipFiles?: ZipChartFiles;
49
+ encodePng?: EncodeChartPng;
50
+ buildPdf?: BuildChartDocument;
51
+ buildHtml?: BuildChartDocument;
52
+ downloadFile?: (blob: Blob, filename: string) => void;
53
+ getZipFilename?: () => string;
54
+ getPdfFilename?: () => string;
55
+ getHtmlFilename?: () => string;
56
+ }
57
+
58
+ const EXPORT_LOADING_LABELS: Record<ChartExportFormat, string> = {
59
+ pdf: "Generating PDF...",
60
+ png: "Generating PNG...",
61
+ html: "Generating HTML...",
62
+ };
63
+
64
+ const defaultZipFilename = (): string => getChartExportFilename("zip");
65
+ const defaultPdfFilename = (): string => getChartExportFilename("pdf");
66
+ const defaultHtmlFilename = (): string => getChartExportFilename("html");
67
+
68
+ async function resolveCapture(
69
+ override?: CaptureChartElement,
70
+ ): Promise<CaptureChartElement> {
71
+ if (override) {
72
+ return override;
73
+ }
74
+ const { captureChartElement } = await import("./captureChartElement.js");
75
+ return captureChartElement;
76
+ }
77
+
78
+ async function resolvePrepareElement(
79
+ overrideCapture?: CaptureChartElement,
80
+ ): Promise<((element: HTMLElement) => () => void) | undefined> {
81
+ if (overrideCapture) {
82
+ return undefined;
83
+ }
84
+ const { revealHiddenChartAncestors } = await import(
85
+ "./captureChartElement.js"
86
+ );
87
+ return revealHiddenChartAncestors;
88
+ }
89
+
90
+ async function resolveZip(override?: ZipChartFiles): Promise<ZipChartFiles> {
91
+ if (override) {
92
+ return override;
93
+ }
94
+ const { zipChartPngs } = await import("./zipChartPngs.js");
95
+ return zipChartPngs;
96
+ }
97
+
98
+ async function resolvePdf(
99
+ override?: BuildChartDocument,
100
+ ): Promise<BuildChartDocument> {
101
+ if (override) {
102
+ return override;
103
+ }
104
+ const { buildPdfFromCharts } = await import("./pdfExport.js");
105
+ return buildPdfFromCharts;
106
+ }
107
+
108
+ async function resolveHtml(
109
+ override?: BuildChartDocument,
110
+ ): Promise<BuildChartDocument> {
111
+ if (override) {
112
+ return override;
113
+ }
114
+ const { buildHtmlFromCharts } = await import("./htmlExport.js");
115
+ return buildHtmlFromCharts;
116
+ }
117
+
118
+ function toCaptureSources(
119
+ charts: RegisteredChart[],
120
+ capture: CaptureChartElement,
121
+ prepareElement?: (element: HTMLElement) => () => void,
122
+ ): ChartCaptureSource[] {
123
+ return toBulkChartCaptureSources(charts, capture, prepareElement);
124
+ }
125
+
126
+ export const ChartExportProvider: FC<ChartExportProviderProps> = ({
127
+ children,
128
+ capture,
129
+ zipFiles,
130
+ encodePng = canvasToBlob,
131
+ buildPdf,
132
+ buildHtml,
133
+ downloadFile = downloadBlob,
134
+ getZipFilename = defaultZipFilename,
135
+ getPdfFilename = defaultPdfFilename,
136
+ getHtmlFilename = defaultHtmlFilename,
137
+ }) => {
138
+ const chartsRef = useRef(new Map<string, RegisteredChart>());
139
+ const isBusyRef = useRef(false);
140
+ const [downloadingChartId, setDownloadingChartId] = useState<string | null>(
141
+ null,
142
+ );
143
+ const [exportingFormat, setExportingFormat] =
144
+ useState<ChartExportFormat | null>(null);
145
+ const [exportError, setExportError] = useState<string | null>(null);
146
+
147
+ const registry = useMemo<ChartExportRegistry>(
148
+ () => ({
149
+ register(chart) {
150
+ const existing = chartsRef.current.get(chart.id);
151
+ if (
152
+ existing &&
153
+ existing.element !== chart.element &&
154
+ existing.element.isConnected
155
+ ) {
156
+ console.warn(
157
+ `Duplicate chart export id "${chart.id}". The previous chart will not be exported.`,
158
+ );
159
+ }
160
+ chartsRef.current.set(chart.id, chart);
161
+ return () => {
162
+ const current = chartsRef.current.get(chart.id);
163
+ if (current?.element === chart.element) {
164
+ chartsRef.current.delete(chart.id);
165
+ }
166
+ };
167
+ },
168
+ }),
169
+ [],
170
+ );
171
+
172
+ const listCharts = useCallback((): RegisteredChart[] => {
173
+ return sortRegisteredChartsByDocumentOrder(
174
+ Array.from(chartsRef.current.values()).filter(
175
+ (chart) => chart.element.isConnected,
176
+ ),
177
+ );
178
+ }, []);
179
+
180
+ const runCapture = useCallback(
181
+ async (
182
+ work: () => Promise<void>,
183
+ chartId: string | null,
184
+ format: ChartExportFormat,
185
+ ) => {
186
+ if (isBusyRef.current) {
187
+ return;
188
+ }
189
+
190
+ isBusyRef.current = true;
191
+ setExportError(null);
192
+ if (chartId) {
193
+ setDownloadingChartId(chartId);
194
+ } else {
195
+ setExportingFormat(format);
196
+ }
197
+
198
+ try {
199
+ await work();
200
+ } catch (error) {
201
+ console.error(`Error exporting ${format}:`, error);
202
+ setExportError(
203
+ `Failed to export ${format.toUpperCase()}. Please try again.`,
204
+ );
205
+ } finally {
206
+ isBusyRef.current = false;
207
+ setDownloadingChartId(null);
208
+ setExportingFormat(null);
209
+ }
210
+ },
211
+ [],
212
+ );
213
+
214
+ const downloadChart = useCallback(
215
+ async (id: string) => {
216
+ await runCapture(
217
+ async () => {
218
+ const chart = chartsRef.current.get(id);
219
+ if (!chart?.element.isConnected) {
220
+ throw new Error("The chart is not ready to export yet.");
221
+ }
222
+ const captureFn = await resolveCapture(capture);
223
+ const canvas = await captureFn(chart.element);
224
+ try {
225
+ downloadFile(await encodePng(canvas), chart.filename);
226
+ } finally {
227
+ releaseCanvas(canvas);
228
+ }
229
+ },
230
+ id,
231
+ "png",
232
+ );
233
+ },
234
+ [capture, downloadFile, encodePng, runCapture],
235
+ );
236
+
237
+ const downloadAll = useCallback(async () => {
238
+ await runCapture(
239
+ async () => {
240
+ const charts = listCharts();
241
+ if (charts.length === 0) {
242
+ throw new Error("The report is not ready to export yet.");
243
+ }
244
+ const captureFn = await resolveCapture(capture);
245
+ const prepareElement = await resolvePrepareElement(capture);
246
+ const zipFn = await resolveZip(zipFiles);
247
+ const files: ChartExportFile[] = [];
248
+ const sources = toBulkChartCaptureSources(
249
+ charts,
250
+ captureFn,
251
+ prepareElement,
252
+ );
253
+ try {
254
+ for (const [index, source] of sources.entries()) {
255
+ const canvas = await source.capture();
256
+ try {
257
+ files.push({
258
+ filename: `${String(index + 1).padStart(2, "0")}-${source.filename}`,
259
+ blob: await encodePng(canvas),
260
+ });
261
+ } finally {
262
+ releaseCanvas(canvas);
263
+ }
264
+ }
265
+ } finally {
266
+ await restoreChartExportViews(charts);
267
+ }
268
+ downloadFile(await zipFn(files), getZipFilename());
269
+ },
270
+ null,
271
+ "png",
272
+ );
273
+ }, [
274
+ capture,
275
+ downloadFile,
276
+ encodePng,
277
+ getZipFilename,
278
+ listCharts,
279
+ runCapture,
280
+ zipFiles,
281
+ ]);
282
+
283
+ const downloadPdf = useCallback(
284
+ async (documentTitle: string) => {
285
+ await runCapture(
286
+ async () => {
287
+ const charts = listCharts();
288
+ if (charts.length === 0) {
289
+ throw new Error("The report is not ready to export yet.");
290
+ }
291
+ const captureFn = await resolveCapture(capture);
292
+ const prepareElement = await resolvePrepareElement(capture);
293
+ const build = await resolvePdf(buildPdf);
294
+ try {
295
+ downloadFile(
296
+ await build(
297
+ toCaptureSources(charts, captureFn, prepareElement),
298
+ documentTitle,
299
+ ),
300
+ getPdfFilename(),
301
+ );
302
+ } finally {
303
+ await restoreChartExportViews(charts);
304
+ }
305
+ },
306
+ null,
307
+ "pdf",
308
+ );
309
+ },
310
+ [buildPdf, capture, downloadFile, getPdfFilename, listCharts, runCapture],
311
+ );
312
+
313
+ const downloadHtml = useCallback(
314
+ async (documentTitle: string) => {
315
+ await runCapture(
316
+ async () => {
317
+ const charts = listCharts();
318
+ if (charts.length === 0) {
319
+ throw new Error("The report is not ready to export yet.");
320
+ }
321
+ const captureFn = await resolveCapture(capture);
322
+ const prepareElement = await resolvePrepareElement(capture);
323
+ const build = await resolveHtml(buildHtml);
324
+ try {
325
+ downloadFile(
326
+ await build(
327
+ toCaptureSources(charts, captureFn, prepareElement),
328
+ documentTitle,
329
+ ),
330
+ getHtmlFilename(),
331
+ );
332
+ } finally {
333
+ await restoreChartExportViews(charts);
334
+ }
335
+ },
336
+ null,
337
+ "html",
338
+ );
339
+ },
340
+ [buildHtml, capture, downloadFile, getHtmlFilename, listCharts, runCapture],
341
+ );
342
+
343
+ const clearExportError = useCallback(() => {
344
+ setExportError(null);
345
+ }, []);
346
+
347
+ const api = useMemo<ChartExportApi>(
348
+ () => ({
349
+ downloadChart,
350
+ downloadAll,
351
+ downloadPdf,
352
+ downloadHtml,
353
+ downloadingChartId,
354
+ exportingFormat,
355
+ exportLoadingLabel: exportingFormat
356
+ ? EXPORT_LOADING_LABELS[exportingFormat]
357
+ : null,
358
+ isExportingAll: exportingFormat !== null,
359
+ isBusy: downloadingChartId !== null || exportingFormat !== null,
360
+ exportError,
361
+ clearExportError,
362
+ }),
363
+ [
364
+ clearExportError,
365
+ downloadAll,
366
+ downloadChart,
367
+ downloadHtml,
368
+ downloadPdf,
369
+ downloadingChartId,
370
+ exportError,
371
+ exportingFormat,
372
+ ],
373
+ );
374
+
375
+ return (
376
+ <ChartExportRegistryContext.Provider value={registry}>
377
+ <ChartExportApiContext.Provider value={api}>
378
+ {children}
379
+ </ChartExportApiContext.Provider>
380
+ </ChartExportRegistryContext.Provider>
381
+ );
382
+ };
383
+
384
+ ChartExportProvider.displayName = "ChartExportProvider";
@@ -0,0 +1,60 @@
1
+ import { type FC, type ReactNode, useCallback, useRef } from "react";
2
+ import {
3
+ type ChartExportView,
4
+ chartExportRootStyle,
5
+ waitForChartExportPaint,
6
+ } from "./chartExport.js";
7
+ import { useRegisterChart } from "./chartExportContext.js";
8
+
9
+ export const ChartExportSurface: FC<{
10
+ id: string;
11
+ title: string;
12
+ filename?: string;
13
+ exportViews?: ChartExportView[];
14
+ activeExportViewId?: string;
15
+ onExportViewChange?: (viewId: string) => void;
16
+ children: ReactNode;
17
+ }> = ({
18
+ id,
19
+ title,
20
+ filename,
21
+ exportViews,
22
+ activeExportViewId,
23
+ onExportViewChange,
24
+ children,
25
+ }) => {
26
+ const activeViewRef = useRef(activeExportViewId);
27
+ activeViewRef.current = activeExportViewId;
28
+ const onExportViewChangeRef = useRef(onExportViewChange);
29
+ onExportViewChangeRef.current = onExportViewChange;
30
+
31
+ const setExportView = useCallback(async (viewId: string) => {
32
+ const change = onExportViewChangeRef.current;
33
+ if (change && activeViewRef.current !== viewId) {
34
+ change(viewId);
35
+ await new Promise<void>((resolve) => {
36
+ window.setTimeout(resolve, 0);
37
+ });
38
+ }
39
+ await waitForChartExportPaint();
40
+ }, []);
41
+
42
+ const ref = useRegisterChart({
43
+ id,
44
+ title,
45
+ filename,
46
+ exportViews,
47
+ activeExportViewId,
48
+ setExportView:
49
+ exportViews && exportViews.length > 1 && onExportViewChange
50
+ ? setExportView
51
+ : undefined,
52
+ });
53
+ return (
54
+ <div ref={ref} id={id} style={chartExportRootStyle}>
55
+ {children}
56
+ </div>
57
+ );
58
+ };
59
+
60
+ ChartExportSurface.displayName = "ChartExportSurface";
@@ -0,0 +1,30 @@
1
+ import { Label } from "@patternfly/react-core";
2
+ import { CheckCircleIcon, TimesCircleIcon } from "@patternfly/react-icons";
3
+ import type { FC } from "react";
4
+ import type { FeatureStatus } from "./infrastructureSummaryModel.js";
5
+
6
+ export interface FeatureStatusBadgeProps {
7
+ status: FeatureStatus;
8
+ }
9
+
10
+ export const FeatureStatusBadge: FC<FeatureStatusBadgeProps> = ({ status }) => {
11
+ if (status === "unknown") {
12
+ return <span>—</span>;
13
+ }
14
+
15
+ if (status === "enabled") {
16
+ return (
17
+ <Label color="green" isCompact icon={<CheckCircleIcon />}>
18
+ Enabled
19
+ </Label>
20
+ );
21
+ }
22
+
23
+ return (
24
+ <Label color="grey" isCompact icon={<TimesCircleIcon />}>
25
+ Disabled
26
+ </Label>
27
+ );
28
+ };
29
+
30
+ FeatureStatusBadge.displayName = "FeatureStatusBadge";
@@ -0,0 +1,38 @@
1
+ import { ServerIcon } from "@patternfly/react-icons";
2
+ import type { FC } from "react";
3
+ import { useMemo } from "react";
4
+ import type { MigrationDonutChartLegendVariant } from "../charts/MigrationDonutChart.js";
5
+ import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
6
+ import { PowerStateCard } from "./PowerStateCard.js";
7
+ import { buildHostPowerStateChart } from "./powerStates.js";
8
+
9
+ export interface HostPowerStatesProps {
10
+ hostPowerStates?: Record<string, number>;
11
+ legendVariant?: MigrationDonutChartLegendVariant;
12
+ }
13
+
14
+ export const HostPowerStates: FC<HostPowerStatesProps> = ({
15
+ hostPowerStates,
16
+ legendVariant,
17
+ }) => {
18
+ const chart = useMemo(
19
+ () => buildHostPowerStateChart(hostPowerStates),
20
+ [hostPowerStates],
21
+ );
22
+
23
+ return (
24
+ <PowerStateCard
25
+ id="esxi-host-power-states"
26
+ title="ESXi host power states"
27
+ icon={<ServerIcon />}
28
+ emptyTitle={REPORT_CARD_EMPTY_STATE_TITLES.hostPowerStates}
29
+ slices={chart.slices}
30
+ legend={chart.legend}
31
+ total={chart.total}
32
+ subTitle="Hosts"
33
+ legendVariant={legendVariant}
34
+ />
35
+ );
36
+ };
37
+
38
+ HostPowerStates.displayName = "HostPowerStates";
@@ -0,0 +1,73 @@
1
+ import {
2
+ Card,
3
+ CardBody,
4
+ CardTitle,
5
+ DescriptionList,
6
+ DescriptionListDescription,
7
+ DescriptionListGroup,
8
+ DescriptionListTerm,
9
+ Flex,
10
+ FlexItem,
11
+ } from "@patternfly/react-core";
12
+ import type { FC } from "react";
13
+ import { ChartHeaderActions } from "./ChartDownloadButton.js";
14
+ import { ChartExportSurface } from "./ChartExportSurface.js";
15
+ import type { InfrastructureSummaryModel } from "./infrastructureSummaryModel.js";
16
+
17
+ export interface InfrastructureSummaryProps {
18
+ summary: InfrastructureSummaryModel;
19
+ }
20
+
21
+ const formatCount = (value: number | undefined): string =>
22
+ typeof value === "number" ? String(value) : "—";
23
+
24
+ const INFRA_CHART_ID = "infrastructure-summary";
25
+ const INFRA_TITLE = "Infrastructure summary";
26
+
27
+ export const InfrastructureSummary: FC<InfrastructureSummaryProps> = ({
28
+ summary,
29
+ }) => (
30
+ <ChartExportSurface id={INFRA_CHART_ID} title={INFRA_TITLE}>
31
+ <Card isFullHeight>
32
+ <CardTitle>
33
+ <Flex
34
+ justifyContent={{ default: "justifyContentSpaceBetween" }}
35
+ alignItems={{ default: "alignItemsCenter" }}
36
+ >
37
+ <FlexItem>{INFRA_TITLE}</FlexItem>
38
+ <ChartHeaderActions chartId={INFRA_CHART_ID} title={INFRA_TITLE} />
39
+ </Flex>
40
+ </CardTitle>
41
+ <CardBody>
42
+ <DescriptionList isAutoFit displaySize="lg">
43
+ <DescriptionListGroup>
44
+ <DescriptionListTerm>VMware version</DescriptionListTerm>
45
+ <DescriptionListDescription>
46
+ {summary.vmwareVersion}
47
+ </DescriptionListDescription>
48
+ </DescriptionListGroup>
49
+ <DescriptionListGroup>
50
+ <DescriptionListTerm>Datacenters</DescriptionListTerm>
51
+ <DescriptionListDescription>
52
+ {formatCount(summary.datacenters)}
53
+ </DescriptionListDescription>
54
+ </DescriptionListGroup>
55
+ <DescriptionListGroup>
56
+ <DescriptionListTerm>vCenters</DescriptionListTerm>
57
+ <DescriptionListDescription>
58
+ {formatCount(summary.vCenters)}
59
+ </DescriptionListDescription>
60
+ </DescriptionListGroup>
61
+ <DescriptionListGroup>
62
+ <DescriptionListTerm>ESXi hosts</DescriptionListTerm>
63
+ <DescriptionListDescription>
64
+ {formatCount(summary.esxiHosts)}
65
+ </DescriptionListDescription>
66
+ </DescriptionListGroup>
67
+ </DescriptionList>
68
+ </CardBody>
69
+ </Card>
70
+ </ChartExportSurface>
71
+ );
72
+
73
+ InfrastructureSummary.displayName = "InfrastructureSummary";