Migrate frontend to Svelte 5 runes semantics

This commit is contained in:
2026-03-11 11:29:24 +03:00
parent 765178f12e
commit 0083d9054e
61 changed files with 989 additions and 922 deletions

View File

@@ -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;
}}

View File

@@ -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>