import { notFound } from "next/navigation";
import { getMeshResponseSchema } from "@turbostarter/api/schema";
import { handle } from "@turbostarter/api/utils";
import { Badge } from "@turbostarter/ui-web/badge";
import { api } from "~/lib/api/server";
import { getMetadata } from "~/lib/metadata";
import {
DashboardHeader,
DashboardHeaderDescription,
DashboardHeaderTitle,
} from "~/modules/common/layout/dashboard/header";
export const generateMetadata = getMetadata({
title: "Mesh detail · Admin",
description: "Members, presences, invites, audit events for a mesh.",
});
export default async function MeshDetailPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const data = await handle(api.admin.meshes[":id"].$get, {
schema: getMeshResponseSchema,
})({ param: { id } }).catch(() => null);
if (!data || !data.mesh) notFound();
const { mesh, members, presences, invites, auditEvents } = data;
return (
<>
{mesh.name}
{mesh.slug}
Owner: {mesh.ownerName ?? "—"} · {mesh.ownerEmail ?? "—"} · tier{" "}
{mesh.tier} · transport {mesh.transport} · visibility{" "}
{mesh.visibility}
| Display name |
Role |
Pubkey |
Joined |
Last seen |
Status |
{members.map((m) => (
| {m.displayName} |
{m.role}
|
{m.peerPubkey.slice(0, 12)}…
|
{new Date(m.joinedAt).toLocaleDateString()}
|
{m.lastSeenAt
? new Date(m.lastSeenAt).toLocaleString()
: "—"}
|
{m.revokedAt ? (
revoked
) : (
active
)}
|
))}
| Peer |
Status |
PID |
CWD |
Last ping |
{presences.map((p) => (
|
{p.displayName ?? "—"}
|
{p.disconnectedAt ? "disconnected" : p.status}
|
{p.pid}
|
{p.cwd}
|
{new Date(p.lastPingAt).toLocaleTimeString()}
|
))}
| Token |
Role |
Uses |
Expires |
Status |
{invites.map((inv) => (
|
{inv.token.slice(0, 12)}…
|
{inv.role}
|
{inv.usedCount} / {inv.maxUses}
|
{new Date(inv.expiresAt).toLocaleDateString()}
|
{inv.revokedAt ? (
revoked
) : new Date(inv.expiresAt) < new Date() ? (
expired
) : (
active
)}
|
))}
| When |
Event |
Actor |
Target |
{auditEvents.map((e) => (
|
{new Date(e.createdAt).toLocaleString()}
|
{e.eventType}
|
{e.actorPeerId?.slice(0, 12) ?? "—"}
|
{e.targetPeerId?.slice(0, 12) ?? "—"}
|
))}
>
);
}
function Section({
title,
count,
empty,
children,
}: {
title: string;
count: number;
empty: string;
children: React.ReactNode;
}) {
return (
{count === 0 ? (
{empty}
) : (
{children}
)}
);
}