@forjio/admin-ui
v0.4.0
Published
Shared admin portal for the Forjio family of SaaS products — the four mandatory areas (dashboard, business metrics, system metrics, feature flags) plus the primitives they are built from. Mirrors @forjio/portal-ui but for the internal staff side.
Maintainers
Readme
@forjio/admin-ui
Shared admin portal for the Forjio family. Mirrors @forjio/portal-ui, but for the internal staff side.
Every Forjio product's admin portal must ship four areas. A product may add anything on top — pawpado's reconcile, plugipay's kyc-inbox, serront's orders — but it may not ship without these:
| Area | Route | Panel |
|---|---|---|
| Dashboard | /admin/dashboard | AdminOverviewPanel |
| Business metrics | /admin/metrics | BusinessMetricsPanel |
| System metrics | /admin/system | SystemHealthPanel |
| Feature flags | /admin/feature-flags | FeatureFlagsPanel |
Spec: forjio/documentation/2. Technical/13-Admin-Portal-Standard.md.
Install
npm i @forjio/admin-uiPeer deps: react, react-dom, next, lucide-react. Styling is Tailwind + CSS variables (--border, --card, --primary), so each product keeps its own brand accent with no prop.
Use
// app/(admin)/admin/(portal)/system/page.tsx
'use client';
import { SystemHealthPanel } from '@forjio/admin-ui';
export default function Page() {
return <SystemHealthPanel />;
}// app/(admin)/admin/(portal)/dashboard/page.tsx
'use client';
import { AdminOverviewPanel } from '@forjio/admin-ui';
export default function Page() {
return (
<AdminOverviewPanel
brand="Serront"
quickLinks={[
{ href: '/admin/orders', label: 'Orders', description: 'Cross-workspace order pipeline.' },
]}
/>
);
}What a product actually implements
The panels are done. Each product implements only the adapter behind three endpoints, mounted behind adminGuard and reached through the /api/v1/console/* proxy so no admin secret touches the browser:
| Endpoint | Returns |
|---|---|
| /api/v1/admin/metrics | BusinessMetrics |
| /api/v1/admin/system-health | SystemHealth |
| /api/v1/admin/feature-flags | FeatureFlag[] (+ PATCH /:key) |
Products differ enormously underneath — Prisma model counts run 12 to 56, pawpado has no Prisma at all, catentio is a Python control plane — which is exactly why the variation lives in the adapter and never in the pages.
Two rules the shapes enforce
- User counts are split, never summed.
signedIn(SSO roster) andworkspaceMembers(membership) overlap and are reported side by side. Merging them hides the thing operators look for: prospects who signed up and never came back. skippedis notok. An unconfigured integration reportsskipped. "We never checked" and "it is healthy" must not look the same on an operator's screen. Likewisequeues: null(no queues) is notqueues: [](queues, all drained).
Money crosses the wire in minor units (grossMinor) and is divided exactly once, in fmtMoneyMinor.
