032: fix(TargetSchemaHint) — differentiate transient errors (503/504) from table not found

HIGH: 'bodyClass' and display logic updated — 503/504 errors now show
warning (yellow) styling and 'Could not verify' message instead of
destructive (red) 'Table not found'. Backend now returns 503 on pool
exhaustion and 504 on upstream timeout after async refactoring.
This commit is contained in:
2026-06-04 23:34:45 +03:00
parent bc32dd65ef
commit 0721681cfe

View File

@@ -7,7 +7,7 @@
<!-- @RELATION BINDS_TO -> [EXT:frontend:api] -->
<!-- @UX_STATE Idle -> Кнопка активна. Ждём нажатия. -->
<!-- @UX_STATE Checking -> Спиннер, кнопка disabled. -->
<!-- @UX_STATE Complete -> Показан результат: все ок / отсутствуют колонки / таблица не найдена. -->
<!-- @UX_STATE Complete -> Показан результат: все ок / отсутствуют колонки / таблица не найдена / бэкенд недоступен (503/504). -->
<!-- @UX_STATE Error -> Ошибка соединения. Кнопка активна для повтора. -->
<!-- @UX_FEEDBACK Цветовой индикатор: зелёный (все ок), жёлтый (не все колонки), красный (таблица не найдена/ошибка). -->
<!-- @UX_REACTIVITY Props -> $props() для полей конфигурации. -->
@@ -29,16 +29,20 @@
targetSourceLanguageColumn = null,
} = $props();
/** @type {'idle'|'checking'|'complete'|'error'} */
/** @type {'idle'|'checking'|'complete'|'complete_error'|'error'} */
let state = $state('idle');
let result = $state(null);
let abortController = $state(null);
let renderKey = $state(0);
/** True if result is a backend/network error (503/504/timeout), not a "table not found" */
let isTransientError = $derived(state === 'complete' && !result?.table_exists && !!result?.error);
/** CSS-класс body в зависимости от состояния */
let bodyClass = $derived.by(() => {
if (state === 'complete' && result?.all_present) return 'px-3 py-2.5 bg-success-light';
if (state === 'complete' && !result?.all_present && result?.table_exists) return 'px-3 py-2.5 bg-warning-light';
if (state === 'complete' && !result?.table_exists && result?.error) return 'px-3 py-2.5 bg-warning-light';
if (state === 'error' || (state === 'complete' && !result?.table_exists)) return 'px-3 py-2.5 bg-destructive-light';
return 'px-3 py-2.5';
});
@@ -134,7 +138,8 @@
<div class="target-schema-hint mt-3 rounded-lg border text-sm transition-colors"
class:border-success={state === 'complete' && result?.all_present}
class:border-warning={state === 'complete' && !result?.all_present && result?.table_exists}
class:border-destructive-ring={state === 'error' || (state === 'complete' && !result?.table_exists)}
class:border-warning={state === 'complete' && !result?.table_exists && result?.error}
class:border-destructive-ring={state === 'error' || (state === 'complete' && !result?.table_exists && !result?.error)}
class:border-border={state === 'idle' || state === 'checking'}
role="region"
aria-label={_('translate.target_schema.region_label') || 'Target Schema Check'}
@@ -143,7 +148,8 @@
<div class="flex items-center justify-between px-3 py-2 border-b border-border"
class:bg-success-light={state === 'complete' && result?.all_present}
class:bg-warning-light={state === 'complete' && !result?.all_present && result?.table_exists}
class:bg-destructive-light={state === 'error' || (state === 'complete' && !result?.table_exists)}
class:bg-warning-light={state === 'complete' && !result?.table_exists && result?.error}
class:bg-destructive-light={state === 'error' || (state === 'complete' && !result?.table_exists && !result?.error)}
class:bg-surface-muted={state === 'idle' || state === 'checking'}
>
<div class="flex items-center gap-2">
@@ -216,12 +222,22 @@
{:else if state === 'complete' && result}
{#key renderKey}
{#if !result.table_exists}
<div class="flex items-center gap-2 text-destructive">
<svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>
</svg>
<p class="font-medium">{_('translate.target_schema.table_not_found') || 'Target table not found in the database.'}</p>
</div>
{#if result.error}
<!-- Transient error (503/504/timeout) — backend unavailable, not "table not found" -->
<div class="flex items-center gap-2 text-warning">
<svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<p class="font-medium">{_('translate.target_schema.check_error') || 'Could not verify target table schema.'}</p>
</div>
{:else}
<div class="flex items-center gap-2 text-destructive">
<svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>
</svg>
<p class="font-medium">{_('translate.target_schema.table_not_found') || 'Target table not found in the database.'}</p>
</div>
{/if}
{:else if result.all_present}
<div class="flex items-center gap-2 text-success">