Merge pull request 'feat(admin): feature-first nav + soft light mode' (#11) from feat/admin-feature-nav into main
Local Build and Deploy / deploy (push) Successful in 1m7s
Local Build and Deploy / deploy (push) Successful in 1m7s
Reviewed-on: #11
This commit is contained in:
commit
4628511bae
9 files changed
+344
-267
No files matched your search
@@ -116,6 +116,7 @@ async function Sidebar({
|
||||
key={`${group.labelKey}-${item.href}`}
|
||||
href={item.href}
|
||||
matchPrefixes={item.matchPrefixes}
|
||||
matchExcludePrefixes={item.matchExcludePrefixes}
|
||||
icon={<ItemIcon size={16} />}
|
||||
>
|
||||
{t(item.labelKey)}
|
||||
|
||||
@@ -30,7 +30,7 @@ export function AdminHubChrome({ children }: { children: ReactNode }) {
|
||||
icon={hub.icon}
|
||||
title={t(hub.titleKey)}
|
||||
subtitle={t(hub.subtitleKey)}
|
||||
tabs={<AdminSectionTabs tabs={tabs} />}
|
||||
tabs={tabs.length > 1 ? <AdminSectionTabs tabs={tabs} /> : undefined}
|
||||
className="admin-hub-panel"
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -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(
|
||||
|
||||
+242
-245
File diff suppressed because it is too large.
Load diff
@@ -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");
|
||||
});
|
||||
|
||||
|
||||
@@ -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<AdminPalette>,
|
||||
@@ -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,
|
||||
|
||||
+20
-2
@@ -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",
|
||||
|
||||
+24
-6
@@ -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",
|
||||
|
||||
+21
-3
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user