Files
tiny-forge/web/src/lib/components/ConfirmDialog.svelte
T
alexei.dolgolyov 1f81ca9eb0 fix(docker-watcher): address final review findings
Security:
- Move config export behind auth middleware
- Validate OIDC callback token before storing in localStorage
- Use constant-time comparison for webhook secret
- Encrypt OIDC client secret at rest (like registry tokens)

Performance:
- Make TriggerDeploy async from HTTP handlers (return deploy ID
  immediately, run pipeline in background goroutine)

Robustness:
- Wrap api.ts res.json() in try/catch for non-JSON responses

i18n:
- Replace ~20 hardcoded English validation messages with $t() calls
- Localize ConfirmDialog cancel button, InstanceCard confirm titles,
  ProjectCard instance/instances pluralization
- Add validation keys to both en.json and ru.json
2026-03-28 00:14:53 +03:00

83 lines
2.4 KiB
Svelte

<!--
Confirm dialog with fade/scale-in animation.
-->
<script lang="ts">
import { IconAlert } from '$lib/components/icons';
import { t } from '$lib/i18n';
interface Props {
open: boolean;
title: string;
message: string;
confirmLabel?: string;
confirmVariant?: 'danger' | 'primary';
onconfirm: () => void;
oncancel: () => void;
}
const {
open,
title,
message,
confirmLabel = 'Confirm',
confirmVariant = 'primary',
onconfirm,
oncancel
}: Props = $props();
const confirmClass = $derived(
confirmVariant === 'danger'
? 'bg-[var(--color-danger)] hover:bg-[var(--color-danger-dark)] focus-visible:outline-[var(--color-danger)]'
: 'bg-[var(--color-brand-600)] hover:bg-[var(--color-brand-700)] focus-visible:outline-[var(--color-brand-600)]'
);
const iconBgClass = $derived(
confirmVariant === 'danger'
? 'bg-[var(--color-danger-light)]'
: 'bg-[var(--color-brand-50)]'
);
const iconColorClass = $derived(
confirmVariant === 'danger'
? 'text-[var(--color-danger)]'
: 'text-[var(--color-brand-600)]'
);
</script>
{#if open}
<!-- Backdrop -->
<div class="fixed inset-0 z-40 bg-[var(--surface-overlay)] animate-fade-in" role="presentation" onclick={oncancel}></div>
<!-- Dialog -->
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
<div class="w-full max-w-md rounded-2xl bg-[var(--surface-card)] p-6 shadow-xl animate-scale-in">
<div class="flex items-start gap-4">
<div class="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full {iconBgClass}">
<IconAlert size={20} class={iconColorClass} />
</div>
<div class="flex-1">
<h3 class="text-lg font-semibold text-[var(--text-primary)]">{title}</h3>
<p class="mt-2 text-sm text-[var(--text-secondary)] leading-relaxed">{message}</p>
</div>
</div>
<div class="mt-6 flex justify-end gap-3">
<button
type="button"
class="rounded-lg px-4 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] transition-colors active:animate-press"
onclick={oncancel}
>
{$t('common.cancel')}
</button>
<button
type="button"
class="rounded-lg px-4 py-2 text-sm font-medium text-white {confirmClass} shadow-sm transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 active:animate-press"
onclick={onconfirm}
>
{confirmLabel}
</button>
</div>
</div>
</div>
{/if}