"use client"; import { Pencil, Plus, Save, Search, Trash2, X } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { addBlacklistWord as addBlacklistWordAction, createPrefix, deletePrefix as deletePrefixAction, removeBlacklistWord as removeBlacklistWordAction, updatePrefix, updatePrefixSettings, } from "@/actions/prefixes"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useServerAction } from "@/hooks/use-server-action"; interface UserPrefix { id: number; user_id: number; text: string; color: string; icon: string; effect: string; active: boolean; created_at: string; username?: string; } interface BlacklistWord { id: number; word: string; created_at: string; } interface PrefixSettings { max_length: string; min_rank_to_buy: string; price_credits: string; price_points: string; points_type: string; } interface PrefixFormData { username: string; text: string; color: string; icon: string; effect: string; active: boolean; } const PRESET_EFFECTS = [ { id: "", label: "Nessuno" }, { id: "glow", label: "✨ Bagliore" }, { id: "shadow", label: "🌑 Ombra" }, { id: "italic", label: "𝑰 Corsivo" }, { id: "outline", label: "🔲 Contorno" }, { id: "pulse", label: "💫 Pulsante" }, { id: "bold-glow", label: "💡 Neon" }, ]; const PRESET_ICONS = [ { emoji: "", label: "Nessuna" }, { emoji: "👑", label: "Corona" }, { emoji: "⭐", label: "Stella" }, { emoji: "❤️", label: "Cuore" }, { emoji: "💎", label: "Diamante" }, { emoji: "🔥", label: "Fuoco" }, { emoji: "⚡", label: "Fulmine" }, { emoji: "🛡️", label: "Scudo" }, { emoji: "💀", label: "Teschio" }, { emoji: "🎵", label: "Musica" }, { emoji: "💠", label: "Gemma" }, { emoji: "⚔️", label: "Spade" }, { emoji: "🏆", label: "Trofeo" }, { emoji: "🚀", label: "Razzo" }, { emoji: "👻", label: "Fantasma" }, { emoji: "🐾", label: "Zampa" }, { emoji: "🍃", label: "Foglia" }, { emoji: "😎", label: "Cool" }, { emoji: "🐺", label: "Lupo" }, { emoji: "🦁", label: "Leone" }, { emoji: "🐉", label: "Drago" }, { emoji: "🦄", label: "Unicorno" }, { emoji: "🌙", label: "Luna" }, { emoji: "☀️", label: "Sole" }, { emoji: "❄️", label: "Neve" }, { emoji: "🌹", label: "Rosa" }, { emoji: "🎮", label: "Controller" }, { emoji: "💰", label: "Soldi" }, { emoji: "🔮", label: "Sfera" }, { emoji: "🏴‍☠️", label: "Pirata" }, ]; 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); // biome-ignore lint/correctness/useExhaustiveDependencies: explicitly chosen here, see surrounding code useEffect(() => { 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 () => { setSaving(true); try { await onSave(form, editPrefix?.id); onClose(); } finally { setSaving(false); } }; // Parse multi-color for preview const colors = form.color.split(","); const hasMultiColor = colors.length > 1 && new Set(colors).size > 1; return (

{editPrefix ? "Modifica Prefisso" : "Aggiungi Prefisso"}

