Files
claudemesh/apps/web/src/app/[locale]/admin/users/[id]/page.tsx
Alejandro Gutiérrez d3163a5bff feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:19:32 +01:00

91 lines
2.6 KiB
TypeScript

import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { notFound } from "next/navigation";
import { getTranslation } from "@turbostarter/i18n/server";
import { getUser } from "~/lib/auth/server";
import { getMetadata } from "~/lib/metadata";
import { getQueryClient } from "~/lib/query/server";
import { admin } from "~/modules/admin/lib/api";
import { AccountsDataTable } from "~/modules/admin/users/user/accounts/data-table/accounts-data-table";
import { UserDetails } from "~/modules/admin/users/user/details";
import { UserHeader } from "~/modules/admin/users/user/header";
import { InvitationsDataTable } from "~/modules/admin/users/user/invitations/data-table/invitations-data-table";
import { MembershipsDataTable } from "~/modules/admin/users/user/memberships/data-table/memberships-data-table";
import { PlansDataTable } from "~/modules/admin/users/user/plans/data-table/plans-data-table";
import { SessionsList } from "~/modules/admin/users/user/sessions/sessions-list";
export const generateMetadata = async ({
params,
}: {
params: Promise<{ id: string; locale: string }>;
}) => {
const id = (await params).id;
const user = await getUser({ id });
if (!user) {
return notFound();
}
return getMetadata({
title: user.name,
})({ params });
};
export default async function UserPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { t } = await getTranslation({ ns: "common" });
const { id } = await params;
const user = await getUser({ id });
if (!user) {
return notFound();
}
const queryClient = getQueryClient();
await queryClient.setQueryData(
admin.queries.users.get({ id }).queryKey,
user,
);
const sections = [
{
label: t("accounts"),
component: <AccountsDataTable id={id} />,
},
{
label: t("plans"),
component: <PlansDataTable userId={id} />,
},
{
label: t("memberships"),
component: <MembershipsDataTable userId={id} />,
},
{
label: t("invitations"),
component: <InvitationsDataTable userId={id} />,
},
];
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<UserHeader id={id} />
<UserDetails id={id} />
<div className="mt-4 flex w-full flex-col gap-10">
{sections.map(({ label, component }) => (
<section key={label} className="flex w-full flex-col gap-4">
<header>
<h3 className="text-xl font-semibold tracking-tight">{label}</h3>
</header>
{component}
</section>
))}
<SessionsList id={id} />
</div>
</HydrationBoundary>
);
}