Fix admin permissions bounce, prefixes APIs, and Italian UI leftovers.
Local Build and Deploy / deploy (push) Successful in 56s

Gate permissions on ACL manage + resolve super-admin from live user ranks, restore prefixes API routes, and anglicize hardcoded admin copy with nav i18n.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-15 20:33:00 +02:00
1 parent 0e89d03940
commit 3403d3b19f
38 files changed
+673 -208

No files matched your search

+86 -81
View File
@@ -60,46 +60,46 @@ interface PrefixFormData {
}
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: "", label: "None" },
{ id: "glow", label: "✨ Glow" },
{ id: "shadow", label: "🌑 Shadow" },
{ id: "italic", label: "𝑰 Italic" },
{ id: "outline", label: "🔲 Outline" },
{ id: "pulse", label: "💫 Pulse" },
{ 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: "None" },
{ emoji: "👑", label: "Crown" },
{ emoji: "⭐", label: "Star" },
{ emoji: "❤️", label: "Heart" },
{ emoji: "💎", label: "Diamond" },
{ emoji: "🔥", label: "Fire" },
{ emoji: "⚡", label: "Lightning" },
{ emoji: "🛡️", label: "Shield" },
{ emoji: "💀", label: "Skull" },
{ emoji: "🎵", label: "Music" },
{ emoji: "💠", label: "Gem" },
{ emoji: "⚔️", label: "Swords" },
{ emoji: "🏆", label: "Trophy" },
{ emoji: "🚀", label: "Rocket" },
{ emoji: "👻", label: "Ghost" },
{ emoji: "🐾", label: "Paw" },
{ emoji: "🍃", label: "Leaf" },
{ 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: "Wolf" },
{ emoji: "🦁", label: "Lion" },
{ emoji: "🐉", label: "Dragon" },
{ emoji: "🦄", label: "Unicorn" },
{ emoji: "🌙", label: "Moon" },
{ emoji: "☀️", label: "Sun" },
{ emoji: "❄️", label: "Snow" },
{ emoji: "🌹", label: "Rose" },
{ emoji: "🎮", label: "Controller" },
{ emoji: "💰", label: "Soldi" },
{ emoji: "🔮", label: "Sfera" },
{ emoji: "🏴‍☠️", label: "Pirata" },
{ emoji: "💰", label: "Money" },
{ emoji: "🔮", label: "Orb" },
{ emoji: "🏴‍☠️", label: "Pirate" },
];
function PrefixDialog({
@@ -167,7 +167,7 @@ function PrefixDialog({
<div className="bg-background rounded-lg shadow-lg w-full max-w-md p-6 space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">
{editPrefix ? "Modifica Prefisso" : "Aggiungi Prefisso"}
{editPrefix ? "Edit Prefix" : "Add Prefix"}
</h3>
<button
type="button"
@@ -213,7 +213,7 @@ function PrefixDialog({
<div className="space-y-2">
<span className="text-sm font-medium">Username</span>
<Input
placeholder="Nome utente..."
placeholder="Username..."
value={form.username}
onChange={(e) => setForm({ ...form, username: e.target.value })}
/>
@@ -221,10 +221,10 @@ function PrefixDialog({
)}
<div className="space-y-2">
<span className="text-sm font-medium">Testo</span>
<span className="text-sm font-medium">Text</span>
<Input
maxLength={15}
placeholder="Testo del prefix..."
placeholder="Prefix text..."
value={form.text}
onChange={(e) => setForm({ ...form, text: e.target.value })}
/>
@@ -235,7 +235,7 @@ function PrefixDialog({
<div className="space-y-2">
<span className="text-sm font-medium">
Colore (HEX o multi: #FF0000,#00FF00)
Color (HEX or multi: #FF0000,#00FF00)
</span>
<div className="flex items-center gap-2">
<Input
@@ -251,7 +251,7 @@ function PrefixDialog({
</div>
<div className="space-y-2">
<span className="text-sm font-medium">Icona</span>
<span className="text-sm font-medium">Icon</span>
<select
className="w-full px-3 py-2 rounded border bg-background text-sm"
value={form.icon}
@@ -266,7 +266,7 @@ function PrefixDialog({
</div>
<div className="space-y-2">
<span className="text-sm font-medium">Effetto</span>
<span className="text-sm font-medium">Effect</span>
<select
className="w-full px-3 py-2 rounded border bg-background text-sm"
value={form.effect}
@@ -288,13 +288,13 @@ function PrefixDialog({
onChange={(e) => setForm({ ...form, active: e.target.checked })}
/>
<label className="text-sm" htmlFor="prefix-active">
Attivo
Active
</label>
</div>
<div className="flex justify-end gap-2 pt-2">
<Button variant="outline" onClick={onClose}>
Annulla
Cancel
</Button>
<Button
disabled={
@@ -305,7 +305,7 @@ function PrefixDialog({
onClick={handleSave}
>
<Save className="h-4 w-4 mr-1" />
{saving ? "Salvando..." : "Salva"}
{saving ? "Saving..." : "Save"}
</Button>
</div>
</div>
@@ -338,6 +338,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
const params = new URLSearchParams({ page: String(page) });
if (searchQuery) params.set("q", searchQuery);
const res = await fetch(`/api/admin/prefixes?${params}`);
if (!res.ok) return;
const data = await res.json();
if (data.ok) {
setPrefixes(data.prefixes);
@@ -347,12 +348,14 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
const fetchBlacklist = useCallback(async () => {
const res = await fetch("/api/admin/prefixes/blacklist");
if (!res.ok) return;
const data = await res.json();
if (data.ok) setBlacklist(data.words);
}, []);
const fetchSettings = useCallback(async () => {
const res = await fetch("/api/admin/prefixes/settings");
if (!res.ok) return;
const data = await res.json();
if (data.ok) setSettings(data.settings);
}, []);
@@ -367,9 +370,9 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
}, [fetchBlacklist, fetchSettings]);
const handleDeletePrefix = async (id: number) => {
if (!confirm("Sei sicuro di voler eliminare questo prefisso?")) return;
if (!confirm("Are you sure you want to delete this prefix?")) return;
run(() => deletePrefixAction({ id }), {
successMessage: "Prefisso eliminato!",
successMessage: "Prefix deleted!",
onSuccess: () => {
fetchPrefixes();
},
@@ -389,7 +392,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
active: data.active ? 1 : 0,
}),
{
successMessage: "Prefisso aggiornato!",
successMessage: "Prefix updated!",
onSuccess: () => {
fetchPrefixes();
},
@@ -407,7 +410,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
active: data.active ? 1 : 0,
}),
{
successMessage: "Prefisso creato!",
successMessage: "Prefix created!",
onSuccess: () => {
fetchPrefixes();
},
@@ -429,7 +432,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
const handleAddBlacklistWord = async () => {
if (!newWord.trim()) return;
run(() => addBlacklistWordAction({ word: newWord.trim() }), {
successMessage: "Parola aggiunta alla blacklist!",
successMessage: "Word added to blacklist!",
onSuccess: () => {
setNewWord("");
fetchBlacklist();
@@ -439,7 +442,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
const handleRemoveBlacklistWord = async (id: number) => {
run(() => removeBlacklistWordAction({ id }), {
successMessage: "Parola rimossa dalla blacklist!",
successMessage: "Word removed from blacklist!",
onSuccess: () => {
fetchBlacklist();
},
@@ -453,7 +456,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
settings: settings as unknown as Record<string, string>,
}),
{
successMessage: "Impostazioni salvate!",
successMessage: "Settings saved!",
},
);
};
@@ -469,9 +472,9 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
<Tabs defaultValue="prefixes">
<TabsList>
<TabsTrigger value="prefixes">Prefissi Utenti</TabsTrigger>
<TabsTrigger value="prefixes">User Prefixes</TabsTrigger>
<TabsTrigger value="blacklist">Blacklist</TabsTrigger>
<TabsTrigger value="settings">Impostazioni</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="prefixes" className="space-y-4">
@@ -480,7 +483,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
className="pl-8"
placeholder="Cerca per utente o testo..."
placeholder="Search by user or text..."
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
@@ -489,12 +492,12 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
/>
</div>
<Button variant="outline" onClick={fetchPrefixes}>
Cerca
Search
</Button>
{canEdit && (
<Button onClick={openAddDialog}>
<Plus className="h-4 w-4 mr-1" />
Aggiungi
Add
</Button>
)}
</div>
@@ -503,14 +506,14 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Utente</TableHead>
<TableHead>User</TableHead>
<TableHead>Prefix</TableHead>
<TableHead>Colore</TableHead>
<TableHead>Icona</TableHead>
<TableHead>Effetto</TableHead>
<TableHead>Attivo</TableHead>
<TableHead>Data</TableHead>
{canEdit && <TableHead>Azioni</TableHead>}
<TableHead>Color</TableHead>
<TableHead>Icon</TableHead>
<TableHead>Effect</TableHead>
<TableHead>Active</TableHead>
<TableHead>Date</TableHead>
{canEdit && <TableHead>Actions</TableHead>}
</TableRow>
</TableHeader>
<TableBody>
@@ -580,9 +583,11 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
?.label || prefix.effect
: "-"}
</TableCell>
<TableCell>{prefix.active ? "Si" : "No"}</TableCell>
<TableCell>{prefix.active ? "Yes" : "No"}</TableCell>
<TableCell className="text-xs">
{new Date(prefix.created_at).toLocaleDateString()}
{prefix.created_at
? new Date(prefix.created_at).toLocaleDateString()
: "—"}
</TableCell>
{canEdit && (
<TableCell>
@@ -614,7 +619,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
className="text-center text-muted-foreground"
colSpan={9}
>
Nessun prefisso trovato
No prefixes found
</TableCell>
</TableRow>
)}
@@ -629,10 +634,10 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
variant="outline"
onClick={() => setPage(page - 1)}
>
Precedente
Previous
</Button>
<span className="text-sm">
Pagina {page} di {totalPages}
Page {page} of {totalPages}
</span>
<Button
disabled={page >= totalPages}
@@ -640,7 +645,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
variant="outline"
onClick={() => setPage(page + 1)}
>
Successiva
Next
</Button>
</div>
)}
@@ -649,13 +654,13 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
<TabsContent value="blacklist" className="space-y-4">
<Card>
<CardHeader>
<CardTitle>Parole Vietate</CardTitle>
<CardTitle>Forbidden Words</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{canEdit && (
<div className="flex items-center gap-2">
<Input
placeholder="Aggiungi parola..."
placeholder="Add word..."
value={newWord}
onChange={(e) => setNewWord(e.target.value)}
onKeyDown={(e) =>
@@ -664,7 +669,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
/>
<Button disabled={isPending} onClick={handleAddBlacklistWord}>
<Plus className="h-4 w-4 mr-1" />
Aggiungi
Add
</Button>
</div>
)}
@@ -689,7 +694,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
))}
{blacklist.length === 0 && (
<p className="text-muted-foreground text-sm">
Nessuna parola in blacklist
No blacklist words
</p>
)}
</div>
@@ -700,12 +705,12 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
<TabsContent value="settings" className="space-y-4">
<Card>
<CardHeader>
<CardTitle>Impostazioni Prefissi</CardTitle>
<CardTitle>Prefix Settings</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<span className="text-sm font-medium">Lunghezza Massima</span>
<span className="text-sm font-medium">Max Length</span>
<Input
disabled={!canEdit}
type="number"
@@ -716,7 +721,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
/>
</div>
<div className="space-y-2">
<span className="text-sm font-medium">Rank Minimo</span>
<span className="text-sm font-medium">Minimum Rank</span>
<Input
disabled={!canEdit}
type="number"
@@ -730,7 +735,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
/>
</div>
<div className="space-y-2">
<span className="text-sm font-medium">Prezzo (Crediti)</span>
<span className="text-sm font-medium">Price (Credits)</span>
<Input
disabled={!canEdit}
type="number"
@@ -744,7 +749,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
/>
</div>
<div className="space-y-2">
<span className="text-sm font-medium">Prezzo (Punti)</span>
<span className="text-sm font-medium">Price (Points)</span>
<Input
disabled={!canEdit}
type="number"
@@ -755,7 +760,7 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
/>
</div>
<div className="space-y-2">
<span className="text-sm font-medium">Tipo Punti</span>
<span className="text-sm font-medium">Points Type</span>
<Input
disabled={!canEdit}
type="number"
@@ -765,14 +770,14 @@ export function PrefixesClient({ canEdit }: { canEdit: boolean }) {
}
/>
<p className="text-xs text-muted-foreground">
0 = Duckets, 5 = Diamonds, ecc.
0 = Duckets, 5 = Diamonds, etc.
</p>
</div>
</div>
{canEdit && (
<Button disabled={isPending} onClick={saveSettings}>
<Save className="h-4 w-4 mr-1" />
Salva Impostazioni
Save Settings
</Button>
)}
</CardContent>