Migrate frontend to Svelte 5 runes semantics
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
<script lang="ts">
|
||||
// [SECTION: IMPORTS]
|
||||
import { onMount } from "svelte";
|
||||
import { fromStore } from "svelte/store";
|
||||
import EnvSelector from "../../components/EnvSelector.svelte";
|
||||
import DashboardGrid from "../../components/DashboardGrid.svelte";
|
||||
import MappingTable from "../../components/MappingTable.svelte";
|
||||
@@ -65,33 +66,35 @@
|
||||
// [/SECTION]
|
||||
|
||||
// [SECTION: STATE]
|
||||
let environments: any[] = [];
|
||||
let sourceEnvId = "";
|
||||
let targetEnvId = "";
|
||||
let replaceDb = false;
|
||||
let fixCrossFilters = true;
|
||||
let loading = true;
|
||||
let error = "";
|
||||
let dashboards: DashboardMetadata[] = [];
|
||||
let selectedDashboardIds: number[] = [];
|
||||
let sourceDatabases: any[] = [];
|
||||
let targetDatabases: any[] = [];
|
||||
let mappings: any[] = [];
|
||||
let suggestions: any[] = [];
|
||||
let fetchingDbs = false;
|
||||
let dryRunLoading = false;
|
||||
let dryRunResult: MigrationDryRunResult | null = null;
|
||||
let environments: any[] = $state([]);
|
||||
let sourceEnvId = $state("");
|
||||
let targetEnvId = $state("");
|
||||
let replaceDb = $state(false);
|
||||
let fixCrossFilters = $state(true);
|
||||
let loading = $state(true);
|
||||
let error = $state("");
|
||||
let dashboards: DashboardMetadata[] = $state([]);
|
||||
let selectedDashboardIds: number[] = $state([]);
|
||||
let sourceDatabases: any[] = $state([]);
|
||||
let targetDatabases: any[] = $state([]);
|
||||
let mappings: any[] = $state([]);
|
||||
let suggestions: any[] = $state([]);
|
||||
let fetchingDbs = $state(false);
|
||||
let dryRunLoading = $state(false);
|
||||
let dryRunResult: MigrationDryRunResult | null = $state(null);
|
||||
|
||||
// UI State for Modals
|
||||
let showLogViewer = false;
|
||||
let logViewerTaskId: string | null = null;
|
||||
let logViewerTaskStatus: string | null = null;
|
||||
let showLogViewer = $state(false);
|
||||
let logViewerTaskId: string | null = $state(null);
|
||||
let logViewerTaskStatus: string | null = $state(null);
|
||||
|
||||
let showPasswordPrompt = false;
|
||||
let passwordPromptDatabases: string[] = [];
|
||||
let passwordPromptErrorMessage = "";
|
||||
let showPasswordPrompt = $state(false);
|
||||
let passwordPromptDatabases: string[] = $state([]);
|
||||
let passwordPromptErrorMessage = $state("");
|
||||
// [/SECTION]
|
||||
|
||||
const selectedTaskState = fromStore(selectedTask);
|
||||
|
||||
const belief_scope = <T>(_id: string, fn: () => T): T => fn();
|
||||
|
||||
// [DEF:fetchEnvironments:Function]
|
||||
@@ -148,10 +151,11 @@
|
||||
* @POST: fetchDashboards is called with the new sourceEnvId.
|
||||
* @UX_STATE: [Loading] -> Triggered when sourceEnvId changes.
|
||||
*/
|
||||
$: if (sourceEnvId) {
|
||||
$effect(() => {
|
||||
if (!sourceEnvId) return;
|
||||
console.info("[ReactiveDashboardFetch][REASON] Source environment changed, fetching dashboards", { sourceEnvId });
|
||||
fetchDashboards(sourceEnvId);
|
||||
}
|
||||
void fetchDashboards(sourceEnvId);
|
||||
});
|
||||
// [/DEF:ReactiveDashboardFetch:Block]
|
||||
|
||||
// [DEF:fetchDatabases:Function]
|
||||
@@ -273,24 +277,23 @@
|
||||
* @POST: showPasswordPrompt is set to true if input_request is database_password.
|
||||
* @UX_STATE: [AwaitingInput] -> Password prompt modal is displayed.
|
||||
*/
|
||||
$: if (
|
||||
$selectedTask &&
|
||||
$selectedTask.status === "AWAITING_INPUT" &&
|
||||
$selectedTask.input_request
|
||||
) {
|
||||
const req = $selectedTask.input_request;
|
||||
$effect(() => {
|
||||
const activeTask = selectedTaskState.current;
|
||||
if (
|
||||
!activeTask ||
|
||||
activeTask.status !== "AWAITING_INPUT" ||
|
||||
!activeTask.input_request
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const req = activeTask.input_request;
|
||||
if (req.type === "database_password") {
|
||||
console.info("[ReactivePasswordPrompt][REASON] Task awaiting database passwords", { taskId: $selectedTask.id });
|
||||
console.info("[ReactivePasswordPrompt][REASON] Task awaiting database passwords", { taskId: activeTask.id });
|
||||
passwordPromptDatabases = req.databases || [];
|
||||
passwordPromptErrorMessage = req.error_message || "";
|
||||
showPasswordPrompt = true;
|
||||
}
|
||||
} else if (!$selectedTask || $selectedTask.status !== "AWAITING_INPUT") {
|
||||
// Close prompt if task is no longer waiting (e.g. resumed)
|
||||
// But only if we are viewing this task.
|
||||
// showPasswordPrompt = false;
|
||||
// Actually, don't auto-close, let the user or success handler close it.
|
||||
}
|
||||
});
|
||||
// [/DEF:ReactivePasswordPrompt:Block]
|
||||
// [/DEF:handlePasswordPrompt:Function]
|
||||
|
||||
@@ -300,11 +303,11 @@
|
||||
// @POST: resumeTask is called and showPasswordPrompt is hidden on success.
|
||||
async function handleResumeMigration(event: CustomEvent) {
|
||||
return belief_scope("handleResumeMigration", async () => {
|
||||
if (!$selectedTask) return;
|
||||
if (!selectedTaskState.current) return;
|
||||
|
||||
const { passwords } = event.detail;
|
||||
try {
|
||||
await resumeTask($selectedTask.id, passwords);
|
||||
await resumeTask(selectedTaskState.current.id, passwords);
|
||||
showPasswordPrompt = false;
|
||||
// Task status update will be handled by store/websocket
|
||||
} catch (e) {
|
||||
@@ -470,7 +473,7 @@
|
||||
<!-- [/DEF:MigrationHeader:Block] -->
|
||||
|
||||
<!-- [DEF:TaskHistorySection:Block] -->
|
||||
<TaskHistory on:viewLogs={handleViewLogs} />
|
||||
<TaskHistory />
|
||||
<!-- [/DEF:TaskHistorySection:Block] -->
|
||||
|
||||
<!-- [DEF:ActiveTaskSection:Block] -->
|
||||
@@ -478,7 +481,7 @@
|
||||
<div class="mt-6">
|
||||
<TaskRunner />
|
||||
<div class="mt-4">
|
||||
<Button variant="secondary" on:click={() => {
|
||||
<Button variant="secondary" onclick={() => {
|
||||
console.info("[ActiveTaskSection][REASON] User cancelled active task view");
|
||||
selectedTask.set(null);
|
||||
}}>
|
||||
@@ -554,7 +557,7 @@
|
||||
id="replace-db"
|
||||
type="checkbox"
|
||||
bind:checked={replaceDb}
|
||||
on:change={() => {
|
||||
onchange={() => {
|
||||
console.info("[MigrationOptionsSection][REASON] Database replacement toggled", { replaceDb });
|
||||
if (replaceDb && sourceDatabases.length === 0) fetchDatabases();
|
||||
}}
|
||||
@@ -583,11 +586,11 @@
|
||||
{targetDatabases}
|
||||
{mappings}
|
||||
{suggestions}
|
||||
on:update={handleMappingUpdate}
|
||||
onupdate={handleMappingUpdate}
|
||||
/>
|
||||
{:else if sourceEnvId && targetEnvId}
|
||||
<button
|
||||
on:click={fetchDatabases}
|
||||
onclick={fetchDatabases}
|
||||
class="text-indigo-600 hover:text-indigo-500 text-sm font-medium"
|
||||
>
|
||||
{$t.migration?.refresh_dbs }
|
||||
@@ -599,7 +602,7 @@
|
||||
<div class="flex items-center gap-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
on:click={startDryRun}
|
||||
onclick={startDryRun}
|
||||
disabled={!sourceEnvId ||
|
||||
!targetEnvId ||
|
||||
sourceEnvId === targetEnvId ||
|
||||
@@ -610,7 +613,7 @@
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
on:click={startMigration}
|
||||
onclick={startMigration}
|
||||
disabled={!sourceEnvId ||
|
||||
!targetEnvId ||
|
||||
sourceEnvId === targetEnvId ||
|
||||
@@ -675,7 +678,7 @@
|
||||
bind:show={showLogViewer}
|
||||
taskId={logViewerTaskId}
|
||||
taskStatus={logViewerTaskStatus}
|
||||
on:close={() => {
|
||||
onclose={() => {
|
||||
console.info("[MigrationModals][REASON] Closing log viewer");
|
||||
showLogViewer = false;
|
||||
}}
|
||||
@@ -685,8 +688,8 @@
|
||||
bind:show={showPasswordPrompt}
|
||||
databases={passwordPromptDatabases}
|
||||
errorMessage={passwordPromptErrorMessage}
|
||||
on:resume={handleResumeMigration}
|
||||
on:cancel={() => {
|
||||
onresume={handleResumeMigration}
|
||||
oncancel={() => {
|
||||
console.info("[MigrationModals][REASON] User cancelled password prompt");
|
||||
showPasswordPrompt = false;
|
||||
}}
|
||||
|
||||
@@ -181,19 +181,19 @@
|
||||
label={$t.migration?.source_env }
|
||||
bind:selectedId={sourceEnvId}
|
||||
{environments}
|
||||
on:change={() => { sourceDatabases = []; mappings = []; suggestions = []; }}
|
||||
onchange={() => { sourceDatabases = []; mappings = []; suggestions = []; }}
|
||||
/>
|
||||
<EnvSelector
|
||||
label={$t.migration?.target_env }
|
||||
bind:selectedId={targetEnvId}
|
||||
{environments}
|
||||
on:change={() => { targetDatabases = []; mappings = []; suggestions = []; }}
|
||||
onchange={() => { targetDatabases = []; mappings = []; suggestions = []; }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-8">
|
||||
<Button
|
||||
on:click={fetchDatabases}
|
||||
onclick={fetchDatabases}
|
||||
disabled={!sourceEnvId || !targetEnvId || sourceEnvId === targetEnvId || fetchingDbs}
|
||||
isLoading={fetchingDbs}
|
||||
>
|
||||
@@ -219,7 +219,7 @@
|
||||
{targetDatabases}
|
||||
{mappings}
|
||||
{suggestions}
|
||||
on:update={handleUpdate}
|
||||
onupdate={handleUpdate}
|
||||
/>
|
||||
{:else if !fetchingDbs && sourceEnvId && targetEnvId}
|
||||
<p class="text-gray-500 italic">{$t.migration?.mapping_hint }</p>
|
||||
|
||||
Reference in New Issue
Block a user