/* * SPDX-FileCopyrightText: 2026 Coffey Labs * * SPDX-License-Identifier: AGPL-3.0-only */ import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { ArrowLeft, ArrowRight, Check, Loader2, X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { PageHeader } from '@/components/common/PageHeader'; import { cn } from '@/lib/utils'; export interface WizardStep { id: string; title: string; } /** * The frame every guided job shares: where you are in it, the step itself, * a side panel saying what this step does (and how to undo it, for the big * jobs), and the way forward or back. The steps own their content and decide * when "Next" is allowed; the shell never does anything on its own. */ export function WizardShell({ icon, title, subtitle, steps, current, children, aside, canNext = true, busy = false, nextLabel, onBack, onNext, onCancel, hideFooter = false, }: { icon: string; title: ReactNode; subtitle?: ReactNode; steps: WizardStep[]; current: number; children: ReactNode; aside?: ReactNode; canNext?: boolean; busy?: boolean; nextLabel?: string; onBack?: () => void; onNext?: () => void; onCancel: () => void; hideFooter?: boolean; }) { const { t } = useTranslation(); return (
{t('wizard.close', 'Close')} } />
    {steps.map((s, i) => { const done = i < current; const here = i === current; return (
  1. {done ? : i + 1} {s.title} {i < steps.length - 1 && }
  2. ); })}
{children} {aside && }
{!hideFooter && (
{onBack ? ( ) : ( )} {onNext && ( )}
)}
); } /** A side-panel note: what this step does, or how to undo it. */ export function WizardNote({ title, children, tone = 'plain', }: { title: string; children: ReactNode; tone?: 'plain' | 'undo'; }) { return (

{title}

{children}
); }