feat(translate): add run preflight and focus execution UX
This commit is contained in:
@@ -8,9 +8,9 @@
|
||||
<!-- @RELATION DEPENDS_ON -> [TranslationRunResult] -->
|
||||
<!-- @RELATION DEPENDS_ON -> [TranslateMetricsRoutesModule] -->
|
||||
<!-- @RELATION BINDS_TO -> [translationRunStore] -->
|
||||
<!-- @UX_STATE Idle — no run active, run cards visible -->
|
||||
<!-- @UX_STATE Running — TranslationRunProgress bar visible, cards show active state -->
|
||||
<!-- @UX_STATE Completed — run history list with expandable details, summary metrics bar -->
|
||||
<!-- @UX_STATE Idle — run estimate and launch choices are the only primary content. -->
|
||||
<!-- @UX_STATE Running — TranslationRunProgress is the sole primary content; setup and history are hidden. -->
|
||||
<!-- @UX_STATE Completed — result remains primary; history and cumulative metrics are collapsed secondary content. -->
|
||||
<!-- @UX_STATE Error — error banner above run history -->
|
||||
<!-- @UX_FEEDBACK Toast on status change / run error / run complete -->
|
||||
<!-- @UX_FEEDBACK BulkReplace modal button in recent runs header -->
|
||||
@@ -61,6 +61,7 @@
|
||||
let metricsLoading = $state(false);
|
||||
let metricsError = $state(null);
|
||||
let wasRunActive = false;
|
||||
let secondaryDetailsOpen = $state(false);
|
||||
|
||||
// Confirmation dialog state
|
||||
let showConfirmDialog = $state(false);
|
||||
@@ -124,6 +125,7 @@
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (isRunActive) secondaryDetailsOpen = false;
|
||||
if (wasRunActive && !isRunActive) loadJobMetrics();
|
||||
wasRunActive = isRunActive;
|
||||
});
|
||||
@@ -199,12 +201,13 @@
|
||||
<!-- Status display + transition -->
|
||||
<div class="flex items-center gap-3 mb-4 p-3 bg-surface-muted rounded-lg">
|
||||
<span class="text-sm text-text-muted">{_t.translate?.config?.status}:</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {statusClass}">
|
||||
{getJobStatusLabel(status)}
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {isRunActive ? 'bg-primary-light text-primary' : statusClass}">
|
||||
{isRunActive ? (_t.translate?.config?.running || 'Running') : getJobStatusLabel(status)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
{#if !isRunActive}
|
||||
<!-- Run evidence: real source scope, local Lingua distribution, and cost before execution -->
|
||||
<div class="border border-border rounded-lg bg-surface-card p-4" aria-busy={preflightLoading}>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
@@ -303,6 +306,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if runError}
|
||||
<div class="bg-destructive-light border border-destructive-light rounded-lg p-3">
|
||||
@@ -319,6 +323,24 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !isRunActive}
|
||||
<!-- Secondary context: it must not compete with the next launch or an active run. -->
|
||||
<div class="rounded-lg border border-border bg-surface-card">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => secondaryDetailsOpen = !secondaryDetailsOpen}
|
||||
aria-expanded={secondaryDetailsOpen}
|
||||
class="flex w-full items-center justify-between gap-3 px-4 py-3 text-left text-sm font-medium text-text hover:bg-surface-muted"
|
||||
>
|
||||
<span>{_t.translate?.run?.secondary_details || 'History and job metrics'}</span>
|
||||
<span class="text-xs font-normal text-text-muted">
|
||||
{secondaryDetailsOpen
|
||||
? (_t.translate?.run?.hide_details || 'Hide details')
|
||||
: (_t.translate?.run?.show_details || 'Show details')}
|
||||
</span>
|
||||
</button>
|
||||
{#if secondaryDetailsOpen}
|
||||
<div class="border-t border-border p-4 space-y-4">
|
||||
<!-- Summary metrics bar -->
|
||||
{#if jobMetrics}
|
||||
<div class="bg-surface-card border border-border rounded-lg p-4">
|
||||
@@ -401,7 +423,7 @@
|
||||
{/if}
|
||||
|
||||
{#if completedRuns.length > 0}
|
||||
<div class="mt-4">
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h4 class="text-sm font-medium text-text">{_t.translate?.config?.recent_runs}</h4>
|
||||
<button
|
||||
@@ -489,6 +511,10 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -527,6 +527,7 @@
|
||||
"preflight_rows_sec": "rows/s",
|
||||
"preflight_languages": "Source language distribution",
|
||||
"preflight_lingua_slow": "Lingua exceeded the performance threshold. The run will continue without local language detection.",
|
||||
"secondary_details": "History and job metrics",
|
||||
"loading": "Loading run status...",
|
||||
"loading_result": "Loading result...",
|
||||
"result_title": "Run Result",
|
||||
|
||||
@@ -528,6 +528,7 @@
|
||||
"preflight_rows_sec": "строк/с",
|
||||
"preflight_languages": "Распределение исходных языков",
|
||||
"preflight_lingua_slow": "Lingua превысила порог производительности. Перевод продолжится без локального определения языка.",
|
||||
"secondary_details": "История и метрики задания",
|
||||
"loading": "Загрузка статуса запуска...",
|
||||
"loading_result": "Загрузка результата...",
|
||||
"result_title": "Результат запуска",
|
||||
|
||||
Reference in New Issue
Block a user