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

+4
View File
@@ -116,10 +116,14 @@ const SETTINGS_WHITELIST = new Set([
"enabled",
"max_length",
"min_rank",
"min_rank_to_buy",
"allow_colors",
"allow_bold",
"allow_italic",
"default_color",
"price_credits",
"price_points",
"points_type",
]);
const updatePrefixSettingsSchema = z.object({
@@ -408,7 +408,7 @@ export function NitroEditorDialog({
htmlFor="dim-y"
className="text-xs text-muted-foreground"
>
Y (profondità)
Y (depth)
</Label>
<Input
id="dim-y"
@@ -817,7 +817,7 @@ export function NitroEditorDialog({
onClick={() => onOpenChange(false)}
disabled={saving}
>
Annulla
Cancel
</Button>
<Button
onClick={handleSave}
@@ -829,7 +829,7 @@ export function NitroEditorDialog({
) : (
<Save className="w-4 h-4" />
)}
Salva
Save
</Button>
</div>
</DialogFooter>
+10 -10
View File
@@ -109,24 +109,24 @@ function getNavGroups(t: (key: string) => string) {
},
{
href: "/admin/events",
label: "Events",
label: t("events"),
icon: <Calendar size={16} />,
},
{
href: "/admin/polls",
label: "Polls",
label: t("polls"),
icon: <ClipboardList size={16} />,
},
{ href: "/admin/banners", label: "Banners", icon: <Image size={16} /> },
{ href: "/admin/banners", label: t("banners"), icon: <Image size={16} /> },
{
href: "/admin/prefixes",
label: "Prefixes",
label: t("prefixes"),
icon: <Languages size={16} />,
},
],
},
{
label: t("usersAccess"),
label: t("usersAndAccess"),
icon: <Shield size={14} />,
items: [
{ href: "/admin/users", label: t("users"), icon: <Users size={16} /> },
@@ -150,12 +150,12 @@ function getNavGroups(t: (key: string) => string) {
},
{
href: "/admin/online",
label: "Online Users",
label: t("onlineUsers"),
icon: <Users size={16} />,
},
{
href: "/admin/moderation",
label: "Moderation",
label: t("moderation"),
icon: <Shield size={16} />,
},
],
@@ -286,15 +286,15 @@ function getNavGroups(t: (key: string) => string) {
{ href: "/admin/logs", label: t("logs"), icon: <FileText size={16} /> },
{
href: "/admin/logs/audit",
label: "Audit Log",
label: t("auditLog"),
icon: <FileText size={16} />,
},
{
href: "/admin/analytics",
label: "Analytics",
label: t("analytics"),
icon: <LayoutDashboard size={16} />,
},
{ href: "/admin/devops", label: "DevOps", icon: <Monitor size={16} /> },
{ href: "/admin/devops", label: t("devops"), icon: <Monitor size={16} /> },
{
href: "/admin/wordfilter",
label: t("wordFilter"),
+1 -1
View File
@@ -168,7 +168,7 @@ export default async function AdminDashboard() {
{t("recentStaffActivity")}
</h2>
<Link
href="/admin/commandocentrum"
href="/admin/logs"
className="text-xs font-medium text-[var(--admin-accent)] hover:text-[var(--admin-accent)] flex items-center gap-1 no-underline"
>
{t("viewAll")} <ArrowRight size={12} />
+7 -3
View File
@@ -1,6 +1,6 @@
import { notFound, redirect } from "next/navigation";
import { AdminBreadcrumb } from "@/components/admin/admin-breadcrumb";
import { getAdminContext } from "@/lib/permissions";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
import { fetchEmulatorRankForEdit } from "@/lib/services/permission-ranks";
import { RankEditClient } from "./rank-edit-client";
@@ -11,8 +11,12 @@ export default async function RankEditPage({
params: Promise<{ locale: string; id: string }>;
}) {
const { id: rawId } = await params;
const { permissions } = await getAdminContext();
if (!permissions.isSuperAdmin) redirect("/admin");
const { session, permissions } = await getAdminContext();
if (
!canAccess(permissions, PERMS.PERMISSIONS_MANAGE, session.user.rank)
) {
redirect("/admin");
}
const rankId = parseInt(rawId, 10);
if (Number.isNaN(rankId)) notFound();
+7 -3
View File
@@ -1,6 +1,6 @@
import { redirect } from "next/navigation";
import { Prisma } from "@/generated/prisma/client";
import { getAdminContext } from "@/lib/permissions";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
import { fetchEmulatorRankSummaries } from "@/lib/services/permission-ranks";
import { PermissionsList } from "./permissions-list";
@@ -8,8 +8,12 @@ import { PermissionsList } from "./permissions-list";
export default async function PermissionsPage(_props: {
params: Promise<{ locale: string }>;
}) {
const { permissions } = await getAdminContext();
if (!permissions.isSuperAdmin) redirect("/admin");
const { session, permissions } = await getAdminContext();
if (
!canAccess(permissions, PERMS.PERMISSIONS_MANAGE, session.user.rank)
) {
redirect("/admin");
}
const ranks = await fetchEmulatorRankSummaries(prisma);
+1 -1
View File
@@ -20,7 +20,7 @@ export default async function PrefixesPage() {
<div>
<h1 className="text-3xl font-bold tracking-tight">Custom Prefixes</h1>
<p className="text-muted-foreground">
Gestisci i prefissi degli utenti, la blacklist e le impostazioni
Manage user prefixes, blacklist and settings
</p>
</div>
<PrefixesClient canEdit={canEdit} />
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -70,11 +70,11 @@ export default async function SoundsPage() {
<div>
<h1 className="text-3xl font-bold tracking-tight">Song Disks</h1>
<p className="text-muted-foreground">
Carica e gestisci i file MP3 dei song disk riprodotti dai jukebox
in-game. I file sono serviti da <code>{baseUrl}</code> (configurabile
in{" "}
<a href="/admin/settings/cms" className="underline">
Impostazioni &gt; CMS
Upload and manage MP3 files for song disks played by in-game
jukeboxes. Files are served from <code>{baseUrl}</code>{" "}
(configurable in{" "}
<a href="/admin/settings" className="underline">
Settings
</a>
).
</p>
File diff suppressed because it is too large. Load diff
@@ -74,11 +74,11 @@ export function ClientTranslations({
function handleSave() {
if (activeFile.readOnly) return;
run(() => saveClientTranslations({ fileId: activeFile.id, data }), {
successMessage: "File salvato.",
successMessage: "File saved.",
onSuccess: (result) => {
if (result?.commentsLost) {
toast.warning(
"Alcune chiavi non corrispondevano al formato standard: file riserializzato e commenti rimossi.",
"Some keys did not match the standard format: file was reserialized and comments were removed.",
);
}
router.refresh();
@@ -115,7 +115,7 @@ export function ClientTranslations({
disabled={isPending || activeFile.readOnly || !!parseError}
>
<Save className="w-4 h-4 mr-1" />{" "}
{isPending ? "Salvataggio..." : "Salva"}
{isPending ? "Saving..." : "Save"}
</Button>
</div>
</div>
@@ -144,7 +144,7 @@ export function ClientTranslations({
</Select>
<Input
placeholder="Cerca per chiave o valore..."
placeholder="Search by key or value..."
value={search}
onChange={(e) => {
setSearch(e.target.value);
@@ -154,7 +154,7 @@ export function ClientTranslations({
/>
<span className="text-sm text-muted-foreground">
{filteredKeys.length} chiavi
{filteredKeys.length} keys
</span>
</div>
@@ -174,7 +174,7 @@ export function ClientTranslations({
)}
{activeFile.hasComments && !activeFile.readOnly && (
<Badge variant="secondary" className="gap-1 text-[11px]">
<Info className="w-3 h-3" /> commenti preservati
<Info className="w-3 h-3" /> comments preserved
</Badge>
)}
</div>
@@ -189,9 +189,9 @@ export function ClientTranslations({
<div className="rounded-md border border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)] px-3 py-2 text-xs text-[var(--admin-warning)] dark:text-[var(--admin-warning)] flex items-start gap-2">
<Lock className="w-4 h-4 flex-shrink-0 mt-0.5" />
<div>
Questo file è di sola lettura.{" "}
This file is read-only.{" "}
{activeFile.note ??
"Modificarlo manualmente non è supportato dal CMS."}
"Manual editing is not supported by the CMS."}
</div>
</div>
)}
@@ -200,24 +200,23 @@ export function ClientTranslations({
<div className="rounded-md border border-[var(--admin-info-border)] bg-[var(--admin-info-subtle)] px-3 py-2 text-xs text-[var(--admin-info)] dark:text-[var(--admin-info)] flex items-start gap-2">
<Info className="w-4 h-4 flex-shrink-0 mt-0.5" />
<div>
I commenti vengono preservati: il salvataggio modifica solo le righe
dei valori cambiati. Se una chiave usa un formato non standard ed è
impossibile patchare in-place, il file viene riserializzato
(commenti persi) e ti avviseremo.
Comments are preserved: saving only patches changed value lines. If
a key uses a non-standard format and cannot be patched in-place, the
file is reserialized (comments lost) and you will be warned.
</div>
</div>
)}
{parseError && (
<div className="rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive">
Errore di parsing: {parseError}
Parse error: {parseError}
</div>
)}
<div className="rounded-lg border bg-card divide-y">
{pageKeys.length === 0 && !parseError && (
<div className="px-4 py-6 text-sm text-muted-foreground text-center">
Nessuna voce.
No entries.
</div>
)}
{pageKeys.map((key) => (
@@ -241,7 +240,7 @@ export function ClientTranslations({
{totalPages > 1 && (
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">
Pagina {page} di {totalPages}
Page {page} of {totalPages}
</span>
<div className="flex gap-1">
<Button
+1 -1
View File
@@ -83,7 +83,7 @@ export default async function EmulatorTranslationsPage({
<p className="text-sm text-muted-foreground">{t("subtitle")}</p>
</div>
<Button asChild variant="outline" size="sm">
<Link href="/admin/settings/emulator">
<Link href="/admin/emulator">
<ExternalLink className="w-4 h-4 mr-1" />
{t("fullSettings")}
</Link>
@@ -0,0 +1,48 @@
import { withAdmin } from "@/lib/api-handler";
import { apiError, apiOk } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
interface BlacklistWord {
id: number;
word: string;
}
export const GET = withAdmin({ permission: PERMS.PREFIXES_VIEW }, async () => {
const words = await prisma.$queryRaw<BlacklistWord[]>`
SELECT id, word FROM custom_prefix_blacklist ORDER BY word ASC
`;
return apiOk({
words: words.map((w) => ({ ...w, created_at: "" })),
});
});
export const POST = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const word = typeof body.word === "string" ? body.word.trim() : "";
if (!word) return apiError("Word is required");
await prisma.$executeRaw`INSERT INTO custom_prefix_blacklist (word) VALUES (${word})`;
return apiOk();
},
);
export const DELETE = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const id = Number(body.id);
if (!Number.isInteger(id) || id <= 0)
return apiError("Missing or invalid word id");
await prisma.$executeRaw`DELETE FROM custom_prefix_blacklist WHERE id = ${id}`;
return apiOk({ deleted: id });
},
);
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,70 @@
import { withAdmin } from "@/lib/api-handler";
import { apiError, apiOk } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
interface PrefixSetting {
key: string;
value: string;
}
const VALID_KEYS = new Set([
"enabled",
"max_length",
"min_rank",
"min_rank_to_buy",
"allow_colors",
"allow_bold",
"allow_italic",
"default_color",
"price_credits",
"price_points",
"points_type",
]);
const DEFAULT_SETTINGS: Record<string, string> = {
max_length: "15",
min_rank_to_buy: "1",
price_credits: "5",
price_points: "0",
points_type: "0",
};
export const GET = withAdmin({ permission: PERMS.PREFIXES_VIEW }, async () => {
const settings = await prisma.$queryRaw<PrefixSetting[]>`
SELECT \`key\`, \`value\` FROM custom_prefix_settings
`;
const result: Record<string, string> = { ...DEFAULT_SETTINGS };
for (const s of settings) {
result[s.key] = s.value;
}
return apiOk({ settings: result });
});
export const PUT = withAdmin(
{ permission: PERMS.PREFIXES_EDIT },
async (request) => {
const body = await request.json();
const settings = body.settings;
if (!settings || typeof settings !== "object") {
return apiError("Settings object is required");
}
for (const [key, value] of Object.entries(settings)) {
if (!VALID_KEYS.has(key)) {
return apiError(`Invalid setting key: ${key}`);
}
const strValue = String(value);
await prisma.$executeRaw`
INSERT INTO custom_prefix_settings (\`key\`, \`value\`)
VALUES (${key}, ${strValue})
ON DUPLICATE KEY UPDATE \`value\` = ${strValue}
`;
}
return apiOk();
},
);
+11 -1
View File
@@ -104,10 +104,20 @@ const getCurrentAuthorizationState = cache(async (userId: number) =>
resolveAuthorizationState(userId, {
user: prisma.user,
highestRank: async () => {
// Prefer the highest rank actually held by a user. Unused high IDs in
// permission_ranks (common on Habbo DBs) would otherwise lock the real
// owner out of super-admin / permissions management.
const rows = await prisma.$queryRaw<
{ highest_rank: number | bigint | null }[]
>`
SELECT MAX(id) AS highest_rank FROM permission_ranks
SELECT COALESCE(
(
SELECT MAX(u.\`rank\`)
FROM users u
INNER JOIN permission_ranks pr ON pr.id = u.\`rank\`
),
(SELECT MAX(id) FROM permission_ranks)
) AS highest_rank
`;
return rows[0]?.highest_rank == null
? null
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Кутии за писане",
"photos": "Снимки",
"tickets": "Билети",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Потребители и достъп",
"users": "Потребители",
"bans": "забрани",
@@ -706,6 +710,8 @@
"applications": "Приложения",
"teams": "Екипи",
"permissions": "Разрешения",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "икономичност",
"catalog": "Каталог",
"shop": "Магазин",
@@ -732,6 +738,9 @@
"emulator": "Емулатор",
"alerts": "Сигнали",
"logs": "трупи",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Филтър за думи",
"maintenance": "Поддръжка",
"rooms": "Стаи",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Popsatelné krabice",
"photos": "Fotografie",
"tickets": "Vstupenky",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Uživatelé a přístup",
"users": "Uživatelé",
"bans": "Zákazy",
@@ -706,6 +710,8 @@
"applications": "Aplikace",
"teams": "Týmy",
"permissions": "Oprávnění",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Ekonomika",
"catalog": "Katalog",
"shop": "Obchod",
@@ -732,6 +738,9 @@
"emulator": "Emulátor",
"alerts": "Upozornění",
"logs": "Protokoly",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filtr slov",
"maintenance": "Údržba",
"rooms": "Pokoje",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Skrivbare kasser",
"photos": "Fotos",
"tickets": "Billetter",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Brugere og adgang",
"users": "Brugere",
"bans": "Forbud",
@@ -706,6 +710,8 @@
"applications": "Ansøgninger",
"teams": "Hold",
"permissions": "Tilladelser",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Økonomi",
"catalog": "Katalog",
"shop": "Butik",
@@ -732,6 +738,9 @@
"emulator": "Emulator",
"alerts": "Advarsler",
"logs": "Logs",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Ord filter",
"maintenance": "Vedligeholdelse",
"rooms": "Værelser",
+15 -6
View File
@@ -662,6 +662,10 @@
"writeableBoxes": "Editierbare Boxen",
"photos": "Fotos",
"tickets": "Tickets",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Benutzer & Zugang",
"users": "Benutzer",
"bans": "Banns",
@@ -670,6 +674,8 @@
"applications": "Bewerbungen",
"teams": "Teams",
"permissions": "Berechtigungen",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Wirtschaft",
"catalog": "Katalog",
"shop": "Shop",
@@ -696,21 +702,24 @@
"emulator": "Emulator",
"alerts": "Alarme",
"logs": "Logs",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Wortfilter",
"maintenance": "Wartung",
"housekeeping": "Hausverwaltung",
"rooms": "Räume",
"emailTemplates": "E-Mail-Vorlagen",
"theme": "Design",
"media": "Medien",
"settings": "Einstellungen",
"backToSite": "Zurück zur Website",
"hotelManagementPanel": "Hotel-Verwaltungsbereich",
"rankLabel": "Rang {rank}",
"favicon": "Favicon",
"sounds": "Sounds",
"translations": "Übersetzungen",
"import": "Importieren"
"import": "Importieren",
"settings": "Einstellungen",
"backToSite": "Zurück zur Website",
"housekeeping": "Hausverwaltung",
"hotelManagementPanel": "Hotel-Verwaltungsbereich",
"rankLabel": "Rang {rank}"
},
"dashboard": {
"title": "Dashboard",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Εγγράψιμα κουτιά",
"photos": "Φωτογραφίες",
"tickets": "Εισιτήρια",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Χρήστες και πρόσβαση",
"users": "Χρήστες",
"bans": "Απαγορεύσεις",
@@ -706,6 +710,8 @@
"applications": "Εφαρμογές",
"teams": "Ομάδες",
"permissions": "Άδειες",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Οικονομία",
"catalog": "Κατάλογος",
"shop": "Κατάστημα",
@@ -732,6 +738,9 @@
"emulator": "Εξομοιωτής",
"alerts": "Ειδοποιήσεις",
"logs": "κούτσουρα",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Φίλτρο λέξεων",
"maintenance": "Συντήρηση",
"rooms": "Δωμάτια",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Writeable boxes",
"photos": "Photos",
"tickets": "Tickets",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Users & access",
"users": "Users",
"bans": "Bans",
@@ -706,6 +710,8 @@
"applications": "Applications",
"teams": "Teams",
"permissions": "Permissions",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Economy",
"catalog": "Catalog",
"shop": "Shop",
@@ -732,6 +738,9 @@
"emulator": "Emulator",
"alerts": "Alerts",
"logs": "Logs",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Word filter",
"maintenance": "Maintenance",
"rooms": "Rooms",
+15 -6
View File
@@ -662,6 +662,10 @@
"writeableBoxes": "Cajas editables",
"photos": "Fotos",
"tickets": "Tickets",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Usuarios y acceso",
"users": "Usuarios",
"bans": "Sanciones",
@@ -670,6 +674,8 @@
"applications": "Solicitudes",
"teams": "Equipos",
"permissions": "Permisos",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Economía",
"catalog": "Catálogo",
"shop": "Tienda",
@@ -696,21 +702,24 @@
"emulator": "Emulador",
"alerts": "Alertas",
"logs": "Registros",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filtro de palabras",
"maintenance": "Mantenimiento",
"housekeeping": "Housekeeping",
"rooms": "Salas",
"emailTemplates": "Plantillas de correo",
"theme": "Tema",
"media": "Multimedia",
"settings": "Ajustes",
"backToSite": "Volver al sitio",
"hotelManagementPanel": "Panel de gestión del hotel",
"rankLabel": "Rango {rank}",
"favicon": "Favicon",
"sounds": "Sonidos",
"translations": "Traducciones",
"import": "Importar"
"import": "Importar",
"settings": "Ajustes",
"backToSite": "Volver al sitio",
"housekeeping": "Housekeeping",
"hotelManagementPanel": "Panel de gestión del hotel",
"rankLabel": "Rango {rank}"
},
"dashboard": {
"title": "Panel",
+15 -6
View File
@@ -662,6 +662,10 @@
"writeableBoxes": "Boîtes modifiables",
"photos": "Photos",
"tickets": "Tickets",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Utilisateurs & accès",
"users": "Utilisateurs",
"bans": "Bannissements",
@@ -670,6 +674,8 @@
"applications": "Candidatures",
"teams": "Équipes",
"permissions": "Permissions",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Économie",
"catalog": "Catalogue",
"shop": "Boutique",
@@ -696,21 +702,24 @@
"emulator": "Émulateur",
"alerts": "Alertes",
"logs": "Journaux",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filtre de mots",
"maintenance": "Maintenance",
"housekeeping": "Gouvernance",
"rooms": "Salons",
"emailTemplates": "Modèles d'e-mail",
"theme": "Thème",
"media": "Médias",
"settings": "Paramètres",
"backToSite": "Retour au site",
"hotelManagementPanel": "Panneau de gestion de l'hôtel",
"rankLabel": "Rang {rank}",
"favicon": "Favicon",
"sounds": "Sons",
"translations": "Traductions",
"import": "Importer"
"import": "Importer",
"settings": "Paramètres",
"backToSite": "Retour au site",
"housekeeping": "Gouvernance",
"hotelManagementPanel": "Panneau de gestion de l'hôtel",
"rankLabel": "Rang {rank}"
},
"dashboard": {
"title": "Tableau de bord",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Kutije za pisanje",
"photos": "Fotografije",
"tickets": "Ulaznice",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Korisnici i pristup",
"users": "Korisnici",
"bans": "Zabrane",
@@ -706,6 +710,8 @@
"applications": "Prijave",
"teams": "Timovi",
"permissions": "Dozvole",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Gospodarstvo",
"catalog": "Katalog",
"shop": "Trgovina",
@@ -732,6 +738,9 @@
"emulator": "Emulator",
"alerts": "upozorenja",
"logs": "Dnevnici",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filter riječi",
"maintenance": "Održavanje",
"rooms": "Sobe",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Írható dobozok",
"photos": "Fényképek",
"tickets": "Jegyek",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Felhasználók és hozzáférés",
"users": "Felhasználók",
"bans": "Tiltások",
@@ -706,6 +710,8 @@
"applications": "Alkalmazások",
"teams": "Csapatok",
"permissions": "Engedélyek",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Gazdaság",
"catalog": "Katalógus",
"shop": "Bolt",
@@ -732,6 +738,9 @@
"emulator": "Emulátor",
"alerts": "Figyelmeztetések",
"logs": "Naplók",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Szószűrő",
"maintenance": "Karbantartás",
"rooms": "Szobák",
+9
View File
@@ -662,6 +662,10 @@
"writeableBoxes": "Box modificabili",
"photos": "Foto",
"tickets": "Ticket",
"events": "Eventi",
"polls": "Sondaggi",
"banners": "Banner",
"prefixes": "Prefissi",
"usersAndAccess": "Utenti e accesso",
"users": "Utenti",
"bans": "Ban",
@@ -670,6 +674,8 @@
"applications": "Candidature",
"teams": "Team",
"permissions": "Permessi",
"onlineUsers": "Utenti online",
"moderation": "Moderazione",
"economy": "Economia",
"catalog": "Catalogo",
"shop": "Negozio",
@@ -696,6 +702,9 @@
"emulator": "Emulatore",
"alerts": "Avvisi",
"logs": "Registri",
"auditLog": "Log di audit",
"analytics": "Analitiche",
"devops": "DevOps",
"wordFilter": "Filtro parole",
"maintenance": "Manutenzione",
"housekeeping": "Housekeeping",
+21 -12
View File
File diff suppressed because it is too large. Load diff
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Skrivbare bokser",
"photos": "Bilder",
"tickets": "Billetter",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Brukere og tilgang",
"users": "Brukere",
"bans": "Forbud",
@@ -706,6 +710,8 @@
"applications": "Søknader",
"teams": "Lag",
"permissions": "Tillatelser",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Økonomi",
"catalog": "Katalog",
"shop": "Handle",
@@ -732,6 +738,9 @@
"emulator": "Emulator",
"alerts": "Varsler",
"logs": "Logger",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Ordfilter",
"maintenance": "Vedlikehold",
"rooms": "Rom",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Pudełka z możliwością zapisu",
"photos": "Zdjęcia",
"tickets": "Bilety",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Użytkownicy i dostęp",
"users": "Użytkownicy",
"bans": "Zakazy",
@@ -706,6 +710,8 @@
"applications": "Aplikacje",
"teams": "Zespoły",
"permissions": "Uprawnienia",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Ekonomia",
"catalog": "Katalog",
"shop": "Sklep",
@@ -732,6 +738,9 @@
"emulator": "Emulator",
"alerts": "Alerty",
"logs": "Dzienniki",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filtr słów",
"maintenance": "Konserwacja",
"rooms": "Pokoje",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Caixas graváveis",
"photos": "Fotos",
"tickets": "Ingressos",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Usuários e acesso",
"users": "Usuários",
"bans": "Proibições",
@@ -706,6 +710,8 @@
"applications": "Aplicativos",
"teams": "Equipes",
"permissions": "Permissões",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Economia",
"catalog": "Catálogo",
"shop": "Loja",
@@ -732,6 +738,9 @@
"emulator": "Emulador",
"alerts": "Alertas",
"logs": "Registros",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filtro de palavras",
"maintenance": "Manutenção",
"rooms": "Quartos",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Cutii care pot fi scrise",
"photos": "Fotografii",
"tickets": "bilete",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Utilizatori și acces",
"users": "Utilizatori",
"bans": "Interdicții",
@@ -706,6 +710,8 @@
"applications": "Aplicații",
"teams": "Echipe",
"permissions": "Permisiuni",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Economie",
"catalog": "Catalog",
"shop": "Magazin",
@@ -732,6 +738,9 @@
"emulator": "Emulator",
"alerts": "Alerte",
"logs": "Bușteni",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filtru de cuvinte",
"maintenance": "Întreținere",
"rooms": "Camerele",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Ящики для записи",
"photos": "Фото",
"tickets": "Билеты",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Пользователи и доступ",
"users": "Пользователи",
"bans": "Запреты",
@@ -706,6 +710,8 @@
"applications": "Области применения",
"teams": "Команды",
"permissions": "Разрешения",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Экономика",
"catalog": "Каталог",
"shop": "Магазин",
@@ -732,6 +738,9 @@
"emulator": "Эмулятор",
"alerts": "Оповещения",
"logs": "Журналы",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Фильтр слов",
"maintenance": "Техническое обслуживание",
"rooms": "Номера",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Popisovateľné boxy",
"photos": "Fotografie",
"tickets": "Vstupenky",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Používatelia a prístup",
"users": "Používatelia",
"bans": "Zákazy",
@@ -706,6 +710,8 @@
"applications": "Aplikácie",
"teams": "Tímy",
"permissions": "Povolenia",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "ekonomika",
"catalog": "Katalóg",
"shop": "Obchod",
@@ -732,6 +738,9 @@
"emulator": "Emulátor",
"alerts": "Upozornenia",
"logs": "Denníky",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Filter slov",
"maintenance": "Údržba",
"rooms": "Izby",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Кутије за писање",
"photos": "Фотографије",
"tickets": "Улазнице",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Корисници и приступ",
"users": "Корисници",
"bans": "Банс",
@@ -706,6 +710,8 @@
"applications": "Апликације",
"teams": "Тимови",
"permissions": "Дозволе",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Економија",
"catalog": "Каталог",
"shop": "Схоп",
@@ -732,6 +738,9 @@
"emulator": "Емулатор",
"alerts": "Алертс",
"logs": "Дневници",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Филтер речи",
"maintenance": "Одржавање",
"rooms": "Собе",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Skrivbara lådor",
"photos": "Foton",
"tickets": "Biljetter",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Användare och åtkomst",
"users": "Användare",
"bans": "Förbud",
@@ -706,6 +710,8 @@
"applications": "Ansökningar",
"teams": "Lag",
"permissions": "Behörigheter",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Ekonomi",
"catalog": "Katalog",
"shop": "Handla",
@@ -732,6 +738,9 @@
"emulator": "Emulator",
"alerts": "Varningar",
"logs": "Loggar",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Ordfilter",
"maintenance": "Underhåll",
"rooms": "Rum",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Yazılabilir kutular",
"photos": "Fotoğraflar",
"tickets": "Biletler",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Kullanıcılar ve erişim",
"users": "Kullanıcılar",
"bans": "Yasaklar",
@@ -706,6 +710,8 @@
"applications": "Uygulamalar",
"teams": "Takımlar",
"permissions": "İzinler",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Ekonomi",
"catalog": "Katalog",
"shop": "Mağaza",
@@ -732,6 +738,9 @@
"emulator": "Emülatör",
"alerts": "Uyarılar",
"logs": "Günlükler",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Kelime filtresi",
"maintenance": "Bakım",
"rooms": "Odalar",
+9
View File
@@ -698,6 +698,10 @@
"writeableBoxes": "Ящики для запису",
"photos": "Фотографії",
"tickets": "квитки",
"events": "Events",
"polls": "Polls",
"banners": "Banners",
"prefixes": "Prefixes",
"usersAndAccess": "Користувачі та доступ",
"users": "Користувачі",
"bans": "Заборони",
@@ -706,6 +710,8 @@
"applications": "Додатки",
"teams": "Команди",
"permissions": "Дозволи",
"onlineUsers": "Online users",
"moderation": "Moderation",
"economy": "Економіка",
"catalog": "Каталог",
"shop": "Магазин",
@@ -732,6 +738,9 @@
"emulator": "Емулятор",
"alerts": "Оповіщення",
"logs": "Журнали",
"auditLog": "Audit log",
"analytics": "Analytics",
"devops": "DevOps",
"wordFilter": "Фільтр Word",
"maintenance": "Технічне обслуговування",
"rooms": "Кімнати",