Files
claudemesh/apps/web/src/app/[locale]/admin/layout.tsx
Alejandro Gutiérrez 9dd5face01
Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled
feat(web): admin backoffice — meshes, sessions, invites, audit, overview
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>
2026-04-04 22:47:47 +01:00

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>
);
}