refactor(frontend): migrate inline SVGs to Icon component (10 files, 36 icons)
Replaced 36 inline <svg> patterns with <Icon name="..."> from $lib/ui: - GitManager (9), Toast (5), migration/+page (12), datasets/[id] (3), TaskRunner (2), GitWorkspacePanel (2), dashboards/+page (1), dashboards/[id] (1), TaskHistory (1)
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { HelpTooltip } from '$lib/ui';
|
||||
import { HelpTooltip, Icon } from '$lib/ui';
|
||||
import { stageBadgeClass, resolveDefaultConfig } from '../../../services/git-utils.js';
|
||||
import { GitManagerModel } from '$lib/models/GitManagerModel.svelte.ts';
|
||||
import BranchSelector from './BranchSelector.svelte';
|
||||
@@ -64,7 +64,7 @@
|
||||
<div class="flex items-center justify-between border-b border-border bg-gradient-to-r from-slate-50 to-white px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-primary text-white shadow-sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg>
|
||||
<Icon name="code" size={20} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-bold text-text">{$t.git?.management || 'Управление Git'}</h2>
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onclick={closeModal} class="flex h-9 w-9 items-center justify-center rounded-lg text-text-subtle transition-colors hover:bg-surface-muted hover:text-text" aria-label={$t.common?.close || 'Close'}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
|
||||
<Icon name="close" size={20} strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -85,9 +85,9 @@
|
||||
>
|
||||
<div class="mt-0.5 flex-shrink-0">
|
||||
{#if model.gitErrorType === 'warning'}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-warning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg>
|
||||
<Icon name="warning" size={20} class="text-warning" strokeWidth={2} />
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-destructive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 9l-6 6m0-6l6 6"/></svg>
|
||||
<Icon name="error" size={20} class="text-destructive" strokeWidth={2} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex-1 space-y-2">
|
||||
@@ -119,7 +119,7 @@
|
||||
class="flex-shrink-0 rounded p-1 transition-colors {model.gitErrorType === 'warning' ? 'hover:bg-warning-light' : 'hover:bg-destructive-light'}"
|
||||
aria-label="Закрыть"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
<Icon name="close" size={16} strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -144,7 +144,7 @@
|
||||
</span>
|
||||
<span class="hidden text-xs text-text-muted sm:inline">·</span>
|
||||
<span class="flex items-center gap-1.5 text-sm text-text-muted">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-text-subtle" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg>
|
||||
<Icon name="code" size={16} class="text-text-subtle" strokeWidth={2} />
|
||||
<span class="hidden sm:inline">Ветка:</span> <strong class="font-mono text-text">{model.currentBranch}</strong>
|
||||
</span>
|
||||
{#if model.changedFilesCount > 0}
|
||||
@@ -158,17 +158,17 @@
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-1 border-b border-border pb-0">
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors ${model.activeTab === 'workspace' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'}`} onclick={() => (model.activeTab = 'workspace')}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
|
||||
<Icon name="edit" size={16} strokeWidth={2} />
|
||||
Фиксация изменений
|
||||
<HelpTooltip text={$t.git?.hint_workspace || ''} />
|
||||
</button>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors ${model.activeTab === 'release' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'}`} onclick={() => (model.activeTab = 'release')}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
|
||||
<Icon name="lightning" size={16} strokeWidth={2} />
|
||||
Релиз
|
||||
<HelpTooltip text={$t.git?.hint_release || ''} />
|
||||
</button>
|
||||
<button class={`relative -mb-px inline-flex items-center gap-2 rounded-t-lg px-4 py-2.5 text-sm font-medium transition-colors ${model.activeTab === 'operations' ? 'border border-b-white bg-surface-card text-primary shadow-sm' : 'border border-transparent text-text-muted hover:bg-surface-muted hover:text-text'}`} onclick={() => (model.activeTab = 'operations')}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
|
||||
<Icon name="settings" size={16} strokeWidth={2} />
|
||||
Серверные операции
|
||||
<HelpTooltip text={$t.git?.hint_operations || ''} />
|
||||
</button>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- @UX_RECOVERY Scroll down to load more diff chunks; sentinel triggers lazy render. -->
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { Button } from "$lib/ui";
|
||||
import { Button, Icon } from "$lib/ui";
|
||||
import * as Diff2Html from 'diff2html';
|
||||
import 'diff2html/bundles/css/diff2html.min.css';
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
isLoading={loading}
|
||||
class="w-full"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-1.5 h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
|
||||
<Icon name="refresh" size={16} class="-ml-1 mr-1.5" strokeWidth={2} />
|
||||
Синхронизировать из Superset
|
||||
</Button>
|
||||
</div>
|
||||
@@ -169,7 +169,7 @@
|
||||
isLoading={committing}
|
||||
class="w-full"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-1.5 h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>
|
||||
<Icon name="check" size={16} class="-ml-1 mr-1.5" strokeWidth={2} />
|
||||
Зафиксировать (Commit)
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
import { auth } from "$lib/auth/store.svelte.js";
|
||||
import { hasPermission } from "$lib/auth/permissions.js";
|
||||
import { toggleAssistantChat } from "$lib/stores/assistantChat.svelte.js";
|
||||
import Icon from "$lib/ui/Icon.svelte";
|
||||
import { Icon } from "$lib/ui";
|
||||
import LanguageSwitcher from "$lib/ui/LanguageSwitcher.svelte";
|
||||
import {
|
||||
environmentContextStore,
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { selectedTask } from '$lib/stores.svelte.js';
|
||||
import { api, getTaskEventsWsUrl } from '$lib/api';
|
||||
import { ConfirmDialog } from '$lib/ui';
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { Icon } from '$lib/ui';
|
||||
|
||||
/** @type {string | undefined} — if set, only shows tasks with this plugin_id (e.g. "superset-migration") */
|
||||
let { pluginId = undefined } = $props();
|
||||
@@ -22,6 +24,8 @@
|
||||
let loading = $state(true);
|
||||
let error = $state("");
|
||||
let taskWs = null;
|
||||
let showClearTasks = $state(false);
|
||||
let pendingClearStatus = $state(null);
|
||||
|
||||
// #region fetchTasks:Function [TYPE Function]
|
||||
// @ingroup Tasks
|
||||
@@ -107,8 +111,14 @@
|
||||
// @PURPOSE: Clears tasks from the history, optionally filtered by status.
|
||||
// @PRE: User confirms deletion via prompt.
|
||||
// @POST: Tasks are deleted from backend and list is re-fetched.
|
||||
async function clearTasks(status = null) {
|
||||
if (!confirm($t.tasks?.clear_confirm )) return;
|
||||
function clearTasks(status = null) {
|
||||
pendingClearStatus = status;
|
||||
showClearTasks = true;
|
||||
}
|
||||
|
||||
async function confirmClearTasks() {
|
||||
const status = pendingClearStatus;
|
||||
pendingClearStatus = null;
|
||||
try {
|
||||
let endpoint = '/tasks';
|
||||
if (status) endpoint += `?status=${status}`;
|
||||
@@ -188,7 +198,7 @@
|
||||
<div class="relative inline-block text-left group">
|
||||
<button class="text-sm text-destructive hover:text-destructive focus:outline-none flex items-center py-2">
|
||||
{$t.tasks?.clear_tasks }
|
||||
<svg class="ml-1 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg>
|
||||
<Icon name="chevronDown" size={16} strokeWidth={2} />
|
||||
</button>
|
||||
<!-- Added a transparent bridge to prevent menu closing when moving cursor -->
|
||||
<div class="absolute h-2 w-full top-full left-0"></div>
|
||||
@@ -259,4 +269,16 @@
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
bind:show={showClearTasks}
|
||||
title={$t.tasks?.clear_title || 'Clear tasks?'}
|
||||
message={$t.tasks?.clear_confirm}
|
||||
variant="destructive"
|
||||
confirmLabel={$t.tasks?.clear || 'Clear'}
|
||||
cancelLabel={$t.common?.cancel || 'Cancel'}
|
||||
onConfirm={confirmClearTasks}
|
||||
onCancel={() => { pendingClearStatus = null; }}
|
||||
/>
|
||||
|
||||
<!-- #endregion TaskHistory -->
|
||||
|
||||
@@ -21,6 +21,7 @@ import { SvelteURLSearchParams, SvelteDate } from "svelte/reactivity";
|
||||
import PasswordPrompt from '$lib/components/migration/PasswordPrompt.svelte';
|
||||
import TaskLogPanel from './TaskLogPanel.svelte';
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { Icon } from '$lib/ui';
|
||||
// [/SECTION]
|
||||
|
||||
let ws;
|
||||
@@ -339,10 +340,7 @@ import { SvelteURLSearchParams, SvelteDate } from "svelte/reactivity";
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-surface-muted text-text-muted">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 20h9"></path>
|
||||
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
|
||||
</svg>
|
||||
<Icon name="edit" size={20} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-text">{$t.tasks?.task_label}</h2>
|
||||
@@ -424,10 +422,7 @@ import { SvelteURLSearchParams, SvelteDate } from "svelte/reactivity";
|
||||
|
||||
{#if waitingForData && connectionStatus === 'connected'}
|
||||
<div class="flex items-center gap-2 text-text-muted italic mt-3 animate-pulse text-xs">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
<Icon name="clock" size={14} strokeWidth={2} />
|
||||
{$t.tasks?.waiting_logs}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<!-- @UX_FEEDBACK Close button on persistent toasts; auto-dismiss on transient toasts. -->
|
||||
<script lang="ts">
|
||||
import { toasts, removeToast } from '$lib/toasts.svelte.js';
|
||||
import { Icon } from '$lib/ui';
|
||||
|
||||
function handleDismiss(id: string) {
|
||||
removeToast(id);
|
||||
@@ -31,13 +32,13 @@
|
||||
>
|
||||
<div class="mt-0.5 flex-shrink-0">
|
||||
{#if toast.type === 'warning'}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-warning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg>
|
||||
<Icon name="warning" size={20} class="text-warning" strokeWidth={2} />
|
||||
{:else if toast.type === 'error'}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-destructive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 9l-6 6m0-6l6 6"/></svg>
|
||||
<Icon name="error" size={20} class="text-destructive" strokeWidth={2} />
|
||||
{:else if toast.type === 'success'}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-success" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
<Icon name="check" size={20} class="text-success" strokeWidth={2} />
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path stroke-linecap="round" stroke-linejoin="round" d="M12 16v-4m0-4h.01"/></svg>
|
||||
<Icon name="info" size={20} class="text-primary" strokeWidth={2} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex-1 text-sm">
|
||||
@@ -54,7 +55,7 @@
|
||||
"
|
||||
aria-label="Закрыть"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
<Icon name="close" size={16} strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
import DashboardMaintenanceBadge from "$lib/components/DashboardMaintenanceBadge.svelte";
|
||||
|
||||
import { environmentContextStore, initializeEnvironmentContext, setSelectedEnvironment } from "$lib/stores/environmentContext.svelte.js";
|
||||
import { Button, Pagination, Skeleton } from "$lib/ui";
|
||||
import { Button, Icon, Pagination, Skeleton } from "$lib/ui";
|
||||
import ColumnFilterPopover from "./ColumnFilterPopover.svelte";
|
||||
import { formatDate, getSortIndicator } from "./dashboard-helpers.js";
|
||||
import { DashboardHubModel } from "$lib/models/DashboardHubModel.svelte.ts";
|
||||
@@ -632,18 +632,7 @@ class="inline-flex h-8 w-8 items-center justify-center rounded-md border border-
|
||||
onclick={() => m.handleAction(dashboard, "backup")}
|
||||
title={$t.dashboard?.action_backup}
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="7,10 12,15 17,10" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" />
|
||||
</svg>
|
||||
<Icon name="download" size={14} strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import { openDrawerForTaskIfPreferred } from '$lib/stores/taskDrawer.svelte.js';
|
||||
import { DashboardDetailModel } from '$lib/models/DashboardDetailModel.svelte';
|
||||
import { ROUTES } from '$lib/routes.js';
|
||||
import { Skeleton } from '$lib/ui';
|
||||
import { Icon, Skeleton } from '$lib/ui';
|
||||
|
||||
import CommitHistory from '$lib/components/git/CommitHistory.svelte';
|
||||
import GitManager from '$lib/components/git/GitManager.svelte';
|
||||
@@ -175,7 +175,7 @@
|
||||
<div class="border-t border-border px-3 py-2 text-right">
|
||||
<a href={ROUTES.dashboards.validation(m.resolvedDashboardId ?? m.dashboardRef, m.envId || '')} class="inline-flex items-center gap-1 text-xs font-medium text-primary hover:text-primary transition-colors">
|
||||
{$t.dashboard?.view_all_validation || 'View all validation runs'}
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
|
||||
<Icon name="chevronRight" size={14} strokeWidth={2} />
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/state';
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { Button, Skeleton } from '$lib/ui';
|
||||
import { Button, Icon, Skeleton } from '$lib/ui';
|
||||
import { DatasetDetailModel } from '$lib/models/DatasetDetailModel.svelte';
|
||||
|
||||
let datasetId = $derived(page.params.id);
|
||||
@@ -34,9 +34,7 @@
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<Button variant="ghost" class="flex items-center gap-2 text-text-muted hover:text-text" onclick={() => model.goBack()}>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
||||
</svg>
|
||||
<Icon name="back" size={20} strokeWidth={2} />
|
||||
{$t.common?.back}
|
||||
</Button>
|
||||
{#if model.dataset}
|
||||
@@ -139,19 +137,13 @@
|
||||
<Button type="button" variant="ghost" class="flex w-full items-center gap-3 p-3 border border-border rounded-lg hover:bg-surface-muted transition-colors text-left"
|
||||
onclick={() => model.navigateToDashboard(dashboard)}>
|
||||
<div class="w-8 h-8 bg-primary-light rounded-lg flex items-center justify-center text-primary">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
||||
<line x1="3" y1="9" x2="21" y2="9" />
|
||||
<line x1="9" y1="21" x2="9" y2="9" />
|
||||
</svg>
|
||||
<Icon name="dashboard" size={16} strokeWidth={2} />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-text">{dashboard.title}</div>
|
||||
<div class="text-xs text-text-muted">ID: {dashboard.id}{#if dashboard.slug} • {dashboard.slug}{/if}</div>
|
||||
</div>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-text-subtle">
|
||||
<path d="M9 18l6-6-6-6" />
|
||||
</svg>
|
||||
<Icon name="chevronRight" size={16} class="text-text-subtle" strokeWidth={2} />
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
import { MigrationModel } from "../../lib/models/MigrationModel.svelte.ts";
|
||||
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { Button, Card, PageHeader } from "$lib/ui";
|
||||
import { Button, Card, Icon, PageHeader } from "$lib/ui";
|
||||
|
||||
// ── Model: single source of truth ───────────────────────────────
|
||||
const model = new MigrationModel();
|
||||
@@ -87,13 +87,9 @@
|
||||
variant="ghost"
|
||||
onclick={() => model.toggleTaskHistory()}
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
|
||||
</svg>
|
||||
<Icon name="clipboard" size={16} strokeWidth={2} />
|
||||
{$t.tasks?.recent || "Task History"}
|
||||
<svg class="w-4 h-4 transition-transform" class:rotate-180={model.showTaskHistory} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
<Icon name="chevronDown" size={16} class="transition-transform {model.showTaskHistory ? 'rotate-180' : ''}" strokeWidth={2} />
|
||||
</Button>
|
||||
</div>
|
||||
<!-- #endregion MigrationHeader -->
|
||||
@@ -147,9 +143,7 @@
|
||||
${(s.step <= model.currentStep || (s.step === 2 && model.stepReady[1]) || (s.step === 3 && model.stepReady[1] && model.stepReady[2])) ? 'cursor-pointer hover:border-primary-ring' : 'cursor-not-allowed'}
|
||||
`}>
|
||||
{#if model.currentStep > s.step}
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<Icon name="check" size={20} strokeWidth={2.5} />
|
||||
{:else}
|
||||
{s.step}
|
||||
{/if}
|
||||
@@ -195,9 +189,7 @@
|
||||
<p class="text-sm font-medium text-destructive">{model.error}</p>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" onclick={() => model.clearError()} aria-label={$t.common?.close || "Close"}>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
<Icon name="close" size={16} strokeWidth={2} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -240,9 +232,7 @@
|
||||
{#if model.sourceEnvId && model.targetEnvId && model.sourceEnvId === model.targetEnvId}
|
||||
<div class="mt-4 p-3 bg-warning-light border border-warning-ring rounded-lg">
|
||||
<p class="text-sm text-warning flex items-center gap-2">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
||||
</svg>
|
||||
<Icon name="warning" size={16} class="flex-shrink-0" strokeWidth={2} />
|
||||
{$t.migration?.different_envs || "Source and target environments must be different."}
|
||||
</p>
|
||||
</div>
|
||||
@@ -333,17 +323,13 @@
|
||||
onclick={() => model.fetchDatabases()}
|
||||
disabled={model.fetchingDbs || !model.sourceEnvId || !model.targetEnvId}
|
||||
>
|
||||
<svg class="w-3.5 h-3.5" class:animate-spin={model.fetchingDbs} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
<Icon name="refresh" size={14} class={model.fetchingDbs ? 'animate-spin' : ''} strokeWidth={2} />
|
||||
{$t.migration?.fetch_dbs || "Fetch Databases"}
|
||||
</Button>
|
||||
</div>
|
||||
{#if model.fetchingDbs}
|
||||
<div class="flex items-center gap-2 text-sm text-text-muted">
|
||||
<svg class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
<Icon name="refresh" size={16} class="animate-spin" strokeWidth={2} />
|
||||
<span>{$t.migration?.loading_dbs || "Loading databases and suggestions..."}</span>
|
||||
</div>
|
||||
{:else if model.sourceDatabases.length > 0}
|
||||
@@ -371,7 +357,7 @@
|
||||
disabled={!model.stepReady[2] || model.dryRunLoading}
|
||||
>
|
||||
{#if model.dryRunLoading}
|
||||
<svg class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg>
|
||||
<Icon name="refresh" size={16} class="animate-spin" strokeWidth={2} />
|
||||
{/if}
|
||||
{$t.dashboard?.view_summary || "Dry Run"}
|
||||
</Button>
|
||||
@@ -415,9 +401,7 @@
|
||||
<div class="p-4 border border-border rounded-xl bg-surface-card">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<div class="w-8 h-8 rounded-lg bg-info-light flex items-center justify-center">
|
||||
<svg class="w-4 h-4 text-info" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
|
||||
</svg>
|
||||
<Icon name="dashboard" size={16} class="text-info" strokeWidth={2} />
|
||||
</div>
|
||||
<h4 class="text-sm font-semibold text-text">{$t.dashboard?.title || "Dashboards"}</h4>
|
||||
</div>
|
||||
@@ -467,9 +451,7 @@
|
||||
<div class="p-4 border border-border rounded-xl bg-surface-card">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<div class="w-8 h-8 rounded-lg bg-success-light flex items-center justify-center">
|
||||
<svg class="w-4 h-4 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4" />
|
||||
</svg>
|
||||
<Icon name="database" size={16} class="text-success" strokeWidth={2} />
|
||||
</div>
|
||||
<h4 class="text-sm font-semibold text-text">{$t.dashboard?.linked_resources || "Datasets"}</h4>
|
||||
</div>
|
||||
@@ -561,9 +543,7 @@
|
||||
<!-- Raw JSON Details -->
|
||||
<details class="border border-border rounded-xl overflow-hidden">
|
||||
<summary class="px-4 py-3 bg-surface-muted cursor-pointer text-sm font-medium text-text hover:bg-surface-muted transition-colors flex items-center gap-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
||||
</svg>
|
||||
<Icon name="code" size={16} strokeWidth={2} />
|
||||
{$t.tasks?.diff_preview || "Diff JSON"}
|
||||
</summary>
|
||||
<pre class="p-4 max-h-72 overflow-auto text-xs bg-surface-muted text-text">{JSON.stringify(model.dryRunResult, null, 2)}</pre>
|
||||
@@ -600,9 +580,7 @@
|
||||
{#if model.currentStep === 4 && !activeTask}
|
||||
<Card padding="none" class="mb-6">
|
||||
<div class="flex flex-col items-center justify-center p-12">
|
||||
<svg class="w-16 h-16 text-info mb-4 animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
<Icon name="lightning" size={64} class="text-info mb-4 animate-pulse" strokeWidth={1.5} />
|
||||
<h3 class="text-lg font-semibold text-text mb-2">{$t.migration?.starting || "Starting Migration..."}</h3>
|
||||
<p class="text-sm text-text-muted">{$t.common.loading || "Please wait..."}</p>
|
||||
<Button variant="secondary" class="mt-6" onclick={() => model.goToStep(3)}>
|
||||
|
||||
Reference in New Issue
Block a user