79a40f3d9c
Add all frontend pages for observability & proxy management: - Proxy Viewer: /proxies with grouped view, filtering, health indicators - Proxy Creation: form with live validation, diagnostic hints, edit/delete - Stale Containers: /containers/stale with dashboard widget, cleanup actions - Event Log: /events with filters, pagination, real-time SSE streaming - Navigation: proxies and events links in sidebar - i18n: full EN/RU translations for all new features - Settings: stale threshold configuration
74 lines
2.4 KiB
Svelte
74 lines
2.4 KiB
Svelte
<!--
|
|
Phase 6: Validation checklist for proxy destination validation.
|
|
Shows each validation step with pass/fail/pending status indicators.
|
|
-->
|
|
<script lang="ts">
|
|
import type { ValidationResult } from '$lib/types';
|
|
import { IconCheck, IconX, IconLoader } from '$lib/components/icons';
|
|
import { t } from '$lib/i18n';
|
|
|
|
interface Props {
|
|
result: ValidationResult | null;
|
|
loading?: boolean;
|
|
}
|
|
|
|
const { result, loading = false }: Props = $props();
|
|
|
|
/** Map step names to i18n keys. */
|
|
const stepLabelKeys: Record<string, string> = {
|
|
syntax: 'proxies.validation.syntax',
|
|
dns: 'proxies.validation.dns',
|
|
tcp: 'proxies.validation.tcp',
|
|
http: 'proxies.validation.http'
|
|
};
|
|
|
|
function getStepLabel(name: string): string {
|
|
const key = stepLabelKeys[name];
|
|
return key ? $t(key) : name;
|
|
}
|
|
</script>
|
|
|
|
{#if loading || result}
|
|
<div class="rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] p-4">
|
|
<h4 class="text-sm font-medium text-[var(--text-primary)] mb-3">
|
|
{$t('proxies.validation.title')}
|
|
</h4>
|
|
|
|
{#if loading && !result}
|
|
<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
|
<IconLoader size={16} />
|
|
<span>{$t('proxies.validation.checking')}</span>
|
|
</div>
|
|
{:else if result}
|
|
<ul class="space-y-2">
|
|
{#each result.steps as step}
|
|
<li>
|
|
<div class="flex items-center gap-2">
|
|
{#if step.passed}
|
|
<span class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-950">
|
|
<IconCheck size={14} class="text-emerald-600 dark:text-emerald-400" />
|
|
</span>
|
|
<span class="text-sm text-[var(--text-primary)]">{getStepLabel(step.name)}</span>
|
|
{#if step.message}
|
|
<span class="text-xs text-[var(--text-tertiary)]">— {step.message}</span>
|
|
{/if}
|
|
{:else}
|
|
<span class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-red-100 dark:bg-red-950">
|
|
<IconX size={14} class="text-red-600 dark:text-red-400" />
|
|
</span>
|
|
<span class="text-sm text-[var(--text-primary)]">{getStepLabel(step.name)}</span>
|
|
{#if step.message}
|
|
<span class="text-xs text-[var(--text-tertiary)]">— {step.message}</span>
|
|
{/if}
|
|
{/if}
|
|
</div>
|
|
{#if !step.passed && step.hint}
|
|
<p class="ml-7 mt-1 text-xs text-amber-600 dark:text-amber-400">{step.hint}</p>
|
|
{/if}
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
</div>
|
|
{/if}
|