@solidxai/core-ui 0.1.12-beta.6 → 0.1.12-beta.8
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/components/auth/AuthLayout.d.ts.map +1 -1
- package/dist/components/auth/AuthLayout.js +19 -17
- package/dist/components/auth/AuthLayout.js.map +1 -1
- package/dist/components/auth/AuthLayout.tsx +59 -34
- package/dist/components/common/BackendReconnectIndicator.d.ts +6 -0
- package/dist/components/common/BackendReconnectIndicator.d.ts.map +1 -0
- package/dist/components/common/BackendReconnectIndicator.js +25 -0
- package/dist/components/common/BackendReconnectIndicator.js.map +1 -0
- package/dist/components/common/BackendReconnectIndicator.tsx +45 -0
- package/dist/components/common/SettingsComponent.d.ts.map +1 -1
- package/dist/components/common/SettingsComponent.js +7 -3
- package/dist/components/common/SettingsComponent.js.map +1 -1
- package/dist/components/common/SettingsComponent.module.css +144 -12
- package/dist/components/common/SettingsComponent.tsx +22 -3
- package/dist/components/common/SolidAdmin.d.ts.map +1 -1
- package/dist/components/common/SolidAdmin.js +7 -2
- package/dist/components/common/SolidAdmin.js.map +1 -1
- package/dist/components/common/SolidAdmin.tsx +7 -2
- package/dist/components/common/SolidBreadcrumb.d.ts.map +1 -1
- package/dist/components/common/SolidBreadcrumb.js +1 -6
- package/dist/components/common/SolidBreadcrumb.js.map +1 -1
- package/dist/components/common/SolidBreadcrumb.tsx +1 -8
- package/dist/components/common/SolidMaterialSymbol.d.ts +10 -0
- package/dist/components/common/SolidMaterialSymbol.d.ts.map +1 -0
- package/dist/components/common/SolidMaterialSymbol.js +69 -0
- package/dist/components/common/SolidMaterialSymbol.js.map +1 -0
- package/dist/components/common/SolidMaterialSymbol.tsx +96 -0
- package/dist/components/common/SolidToastProvider.d.ts.map +1 -1
- package/dist/components/common/SolidToastProvider.js +14 -1
- package/dist/components/common/SolidToastProvider.js.map +1 -1
- package/dist/components/common/SolidToastProvider.tsx +19 -1
- package/dist/components/core/card/CardGrid.d.ts +1 -1
- package/dist/components/core/card/CardGrid.d.ts.map +1 -1
- package/dist/components/core/card/CardGrid.js +2 -2
- package/dist/components/core/card/CardGrid.js.map +1 -1
- package/dist/components/core/card/CardGrid.tsx +4 -0
- package/dist/components/core/card/CardItem.d.ts +2 -0
- package/dist/components/core/card/CardItem.d.ts.map +1 -1
- package/dist/components/core/card/CardItem.js +7 -2
- package/dist/components/core/card/CardItem.js.map +1 -1
- package/dist/components/core/card/CardItem.tsx +30 -8
- package/dist/components/core/card/SolidCardView.d.ts.map +1 -1
- package/dist/components/core/card/SolidCardView.js +87 -21
- package/dist/components/core/card/SolidCardView.js.map +1 -1
- package/dist/components/core/card/SolidCardView.tsx +96 -40
- package/dist/components/core/card/SolidCardViewConfigure.d.ts +1 -1
- package/dist/components/core/card/SolidCardViewConfigure.d.ts.map +1 -1
- package/dist/components/core/card/SolidCardViewConfigure.js +9 -7
- package/dist/components/core/card/SolidCardViewConfigure.js.map +1 -1
- package/dist/components/core/card/SolidCardViewConfigure.tsx +14 -1
- package/dist/components/core/common/SolidAccountSettings/SolidAccountSettings.module.css +3 -1
- package/dist/components/core/common/SolidGlobalSearchElement.d.ts.map +1 -1
- package/dist/components/core/common/SolidGlobalSearchElement.js +4 -3
- package/dist/components/core/common/SolidGlobalSearchElement.js.map +1 -1
- package/dist/components/core/common/SolidGlobalSearchElement.tsx +15 -19
- package/dist/components/core/extension/solid-core/modelMetadata/list/DeleteModelRowAction.d.ts.map +1 -1
- package/dist/components/core/extension/solid-core/modelMetadata/list/DeleteModelRowAction.js +43 -16
- package/dist/components/core/extension/solid-core/modelMetadata/list/DeleteModelRowAction.js.map +1 -1
- package/dist/components/core/extension/solid-core/modelMetadata/list/DeleteModelRowAction.tsx +24 -2
- package/dist/components/core/form/SolidFormFooter.js +1 -1
- package/dist/components/core/form/SolidFormFooter.js.map +1 -1
- package/dist/components/core/form/SolidFormFooter.tsx +1 -1
- package/dist/components/core/form/SolidFormLayouts.js +1 -1
- package/dist/components/core/form/SolidFormLayouts.js.map +1 -1
- package/dist/components/core/form/SolidFormLayouts.tsx +2 -2
- package/dist/components/core/form/fields/SolidMediaMultipleField.d.ts.map +1 -1
- package/dist/components/core/form/fields/SolidMediaMultipleField.js +120 -56
- package/dist/components/core/form/fields/SolidMediaMultipleField.js.map +1 -1
- package/dist/components/core/form/fields/SolidMediaMultipleField.tsx +63 -50
- package/dist/components/core/form/fields/SolidMediaSingleField.d.ts.map +1 -1
- package/dist/components/core/form/fields/SolidMediaSingleField.js +115 -37
- package/dist/components/core/form/fields/SolidMediaSingleField.js.map +1 -1
- package/dist/components/core/form/fields/SolidMediaSingleField.tsx +36 -35
- package/dist/components/core/form/fields/SolidRichTextField.d.ts.map +1 -1
- package/dist/components/core/form/fields/SolidRichTextField.js +67 -4
- package/dist/components/core/form/fields/SolidRichTextField.js.map +1 -1
- package/dist/components/core/form/fields/SolidRichTextField.tsx +89 -7
- package/dist/components/core/form/fields/mediaFieldUtils.d.ts +3 -0
- package/dist/components/core/form/fields/mediaFieldUtils.d.ts.map +1 -0
- package/dist/components/core/form/fields/mediaFieldUtils.js +30 -0
- package/dist/components/core/form/fields/mediaFieldUtils.js.map +1 -0
- package/dist/components/core/form/fields/mediaFieldUtils.ts +45 -0
- package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.d.ts.map +1 -1
- package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.js +1 -1
- package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.js.map +1 -1
- package/dist/components/core/form/fields/relations/RolesGroupedByModuleWidget.tsx +3 -1
- package/dist/components/core/form/fields/relations/SolidRelationManyToOneField.d.ts.map +1 -1
- package/dist/components/core/form/fields/relations/SolidRelationManyToOneField.js +22 -23
- package/dist/components/core/form/fields/relations/SolidRelationManyToOneField.js.map +1 -1
- package/dist/components/core/form/fields/relations/SolidRelationManyToOneField.tsx +13 -7
- package/dist/components/core/form/fields/widgets/SolidIconEditWidget.d.ts.map +1 -1
- package/dist/components/core/form/fields/widgets/SolidIconEditWidget.js +16 -11
- package/dist/components/core/form/fields/widgets/SolidIconEditWidget.js.map +1 -1
- package/dist/components/core/form/fields/widgets/SolidIconEditWidget.tsx +32 -18
- package/dist/components/core/form/fields/widgets/SolidIconViewWidget.d.ts.map +1 -1
- package/dist/components/core/form/fields/widgets/SolidIconViewWidget.js +3 -1
- package/dist/components/core/form/fields/widgets/SolidIconViewWidget.js.map +1 -1
- package/dist/components/core/form/fields/widgets/SolidIconViewWidget.tsx +11 -5
- package/dist/components/core/kanban/KanbanBoard.d.ts +1 -1
- package/dist/components/core/kanban/KanbanBoard.d.ts.map +1 -1
- package/dist/components/core/kanban/KanbanBoard.js +2 -2
- package/dist/components/core/kanban/KanbanBoard.js.map +1 -1
- package/dist/components/core/kanban/KanbanBoard.tsx +3 -1
- package/dist/components/core/kanban/KanbanCard.d.ts +2 -0
- package/dist/components/core/kanban/KanbanCard.d.ts.map +1 -1
- package/dist/components/core/kanban/KanbanCard.js +10 -5
- package/dist/components/core/kanban/KanbanCard.js.map +1 -1
- package/dist/components/core/kanban/KanbanCard.tsx +28 -12
- package/dist/components/core/kanban/KanbanColumn.d.ts +3 -1
- package/dist/components/core/kanban/KanbanColumn.d.ts.map +1 -1
- package/dist/components/core/kanban/KanbanColumn.js +2 -2
- package/dist/components/core/kanban/KanbanColumn.js.map +1 -1
- package/dist/components/core/kanban/KanbanColumn.tsx +4 -2
- package/dist/components/core/kanban/SolidKanbanView.d.ts.map +1 -1
- package/dist/components/core/kanban/SolidKanbanView.js +154 -25
- package/dist/components/core/kanban/SolidKanbanView.js.map +1 -1
- package/dist/components/core/kanban/SolidKanbanView.tsx +129 -16
- package/dist/components/core/kanban/SolidKanbanViewConfigure.d.ts +1 -1
- package/dist/components/core/kanban/SolidKanbanViewConfigure.d.ts.map +1 -1
- package/dist/components/core/kanban/SolidKanbanViewConfigure.js +13 -10
- package/dist/components/core/kanban/SolidKanbanViewConfigure.js.map +1 -1
- package/dist/components/core/kanban/SolidKanbanViewConfigure.tsx +18 -3
- package/dist/components/core/list/SolidDataTable.d.ts.map +1 -1
- package/dist/components/core/list/SolidDataTable.js +3 -2
- package/dist/components/core/list/SolidDataTable.js.map +1 -1
- package/dist/components/core/list/SolidDataTable.tsx +3 -2
- package/dist/components/core/list/SolidListView.d.ts.map +1 -1
- package/dist/components/core/list/SolidListView.js +16 -5
- package/dist/components/core/list/SolidListView.js.map +1 -1
- package/dist/components/core/list/SolidListView.tsx +20 -8
- package/dist/components/core/list/SolidListViewColumn.d.ts.map +1 -1
- package/dist/components/core/list/SolidListViewColumn.js +1 -0
- package/dist/components/core/list/SolidListViewColumn.js.map +1 -1
- package/dist/components/core/list/SolidListViewColumn.tsx +1 -0
- package/dist/components/core/list/columns/SolidShortTextColumn.d.ts.map +1 -1
- package/dist/components/core/list/columns/SolidShortTextColumn.js +7 -2
- package/dist/components/core/list/columns/SolidShortTextColumn.js.map +1 -1
- package/dist/components/core/list/columns/SolidShortTextColumn.tsx +7 -2
- package/dist/components/core/list/columns/relations/SolidRelationManyToManyColumn.d.ts.map +1 -1
- package/dist/components/core/list/columns/relations/SolidRelationManyToManyColumn.js +4 -1
- package/dist/components/core/list/columns/relations/SolidRelationManyToManyColumn.js.map +1 -1
- package/dist/components/core/list/columns/relations/SolidRelationManyToManyColumn.tsx +5 -2
- package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.d.ts.map +1 -1
- package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.js +2 -1
- package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.js.map +1 -1
- package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.tsx +2 -1
- package/dist/components/core/list/columns/relations/SolidRelationOneToManyColumn.d.ts.map +1 -1
- package/dist/components/core/list/columns/relations/SolidRelationOneToManyColumn.js +4 -1
- package/dist/components/core/list/columns/relations/SolidRelationOneToManyColumn.js.map +1 -1
- package/dist/components/core/list/columns/relations/SolidRelationOneToManyColumn.tsx +5 -2
- package/dist/components/core/locales/SolidLocale.d.ts.map +1 -1
- package/dist/components/core/locales/SolidLocale.js +1 -1
- package/dist/components/core/locales/SolidLocale.js.map +1 -1
- package/dist/components/core/locales/SolidLocale.tsx +1 -0
- package/dist/components/core/model/CreateModel.css +26 -0
- package/dist/components/core/model/FieldMetaData.d.ts.map +1 -1
- package/dist/components/core/model/FieldMetaData.js +21 -3
- package/dist/components/core/model/FieldMetaData.js.map +1 -1
- package/dist/components/core/model/FieldMetaData.tsx +45 -4
- package/dist/components/core/model/FieldMetaDataForm.css +82 -0
- package/dist/components/core/model/FieldMetaDataForm.d.ts +1 -0
- package/dist/components/core/model/FieldMetaDataForm.d.ts.map +1 -1
- package/dist/components/core/model/FieldMetaDataForm.js +246 -235
- package/dist/components/core/model/FieldMetaDataForm.js.map +1 -1
- package/dist/components/core/model/FieldMetaDataForm.tsx +46 -34
- package/dist/components/core/model/FieldSelector.js +1 -1
- package/dist/components/core/model/FieldSelector.js.map +1 -1
- package/dist/components/core/model/FieldSelector.tsx +1 -1
- package/dist/components/core/model/ModelMetaData.js +1 -1
- package/dist/components/core/model/ModelMetaData.js.map +1 -1
- package/dist/components/core/model/ModelMetaData.tsx +1 -1
- package/dist/components/core/module/CreateModule.css +55 -2
- package/dist/components/core/module/CreateModule.d.ts.map +1 -1
- package/dist/components/core/module/CreateModule.js +2 -2
- package/dist/components/core/module/CreateModule.js.map +1 -1
- package/dist/components/core/module/CreateModule.tsx +3 -2
- package/dist/components/core/module/ModuleMetadataExplorer.css +27 -0
- package/dist/components/core/module/ModuleMetadataExplorer.d.ts.map +1 -1
- package/dist/components/core/module/ModuleMetadataExplorer.js +1 -1
- package/dist/components/core/module/ModuleMetadataExplorer.js.map +1 -1
- package/dist/components/core/module/ModuleMetadataExplorer.tsx +1 -0
- package/dist/components/core/tree/SolidTreeTable.d.ts +2 -1
- package/dist/components/core/tree/SolidTreeTable.d.ts.map +1 -1
- package/dist/components/core/tree/SolidTreeTable.js +2 -2
- package/dist/components/core/tree/SolidTreeTable.js.map +1 -1
- package/dist/components/core/tree/SolidTreeTable.tsx +3 -1
- package/dist/components/core/tree/SolidTreeView.d.ts.map +1 -1
- package/dist/components/core/tree/SolidTreeView.js +44 -16
- package/dist/components/core/tree/SolidTreeView.js.map +1 -1
- package/dist/components/core/tree/SolidTreeView.tsx +55 -24
- package/dist/components/core/users/CreateUser.d.ts.map +1 -1
- package/dist/components/core/users/CreateUser.js +45 -32
- package/dist/components/core/users/CreateUser.js.map +1 -1
- package/dist/components/core/users/CreateUser.tsx +15 -4
- package/dist/components/layout/AdminHeaderActions.d.ts.map +1 -1
- package/dist/components/layout/AdminHeaderActions.js +10 -9
- package/dist/components/layout/AdminHeaderActions.js.map +1 -1
- package/dist/components/layout/AdminHeaderActions.tsx +2 -0
- package/dist/components/layout/AppSidebar.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.js +2 -1
- package/dist/components/layout/AppSidebar.js.map +1 -1
- package/dist/components/layout/AppSidebar.tsx +12 -5
- package/dist/components/layout/SolidMenuItemIcon.d.ts +9 -0
- package/dist/components/layout/SolidMenuItemIcon.d.ts.map +1 -0
- package/dist/components/layout/SolidMenuItemIcon.js +20 -0
- package/dist/components/layout/SolidMenuItemIcon.js.map +1 -0
- package/dist/components/layout/SolidMenuItemIcon.tsx +45 -0
- package/dist/components/layout/navbar-two-menu.d.ts.map +1 -1
- package/dist/components/layout/navbar-two-menu.js +6 -7
- package/dist/components/layout/navbar-two-menu.js.map +1 -1
- package/dist/components/layout/navbar-two-menu.tsx +4 -12
- package/dist/components/shad-cn-ui/SolidCodeEditor.d.ts.map +1 -1
- package/dist/components/shad-cn-ui/SolidCodeEditor.js +30 -2
- package/dist/components/shad-cn-ui/SolidCodeEditor.js.map +1 -1
- package/dist/components/shad-cn-ui/SolidCodeEditor.tsx +38 -1
- package/dist/helpers/backendHealthMonitor.d.ts +39 -0
- package/dist/helpers/backendHealthMonitor.d.ts.map +1 -0
- package/dist/helpers/backendHealthMonitor.js +255 -0
- package/dist/helpers/backendHealthMonitor.js.map +1 -0
- package/dist/helpers/backendHealthMonitor.ts +177 -0
- package/dist/helpers/helpers.d.ts +1 -0
- package/dist/helpers/helpers.d.ts.map +1 -1
- package/dist/helpers/helpers.js +9 -0
- package/dist/helpers/helpers.js.map +1 -1
- package/dist/helpers/helpers.ts +11 -1
- package/dist/helpers/menuItemIcons.d.ts +34 -0
- package/dist/helpers/menuItemIcons.d.ts.map +1 -0
- package/dist/helpers/menuItemIcons.js +144 -0
- package/dist/helpers/menuItemIcons.js.map +1 -0
- package/dist/helpers/menuItemIcons.ts +191 -0
- package/dist/helpers/relationDisplay.d.ts +3 -0
- package/dist/helpers/relationDisplay.d.ts.map +1 -0
- package/dist/helpers/relationDisplay.js +92 -0
- package/dist/helpers/relationDisplay.js.map +1 -0
- package/dist/helpers/relationDisplay.ts +101 -0
- package/dist/helpers/settingsPayload.d.ts +1 -0
- package/dist/helpers/settingsPayload.d.ts.map +1 -1
- package/dist/helpers/settingsPayload.js.map +1 -1
- package/dist/helpers/settingsPayload.ts +1 -0
- package/dist/helpers/solidIcons.d.ts +2 -0
- package/dist/helpers/solidIcons.d.ts.map +1 -1
- package/dist/helpers/solidIcons.js +8 -0
- package/dist/helpers/solidIcons.js.map +1 -1
- package/dist/helpers/solidIcons.tsx +10 -1
- package/dist/helpers/waitForBackendAvailability.d.ts +1 -0
- package/dist/helpers/waitForBackendAvailability.d.ts.map +1 -1
- package/dist/helpers/waitForBackendAvailability.js +35 -22
- package/dist/helpers/waitForBackendAvailability.js.map +1 -1
- package/dist/helpers/waitForBackendAvailability.ts +18 -12
- package/dist/http/solidHttp.d.ts.map +1 -1
- package/dist/http/solidHttp.js +10 -4
- package/dist/http/solidHttp.js.map +1 -1
- package/dist/http/solidHttp.ts +9 -4
- package/dist/redux/api/datasourceIntrospectionApi.d.ts +4 -0
- package/dist/redux/api/datasourceIntrospectionApi.d.ts.map +1 -1
- package/dist/redux/api/datasourceIntrospectionApi.js.map +1 -1
- package/dist/redux/api/datasourceIntrospectionApi.ts +4 -0
- package/dist/redux/api/fetchBaseQuery.d.ts.map +1 -1
- package/dist/redux/api/fetchBaseQuery.js +9 -3
- package/dist/redux/api/fetchBaseQuery.js.map +1 -1
- package/dist/redux/api/fetchBaseQuery.tsx +9 -3
- package/dist/redux/store/StoreProvider.d.ts.map +1 -1
- package/dist/redux/store/StoreProvider.js +4 -3
- package/dist/redux/store/StoreProvider.js.map +1 -1
- package/dist/redux/store/StoreProvider.tsx +4 -3
- package/dist/redux/store/defaultStoreConfig.d.ts +5 -5
- package/dist/resources/globals.css +341 -49
- package/dist/resources/solid-custom.css +464 -11
- package/dist/resources/solid-responsive.css +31 -20
- package/dist/resources/solid-tailwind-generated.css +1 -1
- package/dist/routes/AppEventListener.d.ts.map +1 -1
- package/dist/routes/AppEventListener.js +23 -0
- package/dist/routes/AppEventListener.js.map +1 -1
- package/dist/routes/AppEventListener.tsx +29 -0
- package/dist/routes/pages/admin/core/DashboardPage.module.css +12 -0
- package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.css +306 -3
- package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.d.ts.map +1 -1
- package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.js +745 -393
- package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.js.map +1 -1
- package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.tsx +1074 -420
- package/dist/routes/pages/admin/core/DatasourcesPage.css +15 -0
- package/dist/routes/pages/admin/core/DatasourcesPage.d.ts.map +1 -1
- package/dist/routes/pages/admin/core/DatasourcesPage.js +7 -188
- package/dist/routes/pages/admin/core/DatasourcesPage.js.map +1 -1
- package/dist/routes/pages/admin/core/DatasourcesPage.tsx +3 -393
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
ArrowLeft,
|
|
3
3
|
Check,
|
|
4
4
|
Cog,
|
|
5
|
+
Copy,
|
|
5
6
|
Database,
|
|
6
7
|
ExternalLink,
|
|
7
8
|
RefreshCw,
|
|
@@ -44,16 +45,16 @@ import {
|
|
|
44
45
|
type DatasourceIntrospectionTableRecord,
|
|
45
46
|
type DatasourceIntrospectionApplyResponse,
|
|
46
47
|
type DatasourceIntrospectionCreateMigrationResponse,
|
|
48
|
+
datasourceIntrospectionApi,
|
|
47
49
|
useApplyDatasourceIntrospectionMappingMutation,
|
|
48
50
|
useCreateDatasourceIntrospectionMigrationArtifactsMutation,
|
|
49
51
|
useGenerateDatasourceIntrospectionCodeMutation,
|
|
50
52
|
useGetDatasourceIntrospectionBootstrapQuery,
|
|
51
|
-
useGetDatasourceIntrospectionTableDetailQuery,
|
|
52
53
|
useGetDatasourceIntrospectionTablesQuery,
|
|
53
54
|
usePreviewDatasourceIntrospectionMappingMutation,
|
|
54
55
|
useRunDatasourceIntrospectionMigrationMutation,
|
|
55
56
|
} from "../../../../redux/api/datasourceIntrospectionApi";
|
|
56
|
-
import {
|
|
57
|
+
import { modelsApi } from "../../../../redux/api/modelApi";
|
|
57
58
|
import "./DatasourceIntrospectionPage.css";
|
|
58
59
|
|
|
59
60
|
type SessionSettings = {
|
|
@@ -61,6 +62,13 @@ type SessionSettings = {
|
|
|
61
62
|
prioritizeUnmappedTables: boolean;
|
|
62
63
|
};
|
|
63
64
|
|
|
65
|
+
type BatchWorkspaceItemState = "idle" | "loading" | "ready" | "success" | "error";
|
|
66
|
+
|
|
67
|
+
type BatchWorkspaceItemStatus = {
|
|
68
|
+
state: BatchWorkspaceItemState;
|
|
69
|
+
message: string;
|
|
70
|
+
};
|
|
71
|
+
|
|
64
72
|
type WorkspaceStep =
|
|
65
73
|
| "configure"
|
|
66
74
|
| "review-json"
|
|
@@ -111,10 +119,34 @@ type DrawerDraft = {
|
|
|
111
119
|
enableSoftDelete: boolean;
|
|
112
120
|
draftPublishWorkflow: boolean;
|
|
113
121
|
internationalisation: boolean;
|
|
122
|
+
hasPrimaryKey: boolean;
|
|
123
|
+
primaryKeyColumnCount: number;
|
|
124
|
+
primaryKeyColumnNames: string[];
|
|
125
|
+
mappingBlockedReason: string | null;
|
|
114
126
|
columns: DrawerColumnDraft[];
|
|
115
127
|
preservedFields: any[];
|
|
116
128
|
};
|
|
117
129
|
|
|
130
|
+
type WorkspaceBatchItem = {
|
|
131
|
+
key: string;
|
|
132
|
+
record: DatasourceIntrospectionTableRecord;
|
|
133
|
+
detail: DatasourceIntrospectionTableDetail | null;
|
|
134
|
+
mappedModelData: any | null;
|
|
135
|
+
draft: DrawerDraft | null;
|
|
136
|
+
loadStatus: BatchWorkspaceItemStatus;
|
|
137
|
+
reviewStatus: BatchWorkspaceItemStatus;
|
|
138
|
+
saveStatus: BatchWorkspaceItemStatus;
|
|
139
|
+
migrationStatus: BatchWorkspaceItemStatus;
|
|
140
|
+
mappingPreview: DatasourceIntrospectionMappingPreview | null;
|
|
141
|
+
persistedMappingResult: DatasourceIntrospectionApplyResponse | null;
|
|
142
|
+
migrationArtifactsResult: DatasourceIntrospectionCreateMigrationResponse | null;
|
|
143
|
+
reviewedMetadataJsonValue: any | null;
|
|
144
|
+
metadataJsonText: string;
|
|
145
|
+
metadataJsonValidationError: string | null;
|
|
146
|
+
metadataJsonResetToken: string;
|
|
147
|
+
error: string | null;
|
|
148
|
+
};
|
|
149
|
+
|
|
118
150
|
function providerLabel(type: string) {
|
|
119
151
|
if (type === "postgres") return "PostgreSQL";
|
|
120
152
|
if (type === "mysql") return "MySQL";
|
|
@@ -139,7 +171,9 @@ const scalarFieldTypeOptions = [
|
|
|
139
171
|
{ value: "password", label: "Password" },
|
|
140
172
|
];
|
|
141
173
|
|
|
142
|
-
|
|
174
|
+
function getFieldTypeLabel(type: string) {
|
|
175
|
+
return scalarFieldTypeOptions.find((option) => option.value === type)?.label ?? startCase(type);
|
|
176
|
+
}
|
|
143
177
|
const mappingStrategyOptions = [
|
|
144
178
|
{ value: "generated_id", label: "Legacy generated id" },
|
|
145
179
|
{ value: "existing_id", label: "Legacy existing id" },
|
|
@@ -242,6 +276,10 @@ function getDefaultOrmTypeForSolidFieldType(dataSourceType: string, solidFieldTy
|
|
|
242
276
|
}
|
|
243
277
|
|
|
244
278
|
function statusLabel(record: DatasourceIntrospectionTableRecord) {
|
|
279
|
+
if (!record.hasPrimaryKey) {
|
|
280
|
+
return "Blocked";
|
|
281
|
+
}
|
|
282
|
+
|
|
245
283
|
return record.mapped ? "Mapped" : "Ready to map";
|
|
246
284
|
}
|
|
247
285
|
|
|
@@ -351,7 +389,13 @@ function buildDrawerDraft(
|
|
|
351
389
|
};
|
|
352
390
|
});
|
|
353
391
|
|
|
354
|
-
const
|
|
392
|
+
const requestedUserKeyField = modelUserKeyField || tableDetail.suggestedMetadata.userKeyField;
|
|
393
|
+
const resolvedUserKeyField = (
|
|
394
|
+
columns.find((column) => column.fieldName === requestedUserKeyField)
|
|
395
|
+
|| columns.find((column) => column.columnName === requestedUserKeyField)
|
|
396
|
+
|| columns.find((column) => !column.handledBySuperclass && column.isPrimaryKey)
|
|
397
|
+
|| columns.find((column) => !column.handledBySuperclass && column.include)
|
|
398
|
+
)?.fieldName ?? requestedUserKeyField;
|
|
355
399
|
|
|
356
400
|
return {
|
|
357
401
|
mapped: selectedTable.mapped,
|
|
@@ -367,11 +411,15 @@ function buildDrawerDraft(
|
|
|
367
411
|
dataSourceType: modelData?.dataSourceType ?? tableDetail.suggestedMetadata.dataSourceType,
|
|
368
412
|
legacyTableType: modelData?.legacyTableType ?? tableDetail.suggestedMetadata.legacyTableType,
|
|
369
413
|
baseClassName: tableDetail.suggestedMetadata.baseClassName,
|
|
370
|
-
userKeyField:
|
|
414
|
+
userKeyField: resolvedUserKeyField,
|
|
371
415
|
enableAuditTracking: modelData?.enableAuditTracking ?? true,
|
|
372
416
|
enableSoftDelete: modelData?.enableSoftDelete ?? false,
|
|
373
417
|
draftPublishWorkflow: modelData?.draftPublishWorkflow ?? false,
|
|
374
418
|
internationalisation: modelData?.internationalisation ?? false,
|
|
419
|
+
hasPrimaryKey: selectedTable.hasPrimaryKey,
|
|
420
|
+
primaryKeyColumnCount: selectedTable.primaryKeyColumnCount,
|
|
421
|
+
primaryKeyColumnNames: selectedTable.primaryKeyColumnNames,
|
|
422
|
+
mappingBlockedReason: selectedTable.mappingBlockedReason,
|
|
375
423
|
columns,
|
|
376
424
|
preservedFields,
|
|
377
425
|
};
|
|
@@ -395,6 +443,13 @@ function getDrawerValidationErrors(draft: DrawerDraft) {
|
|
|
395
443
|
errors.push("Select at least one physical column to include in the mapping.");
|
|
396
444
|
}
|
|
397
445
|
|
|
446
|
+
if (!draft.hasPrimaryKey) {
|
|
447
|
+
errors.push(
|
|
448
|
+
draft.mappingBlockedReason
|
|
449
|
+
|| `Table "${draft.tableName}" cannot be mapped because the datasource table does not define any primary key.`,
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
398
453
|
for (const column of includedColumns) {
|
|
399
454
|
const normalizedFieldName = column.fieldName.trim();
|
|
400
455
|
if (!normalizedFieldName) {
|
|
@@ -407,8 +462,8 @@ function getDrawerValidationErrors(draft: DrawerDraft) {
|
|
|
407
462
|
continue;
|
|
408
463
|
}
|
|
409
464
|
|
|
410
|
-
if (!
|
|
411
|
-
errors.push(`SolidX type
|
|
465
|
+
if (!column.solidFieldType.trim()) {
|
|
466
|
+
errors.push(`SolidX type is required for column "${column.columnName}".`);
|
|
412
467
|
continue;
|
|
413
468
|
}
|
|
414
469
|
|
|
@@ -423,17 +478,26 @@ function getDrawerValidationErrors(draft: DrawerDraft) {
|
|
|
423
478
|
if (!draft.userKeyField.trim()) {
|
|
424
479
|
errors.push("Choose a user key field before saving the mapping.");
|
|
425
480
|
} else {
|
|
426
|
-
const userKeyExists =
|
|
481
|
+
const userKeyExists = draft.columns.some((column) => (
|
|
482
|
+
column.fieldName === draft.userKeyField
|
|
483
|
+
&& (column.handledBySuperclass || column.include)
|
|
484
|
+
));
|
|
427
485
|
if (!userKeyExists) {
|
|
428
486
|
errors.push("The chosen user key field must remain part of the mapped columns.");
|
|
429
487
|
}
|
|
430
488
|
}
|
|
431
489
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
490
|
+
const includedPrimaryKeyColumns = includedColumns.filter((column) => column.isPrimaryKey);
|
|
491
|
+
if (draft.hasPrimaryKey && !includedPrimaryKeyColumns.length) {
|
|
492
|
+
errors.push("Keep the datasource primary key columns included in the mapping.");
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const missingDetectedPrimaryKeyColumns = draft.columns
|
|
496
|
+
.filter((column) => column.isDetectedPrimaryKey && !column.handledBySuperclass)
|
|
497
|
+
.filter((column) => !column.include || !column.isPrimaryKey)
|
|
498
|
+
.map((column) => column.columnName);
|
|
499
|
+
if (missingDetectedPrimaryKeyColumns.length) {
|
|
500
|
+
errors.push(`Keep all datasource primary key columns mapped and marked as primary key: ${missingDetectedPrimaryKeyColumns.join(", ")}.`);
|
|
437
501
|
}
|
|
438
502
|
|
|
439
503
|
return errors;
|
|
@@ -616,23 +680,90 @@ function parseAndValidateReviewedModelJson(
|
|
|
616
680
|
seenFieldNames.add(normalizedFieldName);
|
|
617
681
|
}
|
|
618
682
|
|
|
619
|
-
|
|
683
|
+
const expectedUserKeyFieldName = `${parsed.userKeyFieldUserKey ?? draft.userKeyField ?? ""}`.trim();
|
|
684
|
+
const resolvedUserKeyFieldName = resolveReviewedUserKeyFieldName(
|
|
685
|
+
parsed.fields,
|
|
686
|
+
expectedUserKeyFieldName,
|
|
687
|
+
draft.userKeyField,
|
|
688
|
+
);
|
|
689
|
+
const inheritedGeneratedIdUserKey =
|
|
690
|
+
draft.legacyTableType === "generated_id" && expectedUserKeyFieldName.toLowerCase() === "id";
|
|
691
|
+
|
|
692
|
+
if (!resolvedUserKeyFieldName && !inheritedGeneratedIdUserKey) {
|
|
620
693
|
return {
|
|
621
694
|
error: "Metadata JSON must keep userKeyFieldUserKey present in the fields array.",
|
|
622
695
|
parsed: null,
|
|
623
696
|
};
|
|
624
697
|
}
|
|
625
698
|
|
|
699
|
+
if (draft.hasPrimaryKey) {
|
|
700
|
+
const activeFields = parsed.fields.filter((field: any) => !field?.isMarkedForRemoval);
|
|
701
|
+
const missingPrimaryKeyColumns = draft.columns
|
|
702
|
+
.filter((column) => column.isDetectedPrimaryKey && !column.handledBySuperclass)
|
|
703
|
+
.filter((column) => {
|
|
704
|
+
const matchingField = activeFields.find((field: any) =>
|
|
705
|
+
`${field?.columnName ?? ""}`.trim().toLowerCase() === column.columnName.trim().toLowerCase(),
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
return !matchingField || matchingField.isPrimaryKey !== true;
|
|
709
|
+
})
|
|
710
|
+
.map((column) => column.columnName);
|
|
711
|
+
|
|
712
|
+
if (missingPrimaryKeyColumns.length) {
|
|
713
|
+
return {
|
|
714
|
+
error: `Metadata JSON must keep all datasource primary key columns marked with isPrimaryKey: true: ${missingPrimaryKeyColumns.join(", ")}.`,
|
|
715
|
+
parsed: null,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
parsed.userKeyFieldUserKey = resolvedUserKeyFieldName ?? expectedUserKeyFieldName;
|
|
721
|
+
|
|
626
722
|
return {
|
|
627
723
|
error: null,
|
|
628
724
|
parsed,
|
|
629
725
|
};
|
|
630
726
|
}
|
|
631
727
|
|
|
728
|
+
function resolveReviewedUserKeyFieldName(
|
|
729
|
+
reviewedFields: Array<Record<string, any>>,
|
|
730
|
+
requestedUserKeyFieldName: string,
|
|
731
|
+
fallbackUserKeyFieldName?: string,
|
|
732
|
+
) {
|
|
733
|
+
const activeFields = reviewedFields.filter((field) => !field?.isMarkedForRemoval);
|
|
734
|
+
const normalizedRequested = `${requestedUserKeyFieldName ?? ""}`.trim().toLowerCase();
|
|
735
|
+
const normalizedFallback = `${fallbackUserKeyFieldName ?? ""}`.trim().toLowerCase();
|
|
736
|
+
|
|
737
|
+
const byExactRequested = activeFields.find((field) => field?.name?.toLowerCase?.() === normalizedRequested);
|
|
738
|
+
if (byExactRequested?.name) {
|
|
739
|
+
return byExactRequested.name;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const byExistingUserKeyFlag = activeFields.find((field) => field?.isUserKey);
|
|
743
|
+
if (byExistingUserKeyFlag?.name) {
|
|
744
|
+
return byExistingUserKeyFlag.name;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const byPrimaryKeyFallback = activeFields.find((field) => {
|
|
748
|
+
if (!field?.isPrimaryKey) {
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const normalizedName = `${field?.name ?? ""}`.trim().toLowerCase();
|
|
753
|
+
return normalizedName === normalizedFallback || normalizedName === "id" || normalizedName === "legacyid";
|
|
754
|
+
});
|
|
755
|
+
if (byPrimaryKeyFallback?.name) {
|
|
756
|
+
return byPrimaryKeyFallback.name;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const byFallbackFieldName = activeFields.find((field) => field?.name?.toLowerCase?.() === normalizedFallback);
|
|
760
|
+
return byFallbackFieldName?.name ?? null;
|
|
761
|
+
}
|
|
762
|
+
|
|
632
763
|
export function DatasourceIntrospectionPage() {
|
|
633
764
|
const params = useParams();
|
|
634
765
|
const navigate = useNavigate();
|
|
635
|
-
const dispatch = useDispatch();
|
|
766
|
+
const dispatch = useDispatch<any>();
|
|
636
767
|
const moduleId = Number(params.moduleId ?? 0);
|
|
637
768
|
const isValidModuleId = Number.isFinite(moduleId) && moduleId > 0;
|
|
638
769
|
|
|
@@ -648,36 +779,30 @@ export function DatasourceIntrospectionPage() {
|
|
|
648
779
|
|
|
649
780
|
const [selectedDatasource, setSelectedDatasource] = useState("");
|
|
650
781
|
const [selectedRows, setSelectedRows] = useState<DatasourceIntrospectionTableRecord[]>([]);
|
|
651
|
-
const [
|
|
782
|
+
const [workspaceTableKeys, setWorkspaceTableKeys] = useState<string[]>([]);
|
|
783
|
+
const [activeWorkspaceTableKey, setActiveWorkspaceTableKey] = useState("");
|
|
784
|
+
const [workspaceItems, setWorkspaceItems] = useState<Record<string, WorkspaceBatchItem>>({});
|
|
785
|
+
const workspaceRailItemRefs = useRef<Record<string, HTMLButtonElement | null>>({});
|
|
652
786
|
const [tableSearch, setTableSearch] = useState("");
|
|
653
787
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
654
788
|
const [settingsTab, setSettingsTab] = useState("datasource-info");
|
|
655
|
-
const [drawerDraft, setDrawerDraft] = useState<DrawerDraft | null>(null);
|
|
656
789
|
const [drawerTab, setDrawerTab] = useState("general-info");
|
|
657
790
|
const [workspaceStep, setWorkspaceStep] = useState<WorkspaceStep>("configure");
|
|
658
|
-
const [mappingPreview, setMappingPreview] = useState<DatasourceIntrospectionMappingPreview | null>(null);
|
|
659
|
-
const [persistedMappingResult, setPersistedMappingResult] = useState<DatasourceIntrospectionApplyResponse | null>(null);
|
|
660
|
-
const [migrationArtifactsResult, setMigrationArtifactsResult] = useState<DatasourceIntrospectionCreateMigrationResponse | null>(null);
|
|
661
791
|
const [generateCodeOutput, setGenerateCodeOutput] = useState("");
|
|
662
792
|
const [isGeneratingCodeWorkflow, setIsGeneratingCodeWorkflow] = useState(false);
|
|
663
793
|
const [generateCodeStatusMessage, setGenerateCodeStatusMessage] = useState("");
|
|
664
794
|
const [generateCodeTimeoutMessage, setGenerateCodeTimeoutMessage] = useState<string | null>(null);
|
|
665
795
|
const [generateCodeReady, setGenerateCodeReady] = useState(false);
|
|
666
796
|
const [hasAttemptedGenerateCode, setHasAttemptedGenerateCode] = useState(false);
|
|
797
|
+
const [migrationPreviewCopied, setMigrationPreviewCopied] = useState(false);
|
|
667
798
|
const [runMigrationOutput, setRunMigrationOutput] = useState("");
|
|
668
799
|
const [runMigrationCompleted, setRunMigrationCompleted] = useState(false);
|
|
669
|
-
const [reviewedMetadataJsonValue, setReviewedMetadataJsonValue] = useState<any>(null);
|
|
670
|
-
const [metadataJsonText, setMetadataJsonText] = useState("");
|
|
671
|
-
const [metadataJsonValidationError, setMetadataJsonValidationError] = useState<string | null>(null);
|
|
672
|
-
const [metadataJsonResetToken, setMetadataJsonResetToken] = useState("initial");
|
|
673
|
-
const [drawerBootstrapping, setDrawerBootstrapping] = useState(false);
|
|
674
800
|
const [fieldConfigColumnName, setFieldConfigColumnName] = useState<string | null>(null);
|
|
675
801
|
const [sessionSettings, setSessionSettings] = useState<SessionSettings>({
|
|
676
802
|
relationInferenceEnabled: false,
|
|
677
803
|
prioritizeUnmappedTables: true,
|
|
678
804
|
});
|
|
679
805
|
const [draftSessionSettings, setDraftSessionSettings] = useState<SessionSettings>(sessionSettings);
|
|
680
|
-
const drawerBootstrapTimerRef = useRef<number | null>(null);
|
|
681
806
|
|
|
682
807
|
const [
|
|
683
808
|
previewMapping,
|
|
@@ -725,14 +850,12 @@ export function DatasourceIntrospectionPage() {
|
|
|
725
850
|
|
|
726
851
|
useEffect(() => {
|
|
727
852
|
setSelectedRows([]);
|
|
728
|
-
|
|
729
|
-
|
|
853
|
+
setWorkspaceTableKeys([]);
|
|
854
|
+
setActiveWorkspaceTableKey("");
|
|
855
|
+
setWorkspaceItems({});
|
|
730
856
|
setFieldConfigColumnName(null);
|
|
731
857
|
setTableSearch("");
|
|
732
858
|
setWorkspaceStep("configure");
|
|
733
|
-
setMappingPreview(null);
|
|
734
|
-
setPersistedMappingResult(null);
|
|
735
|
-
setMigrationArtifactsResult(null);
|
|
736
859
|
setGenerateCodeOutput("");
|
|
737
860
|
setIsGeneratingCodeWorkflow(false);
|
|
738
861
|
setGenerateCodeStatusMessage("");
|
|
@@ -741,17 +864,35 @@ export function DatasourceIntrospectionPage() {
|
|
|
741
864
|
setHasAttemptedGenerateCode(false);
|
|
742
865
|
setRunMigrationOutput("");
|
|
743
866
|
setRunMigrationCompleted(false);
|
|
744
|
-
setReviewedMetadataJsonValue(null);
|
|
745
|
-
setMetadataJsonText("");
|
|
746
|
-
setMetadataJsonValidationError(null);
|
|
747
|
-
setMetadataJsonResetToken(`datasource-${Date.now()}`);
|
|
748
867
|
}, [selectedDatasource]);
|
|
749
868
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
869
|
+
const activeWorkspaceProcessingKey = useMemo(
|
|
870
|
+
() =>
|
|
871
|
+
workspaceTableKeys.find((key) => {
|
|
872
|
+
const item = workspaceItems[key];
|
|
873
|
+
if (!item) return false;
|
|
874
|
+
|
|
875
|
+
return [
|
|
876
|
+
item.loadStatus.state,
|
|
877
|
+
item.reviewStatus.state,
|
|
878
|
+
item.saveStatus.state,
|
|
879
|
+
item.migrationStatus.state,
|
|
880
|
+
].includes("loading");
|
|
881
|
+
}) ?? null,
|
|
882
|
+
[workspaceItems, workspaceTableKeys],
|
|
883
|
+
);
|
|
884
|
+
|
|
885
|
+
useEffect(() => {
|
|
886
|
+
const targetKey = activeWorkspaceProcessingKey ?? activeWorkspaceTableKey;
|
|
887
|
+
if (!targetKey) return;
|
|
888
|
+
|
|
889
|
+
const targetNode = workspaceRailItemRefs.current[targetKey];
|
|
890
|
+
targetNode?.scrollIntoView({
|
|
891
|
+
block: "nearest",
|
|
892
|
+
inline: "nearest",
|
|
893
|
+
behavior: "smooth",
|
|
894
|
+
});
|
|
895
|
+
}, [activeWorkspaceProcessingKey, activeWorkspaceTableKey]);
|
|
755
896
|
|
|
756
897
|
const datasourceOptions = useMemo(
|
|
757
898
|
() => (bootstrap?.datasources ?? []).map((datasource) => ({
|
|
@@ -773,45 +914,27 @@ export function DatasourceIntrospectionPage() {
|
|
|
773
914
|
skip: !isValidModuleId || !selectedDatasource,
|
|
774
915
|
},
|
|
775
916
|
);
|
|
776
|
-
|
|
777
|
-
const
|
|
778
|
-
const
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
() => tablesResponse?.records?.find((record) => `${record.schema ?? ""}::${record.tableName}` === workspaceTableKey) ?? null,
|
|
783
|
-
[tablesResponse, workspaceTableKey],
|
|
917
|
+
const selectedDatasourceRecord = bootstrap?.datasources?.find((datasource) => datasource.name === selectedDatasource) ?? null;
|
|
918
|
+
const isSynchronizeBlocked = Boolean(selectedDatasource && tablesResponse?.synchronizeBlocked);
|
|
919
|
+
const makeTableKey = (schema: string | null | undefined, tableName: string) => `${schema ?? ""}::${tableName}`;
|
|
920
|
+
const selectedListRowKeySet = useMemo(
|
|
921
|
+
() => new Set(selectedRows.map((row) => makeTableKey(row.schema, row.tableName))),
|
|
922
|
+
[selectedRows],
|
|
784
923
|
);
|
|
785
|
-
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
isFetching: isTableDetailFetching,
|
|
790
|
-
error: tableDetailError,
|
|
791
|
-
refetch: refetchTableDetail,
|
|
792
|
-
} = useGetDatasourceIntrospectionTableDetailQuery(
|
|
793
|
-
{
|
|
794
|
-
moduleId,
|
|
795
|
-
datasource: selectedDatasource,
|
|
796
|
-
table: selectedTable?.tableName ?? "",
|
|
797
|
-
schema: selectedTable?.schema ?? undefined,
|
|
798
|
-
},
|
|
799
|
-
{
|
|
800
|
-
skip: !isWorkspaceMode || !isValidModuleId || !selectedDatasource || !selectedTable || !!tablesResponse?.synchronizeBlocked,
|
|
801
|
-
},
|
|
924
|
+
const isWorkspaceMode = workspaceTableKeys.length > 0 && Boolean(activeWorkspaceTableKey);
|
|
925
|
+
const selectedTable = useMemo(
|
|
926
|
+
() => workspaceItems[activeWorkspaceTableKey]?.record ?? null,
|
|
927
|
+
[activeWorkspaceTableKey, workspaceItems],
|
|
802
928
|
);
|
|
803
|
-
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
const mappedModelData = mappedModelResponse?.data;
|
|
814
|
-
const selectedDatasourceRecord = bootstrap?.datasources?.find((datasource) => datasource.name === selectedDatasource) ?? null;
|
|
929
|
+
const activeWorkspaceItem = activeWorkspaceTableKey ? workspaceItems[activeWorkspaceTableKey] ?? null : null;
|
|
930
|
+
const drawerDraft = activeWorkspaceItem?.draft ?? null;
|
|
931
|
+
const mappingPreview = activeWorkspaceItem?.mappingPreview ?? null;
|
|
932
|
+
const persistedMappingResult = activeWorkspaceItem?.persistedMappingResult ?? null;
|
|
933
|
+
const migrationArtifactsResult = activeWorkspaceItem?.migrationArtifactsResult ?? null;
|
|
934
|
+
const reviewedMetadataJsonValue = activeWorkspaceItem?.reviewedMetadataJsonValue ?? null;
|
|
935
|
+
const metadataJsonText = activeWorkspaceItem?.metadataJsonText ?? "";
|
|
936
|
+
const metadataJsonValidationError = activeWorkspaceItem?.metadataJsonValidationError ?? null;
|
|
937
|
+
const metadataJsonResetToken = activeWorkspaceItem?.metadataJsonResetToken ?? "initial";
|
|
815
938
|
const activeFieldConfigColumn = drawerDraft?.columns.find((column) => column.columnName === fieldConfigColumnName) ?? null;
|
|
816
939
|
const activeFieldConfigMetaData = useMemo(() => {
|
|
817
940
|
if (!drawerDraft || !activeFieldConfigColumn) {
|
|
@@ -864,76 +987,12 @@ export function DatasourceIntrospectionPage() {
|
|
|
864
987
|
columnName: column.columnName,
|
|
865
988
|
}));
|
|
866
989
|
}, [activeFieldConfigColumn, drawerDraft]);
|
|
867
|
-
const drawerLoadingError =
|
|
868
|
-
const isDrawerBlockingLoading =
|
|
869
|
-
selectedTable
|
|
870
|
-
&& (
|
|
871
|
-
drawerBootstrapping
|
|
872
|
-
|| !drawerDraft
|
|
873
|
-
|| isTableDetailLoading
|
|
874
|
-
|| isTableDetailFetching
|
|
875
|
-
|| (selectedTable.mapped && (isMappedModelLoading || isMappedModelFetching))
|
|
876
|
-
),
|
|
877
|
-
);
|
|
878
|
-
|
|
879
|
-
useEffect(() => {
|
|
880
|
-
if (!isWorkspaceMode) {
|
|
881
|
-
setDrawerDraft(null);
|
|
882
|
-
setFieldConfigColumnName(null);
|
|
883
|
-
return;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (drawerBootstrapTimerRef.current) {
|
|
887
|
-
window.clearTimeout(drawerBootstrapTimerRef.current);
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
setDrawerDraft(null);
|
|
891
|
-
setFieldConfigColumnName(null);
|
|
892
|
-
setDrawerTab("general-info");
|
|
893
|
-
setWorkspaceStep("configure");
|
|
894
|
-
setMappingPreview(null);
|
|
895
|
-
setPersistedMappingResult(null);
|
|
896
|
-
setMigrationArtifactsResult(null);
|
|
897
|
-
setGenerateCodeOutput("");
|
|
898
|
-
setIsGeneratingCodeWorkflow(false);
|
|
899
|
-
setGenerateCodeStatusMessage("");
|
|
900
|
-
setGenerateCodeTimeoutMessage(null);
|
|
901
|
-
setGenerateCodeReady(false);
|
|
902
|
-
setHasAttemptedGenerateCode(false);
|
|
903
|
-
setRunMigrationOutput("");
|
|
904
|
-
setRunMigrationCompleted(false);
|
|
905
|
-
setReviewedMetadataJsonValue(null);
|
|
906
|
-
setMetadataJsonText("");
|
|
907
|
-
setMetadataJsonValidationError(null);
|
|
908
|
-
setMetadataJsonResetToken(`table-${workspaceTableKey}-${Date.now()}`);
|
|
909
|
-
setDrawerBootstrapping(true);
|
|
910
|
-
}, [isWorkspaceMode, workspaceTableKey]);
|
|
911
|
-
|
|
912
|
-
useEffect(() => {
|
|
913
|
-
if (!isWorkspaceMode || !selectedTable) return;
|
|
914
|
-
setSelectedRows([selectedTable]);
|
|
915
|
-
}, [isWorkspaceMode, selectedTable]);
|
|
916
|
-
|
|
917
|
-
useEffect(() => {
|
|
918
|
-
if (!isWorkspaceMode || !selectedTable || !tableDetail) return;
|
|
919
|
-
if (selectedTable.mapped && (!mappedModelData || mappedModelData.id !== selectedTable.mappedModelId)) return;
|
|
920
|
-
|
|
921
|
-
if (drawerBootstrapTimerRef.current) {
|
|
922
|
-
window.clearTimeout(drawerBootstrapTimerRef.current);
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
setDrawerDraft(buildDrawerDraft(
|
|
926
|
-
tableDetail,
|
|
927
|
-
selectedTable,
|
|
928
|
-
selectedTable.mapped ? mappedModelResponse : undefined,
|
|
929
|
-
));
|
|
930
|
-
drawerBootstrapTimerRef.current = window.setTimeout(() => {
|
|
931
|
-
setDrawerBootstrapping(false);
|
|
932
|
-
}, 150);
|
|
933
|
-
}, [isWorkspaceMode, selectedTable, tableDetail, mappedModelData, mappedModelResponse]);
|
|
990
|
+
const drawerLoadingError = activeWorkspaceItem?.error ?? null;
|
|
991
|
+
const isDrawerBlockingLoading = activeWorkspaceItem?.loadStatus.state === "loading" || !drawerDraft;
|
|
934
992
|
|
|
935
993
|
const filteredRecords = useMemo(() => {
|
|
936
|
-
const records = [...(tablesResponse?.records ?? [])]
|
|
994
|
+
const records = [...(tablesResponse?.records ?? [])]
|
|
995
|
+
.filter((record) => !record.tableName.toLowerCase().startsWith("ss_"));
|
|
937
996
|
const searchNeedle = tableSearch.trim().toLowerCase();
|
|
938
997
|
|
|
939
998
|
const filtered = searchNeedle
|
|
@@ -958,10 +1017,183 @@ export function DatasourceIntrospectionPage() {
|
|
|
958
1017
|
return filtered;
|
|
959
1018
|
}, [sessionSettings.prioritizeUnmappedTables, tableSearch, tablesResponse?.records]);
|
|
960
1019
|
|
|
1020
|
+
const workspaceRecords = useMemo(
|
|
1021
|
+
() => workspaceTableKeys
|
|
1022
|
+
.map((key) => workspaceItems[key]?.record)
|
|
1023
|
+
.filter(Boolean) as DatasourceIntrospectionTableRecord[],
|
|
1024
|
+
[workspaceItems, workspaceTableKeys],
|
|
1025
|
+
);
|
|
1026
|
+
const selectedRowCount = selectedRows.length;
|
|
1027
|
+
const visibleRowKeySet = useMemo(
|
|
1028
|
+
() => new Set(filteredRecords.map((row) => makeTableKey(row.schema, row.tableName))),
|
|
1029
|
+
[filteredRecords],
|
|
1030
|
+
);
|
|
1031
|
+
const visibleSelectedRowCount = useMemo(
|
|
1032
|
+
() => selectedRows.filter((row) => row.hasPrimaryKey && visibleRowKeySet.has(makeTableKey(row.schema, row.tableName))).length,
|
|
1033
|
+
[selectedRows, visibleRowKeySet],
|
|
1034
|
+
);
|
|
1035
|
+
const visibleSelectableRowCount = useMemo(
|
|
1036
|
+
() => filteredRecords.filter((row) => row.hasPrimaryKey).length,
|
|
1037
|
+
[filteredRecords],
|
|
1038
|
+
);
|
|
1039
|
+
const hasVisibleRows = filteredRecords.length > 0;
|
|
1040
|
+
const allVisibleRowsSelected = visibleSelectableRowCount > 0 && visibleSelectedRowCount === visibleSelectableRowCount;
|
|
1041
|
+
const totalWorkspaceCount = workspaceTableKeys.length;
|
|
1042
|
+
const activeWorkspacePosition = activeWorkspaceTableKey
|
|
1043
|
+
? Math.max(0, workspaceTableKeys.indexOf(activeWorkspaceTableKey)) + 1
|
|
1044
|
+
: 0;
|
|
1045
|
+
|
|
1046
|
+
const allWorkspaceItemsReady = useMemo(
|
|
1047
|
+
() => workspaceTableKeys.length > 0 && workspaceTableKeys.every((key) => workspaceItems[key]?.draft),
|
|
1048
|
+
[workspaceItems, workspaceTableKeys],
|
|
1049
|
+
);
|
|
1050
|
+
const allWorkspaceItemsPreviewed = useMemo(
|
|
1051
|
+
() => workspaceTableKeys.length > 0 && workspaceTableKeys.every((key) => workspaceItems[key]?.mappingPreview),
|
|
1052
|
+
[workspaceItems, workspaceTableKeys],
|
|
1053
|
+
);
|
|
1054
|
+
const allWorkspaceItemsPersisted = useMemo(
|
|
1055
|
+
() => workspaceTableKeys.length > 0 && workspaceTableKeys.every((key) => workspaceItems[key]?.persistedMappingResult),
|
|
1056
|
+
[workspaceItems, workspaceTableKeys],
|
|
1057
|
+
);
|
|
1058
|
+
const allWorkspaceItemsMigrated = useMemo(
|
|
1059
|
+
() => workspaceTableKeys.length > 0 && workspaceTableKeys.every((key) => workspaceItems[key]?.migrationArtifactsResult),
|
|
1060
|
+
[workspaceItems, workspaceTableKeys],
|
|
1061
|
+
);
|
|
1062
|
+
const hasMigrationGenerationWork = useMemo(
|
|
1063
|
+
() => workspaceTableKeys.some((key) => Boolean(workspaceItems[key]?.mappingPreview?.migration.willGenerate)),
|
|
1064
|
+
[workspaceItems, workspaceTableKeys],
|
|
1065
|
+
);
|
|
1066
|
+
const reviewedWorkspaceCount = useMemo(
|
|
1067
|
+
() => workspaceTableKeys.filter((key) => workspaceItems[key]?.mappingPreview).length,
|
|
1068
|
+
[workspaceItems, workspaceTableKeys],
|
|
1069
|
+
);
|
|
1070
|
+
const savedWorkspaceCount = useMemo(
|
|
1071
|
+
() => workspaceTableKeys.filter((key) => workspaceItems[key]?.persistedMappingResult).length,
|
|
1072
|
+
[workspaceItems, workspaceTableKeys],
|
|
1073
|
+
);
|
|
1074
|
+
const migrationReadyWorkspaceCount = useMemo(
|
|
1075
|
+
() => workspaceTableKeys.filter((key) => workspaceItems[key]?.migrationArtifactsResult).length,
|
|
1076
|
+
[workspaceItems, workspaceTableKeys],
|
|
1077
|
+
);
|
|
1078
|
+
const reviewErrorWorkspaceCount = useMemo(
|
|
1079
|
+
() => workspaceTableKeys.filter((key) => workspaceItems[key]?.reviewStatus.state === "error").length,
|
|
1080
|
+
[workspaceItems, workspaceTableKeys],
|
|
1081
|
+
);
|
|
1082
|
+
const saveErrorWorkspaceCount = useMemo(
|
|
1083
|
+
() => workspaceTableKeys.filter((key) => workspaceItems[key]?.saveStatus.state === "error").length,
|
|
1084
|
+
[workspaceItems, workspaceTableKeys],
|
|
1085
|
+
);
|
|
1086
|
+
const migrationErrorWorkspaceCount = useMemo(
|
|
1087
|
+
() => workspaceTableKeys.filter((key) => workspaceItems[key]?.migrationStatus.state === "error").length,
|
|
1088
|
+
[workspaceItems, workspaceTableKeys],
|
|
1089
|
+
);
|
|
1090
|
+
const totalIncludedMappedColumns = useMemo(
|
|
1091
|
+
() => workspaceTableKeys.reduce((count, key) => {
|
|
1092
|
+
const preview = workspaceItems[key]?.mappingPreview;
|
|
1093
|
+
return count + (preview?.summary.includedColumnNames.length ?? 0);
|
|
1094
|
+
}, 0),
|
|
1095
|
+
[workspaceItems, workspaceTableKeys],
|
|
1096
|
+
);
|
|
1097
|
+
const totalGeneratedSystemColumns = useMemo(
|
|
1098
|
+
() => workspaceTableKeys.reduce((count, key) => {
|
|
1099
|
+
const preview = workspaceItems[key]?.mappingPreview;
|
|
1100
|
+
return count + (preview?.summary.generatedSystemColumnCount ?? 0);
|
|
1101
|
+
}, 0),
|
|
1102
|
+
[workspaceItems, workspaceTableKeys],
|
|
1103
|
+
);
|
|
1104
|
+
const totalGeneratedMigrations = useMemo(
|
|
1105
|
+
() => workspaceTableKeys.reduce((count, key) => {
|
|
1106
|
+
return count + (workspaceItems[key]?.mappingPreview?.migration.willGenerate ? 1 : 0);
|
|
1107
|
+
}, 0),
|
|
1108
|
+
[workspaceItems, workspaceTableKeys],
|
|
1109
|
+
);
|
|
1110
|
+
|
|
1111
|
+
const updateWorkspaceItem = (key: string, updater: (current: WorkspaceBatchItem) => WorkspaceBatchItem) => {
|
|
1112
|
+
setWorkspaceItems((current) => {
|
|
1113
|
+
const item = current[key];
|
|
1114
|
+
if (!item) {
|
|
1115
|
+
return current;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return {
|
|
1119
|
+
...current,
|
|
1120
|
+
[key]: updater(item),
|
|
1121
|
+
};
|
|
1122
|
+
});
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
const buildWorkspacePlaceholder = (record: DatasourceIntrospectionTableRecord): WorkspaceBatchItem => ({
|
|
1126
|
+
key: makeTableKey(record.schema, record.tableName),
|
|
1127
|
+
record,
|
|
1128
|
+
detail: null,
|
|
1129
|
+
mappedModelData: null,
|
|
1130
|
+
draft: null,
|
|
1131
|
+
loadStatus: { state: "loading", message: "Loading mapping workspace..." },
|
|
1132
|
+
reviewStatus: { state: "idle", message: "Not reviewed yet." },
|
|
1133
|
+
saveStatus: { state: "idle", message: "Not saved yet." },
|
|
1134
|
+
migrationStatus: { state: "idle", message: "Migration artifacts not created yet." },
|
|
1135
|
+
mappingPreview: null,
|
|
1136
|
+
persistedMappingResult: null,
|
|
1137
|
+
migrationArtifactsResult: null,
|
|
1138
|
+
reviewedMetadataJsonValue: null,
|
|
1139
|
+
metadataJsonText: "",
|
|
1140
|
+
metadataJsonValidationError: null,
|
|
1141
|
+
metadataJsonResetToken: `workspace-${record.tableName}-${Date.now()}`,
|
|
1142
|
+
error: null,
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
const loadWorkspaceItem = async (record: DatasourceIntrospectionTableRecord) => {
|
|
1146
|
+
const key = makeTableKey(record.schema, record.tableName);
|
|
1147
|
+
|
|
1148
|
+
try {
|
|
1149
|
+
const tableDetailRequest = dispatch(
|
|
1150
|
+
datasourceIntrospectionApi.endpoints.getDatasourceIntrospectionTableDetail.initiate({
|
|
1151
|
+
moduleId,
|
|
1152
|
+
datasource: selectedDatasource,
|
|
1153
|
+
table: record.tableName,
|
|
1154
|
+
schema: record.schema ?? undefined,
|
|
1155
|
+
}),
|
|
1156
|
+
) as any;
|
|
1157
|
+
const tableDetailData = await tableDetailRequest.unwrap();
|
|
1158
|
+
tableDetailRequest.unsubscribe?.();
|
|
1159
|
+
|
|
1160
|
+
let mappedModelData: any = null;
|
|
1161
|
+
if (record.mapped && record.mappedModelId) {
|
|
1162
|
+
const modelRequest = dispatch(
|
|
1163
|
+
modelsApi.endpoints.getmodelById.initiate(record.mappedModelId),
|
|
1164
|
+
) as any;
|
|
1165
|
+
const modelResponse = await modelRequest.unwrap();
|
|
1166
|
+
modelRequest.unsubscribe?.();
|
|
1167
|
+
mappedModelData = modelResponse?.data ?? modelResponse ?? null;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
const draft = buildDrawerDraft(
|
|
1171
|
+
tableDetailData,
|
|
1172
|
+
record,
|
|
1173
|
+
mappedModelData ? { data: mappedModelData } : undefined,
|
|
1174
|
+
);
|
|
1175
|
+
|
|
1176
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1177
|
+
...current,
|
|
1178
|
+
detail: tableDetailData,
|
|
1179
|
+
mappedModelData,
|
|
1180
|
+
draft,
|
|
1181
|
+
loadStatus: { state: "ready", message: "Ready to configure." },
|
|
1182
|
+
error: null,
|
|
1183
|
+
}));
|
|
1184
|
+
} catch (error: any) {
|
|
1185
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1186
|
+
...current,
|
|
1187
|
+
loadStatus: { state: "error", message: readQueryError(error) },
|
|
1188
|
+
error: readQueryError(error),
|
|
1189
|
+
}));
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
|
|
961
1193
|
const drawerUserKeyOptions = useMemo(() => {
|
|
962
1194
|
const options = new Map<string, { label: string; value: string }>();
|
|
963
1195
|
|
|
964
|
-
|
|
1196
|
+
activeWorkspaceItem?.detail?.userKeyCandidates?.forEach((candidate) => {
|
|
965
1197
|
options.set(candidate.name, {
|
|
966
1198
|
label: `${candidate.label} · ${candidate.reason}`,
|
|
967
1199
|
value: candidate.name,
|
|
@@ -980,7 +1212,7 @@ export function DatasourceIntrospectionPage() {
|
|
|
980
1212
|
});
|
|
981
1213
|
|
|
982
1214
|
return Array.from(options.values());
|
|
983
|
-
}, [
|
|
1215
|
+
}, [activeWorkspaceItem?.detail?.userKeyCandidates, drawerDraft?.columns]);
|
|
984
1216
|
|
|
985
1217
|
const settingsTabs = [
|
|
986
1218
|
{
|
|
@@ -1063,33 +1295,109 @@ export function DatasourceIntrospectionPage() {
|
|
|
1063
1295
|
if (selectedDatasource) {
|
|
1064
1296
|
await refetchTables();
|
|
1065
1297
|
}
|
|
1066
|
-
if (isWorkspaceMode
|
|
1067
|
-
|
|
1298
|
+
if (isWorkspaceMode) {
|
|
1299
|
+
workspaceRecords.forEach((record) => {
|
|
1300
|
+
void loadWorkspaceItem(record);
|
|
1301
|
+
});
|
|
1068
1302
|
}
|
|
1069
1303
|
};
|
|
1070
1304
|
|
|
1071
|
-
const
|
|
1072
|
-
if (
|
|
1073
|
-
|
|
1305
|
+
const toggleSelectedRow = (row: DatasourceIntrospectionTableRecord, checked?: boolean) => {
|
|
1306
|
+
if (!row.hasPrimaryKey) {
|
|
1307
|
+
return;
|
|
1074
1308
|
}
|
|
1075
1309
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1310
|
+
const rowKey = makeTableKey(row.schema, row.tableName);
|
|
1311
|
+
setSelectedRows((current) => {
|
|
1312
|
+
const exists = current.some((item) => makeTableKey(item.schema, item.tableName) === rowKey);
|
|
1313
|
+
const shouldInclude = checked ?? !exists;
|
|
1314
|
+
|
|
1315
|
+
if (shouldInclude) {
|
|
1316
|
+
if (exists) return current;
|
|
1317
|
+
return [...current, row];
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
return current.filter((item) => makeTableKey(item.schema, item.tableName) !== rowKey);
|
|
1321
|
+
});
|
|
1078
1322
|
};
|
|
1079
1323
|
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1324
|
+
const toggleAllVisibleRows = (checked: boolean) => {
|
|
1325
|
+
setSelectedRows((current) => {
|
|
1326
|
+
if (checked) {
|
|
1327
|
+
const merged = [...current];
|
|
1328
|
+
const currentKeySet = new Set(current.map((row) => makeTableKey(row.schema, row.tableName)));
|
|
1329
|
+
|
|
1330
|
+
filteredRecords.forEach((row) => {
|
|
1331
|
+
if (!row.hasPrimaryKey) {
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const rowKey = makeTableKey(row.schema, row.tableName);
|
|
1336
|
+
if (!currentKeySet.has(rowKey)) {
|
|
1337
|
+
merged.push(row);
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1341
|
+
return merged;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
return current.filter((row) => !visibleRowKeySet.has(makeTableKey(row.schema, row.tableName)));
|
|
1345
|
+
});
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
const startMappingForRows = (records: DatasourceIntrospectionTableRecord[]) => {
|
|
1349
|
+
if (!records.length) return;
|
|
1350
|
+
|
|
1351
|
+
const eligibleRecords = records.filter((record) => record.hasPrimaryKey);
|
|
1352
|
+
const skippedRecords = records.filter((record) => !record.hasPrimaryKey);
|
|
1353
|
+
|
|
1354
|
+
if (skippedRecords.length) {
|
|
1355
|
+
dispatch(showToast({
|
|
1356
|
+
severity: "warn",
|
|
1357
|
+
summary: "Skipped blocked tables",
|
|
1358
|
+
detail: skippedRecords.length === 1
|
|
1359
|
+
? skippedRecords[0].mappingBlockedReason || `Table "${skippedRecords[0].tableName}" was skipped because it has no primary key.`
|
|
1360
|
+
: `${skippedRecords.length} tables were skipped because they do not define a datasource primary key.`,
|
|
1361
|
+
}));
|
|
1083
1362
|
}
|
|
1084
1363
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1364
|
+
if (!eligibleRecords.length) {
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const keys = eligibleRecords.map((record) => makeTableKey(record.schema, record.tableName));
|
|
1369
|
+
const nextWorkspaceItems = Object.fromEntries(
|
|
1370
|
+
eligibleRecords.map((record) => [makeTableKey(record.schema, record.tableName), buildWorkspacePlaceholder(record)]),
|
|
1371
|
+
);
|
|
1372
|
+
|
|
1373
|
+
setSelectedRows(eligibleRecords);
|
|
1374
|
+
setWorkspaceTableKeys(keys);
|
|
1375
|
+
setActiveWorkspaceTableKey(keys[0]);
|
|
1376
|
+
setWorkspaceItems(nextWorkspaceItems);
|
|
1377
|
+
setFieldConfigColumnName(null);
|
|
1378
|
+
setDrawerTab("general-info");
|
|
1379
|
+
setWorkspaceStep("configure");
|
|
1380
|
+
setGenerateCodeOutput("");
|
|
1381
|
+
setIsGeneratingCodeWorkflow(false);
|
|
1382
|
+
setGenerateCodeStatusMessage("");
|
|
1383
|
+
setGenerateCodeTimeoutMessage(null);
|
|
1384
|
+
setGenerateCodeReady(false);
|
|
1385
|
+
setHasAttemptedGenerateCode(false);
|
|
1386
|
+
setRunMigrationOutput("");
|
|
1387
|
+
setRunMigrationCompleted(false);
|
|
1388
|
+
|
|
1389
|
+
eligibleRecords.forEach((record) => {
|
|
1390
|
+
void loadWorkspaceItem(record);
|
|
1391
|
+
});
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1394
|
+
const leaveWorkspace = () => {
|
|
1395
|
+
setWorkspaceTableKeys([]);
|
|
1396
|
+
setActiveWorkspaceTableKey("");
|
|
1397
|
+
setWorkspaceItems({});
|
|
1087
1398
|
setFieldConfigColumnName(null);
|
|
1088
1399
|
setWorkspaceStep("configure");
|
|
1089
1400
|
setDrawerTab("general-info");
|
|
1090
|
-
setMappingPreview(null);
|
|
1091
|
-
setPersistedMappingResult(null);
|
|
1092
|
-
setMigrationArtifactsResult(null);
|
|
1093
1401
|
setGenerateCodeOutput("");
|
|
1094
1402
|
setIsGeneratingCodeWorkflow(false);
|
|
1095
1403
|
setGenerateCodeStatusMessage("");
|
|
@@ -1098,11 +1406,6 @@ export function DatasourceIntrospectionPage() {
|
|
|
1098
1406
|
setHasAttemptedGenerateCode(false);
|
|
1099
1407
|
setRunMigrationOutput("");
|
|
1100
1408
|
setRunMigrationCompleted(false);
|
|
1101
|
-
setReviewedMetadataJsonValue(null);
|
|
1102
|
-
setMetadataJsonText("");
|
|
1103
|
-
setMetadataJsonValidationError(null);
|
|
1104
|
-
setMetadataJsonResetToken(`workspace-closed-${Date.now()}`);
|
|
1105
|
-
setDrawerBootstrapping(false);
|
|
1106
1409
|
};
|
|
1107
1410
|
|
|
1108
1411
|
const applyDraftSettings = () => {
|
|
@@ -1110,18 +1413,54 @@ export function DatasourceIntrospectionPage() {
|
|
|
1110
1413
|
setSettingsOpen(false);
|
|
1111
1414
|
};
|
|
1112
1415
|
|
|
1416
|
+
const updateActiveWorkspaceItem = (updater: (current: WorkspaceBatchItem) => WorkspaceBatchItem) => {
|
|
1417
|
+
if (!activeWorkspaceTableKey) return;
|
|
1418
|
+
updateWorkspaceItem(activeWorkspaceTableKey, updater);
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1113
1421
|
const updateDrawerDraft = (updater: (current: DrawerDraft) => DrawerDraft) => {
|
|
1114
|
-
setMappingPreview(null);
|
|
1115
|
-
setPersistedMappingResult(null);
|
|
1116
|
-
setMigrationArtifactsResult(null);
|
|
1117
1422
|
setGenerateCodeOutput("");
|
|
1118
1423
|
setIsGeneratingCodeWorkflow(false);
|
|
1119
1424
|
setGenerateCodeStatusMessage("");
|
|
1120
1425
|
setGenerateCodeTimeoutMessage(null);
|
|
1121
1426
|
setGenerateCodeReady(false);
|
|
1122
1427
|
setHasAttemptedGenerateCode(false);
|
|
1428
|
+
setMigrationPreviewCopied(false);
|
|
1123
1429
|
setRunMigrationOutput("");
|
|
1124
|
-
|
|
1430
|
+
updateActiveWorkspaceItem((current) => ({
|
|
1431
|
+
...current,
|
|
1432
|
+
mappingPreview: null,
|
|
1433
|
+
persistedMappingResult: null,
|
|
1434
|
+
migrationArtifactsResult: null,
|
|
1435
|
+
reviewStatus: { state: "idle", message: "Not reviewed yet." },
|
|
1436
|
+
saveStatus: { state: "idle", message: "Not saved yet." },
|
|
1437
|
+
migrationStatus: { state: "idle", message: "Migration artifacts not created yet." },
|
|
1438
|
+
draft: current.draft ? updater(current.draft) : current.draft,
|
|
1439
|
+
}));
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
const handleCopyMigrationPreview = async () => {
|
|
1443
|
+
const migrationContent = mappingPreview?.migration.content;
|
|
1444
|
+
if (!migrationContent) {
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
try {
|
|
1449
|
+
await navigator.clipboard.writeText(migrationContent);
|
|
1450
|
+
setMigrationPreviewCopied(true);
|
|
1451
|
+
window.setTimeout(() => setMigrationPreviewCopied(false), 2000);
|
|
1452
|
+
dispatch(showToast({
|
|
1453
|
+
severity: "success",
|
|
1454
|
+
summary: "Migration copied",
|
|
1455
|
+
detail: "The migration preview was copied to the clipboard.",
|
|
1456
|
+
}));
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
dispatch(showToast({
|
|
1459
|
+
severity: "error",
|
|
1460
|
+
summary: "Copy failed",
|
|
1461
|
+
detail: "Unable to copy the migration preview to the clipboard.",
|
|
1462
|
+
}));
|
|
1463
|
+
}
|
|
1125
1464
|
};
|
|
1126
1465
|
|
|
1127
1466
|
const updateGeneralField = (field: keyof DrawerDraft, value: any) => {
|
|
@@ -1182,29 +1521,6 @@ export function DatasourceIntrospectionPage() {
|
|
|
1182
1521
|
}));
|
|
1183
1522
|
};
|
|
1184
1523
|
|
|
1185
|
-
const updateColumnSolidFieldType = (columnName: string, nextSolidFieldType: string) => {
|
|
1186
|
-
updateDrawerDraft((current) => ({
|
|
1187
|
-
...current,
|
|
1188
|
-
columns: current.columns.map((column) => {
|
|
1189
|
-
if (column.columnName !== columnName) return column;
|
|
1190
|
-
if (column.handledBySuperclass) return column;
|
|
1191
|
-
|
|
1192
|
-
return {
|
|
1193
|
-
...column,
|
|
1194
|
-
solidFieldType: nextSolidFieldType,
|
|
1195
|
-
ormType: getDefaultOrmTypeForSolidFieldType(current.dataSourceType, nextSolidFieldType),
|
|
1196
|
-
fieldConfig: column.fieldConfig
|
|
1197
|
-
? {
|
|
1198
|
-
...column.fieldConfig,
|
|
1199
|
-
type: nextSolidFieldType,
|
|
1200
|
-
ormType: getDefaultOrmTypeForSolidFieldType(current.dataSourceType, nextSolidFieldType),
|
|
1201
|
-
}
|
|
1202
|
-
: column.fieldConfig,
|
|
1203
|
-
};
|
|
1204
|
-
}),
|
|
1205
|
-
}));
|
|
1206
|
-
};
|
|
1207
|
-
|
|
1208
1524
|
const openFieldConfigEditor = (columnName: string) => {
|
|
1209
1525
|
setFieldConfigColumnName(columnName);
|
|
1210
1526
|
};
|
|
@@ -1379,86 +1695,167 @@ export function DatasourceIntrospectionPage() {
|
|
|
1379
1695
|
};
|
|
1380
1696
|
|
|
1381
1697
|
const reviewMappingJson = async () => {
|
|
1382
|
-
if (!
|
|
1383
|
-
|
|
1384
|
-
const
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1698
|
+
if (!bootstrap?.module?.id || !workspaceTableKeys.length) return;
|
|
1699
|
+
|
|
1700
|
+
const tablesWithErrors = workspaceTableKeys
|
|
1701
|
+
.map((key) => ({ key, item: workspaceItems[key] }))
|
|
1702
|
+
.filter((entry) => entry.item?.draft)
|
|
1703
|
+
.flatMap(({ item }) => getDrawerValidationErrors(item.draft as DrawerDraft).map((detail) => ({
|
|
1704
|
+
tableName: item?.record.tableName,
|
|
1705
|
+
detail,
|
|
1706
|
+
})));
|
|
1707
|
+
|
|
1708
|
+
if (tablesWithErrors.length) {
|
|
1709
|
+
tablesWithErrors.forEach(({ tableName, detail }) => {
|
|
1710
|
+
dispatch(showToast({ severity: "error", summary: tableName || "Validation", detail }));
|
|
1388
1711
|
});
|
|
1389
1712
|
return;
|
|
1390
1713
|
}
|
|
1391
1714
|
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1715
|
+
let firstSuccessKey = "";
|
|
1716
|
+
|
|
1717
|
+
for (const key of workspaceTableKeys) {
|
|
1718
|
+
const item = workspaceItems[key];
|
|
1719
|
+
if (!item?.draft) continue;
|
|
1720
|
+
|
|
1721
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1722
|
+
...current,
|
|
1723
|
+
reviewStatus: { state: "loading", message: "Generating mapping preview..." },
|
|
1724
|
+
}));
|
|
1725
|
+
|
|
1726
|
+
try {
|
|
1727
|
+
const preview = await previewMapping({
|
|
1728
|
+
moduleId: bootstrap.module.id,
|
|
1729
|
+
payload: buildMappingRequest(item.draft),
|
|
1730
|
+
}).unwrap();
|
|
1731
|
+
|
|
1732
|
+
if (!firstSuccessKey) {
|
|
1733
|
+
firstSuccessKey = key;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1737
|
+
...current,
|
|
1738
|
+
mappingPreview: preview,
|
|
1739
|
+
reviewedMetadataJsonValue: preview.metadataJson.model,
|
|
1740
|
+
metadataJsonText: JSON.stringify(preview.metadataJson.model, null, 2),
|
|
1741
|
+
metadataJsonValidationError: null,
|
|
1742
|
+
metadataJsonResetToken: `preview-${preview.metadataJson.filePath}-${Date.now()}`,
|
|
1743
|
+
persistedMappingResult: null,
|
|
1744
|
+
migrationArtifactsResult: null,
|
|
1745
|
+
reviewStatus: { state: "success", message: "Preview ready." },
|
|
1746
|
+
saveStatus: { state: "idle", message: "Not saved yet." },
|
|
1747
|
+
migrationStatus: { state: "idle", message: "Migration artifacts not created yet." },
|
|
1748
|
+
}));
|
|
1749
|
+
} catch (error: any) {
|
|
1750
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1751
|
+
...current,
|
|
1752
|
+
reviewStatus: { state: "error", message: readQueryError(error) },
|
|
1753
|
+
}));
|
|
1754
|
+
dispatch(showToast({
|
|
1755
|
+
severity: "error",
|
|
1756
|
+
summary: `Preview failed for ${item.record.tableName}`,
|
|
1757
|
+
detail: readQueryError(error),
|
|
1758
|
+
}));
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1397
1761
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
setMetadataJsonValidationError(null);
|
|
1402
|
-
setMetadataJsonResetToken(`preview-${preview.metadataJson.filePath}-${Date.now()}`);
|
|
1403
|
-
setPersistedMappingResult(null);
|
|
1404
|
-
setMigrationArtifactsResult(null);
|
|
1762
|
+
if (firstSuccessKey) {
|
|
1763
|
+
setActiveWorkspaceTableKey(firstSuccessKey);
|
|
1764
|
+
setWorkspaceStep("review-json");
|
|
1405
1765
|
setGenerateCodeOutput("");
|
|
1406
1766
|
setRunMigrationOutput("");
|
|
1407
|
-
setWorkspaceStep("review-json");
|
|
1408
|
-
} catch (error: any) {
|
|
1409
|
-
dispatch(showToast({
|
|
1410
|
-
severity: "error",
|
|
1411
|
-
summary: "Preview failed",
|
|
1412
|
-
detail: readQueryError(error),
|
|
1413
|
-
}));
|
|
1414
1767
|
}
|
|
1415
1768
|
};
|
|
1416
1769
|
|
|
1417
1770
|
const persistMappingArtifacts = async () => {
|
|
1418
|
-
if (!
|
|
1771
|
+
if (!bootstrap?.module?.id || !workspaceTableKeys.length) return;
|
|
1419
1772
|
|
|
1420
|
-
|
|
1421
|
-
if (validationErrors.length) {
|
|
1422
|
-
validationErrors.forEach((detail) => {
|
|
1423
|
-
dispatch(showToast({ severity: "error", summary: "Validation", detail }));
|
|
1424
|
-
});
|
|
1425
|
-
setWorkspaceStep("configure");
|
|
1426
|
-
return;
|
|
1427
|
-
}
|
|
1773
|
+
let firstSavedKey = "";
|
|
1428
1774
|
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
setWorkspaceStep("review-json");
|
|
1433
|
-
dispatch(showToast({
|
|
1434
|
-
severity: "error",
|
|
1435
|
-
summary: "Validation",
|
|
1436
|
-
detail: reviewedModelValidation.error ?? "Metadata JSON is invalid.",
|
|
1437
|
-
}));
|
|
1438
|
-
return;
|
|
1439
|
-
}
|
|
1775
|
+
for (const key of workspaceTableKeys) {
|
|
1776
|
+
const item = workspaceItems[key];
|
|
1777
|
+
if (!item?.draft || !item.mappingPreview) continue;
|
|
1440
1778
|
|
|
1441
|
-
|
|
1779
|
+
const validationErrors = getDrawerValidationErrors(item.draft);
|
|
1780
|
+
if (validationErrors.length) {
|
|
1781
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1782
|
+
...current,
|
|
1783
|
+
saveStatus: { state: "error", message: validationErrors[0] },
|
|
1784
|
+
}));
|
|
1785
|
+
validationErrors.forEach((detail) => {
|
|
1786
|
+
dispatch(showToast({ severity: "error", summary: item.record.tableName, detail }));
|
|
1787
|
+
});
|
|
1788
|
+
continue;
|
|
1789
|
+
}
|
|
1442
1790
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1791
|
+
const reviewedModelValidation = parseAndValidateReviewedModelJson(item.metadataJsonText, item.draft);
|
|
1792
|
+
if (reviewedModelValidation.error || !reviewedModelValidation.parsed) {
|
|
1793
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1794
|
+
...current,
|
|
1795
|
+
metadataJsonValidationError: reviewedModelValidation.error ?? "Metadata JSON is invalid.",
|
|
1796
|
+
saveStatus: { state: "error", message: reviewedModelValidation.error ?? "Metadata JSON is invalid." },
|
|
1797
|
+
}));
|
|
1798
|
+
dispatch(showToast({
|
|
1799
|
+
severity: "error",
|
|
1800
|
+
summary: item.record.tableName,
|
|
1801
|
+
detail: reviewedModelValidation.error ?? "Metadata JSON is invalid.",
|
|
1802
|
+
}));
|
|
1803
|
+
continue;
|
|
1804
|
+
}
|
|
1451
1805
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
? "The model mapping metadata was updated successfully."
|
|
1457
|
-
: "The model mapping metadata was created successfully.",
|
|
1806
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1807
|
+
...current,
|
|
1808
|
+
metadataJsonValidationError: null,
|
|
1809
|
+
saveStatus: { state: "loading", message: "Saving mapping..." },
|
|
1458
1810
|
}));
|
|
1459
1811
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1812
|
+
try {
|
|
1813
|
+
const response = await applyMapping({
|
|
1814
|
+
moduleId: bootstrap.module.id,
|
|
1815
|
+
payload: {
|
|
1816
|
+
...buildMappingRequest(item.draft, item.mappingPreview.migration.timestamp ?? null),
|
|
1817
|
+
reviewedModel: reviewedModelValidation.parsed,
|
|
1818
|
+
},
|
|
1819
|
+
}).unwrap();
|
|
1820
|
+
|
|
1821
|
+
if (!firstSavedKey) {
|
|
1822
|
+
firstSavedKey = key;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1826
|
+
...current,
|
|
1827
|
+
persistedMappingResult: response,
|
|
1828
|
+
migrationArtifactsResult: null,
|
|
1829
|
+
saveStatus: { state: "success", message: "Mapping saved." },
|
|
1830
|
+
migrationStatus: { state: "idle", message: "Migration artifacts not created yet." },
|
|
1831
|
+
draft: current.draft
|
|
1832
|
+
? {
|
|
1833
|
+
...current.draft,
|
|
1834
|
+
mapped: true,
|
|
1835
|
+
modelId: response.modelId,
|
|
1836
|
+
}
|
|
1837
|
+
: current.draft,
|
|
1838
|
+
record: {
|
|
1839
|
+
...current.record,
|
|
1840
|
+
mapped: true,
|
|
1841
|
+
mappedModelId: response.modelId,
|
|
1842
|
+
},
|
|
1843
|
+
}));
|
|
1844
|
+
} catch (error: any) {
|
|
1845
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1846
|
+
...current,
|
|
1847
|
+
saveStatus: { state: "error", message: readQueryError(error) },
|
|
1848
|
+
}));
|
|
1849
|
+
dispatch(showToast({
|
|
1850
|
+
severity: "error",
|
|
1851
|
+
summary: `Save failed for ${item.record.tableName}`,
|
|
1852
|
+
detail: readQueryError(error),
|
|
1853
|
+
}));
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
if (firstSavedKey) {
|
|
1858
|
+
setActiveWorkspaceTableKey(firstSavedKey);
|
|
1462
1859
|
setGenerateCodeOutput("");
|
|
1463
1860
|
setIsGeneratingCodeWorkflow(false);
|
|
1464
1861
|
setGenerateCodeStatusMessage("");
|
|
@@ -1467,21 +1864,11 @@ export function DatasourceIntrospectionPage() {
|
|
|
1467
1864
|
setHasAttemptedGenerateCode(false);
|
|
1468
1865
|
setRunMigrationOutput("");
|
|
1469
1866
|
setRunMigrationCompleted(false);
|
|
1470
|
-
setDrawerDraft((current) => (
|
|
1471
|
-
current
|
|
1472
|
-
? {
|
|
1473
|
-
...current,
|
|
1474
|
-
mapped: true,
|
|
1475
|
-
modelId: response.modelId,
|
|
1476
|
-
}
|
|
1477
|
-
: current
|
|
1478
|
-
));
|
|
1479
1867
|
setWorkspaceStep("generate-code");
|
|
1480
|
-
} catch (error: any) {
|
|
1481
1868
|
dispatch(showToast({
|
|
1482
|
-
severity: "
|
|
1483
|
-
summary: "
|
|
1484
|
-
detail:
|
|
1869
|
+
severity: "success",
|
|
1870
|
+
summary: "Mappings saved",
|
|
1871
|
+
detail: "The selected table mappings were saved successfully.",
|
|
1485
1872
|
}));
|
|
1486
1873
|
}
|
|
1487
1874
|
};
|
|
@@ -1543,7 +1930,6 @@ export function DatasourceIntrospectionPage() {
|
|
|
1543
1930
|
|
|
1544
1931
|
setGenerateCodeStatusMessage("Backend is back online. You can continue.");
|
|
1545
1932
|
setGenerateCodeReady(true);
|
|
1546
|
-
setMigrationArtifactsResult(null);
|
|
1547
1933
|
setRunMigrationOutput("");
|
|
1548
1934
|
dispatch(showToast({
|
|
1549
1935
|
severity: "success",
|
|
@@ -1553,50 +1939,83 @@ export function DatasourceIntrospectionPage() {
|
|
|
1553
1939
|
};
|
|
1554
1940
|
|
|
1555
1941
|
const handleCreateMigrationArtifacts = async () => {
|
|
1556
|
-
if (!
|
|
1942
|
+
if (!bootstrap?.module?.id || !workspaceTableKeys.length) return;
|
|
1557
1943
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1944
|
+
let firstMigrationKey = "";
|
|
1945
|
+
|
|
1946
|
+
for (const key of workspaceTableKeys) {
|
|
1947
|
+
const item = workspaceItems[key];
|
|
1948
|
+
if (!item?.draft || !item.persistedMappingResult?.modelId || !item.mappingPreview) continue;
|
|
1949
|
+
|
|
1950
|
+
const reviewedModelValidation = parseAndValidateReviewedModelJson(item.metadataJsonText, item.draft);
|
|
1951
|
+
if (reviewedModelValidation.error || !reviewedModelValidation.parsed) {
|
|
1952
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1953
|
+
...current,
|
|
1954
|
+
metadataJsonValidationError: reviewedModelValidation.error ?? "Metadata JSON is invalid.",
|
|
1955
|
+
migrationStatus: { state: "error", message: reviewedModelValidation.error ?? "Metadata JSON is invalid." },
|
|
1956
|
+
}));
|
|
1957
|
+
dispatch(showToast({
|
|
1958
|
+
severity: "error",
|
|
1959
|
+
summary: item.record.tableName,
|
|
1960
|
+
detail: reviewedModelValidation.error ?? "Metadata JSON is invalid.",
|
|
1961
|
+
}));
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1966
|
+
...current,
|
|
1967
|
+
metadataJsonValidationError: null,
|
|
1968
|
+
migrationStatus: { state: "loading", message: "Creating migration artifacts..." },
|
|
1566
1969
|
}));
|
|
1567
|
-
return;
|
|
1568
|
-
}
|
|
1569
1970
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1971
|
+
try {
|
|
1972
|
+
const response = await createMigrationArtifacts({
|
|
1973
|
+
moduleId: bootstrap.module.id,
|
|
1974
|
+
payload: {
|
|
1975
|
+
...buildMappingRequest(
|
|
1976
|
+
{
|
|
1977
|
+
...item.draft,
|
|
1978
|
+
mapped: true,
|
|
1979
|
+
modelId: item.persistedMappingResult.modelId,
|
|
1980
|
+
},
|
|
1981
|
+
item.mappingPreview.migration.timestamp ?? null,
|
|
1982
|
+
),
|
|
1983
|
+
reviewedModel: reviewedModelValidation.parsed,
|
|
1984
|
+
},
|
|
1985
|
+
}).unwrap();
|
|
1986
|
+
|
|
1987
|
+
if (!firstMigrationKey) {
|
|
1988
|
+
firstMigrationKey = key;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
updateWorkspaceItem(key, (current) => ({
|
|
1992
|
+
...current,
|
|
1993
|
+
migrationArtifactsResult: response,
|
|
1994
|
+
migrationStatus: {
|
|
1995
|
+
state: "success",
|
|
1996
|
+
message: response.migration.written ? "Migration artifacts created." : "No migration file required.",
|
|
1997
|
+
},
|
|
1998
|
+
}));
|
|
1999
|
+
} catch (error: any) {
|
|
2000
|
+
updateWorkspaceItem(key, (current) => ({
|
|
2001
|
+
...current,
|
|
2002
|
+
migrationStatus: { state: "error", message: readQueryError(error) },
|
|
2003
|
+
}));
|
|
2004
|
+
dispatch(showToast({
|
|
2005
|
+
severity: "error",
|
|
2006
|
+
summary: `Create migration artifacts failed for ${item.record.tableName}`,
|
|
2007
|
+
detail: readQueryError(error),
|
|
2008
|
+
}));
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1585
2011
|
|
|
1586
|
-
|
|
2012
|
+
if (firstMigrationKey) {
|
|
2013
|
+
setActiveWorkspaceTableKey(firstMigrationKey);
|
|
1587
2014
|
setWorkspaceStep("run-migration");
|
|
1588
2015
|
dispatch(showToast({
|
|
1589
2016
|
severity: "success",
|
|
1590
|
-
summary: "Migration artifacts
|
|
1591
|
-
detail:
|
|
1592
|
-
? "The migration file and datasource registration were written successfully."
|
|
1593
|
-
: "No migration file was required, but the datasource registration was updated successfully.",
|
|
1594
|
-
}));
|
|
1595
|
-
} catch (error: any) {
|
|
1596
|
-
dispatch(showToast({
|
|
1597
|
-
severity: "error",
|
|
1598
|
-
summary: "Create migration artifacts failed",
|
|
1599
|
-
detail: readQueryError(error),
|
|
2017
|
+
summary: "Migration artifacts ready",
|
|
2018
|
+
detail: "Datasource registration and migration artifacts were prepared for the selected tables.",
|
|
1600
2019
|
}));
|
|
1601
2020
|
}
|
|
1602
2021
|
};
|
|
@@ -1664,19 +2083,19 @@ export function DatasourceIntrospectionPage() {
|
|
|
1664
2083
|
label: "Review Mapping JSON",
|
|
1665
2084
|
complete: currentStepIndex > 1,
|
|
1666
2085
|
active: workspaceStep === "review-json",
|
|
1667
|
-
disabled: !
|
|
2086
|
+
disabled: !allWorkspaceItemsPreviewed,
|
|
1668
2087
|
onClick: () => {
|
|
1669
|
-
if (
|
|
2088
|
+
if (allWorkspaceItemsPreviewed) setWorkspaceStep("review-json");
|
|
1670
2089
|
},
|
|
1671
2090
|
},
|
|
1672
2091
|
{
|
|
1673
2092
|
key: "generate-code" as const,
|
|
1674
2093
|
label: "Generate Code",
|
|
1675
|
-
complete: currentStepIndex > 2,
|
|
2094
|
+
complete: currentStepIndex > 2 || generateCodeReady,
|
|
1676
2095
|
active: workspaceStep === "generate-code",
|
|
1677
|
-
disabled: !
|
|
2096
|
+
disabled: !allWorkspaceItemsPersisted,
|
|
1678
2097
|
onClick: () => {
|
|
1679
|
-
if (
|
|
2098
|
+
if (allWorkspaceItemsPersisted) setWorkspaceStep("generate-code");
|
|
1680
2099
|
},
|
|
1681
2100
|
},
|
|
1682
2101
|
{
|
|
@@ -1691,12 +2110,12 @@ export function DatasourceIntrospectionPage() {
|
|
|
1691
2110
|
},
|
|
1692
2111
|
{
|
|
1693
2112
|
key: "run-migration" as const,
|
|
1694
|
-
label: "Run Migration",
|
|
2113
|
+
label: hasMigrationGenerationWork ? "Run Migration" : "Finish",
|
|
1695
2114
|
complete: false,
|
|
1696
2115
|
active: workspaceStep === "run-migration",
|
|
1697
|
-
disabled: !
|
|
2116
|
+
disabled: !allWorkspaceItemsMigrated,
|
|
1698
2117
|
onClick: () => {
|
|
1699
|
-
if (
|
|
2118
|
+
if (allWorkspaceItemsMigrated) setWorkspaceStep("run-migration");
|
|
1700
2119
|
},
|
|
1701
2120
|
},
|
|
1702
2121
|
];
|
|
@@ -1706,7 +2125,7 @@ export function DatasourceIntrospectionPage() {
|
|
|
1706
2125
|
) ?? [];
|
|
1707
2126
|
const allSelectableUnmappedIncluded = selectableUnmappedColumns.length > 0
|
|
1708
2127
|
&& selectableUnmappedColumns.every((column) => column.include);
|
|
1709
|
-
const drawerRawDdl =
|
|
2128
|
+
const drawerRawDdl = activeWorkspaceItem?.detail?.rawDdl ?? null;
|
|
1710
2129
|
const drawerTabs = drawerDraft ? [
|
|
1711
2130
|
{
|
|
1712
2131
|
value: "general-info",
|
|
@@ -1845,7 +2264,7 @@ export function DatasourceIntrospectionPage() {
|
|
|
1845
2264
|
<span>Database</span>
|
|
1846
2265
|
<span>SolidX</span>
|
|
1847
2266
|
<span>Status</span>
|
|
1848
|
-
<span>
|
|
2267
|
+
<span>Mapping</span>
|
|
1849
2268
|
</div>
|
|
1850
2269
|
{drawerDraft.columns.map((column) => {
|
|
1851
2270
|
const isUserKey = drawerDraft.userKeyField === column.fieldName;
|
|
@@ -1911,33 +2330,27 @@ export function DatasourceIntrospectionPage() {
|
|
|
1911
2330
|
<span className="sdix-column-meta">
|
|
1912
2331
|
<strong>{column.dataType}</strong>
|
|
1913
2332
|
</span>
|
|
1914
|
-
<span>
|
|
1915
|
-
{column.handledBySuperclass ? (
|
|
1916
|
-
<
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2333
|
+
<span className="sdix-column-type-cell">
|
|
2334
|
+
{!column.handledBySuperclass && (column.include || column.matched) ? (
|
|
2335
|
+
<SolidButton
|
|
2336
|
+
size="small"
|
|
2337
|
+
variant="outline"
|
|
2338
|
+
className="sdix-column-action-icon"
|
|
2339
|
+
onClick={() => openFieldConfigEditor(column.columnName)}
|
|
2340
|
+
aria-label={`Configure ${column.fieldName || column.columnName} field`}
|
|
2341
|
+
>
|
|
2342
|
+
<Cog size={14} />
|
|
2343
|
+
</SolidButton>
|
|
2344
|
+
) : null}
|
|
2345
|
+
<span className={`sdix-status-badge ${column.handledBySuperclass ? "is-superclass" : "is-muted"}`}>
|
|
2346
|
+
{getFieldTypeLabel(column.solidFieldType)}
|
|
2347
|
+
</span>
|
|
1924
2348
|
</span>
|
|
1925
2349
|
<span>
|
|
1926
2350
|
<span className={`sdix-status-badge ${statusClassName}`}>{statusText}</span>
|
|
1927
2351
|
</span>
|
|
1928
2352
|
<span className="sdix-column-action">
|
|
1929
2353
|
<div className="sdix-column-action-stack">
|
|
1930
|
-
{!column.handledBySuperclass && (column.include || column.matched) ? (
|
|
1931
|
-
<SolidButton
|
|
1932
|
-
size="small"
|
|
1933
|
-
variant="outline"
|
|
1934
|
-
className="sdix-column-action-icon"
|
|
1935
|
-
onClick={() => openFieldConfigEditor(column.columnName)}
|
|
1936
|
-
aria-label={`Configure ${column.fieldName || column.columnName} field`}
|
|
1937
|
-
>
|
|
1938
|
-
<Cog size={14} />
|
|
1939
|
-
</SolidButton>
|
|
1940
|
-
) : null}
|
|
1941
2354
|
{column.handledBySuperclass ? (
|
|
1942
2355
|
<SolidButton size="small" variant="outline" disabled className="sdix-column-action-chip sdix-column-action-chip--managed">
|
|
1943
2356
|
Managed
|
|
@@ -1982,6 +2395,24 @@ export function DatasourceIntrospectionPage() {
|
|
|
1982
2395
|
|
|
1983
2396
|
const reviewJsonPanel = mappingPreview ? (
|
|
1984
2397
|
<div className="sdix-review-panel sdix-review-panel--metadata">
|
|
2398
|
+
<div className="sdix-workspace-summary">
|
|
2399
|
+
<div className="sdix-workspace-summary__item">
|
|
2400
|
+
<span>Selected tables</span>
|
|
2401
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2402
|
+
</div>
|
|
2403
|
+
<div className="sdix-workspace-summary__item">
|
|
2404
|
+
<span>Previews ready</span>
|
|
2405
|
+
<strong>{reviewedWorkspaceCount}/{totalWorkspaceCount}</strong>
|
|
2406
|
+
</div>
|
|
2407
|
+
<div className="sdix-workspace-summary__item">
|
|
2408
|
+
<span>Current table</span>
|
|
2409
|
+
<strong>{activeWorkspacePosition}/{totalWorkspaceCount}</strong>
|
|
2410
|
+
</div>
|
|
2411
|
+
<div className="sdix-workspace-summary__item">
|
|
2412
|
+
<span>Review errors</span>
|
|
2413
|
+
<strong>{reviewErrorWorkspaceCount}</strong>
|
|
2414
|
+
</div>
|
|
2415
|
+
</div>
|
|
1985
2416
|
<div className="sdix-review-meta sdix-review-meta--compact">
|
|
1986
2417
|
<span>Target file</span>
|
|
1987
2418
|
<strong>{mappingPreview.metadataJson.filePath}</strong>
|
|
@@ -1991,15 +2422,24 @@ export function DatasourceIntrospectionPage() {
|
|
|
1991
2422
|
value={reviewedMetadataJsonValue ?? mappingPreview.metadataJson.model}
|
|
1992
2423
|
resetToken={metadataJsonResetToken}
|
|
1993
2424
|
onValueChange={(nextValue) => {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2425
|
+
updateActiveWorkspaceItem((current) => ({
|
|
2426
|
+
...current,
|
|
2427
|
+
reviewedMetadataJsonValue: nextValue,
|
|
2428
|
+
metadataJsonValidationError: null,
|
|
2429
|
+
}));
|
|
1998
2430
|
}}
|
|
1999
2431
|
onTextChange={(nextText) => {
|
|
2000
|
-
|
|
2432
|
+
updateActiveWorkspaceItem((current) => ({
|
|
2433
|
+
...current,
|
|
2434
|
+
metadataJsonText: nextText,
|
|
2435
|
+
}));
|
|
2436
|
+
}}
|
|
2437
|
+
onErrorChange={(nextError) => {
|
|
2438
|
+
updateActiveWorkspaceItem((current) => ({
|
|
2439
|
+
...current,
|
|
2440
|
+
metadataJsonValidationError: nextError,
|
|
2441
|
+
}));
|
|
2001
2442
|
}}
|
|
2002
|
-
onErrorChange={setMetadataJsonValidationError}
|
|
2003
2443
|
/>
|
|
2004
2444
|
{metadataJsonValidationError ? (
|
|
2005
2445
|
<div className="sdix-json-validation-error">
|
|
@@ -2011,11 +2451,42 @@ export function DatasourceIntrospectionPage() {
|
|
|
2011
2451
|
|
|
2012
2452
|
const createMigrationsPanel = mappingPreview ? (
|
|
2013
2453
|
<div className="sdix-review-panel sdix-review-panel--migration">
|
|
2454
|
+
<div className="sdix-workspace-summary">
|
|
2455
|
+
<div className="sdix-workspace-summary__item">
|
|
2456
|
+
<span>Selected tables</span>
|
|
2457
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2458
|
+
</div>
|
|
2459
|
+
<div className="sdix-workspace-summary__item">
|
|
2460
|
+
<span>Migration previews ready</span>
|
|
2461
|
+
<strong>{migrationReadyWorkspaceCount}/{totalWorkspaceCount}</strong>
|
|
2462
|
+
</div>
|
|
2463
|
+
<div className="sdix-workspace-summary__item">
|
|
2464
|
+
<span>Migration files to write</span>
|
|
2465
|
+
<strong>{totalGeneratedMigrations}</strong>
|
|
2466
|
+
</div>
|
|
2467
|
+
<div className="sdix-workspace-summary__item">
|
|
2468
|
+
<span>System columns to add</span>
|
|
2469
|
+
<strong>{totalGeneratedSystemColumns}</strong>
|
|
2470
|
+
</div>
|
|
2471
|
+
</div>
|
|
2014
2472
|
{mappingPreview.migration.willGenerate && mappingPreview.migration.filePath ? (
|
|
2015
2473
|
<>
|
|
2016
|
-
<div className="sdix-review-
|
|
2017
|
-
<
|
|
2018
|
-
|
|
2474
|
+
<div className="sdix-review-code-header">
|
|
2475
|
+
<div className="sdix-review-meta sdix-review-meta--compact">
|
|
2476
|
+
<span>Target file</span>
|
|
2477
|
+
<strong>{mappingPreview.migration.filePath}</strong>
|
|
2478
|
+
</div>
|
|
2479
|
+
{mappingPreview.migration.content ? (
|
|
2480
|
+
<SolidButton
|
|
2481
|
+
size="small"
|
|
2482
|
+
variant="outline"
|
|
2483
|
+
className="sdix-review-copy-button"
|
|
2484
|
+
leftIcon={migrationPreviewCopied ? <Check size={14} /> : <Copy size={14} />}
|
|
2485
|
+
onClick={() => void handleCopyMigrationPreview()}
|
|
2486
|
+
>
|
|
2487
|
+
{migrationPreviewCopied ? "Copied" : "Copy"}
|
|
2488
|
+
</SolidButton>
|
|
2489
|
+
) : null}
|
|
2019
2490
|
</div>
|
|
2020
2491
|
<p className="sdix-review-note">
|
|
2021
2492
|
This migration preview is generated from the current mapping and stays read-only here. Moving ahead writes the migration file to disk and updates the datasource registration now that the entity code already exists.
|
|
@@ -2038,15 +2509,33 @@ export function DatasourceIntrospectionPage() {
|
|
|
2038
2509
|
<div className="sdix-stage-panel__header">
|
|
2039
2510
|
<span className="sdix-stage-panel__eyebrow">Step 3</span>
|
|
2040
2511
|
<h3>Generate module code</h3>
|
|
2041
|
-
<p>The reviewed metadata JSON is now persisted. Run the normal SolidX module generation flow next so
|
|
2512
|
+
<p>The reviewed metadata JSON is now persisted for the selected batch. Run the normal SolidX module generation flow next so every mapped entity, DTO, repository, service, controller, and UI asset is refreshed before datasource registration and migration files are written.</p>
|
|
2042
2513
|
</div>
|
|
2043
2514
|
<div className="sdix-stage-panel__body">
|
|
2515
|
+
<div className="sdix-workspace-summary">
|
|
2516
|
+
<div className="sdix-workspace-summary__item">
|
|
2517
|
+
<span>Selected tables</span>
|
|
2518
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2519
|
+
</div>
|
|
2520
|
+
<div className="sdix-workspace-summary__item">
|
|
2521
|
+
<span>Saved mappings</span>
|
|
2522
|
+
<strong>{savedWorkspaceCount}/{totalWorkspaceCount}</strong>
|
|
2523
|
+
</div>
|
|
2524
|
+
<div className="sdix-workspace-summary__item">
|
|
2525
|
+
<span>Columns queued</span>
|
|
2526
|
+
<strong>{totalIncludedMappedColumns}</strong>
|
|
2527
|
+
</div>
|
|
2528
|
+
<div className="sdix-workspace-summary__item">
|
|
2529
|
+
<span>System columns pending</span>
|
|
2530
|
+
<strong>{totalGeneratedSystemColumns}</strong>
|
|
2531
|
+
</div>
|
|
2532
|
+
</div>
|
|
2044
2533
|
<div className="sdix-stage-panel__card">
|
|
2045
2534
|
<div className="sdix-stage-panel__card-title">What happens here</div>
|
|
2046
2535
|
<ul className="sdix-stage-panel__list">
|
|
2047
|
-
<li>The module generate-code pipeline runs for <strong>{bootstrap?.module?.displayName}</strong>.</li>
|
|
2048
|
-
<li>
|
|
2049
|
-
<li>Any existing generated assets for this module may be refreshed.</li>
|
|
2536
|
+
<li>The module generate-code pipeline runs once for <strong>{bootstrap?.module?.displayName}</strong>.</li>
|
|
2537
|
+
<li>All saved mappings in this batch contribute to the generated entities inside the module source folder.</li>
|
|
2538
|
+
<li>Any existing generated assets for this module may be refreshed in the same pass.</li>
|
|
2050
2539
|
</ul>
|
|
2051
2540
|
</div>
|
|
2052
2541
|
<div className="sdix-stage-panel__actions">
|
|
@@ -2059,6 +2548,13 @@ export function DatasourceIntrospectionPage() {
|
|
|
2059
2548
|
Generate Code
|
|
2060
2549
|
</SolidButton>
|
|
2061
2550
|
</div>
|
|
2551
|
+
{generateCodeReady && !isGeneratingCodeWorkflow ? (
|
|
2552
|
+
<div className="sdix-empty-state is-inline sdix-stage-progress sdix-stage-progress--success">
|
|
2553
|
+
<Check size={24} />
|
|
2554
|
+
<h3>Code generation finished</h3>
|
|
2555
|
+
<p>The module generation workflow completed and the backend is back online. You can continue to migrations, or run generate code again if you want to retry.</p>
|
|
2556
|
+
</div>
|
|
2557
|
+
) : null}
|
|
2062
2558
|
{isGeneratingCodeWorkflow ? (
|
|
2063
2559
|
<div className="sdix-empty-state is-inline sdix-stage-progress">
|
|
2064
2560
|
<SolidSpinner size={30} />
|
|
@@ -2085,11 +2581,33 @@ export function DatasourceIntrospectionPage() {
|
|
|
2085
2581
|
{!runMigrationCompleted ? (
|
|
2086
2582
|
<div className="sdix-stage-panel__header">
|
|
2087
2583
|
<span className="sdix-stage-panel__eyebrow">Step 5</span>
|
|
2088
|
-
<h3>Run migration, build, and seed</h3>
|
|
2089
|
-
<p>
|
|
2584
|
+
<h3>{hasMigrationGenerationWork ? "Run migration, build, and seed" : "Finish mapping workflow"}</h3>
|
|
2585
|
+
<p>
|
|
2586
|
+
{hasMigrationGenerationWork
|
|
2587
|
+
? "With datasource registration updated and module code generated, SolidX can now run the TypeORM migration, rebuild the project, and reseed metadata for the full selected batch."
|
|
2588
|
+
: "SolidX will now run the final wrap-up workflow for the full selected batch: execute the datasource migration command, rebuild the project, and reseed the mapped module metadata."}
|
|
2589
|
+
</p>
|
|
2090
2590
|
</div>
|
|
2091
2591
|
) : null}
|
|
2092
2592
|
<div className="sdix-stage-panel__body">
|
|
2593
|
+
<div className="sdix-workspace-summary">
|
|
2594
|
+
<div className="sdix-workspace-summary__item">
|
|
2595
|
+
<span>Selected tables</span>
|
|
2596
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2597
|
+
</div>
|
|
2598
|
+
<div className="sdix-workspace-summary__item">
|
|
2599
|
+
<span>Generated code ready</span>
|
|
2600
|
+
<strong>{hasAttemptedGenerateCode ? "Yes" : "No"}</strong>
|
|
2601
|
+
</div>
|
|
2602
|
+
<div className="sdix-workspace-summary__item">
|
|
2603
|
+
<span>Migration artifacts ready</span>
|
|
2604
|
+
<strong>{migrationReadyWorkspaceCount}/{totalWorkspaceCount}</strong>
|
|
2605
|
+
</div>
|
|
2606
|
+
<div className="sdix-workspace-summary__item">
|
|
2607
|
+
<span>Datasource</span>
|
|
2608
|
+
<strong>{selectedDatasource}</strong>
|
|
2609
|
+
</div>
|
|
2610
|
+
</div>
|
|
2093
2611
|
{!runMigrationCompleted ? (
|
|
2094
2612
|
<div className="sdix-stage-panel__card">
|
|
2095
2613
|
<div className="sdix-stage-panel__card-title">Commands</div>
|
|
@@ -2141,7 +2659,16 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2141
2659
|
case "review-json":
|
|
2142
2660
|
return (
|
|
2143
2661
|
<span>
|
|
2144
|
-
|
|
2662
|
+
Reviewing
|
|
2663
|
+
{" "}
|
|
2664
|
+
<strong>{activeWorkspacePosition} of {totalWorkspaceCount}</strong>
|
|
2665
|
+
{" "}
|
|
2666
|
+
selected tables. Preview generation is ready for
|
|
2667
|
+
{" "}
|
|
2668
|
+
<strong>{reviewedWorkspaceCount}</strong>
|
|
2669
|
+
{" "}
|
|
2670
|
+
table(s)
|
|
2671
|
+
{reviewErrorWorkspaceCount ? `, with ${reviewErrorWorkspaceCount} table(s) still needing review fixes.` : "."}
|
|
2145
2672
|
</span>
|
|
2146
2673
|
);
|
|
2147
2674
|
case "generate-code":
|
|
@@ -2149,24 +2676,37 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2149
2676
|
<span>
|
|
2150
2677
|
Metadata JSON has been saved for
|
|
2151
2678
|
{" "}
|
|
2152
|
-
<strong>{
|
|
2153
|
-
|
|
2679
|
+
<strong>{savedWorkspaceCount}</strong>
|
|
2680
|
+
{" "}
|
|
2681
|
+
of
|
|
2682
|
+
{" "}
|
|
2683
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2684
|
+
{" "}
|
|
2685
|
+
selected tables. Generate the module code next so every entity file exists before datasource registration is updated. Continue stays disabled until you attempt code generation at least once.
|
|
2154
2686
|
</span>
|
|
2155
2687
|
) : (
|
|
2156
|
-
<span>Save the metadata JSON before generating code.</span>
|
|
2688
|
+
<span>Save the reviewed metadata JSON for the selected tables before generating code.</span>
|
|
2157
2689
|
);
|
|
2158
2690
|
case "create-migrations":
|
|
2159
2691
|
return mappingPreview ? (
|
|
2160
2692
|
<span>
|
|
2161
|
-
Create
|
|
2693
|
+
Create migration artifacts for
|
|
2694
|
+
{" "}
|
|
2695
|
+
<strong>{migrationReadyWorkspaceCount}</strong>
|
|
2696
|
+
{" "}
|
|
2697
|
+
of
|
|
2162
2698
|
{" "}
|
|
2163
|
-
<strong>{
|
|
2699
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2164
2700
|
{" "}
|
|
2165
|
-
|
|
2166
|
-
{
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2701
|
+
selected tables. This batch currently includes
|
|
2702
|
+
{" "}
|
|
2703
|
+
<strong>{totalIncludedMappedColumns}</strong>
|
|
2704
|
+
{" "}
|
|
2705
|
+
mapped columns and
|
|
2706
|
+
{" "}
|
|
2707
|
+
<strong>{totalGeneratedSystemColumns}</strong>
|
|
2708
|
+
{" "}
|
|
2709
|
+
SolidX system column changes.
|
|
2170
2710
|
</span>
|
|
2171
2711
|
) : null;
|
|
2172
2712
|
case "run-migration":
|
|
@@ -2175,28 +2715,54 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2175
2715
|
The migration, build, and seed workflow has completed for
|
|
2176
2716
|
{" "}
|
|
2177
2717
|
<strong>{selectedDatasource}</strong>
|
|
2178
|
-
|
|
2718
|
+
{" "}
|
|
2719
|
+
across
|
|
2720
|
+
{" "}
|
|
2721
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2722
|
+
{" "}
|
|
2723
|
+
selected table mappings. Review the output below and click Finish when you are ready to return to the model list.
|
|
2179
2724
|
</span>
|
|
2180
2725
|
) : (
|
|
2181
2726
|
<span>
|
|
2182
|
-
Run the migration, build, and seed workflow for
|
|
2727
|
+
{hasMigrationGenerationWork ? "Run the migration, build, and seed workflow for" : "Finish the mapping workflow for"}
|
|
2183
2728
|
{" "}
|
|
2184
2729
|
<strong>{selectedDatasource}</strong>
|
|
2185
2730
|
{" "}
|
|
2186
|
-
|
|
2731
|
+
across
|
|
2732
|
+
{" "}
|
|
2733
|
+
<strong>{totalWorkspaceCount}</strong>
|
|
2734
|
+
{" "}
|
|
2735
|
+
selected table mappings.
|
|
2736
|
+
{" "}
|
|
2737
|
+
{hasMigrationGenerationWork
|
|
2738
|
+
? "to apply the SolidX system columns on the legacy table and refresh the module metadata state."
|
|
2739
|
+
: "to execute the final rebuild and reseed flow after the mapping changes are already in place."}
|
|
2187
2740
|
</span>
|
|
2188
2741
|
);
|
|
2189
2742
|
case "configure":
|
|
2190
2743
|
default:
|
|
2191
2744
|
return drawerDraft.mapped ? (
|
|
2192
2745
|
<span>
|
|
2193
|
-
Editing
|
|
2746
|
+
Editing
|
|
2747
|
+
{" "}
|
|
2748
|
+
<strong>{activeWorkspacePosition} of {totalWorkspaceCount}</strong>
|
|
2749
|
+
{" "}
|
|
2750
|
+
selected tables.
|
|
2751
|
+
{" "}
|
|
2752
|
+
Current mapped model:
|
|
2194
2753
|
{" "}
|
|
2195
2754
|
<strong>{selectedTable?.mappedModelDisplayName || selectedTable?.mappedModelSingularName}</strong>
|
|
2196
2755
|
{drawerDraft.mappedModuleLabel ? ` in ${drawerDraft.mappedModuleLabel}` : ""}
|
|
2197
2756
|
</span>
|
|
2198
2757
|
) : (
|
|
2199
|
-
<span>
|
|
2758
|
+
<span>
|
|
2759
|
+
Configure
|
|
2760
|
+
{" "}
|
|
2761
|
+
<strong>{activeWorkspacePosition} of {totalWorkspaceCount}</strong>
|
|
2762
|
+
{" "}
|
|
2763
|
+
selected tables before generating the batch metadata JSON previews.
|
|
2764
|
+
{allWorkspaceItemsReady ? " All selected mapping drafts are loaded." : ""}
|
|
2765
|
+
</span>
|
|
2200
2766
|
);
|
|
2201
2767
|
}
|
|
2202
2768
|
})() : null;
|
|
@@ -2241,12 +2807,8 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2241
2807
|
<SolidButton
|
|
2242
2808
|
size="small"
|
|
2243
2809
|
leftIcon={<Sparkles size={14} />}
|
|
2244
|
-
onClick={() =>
|
|
2245
|
-
|
|
2246
|
-
startMappingForRecord(selectedListRow);
|
|
2247
|
-
}
|
|
2248
|
-
}}
|
|
2249
|
-
disabled={!selectedListRow}
|
|
2810
|
+
onClick={() => startMappingForRows(selectedRows)}
|
|
2811
|
+
disabled={!selectedRowCount}
|
|
2250
2812
|
>
|
|
2251
2813
|
Start Mapping
|
|
2252
2814
|
</SolidButton>
|
|
@@ -2281,12 +2843,18 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2281
2843
|
</div>
|
|
2282
2844
|
) : null}
|
|
2283
2845
|
|
|
2284
|
-
{
|
|
2846
|
+
{isSynchronizeBlocked ? (
|
|
2285
2847
|
<div className="sdix-blocker">
|
|
2286
|
-
<
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2848
|
+
<div className="sdix-blocker__icon">
|
|
2849
|
+
<ShieldAlert size={22} />
|
|
2850
|
+
</div>
|
|
2851
|
+
<div className="sdix-blocker__content">
|
|
2852
|
+
<span className="sdix-blocker__eyebrow">Configuration blocker</span>
|
|
2853
|
+
<h3>Disable TypeORM synchronize before running datasource introspection</h3>
|
|
2854
|
+
<p>{tablesResponse?.synchronizeMessage}</p>
|
|
2855
|
+
<div className="sdix-blocker__note">
|
|
2856
|
+
Introspection is paused here to avoid schema-sync side effects while reviewing legacy datasource tables.
|
|
2857
|
+
</div>
|
|
2290
2858
|
</div>
|
|
2291
2859
|
</div>
|
|
2292
2860
|
) : null}
|
|
@@ -2311,7 +2879,7 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2311
2879
|
Retry
|
|
2312
2880
|
</SolidButton>
|
|
2313
2881
|
</div>
|
|
2314
|
-
) : !filteredRecords.length ? (
|
|
2882
|
+
) : isSynchronizeBlocked ? null : !filteredRecords.length ? (
|
|
2315
2883
|
<div className="sdix-empty-state is-inline">
|
|
2316
2884
|
<Database size={22} />
|
|
2317
2885
|
<p>{tableSearch ? "No tables match the current quick filter." : "No database tables were discovered for this datasource."}</p>
|
|
@@ -2323,19 +2891,33 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2323
2891
|
dataKey="tableName"
|
|
2324
2892
|
viewportHeight="calc(100vh - 162px)"
|
|
2325
2893
|
size="small"
|
|
2326
|
-
onRowClick={({ data }) =>
|
|
2327
|
-
rowClassName={(rowData) =>
|
|
2894
|
+
onRowClick={({ data }) => toggleSelectedRow(data)}
|
|
2895
|
+
rowClassName={(rowData) => selectedListRowKeySet.has(makeTableKey(rowData.schema, rowData.tableName)) ? "sdix-table-row-selected" : ""}
|
|
2328
2896
|
emptyMessage="No datasource tables found"
|
|
2329
2897
|
>
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2898
|
+
<Column
|
|
2899
|
+
header={
|
|
2900
|
+
<div className="sdix-selection-cell" data-no-row-click="true">
|
|
2901
|
+
<SolidCheckbox
|
|
2902
|
+
checked={allVisibleRowsSelected}
|
|
2903
|
+
disabled={!hasVisibleRows || !filteredRecords.some((row) => row.hasPrimaryKey)}
|
|
2904
|
+
onChange={(event) => toggleAllVisibleRows(event.target.checked)}
|
|
2905
|
+
aria-label={
|
|
2906
|
+
allVisibleRowsSelected
|
|
2907
|
+
? "Deselect all visible tables"
|
|
2908
|
+
: "Select all visible tables"
|
|
2909
|
+
}
|
|
2910
|
+
/>
|
|
2911
|
+
</div>
|
|
2912
|
+
}
|
|
2913
|
+
body={(rowData: DatasourceIntrospectionTableRecord) => {
|
|
2914
|
+
const rowKey = `${rowData.schema ?? ""}::${rowData.tableName}`;
|
|
2915
|
+
return (
|
|
2335
2916
|
<div className="sdix-selection-cell" data-no-row-click="true">
|
|
2336
2917
|
<SolidCheckbox
|
|
2337
|
-
checked={rowKey
|
|
2338
|
-
|
|
2918
|
+
checked={selectedListRowKeySet.has(rowKey)}
|
|
2919
|
+
disabled={!rowData.hasPrimaryKey}
|
|
2920
|
+
onChange={(event) => toggleSelectedRow(rowData, event.target.checked)}
|
|
2339
2921
|
/>
|
|
2340
2922
|
</div>
|
|
2341
2923
|
);
|
|
@@ -2357,13 +2939,24 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2357
2939
|
header="Status"
|
|
2358
2940
|
body={(rowData: DatasourceIntrospectionTableRecord) => (
|
|
2359
2941
|
<span
|
|
2360
|
-
className={`sdix-status-badge ${rowData.mapped ? "is-mapped" : "is-unmapped"}`}
|
|
2942
|
+
className={`sdix-status-badge ${!rowData.hasPrimaryKey ? "is-blocked" : rowData.mapped ? "is-mapped" : "is-unmapped"}`}
|
|
2361
2943
|
>
|
|
2362
2944
|
{statusLabel(rowData)}
|
|
2363
2945
|
</span>
|
|
2364
2946
|
)}
|
|
2365
2947
|
style={{ minWidth: 150 }}
|
|
2366
2948
|
/>
|
|
2949
|
+
<Column
|
|
2950
|
+
header="Primary Key"
|
|
2951
|
+
body={(rowData: DatasourceIntrospectionTableRecord) => (
|
|
2952
|
+
rowData.hasPrimaryKey
|
|
2953
|
+
? rowData.primaryKeyColumnCount > 1
|
|
2954
|
+
? `${rowData.primaryKeyColumnCount} columns`
|
|
2955
|
+
: rowData.primaryKeyColumnNames[0] ?? "1 column"
|
|
2956
|
+
: <span className="sdix-muted-cell">Missing</span>
|
|
2957
|
+
)}
|
|
2958
|
+
style={{ minWidth: 160 }}
|
|
2959
|
+
/>
|
|
2367
2960
|
<Column
|
|
2368
2961
|
header="Superclass"
|
|
2369
2962
|
body={(rowData: DatasourceIntrospectionTableRecord) => rowData.suggestedBaseClassName}
|
|
@@ -2427,7 +3020,7 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2427
3020
|
) : drawerLoadingError ? (
|
|
2428
3021
|
<div className="sdix-error">
|
|
2429
3022
|
<span>{readQueryError(drawerLoadingError)}</span>
|
|
2430
|
-
<SolidButton size="small" variant="outline" onClick={() => void
|
|
3023
|
+
<SolidButton size="small" variant="outline" onClick={() => selectedTable && void loadWorkspaceItem(selectedTable)}>
|
|
2431
3024
|
Retry
|
|
2432
3025
|
</SolidButton>
|
|
2433
3026
|
</div>
|
|
@@ -2438,6 +3031,59 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2438
3031
|
</div>
|
|
2439
3032
|
) : (
|
|
2440
3033
|
<div className="sdix-wizard-shell">
|
|
3034
|
+
<div className="sdix-workspace-body">
|
|
3035
|
+
<aside className="sdix-workspace-rail">
|
|
3036
|
+
<div className="sdix-workspace-rail__header">
|
|
3037
|
+
<strong>Selected tables</strong>
|
|
3038
|
+
<span>{workspaceRecords.length}</span>
|
|
3039
|
+
</div>
|
|
3040
|
+
<div className="sdix-workspace-rail__list">
|
|
3041
|
+
{workspaceTableKeys.map((key) => {
|
|
3042
|
+
const item = workspaceItems[key];
|
|
3043
|
+
if (!item) return null;
|
|
3044
|
+
const isActive = key === activeWorkspaceTableKey;
|
|
3045
|
+
const status =
|
|
3046
|
+
item.migrationStatus.state !== "idle" ? item.migrationStatus
|
|
3047
|
+
: item.saveStatus.state !== "idle" ? item.saveStatus
|
|
3048
|
+
: item.reviewStatus.state !== "idle" ? item.reviewStatus
|
|
3049
|
+
: item.loadStatus;
|
|
3050
|
+
|
|
3051
|
+
return (
|
|
3052
|
+
<button
|
|
3053
|
+
key={key}
|
|
3054
|
+
ref={(node) => {
|
|
3055
|
+
workspaceRailItemRefs.current[key] = node;
|
|
3056
|
+
}}
|
|
3057
|
+
type="button"
|
|
3058
|
+
className={`sdix-workspace-rail__item${isActive ? " is-active" : ""}`}
|
|
3059
|
+
onClick={() => {
|
|
3060
|
+
setActiveWorkspaceTableKey(key);
|
|
3061
|
+
if (workspaceStep === "configure") {
|
|
3062
|
+
setDrawerTab("general-info");
|
|
3063
|
+
}
|
|
3064
|
+
}}
|
|
3065
|
+
>
|
|
3066
|
+
<div className="sdix-workspace-rail__item-main">
|
|
3067
|
+
<strong>{item.record.tableName}</strong>
|
|
3068
|
+
<span>{item.record.schema || "dbo"}</span>
|
|
3069
|
+
</div>
|
|
3070
|
+
<div className="sdix-workspace-rail__item-status">
|
|
3071
|
+
{status.state === "loading" ? <SolidSpinner size={12} /> : null}
|
|
3072
|
+
<span className={`sdix-workspace-rail__status sdix-workspace-rail__status--${status.state}`}>
|
|
3073
|
+
{status.state === "success"
|
|
3074
|
+
? "Ready"
|
|
3075
|
+
: status.state === "error"
|
|
3076
|
+
? "Needs attention"
|
|
3077
|
+
: status.message}
|
|
3078
|
+
</span>
|
|
3079
|
+
</div>
|
|
3080
|
+
</button>
|
|
3081
|
+
);
|
|
3082
|
+
})}
|
|
3083
|
+
</div>
|
|
3084
|
+
</aside>
|
|
3085
|
+
|
|
3086
|
+
<div className="sdix-workspace-main">
|
|
2441
3087
|
<div className="sdix-wizard-stepper">
|
|
2442
3088
|
{stepperItems.map((step, index) => (
|
|
2443
3089
|
<button
|
|
@@ -2465,6 +3111,8 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2465
3111
|
{workspaceContent}
|
|
2466
3112
|
</div>
|
|
2467
3113
|
</div>
|
|
3114
|
+
</div>
|
|
3115
|
+
</div>
|
|
2468
3116
|
|
|
2469
3117
|
{drawerDraft ? (
|
|
2470
3118
|
<div className="sdix-wizard-footer">
|
|
@@ -2554,45 +3202,51 @@ npx @solidxai/solidctl@latest seed --modules-to-seed ${bootstrap?.module?.name |
|
|
|
2554
3202
|
setFieldConfigColumnName(null);
|
|
2555
3203
|
}
|
|
2556
3204
|
}}
|
|
2557
|
-
style={{ width: "min(
|
|
3205
|
+
style={{ width: "min(760px, calc(100vw - 64px))" }}
|
|
2558
3206
|
>
|
|
2559
3207
|
<SolidDialogHeader>
|
|
2560
3208
|
<div>
|
|
2561
3209
|
<SolidDialogTitle>Field configuration</SolidDialogTitle>
|
|
2562
3210
|
<SolidDialogDescription>
|
|
2563
|
-
|
|
3211
|
+
Configure how this mapped field should work in SolidX before saving it to the mapping draft.
|
|
2564
3212
|
</SolidDialogDescription>
|
|
2565
3213
|
</div>
|
|
2566
3214
|
<SolidDialogClose />
|
|
2567
3215
|
</SolidDialogHeader>
|
|
2568
3216
|
<SolidDialogSeparator />
|
|
2569
3217
|
<SolidDialogBody>
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
3218
|
+
<div className="sdix-field-config-dialog">
|
|
3219
|
+
{activeFieldConfigColumn && activeFieldConfigMetaData && drawerDraft ? (
|
|
3220
|
+
<FieldMetaDataForm
|
|
3221
|
+
modelMetaData={{
|
|
3222
|
+
singularName: drawerDraft.singularName,
|
|
3223
|
+
pluralName: drawerDraft.pluralName,
|
|
3224
|
+
dataSourceType: drawerDraft.dataSourceType,
|
|
3225
|
+
module: {
|
|
3226
|
+
name: bootstrap?.module?.name,
|
|
3227
|
+
displayName: bootstrap?.module?.displayName,
|
|
3228
|
+
},
|
|
3229
|
+
}}
|
|
3230
|
+
fieldMetaData={activeFieldConfigMetaData}
|
|
3231
|
+
setFieldMetaData={() => undefined}
|
|
3232
|
+
setVisiblePopup={() => undefined}
|
|
3233
|
+
setIsDirty={() => undefined}
|
|
3234
|
+
allFields={fieldDraftAllFields}
|
|
3235
|
+
deleteModelFunction={() => undefined}
|
|
3236
|
+
params={params}
|
|
3237
|
+
setIsRequiredPopUp={() => undefined}
|
|
3238
|
+
showToaster={showFieldDraftToaster}
|
|
3239
|
+
onDraftSubmit={(nextFieldConfig: Record<string, any>) => applyFieldConfigDraft(activeFieldConfigColumn.columnName, nextFieldConfig)}
|
|
3240
|
+
onClose={() => setFieldConfigColumnName(null)}
|
|
3241
|
+
forceShowTypeSelector
|
|
3242
|
+
selectorRequireContinue
|
|
3243
|
+
selectorContinueLabel="Next"
|
|
3244
|
+
selectorInitialFieldType={activeFieldConfigColumn.solidFieldType}
|
|
3245
|
+
disableIdentityEditingForExisting={false}
|
|
3246
|
+
submitLabel="Save field config"
|
|
3247
|
+
/>
|
|
3248
|
+
) : null}
|
|
3249
|
+
</div>
|
|
2596
3250
|
</SolidDialogBody>
|
|
2597
3251
|
</SolidDialog>
|
|
2598
3252
|
|