@solidxai/core-ui 0.1.9-beta.18 → 0.1.9-beta.19

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 (177) hide show
  1. package/dist/adapters/auth/handleLogout.d.ts +1 -1
  2. package/dist/adapters/auth/handleLogout.d.ts.map +1 -1
  3. package/dist/adapters/auth/handleLogout.js +20 -32
  4. package/dist/adapters/auth/handleLogout.js.map +1 -1
  5. package/dist/adapters/auth/handleLogout.ts +7 -14
  6. package/dist/components/core/common/SolidAccountSettings/SolidChangePassword.d.ts.map +1 -1
  7. package/dist/components/core/common/SolidAccountSettings/SolidChangePassword.js +1 -10
  8. package/dist/components/core/common/SolidAccountSettings/SolidChangePassword.js.map +1 -1
  9. package/dist/components/core/common/SolidAccountSettings/SolidChangePassword.tsx +1 -8
  10. package/dist/components/core/common/SolidAccountSettings/SolidNotifications.d.ts.map +1 -1
  11. package/dist/components/core/common/SolidAccountSettings/SolidNotifications.js +6 -11
  12. package/dist/components/core/common/SolidAccountSettings/SolidNotifications.js.map +1 -1
  13. package/dist/components/core/common/SolidAccountSettings/SolidNotifications.tsx +5 -31
  14. package/dist/components/core/common/SolidGlobalSearchElement.d.ts.map +1 -1
  15. package/dist/components/core/common/SolidGlobalSearchElement.js +47 -13
  16. package/dist/components/core/common/SolidGlobalSearchElement.js.map +1 -1
  17. package/dist/components/core/common/SolidGlobalSearchElement.tsx +38 -4
  18. package/dist/components/core/extension/solid-core/metadata/MetadataExplorerFormWidget.d.ts +16 -0
  19. package/dist/components/core/extension/solid-core/metadata/MetadataExplorerFormWidget.d.ts.map +1 -0
  20. package/dist/components/core/extension/solid-core/metadata/MetadataExplorerFormWidget.js +31 -0
  21. package/dist/components/core/extension/solid-core/metadata/MetadataExplorerFormWidget.js.map +1 -0
  22. package/dist/components/core/extension/solid-core/metadata/MetadataExplorerFormWidget.tsx +43 -0
  23. package/dist/components/core/extension/solid-core/moduleMetadata/list/ClearModulePackageRuntimeHeaderAction.d.ts +3 -0
  24. package/dist/components/core/extension/solid-core/moduleMetadata/list/ClearModulePackageRuntimeHeaderAction.d.ts.map +1 -0
  25. package/dist/components/core/extension/solid-core/moduleMetadata/list/ClearModulePackageRuntimeHeaderAction.js +141 -0
  26. package/dist/components/core/extension/solid-core/moduleMetadata/list/ClearModulePackageRuntimeHeaderAction.js.map +1 -0
  27. package/dist/components/core/extension/solid-core/moduleMetadata/list/ClearModulePackageRuntimeHeaderAction.tsx +205 -0
  28. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.d.ts +4 -0
  29. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.d.ts.map +1 -0
  30. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.js +112 -0
  31. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.js.map +1 -0
  32. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.tsx +146 -0
  33. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.d.ts +11 -0
  34. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.d.ts.map +1 -0
  35. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.js +22 -0
  36. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.js.map +1 -0
  37. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.tsx +36 -0
  38. package/dist/components/core/extension/solid-core/moduleMetadata/list/moduleMetadataListOnLoad.d.ts +3 -0
  39. package/dist/components/core/extension/solid-core/moduleMetadata/list/moduleMetadataListOnLoad.d.ts.map +1 -0
  40. package/dist/components/core/extension/solid-core/moduleMetadata/list/moduleMetadataListOnLoad.js +133 -0
  41. package/dist/components/core/extension/solid-core/moduleMetadata/list/moduleMetadataListOnLoad.js.map +1 -0
  42. package/dist/components/core/extension/solid-core/moduleMetadata/list/moduleMetadataListOnLoad.ts +76 -0
  43. package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.d.ts +12 -0
  44. package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.d.ts.map +1 -0
  45. package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.js +168 -0
  46. package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.js.map +1 -0
  47. package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.tsx +241 -0
  48. package/dist/components/core/list/SolidEmptyListViewPlaceholder.d.ts.map +1 -1
  49. package/dist/components/core/list/SolidEmptyListViewPlaceholder.js +3 -0
  50. package/dist/components/core/list/SolidEmptyListViewPlaceholder.js.map +1 -1
  51. package/dist/components/core/list/SolidEmptyListViewPlaceholder.tsx +2 -0
  52. package/dist/components/core/list/SolidListView.d.ts.map +1 -1
  53. package/dist/components/core/list/SolidListView.js +74 -68
  54. package/dist/components/core/list/SolidListView.js.map +1 -1
  55. package/dist/components/core/list/SolidListView.tsx +13 -4
  56. package/dist/components/core/list/SolidListViewColumn.d.ts.map +1 -1
  57. package/dist/components/core/list/SolidListViewColumn.js +0 -2
  58. package/dist/components/core/list/SolidListViewColumn.js.map +1 -1
  59. package/dist/components/core/list/SolidListViewColumn.tsx +0 -1
  60. package/dist/components/core/list/SolidListViewConfigure.d.ts.map +1 -1
  61. package/dist/components/core/list/SolidListViewConfigure.js +10 -9
  62. package/dist/components/core/list/SolidListViewConfigure.js.map +1 -1
  63. package/dist/components/core/list/SolidListViewConfigure.tsx +3 -1
  64. package/dist/components/core/list/SolidListViewHeaderButton.d.ts.map +1 -1
  65. package/dist/components/core/list/SolidListViewHeaderButton.js +3 -0
  66. package/dist/components/core/list/SolidListViewHeaderButton.js.map +1 -1
  67. package/dist/components/core/list/SolidListViewHeaderButton.tsx +2 -0
  68. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.d.ts.map +1 -1
  69. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.js +3 -0
  70. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.js.map +1 -1
  71. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.tsx +2 -0
  72. package/dist/components/core/list/SolidListViewRowActionsMenu.d.ts.map +1 -1
  73. package/dist/components/core/list/SolidListViewRowActionsMenu.js +7 -1
  74. package/dist/components/core/list/SolidListViewRowActionsMenu.js.map +1 -1
  75. package/dist/components/core/list/SolidListViewRowActionsMenu.tsx +5 -1
  76. package/dist/components/core/list/columns/SolidBooleanColumn.d.ts.map +1 -1
  77. package/dist/components/core/list/columns/SolidBooleanColumn.js +4 -6
  78. package/dist/components/core/list/columns/SolidBooleanColumn.js.map +1 -1
  79. package/dist/components/core/list/columns/SolidBooleanColumn.tsx +1 -3
  80. package/dist/components/core/list/columns/SolidMediaMultipleColumn.d.ts.map +1 -1
  81. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js +3 -2
  82. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js.map +1 -1
  83. package/dist/components/core/list/columns/SolidMediaMultipleColumn.tsx +3 -2
  84. package/dist/components/core/list/columns/SolidMediaSingleColumn.d.ts.map +1 -1
  85. package/dist/components/core/list/columns/SolidMediaSingleColumn.js +3 -2
  86. package/dist/components/core/list/columns/SolidMediaSingleColumn.js.map +1 -1
  87. package/dist/components/core/list/columns/SolidMediaSingleColumn.tsx +3 -2
  88. package/dist/components/core/model/CreateModel.css +48 -0
  89. package/dist/components/core/model/CreateModel.d.ts +1 -0
  90. package/dist/components/core/model/CreateModel.d.ts.map +1 -1
  91. package/dist/components/core/model/CreateModel.js +69 -104
  92. package/dist/components/core/model/CreateModel.js.map +1 -1
  93. package/dist/components/core/model/CreateModel.tsx +92 -204
  94. package/dist/components/core/model/FieldMetaData.d.ts +1 -1
  95. package/dist/components/core/model/FieldMetaData.d.ts.map +1 -1
  96. package/dist/components/core/model/FieldMetaData.js +6 -22
  97. package/dist/components/core/model/FieldMetaData.js.map +1 -1
  98. package/dist/components/core/model/FieldMetaData.tsx +6 -45
  99. package/dist/components/core/model/FieldMetaDataForm.d.ts.map +1 -1
  100. package/dist/components/core/model/FieldMetaDataForm.js +196 -222
  101. package/dist/components/core/model/FieldMetaDataForm.js.map +1 -1
  102. package/dist/components/core/model/FieldMetaDataForm.tsx +0 -72
  103. package/dist/components/core/model/ModelMetaData.d.ts.map +1 -1
  104. package/dist/components/core/model/ModelMetaData.js +7 -7
  105. package/dist/components/core/model/ModelMetaData.js.map +1 -1
  106. package/dist/components/core/model/ModelMetaData.tsx +7 -0
  107. package/dist/components/core/module/CreateModule.css +44 -0
  108. package/dist/components/core/module/CreateModule.d.ts +1 -0
  109. package/dist/components/core/module/CreateModule.d.ts.map +1 -1
  110. package/dist/components/core/module/CreateModule.js +40 -43
  111. package/dist/components/core/module/CreateModule.js.map +1 -1
  112. package/dist/components/core/module/CreateModule.tsx +243 -247
  113. package/dist/components/core/module/ModuleListViewData.d.ts.map +1 -1
  114. package/dist/components/core/module/ModuleListViewData.js +47 -7
  115. package/dist/components/core/module/ModuleListViewData.js.map +1 -1
  116. package/dist/components/core/module/ModuleListViewData.tsx +58 -0
  117. package/dist/components/core/module/ModuleMetadataExplorer.css +638 -0
  118. package/dist/components/core/module/ModuleMetadataExplorer.d.ts +15 -0
  119. package/dist/components/core/module/ModuleMetadataExplorer.d.ts.map +1 -0
  120. package/dist/components/core/module/ModuleMetadataExplorer.js +666 -0
  121. package/dist/components/core/module/ModuleMetadataExplorer.js.map +1 -0
  122. package/dist/components/core/module/ModuleMetadataExplorer.tsx +1000 -0
  123. package/dist/components/core/module/ModulePackageDialog.css +540 -0
  124. package/dist/components/core/module/ModulePackageDialog.d.ts +16 -0
  125. package/dist/components/core/module/ModulePackageDialog.d.ts.map +1 -0
  126. package/dist/components/core/module/ModulePackageDialog.js +625 -0
  127. package/dist/components/core/module/ModulePackageDialog.js.map +1 -0
  128. package/dist/components/core/module/ModulePackageDialog.tsx +906 -0
  129. package/dist/components/layout/user-profile-menu.d.ts.map +1 -1
  130. package/dist/components/layout/user-profile-menu.js +57 -33
  131. package/dist/components/layout/user-profile-menu.js.map +1 -1
  132. package/dist/components/layout/user-profile-menu.tsx +11 -59
  133. package/dist/helpers/buttonEnvironment.d.ts +5 -0
  134. package/dist/helpers/buttonEnvironment.d.ts.map +1 -0
  135. package/dist/helpers/buttonEnvironment.js +30 -0
  136. package/dist/helpers/buttonEnvironment.js.map +1 -0
  137. package/dist/helpers/buttonEnvironment.ts +35 -0
  138. package/dist/helpers/fileTypeIcon.d.ts +12 -0
  139. package/dist/helpers/fileTypeIcon.d.ts.map +1 -0
  140. package/dist/helpers/fileTypeIcon.js +37 -0
  141. package/dist/helpers/fileTypeIcon.js.map +1 -0
  142. package/dist/helpers/fileTypeIcon.tsx +65 -0
  143. package/dist/helpers/registry.d.ts.map +1 -1
  144. package/dist/helpers/registry.js +10 -0
  145. package/dist/helpers/registry.js.map +1 -1
  146. package/dist/helpers/registry.ts +10 -0
  147. package/dist/helpers/waitForBackendAvailability.d.ts +7 -0
  148. package/dist/helpers/waitForBackendAvailability.d.ts.map +1 -0
  149. package/dist/helpers/waitForBackendAvailability.js +86 -0
  150. package/dist/helpers/waitForBackendAvailability.js.map +1 -0
  151. package/dist/helpers/waitForBackendAvailability.ts +37 -0
  152. package/dist/index.d.ts +2 -1
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +2 -1
  155. package/dist/index.js.map +1 -1
  156. package/dist/index.ts +18 -0
  157. package/dist/redux/api/moduleApi.d.ts +26 -1
  158. package/dist/redux/api/moduleApi.d.ts.map +1 -1
  159. package/dist/redux/api/moduleApi.js +237 -1
  160. package/dist/redux/api/moduleApi.js.map +1 -1
  161. package/dist/redux/api/moduleApi.ts +181 -1
  162. package/dist/redux/store/defaultStoreConfig.d.ts +22 -0
  163. package/dist/redux/store/defaultStoreConfig.d.ts.map +1 -1
  164. package/dist/redux/store/solidEntityApiPool.d.ts +1 -0
  165. package/dist/redux/store/solidEntityApiPool.d.ts.map +1 -1
  166. package/dist/redux/store/solidEntityApiPool.js +4 -0
  167. package/dist/redux/store/solidEntityApiPool.js.map +1 -1
  168. package/dist/redux/store/solidEntityApiPool.ts +4 -0
  169. package/dist/resources/globals.css +56 -0
  170. package/dist/resources/shadcn-base.css +5 -0
  171. package/dist/routes/AppEventListener.d.ts.map +1 -1
  172. package/dist/routes/AppEventListener.js +58 -8
  173. package/dist/routes/AppEventListener.js.map +1 -1
  174. package/dist/routes/AppEventListener.tsx +69 -8
  175. package/dist/types/index.d.ts +1 -0
  176. package/dist/types/jsoneditor.d.ts +4 -0
  177. package/package.json +2 -1
