diff --git a/frontend/src/routes/dashboards/+page.svelte b/frontend/src/routes/dashboards/+page.svelte index aae69cd2..64e1939b 100644 --- a/frontend/src/routes/dashboards/+page.svelte +++ b/frontend/src/routes/dashboards/+page.svelte @@ -34,6 +34,8 @@ import ColumnFilterPopover from "./ColumnFilterPopover.svelte"; import { formatDate, getPaginationRange, getSortIndicator } from "./dashboard-helpers.js"; import { DashboardHubModel } from "$lib/models/DashboardHubModel.svelte.ts"; + import MigrateDashboardModal from "./MigrateDashboardModal.svelte"; + import BackupDashboardModal from "./BackupDashboardModal.svelte"; const m = new DashboardHubModel(); const debouncedSearch = debounce((query: string) => { m.searchQuery = query.trim(); m.currentPage = 1; void m.loadDashboards(); }, 300); @@ -793,543 +795,9 @@ class="px-4 py-2 border border-border-strong rounded-lg bg-surface-card focus-vi {/if} {/if} - - {#if m.showMigrateModal} -
(m.showMigrateModal = false)} - onkeydown={(e) => e.key === "Escape" && (m.showMigrateModal = false)} - role="presentation" - > -
e.stopPropagation()} - role="dialog" - aria-modal="true" - tabindex="-1" - aria-labelledby="migrate-modal-title" - onkeydown={(e) => e.stopPropagation()} - > -
-

- {($t.dashboard?.migrate_modal_title).replace( - "{count}", - String(m.selectedIds.size), - )} -

