168 lines
6.3 KiB
TypeScript
168 lines
6.3 KiB
TypeScript
'use client';
|
|
|
|
import { useTranslation } from '../../../i18n/useTranslation';
|
|
import type { LanguageEntry } from '../hooks/useLanguageManagementTranslations';
|
|
import { useModalAnimation } from '../hooks/useModalAnimation';
|
|
|
|
type Props = {
|
|
isOpen: boolean;
|
|
currentWizardKey: string | null;
|
|
wizardIndex: number;
|
|
wizardMissingCount: number;
|
|
activeLang: string;
|
|
allLanguages: LanguageEntry[];
|
|
wizardInput: string;
|
|
setWizardInput: (value: string) => void;
|
|
wizardMarkGlobal: boolean;
|
|
setWizardMarkGlobal: (value: boolean) => void;
|
|
wizardUseEnglishReference: boolean;
|
|
setWizardUseEnglishReference: (value: boolean) => void;
|
|
englishValue: string;
|
|
addGlobalKey: (key: string) => void;
|
|
removeGlobalKey: (key: string) => void;
|
|
onClose: () => void;
|
|
onPrevious: () => void;
|
|
onSkip: () => void;
|
|
onNext: () => void | Promise<void>;
|
|
isSavingStep?: boolean;
|
|
};
|
|
|
|
export default function TranslationWizardModal({
|
|
isOpen,
|
|
currentWizardKey,
|
|
wizardIndex,
|
|
wizardMissingCount,
|
|
activeLang,
|
|
allLanguages,
|
|
wizardInput,
|
|
setWizardInput,
|
|
wizardMarkGlobal,
|
|
setWizardMarkGlobal,
|
|
wizardUseEnglishReference,
|
|
setWizardUseEnglishReference,
|
|
englishValue,
|
|
addGlobalKey,
|
|
removeGlobalKey,
|
|
onClose,
|
|
onPrevious,
|
|
onSkip,
|
|
onNext,
|
|
isSavingStep = false,
|
|
}: Props) {
|
|
const { t } = useTranslation();
|
|
const { isRendered, isVisible } = useModalAnimation(isOpen && Boolean(currentWizardKey));
|
|
|
|
if (!isRendered || !currentWizardKey) return null;
|
|
|
|
return (
|
|
<div className={`fixed inset-0 z-[120] flex items-center justify-center bg-black/45 backdrop-blur-sm transition-opacity duration-200 ${
|
|
isVisible ? 'opacity-100' : 'opacity-0'
|
|
}`}>
|
|
<div className={`mx-4 w-full max-w-2xl rounded-2xl border border-slate-200 bg-white shadow-2xl overflow-hidden transform transition-all duration-200 ${
|
|
isVisible ? 'opacity-100 translate-y-0 scale-100' : 'opacity-0 translate-y-2 scale-[0.98]'
|
|
}`}>
|
|
<div className="px-6 py-4 border-b border-slate-200 bg-slate-50 flex items-start justify-between gap-4">
|
|
<div>
|
|
<h2 className="text-lg font-bold text-[#1C2B4A]">{t('autofix.kcd190bdd')}</h2>
|
|
<p className="text-xs text-slate-600 mt-1">
|
|
Step {wizardIndex + 1} of {wizardMissingCount} for {allLanguages.find((l) => l.code === activeLang)?.name ?? activeLang}
|
|
</p>
|
|
</div>
|
|
<button onClick={onClose} className="text-slate-400 hover:text-slate-700 text-xl leading-none">
|
|
✕
|
|
</button>
|
|
</div>
|
|
|
|
<div className="px-6 py-5 space-y-4">
|
|
<div className="rounded-lg border border-slate-200 bg-slate-50 px-3 py-2">
|
|
<p className="text-[11px] uppercase tracking-wide text-slate-500">Key</p>
|
|
<p className="font-mono text-xs text-slate-700 mt-1 break-all">{currentWizardKey}</p>
|
|
</div>
|
|
|
|
<div className="rounded-lg border border-slate-200 bg-white px-3 py-2">
|
|
<p className="text-[11px] uppercase tracking-wide text-slate-500">{t('autofix.kc518ff5c')}</p>
|
|
<p className="text-sm text-slate-700 mt-1">{englishValue}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-1">Translation</label>
|
|
<textarea
|
|
value={wizardInput}
|
|
onChange={(e) => setWizardInput(e.target.value)}
|
|
placeholder={t('languageManagement.wizardInputPlaceholder')}
|
|
rows={4}
|
|
className="w-full rounded-lg border border-slate-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#1C2B4A]"
|
|
/>
|
|
</div>
|
|
|
|
<label className="inline-flex items-center gap-2 text-sm text-slate-700">
|
|
<input
|
|
type="checkbox"
|
|
checked={wizardMarkGlobal}
|
|
onChange={(e) => {
|
|
const checked = e.target.checked;
|
|
setWizardMarkGlobal(checked);
|
|
if (!currentWizardKey) return;
|
|
if (checked) {
|
|
addGlobalKey(currentWizardKey);
|
|
return;
|
|
}
|
|
removeGlobalKey(currentWizardKey);
|
|
}}
|
|
className="h-4 w-4 rounded border-slate-300 text-[#1C2B4A] focus:ring-[#1C2B4A]"
|
|
/>
|
|
Counts as global key (same value as English)
|
|
</label>
|
|
|
|
{activeLang !== 'en' && (
|
|
<label className="inline-flex items-center gap-2 text-sm text-slate-700">
|
|
<input
|
|
type="checkbox"
|
|
checked={wizardUseEnglishReference}
|
|
onChange={(e) => {
|
|
const checked = e.target.checked;
|
|
setWizardUseEnglishReference(checked);
|
|
if (checked) {
|
|
setWizardInput(englishValue);
|
|
}
|
|
}}
|
|
className="h-4 w-4 rounded border-slate-300 text-[#1C2B4A] focus:ring-[#1C2B4A]"
|
|
/>
|
|
Use English value (this language only, not global)
|
|
</label>
|
|
)}
|
|
</div>
|
|
|
|
<div className="px-6 py-4 border-t border-slate-200 bg-white flex items-center justify-between gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={onPrevious}
|
|
disabled={wizardIndex === 0 || isSavingStep}
|
|
className="rounded-md border border-slate-300 px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 disabled:opacity-50"
|
|
>
|
|
Back
|
|
</button>
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={onSkip}
|
|
disabled={isSavingStep}
|
|
className="rounded-md border border-slate-300 px-3 py-2 text-sm text-slate-700 hover:bg-slate-50"
|
|
>
|
|
Skip
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={onNext}
|
|
disabled={isSavingStep || (wizardInput.trim() === '' && !wizardMarkGlobal && !wizardUseEnglishReference)}
|
|
className="rounded-md bg-[#1C2B4A] text-white px-4 py-2 text-sm font-semibold hover:bg-[#1C2B4A]/90 disabled:opacity-50"
|
|
>{isSavingStep
|
|
? t('common.saving')
|
|
: (wizardIndex >= wizardMissingCount - 1 ? t('autofix.k230e2c3c') : t('autofix.kb270a988'))}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|