Tenant Dashboard
Per-tenant view: usage metrics, plan status, active users, and quick actions. Typically shown to tenant admins after login.
Components used: Layout · Sidebar · Header · StatsCard · DashboardGrid · Card · Badge · ActivityFeed · Button
Code
- HTML · @primus/ui-core
- React
- Angular
<div style="display:flex;height:480px;border:1px solid var(--border);border-radius:var(--radius-medium);overflow:hidden">
<aside style="width:180px;background:var(--card);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0">
<div style="padding:0.875rem 1rem;border-bottom:1px solid var(--border);font-weight:700;font-size:0.875rem">Acme Corp</div>
<nav style="padding:0.5rem;flex:1">
<ul style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px">
<li><a href="#" style="display:block;padding:0.45rem 0.75rem;border-radius:5px;background:color-mix(in srgb,var(--primary) 10%,transparent);color:var(--primary);text-decoration:none;font-size:0.8rem;font-weight:600" aria-current="page">Overview</a></li>
<li><a href="#" style="display:block;padding:0.45rem 0.75rem;border-radius:5px;color:var(--foreground);text-decoration:none;font-size:0.8rem">Users</a></li>
<li><a href="#" style="display:block;padding:0.45rem 0.75rem;border-radius:5px;color:var(--foreground);text-decoration:none;font-size:0.8rem">Billing</a></li>
<li><a href="#" style="display:block;padding:0.45rem 0.75rem;border-radius:5px;color:var(--foreground);text-decoration:none;font-size:0.8rem">Settings</a></li>
</ul>
</nav>
<div style="padding:0.75rem;border-top:1px solid var(--border)">
<span class="badge accent" style="font-size:0.7rem">Enterprise</span>
</div>
</aside>
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden">
<header style="padding:0.75rem 1.25rem;border-bottom:1px solid var(--border);background:var(--card);display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
<div>
<div style="font-weight:700;font-size:0.875rem">Overview</div>
<div style="font-size:0.75rem;color:var(--muted-foreground)">Good morning, Jane</div>
</div>
<button class="small" style="font-size:0.8rem">+ Invite user</button>
</header>
<main style="flex:1;overflow-y:auto;padding:1.25rem;background:var(--background);display:flex;flex-direction:column;gap:1rem">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.75rem">
<div class="card stat">
<span class="card-label">Active Users</span>
<span class="card-value">47</span>
<span class="card-delta up">+3 this week</span>
</div>
<div class="card stat">
<span class="card-label">API Calls</span>
<span class="card-value">124k</span>
<span class="card-delta up">+18% vs last month</span>
</div>
<div class="card stat">
<span class="card-label">Storage Used</span>
<span class="card-value">8.2 GB</span>
<span class="card-delta down">82% of 10 GB</span>
</div>
</div>
<div class="card" style="padding:1rem">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:0.75rem">
<span style="font-size:0.8125rem;font-weight:600">Storage usage</span>
<span class="badge warning" style="font-size:0.7rem">82% used</span>
</div>
<progress value="82" max="100"></progress>
<div style="display:flex;justify-content:space-between;font-size:0.75rem;color:var(--muted-foreground);margin-top:0.5rem">
<span>8.2 GB used</span><span>10 GB limit</span>
</div>
</div>
<div class="card" style="padding:0">
<div style="padding:0.75rem 1rem;border-bottom:1px solid var(--border);font-size:0.8125rem;font-weight:600">Recent activity</div>
<div style="padding:0.875rem 1rem;display:flex;flex-direction:column;gap:0.625rem">
<div class="hstack"><div style="font-size:0.8rem">Alice J. signed in</div><span style="margin-inline-start:auto;font-size:0.75rem;color:var(--muted-foreground)">2 min ago</span></div>
<div class="hstack"><div style="font-size:0.8rem">Bob S. updated profile</div><span style="margin-inline-start:auto;font-size:0.75rem;color:var(--muted-foreground)">1 hr ago</span></div>
<div class="hstack"><div style="font-size:0.8rem">API key rotated</div><span style="margin-inline-start:auto;font-size:0.75rem;color:var(--muted-foreground)">Yesterday</span></div>
</div>
</div>
</main>
</div>
</div>
import {
PrimusLayout, PrimusSidebar, PrimusHeader,
DashboardGrid, PrimusStatCard,
Card, Badge, PrimusActivityFeed, PrimusButton,
} from 'primus-react-ui';
export function TenantDashboard() {
const { stats, activity } = useTenantDashboard();
const { user } = useCurrentUser();
const navItems = [
{ id: 'overview', label: 'Overview', href: '/dashboard' },
{ id: 'users', label: 'Users', href: '/dashboard/users' },
{ id: 'billing', label: 'Billing', href: '/dashboard/billing' },
{ id: 'settings', label: 'Settings', href: '/dashboard/settings' },
];
return (
<PrimusLayout
sidebar={<PrimusSidebar items={navItems} activeId="overview"
footer={<Badge variant="accent">{user?.tenant?.plan}</Badge>} />}
header={
<PrimusHeader
title="Overview"
subtitle={"Good morning, " + user?.firstName}
actions={
<PrimusButton onClick={() => navigate('/dashboard/users/invite')}>
+ Invite user
</PrimusButton>
}
/>
}
>
<DashboardGrid columns={3}>
<PrimusStatCard title="Active Users"
value={stats.users}
change={{ value: 3, type: 'increase' }} description="this week" />
<PrimusStatCard title="API Calls"
value={stats.apiCalls}
change={{ value: 18, type: 'increase' }} description="vs last month" />
<PrimusStatCard title="Storage Used"
value={stats.storage}
change={{ value: stats.storagePercent, type: 'neutral' }}
description={"of " + stats.storageLimit} />
</DashboardGrid>
<Card>
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:'0.75rem' }}>
<strong>Storage usage</strong>
<Badge variant={stats.storagePercent > 80 ? 'warning' : 'success'}>
{stats.storagePercent}% used
</Badge>
</div>
<progress value={stats.storagePercent} max={100} />
<div style={{ display:'flex', justifyContent:'space-between', fontSize:'0.75rem', color:'var(--muted-foreground)', marginTop:'0.5rem' }}>
<span>{stats.storage} used</span>
<span>{stats.storageLimit} limit</span>
</div>
</Card>
<PrimusActivityFeed items={activity} maxItems={10} showTimestamps />
</PrimusLayout>
);
}
<!-- tenant-dashboard.component.html -->
<primus-layout>
<primus-sidebar slot="sidebar" [items]="navItems" activeId="overview">
<div sidebar-footer>
<primus-badge variant="accent">{{ user?.tenant?.plan }}</primus-badge>
</div>
</primus-sidebar>
<primus-header slot="header" title="Overview"
[subtitle]="'Good morning, ' + user?.firstName">
<div header-actions>
<primus-button routerLink="/dashboard/users/invite">
+ Invite user
</primus-button>
</div>
</primus-header>
<primus-dashboard-grid [columns]="3">
<primus-stats-card label="Active Users" [value]="stats.users"
delta="+3" trend="up" description="this week">
</primus-stats-card>
<primus-stats-card label="API Calls" [value]="stats.apiCalls"
delta="+18%" trend="up" description="vs last month">
</primus-stats-card>
<primus-stats-card label="Storage Used" [value]="stats.storage">
</primus-stats-card>
</primus-dashboard-grid>
<primus-card style="padding:1rem">
<div class="hstack justify-between" style="margin-bottom:0.75rem">
<strong>Storage usage</strong>
<primus-badge [variant]="stats.storagePercent > 80 ? 'warning' : 'success'">
{{ stats.storagePercent }}% used
</primus-badge>
</div>
<progress [value]="stats.storagePercent" max="100"></progress>
</primus-card>
<primus-activity-feed [items]="activity" [maxItems]="10" [showTimestamps]="true">
</primus-activity-feed>
</primus-layout>