{/* Preview */}
{form.icon && {form.icon}} {hasMultiColor ? ( {"{"} {[...(form.text || "...")].map((char, i) => ( // biome-ignore lint/suspicious/noArrayIndexKey: char position drives color slot {char} ))} {"}"} ) : ( {"{"} {form.text || "..."} {"}"} )} {form.username || "Username"}
{!editPrefix && (
Username setForm({ ...form, username: e.target.value })} />
)}
Testo setForm({ ...form, text: e.target.value })} /> {form.text.length}/15
Colore (HEX o multi: #FF0000,#00FF00)
setForm({ ...form, color: e.target.value })} />
Icona
Effetto
setForm({ ...form, active: e.target.checked })} />
); } export function PrefixesClient({ canEdit }: { canEdit: boolean }) { const { run, isPending } = useServerAction(); const [prefixes, setPrefixes] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [blacklist, setBlacklist] = useState([]); const [newWord, setNewWord] = useState(""); const [settings, setSettings] = useState({ max_length: "15", min_rank_to_buy: "1", price_credits: "5", price_points: "0", points_type: "0", }); const [dialogOpen, setDialogOpen] = useState(false); const [editPrefix, setEditPrefix] = useState(null); const fetchPrefixes = useCallback(async () => { const params = new URLSearchParams({ page: String(page) }); if (searchQuery) params.set("q", searchQuery); const res = await fetch(`/api/admin/prefixes?${params}`); const data = await res.json(); if (data.ok) { setPrefixes(data.prefixes); setTotalPages(data.pages); } }, [page, searchQuery]); const fetchBlacklist = useCallback(async () => { const res = await fetch("/api/admin/prefixes/blacklist"); const data = await res.json(); if (data.ok) setBlacklist(data.words); }, []); const fetchSettings = useCallback(async () => { const res = await fetch("/api/admin/prefixes/settings"); const data = await res.json(); if (data.ok) setSettings(data.settings); }, []); useEffect(() => { fetchPrefixes(); }, [fetchPrefixes]); useEffect(() => { fetchBlacklist(); fetchSettings(); }, [fetchBlacklist, fetchSettings]); const handleDeletePrefix = async (id: number) => { if (!confirm("Sei sicuro di voler eliminare questo prefisso?")) return; run(() => deletePrefixAction({ id }), { successMessage: "Prefisso eliminato!", onSuccess: () => { fetchPrefixes(); }, }); }; const handleSavePrefix = async (data: PrefixFormData, editId?: number) => { if (editId) { run( () => updatePrefix({ id: editId, text: data.text, color: data.color, icon: data.icon, effect: data.effect, active: data.active ? 1 : 0, }), { successMessage: "Prefisso aggiornato!", onSuccess: () => { fetchPrefixes(); }, }, ); } else { run( () => createPrefix({ username: data.username, text: data.text, color: data.color, icon: data.icon, effect: data.effect, active: data.active ? 1 : 0, }), { successMessage: "Prefisso creato!", onSuccess: () => { fetchPrefixes(); }, }, ); } }; const openAddDialog = () => { setEditPrefix(null); setDialogOpen(true); }; const openEditDialog = (prefix: UserPrefix) => { setEditPrefix(prefix); setDialogOpen(true); }; const handleAddBlacklistWord = async () => { if (!newWord.trim()) return; run(() => addBlacklistWordAction({ word: newWord.trim() }), { successMessage: "Parola aggiunta alla blacklist!", onSuccess: () => { setNewWord(""); fetchBlacklist(); }, }); }; const handleRemoveBlacklistWord = async (id: number) => { run(() => removeBlacklistWordAction({ id }), { successMessage: "Parola rimossa dalla blacklist!", onSuccess: () => { fetchBlacklist(); }, }); }; const saveSettings = async () => { run(() => updatePrefixSettings({ settings: settings as unknown as Record }), { successMessage: "Impostazioni salvate!", }); }; return ( <> setDialogOpen(false)} onSave={handleSavePrefix} /> Prefissi Utenti Blacklist Impostazioni
{ setSearchQuery(e.target.value); setPage(1); }} />
{canEdit && ( )}
ID Utente Prefix Colore Icona Effetto Attivo Data {canEdit && Azioni} {prefixes.map((prefix) => { const colors = prefix.color.split(","); const hasMultiColor = colors.length > 1 && new Set(colors).size > 1; const prefixIcon = prefix.icon || ""; return ( {prefix.id} {prefix.username || `#${prefix.user_id}`} {hasMultiColor ? ( <> {"{"} {[...prefix.text].map((char, i) => ( {char} ))} {"}"} ) : ( {"{"} {prefix.text} {"}"} )}
{colors.slice(0, 5).map((c, i) => (
))} {colors.length > 5 && ( +{colors.length - 5} )}
{prefixIcon || "-"} {prefix.effect ? PRESET_EFFECTS.find((fx) => fx.id === prefix.effect)?.label || prefix.effect : "-"} {prefix.active ? "Si" : "No"} {new Date(prefix.created_at).toLocaleDateString()} {canEdit && (
)} ); })} {prefixes.length === 0 && ( Nessun prefisso trovato )}
{totalPages > 1 && (
Pagina {page} di {totalPages}
)}
Parole Vietate {canEdit && (
setNewWord(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleAddBlacklistWord()} />
)}
{blacklist.map((word) => (
{word.word} {canEdit && ( )}
))} {blacklist.length === 0 && (

Nessuna parola in blacklist

)}
Impostazioni Prefissi
Lunghezza Massima setSettings({ ...settings, max_length: e.target.value })} />
Rank Minimo setSettings({ ...settings, min_rank_to_buy: e.target.value })} />
Prezzo (Crediti) setSettings({ ...settings, price_credits: e.target.value })} />
Prezzo (Punti) setSettings({ ...settings, price_points: e.target.value })} />
Tipo Punti setSettings({ ...settings, points_type: e.target.value })} />

0 = Duckets, 5 = Diamonds, ecc.

{canEdit && ( )}
); }