import React, { useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { apiGet, apiPost, apiPatch, apiDelete } from '@/api/client'; interface Sequence { id: string; name: string; prefix: string; next_number: number; padding: number; } // ── Zod Schema ── const sequenceSchema = z.object({ name: z.string().min(1, 'required'), prefix: z.string().optional().default(''), padding: z.coerce.number().int().min(1, 'invalidNumber').max(10, 'invalidNumber'), }); type SequenceFormData = z.infer; export function SettingsSequencesPage() { const { t } = useTranslation(); const [sequences, setSequences] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showForm, setShowForm] = useState(false); const [editing, setEditing] = useState(null); const { register, handleSubmit, reset, formState: { errors, isSubmitting }, } = useForm({ resolver: zodResolver(sequenceSchema), defaultValues: { name: '', prefix: '', padding: 4 }, }); const fetchSequences = useCallback(async () => { setLoading(true); setError(null); try { const data = await apiGet<{ items: Sequence[]; total: number }>('/sequences'); setSequences(data.items); } catch (err: any) { setError(err.message || t('common.error')); } finally { setLoading(false); } }, [t]); React.useEffect(() => { fetchSequences(); }, [fetchSequences]); const onSubmit = async (data: SequenceFormData) => { try { if (editing) { await apiPatch(`/sequences/${editing.id}`, data); } else { await apiPost('/sequences', data); } setShowForm(false); setEditing(null); reset({ name: '', prefix: '', padding: 4 }); await fetchSequences(); } catch (err: any) { setError(err.message || t('common.error')); } }; const handleDelete = async (id: string) => { if (!window.confirm(t('sequences.confirmDelete'))) return; try { await apiDelete(`/sequences/${id}`); await fetchSequences(); } catch (err: any) { setError(err.message || t('common.error')); } }; const handleEdit = (seq: Sequence) => { setEditing(seq); reset({ name: seq.name, prefix: seq.prefix, padding: seq.padding }); setShowForm(true); }; const handleNew = () => { setEditing(null); reset({ name: '', prefix: '', padding: 4 }); setShowForm(true); }; const errorMsg = (key: string | undefined) => { if (!key) return undefined; if (key === 'required') return t('validation.required'); if (key === 'invalidNumber') return t('validation.invalidNumber', 'Invalid number'); return key; }; if (loading) return
{t('common.loading')}
; return (

{t('sequences.title')}

{error &&
{error}
} {showForm && (
{errors.name &&

{errorMsg(errors.name.message)}

}
{errors.prefix &&

{errorMsg(errors.prefix.message)}

}
{errors.padding &&

{errorMsg(errors.padding.message)}

}
)}
{sequences.map((seq) => ( ))}
{t('sequences.name')} {t('sequences.prefix')} {t('sequences.nextNumber')} {t('sequences.padding')}
{seq.name} {seq.prefix} {seq.next_number} {seq.padding}
); }