"use client"; import { Save, X } from "lucide-react"; import { useTranslations } from "next-intl"; import { useEffect, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { useUnsavedChanges } from "@/hooks/use-unsaved-changes"; import { PRESET_EFFECTS, PRESET_ICONS, type PrefixFormData, type UserPrefix, } from "./prefix-model"; export function PrefixDialog({ isOpen, onClose, onSave, editPrefix, }: { isOpen: boolean; onClose: () => void; onSave: (data: PrefixFormData, editId?: number) => Promise; editPrefix: UserPrefix | null; }) { const [form, setForm] = useState({ username: "", text: "", color: "#FFFFFF", icon: "", effect: "", active: false, }); const [saving, setSaving] = useState(false); const savingRef = useRef(false); const [saveError, setSaveError] = useState(false); const tAction = useTranslations("pages.admin.actions"); const initial: PrefixFormData = { username: editPrefix?.username || "", text: editPrefix?.text || "", color: editPrefix?.color || "#FFFFFF", icon: editPrefix?.icon || "", effect: editPrefix?.effect || "", active: editPrefix?.active ?? false, }; const dirty = Object.keys(initial).some( (key) => form[key as keyof PrefixFormData] !== initial[key as keyof PrefixFormData], ); const { confirmLeave, markSaved } = useUnsavedChanges( isOpen && dirty, tAction("leaveUnsaved"), ); function requestClose() { if (!saving && confirmLeave()) onClose(); } // biome-ignore lint/correctness/useExhaustiveDependencies: explicitly chosen here, see surrounding code useEffect(() => { setSaveError(false); if (editPrefix) { setForm({ username: editPrefix.username || "", text: editPrefix.text, color: editPrefix.color, icon: editPrefix.icon || "", effect: editPrefix.effect || "", active: editPrefix.active, }); } else { setForm({ username: "", text: "", color: "#FFFFFF", icon: "", effect: "", active: false, }); } }, [editPrefix, isOpen]); if (!isOpen) return null; const handleSave = async () => { if (savingRef.current) return; savingRef.current = true; setSaveError(false); setSaving(true); try { if (!(await onSave(form, editPrefix?.id))) { setSaveError(true); return; } markSaved(); onClose(); } catch { setSaveError(true); } finally { savingRef.current = false; setSaving(false); } }; // Parse multi-color for preview const colors = form.color.split(","); const hasMultiColor = colors.length > 1 && new Set(colors).size > 1; return ( { if (!open) requestClose(); }} disablePointerDismissal >
{saveError &&

{tAction("saveError")}

}
{editPrefix ? "Edit Prefix" : "Add Prefix"}
{/* Preview */}
{form.icon && {form.icon}} {hasMultiColor ? ( {"{"} {[...(form.text || "...")] .map((char, i) => ({ char, n: i })) .map(({ char, n }) => ( {char} ))} {"}"} ) : ( {"{"} {form.text || "..."} {"}"} )} {form.username || "Username"}
{!editPrefix && (
Username setForm({ ...form, username: e.target.value })} />
)}
Text setForm({ ...form, text: e.target.value })} /> {form.text.length}/15
Color (HEX or multi: #FF0000,#00FF00)
setForm({ ...form, color: e.target.value })} />
Icon
Effect
setForm({ ...form, active: e.target.checked })} />
); }