Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled
Four new admin routes backed by the mesh API modules: - /admin/meshes — paginated data-table (name, owner, tier, transport, members, created). Tier + transport multiSelect filters. - /admin/meshes/[id] — detail page: owner row + 4 live sections (members, presences, invites, last 50 audit events). - /admin/sessions — live Claude Code WS presences. Status filter, pulse dot for working sessions, disconnected badge. - /admin/invites — invite tokens w/ status derived client-side (active/revoked/expired/exhausted). - /admin/audit — metadata-only event log, event-type + mesh + date filters. Overview page at /admin rewritten to 6 summary cards (users, orgs, customers, meshes, sessions, messages 24h) joining the base /admin/summary and /admin/summary/mesh endpoints. Sidebar navigation gains a second "mesh" group with the four new entries. paths.ts extended with admin.meshes / sessions / invites / audit. All UI reuses @turbostarter/ui-web/data-table — columns.tsx + thin *-data-table.tsx wrapper per the existing users pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
87 lines
1.9 KiB
TypeScript
87 lines
1.9 KiB
TypeScript
import { redirect } from "next/navigation";
|
|
|
|
import { hasAdminPermission } from "@turbostarter/auth";
|
|
import { Icons } from "@turbostarter/ui-web/icons";
|
|
import { SidebarProvider } from "@turbostarter/ui-web/sidebar";
|
|
|
|
import { pathsConfig } from "~/config/paths";
|
|
import { getSession } from "~/lib/auth/server";
|
|
import { AdminSidebar } from "~/modules/admin/layout/sidebar";
|
|
import { DashboardInset } from "~/modules/common/layout/dashboard/inset";
|
|
|
|
const menu = [
|
|
{
|
|
label: "admin",
|
|
items: [
|
|
{
|
|
title: "home",
|
|
href: pathsConfig.admin.index,
|
|
icon: Icons.Home,
|
|
},
|
|
{
|
|
title: "users",
|
|
href: pathsConfig.admin.users.index,
|
|
icon: Icons.UsersRound,
|
|
},
|
|
{
|
|
title: "organizations",
|
|
href: pathsConfig.admin.organizations.index,
|
|
icon: Icons.Building,
|
|
},
|
|
{
|
|
title: "customers",
|
|
href: pathsConfig.admin.customers.index,
|
|
icon: Icons.HandCoins,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "mesh",
|
|
items: [
|
|
{
|
|
title: "meshes",
|
|
href: pathsConfig.admin.meshes.index,
|
|
icon: Icons.Share,
|
|
},
|
|
{
|
|
title: "sessions",
|
|
href: pathsConfig.admin.sessions.index,
|
|
icon: Icons.Activity,
|
|
},
|
|
{
|
|
title: "invites",
|
|
href: pathsConfig.admin.invites.index,
|
|
icon: Icons.Link,
|
|
},
|
|
{
|
|
title: "audit",
|
|
href: pathsConfig.admin.audit.index,
|
|
icon: Icons.ScrollText,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
export default async function AdminLayout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const { user } = await getSession();
|
|
|
|
if (!user) {
|
|
return redirect(pathsConfig.auth.login);
|
|
}
|
|
|
|
if (!hasAdminPermission(user)) {
|
|
return redirect(pathsConfig.dashboard.user.index);
|
|
}
|
|
|
|
return (
|
|
<SidebarProvider>
|
|
<AdminSidebar user={user} menu={menu} />
|
|
<DashboardInset>{children}</DashboardInset>
|
|
</SidebarProvider>
|
|
);
|
|
}
|