Analytics Dashboard
KPI metrics, line/bar charts, date range filter, and export controls.
Components used: Layout · Sidebar · Header · StatsCard · DashboardGrid · Charts · FilterBar · DatePicker · ExportMenu · Card
Code
- HTML · @primus/ui-core
- React
- Angular
<div style="display:flex;flex-direction:column;height:460px;background:var(--background);border:1px solid var(--border);border-radius:var(--radius-medium);overflow:hidden">
<div style="padding:0.875rem 1.5rem;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.9rem">Analytics</div>
</div>
<div class="hstack">
<input type="date" value="2026-02-01" style="max-width:140px" />
<span style="font-size:0.8rem;color:var(--muted-foreground)">to</span>
<input type="date" value="2026-03-03" style="max-width:140px" />
<select style="max-width:120px"><option>All tenants</option><option>Acme Corp</option><option>Globex</option></select>
<button class="outline small">Export</button>
</div>
</div>
<div style="flex:1;overflow-y:auto;padding:1.25rem;display:flex;flex-direction:column;gap:1rem">
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.75rem">
<div class="card stat">
<span class="card-label">Total Signups</span>
<span class="card-value">1,247</span>
<span class="card-delta up">+22%</span>
</div>
<div class="card stat">
<span class="card-label">Active Tenants</span>
<span class="card-value">142</span>
<span class="card-delta up">+8</span>
</div>
<div class="card stat">
<span class="card-label">Avg Session</span>
<span class="card-value">14.2 min</span>
<span class="card-delta up">+3 min</span>
</div>
<div class="card stat">
<span class="card-label">Conversion</span>
<span class="card-value">34.8%</span>
<span class="card-delta down">-2.1%</span>
</div>
</div>
<div class="card" style="padding:1.25rem;flex:1">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem">
<span style="font-size:0.8125rem;font-weight:600">Signups over time</span>
<div class="hstack">
<button class="ghost small" style="font-size:0.75rem;background:color-mix(in srgb,var(--primary) 10%,transparent);color:var(--primary)">Day</button>
<button class="ghost small" style="font-size:0.75rem">Week</button>
<button class="ghost small" style="font-size:0.75rem">Month</button>
</div>
</div>
<div style="display:flex;align-items:flex-end;gap:4px;height:80px">
<div style="flex:1;background:var(--primary);opacity:0.35;border-radius:3px 3px 0 0;height:30%"></div>
<div style="flex:1;background:var(--primary);opacity:0.45;border-radius:3px 3px 0 0;height:45%"></div>
<div style="flex:1;background:var(--primary);opacity:0.55;border-radius:3px 3px 0 0;height:60%"></div>
<div style="flex:1;background:var(--primary);opacity:0.65;border-radius:3px 3px 0 0;height:55%"></div>
<div style="flex:1;background:var(--primary);opacity:0.75;border-radius:3px 3px 0 0;height:72%"></div>
<div style="flex:1;background:var(--primary);opacity:0.85;border-radius:3px 3px 0 0;height:68%"></div>
<div style="flex:1;background:var(--primary);border-radius:3px 3px 0 0;height:100%"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:0.7rem;color:var(--muted-foreground);margin-top:4px">
<span>Feb 1</span><span>Feb 8</span><span>Feb 15</span><span>Feb 22</span><span>Mar 1</span>
</div>
</div>
</div>
</div>
import {
PrimusLayout, PrimusSidebar, PrimusHeader,
DashboardGrid, PrimusStatCard,
PrimusLineChart, PrimusBarChart,
PrimusFilterBar, PrimusExportMenu, Card,
} from 'primus-react-ui';
export function AnalyticsDashboard() {
const [dateRange, setDateRange] = useState({ from: '2026-02-01', to: '2026-03-03' });
const [filters, setFilters] = useState({});
const { stats, chartData, loading } = useAnalytics({ dateRange, filters });
return (
<PrimusLayout
sidebar={<PrimusSidebar items={NAV_ITEMS} activeId="analytics" />}
header={
<PrimusHeader title="Analytics"
actions={
<div style={{ display:'flex', gap:'0.5rem', alignItems:'center' }}>
<input type="date" value={dateRange.from}
onChange={e => setDateRange(r => ({ ...r, from: e.target.value }))} />
<span>to</span>
<input type="date" value={dateRange.to}
onChange={e => setDateRange(r => ({ ...r, to: e.target.value }))} />
<PrimusExportMenu formats={['CSV','PDF']} />
</div>
}
/>
}
>
<PrimusFilterBar
filters={[{ key:'tenant', label:'Tenant', type:'select', options:tenantList }]}
activeFilters={filters} onChange={setFilters} />
<DashboardGrid columns={4}>
<PrimusStatCard title="Total Signups" value={stats.signups}
change={{ value: 22, type: 'increase' }} />
<PrimusStatCard title="Active Tenants" value={stats.activeTenants}
change={{ value: 8, type: 'increase' }} />
<PrimusStatCard title="Avg Session" value={stats.avgSession}
change={{ value: 3, type: 'increase' }} />
<PrimusStatCard title="Conversion" value={stats.conversion}
change={{ value: 2, type: 'decrease' }} />
</DashboardGrid>
<Card>
<PrimusLineChart
labels={chartData.labels}
series={[{ label: 'Signups', data: chartData.signups }]}
height={280}
/>
</Card>
<Card>
<PrimusBarChart
labels={chartData.labels}
series={[
{ label: 'New tenants', data: chartData.newTenants },
{ label: 'Churned', data: chartData.churned },
]}
height={240}
/>
</Card>
</PrimusLayout>
);
}
<!-- analytics-dashboard.component.html -->
<primus-layout>
<primus-sidebar slot="sidebar" [items]="navItems" activeId="analytics">
</primus-sidebar>
<primus-header slot="header" title="Analytics">
<div header-actions class="hstack">
<input type="date" [(ngModel)]="dateFrom" />
<span>to</span>
<input type="date" [(ngModel)]="dateTo" />
<primus-export-menu [formats]="['CSV','PDF']"
(exportTriggered)="onExport($event)">
</primus-export-menu>
</div>
</primus-header>
<primus-filter-bar [filters]="filterConfig" [activeFilters]="activeFilters"
(filtersChange)="onFilter($event)">
</primus-filter-bar>
<primus-dashboard-grid [columns]="4">
<primus-stats-card label="Total Signups" [value]="stats.signups" delta="+22%" trend="up"></primus-stats-card>
<primus-stats-card label="Active Tenants" [value]="stats.activeTenants" delta="+8" trend="up"></primus-stats-card>
<primus-stats-card label="Avg Session" [value]="stats.avgSession" delta="+3min" trend="up"></primus-stats-card>
<primus-stats-card label="Conversion" [value]="stats.conversion" delta="-2.1%" trend="down"></primus-stats-card>
</primus-dashboard-grid>
<primus-card style="padding:1.25rem;margin-bottom:1rem">
<primus-line-chart [labels]="chartLabels" [series]="signupSeries" [height]="280">
</primus-line-chart>
</primus-card>
<primus-card style="padding:1.25rem">
<primus-bar-chart [labels]="chartLabels" [series]="tenantSeries" [height]="240">
</primus-bar-chart>
</primus-card>
</primus-layout>