@@ -0,0 +1,906 @@
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { useDropzone } from "react-dropzone";
3
+ import { FileArchive, FolderTree, RefreshCw, Sparkles, UploadCloud, X } from "lucide-react";
4
+ import {
5
+ useConfirmModulePackageImportMutation,
6
+ useDismissModulePackageImportMutation,
7
+ useLazyGetModulePackageImportStatusQuery,
8
+ useRunModulePackageBuildMutation,
9
+ useRunModulePackageSeedMutation,
10
+ useValidateModulePackageImportMutation,
11
+ } from "../../../redux/api/moduleApi";
12
+ import { showToast } from "../../../redux/features/toastSlice";
13
+ import { useDispatch } from "react-redux";
14
+ import {
15
+ SolidButton,
16
+ SolidCheckbox,
17
+ SolidDialog,
18
+ SolidDialogBody,
19
+ SolidDialogDescription,
20
+ SolidDialogHeader,
21
+ SolidDialogSeparator,
22
+ SolidDialogTitle,
23
+ SolidIcon,
24
+ SolidSpinner,
25
+ } from "../../shad-cn-ui";
26
+ import "./ModulePackageDialog.css";
27
+
28
+ type ModulePackageDialogProps = {
29
+ open: boolean;
30
+ onOpenChange: (open: boolean) => void;
31
+ onImported?: () => void;
32
+ };
33
+
34
+ type ModulePackageImportContentProps = {
35
+ onClose: () => void;
36
+ onImported?: () => void;
37
+ initialTransactionKey?: string | null;
38
+ autoResume?: boolean;
39
+ };
40
+
41
+ type StepStatus = "pending" | "running" | "success" | "warning" | "error";
42
+ type StepKey = "preview" | "import" | "build" | "seed";
43
+
44
+ type StepState = {
45
+ title: string;
46
+ description: string;
47
+ status: StepStatus;
48
+ output?: string;
49
+ };
50
+
51
+ type StepMap = Record<StepKey, StepState>;
52
+
53
+ const STEP_ORDER: StepKey[] = ["preview", "import", "build", "seed"];
54
+
55
+ function createInitialSteps(): StepMap {
56
+ return {
57
+ preview: {
58
+ title: "Preview",
59
+ description: "Upload a `.sldx` archive to validate it and preview its contents automatically.",
60
+ status: "pending",
61
+ },
62
+ import: {
63
+ title: "Import",
64
+ description: "Import the files locally. This may restart the UI and API, dismiss the popup, and require a page refresh before you continue.",
65
+ status: "pending",
66
+ },
67
+ build: {
68
+ title: "Build",
69
+ description: "After the services restart and you refresh this module list page, continue with the build step.",
70
+ status: "pending",
71
+ },
72
+ seed: {
73
+ title: "Seed",
74
+ description: "Seed the imported module after the build step completes.",
75
+ status: "pending",
76
+ },
77
+ };
78
+ }
79
+
80
+ function getErrorMessage(error: any): string {
81
+ const responseData = error?.data;
82
+ if (typeof responseData?.message === "string") return responseData.message;
83
+ if (Array.isArray(responseData?.message)) return responseData.message.join(", ");
84
+ if (typeof responseData?.error === "string") return responseData.error;
85
+ if (typeof error?.error === "string") return error.error;
86
+ if (typeof error?.message === "string") return error.message;
87
+ return "Something went wrong while processing the module package.";
88
+ }
89
+
90
+ function formatBytes(bytes: number) {
91
+ if (!bytes) return "0 B";
92
+ const units = ["B", "KB", "MB", "GB"];
93
+ const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
94
+ const value = bytes / Math.pow(1024, exponent);
95
+ return `${value.toFixed(exponent === 0 ? 0 : 2)} ${units[exponent]}`;
96
+ }
97
+
98
+ function isResumableModulePackageStatus(status?: string | null) {
99
+ return [
100
+ "import_running",
101
+ "awaiting_restart",
102
+ "build_running",
103
+ "build_failed",
104
+ "build_succeeded",
105
+ "seed_running",
106
+ "seed_failed",
107
+ ].includes(String(status ?? ""));
108
+ }
109
+
110
+ function getBuildStepStatusFromTransaction(transaction: any): StepStatus {
111
+ if (transaction?.status === "build_running") return "warning";
112
+ if (transaction?.outputs?.build) {
113
+ return String(transaction.outputs.build).includes("[failed]") ? "warning" : "success";
114
+ }
115
+ return "pending";
116
+ }
117
+
118
+ function hydrateStepsFromTransaction(transaction: any): StepMap {
119
+ const nextSteps = createInitialSteps();
120
+ const status = String(transaction?.status ?? "");
121
+
122
+ if (transaction?.preview || transaction?.validation) {
123
+ nextSteps.preview = {
124
+ ...nextSteps.preview,
125
+ status: transaction?.validation?.valid ? "success" : "warning",
126
+ description: transaction?.validation?.valid
127
+ ? "Archive validated and ready for import."
128
+ : "Archive preview loaded, but the package has validation issues to fix first.",
129
+ };
130
+ }
131
+
132
+ if (
133
+ transaction?.outputs?.import ||
134
+ ["awaiting_restart", "build_running", "build_failed", "build_succeeded", "seed_running", "seed_failed", "completed"].includes(status)
135
+ ) {
136
+ nextSteps.import = {
137
+ ...nextSteps.import,
138
+ status: "success",
139
+ description: "Archive extracted. Wait for the UI and API to finish restarting, then refresh this page to continue.",
140
+ output: transaction?.outputs?.import ?? undefined,
141
+ };
142
+ }
143
+
144
+ const buildStatus = getBuildStepStatusFromTransaction(transaction);
145
+ if (buildStatus !== "pending") {
146
+ nextSteps.build = {
147
+ ...nextSteps.build,
148
+ status: buildStatus,
149
+ description:
150
+ status === "build_running"
151
+ ? "Build was previously started. Use Build Now to run it again once you are ready."
152
+ : buildStatus === "warning"
153
+ ? transaction?.errorMessage || "The build step completed with warnings."
154
+ : "Both build targets completed successfully.",
155
+ output: transaction?.outputs?.build ?? undefined,
156
+ };
157
+ } else if (status === "awaiting_restart") {
158
+ nextSteps.build = {
159
+ ...nextSteps.build,
160
+ status: "pending",
161
+ description: "Import completed. Once the UI and API are back, refresh this module list page and continue with Build Now.",
162
+ };
163
+ }
164
+
165
+ if (status === "seed_running") {
166
+ nextSteps.seed = {
167
+ ...nextSteps.seed,
168
+ status: "warning",
169
+ description: "Seed was previously started. Use Seed to run it again once you are ready.",
170
+ output: transaction?.outputs?.seed ?? undefined,
171
+ };
172
+ } else if (status === "seed_failed") {
173
+ nextSteps.seed = {
174
+ ...nextSteps.seed,
175
+ status: "warning",
176
+ description: transaction?.errorMessage || "The seed step finished with warnings.",
177
+ output: transaction?.outputs?.seed ?? undefined,
178
+ };
179
+ } else if (status === "completed") {
180
+ nextSteps.seed = {
181
+ ...nextSteps.seed,
182
+ status: "success",
183
+ description: "Module metadata seeding completed successfully.",
184
+ output: transaction?.outputs?.seed ?? undefined,
185
+ };
186
+ }
187
+
188
+ return nextSteps;
189
+ }
190
+
191
+ function getSuggestedStepFromSteps(steps: StepMap, hasPreview: boolean, isArchiveValid: boolean): StepKey {
192
+ if (!hasPreview || !isArchiveValid) return "preview";
193
+ if (steps.import.status !== "success") return "import";
194
+ if (steps.build.status !== "success" && steps.build.status !== "warning") return "build";
195
+ if (steps.seed.status !== "success" && steps.seed.status !== "warning") return "seed";
196
+ return "seed";
197
+ }
198
+
199
+ function StepStatusIcon({ status }: { status: StepStatus }) {
200
+ if (status === "running") {
201
+ return <SolidSpinner size={16} />;
202
+ }
203
+
204
+ if (status === "success") {
205
+ return <SolidIcon name="si-check-circle" />;
206
+ }
207
+
208
+ if (status === "warning" || status === "error") {
209
+ return <SolidIcon name="si-exclamation-triangle" />;
210
+ }
211
+
212
+ return <span className="solid-module-package-step-dot" aria-hidden="true" />;
213
+ }
214
+
215
+ export function ModulePackageDialog({ open, onOpenChange, onImported }: ModulePackageDialogProps) {
216
+ return (
217
+ <SolidDialog
218
+ open={open}
219
+ onOpenChange={(next) => {
220
+ if (!next) {
221
+ onOpenChange(false);
222
+ }
223
+ }}
224
+ className="solid-module-package-dialog"
225
+ dismissible={false}
226
+ showHeader={false}
227
+ >
228
+ {open ? <ModulePackageImportContent onClose={() => onOpenChange(false)} onImported={onImported} /> : null}
229
+ </SolidDialog>
230
+ );
231
+ }
232
+
233
+ export function ModulePackageImportContent({ onClose, onImported, initialTransactionKey = null, autoResume = false }: ModulePackageImportContentProps) {
234
+ const dispatch = useDispatch();
235
+ const [selectedFile, setSelectedFile] = useState<File | null>(null);
236
+ const [previewResponse, setPreviewResponse] = useState<any | null>(null);
237
+ const [transactionKey, setTransactionKey] = useState<string | null>(null);
238
+ const [overwriteExisting, setOverwriteExisting] = useState(false);
239
+ const [steps, setSteps] = useState<StepMap>(() => createInitialSteps());
240
+ const [selectedStep, setSelectedStep] = useState<StepKey>("preview");
241
+ const [finalSummary, setFinalSummary] = useState<string | null>(null);
242
+ const [isPreviewing, setIsPreviewing] = useState(false);
243
+ const [isProcessing, setIsProcessing] = useState(false);
244
+
245
+ const [validateModulePackageImport] = useValidateModulePackageImportMutation();
246
+ const [confirmModulePackageImport] = useConfirmModulePackageImportMutation();
247
+ const [dismissModulePackageImport] = useDismissModulePackageImportMutation();
248
+ const [fetchModulePackageImportStatus] = useLazyGetModulePackageImportStatusQuery();
249
+ const [runModulePackageBuild] = useRunModulePackageBuildMutation();
250
+ const [runModulePackageSeed] = useRunModulePackageSeedMutation();
251
+
252
+ const hasResumedTransactionRef = useRef(false);
253
+ const lastAutoPreviewFileRef = useRef<string | null>(null);
254
+
255
+ const updateStep = useCallback((key: StepKey, nextState: Partial<StepState>) => {
256
+ setSteps((current) => ({
257
+ ...current,
258
+ [key]: {
259
+ ...current[key],
260
+ ...nextState,
261
+ },
262
+ }));
263
+ }, []);
264
+
265
+ const { getRootProps, getInputProps, open: openFilePicker, isDragActive } = useDropzone({
266
+ accept: {
267
+ "application/octet-stream": [".sldx"],
268
+ "application/x-zip-compressed": [".sldx"],
269
+ "application/zip": [".sldx"],
270
+ },
271
+ multiple: false,
272
+ noClick: true,
273
+ noKeyboard: true,
274
+ onDrop: (acceptedFiles) => {
275
+ if (acceptedFiles[0]) {
276
+ setSelectedFile(acceptedFiles[0]);
277
+ }
278
+ },
279
+ });
280
+
281
+ const isArchiveValid = Boolean(previewResponse?.validation?.valid);
282
+ const hasConflicts = (previewResponse?.conflicts?.length ?? 0) > 0;
283
+ const suggestedStep = getSuggestedStepFromSteps(steps, Boolean(previewResponse), isArchiveValid);
284
+
285
+ const canImport = steps.preview.status !== "pending" && isArchiveValid && steps.import.status !== "success" && !isProcessing && !isPreviewing;
286
+ const canRunBuild = steps.import.status === "success" && !isProcessing;
287
+ const canRunSeed = (steps.build.status === "success" || steps.build.status === "warning") && !isProcessing;
288
+ const canContinueFromPreview = Boolean(previewResponse) && isArchiveValid && !isPreviewing;
289
+
290
+ const activeStep = useMemo<StepKey>(() => {
291
+ if (selectedStep === "seed" && !(steps.build.status === "success" || steps.build.status === "warning" || steps.seed.status !== "pending")) {
292
+ return suggestedStep;
293
+ }
294
+
295
+ if (selectedStep === "build" && !(steps.import.status === "success" || steps.build.status !== "pending")) {
296
+ return suggestedStep;
297
+ }
298
+
299
+ if (selectedStep === "import" && !(isArchiveValid || steps.import.status !== "pending")) {
300
+ return suggestedStep;
301
+ }
302
+
303
+ return selectedStep;
304
+ }, [
305
+ previewResponse,
306
+ selectedStep,
307
+ steps.build.status,
308
+ steps.import.status,
309
+ steps.seed.status,
310
+ suggestedStep,
311
+ ]);
312
+
313
+ const completedSteps = STEP_ORDER.filter((stepKey) => steps[stepKey].status === "success").length;
314
+ const maxUnlockedStepIndex = useMemo(() => {
315
+ if (!previewResponse || !isArchiveValid) return 0;
316
+ if (steps.import.status !== "success") return 1;
317
+ if (steps.build.status !== "success" && steps.build.status !== "warning") return 2;
318
+ return 3;
319
+ }, [isArchiveValid, previewResponse, steps.build.status, steps.import.status]);
320
+
321
+ const handlePreviewArchive = useCallback(async () => {
322
+ if (!selectedFile) {
323
+ return;
324
+ }
325
+
326
+ const formData = new FormData();
327
+ formData.append("modulePackage", selectedFile);
328
+
329
+ try {
330
+ setIsPreviewing(true);
331
+ const response = await validateModulePackageImport(formData).unwrap();
332
+ setPreviewResponse(response);
333
+ setTransactionKey(response?.transactionKey ?? null);
334
+ setOverwriteExisting(false);
335
+ setFinalSummary(null);
336
+ setSteps(hydrateStepsFromTransaction(response));
337
+ setSelectedStep("preview");
338
+ dispatch(
339
+ showToast({
340
+ severity: response?.validation?.valid ? "success" : "warn",
341
+ summary: response?.validation?.valid ? "Archive validated" : "Archive needs attention",
342
+ detail: response?.validation?.valid
343
+ ? "Review the preview and continue with the import when you are ready."
344
+ : "The uploaded archive is missing one or more required SolidX validation markers.",
345
+ }),
346
+ );
347
+ } catch (error: any) {
348
+ dispatch(showToast({ severity: "error", summary: "Preview failed", detail: getErrorMessage(error) }));
349
+ } finally {
350
+ setIsPreviewing(false);
351
+ }
352
+ }, [dispatch, selectedFile, validateModulePackageImport]);
353
+
354
+ useEffect(() => {
355
+ if (!selectedFile) {
356
+ lastAutoPreviewFileRef.current = null;
357
+ return;
358
+ }
359
+
360
+ const fileSignature = `${selectedFile.name}:${selectedFile.size}:${selectedFile.lastModified}`;
361
+ if (lastAutoPreviewFileRef.current === fileSignature) {
362
+ return;
363
+ }
364
+
365
+ lastAutoPreviewFileRef.current = fileSignature;
366
+ void handlePreviewArchive();
367
+ }, [handlePreviewArchive, selectedFile]);
368
+
369
+ const executeBuildStep = useCallback(async (activeTransactionKey: string) => {
370
+ updateStep("build", {
371
+ status: "running",
372
+ description: "Running `solidctl build` followed by `solidctl build --ui-only`.",
373
+ });
374
+
375
+ const buildResponse = await runModulePackageBuild({
376
+ transactionKey: activeTransactionKey,
377
+ buildSolidApi: true,
378
+ buildSolidUi: true,
379
+ }).unwrap();
380
+
381
+ const buildFailed = buildResponse?.status === "build_failed";
382
+ updateStep("build", {
383
+ status: buildFailed ? "warning" : "success",
384
+ description: buildFailed
385
+ ? buildResponse?.errorMessage || "The module was imported, but one or more build targets failed."
386
+ : "Both build targets completed successfully.",
387
+ output: buildResponse?.outputs?.build,
388
+ });
389
+
390
+ return buildResponse;
391
+ }, [runModulePackageBuild, updateStep]);
392
+
393
+ const executeSeedStep = useCallback(async (activeTransactionKey: string) => {
394
+ updateStep("seed", {
395
+ status: "running",
396
+ description: "Running `solidctl seed --modules-to-seed` for the imported module.",
397
+ });
398
+
399
+ const seedResponse = await runModulePackageSeed({
400
+ transactionKey: activeTransactionKey,
401
+ seedGlobalMetadata: false,
402
+ }).unwrap();
403
+
404
+ const seedFailed = seedResponse?.status === "seed_failed";
405
+ updateStep("seed", {
406
+ status: seedFailed ? "warning" : "success",
407
+ description: seedFailed
408
+ ? seedResponse?.errorMessage || "The seed step finished with warnings."
409
+ : "Module metadata seeding completed successfully.",
410
+ output: seedResponse?.outputs?.seed,
411
+ });
412
+
413
+ return seedResponse;
414
+ }, [runModulePackageSeed, updateStep]);
415
+
416
+ const handleRunBuild = useCallback(async () => {
417
+ if (!transactionKey) {
418
+ dispatch(showToast({ severity: "error", summary: "Missing transaction", detail: "Import the module before running the build step." }));
419
+ return;
420
+ }
421
+
422
+ try {
423
+ setIsProcessing(true);
424
+ setFinalSummary(null);
425
+ const buildResponse = await executeBuildStep(transactionKey);
426
+ setPreviewResponse(buildResponse);
427
+ setSelectedStep("seed");
428
+ setIsProcessing(false);
429
+ } catch (error: any) {
430
+ updateStep("build", {
431
+ status: "error",
432
+ description: getErrorMessage(error),
433
+ });
434
+ setFinalSummary("The build step needs attention before you continue.");
435
+ setIsProcessing(false);
436
+ }
437
+ }, [dispatch, executeBuildStep, transactionKey, updateStep]);
438
+
439
+ const handleRunSeed = useCallback(async () => {
440
+ if (!transactionKey) {
441
+ dispatch(showToast({ severity: "error", summary: "Missing transaction", detail: "Import the module before running the seed step." }));
442
+ return;
443
+ }
444
+
445
+ try {
446
+ setIsProcessing(true);
447
+ setFinalSummary(null);
448
+ const seedResponse = await executeSeedStep(transactionKey);
449
+ setPreviewResponse(seedResponse);
450
+ setSelectedStep("seed");
451
+ setIsProcessing(false);
452
+
453
+ const buildFailed = steps.build.status === "warning" || String(previewResponse?.outputs?.build ?? "").includes("[failed]");
454
+ const seedFailed = seedResponse?.status === "seed_failed";
455
+
456
+ if (!seedFailed) {
457
+ onImported?.();
458
+ }
459
+
460
+ if (buildFailed && !seedFailed) {
461
+ setFinalSummary("Module imported and seeded successfully, but the build step reported errors.");
462
+ return;
463
+ }
464
+
465
+ if (seedFailed) {
466
+ setFinalSummary("Module imported, but the seed step needs attention.");
467
+ return;
468
+ }
469
+
470
+ setFinalSummary("Module import completed successfully.");
471
+ } catch (error: any) {
472
+ updateStep("seed", {
473
+ status: "error",
474
+ description: getErrorMessage(error),
475
+ });
476
+ setFinalSummary("The seed step needs attention before the workflow can be closed.");
477
+ setIsProcessing(false);
478
+ }
479
+ }, [dispatch, executeSeedStep, onImported, previewResponse?.outputs?.build, steps.build.status, transactionKey, updateStep]);
480
+
481
+ useEffect(() => {
482
+ if (!initialTransactionKey || hasResumedTransactionRef.current) {
483
+ return;
484
+ }
485
+
486
+ hasResumedTransactionRef.current = true;
487
+
488
+ const loadTransaction = async () => {
489
+ try {
490
+ const transaction = await fetchModulePackageImportStatus({ transactionKey: initialTransactionKey }).unwrap();
491
+ const hydratedSteps = hydrateStepsFromTransaction(transaction);
492
+ setPreviewResponse(transaction);
493
+ setTransactionKey(transaction?.transactionKey ?? null);
494
+ setSteps(hydratedSteps);
495
+ setSelectedStep(getSuggestedStepFromSteps(hydratedSteps, Boolean(transaction?.preview || transaction?.validation), Boolean(transaction?.validation?.valid)));
496
+
497
+ if (transaction?.status === "completed") {
498
+ setFinalSummary("Module import completed successfully.");
499
+ return;
500
+ }
501
+
502
+ if (transaction?.status === "seed_failed") {
503
+ setFinalSummary("Module imported, but the seed step needs attention.");
504
+ return;
505
+ }
506
+
507
+ if (!autoResume || !isResumableModulePackageStatus(transaction?.status)) {
508
+ return;
509
+ }
510
+ } catch (error: any) {
511
+ dispatch(showToast({ severity: "error", summary: "Unable to resume import", detail: getErrorMessage(error) }));
512
+ }
513
+ };
514
+
515
+ void loadTransaction();
516
+ }, [autoResume, dispatch, fetchModulePackageImportStatus, initialTransactionKey]);
517
+
518
+ const handleStartImport = async () => {
519
+ if (!transactionKey) {
520
+ dispatch(showToast({ severity: "error", summary: "Missing preview", detail: "Upload an archive and wait for preview to finish first." }));
521
+ return;
522
+ }
523
+
524
+ try {
525
+ setIsProcessing(true);
526
+ setFinalSummary(null);
527
+ updateStep("import", {
528
+ status: "running",
529
+ description: "Extracting the archive and placing files into `solid-api` and `solid-ui`.",
530
+ });
531
+
532
+ const response = await confirmModulePackageImport({
533
+ transactionKey,
534
+ overwriteExisting,
535
+ }).unwrap();
536
+
537
+ updateStep("import", {
538
+ status: "success",
539
+ description: "Archive extracted. The local UI/API may restart now, so refresh this page once both services are back.",
540
+ output: response?.outputs?.import,
541
+ });
542
+ setPreviewResponse(response);
543
+ setIsProcessing(false);
544
+ dispatch(
545
+ showToast({
546
+ severity: "success",
547
+ summary: "Module imported",
548
+ detail: "The module files were placed locally. Wait for the UI and API to restart, then refresh this module list page to continue with build and seed.",
549
+ }),
550
+ );
551
+ onClose();
552
+ } catch (error: any) {
553
+ updateStep("import", {
554
+ status: "error",
555
+ description: getErrorMessage(error),
556
+ });
557
+ setFinalSummary("The module import could not be completed.");
558
+ setIsProcessing(false);
559
+ }
560
+ };
561
+
562
+ const handleClose = () => {
563
+ if (isProcessing) return;
564
+ if (finalSummary && transactionKey) {
565
+ void dismissModulePackageImport({ transactionKey });
566
+ }
567
+ onClose();
568
+ };
569
+
570
+ const renderPreviewContent = () => {
571
+ return (
572
+ <div className="solid-module-package-preview">
573
+ {!selectedFile ? (
574
+ <div
575
+ {...getRootProps({
576
+ className: `solid-module-package-dropzone${isDragActive ? " is-active" : ""}`,
577
+ })}
578
+ >
579
+ <input {...getInputProps()} />
580
+ <div className="solid-module-package-dropzone__icon">
581
+ <UploadCloud size={22} />
582
+ </div>
583
+ <div className="solid-module-package-dropzone__copy">
584
+ <div className="solid-module-package-dropzone__title">Upload `.sldx` archive</div>
585
+ <p>Choose a packaged SolidX module archive from the local file system. Preview starts automatically after upload.</p>
586
+ </div>
587
+ <div className="solid-module-package-dropzone__actions">
588
+ <SolidButton
589
+ type="button"
590
+ size="small"
591
+ variant="outline"
592
+ onClick={(event) => {
593
+ event.stopPropagation();
594
+ openFilePicker();
595
+ }}
596
+ >
597
+ Browse archive
598
+ </SolidButton>
599
+ </div>
600
+ </div>
601
+ ) : (
602
+ <div className="solid-module-package-file-card">
603
+ <div className="solid-module-package-file-card__main">
604
+ <div className="solid-module-package-file-card__icon">
605
+ <FileArchive size={18} />
606
+ </div>
607
+ <div>
608
+ <div className="solid-module-package-file-card__name">{selectedFile.name}</div>
609
+ <div className="solid-module-package-file-card__meta">
610
+ {formatBytes(selectedFile.size)}
611
+ {isPreviewing ? " • Previewing archive..." : ""}
612
+ </div>
613
+ </div>
614
+ </div>
615
+ <SolidButton
616
+ type="button"
617
+ size="small"
618
+ variant="ghost"
619
+ onClick={() => {
620
+ setSelectedFile(null);
621
+ setPreviewResponse(null);
622
+ setTransactionKey(null);
623
+ setOverwriteExisting(false);
624
+ setFinalSummary(null);
625
+ setSelectedStep("preview");
626
+ setSteps(createInitialSteps());
627
+ }}
628
+ disabled={isProcessing}
629
+ >
630
+ Remove
631
+ </SolidButton>
632
+ </div>
633
+ )}
634
+
635
+ {!previewResponse ? (
636
+ <div className="solid-module-package-empty">
637
+ {isPreviewing ? <SolidSpinner size={18} /> : <FolderTree size={18} />}
638
+ <p>
639
+ {isPreviewing
640
+ ? "Reading the archive and validating the SolidX markers."
641
+ : "Upload an archive to start the automatic preview and validation flow."}
642
+ </p>
643
+ </div>
644
+ ) : (
645
+ <>
646
+ <div className="solid-module-package-metrics">
647
+ <div className="solid-module-package-metric">
648
+ <span className="solid-module-package-metric__label">Module</span>
649
+ <span className="solid-module-package-metric__value">{previewResponse?.preview?.module?.name || "Unknown"}</span>
650
+ </div>
651
+ <div className="solid-module-package-metric">
652
+ <span className="solid-module-package-metric__label">solid-api files</span>
653
+ <span className="solid-module-package-metric__value">{previewResponse?.preview?.contentsSummary?.solidApiEntries ?? 0}</span>
654
+ </div>
655
+ <div className="solid-module-package-metric">
656
+ <span className="solid-module-package-metric__label">solid-ui files</span>
657
+ <span className="solid-module-package-metric__value">{previewResponse?.preview?.contentsSummary?.solidUiEntries ?? 0}</span>
658
+ </div>
659
+ </div>
660
+
661
+ <div className="solid-module-package-checklist">
662
+ <div className="solid-module-package-checklist__section">
663
+ <div className="solid-module-package-checklist__title">Required markers</div>
664
+ <ul className="solid-module-package-inline-list">
665
+ <li>{previewResponse?.preview?.requiredPaths?.metadataPath}</li>
666
+ <li>{previewResponse?.preview?.requiredPaths?.apiModulePath}</li>
667
+ <li>{previewResponse?.preview?.requiredPaths?.uiModulePath}</li>
668
+ </ul>
669
+ </div>
670
+
671
+ {(previewResponse?.validation?.warnings?.length ?? 0) > 0 ? (
672
+ <div className="solid-module-package-notice is-warning">
673
+ <div className="solid-module-package-notice__title">Warnings</div>
674
+ <ul className="solid-module-package-inline-list">
675
+ {previewResponse.validation.warnings.map((warning: string) => (
676
+ <li key={warning}>{warning}</li>
677
+ ))}
678
+ </ul>
679
+ </div>
680
+ ) : null}
681
+
682
+ {(previewResponse?.validation?.errors?.length ?? 0) > 0 ? (
683
+ <div className="solid-module-package-notice is-danger">
684
+ <div className="solid-module-package-notice__title">Validation issues</div>
685
+ <ul className="solid-module-package-inline-list">
686
+ {previewResponse.validation.errors.map((error: string) => (
687
+ <li key={error}>{error}</li>
688
+ ))}
689
+ </ul>
690
+ </div>
691
+ ) : null}
692
+
693
+ {hasConflicts ? (
694
+ <div className="solid-module-package-notice is-warning">
695
+ <div className="solid-module-package-notice__title">Existing files detected</div>
696
+ <ul className="solid-module-package-inline-list">
697
+ {previewResponse.conflicts.map((conflict: string) => (
698
+ <li key={conflict}>{conflict}</li>
699
+ ))}
700
+ </ul>
701
+ <SolidCheckbox
702
+ checked={overwriteExisting}
703
+ onChange={(event) => setOverwriteExisting(event.currentTarget.checked)}
704
+ label="Overwrite the existing module folders during import"
705
+ />
706
+ </div>
707
+ ) : null}
708
+ </div>
709
+
710
+ <div className="solid-module-package-tree">
711
+ {(previewResponse?.preview?.fileTree ?? []).map((entry: string) => (
712
+ <div key={entry} className="solid-module-package-tree__row">
713
+ {entry}
714
+ </div>
715
+ ))}
716
+ </div>
717
+ </>
718
+ )}
719
+ </div>
720
+ );
721
+ };
722
+
723
+ const renderActiveStepCta = () => {
724
+ if (activeStep === "preview") {
725
+ return (
726
+ <SolidButton
727
+ type="button"
728
+ size="small"
729
+ onClick={() => setSelectedStep("import")}
730
+ disabled={!canContinueFromPreview}
731
+ rightIcon={<Sparkles size={14} />}
732
+ >
733
+ Continue to import
734
+ </SolidButton>
735
+ );
736
+ }
737
+
738
+ if (activeStep === "import") {
739
+ return (
740
+ <SolidButton
741
+ type="button"
742
+ size="small"
743
+ onClick={handleStartImport}
744
+ disabled={!canImport}
745
+ loading={isProcessing && steps.import.status === "running"}
746
+ leftIcon={<Sparkles size={14} />}
747
+ >
748
+ Import Module
749
+ </SolidButton>
750
+ );
751
+ }
752
+
753
+ if (activeStep === "build") {
754
+ return (
755
+ <SolidButton
756
+ type="button"
757
+ size="small"
758
+ variant="outline"
759
+ onClick={handleRunBuild}
760
+ disabled={!canRunBuild}
761
+ loading={isProcessing && steps.build.status === "running"}
762
+ >
763
+ {steps.build.status === "success" || steps.build.status === "warning" ? "Build Again" : "Build Now"}
764
+ </SolidButton>
765
+ );
766
+ }
767
+
768
+ if (activeStep === "seed") {
769
+ return (
770
+ <SolidButton
771
+ type="button"
772
+ size="small"
773
+ variant="outline"
774
+ onClick={handleRunSeed}
775
+ disabled={!canRunSeed}
776
+ loading={isProcessing && steps.seed.status === "running"}
777
+ >
778
+ {steps.seed.status === "success" || steps.seed.status === "warning" ? "Seed Again" : "Seed"}
779
+ </SolidButton>
780
+ );
781
+ }
782
+
783
+ return null;
784
+ };
785
+
786
+ const renderActiveStepContent = () => {
787
+ if (activeStep === "preview") {
788
+ return renderPreviewContent();
789
+ }
790
+
791
+ if (activeStep === "import") {
792
+ return (
793
+ <div className="solid-module-package-step-stack">
794
+ <div className="solid-module-package-notice is-warning">
795
+ <div className="solid-module-package-notice__title">What happens after import</div>
796
+ <ul className="solid-module-package-inline-list">
797
+ <li>Depending on the files in this archive, both the UI and API services may restart immediately.</li>
798
+ <li>This popup may get dismissed as part of that restart.</li>
799
+ <li>Wait for both services to come back, then refresh this module list page to continue with build and seed.</li>
800
+ </ul>
801
+ </div>
802
+ {steps.import.output ? <pre className="solid-module-package-step__output">{steps.import.output}</pre> : null}
803
+ </div>
804
+ );
805
+ }
806
+
807
+ if (activeStep === "build" && steps.build.output) {
808
+ return <pre className="solid-module-package-step__output">{steps.build.output}</pre>;
809
+ }
810
+
811
+ if (activeStep === "seed" && steps.seed.output) {
812
+ return <pre className="solid-module-package-step__output">{steps.seed.output}</pre>;
813
+ }
814
+
815
+ return null;
816
+ };
817
+
818
+ return (
819
+ <>
820
+ <SolidDialogHeader className="solid-filter-dialog-head solid-module-package-dialog__header">
821
+ <div>
822
+ <SolidDialogTitle className="solid-filter-dialog-title m-0">Import Module Package</SolidDialogTitle>
823
+ <SolidDialogDescription className="solid-filter-dialog-subtitle m-0">
824
+ Upload a `.sldx` archive, let SolidX preview it automatically, then move through import, build, and seed.
825
+ </SolidDialogDescription>
826
+ </div>
827
+ {!isProcessing ? (
828
+ <button type="button" className="solid-filter-dialog-close solid-radix-dialog-close" aria-label="Close" onClick={handleClose}>
829
+ <X size={16} />
830
+ </button>
831
+ ) : null}
832
+ </SolidDialogHeader>
833
+
834
+ <SolidDialogSeparator className="solid-filter-dialog-sep" />
835
+
836
+ <SolidDialogBody className="solid-filter-dialog-body solid-module-package-dialog__body">
837
+ <section className="solid-module-package-shell">
838
+ <div className="solid-module-package-panel solid-module-package-panel--wizard">
839
+ <div className="solid-module-package-stepper">
840
+ {STEP_ORDER.map((stepKey, index) => {
841
+ const step = steps[stepKey];
842
+ const isCurrent = activeStep === stepKey;
843
+ const isDone = step.status === "success";
844
+ const isWarning = step.status === "warning" || step.status === "error";
845
+ const isUnlocked = index <= maxUnlockedStepIndex;
846
+
847
+ return (
848
+ <div
849
+ key={stepKey}
850
+ className={`solid-module-package-stepper__item${isCurrent ? " is-current" : ""}${isDone ? " is-done" : ""}${isWarning ? " is-warning" : ""}${isUnlocked ? " is-clickable" : ""}`}
851
+ >
852
+ <div className="solid-module-package-stepper__track" />
853
+ <button
854
+ type="button"
855
+ className="solid-module-package-stepper__button"
856
+ onClick={() => setSelectedStep(stepKey)}
857
+ disabled={!isUnlocked}
858
+ >
859
+ <div className="solid-module-package-stepper__node">
860
+ {isDone ? <SolidIcon name="si-check" /> : <span>{index + 1}</span>}
861
+ </div>
862
+ <div className="solid-module-package-stepper__label">{step.title}</div>
863
+ <div className="solid-module-package-stepper__caption">{step.description}</div>
864
+ </button>
865
+ </div>
866
+ );
867
+ })}
868
+ </div>
869
+
870
+ <div className="solid-module-package-stage">
871
+ <div className="solid-module-package-stage__meta">
872
+ <span>Step {STEP_ORDER.indexOf(activeStep) + 1} of {STEP_ORDER.length}</span>
873
+ <span>{completedSteps} completed</span>
874
+ </div>
875
+ <div className="solid-module-package-stage__header">
876
+ <p>{steps[activeStep].description}</p>
877
+ <div className={`solid-module-package-stage__status is-${steps[activeStep].status}`}>
878
+ <StepStatusIcon status={steps[activeStep].status} />
879
+ </div>
880
+ </div>
881
+
882
+ {renderActiveStepContent()}
883
+
884
+ <div className="solid-module-package-stage__cta">{renderActiveStepCta()}</div>
885
+ </div>
886
+
887
+ {finalSummary ? (
888
+ <div className="solid-module-package-summary">
889
+ <span className="solid-module-package-summary__copy">{finalSummary}</span>
890
+ <SolidButton
891
+ type="button"
892
+ size="small"
893
+ variant="outline"
894
+ leftIcon={<RefreshCw size={14} />}
895
+ onClick={() => window.location.reload()}
896
+ >
897
+ Reload
898
+ </SolidButton>
899
+ </div>
900
+ ) : null}
901
+ </div>
902
+ </section>
903
+ </SolidDialogBody>
904
+ </>
905
+ );
906
+ }