diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx
index cf1ece4c..9508fc45 100644
--- a/src/app/admin/layout.tsx
+++ b/src/app/admin/layout.tsx
@@ -116,6 +116,7 @@ async function Sidebar({
key={`${group.labelKey}-${item.href}`}
href={item.href}
matchPrefixes={item.matchPrefixes}
+ matchExcludePrefixes={item.matchExcludePrefixes}
icon={}
>
{t(item.labelKey)}
diff --git a/src/components/admin/admin-hub-chrome.tsx b/src/components/admin/admin-hub-chrome.tsx
index 893e369f..99fffc68 100644
--- a/src/components/admin/admin-hub-chrome.tsx
+++ b/src/components/admin/admin-hub-chrome.tsx
@@ -30,7 +30,7 @@ export function AdminHubChrome({ children }: { children: ReactNode }) {
icon={hub.icon}
title={t(hub.titleKey)}
subtitle={t(hub.subtitleKey)}
- tabs={}
+ tabs={tabs.length > 1 ? : undefined}
className="admin-hub-panel"
>
{children}
diff --git a/src/components/admin/admin-nav-link.tsx b/src/components/admin/admin-nav-link.tsx
index 5dceb87e..3f63ba0a 100644
--- a/src/components/admin/admin-nav-link.tsx
+++ b/src/components/admin/admin-nav-link.tsx
@@ -13,18 +13,26 @@ export function AdminNavLink({
href,
icon,
matchPrefixes,
+ matchExcludePrefixes,
children,
}: {
href: string;
icon?: ReactNode;
matchPrefixes?: string[];
+ matchExcludePrefixes?: string[];
children: ReactNode;
}) {
const pathname = usePathname() ?? "";
const { close } = useAdminMobile();
+ const excluded = matchExcludePrefixes?.some(
+ (p) => pathname === p || pathname.startsWith(`${p}/`),
+ );
+
let active: boolean;
- if (href === "/admin") {
+ if (excluded) {
+ active = false;
+ } else if (href === "/admin") {
active = pathname === "/admin";
} else if (matchPrefixes?.length) {
active = matchPrefixes.some(
diff --git a/src/lib/admin-nav.ts b/src/lib/admin-nav.ts
index 5c0b21a5..796d96c0 100644
--- a/src/lib/admin-nav.ts
+++ b/src/lib/admin-nav.ts
@@ -1,19 +1,42 @@
import type { LucideIcon } from "lucide-react";
import {
+ Activity,
+ AlertTriangle,
+ BadgeCheck,
Ban,
Calendar,
+ CalendarDays,
+ ClipboardList,
Cog,
Compass,
+ FileText,
+ Filter,
Gavel,
+ HelpCircle,
+ Image,
+ Import,
+ KeyRound,
+ Languages,
LayoutDashboard,
+ Megaphone,
Monitor,
Newspaper,
+ Package,
Radio,
+ Server,
Settings,
Shield,
ShoppingCart,
+ Sparkles,
Store,
+ Tags,
+ Terminal,
+ Ticket,
+ Trophy,
Users,
+ Volume2,
+ Vote,
+ Wifi,
Wrench,
} from "lucide-react";
@@ -43,6 +66,8 @@ export interface AdminNavItem {
icon: LucideIcon;
/** When set, sidebar item is active if pathname matches any prefix. */
matchPrefixes?: string[];
+ /** Prefixes that must NOT count as active (e.g. /admin/users vs multi-accounts). */
+ matchExcludePrefixes?: string[];
}
export interface AdminNavGroup {
@@ -51,65 +76,29 @@ export interface AdminNavGroup {
items: AdminNavItem[];
}
+/**
+ * Hubs with real sibling tabs only. Standalone features have no hub chrome
+ * (sidebar links go straight to the page).
+ */
export const ADMIN_HUBS: AdminHubDefinition[] = [
{
- id: "news-media",
- titleKey: "newsMedia",
- subtitleKey: "newsMediaSubtitle",
- icon: Newspaper,
- prefixes: [
- "/admin/articles",
- "/admin/photos",
- "/admin/banners",
- "/admin/ads",
- "/admin/media",
- ],
- tabs: [
- {
- href: "/admin/articles",
- labelKey: "articles",
- match: ["/admin/articles"],
- },
- { href: "/admin/photos", labelKey: "photos" },
- { href: "/admin/banners", labelKey: "banners" },
- { href: "/admin/ads", labelKey: "ads", match: ["/admin/ads"] },
- { href: "/admin/media", labelKey: "media" },
- ],
- },
- {
- id: "site-content",
- titleKey: "siteContent",
- subtitleKey: "siteContentSubtitle",
- icon: Compass,
- prefixes: [
- "/admin/navigation",
- "/admin/help-questions",
- "/admin/writeable-boxes",
- "/admin/tags",
- "/admin/prefixes",
- ],
- tabs: [
- { href: "/admin/navigation", labelKey: "navigator" },
- {
- href: "/admin/help-questions",
- labelKey: "help",
- match: ["/admin/help-questions"],
- },
- { href: "/admin/writeable-boxes", labelKey: "boxes" },
- { href: "/admin/tags", labelKey: "tags" },
- { href: "/admin/prefixes", labelKey: "prefixes" },
- ],
- },
- {
- id: "engagement",
- titleKey: "engagement",
- subtitleKey: "engagementSubtitle",
+ id: "events",
+ titleKey: "events",
+ subtitleKey: "eventsSubtitle",
icon: Calendar,
- prefixes: ["/admin/events", "/admin/polls", "/admin/tickets"],
+ prefixes: ["/admin/events"],
tabs: [
{ href: "/admin/events", labelKey: "events", match: ["/admin/events"] },
{ href: "/admin/events/types", labelKey: "eventTypes" },
- { href: "/admin/polls", labelKey: "polls", match: ["/admin/polls"] },
+ ],
+ },
+ {
+ id: "tickets",
+ titleKey: "tickets",
+ subtitleKey: "ticketsSubtitle",
+ icon: Ticket,
+ prefixes: ["/admin/tickets"],
+ tabs: [
{
href: "/admin/tickets",
labelKey: "tickets",
@@ -119,50 +108,19 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
],
},
{
- id: "users",
- titleKey: "users",
- subtitleKey: "usersSubtitle",
- icon: Users,
- prefixes: [
- "/admin/users",
- "/admin/online",
- "/admin/applications",
- "/admin/teams",
- ],
+ id: "staff-access",
+ titleKey: "staffAccess",
+ subtitleKey: "staffAccessSubtitle",
+ icon: KeyRound,
+ prefixes: ["/admin/teams", "/admin/permissions", "/admin/housekeeping"],
tabs: [
- {
- href: "/admin/users",
- labelKey: "directory",
- match: ["/admin/users"],
- },
- { href: "/admin/users/multi-accounts", labelKey: "multiAccounts" },
- { href: "/admin/online", labelKey: "online" },
- { href: "/admin/applications", labelKey: "applications" },
{ href: "/admin/teams", labelKey: "teams" },
- ],
- },
- {
- id: "access",
- titleKey: "access",
- subtitleKey: "accessSubtitle",
- icon: Shield,
- prefixes: [
- "/admin/bans",
- "/admin/ip",
- "/admin/vpn",
- "/admin/permissions",
- "/admin/wordfilter",
- ],
- tabs: [
- { href: "/admin/bans", labelKey: "bans" },
- { href: "/admin/ip", labelKey: "ip" },
- { href: "/admin/vpn", labelKey: "vpn" },
{
href: "/admin/permissions",
labelKey: "permissions",
match: ["/admin/permissions"],
},
- { href: "/admin/wordfilter", labelKey: "wordFilter" },
+ { href: "/admin/housekeeping", labelKey: "housekeeping" },
],
},
{
@@ -182,50 +140,6 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
{ href: "/admin/moderation/team", labelKey: "team" },
],
},
- {
- id: "catalog",
- titleKey: "catalog",
- subtitleKey: "catalogSubtitle",
- icon: Store,
- prefixes: [
- "/admin/catalog",
- "/admin/rare-values",
- "/admin/badges",
- "/admin/achievements",
- "/admin/sounds",
- ],
- tabs: [
- { href: "/admin/catalog", labelKey: "catalogTab", match: ["/admin/catalog"] },
- { href: "/admin/rare-values", labelKey: "rares" },
- { href: "/admin/badges", labelKey: "badges" },
- { href: "/admin/achievements", labelKey: "achievements" },
- { href: "/admin/sounds", labelKey: "sounds" },
- ],
- },
- {
- id: "shop",
- titleKey: "shop",
- subtitleKey: "shopSubtitle",
- icon: ShoppingCart,
- prefixes: [
- "/admin/shop",
- "/admin/transactions",
- "/admin/vouchers",
- "/admin/subscriptions",
- "/admin/calendar",
- ],
- tabs: [
- { href: "/admin/shop", labelKey: "shopTab" },
- { href: "/admin/transactions", labelKey: "transactions" },
- { href: "/admin/vouchers", labelKey: "vouchers" },
- { href: "/admin/subscriptions", labelKey: "subscriptions" },
- {
- href: "/admin/calendar",
- labelKey: "calendar",
- match: ["/admin/calendar"],
- },
- ],
- },
{
id: "radio",
titleKey: "radio",
@@ -245,7 +159,11 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
labelKey: "monitoring",
group: "primary",
},
- { href: "/admin/radio/settings", labelKey: "settingsTab", group: "primary" },
+ {
+ href: "/admin/radio/settings",
+ labelKey: "settingsTab",
+ group: "primary",
+ },
{ href: "/admin/radio/banners", labelKey: "banners", group: "tools" },
{ href: "/admin/radio/embed", labelKey: "embed", group: "tools" },
{ href: "/admin/radio/api-keys", labelKey: "apiKeys", group: "tools" },
@@ -255,68 +173,12 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
],
},
{
- id: "settings",
- titleKey: "settings",
- subtitleKey: "settingsSubtitle",
- icon: Cog,
- prefixes: [
- "/admin/settings",
- "/admin/theme",
- "/admin/maintenance",
- "/admin/favicon",
- "/admin/emulator",
- "/admin/email-templates",
- "/admin/housekeeping",
- ],
+ id: "analytics",
+ titleKey: "analytics",
+ subtitleKey: "analyticsSubtitle",
+ icon: Activity,
+ prefixes: ["/admin/analytics"],
tabs: [
- { href: "/admin/settings", labelKey: "cms" },
- { href: "/admin/theme", labelKey: "theme" },
- { href: "/admin/maintenance", labelKey: "maintenance" },
- { href: "/admin/favicon", labelKey: "favicon" },
- { href: "/admin/emulator", labelKey: "emulator" },
- { href: "/admin/email-templates", labelKey: "email" },
- { href: "/admin/housekeeping", labelKey: "housekeeping" },
- ],
- },
- {
- id: "tools",
- titleKey: "tools",
- subtitleKey: "toolsSubtitle",
- icon: Wrench,
- prefixes: [
- "/admin/commandocentrum",
- "/admin/rooms",
- "/admin/import",
- "/admin/translations",
- ],
- tabs: [
- { href: "/admin/commandocentrum", labelKey: "commando" },
- { href: "/admin/rooms", labelKey: "rooms", match: ["/admin/rooms"] },
- { href: "/admin/import", labelKey: "import", match: ["/admin/import"] },
- {
- href: "/admin/translations",
- labelKey: "translations",
- match: ["/admin/translations"],
- },
- ],
- },
- {
- id: "observability",
- titleKey: "observability",
- subtitleKey: "observabilitySubtitle",
- icon: Monitor,
- prefixes: [
- "/admin/logs",
- "/admin/analytics",
- "/admin/devops",
- "/admin/alerts",
- ],
- tabs: [
- { href: "/admin/logs", labelKey: "logs" },
- { href: "/admin/logs/audit", labelKey: "audit" },
- { href: "/admin/logs/chat", labelKey: "chat" },
- { href: "/admin/logs/commands", labelKey: "commands" },
- { href: "/admin/logs/trades", labelKey: "trades" },
{
href: "/admin/analytics",
labelKey: "analytics",
@@ -324,26 +186,38 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
},
{ href: "/admin/analytics/activity", labelKey: "activity" },
{ href: "/admin/analytics/economy", labelKey: "economy" },
+ ],
+ },
+ {
+ id: "devops",
+ titleKey: "devops",
+ subtitleKey: "devopsSubtitle",
+ icon: Server,
+ prefixes: ["/admin/devops"],
+ tabs: [
{ href: "/admin/devops", labelKey: "devops", match: ["/admin/devops"] },
{ href: "/admin/devops/errors", labelKey: "errors" },
- { href: "/admin/alerts", labelKey: "alerts" },
],
},
];
+/** Longest-prefix match so nested routes (e.g. /admin/logs/audit) win. */
export function findAdminHub(pathname: string): AdminHubDefinition | null {
- return (
- ADMIN_HUBS.find((hub) =>
- hub.prefixes.some((p) => pathname === p || pathname.startsWith(`${p}/`)),
- ) ?? null
- );
+ let best: AdminHubDefinition | null = null;
+ let bestLen = -1;
+ for (const hub of ADMIN_HUBS) {
+ for (const prefix of hub.prefixes) {
+ const hit = pathname === prefix || pathname.startsWith(`${prefix}/`);
+ if (hit && prefix.length > bestLen) {
+ best = hub;
+ bestLen = prefix.length;
+ }
+ }
+ }
+ return best;
}
-function hubPrefixes(id: string): string[] {
- return ADMIN_HUBS.find((h) => h.id === id)?.prefixes ?? [];
-}
-
-/** Condensed sidebar — one entry per hub (plus dashboard). */
+/** Feature-first sidebar: one entry per feature; hubs only for sibling tabs. */
export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
{
labelKey: "overview",
@@ -362,45 +236,100 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
items: [
{
href: "/admin/articles",
- labelKey: "hubNewsMedia",
+ labelKey: "articles",
icon: Newspaper,
- matchPrefixes: hubPrefixes("news-media"),
+ matchPrefixes: ["/admin/articles"],
+ },
+ { href: "/admin/photos", labelKey: "photos", icon: Image },
+ { href: "/admin/banners", labelKey: "banners", icon: Megaphone },
+ {
+ href: "/admin/ads",
+ labelKey: "advertisements",
+ icon: FileText,
+ matchPrefixes: ["/admin/ads"],
+ },
+ { href: "/admin/media", labelKey: "media", icon: Image },
+ { href: "/admin/navigation", labelKey: "navigator", icon: Compass },
+ {
+ href: "/admin/help-questions",
+ labelKey: "helpCenter",
+ icon: HelpCircle,
+ matchPrefixes: ["/admin/help-questions"],
},
{
- href: "/admin/navigation",
- labelKey: "hubSiteContent",
- icon: Compass,
- matchPrefixes: hubPrefixes("site-content"),
+ href: "/admin/writeable-boxes",
+ labelKey: "writeableBoxes",
+ icon: Package,
},
+ { href: "/admin/tags", labelKey: "tags", icon: Tags },
+ { href: "/admin/prefixes", labelKey: "prefixes", icon: Sparkles },
+ ],
+ },
+ {
+ labelKey: "community",
+ icon: Calendar,
+ items: [
{
href: "/admin/events",
- labelKey: "hubEngagement",
+ labelKey: "events",
icon: Calendar,
- matchPrefixes: hubPrefixes("engagement"),
+ matchPrefixes: ["/admin/events"],
+ },
+ {
+ href: "/admin/polls",
+ labelKey: "polls",
+ icon: Vote,
+ matchPrefixes: ["/admin/polls"],
+ },
+ {
+ href: "/admin/tickets",
+ labelKey: "tickets",
+ icon: Ticket,
+ matchPrefixes: ["/admin/tickets"],
},
],
},
{
labelKey: "usersAndAccess",
- icon: Shield,
+ icon: Users,
items: [
{
href: "/admin/users",
- labelKey: "hubUsers",
+ labelKey: "users",
icon: Users,
- matchPrefixes: hubPrefixes("users"),
+ matchPrefixes: ["/admin/users"],
+ matchExcludePrefixes: ["/admin/users/multi-accounts"],
},
{
- href: "/admin/bans",
- labelKey: "hubAccess",
- icon: Ban,
- matchPrefixes: hubPrefixes("access"),
+ href: "/admin/users/multi-accounts",
+ labelKey: "multiAccounts",
+ icon: Users,
},
+ { href: "/admin/online", labelKey: "onlineUsers", icon: Wifi },
+ {
+ href: "/admin/applications",
+ labelKey: "applications",
+ icon: ClipboardList,
+ },
+ {
+ href: "/admin/teams",
+ labelKey: "staffAccess",
+ icon: KeyRound,
+ matchPrefixes: [
+ "/admin/teams",
+ "/admin/permissions",
+ "/admin/housekeeping",
+ ],
+ },
+ { href: "/admin/bans", labelKey: "bans", icon: Ban },
+ { href: "/admin/ip", labelKey: "ipManagement", icon: Shield },
+ { href: "/admin/vpn", labelKey: "vpn", icon: Shield },
+ { href: "/admin/wordfilter", labelKey: "wordFilter", icon: Filter },
{
href: "/admin/moderation",
labelKey: "moderation",
icon: Gavel,
- matchPrefixes: hubPrefixes("moderation"),
+ matchPrefixes: ["/admin/moderation"],
},
],
},
@@ -410,15 +339,27 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
items: [
{
href: "/admin/catalog",
- labelKey: "hubCatalog",
+ labelKey: "catalog",
icon: Store,
- matchPrefixes: hubPrefixes("catalog"),
+ matchPrefixes: ["/admin/catalog"],
+ },
+ { href: "/admin/rare-values", labelKey: "rareValues", icon: Sparkles },
+ { href: "/admin/badges", labelKey: "badges", icon: BadgeCheck },
+ { href: "/admin/achievements", labelKey: "achievements", icon: Trophy },
+ { href: "/admin/sounds", labelKey: "sounds", icon: Volume2 },
+ { href: "/admin/shop", labelKey: "shop", icon: ShoppingCart },
+ { href: "/admin/transactions", labelKey: "transactions", icon: Activity },
+ { href: "/admin/vouchers", labelKey: "vouchers", icon: Ticket },
+ {
+ href: "/admin/subscriptions",
+ labelKey: "subscriptions",
+ icon: ClipboardList,
},
{
- href: "/admin/shop",
- labelKey: "hubShop",
- icon: ShoppingCart,
- matchPrefixes: hubPrefixes("shop"),
+ href: "/admin/calendar",
+ labelKey: "calendar",
+ icon: CalendarDays,
+ matchPrefixes: ["/admin/calendar"],
},
],
},
@@ -438,24 +379,80 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
labelKey: "system",
icon: Settings,
items: [
+ { href: "/admin/settings", labelKey: "settings", icon: Cog },
+ { href: "/admin/theme", labelKey: "theme", icon: Sparkles },
+ { href: "/admin/maintenance", labelKey: "maintenance", icon: Wrench },
+ { href: "/admin/favicon", labelKey: "favicon", icon: Image },
+ { href: "/admin/emulator", labelKey: "emulator", icon: Server },
{
- href: "/admin/settings",
- labelKey: "settings",
- icon: Cog,
- matchPrefixes: hubPrefixes("settings"),
- },
- {
- href: "/admin/commandocentrum",
- labelKey: "hubTools",
- icon: Wrench,
- matchPrefixes: hubPrefixes("tools"),
- },
- {
- href: "/admin/logs",
- labelKey: "hubObservability",
- icon: Monitor,
- matchPrefixes: hubPrefixes("observability"),
+ href: "/admin/email-templates",
+ labelKey: "emailTemplates",
+ icon: FileText,
},
],
},
+ {
+ labelKey: "tools",
+ icon: Wrench,
+ items: [
+ {
+ href: "/admin/commandocentrum",
+ labelKey: "commandocentrum",
+ icon: Terminal,
+ },
+ {
+ href: "/admin/rooms",
+ labelKey: "rooms",
+ icon: Store,
+ matchPrefixes: ["/admin/rooms"],
+ },
+ {
+ href: "/admin/import",
+ labelKey: "import",
+ icon: Import,
+ matchPrefixes: ["/admin/import"],
+ },
+ {
+ href: "/admin/translations",
+ labelKey: "translations",
+ icon: Languages,
+ matchPrefixes: ["/admin/translations"],
+ },
+ ],
+ },
+ {
+ labelKey: "monitoring",
+ icon: Monitor,
+ items: [
+ {
+ href: "/admin/logs",
+ labelKey: "logs",
+ icon: FileText,
+ matchPrefixes: ["/admin/logs"],
+ matchExcludePrefixes: [
+ "/admin/logs/audit",
+ "/admin/logs/chat",
+ "/admin/logs/commands",
+ "/admin/logs/trades",
+ ],
+ },
+ { href: "/admin/logs/audit", labelKey: "auditLog", icon: ClipboardList },
+ { href: "/admin/logs/chat", labelKey: "chatLog", icon: FileText },
+ { href: "/admin/logs/commands", labelKey: "commandLog", icon: Terminal },
+ { href: "/admin/logs/trades", labelKey: "tradeLog", icon: Activity },
+ {
+ href: "/admin/analytics",
+ labelKey: "analytics",
+ icon: Activity,
+ matchPrefixes: ["/admin/analytics"],
+ },
+ {
+ href: "/admin/devops",
+ labelKey: "devops",
+ icon: Server,
+ matchPrefixes: ["/admin/devops"],
+ },
+ { href: "/admin/alerts", labelKey: "alerts", icon: AlertTriangle },
+ ],
+ },
];
diff --git a/src/lib/theme-contrast.test.ts b/src/lib/theme-contrast.test.ts
index ed8e3ee6..8541de0e 100644
--- a/src/lib/theme-contrast.test.ts
+++ b/src/lib/theme-contrast.test.ts
@@ -131,7 +131,7 @@ describe("deriveAdminPalette", () => {
expect(admin.surface).not.toBe("#00ff00");
});
- it("in light mode mirrors readable public surfaces", () => {
+ it("in light mode softens harsh white public surfaces", () => {
const admin = deriveAdminPalette(
{
color_background: "#f8fafc",
@@ -150,8 +150,8 @@ describe("deriveAdminPalette", () => {
},
"light",
);
- expect(admin.canvas).toBe("#f8fafc");
- expect(admin.surface).toBe("#ffffff");
+ expect(admin.canvas).toBe("#eef1f4");
+ expect(admin.surface).toBe("#f5f7fa");
expect(contrastRatio(admin.text, admin.canvas)).toBeGreaterThanOrEqual(4.5);
expect(contrastRatio(admin.sidebarText, admin.sidebar)).toBeGreaterThanOrEqual(
4.5,
@@ -169,7 +169,7 @@ describe("deriveAdminPalette", () => {
{ canvas: "#0b0f19", text: "#f8fafc" },
"light",
);
- expect(admin.canvas).toBe("#ffffff");
+ expect(admin.canvas).toBe("#eef1f4");
expect(admin.text.toLowerCase()).not.toBe("#f8fafc");
});
diff --git a/src/lib/theme-contrast.ts b/src/lib/theme-contrast.ts
index 930afc3d..49b9412d 100644
--- a/src/lib/theme-contrast.ts
+++ b/src/lib/theme-contrast.ts
@@ -27,6 +27,19 @@ export interface AdminPalette {
focusRing: string;
}
+/** Pure / near-white public surfaces → soft admin paper (less eye strain). */
+function softLightSurface(
+ value: string | undefined,
+ softDefault: string,
+): string {
+ if (!value) return softDefault;
+ const v = value.trim().toLowerCase();
+ if (v === "#fff" || v === "#ffffff" || v === "white") return softDefault;
+ // Common slate/zinc paper whites (#f8fafc, #fafafa, #f9fafb, …)
+ if (/^#f[89a-f][0-9a-f]{4}$/i.test(v)) return softDefault;
+ return value;
+}
+
export function deriveAdminPalette(
colors: PublicThemeColors,
overrides?: Partial,
@@ -34,13 +47,14 @@ export function deriveAdminPalette(
): AdminPalette {
const accent = overrides?.accent ?? colors.color_primary ?? "#f59e0b";
+ // Soft cool gray-blue paper (not pure white) — easier on the eyes in light HK.
const lightDefaults: AdminPalette = {
- canvas: colors.color_background ?? "#f8fafc",
- surface: colors.color_surface ?? "#ffffff",
- surfaceElevated: colors.color_dropdown ?? "#f1f5f9",
+ canvas: softLightSurface(colors.color_background, "#eef1f4"),
+ surface: softLightSurface(colors.color_surface, "#f5f7fa"),
+ surfaceElevated: softLightSurface(colors.color_dropdown, "#fafbfc"),
text: colors.color_text ?? "#0f172a",
muted: colors.color_text_muted ?? "#64748b",
- border: "#e2e8f0",
+ border: "#d8dee6",
accent,
accentText: accent,
accentForeground: "#ffffff",
@@ -48,7 +62,10 @@ export function deriveAdminPalette(
warning: colors.color_warning ?? "#eab308",
error: colors.color_error ?? "#ef4444",
info: colors.color_info ?? "#0ea5e9",
- sidebar: colors.color_navbar ?? colors.color_surface ?? "#ffffff",
+ sidebar: softLightSurface(
+ colors.color_navbar ?? colors.color_surface,
+ "#e8ecf1",
+ ),
sidebarText: colors.color_navbar_text ?? colors.color_text ?? "#0f172a",
overlay: "rgba(15, 23, 42, 0.45)",
focusRing: accent,
diff --git a/src/messages/en.json b/src/messages/en.json
index a0ef1b7b..f1b1e353 100644
--- a/src/messages/en.json
+++ b/src/messages/en.json
@@ -781,7 +781,15 @@
"backToSite": "Back to site",
"housekeeping": "Housekeeping",
"hotelManagementPanel": "Hotel management panel",
- "rankLabel": "Rank {rank}"
+ "rankLabel": "Rank {rank}",
+ "community": "Community",
+ "monitoring": "Monitoring",
+ "staffAccess": "Ranks & permissions",
+ "multiAccounts": "Multi-accounts",
+ "chatLog": "Chat log",
+ "commandLog": "Command log",
+ "tradeLog": "Trade log",
+ "tools": "Tools"
},
"dashboard": {
"title": "Dashboard",
@@ -2153,7 +2161,17 @@
"errors": "Errors",
"alerts": "Alerts",
"toolsGroup": "Tools"
- }
+ },
+ "events": "Events",
+ "eventsSubtitle": "Hotel events and event types",
+ "tickets": "Tickets",
+ "ticketsSubtitle": "Support tickets and reply templates",
+ "staffAccess": "Ranks & permissions",
+ "staffAccessSubtitle": "Staff ranks, ACL permissions and housekeeping access",
+ "analytics": "Analytics",
+ "analyticsSubtitle": "Hotel activity, retention and economy",
+ "devops": "DevOps",
+ "devopsSubtitle": "Service health and emulator errors"
},
"wordfilter": {
"title": "Word Filter",
diff --git a/src/messages/it.json b/src/messages/it.json
index b47c234a..f86f0bbd 100644
--- a/src/messages/it.json
+++ b/src/messages/it.json
@@ -673,7 +673,7 @@
"content": "Contenuti",
"articles": "Articoli",
"tags": "Tag",
- "navigator": "Navigatore",
+ "navigator": "Navigazione",
"helpCenter": "Centro assistenza",
"advertisements": "Pubblicità",
"writeableBoxes": "Box modificabili",
@@ -737,15 +737,23 @@
"backToSite": "Torna al sito",
"hotelManagementPanel": "Pannello di gestione hotel",
"rankLabel": "Grado {rank}",
- "hubNewsMedia": "News & media",
- "hubSiteContent": "Site content",
- "hubEngagement": "Engagement",
+ "hubNewsMedia": "Notizie e media",
+ "hubSiteContent": "Contenuti sito",
+ "hubEngagement": "Community",
"hubUsers": "Users",
"hubAccess": "Access & security",
"hubCatalog": "Catalog & rares",
"hubShop": "Shop & payments",
"hubTools": "Tools",
- "hubObservability": "Observability"
+ "hubObservability": "Observability",
+ "community": "Community",
+ "monitoring": "Monitoraggio",
+ "staffAccess": "Gradi e permessi",
+ "multiAccounts": "Multi-account",
+ "chatLog": "Log chat",
+ "commandLog": "Log comandi",
+ "tradeLog": "Log trade",
+ "tools": "Strumenti"
},
"dashboard": {
"title": "Dashboard",
@@ -2396,7 +2404,17 @@
"errors": "Errori",
"alerts": "Alert",
"toolsGroup": "Strumenti"
- }
+ },
+ "events": "Eventi",
+ "eventsSubtitle": "Eventi hotel e tipi evento",
+ "tickets": "Ticket",
+ "ticketsSubtitle": "Ticket di supporto e template risposte",
+ "staffAccess": "Gradi e permessi",
+ "staffAccessSubtitle": "Gradi staff, permessi ACL e accesso housekeeping",
+ "analytics": "Analitiche",
+ "analyticsSubtitle": "Attività hotel, retention ed economia",
+ "devops": "DevOps",
+ "devopsSubtitle": "Salute servizi ed errori emulator"
},
"settings": {
"title": "Impostazioni CMS",
diff --git a/src/messages/nl.json b/src/messages/nl.json
index 1a728e75..72618fc3 100644
--- a/src/messages/nl.json
+++ b/src/messages/nl.json
@@ -712,7 +712,7 @@
"navigator": "Navigator",
"helpCenter": "Helpcentrum",
"advertisements": "Advertenties",
- "writeableBoxes": "Beschrijfbare boxes",
+ "writeableBoxes": "Bewerkbare boxes",
"photos": "Foto's",
"tickets": "Tickets",
"events": "Events",
@@ -781,7 +781,15 @@
"hubCatalog": "Catalog & rares",
"hubShop": "Shop & payments",
"hubTools": "Tools",
- "hubObservability": "Observability"
+ "hubObservability": "Observability",
+ "community": "Community",
+ "monitoring": "Monitoring",
+ "staffAccess": "Rangen & rechten",
+ "multiAccounts": "Multi-accounts",
+ "chatLog": "Chatlog",
+ "commandLog": "Commandolog",
+ "tradeLog": "Tradelog",
+ "tools": "Tools"
},
"dashboard": {
"title": "Dashboard",
@@ -2443,7 +2451,17 @@
"errors": "Fouten",
"alerts": "Meldingen",
"toolsGroup": "Tools"
- }
+ },
+ "events": "Evenementen",
+ "eventsSubtitle": "Hotel-evenementen en evenementtypes",
+ "tickets": "Tickets",
+ "ticketsSubtitle": "Supporttickets en antwoordtemplates",
+ "staffAccess": "Rangen & rechten",
+ "staffAccessSubtitle": "Staffrangen, ACL-rechten en housekeeping-toegang",
+ "analytics": "Analytics",
+ "analyticsSubtitle": "Hotelactiviteit, retentie en economie",
+ "devops": "DevOps",
+ "devopsSubtitle": "Servicegezondheid en emulatorfouten"
},
"tickets": {
"title": "Tickets",