From 97660cb71f19c99088160bd014356a92ea0a1617 Mon Sep 17 00:00:00 2001 From: busya Date: Sun, 5 Jul 2026 09:16:32 +0300 Subject: [PATCH] =?UTF-8?q?refactor(frontend):=20extract=20TranslationRunR?= =?UTF-8?q?esult.Model=20=E2=80=94=20state=20+=20API=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TranslationRunResultModel.svelte.ts (200 LOC): 16 $state atoms, 3 $derived projections, 5 API actions (loadData, handleRetry, handleRetryInsert, loadMoreRecords, loadBatches), @RATIONALE/@REJECTED - TranslationRunResult.svelte: 623→456 LOC (167 lines saved, -27%), delegates all state/API to model, retains template markup and DOM helpers (copyToClipboard) - INV_1: 0 naked functions (all logic in model) - Follows TopNavbar pattern — dense anchor, hierarchical ID, BINDS_TO -> [TranslationRunResult.Model] --- .../translate/TranslationRunResult.svelte | 387 +++++------------- .../TranslationRunResultModel.svelte.ts | 210 ++++++++++ 2 files changed, 320 insertions(+), 277 deletions(-) create mode 100644 frontend/src/lib/models/TranslationRunResultModel.svelte.ts diff --git a/frontend/src/lib/components/translate/TranslationRunResult.svelte b/frontend/src/lib/components/translate/TranslationRunResult.svelte index ebd47619..1b7be9a5 100644 --- a/frontend/src/lib/components/translate/TranslationRunResult.svelte +++ b/frontend/src/lib/components/translate/TranslationRunResult.svelte @@ -1,9 +1,17 @@ - + + + + + + - + + + + @@ -13,208 +21,43 @@
- {#if !status} + {#if !model.status}

{_t.translate?.run?.loading_result}

@@ -225,7 +68,7 @@ import { SvelteSet } from "svelte/reactivity";

{_t.translate?.run?.result_title}

- {#if (uxState === 'insert_failed' || uxState === 'failed' || uxState === 'partial') && (status.successful_records || 0) > 0} + {#if (model.uxState === 'insert_failed' || model.uxState === 'failed' || model.uxState === 'partial') && (model.status?.successful_records || 0) > 0} {/if} - {#if uxState === 'failed' || uxState === 'partial'} + {#if model.uxState === 'failed' || model.uxState === 'partial'} {/if} - {#if uxState === 'insert_failed'} + {#if model.uxState === 'insert_failed'} {/if}
{_t.translate?.run?.status_label || 'Status'} - {getRunStatusLabel(status.status)} + {model.getRunStatusLabel(model.status?.status)}
{_t.translate?.run?.insert_status} - - {insertStatusBadge.label} + + {model.insertStatusBadge.label}
{_t.translate?.run?.insert_method || 'Insert Method'} - - {insertMethodBadge.label} + + {model.insertMethodBadge.label} - {#if insertMethodBadge.connectionName} - · {insertMethodBadge.connectionName} + {#if model.insertMethodBadge.connectionName} + · {model.insertMethodBadge.connectionName} {/if}
- {#if status.superset_execution_id} + {#if model.status?.superset_execution_id}
{_t.translate?.run?.superset_query} - {status.superset_execution_id} + {model.status?.superset_execution_id}
{/if} - {#if status.error_message} + {#if model.status?.error_message}
- {_t.translate?.run?.error_label?.replace('{error}', status.error_message)} + {_t.translate?.run?.error_label?.replace('{error}', model.status?.error_message)}
{/if}
@@ -375,33 +218,23 @@ import { SvelteSet } from "svelte/reactivity";
- {#if showBatches} + {#if model.showBatches}
- {#if batchesLoading} + {#if model.batchesLoading}
{_t.translate?.common?.loading}
- {:else if batches.length === 0} + {:else if model.batcheslength === 0}

{_t.translate?.common?.unknown}

{:else} {#each batches as batch, idx} @@ -434,66 +267,66 @@ import { SvelteSet } from "svelte/reactivity";
- {#if status.event_invariants} + {#if model.status?.event_invariants}
{_t.translate?.run?.event_invariants} - - {status.event_invariants.invariant_valid ? _t.translate?.run?.valid : _t.translate?.run?.violated} + + {model.status?.event_invariants.invariant_valid ? _t.translate?.run?.valid : _t.translate?.run?.violated} | - {_t.translate?.run?.superset_query_id_label} {status.event_invariants.has_run_started ? _t.translate?.run?.started_yes : _t.translate?.run?.started_no} + {_t.translate?.run?.superset_query_id_label} {model.status?.event_invariants.has_run_started ? _t.translate?.run?.started_yes : _t.translate?.run?.started_no} | - {_t.translate?.run?.terminal_events?.replace('{count}', status.event_invariants.terminal_event_count)} + {_t.translate?.run?.terminal_events?.replace('{count}', model.status?.event_invariants.terminal_event_count)}
{/if} - {#if records.length > 0} + {#if model.records.length > 0}
- {#if !allRecordsLoaded && recordsTotal > records.length} + {#if !model.allRecordsLoaded && model.recordsTotal > model.records.length} {/if}
- {#if showRecords} + {#if model.showRecords}
{#each ['all','success','failed','skipped'] as filter} @@ -506,14 +339,14 @@ import { SvelteSet } from "svelte/reactivity"; # {_t.translate?.preview?.table_source || 'Source'} - {#each targetLanguages as lang} + {#each model.targetLanguages as lang}
{lang}
{/each} - {#if targetLanguages.length === 0} + {#if model.targetLanguages.length === 0} {_t.translate?.preview?.translation || 'Translation'} {/if} {_t.translate?.preview?.table_status || 'Status'} @@ -521,7 +354,7 @@ import { SvelteSet } from "svelte/reactivity"; - {#each filteredRecords as row, idx} + {#each model.filteredRecords as row, idx} {idx + 1} @@ -532,8 +365,8 @@ import { SvelteSet } from "svelte/reactivity";
{/if} - {#if targetLanguages.length > 0} - {#each targetLanguages as lang} + {#if model.targetLanguages.length > 0} + {#each model.targetLanguages as lang} {@const langData = (row.languages || []).find(l => l.language_code === lang)} {@const cellValue = langData?.final_value || langData?.translated_value || ''} {@const detectedSource = langData?.source_language_detected || row.source_language_detected || ''} @@ -572,11 +405,11 @@ import { SvelteSet } from "svelte/reactivity";
- {#if recordsTotal > 0} + {#if model.recordsTotal > 0}

{(_t.translate?.run?.showing_records || 'Showing {count} of {total} unique rows.') - .replace('{count}', filteredRecords.length) - .replace('{total}', recordsTotal)} + .replace('{count}', model.filteredRecords.length) + .replace('{total}', model.recordsTotal)}

{/if} {/if} @@ -608,16 +441,16 @@ import { SvelteSet } from "svelte/reactivity"; correctionPopupData = null} - onSubmitted={() => { correctionPopupData = null; loadData(); onRefresh(); }} + {...(model.correctionPopupData || {})} + onClose={() => model.correctionPopupData = null} + onSubmitted={() => { model.correctionPopupData = null; model.loadData(runId); onRefresh(); }} /> showBulkReplace = false} - onApplied={(_count) => { showBulkReplace = false; loadData(); onRefresh(); }} + {model.targetLanguages} + onClose={() => model.showBulkReplace = false} + onApplied={(_count) => { model.showBulkReplace = false; model.loadData(runId); onRefresh(); }} /> diff --git a/frontend/src/lib/models/TranslationRunResultModel.svelte.ts b/frontend/src/lib/models/TranslationRunResultModel.svelte.ts new file mode 100644 index 00000000..14e1ba2d --- /dev/null +++ b/frontend/src/lib/models/TranslationRunResultModel.svelte.ts @@ -0,0 +1,210 @@ +// #region TranslationRunResult.Model [C:4] [TYPE Model] [SEMANTICS translate,run-result,model,state,records] +// @defgroup Translate Run result state model — fetches status, records, batches; manages retry and pagination. +// @LAYER Store +// @PRE runId is set before any data is loaded. +// @INVARIANT loadedRunId guards against redundant reloads — loadData() only executes when runId changes. +// @INVARIANT records pagination: page increments atomically, allRecordsLoaded is set when records.length >= recordsTotal. +// @STATE status, records, batches, uxState, isRetrying, isRetryingInsert, showBulkReplace, correctionPopupData, targetLanguages, showBatches, batchesLoading, recordsTotal, recordsPage, recordsLoadingMore, allRecordsLoaded, showRecords, recordStatusFilter +// @ACTION loadData(runId), handleRetry(runId, onRefresh), handleRetryInsert(runId, onRefresh), loadMoreRecords(runId), loadBatches(runId) +// @RELATION CALLS -> [fetchRunStatus] +// @RELATION CALLS -> [fetchRunRecords] +// @RELATION CALLS -> [fetchRunBatches] +// @RELATION CALLS -> [retryFailedBatches] +// @RELATION CALLS -> [retryInsert] +// @DATA_CONTRACT Input: runId → Output: status: RunStatus, records: Record[], derived badges +// @POST All data fetched on runId change. Retry actions reload data and signal parent via onRefresh callback. +// @SIDE_EFFECT API fetch (status, records, batches, retry), toast notifications via addToast. +// @RATIONALE TranslationRunResult.svelte (623 LOC → ~350 LOC after extraction) violated INV_7. State management (16 $state atoms, 6 API calls, 3 $derived projections) was scattered across the component's