Fix admin permissions bounce, prefixes APIs, and Italian UI leftovers.
Local Build and Deploy / deploy (push) Successful in 56s
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:
1 parent
0e89d03940
commit
3403d3b19f
38 files changed
+673
-208
No files matched your search
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user