- -
-
-
- -
- -
- {environments.find((e) => e.id === m.selectedEnv)?.name || - m.selectedEnv} - {$t.dashboard?.read_only} -
-
+ - -
- - -
- - -
-
- - -
- - {#if m.bulkMigrateModel.replaceDb} -
- {#if m.bulkMigrateModel.suggestions.length > 0} - - {/if} -
- - {#if m.isEditingMappings} -
- m.bulkMigrateModel.saveMapping(e.detail.sourceUuid, e.detail.targetUuid)} - /> -
- {:else} -
- - - - - - - - - - {#if m.bulkMigrateModel.suggestions.length > 0} - {#each m.bulkMigrateModel.suggestions as mapping} - {@const targetMapping = m.bulkMigrateModel.mappings.find( - (m) => m.source_db_uuid === mapping.source_db_uuid, - )} - - - - - - {/each} - {:else} - - - - {/if} - -
{$t.dashboard?.source_database}{$t.dashboard?.target_database}{$t.dashboard?.match_percent}
{mapping.source_db || mapping.source_db_name} - {#if targetMapping} - {m.bulkMigrateModel.targetDatabases.find( - (d) => - d.uuid === targetMapping.target_db_uuid, - )?.database_name || mapping.target_db || mapping.target_db_name} - {:else} - {$t.dashboard?.not_mapped} - {/if} - - - {Math.round(mapping.confidence * 100)}% - -
- {m.bulkMigrateModel.targetEnvId - ? $t.dashboard?.no_databases_to_map || - "No databases found to map" - : $t.dashboard?.select_target_for_mappings || - "Select target environment to see mappings"} -
-
- {/if} - {:else} -

- {$t.dashboard?.mapping_disabled_hint || - "Database mapping is disabled. Dashboards will be imported with original database references."} -

- {/if} -
- - -
- -
- - -
-

- {$t.dashboard?.selected_dashboards} -

-
- {#each Array.from(m.selectedIds) as id} - {#each m.allDashboards as d} - {#if d.id === id} -
- - {d.title} -
- {/if} - {/each} - {/each} -
-
- - - {#if m.bulkMigrateModel.targetEnvId && m.selectedIds.size > 0} -
- {#if m.bulkMigrateModel.dryRunLoading} -
- - - - - {$t.migration?.calculating_dry_run || - "Calculating changes..."} -
- {:else if m.bulkMigrateModel.error && !m.bulkMigrateModel.dryRunResult} -
- {$t.common?.error}: - {m.bulkMigrateModel.error} -
- {:else if m.bulkMigrateModel.dryRunResult?.summary} - {@const totalDashboards = - m.bulkMigrateModel.dryRunResult.summary.dashboards?.create + - m.bulkMigrateModel.dryRunResult.summary.dashboards?.update + - m.bulkMigrateModel.dryRunResult.summary.dashboards?.delete || 0} - {@const totalCharts = - m.bulkMigrateModel.dryRunResult.summary.charts?.create + - m.bulkMigrateModel.dryRunResult.summary.charts?.update + - m.bulkMigrateModel.dryRunResult.summary.charts?.delete || 0} - {@const totalDatasets = - m.bulkMigrateModel.dryRunResult.summary.datasets?.create + - m.bulkMigrateModel.dryRunResult.summary.datasets?.update + - m.bulkMigrateModel.dryRunResult.summary.datasets?.delete || 0} - {@const totalAll = - totalDashboards + totalCharts + totalDatasets} -
- - - - - {( - $t.migration?.dry_run_summary || - "Dry-run report: {total} items will be changed (Dashboards: {dashboards}, Charts: {charts}, Datasets: {datasets})" - ) - .replace("{total}", totalAll) - .replace("{dashboards}", totalDashboards) - .replace("{charts}", totalCharts) - .replace("{datasets}", totalDatasets)} - -
- {/if} -
- {/if} -
-
-
- - -
-
-
- {/if} - - - {#if m.showBackupModal} -
(m.showBackupModal = false)} - onkeydown={(e) => e.key === "Escape" && (m.showBackupModal = false)} - role="presentation" - > -
e.stopPropagation()} - role="dialog" - aria-modal="true" - tabindex="-1" - aria-labelledby="backup-modal-title" - onkeydown={(e) => e.stopPropagation()} - > -
-

- {($t.dashboard?.backup_modal_title).replace( - "{count}", - String(m.selectedIds.size), - )} -

- -
-
-
- -
- -
- {environments.find((e) => e.id === m.selectedEnv)?.name || - m.selectedEnv} - {$t.dashboard?.read_only} -
-
- - -
-

- {$t.dashboard?.selected_dashboards} -

-
- {#each Array.from(m.selectedIds) as id} - {#each m.allDashboards as d} - {#if d.id === id} -
- - {d.title} -
- {/if} - {/each} - {/each} -
-
- - -
-

- {$t.dashboard?.schedule} -

-
- - - {#if m.backupSchedule !== ""} -
- - - -
- {/if} -
-
-
-
-
- - -
-
-
- {/if} + + + + + + +{#if model.showBackupModal} +
(model.showBackupModal = false)} + onkeydown={(e) => e.key === 'Escape' && (model.showBackupModal = false)} + role="presentation" + > +
e.stopPropagation()} + role="dialog" + aria-modal="true" + tabindex="-1" + aria-labelledby="backup-modal-title" + onkeydown={(e) => e.stopPropagation()} + > +
+

+ {($t.dashboard?.backup_modal_title).replace('{count}', String(model.selectedIds.size))} +

+ +
+
+
+ +
+ +
+ {environments.find((e) => e.id === model.selectedEnv)?.name || model.selectedEnv} + {$t.dashboard?.read_only} +
+
+ + +
+

{$t.dashboard?.selected_dashboards}

+
+ {#each Array.from(model.selectedIds) as id} + {#each model.allDashboards as d} + {#if d.id === id} +
{d.title}
+ {/if} + {/each} + {/each} +
+
+ + +
+

{$t.dashboard?.schedule}

+
+ + + {#if model.backupSchedule !== ''} +
+ + + +
+ {/if} +
+
+
+
+
+ + +
+
+
+{/if} + diff --git a/frontend/src/routes/dashboards/MigrateDashboardModal.svelte b/frontend/src/routes/dashboards/MigrateDashboardModal.svelte new file mode 100644 index 00000000..7ab332d9 --- /dev/null +++ b/frontend/src/routes/dashboards/MigrateDashboardModal.svelte @@ -0,0 +1,234 @@ + + + + + + + +{#if model.showMigrateModal} +
(model.showMigrateModal = false)} + onkeydown={(e) => e.key === 'Escape' && (model.showMigrateModal = false)} + role="presentation" + > +
e.stopPropagation()} + role="dialog" + aria-modal="true" + tabindex="-1" + aria-labelledby="migrate-modal-title" + onkeydown={(e) => e.stopPropagation()} + > +
+

+ {($t.dashboard?.migrate_modal_title).replace('{count}', String(model.selectedIds.size))} +

+ +
+
+
+ +
+ +
+ {environments.find((e) => e.id === model.selectedEnv)?.name || model.selectedEnv} + {$t.dashboard?.read_only} +
+
+ + +
+ + +
+ + +
+
+ + +
+ + {#if model.bulkMigrateModel.replaceDb} +
+ {#if model.bulkMigrateModel.suggestions.length > 0} + + {/if} +
+ + {#if model.isEditingMappings} +
+ model.bulkMigrateModel.saveMapping(e.detail.sourceUuid, e.detail.targetUuid)} + /> +
+ {:else} +
+ + + + + + + + + + {#if model.bulkMigrateModel.suggestions.length > 0} + {#each model.bulkMigrateModel.suggestions as mapping} + {@const targetMapping = model.bulkMigrateModel.mappings.find((m2) => m2.source_db_uuid === mapping.source_db_uuid)} + + + + + + {/each} + {:else} + + + + {/if} + +
{$t.dashboard?.source_database}{$t.dashboard?.target_database}{$t.dashboard?.match_percent}
{mapping.source_db || mapping.source_db_name} + {#if targetMapping} + {model.bulkMigrateModel.targetDatabases.find((d) => d.uuid === targetMapping.target_db_uuid)?.database_name || mapping.target_db || mapping.target_db_name} + {:else} + {$t.dashboard?.not_mapped} + {/if} + + + {Math.round(mapping.confidence * 100)}% + +
+ {model.bulkMigrateModel.targetEnvId ? $t.dashboard?.no_databases_to_map || 'No databases found to map' : $t.dashboard?.select_target_for_mappings || 'Select target environment to see mappings'} +
+
+ {/if} + {:else} +

{$t.dashboard?.mapping_disabled_hint || 'Database mapping is disabled. Dashboards will be imported with original database references.'}

+ {/if} +
+ + +
+ +
+ + +
+

{$t.dashboard?.selected_dashboards}

+
+ {#each Array.from(model.selectedIds) as id} + {#each model.allDashboards as d} + {#if d.id === id} +
{d.title}
+ {/if} + {/each} + {/each} +
+
+ + + {#if model.bulkMigrateModel.targetEnvId && model.selectedIds.size > 0} +
+ {#if model.bulkMigrateModel.dryRunLoading} +
+ + + + + {$t.migration?.calculating_dry_run || 'Calculating changes...'} +
+ {:else if model.bulkMigrateModel.error && !model.bulkMigrateModel.dryRunResult} +
{$t.common?.error}: {model.bulkMigrateModel.error}
+ {:else if model.bulkMigrateModel.dryRunResult?.summary} + {@const totalDashboards = model.bulkMigrateModel.dryRunResult.summary.dashboards?.create + model.bulkMigrateModel.dryRunResult.summary.dashboards?.update + model.bulkMigrateModel.dryRunResult.summary.dashboards?.delete || 0} + {@const totalCharts = model.bulkMigrateModel.dryRunResult.summary.charts?.create + model.bulkMigrateModel.dryRunResult.summary.charts?.update + model.bulkMigrateModel.dryRunResult.summary.charts?.delete || 0} + {@const totalDatasets = model.bulkMigrateModel.dryRunResult.summary.datasets?.create + model.bulkMigrateModel.dryRunResult.summary.datasets?.update + model.bulkMigrateModel.dryRunResult.summary.datasets?.delete || 0} + {@const totalAll = totalDashboards + totalCharts + totalDatasets} +
+ + {($t.migration?.dry_run_summary || 'Dry-run: {total} items changed').replace('{total}', String(totalAll)).replace('{dashboards}', String(totalDashboards)).replace('{charts}', String(totalCharts)).replace('{datasets}', String(totalDatasets))} +
+ {/if} +
+ {/if} +
+
+
+ + +
+
+
+{/if} +