From 399eb2ada76c1b196bcaa4ab83e80c6d6ae23431 Mon Sep 17 00:00:00 2001 From: busya Date: Wed, 3 Jun 2026 11:48:13 +0300 Subject: [PATCH] feat(ui): refactor PolicyForm and automation page with atoms + i18n - PolicyForm.svelte: replaced raw inputs/selects/buttons with /ui atoms (Button, Input, Select), added i18n for all labels and messages - Automation page: replaced manual layout with PageHeader/Card/EmptyState atoms, added i18n everywhere, stripped debug logging - Added i18n keys for en/ru (settings: 45 new keys, validation: 1 new key) - Fixed validation new page description to use dedicated i18n key --- .../lib/components/health/PolicyForm.svelte | 192 +++++++------- .../src/lib/i18n/locales/en/settings.json | 47 +++- .../src/lib/i18n/locales/en/validation.json | 1 + .../src/lib/i18n/locales/ru/settings.json | 47 +++- .../src/lib/i18n/locales/ru/validation.json | 1 + .../routes/settings/automation/+page.svelte | 250 ++++++------------ .../routes/validation-tasks/new/+page.svelte | 2 +- 7 files changed, 280 insertions(+), 260 deletions(-) diff --git a/frontend/src/lib/components/health/PolicyForm.svelte b/frontend/src/lib/components/health/PolicyForm.svelte index ed774e9a..65cff762 100644 --- a/frontend/src/lib/components/health/PolicyForm.svelte +++ b/frontend/src/lib/components/health/PolicyForm.svelte @@ -5,16 +5,19 @@ @PURPOSE: Form for creating and editing validation policies. @LAYER UI @RELATION DEPENDS_ON -> [ValidationPolicy] -@PRE: Parent provides callable onSave/onCancel handlers and an environments collection that may be empty but remains array-like. -@POST: Form submission forwards the current draft to onSave and cancel delegates dismissal to the parent callback without mutating external state directly. +@PRE: Parent provides callable onSave/onCancel handlers and an environments collection. +@POST: Form submission forwards the current draft to onSave and cancel delegates dismissal to the parent callback. -@UX_STATE: Idle -> Displays the policy form. +@UX_STATE: Idle -> Displays the policy form with editable fields. @UX_STATE: Submitting -> Disables inputs and shows a loading state. -@UX_FEEDBACK: Success -> Parent flow can close the form after a successful save. -@UX_FEEDBACK: Error -> Invalid field or submit failures keep the draft visible for correction. -@UX_REACTIVITY: Uses $state for mutable form data and $derived values for window health checks. +@UX_FEEDBACK: Success -> Parent flow closes the form after a successful save. +@UX_FEEDBACK: Error -> Submit failures keep the draft visible for correction. +@UX_REACTIVITY: Uses $state for mutable form data and $derived for window health checks. --> -
+
- +
-
- - -
+ -
- - -
+ -
-
- - -
+ +
{#if formData.dashboard_ids.length > 0} -
+

- πŸ’‘ System will automatically distribute {formData.dashboard_ids.length} checks within this {Math.floor(windowDurationMinutes() / 60)}h {windowDurationMinutes() % 60}m window. + {($t.settings?.distribution_info || 'System will automatically distribute {count} checks within this {hours}h {minutes}m window.') + .replace('{count}', formData.dashboard_ids.length) + .replace('{hours}', Math.floor(windowDurationMinutes() / 60)) + .replace('{minutes}', windowDurationMinutes() % 60)}

{#if isWindowTooSmall} -

- ⚠️ Window might be too narrow for {formData.dashboard_ids.length} dashboards. +

+ {($t.settings?.window_warning || 'Window might be too narrow for {count} dashboards.') + .replace('{count}', formData.dashboard_ids.length)}

{/if}
{/if} -
- - -
+ +
-
- - + +
+ +
diff --git a/frontend/src/lib/i18n/locales/en/settings.json b/frontend/src/lib/i18n/locales/en/settings.json index 6b3427e7..822e4233 100644 --- a/frontend/src/lib/i18n/locales/en/settings.json +++ b/frontend/src/lib/i18n/locales/en/settings.json @@ -149,5 +149,50 @@ "help_migration_cron": "Cron expression for automatic cross-environment ID synchronization. Format: minute hour day month weekday. Example: 0 2 * * * (daily at 2 AM UTC).", "help_migration_sync_now": "Triggers an immediate synchronization of Dashboard, Chart, and Dataset IDs across all configured environments.", "help_feature_dataset_review": "Enable the Dataset Review workflow: automatic review, clarification sessions, and SQL execution for imported datasets.", - "help_feature_health_monitor": "Enable dashboard health monitoring: validation reports, status aggregation, and health summary display." + "help_feature_health_monitor": "Enable dashboard health monitoring: validation reports, status aggregation, and health summary display.", + + "automation_policies": "Automation Policies", + "automation_policies_subtitle": "Manage scheduled validation rules and execution windows.", + "create_policy": "Create Policy", + "edit_policy": "Edit Policy", + "new_policy": "New Policy", + "back_to_list": "Back to list", + "no_policies": "No policies", + "no_policies_desc": "Get started by creating a new automation policy.", + "translation_schedules": "Translation Schedules", + "policy_name": "Policy Name", + "policy_name_placeholder": "e.g., Production Morning Check", + "environment": "Environment", + "schedule_days": "Schedule Days", + "window_start": "Window Start", + "window_end": "Window End", + "alert_condition": "Alert Condition", + "alert_fail_only": "Only on Failure", + "alert_warn_and_fail": "On Warning or Failure", + "alert_always": "Always", + "notify_owners": "Auto-notify Dashboard Owners", + "cancel": "Cancel", + "save_policy": "Save Policy", + "saving_policy": "Saving...", + "policy_created": "Policy created successfully", + "policy_updated": "Policy updated successfully", + "policy_deleted": "Policy deleted successfully", + "policy_save_failed": "Failed to save policy", + "policy_delete_failed": "Failed to delete policy", + "policy_load_failed": "Failed to load automation data", + "delete_confirm": "Are you sure you want to delete this policy?", + "dashboards_count": "{count} Dashboards", + "active": "Active", + "inactive": "Inactive", + "last_run": "Last: {date}", + "distribution_info": "System will automatically distribute {count} checks within this {hours}h {minutes}m window.", + "window_warning": "Window might be too narrow for {count} dashboards.", + + "day_sun": "Sun", + "day_mon": "Mon", + "day_tue": "Tue", + "day_wed": "Wed", + "day_thu": "Thu", + "day_fri": "Fri", + "day_sat": "Sat" } diff --git a/frontend/src/lib/i18n/locales/en/validation.json b/frontend/src/lib/i18n/locales/en/validation.json index adcfb560..055a4b11 100644 --- a/frontend/src/lib/i18n/locales/en/validation.json +++ b/frontend/src/lib/i18n/locales/en/validation.json @@ -1,6 +1,7 @@ { "tasks_title": "Validation Tasks", "new_task_page": "New Validation Task", + "new_task_description": "Configure a new LLM-powered dashboard validation task", "edit_task_page": "Edit Task", "step_name": "Name & Environment", "step_sources": "Sources", diff --git a/frontend/src/lib/i18n/locales/ru/settings.json b/frontend/src/lib/i18n/locales/ru/settings.json index a9c86a35..d2dcc187 100644 --- a/frontend/src/lib/i18n/locales/ru/settings.json +++ b/frontend/src/lib/i18n/locales/ru/settings.json @@ -149,5 +149,50 @@ "help_migration_cron": "Cron-Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ для автоматичСской синхронизации ID ΠΌΠ΅ΠΆΠ΄Ρƒ окруТСниями. Π€ΠΎΡ€ΠΌΠ°Ρ‚: ΠΌΠΈΠ½ΡƒΡ‚Π° час дСнь мСсяц дСнь_Π½Π΅Π΄Π΅Π»ΠΈ. ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 0 2 * * * (Π΅ΠΆΠ΅Π΄Π½Π΅Π²Π½ΠΎ Π² 02:00 UTC).", "help_migration_sync_now": "ЗапускаСт Π½Π΅ΠΌΠ΅Π΄Π»Π΅Π½Π½ΡƒΡŽ ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·Π°Ρ†ΠΈΡŽ ID Π΄Π°ΡˆΠ±ΠΎΡ€Π΄ΠΎΠ², Ρ‡Π°Ρ€Ρ‚ΠΎΠ² ΠΈ датасСтов ΠΌΠ΅ΠΆΠ΄Ρƒ всСми настроСнными окруТСниями.", "help_feature_dataset_review": "Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ workflow ΠΎΠ±Π·ΠΎΡ€Π° датасСтов: автоматичСский ΠΎΠ±Π·ΠΎΡ€, ΡƒΡ‚ΠΎΡ‡Π½ΡΡŽΡ‰ΠΈΠ΅ сСссии ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ SQL для ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹Ρ… датасСтов.", - "help_feature_health_monitor": "Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ Π·Π΄ΠΎΡ€ΠΎΠ²ΡŒΡ Π΄Π°ΡˆΠ±ΠΎΡ€Π΄ΠΎΠ²: ΠΎΡ‚Ρ‡Ρ‘Ρ‚Ρ‹ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ, агрСгация статусов ΠΈ сводка состояния." + "help_feature_health_monitor": "Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ Π·Π΄ΠΎΡ€ΠΎΠ²ΡŒΡ Π΄Π°ΡˆΠ±ΠΎΡ€Π΄ΠΎΠ²: ΠΎΡ‚Ρ‡Ρ‘Ρ‚Ρ‹ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ, агрСгация статусов ΠΈ сводка состояния.", + + "automation_policies": "ΠŸΠΎΠ»ΠΈΡ‚ΠΈΠΊΠΈ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·Π°Ρ†ΠΈΠΈ", + "automation_policies_subtitle": "Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ расписаниями Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ ΠΈ ΠΎΠΊΠ½Π°ΠΌΠΈ выполнСния.", + "create_policy": "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΡƒ", + "edit_policy": "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΡƒ", + "new_policy": "Новая ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ°", + "back_to_list": "Назад ΠΊ списку", + "no_policies": "НСт ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊ", + "no_policies_desc": "НачнитС с создания Π½ΠΎΠ²ΠΎΠΉ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠΈ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·Π°Ρ†ΠΈΠΈ.", + "translation_schedules": "Расписания ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ΠΎΠ²", + "policy_name": "НазваниС ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠΈ", + "policy_name_placeholder": "Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, УтрСнняя ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Production", + "environment": "ΠžΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅", + "schedule_days": "Π”Π½ΠΈ расписания", + "window_start": "Начало ΠΎΠΊΠ½Π°", + "window_end": "ΠšΠΎΠ½Π΅Ρ† ΠΎΠΊΠ½Π°", + "alert_condition": "УсловиС оповСщСния", + "alert_fail_only": "Волько ΠΏΡ€ΠΈ ошибкС", + "alert_warn_and_fail": "ΠŸΡ€ΠΈ ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠΈ ΠΈΠ»ΠΈ ошибкС", + "alert_always": "ВсСгда", + "notify_owners": "АвтоувСдомлСниС Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Π΅Π² Π΄Π°ΡˆΠ±ΠΎΡ€Π΄ΠΎΠ²", + "cancel": "ΠžΡ‚ΠΌΠ΅Π½Π°", + "save_policy": "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΡƒ", + "saving_policy": "Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅...", + "policy_created": "ΠŸΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ создана", + "policy_updated": "ΠŸΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½Π°", + "policy_deleted": "ΠŸΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΡƒΠ΄Π°Π»Π΅Π½Π°", + "policy_save_failed": "НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΡƒ", + "policy_delete_failed": "НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΡƒ", + "policy_load_failed": "НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·Π°Ρ†ΠΈΠΈ", + "delete_confirm": "Π’Ρ‹ ΡƒΠ²Π΅Ρ€Π΅Π½Ρ‹, Ρ‡Ρ‚ΠΎ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ эту ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΡƒ?", + "dashboards_count": "{count} Π΄Π°ΡˆΠ±ΠΎΡ€Π΄ΠΎΠ²", + "active": "АктивСн", + "inactive": "НСактивСн", + "last_run": "ПослСдний: {date}", + "distribution_info": "БистСма автоматичСски распрСдСлит {count} ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ Π² этом ΠΎΠΊΠ½Π΅ {hours}Ρ‡ {minutes}ΠΌ.", + "window_warning": "Окно ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ слишком ΡƒΠ·ΠΊΠΈΠΌ для {count} Π΄Π°ΡˆΠ±ΠΎΡ€Π΄ΠΎΠ².", + + "day_sun": "Вс", + "day_mon": "Пн", + "day_tue": "Π’Ρ‚", + "day_wed": "Π‘Ρ€", + "day_thu": "Π§Ρ‚", + "day_fri": "ΠŸΡ‚", + "day_sat": "Π‘Π±" } diff --git a/frontend/src/lib/i18n/locales/ru/validation.json b/frontend/src/lib/i18n/locales/ru/validation.json index ec06e6f4..a238fa11 100644 --- a/frontend/src/lib/i18n/locales/ru/validation.json +++ b/frontend/src/lib/i18n/locales/ru/validation.json @@ -1,6 +1,7 @@ { "tasks_title": "Π—Π°Π΄Π°Ρ‡ΠΈ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ", "new_task_page": "Новая Π·Π°Π΄Π°Ρ‡Π° Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ", + "new_task_description": "НастройтС Π½ΠΎΠ²ΡƒΡŽ Π·Π°Π΄Π°Ρ‡Ρƒ LLM-Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ Π΄Π°ΡˆΠ±ΠΎΡ€Π΄ΠΎΠ²", "edit_task_page": "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π·Π°Π΄Π°Ρ‡Ρƒ", "step_name": "НазваниС ΠΈ ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅", "step_sources": "Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊΠΈ", diff --git a/frontend/src/routes/settings/automation/+page.svelte b/frontend/src/routes/settings/automation/+page.svelte index d599bbf7..f345301e 100644 --- a/frontend/src/routes/settings/automation/+page.svelte +++ b/frontend/src/routes/settings/automation/+page.svelte @@ -1,17 +1,17 @@ - + + + + + + + + -
-
-
-

Automation Policies

-

Manage scheduled validation rules and execution windows.

-
- {#if !showForm} - - {/if} -
- +
{#if showForm} -
+ +
- + {$t.settings?.back_to_list || 'Back to list'} + | -

- {selectedPolicy ? 'Edit Policy' : 'New Policy'} +

+ {selectedPolicy ? ($t.settings?.edit_policy || 'Edit Policy') : ($t.settings?.new_policy || 'New Policy')}

{:else if isLoading} +
{:else} -
-
    + + + {#snippet subtitle()} +

    {$t.settings?.automation_policies_subtitle || 'Manage scheduled validation rules and execution windows.'}

    + {/snippet} + {#snippet actions()} + + {/snippet} +
    + + + +
      {#each policies as policy}
    • -
      +
      -
      +
      -
      -

      {policy.name}

      +
      +

      {policy.name}

      - {getEnvName(policy.environment_id)} - β€’ - {policy.dashboard_ids.length} Dashboards + {getEnvName(policy.environment_id)} + β€’ + {$t.settings?.dashboards_count?.replace('{count}', policy.dashboard_ids.length) || `${policy.dashboard_ids.length} Dashboards`}
      -
      +
    • {:else} -
    • - - - -

      No policies

      -

      Get started by creating a new automation policy.

      -
      - -
      +
    • +
    • {/each}
    -
+ + {#if translationSchedules.length > 0} -

Translation Schedules

-
-
    +

    {$t.settings?.translation_schedules || 'Translation Schedules'}

    + +
      {#each translationSchedules as ts}
    • -
      +
      -
      +
      -
      -

      {ts.job_name}

      +
      +

      {ts.job_name}

      {ts.cron_expression} - β€’ + β€’ {ts.timezone}
      -
      - - {ts.is_active ? 'Active' : 'Inactive'} +
      + + {ts.is_active ? ($t.settings?.active || 'Active') : ($t.settings?.inactive || 'Inactive')} {#if ts.last_run_at} - - Last: {new Date(ts.last_run_at).toLocaleString()} + + {($t.settings?.last_run || 'Last: {date}').replace('{date}', new Date(ts.last_run_at).toLocaleString())} {/if}
    • - {:else} -
    • - No translation schedules configured -
    • {/each}
    -
+ {/if} {/if}
- \ No newline at end of file + diff --git a/frontend/src/routes/validation-tasks/new/+page.svelte b/frontend/src/routes/validation-tasks/new/+page.svelte index 3ecc08f3..b9829af8 100644 --- a/frontend/src/routes/validation-tasks/new/+page.svelte +++ b/frontend/src/routes/validation-tasks/new/+page.svelte @@ -70,7 +70,7 @@ {$t.validation?.new_task_page || 'New Validation Task'}

- {$t.validation?.step_x_of_y || 'Configure a new LLM-powered dashboard validation task'} + {$t.validation?.new_task_description || 'Configure a new LLM-powered dashboard validation task'}