diff --git a/src/components/ui/Sparkline.jsx b/src/components/ui/Sparkline.jsx new file mode 100644 index 000000000..6dea651f6 --- /dev/null +++ b/src/components/ui/Sparkline.jsx @@ -0,0 +1,49 @@ +/** + * Sparkline — tiny inline SVG trend line for dashboard cards. + * + * This is a charting primitive, not an icon, so the inline here is the + * sanctioned exception to the MoonIcons-only rule. The default stroke is the + * mm-design blue token; pass `color` (a token value) to override. + * + * data: array of { day: 'YYYY-MM-DD', count: number }. Missing days render as 0. + */ +import { colors } from '../../design/tokens' + +export default function Sparkline({ data, color = colors.blue, days = 30 }) { + if (!data || data.length === 0) { + return
No data
+ } + + // Fill in all days (missing days = 0) + const filled = [] + const today = new Date() + for (let i = days - 1; i >= 0; i--) { + const d = new Date(today) + d.setDate(d.getDate() - i) + const key = d.toISOString().split('T')[0] + const found = data.find(r => r.day === key) + filled.push(found ? found.count : 0) + } + + const max = Math.max(...filled, 1) + const W = 200 + const H = 40 + const pts = filled.map((v, i) => { + const x = (i / (filled.length - 1)) * W + const y = H - (v / max) * H + return `${x},${y}` + }).join(' ') + + return ( + + + + ) +} diff --git a/src/components/ui/StatCard.jsx b/src/components/ui/StatCard.jsx new file mode 100644 index 000000000..4111865a1 --- /dev/null +++ b/src/components/ui/StatCard.jsx @@ -0,0 +1,19 @@ +/** + * StatCard — a labelled KPI number on the canonical flat Card. + * + * The value renders in the inherited body font. The previous admin markup + * applied an undefined --mm-font-heading variable, which resolved to the + * inherited font anyway; dropping it keeps the exact same rendering without + * the phantom variable. + */ +import Card from './Card' + +export default function StatCard({ label, value, sub }) { + return ( + + {label} + {value ?? '—'} + {sub && {sub}} + + ) +} diff --git a/src/components/ui/index.js b/src/components/ui/index.js index 4745510a6..929a0a488 100644 --- a/src/components/ui/index.js +++ b/src/components/ui/index.js @@ -3,3 +3,5 @@ export { default as Card } from './Card' export { default as Badge } from './Badge' export { default as SectionLabel } from './SectionLabel' export { default as DisplayHeading } from './DisplayHeading' +export { default as StatCard } from './StatCard' +export { default as Sparkline } from './Sparkline' diff --git a/src/pages/AdminDashboardPage.jsx b/src/pages/AdminDashboardPage.jsx index 003d4d835..12a7675fe 100644 --- a/src/pages/AdminDashboardPage.jsx +++ b/src/pages/AdminDashboardPage.jsx @@ -33,6 +33,8 @@ import { ResponsiveContainer, LineChart, Line, BarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid, } from 'recharts' +import { Card, StatCard, Sparkline } from '../components/ui' +import { colors } from '../design/tokens' // --------------------------------------------------------------------------- // Shared helpers @@ -57,18 +59,6 @@ function fmt(dt) { // Shared UI primitives // --------------------------------------------------------------------------- -function StatCard({ label, value, sub }) { - return ( -
- {label} - - {value ?? '—'} - - {sub && {sub}} -
- ) -} - function TabButton({ label, active, onClick }) { return ( - + ))} @@ -1122,7 +1069,7 @@ function BlogTab() { {/* Post list */} {error &&

{error}

} -
+ @@ -1195,11 +1142,11 @@ function BlogTab() { })}
-
+ {/* Create / Edit form */} {form && ( -
+

{isNew ? 'New post' : `Edit: ${form.slug}`} @@ -1321,7 +1268,7 @@ function BlogTab() { {saving ? 'Saving…' : 'Publish'}

-
+ )} @@ -1347,10 +1294,7 @@ export default function AdminDashboardPage() { return (
-

+

Admin Dashboard

Staff-only. Handle with care.