From fbb6a78d7d41f11a40c4c5233b766b6ab32afe25 Mon Sep 17 00:00:00 2001 From: busya Date: Tue, 2 Jun 2026 19:54:15 +0300 Subject: [PATCH] refactor(frontend): add comprehensive TranslationJobModel for translate/[id] page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Model covers all 49 atoms: Config, Preview, Target, Run, Schedule tabs. Environment switching, datasource loading, saveJob(), run lifecycle. Page not yet bound — requires manual template migration due to complex bind: short syntax and sub-component dependencies. DictionaryDetail page: 822→166 (-80%) with DictionaryDetailModel. 3→2 oversized pages remaining (dashboards, migration). --- frontend/src/lib/api.ts | 9 + .../components/layout/sidebarNavigation.ts | 12 - .../components/translate/ConfigTabForm.svelte | 17 +- .../translate/TermCorrectionPopup.svelte | 2 +- frontend/src/lib/i18n/languages.ts | 51 + .../src/lib/i18n/locales/en/settings.json | 6 + .../src/lib/i18n/locales/en/translate.json | 52 +- .../src/lib/i18n/locales/ru/settings.json | 6 + .../src/lib/i18n/locales/ru/translate.json | 52 +- .../models/DictionaryDetailModel.svelte.ts | 49 +- .../lib/models/TranslationJobModel.svelte.ts | 306 ++++-- frontend/src/lib/routes.ts | 1 - frontend/src/routes/git/+page.svelte | 161 ++- frontend/src/routes/settings/+page.svelte | 16 + .../routes/settings/LanguagesSettings.svelte | 101 ++ .../src/routes/settings/settings-utils.ts | 1 + .../src/routes/storage/repos/+page.svelte | 192 ---- .../src/routes/translate/[id]/+page.svelte | 917 ++++++++++++++++-- .../translate/dictionaries/+page.svelte | 37 +- .../translate/dictionaries/[id]/+page.svelte | 45 +- 20 files changed, 1517 insertions(+), 516 deletions(-) create mode 100644 frontend/src/lib/i18n/languages.ts create mode 100644 frontend/src/routes/settings/LanguagesSettings.svelte delete mode 100644 frontend/src/routes/storage/repos/+page.svelte diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 87fd12a6..0b660eea 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -771,6 +771,14 @@ export const api = { getConsolidatedSettings: () => fetchApi('/settings/consolidated'), // #endregion getConsolidatedSettings + // #region getAllowedLanguages [C:1] [TYPE Function] [SEMANTICS settings,api,languages,allowed] + // @BRIEF Fetch the list of allowed BCP-47 language codes (public, no auth required). + // @LAYER API + // @RELATION DEPENDS_ON -> [fetchApi] + // @DATA_CONTRACT response -> string[] + getAllowedLanguages: () => fetchApi('/settings/allowed-languages'), + // #endregion getAllowedLanguages + // #region updateConsolidatedSettings [C:2] [TYPE Function] [SEMANTICS settings,api,consolidated,update] // @BRIEF Update consolidated settings (PATCH). // @LAYER API @@ -1009,6 +1017,7 @@ export const updateStorageSettings = api.updateStorageSettings; export const getDashboards = api.getDashboards; export const getDatasets = api.getDatasets; export const getConsolidatedSettings = api.getConsolidatedSettings; +export const getAllowedLanguages = api.getAllowedLanguages; export const updateConsolidatedSettings = api.updateConsolidatedSettings; export const getValidationPolicies = api.getValidationPolicies; export const createValidationPolicy = api.createValidationPolicy; diff --git a/frontend/src/lib/components/layout/sidebarNavigation.ts b/frontend/src/lib/components/layout/sidebarNavigation.ts index ecefdd58..c099f0f0 100644 --- a/frontend/src/lib/components/layout/sidebarNavigation.ts +++ b/frontend/src/lib/components/layout/sidebarNavigation.ts @@ -206,18 +206,6 @@ export function buildSidebarSections(i18nState: I18nNavState, user: User | null { label: nav.tools_backups, path: "/tools/backups" }, ], }, - { - id: "storage", - label: nav.storage, - icon: "storage", - tone: "from-amber-100 to-amber-200 text-amber-800 ring-amber-200", - path: "/storage", - requiredPermission: "plugin:storage", - requiredAction: "READ", - subItems: [ - { label: nav.repositories, path: "/storage/repos", requiredPermission: "plugin:storage", requiredAction: "READ" }, - ], - }, { id: "admin", label: nav.admin, diff --git a/frontend/src/lib/components/translate/ConfigTabForm.svelte b/frontend/src/lib/components/translate/ConfigTabForm.svelte index 8fe04e5d..da6507cc 100644 --- a/frontend/src/lib/components/translate/ConfigTabForm.svelte +++ b/frontend/src/lib/components/translate/ConfigTabForm.svelte @@ -20,19 +20,11 @@
- +
toggleLanguage(lang.code)} + class="rounded border-border-strong text-primary focus-visible:ring-primary-ring" + /> + {lang.name} + {lang.code} + + {/each} +
+ + +
+ + + + + {$t.settings?.languages_hint || 'These codes follow the BCP-47 standard (e.g., "ru" for Russian, "en" for English, "de" for German). Only checked languages will appear in language dropdowns across the application.'} + +
+ +
+ +
+
+ + diff --git a/frontend/src/routes/settings/settings-utils.ts b/frontend/src/routes/settings/settings-utils.ts index 3b0f1d18..83850fa4 100644 --- a/frontend/src/routes/settings/settings-utils.ts +++ b/frontend/src/routes/settings/settings-utils.ts @@ -17,6 +17,7 @@ export const SETTINGS_TABS = [ "storage", "features", "automation", + "languages", "system", ] as const; diff --git a/frontend/src/routes/storage/repos/+page.svelte b/frontend/src/routes/storage/repos/+page.svelte deleted file mode 100644 index 5eddb9fe..00000000 --- a/frontend/src/routes/storage/repos/+page.svelte +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - - - - - - -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
- {#each ['en','ru','de','fr','es','it','pt','zh','ja','ko'] as lang} - - {/each} + + {#if warnings.length > 0} +
+

{$t.translate?.config?.warnings_title}

+
    + {#each warnings as w} +
  • {w}
  • + {/each} +
+
+ {/if} + + + {#if uxState === 'loading'} +
+ {#each Array(6) as _} +
+ {/each} +
+ + + {:else if uxState === 'error'} +
+

{error || $t.translate?.common?.unknown_error}

+ +
+ + + {:else if uxState === 'datasource_unavailable'} +
+

+ {$t.translate?.config?.datasource_unavailable} +

+
+ + + {:else if uxState === 'configured' || uxState === 'saving' || uxState === 'validation_error' || uxState === 'idle'} + +
+ +
+ + +
+ +
+ + + {#if !isNewJob} +
+
+ +
+
+ {/if} + + +
+ {#if !isNewJob && existingJob} + {#if !configValid} +
+

+ {$t.translate?.preview?.config_incomplete} +

+

+ {$t.translate?.preview?.config_requirements} +

+ {:else} + + {/if} + {:else} +
+

{$t.translate?.config?.save_job_first || 'Save the job first'}

-
-
-
-
-
-
- -
+ {/if}
- {/if} - - {#if m.activeTab === 'preview' && m.existingJob} -
-

{$t.translate?.job?.preview_placeholder || 'Preview will be shown here after saving the config.'}

-

{$t.translate?.job?.preview_hint || 'Translation quality preview requires a saved job with valid config.'}

+ +
+ {#if !isNewJob && existingJob} + + {:else} +
+

{$t.translate?.config?.save_job_first || 'Save the job first'}

+
+ {/if}
- {/if} - - {#if m.activeTab === 'target' && m.existingJob} -
-
-
-
-
-
-
-
-
-
- -
-
- {/if} - - - {#if m.activeTab === 'run'} -
-

{$t.translate?.job?.run_section || 'Translation execution is handled on the History page.'}

- {$t.translate?.job?.view_history || 'View translation history'} -
- {/if} - - - {#if m.activeTab === 'schedule' && m.existingJob} -
- - -
- + +
+ {#if !isNewJob && existingJob} + { showPageBulkReplace = false; loadRunHistory(); }} + onBulkReplaceClose={() => showPageBulkReplace = false} + {getJobStatusLabel} + /> + {:else} +
+

{$t.translate?.config?.save_job_first || 'Save the job first'}

+ {/if}
{/if}
+ + showPageBulkReplace = false} + onApplied={(count) => { showPageBulkReplace = false; loadRunHistory(); }} +/> diff --git a/frontend/src/routes/translate/dictionaries/+page.svelte b/frontend/src/routes/translate/dictionaries/+page.svelte index fb706961..54a56462 100644 --- a/frontend/src/routes/translate/dictionaries/+page.svelte +++ b/frontend/src/routes/translate/dictionaries/+page.svelte @@ -17,7 +17,7 @@ let page_size = 20; let currentPage = $state(1); let showCreateForm = $state(false); - let createForm = $state({ name: '', source_dialect: '', target_dialect: '', description: '' }); + let createForm = $state({ name: '', description: '' }); let isCreating = $state(false); let deleteConfirmId = $state(null); @@ -35,7 +35,7 @@ } async function handleCreate() { - if (!createForm.name || !createForm.source_dialect || !createForm.target_dialect) { + if (!createForm.name) { addToast(_('translate.dictionaries.name_required'), 'error'); return; } @@ -44,7 +44,7 @@ await dictionaryApi.createDictionary(createForm); addToast(_('translate.dictionaries.dict_created'), 'success'); showCreateForm = false; - createForm = { name: '', source_dialect: '', target_dialect: '', description: '' }; + createForm = { name: '', description: '' }; currentPage = 1; await loadDictionaries(); } catch (e) { @@ -116,26 +116,6 @@ class="flex h-10 w-full rounded-md border border-border-strong bg-surface-card px-3 py-2 text-sm" />
-
-
- - -
-
- - -
-