Improve admin UX: hub i18n, dynamic topbar, settings strings, staff logs DataTable.
Local Build and Deploy / deploy (push) Successful in 1m12s

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-17 20:14:55 +02:00
1 parent 604e63da51
commit 1039044e48
11 files changed
+526 -305

No files matched your search

+12 -3
View File
@@ -1,6 +1,7 @@
"use client";
import { usePathname } from "next/navigation";
import { useTranslations } from "next-intl";
import type { ReactNode } from "react";
import { AdminPageShell } from "@/components/admin/admin-page-shell";
import { AdminSectionTabs } from "@/components/admin/admin-section-tabs";
@@ -13,15 +14,23 @@ import { findAdminHub } from "@/lib/admin-nav";
export function AdminHubChrome({ children }: { children: ReactNode }) {
const pathname = usePathname() ?? "";
const hub = findAdminHub(pathname);
const t = useTranslations("pages.admin.hubs");
if (!hub) return <>{children}</>;
const tabs = hub.tabs.map((tab) => ({
href: tab.href,
label: t(`tabs.${tab.labelKey}`),
match: tab.match,
group: tab.group,
}));
return (
<AdminPageShell
icon={hub.icon}
title={hub.title}
subtitle={hub.subtitle}
tabs={<AdminSectionTabs tabs={hub.tabs} />}
title={t(hub.titleKey)}
subtitle={t(hub.subtitleKey)}
tabs={<AdminSectionTabs tabs={tabs} />}
className="admin-hub-panel"
>
{children}
+7 -10
View File
@@ -2,6 +2,7 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useTranslations } from "next-intl";
import { cn } from "@/lib/utils";
export interface AdminTab {
@@ -13,11 +14,6 @@ export interface AdminTab {
group?: string;
}
const GROUP_LABELS: Record<string, string> = {
primary: "",
tools: "Tools",
};
function matchLength(pathname: string, prefix: string): number {
if (pathname === prefix) return prefix.length + 1; // prefer exact
if (pathname.startsWith(`${prefix}/`)) return prefix.length;
@@ -71,6 +67,7 @@ function TabRow({
export function AdminSectionTabs({ tabs }: { tabs: AdminTab[] }) {
const pathname = usePathname() ?? "";
const t = useTranslations("pages.admin.hubs");
let bestIndex = -1;
let bestScore = -1;
@@ -87,9 +84,7 @@ export function AdminSectionTabs({ tabs }: { tabs: AdminTab[] }) {
const hasGroups = tabs.some((t) => t.group);
if (!hasGroups) {
return (
<TabRow tabs={tabs} bestIndex={bestIndex} globalOffset={0} />
);
return <TabRow tabs={tabs} bestIndex={bestIndex} globalOffset={0} />;
}
const order: string[] = [];
@@ -102,16 +97,18 @@ export function AdminSectionTabs({ tabs }: { tabs: AdminTab[] }) {
return (
<div className="space-y-3">
{order.map((groupId) => {
const groupTabs = tabs.filter((t) => (t.group ?? "primary") === groupId);
const groupTabs = tabs.filter((tab) => (tab.group ?? "primary") === groupId);
const rowOffset = offset;
offset += groupTabs.length;
const label =
groupId === "tools" ? t("tabs.toolsGroup") : undefined;
return (
<TabRow
key={groupId}
tabs={groupTabs}
bestIndex={bestIndex}
globalOffset={rowOffset}
label={GROUP_LABELS[groupId] || undefined}
label={label}
/>
);
})}
+45
View File
@@ -0,0 +1,45 @@
"use client";
import { usePathname } from "next/navigation";
import { useTranslations } from "next-intl";
import { LanguageSwitcher } from "@/components/language-switcher";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { findAdminHub } from "@/lib/admin-nav";
export function AdminTopbar({
staff,
}: {
staff: { id: number; username: string; rank: number };
}) {
const tNav = useTranslations("pages.admin.nav");
const tHubs = useTranslations("pages.admin.hubs");
const pathname = usePathname() ?? "";
const hub = findAdminHub(pathname);
const title = hub ? tHubs(hub.titleKey) : tNav("housekeeping");
return (
<header className="flex items-center justify-between gap-4 bg-[var(--admin-surface)] border border-[var(--admin-border)] rounded-2xl px-5 py-3 mb-5 shadow-card">
<div className="flex items-center gap-3">
<h2 className="m-0 text-base font-extrabold text-[var(--admin-text)]">
{title}
</h2>
<span className="hidden sm:inline-block w-px h-5 bg-[var(--admin-border)]" />
<span className="hidden sm:inline text-xs text-[var(--admin-text-muted)]">
{tNav("hotelManagementPanel")}
</span>
</div>
<div className="flex items-center gap-3">
<div className="flex items-center gap-1.5 lg:hidden">
<LanguageSwitcher variant="admin" />
<ThemeSwitcher variant="admin" />
</div>
<span className="text-xs text-[var(--admin-text-muted)]">
<strong className="text-[var(--admin-text)]">{staff.username}</strong>
</span>
<span className="inline-flex items-center gap-1.5 text-[0.6rem] font-bold uppercase tracking-wider text-[var(--admin-accent-foreground)] bg-[var(--admin-accent)] rounded-full px-2.5 py-1">
{tNav("rankLabel", { rank: staff.rank })}
</span>
</div>
</header>
);
}