Skip to content

Admin Dashboard

@cruzjs/pro includes an admin module for system-wide management. The admin dashboard provides user management, organization oversight, and system metrics through the AdminDashboardService, AdminUserService, and AdminOrgService.

The AdminDashboardService aggregates system-wide statistics:

import { AdminDashboardService } from '@cruzjs/pro/admin/dashboard.service';
const metrics = await dashboardService.getMetrics();
// {
// users: { total, active, verified },
// subscriptions: { total, byPlan, active, trialing, canceled },
// revenue: { mrr, arr },
// jobs: { pending, processing, failed, completed },
// organizations: { total, withSubscription },
// }
MetricDescription
users.totalTotal registered users
users.verifiedUsers with verified email
subscriptions.activeCurrently active subscriptions
subscriptions.byPlanSubscription count per Stripe price ID
jobs.pendingBackground jobs waiting to be processed
jobs.failedJobs that have failed all retry attempts
organizations.totalTotal organizations created
organizations.withSubscriptionOrganizations with active subscriptions

The admin tRPC router provides paginated access to users and organizations:

import { adminRouter } from '@cruzjs/pro/admin/admin.router';
// Dashboard stats
const stats = await trpc.admin.dashboard.query();
// { users: { total }, organizations: { total } }
// List users with pagination
const users = await trpc.admin.users.query({ page: 1, limit: 20 });
// {
// users: [{ id, email, name, emailVerified, createdAt }],
// pagination: { page, limit, total, totalPages }
// }
// Get a specific user
const user = await trpc.admin.getUser.query({ id: userId });
// List organizations with pagination
const orgs = await trpc.admin.orgs.query({ page: 1, limit: 20 });
// {
// organizations: [{ id, name, slug, createdAt }],
// pagination: { page, limit, total, totalPages }
// }
// Get a specific organization
const org = await trpc.admin.getOrg.query({ id: orgId });

The AdminUserService provides user administration:

import { AdminUserService } from '@cruzjs/pro/admin/user.service';
// List users with filters and pagination
const result = await adminUserService.listUsers(
{ search: 'john' }, // Filters
{ page: 1, pageSize: 20 } // Pagination
);
// Get user details
const user = await adminUserService.getUser(userId);

The AdminOrgService provides org-level administration:

import { AdminOrgService } from '@cruzjs/pro/admin/org.service';
// List organizations with pagination
const result = await adminOrgService.listOrgs(
{}, // Filters
{ page: 1, pageSize: 20 } // Pagination
);
// Get org details
const org = await adminOrgService.getOrg(orgId);

Protect admin routes by checking the user’s system role in the loader:

app/routes/admin/dashboard.tsx
export async function loader({ request }: LoaderFunctionArgs) {
const auth = await requireSession(request);
// Check if user is a system admin
// You can implement this check based on:
// 1. A flag on the authIdentity table
// 2. A separate admin roles table
// 3. An environment variable allowlist
const container = await getAppContainer();
const adminUserService = container.get(AdminUserService);
// Example: check against an admin email allowlist
const adminEmails = process.env.ADMIN_EMAILS?.split(',') ?? [];
if (!adminEmails.includes(auth.user.email)) {
throw new Response('Forbidden', { status: 403 });
}
const dashboardService = container.get(AdminDashboardService);
return { metrics: await dashboardService.getMetrics() };
}

Create an admin layout that wraps all admin routes:

app/routes/admin/layout.tsx
export async function loader({ request }: LoaderFunctionArgs) {
const auth = await requireSession(request);
await requireAdmin(auth); // Your admin check
return { user: auth.user };
}
export default function AdminLayout() {
return (
<div className="admin-layout">
<nav>
<a href="/admin">Dashboard</a>
<a href="/admin/users">Users</a>
<a href="/admin/orgs">Organizations</a>
</nav>
<main>
<Outlet />
</main>
</div>
);
}

The admin router is automatically registered as part of the AdminModule in the framework’s core providers:

// This is handled internally by the framework
@Module({
providers: [AdminDashboardService, AdminUserService, AdminOrgService, RoleService],
trpcRouters: {
admin: adminRouter,
},
})
export class AdminModule {}

Access admin endpoints via trpc.admin.* in your React components:

function AdminDashboard() {
const { data } = trpc.admin.dashboard.useQuery();
return (
<div>
<h1>Admin Dashboard</h1>
<div className="stats-grid">
<StatCard title="Total Users" value={data?.users.total} />
<StatCard title="Total Organizations" value={data?.organizations.total} />
</div>
</div>
);
}