Frontend redesign in progress: updated styles, layout, and components across all pages to align with new design system. Includes Jira API compliance specs, property tests, and load test script.
482 lines
23 KiB
JavaScript
482 lines
23 KiB
JavaScript
// KitDocs.jsx — browseable docs page for the Reporting kit.
|
|
// Sections: Overview · Tokens · Components · Assemblies · Reference page.
|
|
|
|
const { useState: useDocsState } = React;
|
|
const {
|
|
COLORS: DC, PageHeader, RptButton, KbCard, PillTab, FilterChip, StatusBanner,
|
|
ToolbarLabel, SeverityDot, SlaPill, WorkflowBadge, DonutSample, RptIcon: DI,
|
|
} = window.RPT;
|
|
const { ReportingPage } = window.RPT_PAGE;
|
|
|
|
/* ── Layout primitives ─────────────────────────────────────────── */
|
|
function Section({ id, eyebrow, title, blurb, children }) {
|
|
return (
|
|
<section id={id} style={{ paddingTop: 32, scrollMarginTop: 120 }}>
|
|
<div style={{ marginBottom: 16 }}>
|
|
{eyebrow && (
|
|
<div style={{
|
|
fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 700,
|
|
color: DC.sky, textTransform: 'uppercase', letterSpacing: '0.18em',
|
|
marginBottom: 6,
|
|
}}>{eyebrow}</div>
|
|
)}
|
|
<h2 style={{
|
|
fontFamily: 'var(--font-mono)', fontSize: 22, fontWeight: 700,
|
|
color: 'var(--fg-1)', margin: 0, letterSpacing: '0.02em',
|
|
}}>{title}</h2>
|
|
{blurb && (
|
|
<p style={{
|
|
fontFamily: 'var(--font-display)', fontSize: 14, lineHeight: 1.6,
|
|
color: 'var(--fg-muted)', maxWidth: 640, margin: '8px 0 0 0',
|
|
}}>{blurb}</p>
|
|
)}
|
|
</div>
|
|
{children}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function Spec({ label, children }) {
|
|
return (
|
|
<div style={{
|
|
display: 'grid', gridTemplateColumns: '160px 1fr', gap: 16,
|
|
padding: '10px 0', borderBottom: '1px solid rgba(255,255,255,0.04)',
|
|
fontFamily: 'var(--font-mono)', fontSize: 12,
|
|
}}>
|
|
<div style={{ color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.06em', fontSize: 10, fontWeight: 600 }}>
|
|
{label}
|
|
</div>
|
|
<div style={{ color: 'var(--fg-2)' }}>{children}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function CodeChip({ children }) {
|
|
return (
|
|
<code style={{
|
|
display: 'inline-block', padding: '2px 6px', borderRadius: 3,
|
|
background: 'rgba(14,165,233,0.10)', border: '1px solid rgba(14,165,233,0.18)',
|
|
fontFamily: 'var(--font-mono)', fontSize: 11, color: DC.skySoft,
|
|
}}>{children}</code>
|
|
);
|
|
}
|
|
|
|
function SwatchRow({ name, value, role }) {
|
|
return (
|
|
<div style={{
|
|
display: 'grid', gridTemplateColumns: '52px 1fr auto', alignItems: 'center', gap: 14,
|
|
padding: '8px 0', borderBottom: '1px solid rgba(255,255,255,0.04)',
|
|
}}>
|
|
<div style={{
|
|
height: 36, borderRadius: 6, background: value,
|
|
border: '1px solid rgba(255,255,255,0.08)',
|
|
}} />
|
|
<div>
|
|
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-1)', fontWeight: 600 }}>{name}</div>
|
|
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-disabled)' }}>{role}</div>
|
|
</div>
|
|
<CodeChip>{value}</CodeChip>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/* ── Sticky tab nav ─────────────────────────────────────────────── */
|
|
function TabNav({ active, onChange }) {
|
|
const items = [
|
|
{ id: 'overview', label: 'Overview' },
|
|
{ id: 'tokens', label: 'Tokens' },
|
|
{ id: 'components', label: 'Components' },
|
|
{ id: 'assemblies', label: 'Assemblies' },
|
|
{ id: 'reference', label: 'Reference page' },
|
|
];
|
|
return (
|
|
<div style={{
|
|
position: 'sticky', top: 0, zIndex: 20,
|
|
background: 'rgba(15,23,42,0.92)',
|
|
backdropFilter: 'blur(8px)',
|
|
borderBottom: '1px solid rgba(14,165,233,0.12)',
|
|
padding: '14px 24px',
|
|
}}>
|
|
<div style={{ maxWidth: 1280, margin: '0 auto', display: 'flex', alignItems: 'center', gap: 16 }}>
|
|
<div style={{
|
|
fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 700,
|
|
color: DC.green, textTransform: 'uppercase', letterSpacing: '0.12em',
|
|
textShadow: '0 0 12px rgba(16,185,129,0.25)',
|
|
flexShrink: 0,
|
|
}}>
|
|
Reporting Kit
|
|
</div>
|
|
<div style={{ width: 1, height: 18, background: 'rgba(255,255,255,0.08)' }} />
|
|
<div style={{ display: 'flex', gap: 4 }}>
|
|
{items.map((it) => (
|
|
<PillTab key={it.id} active={active === it.id} onClick={() => onChange(it.id)}>
|
|
{it.label}
|
|
</PillTab>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/* ── Overview ───────────────────────────────────────────────────── */
|
|
function OverviewSection() {
|
|
return (
|
|
<Section
|
|
id="overview"
|
|
eyebrow="01 · Overview"
|
|
title="Reporting page UI kit"
|
|
blurb="The visual vocabulary used by /reporting. Aligned to the Knowledge Base pattern: green-glow page identity, sky-blue surface accents, mono uppercase labels, Knowledge-Base card chrome on every panel."
|
|
>
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 14 }}>
|
|
<KbCard label="Page identity" hover={false}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
<div style={{
|
|
fontFamily: 'var(--font-mono)', fontSize: 18, fontWeight: 700,
|
|
color: DC.green, textTransform: 'uppercase', letterSpacing: '0.1em',
|
|
textShadow: '0 0 12px rgba(16,185,129,0.25)',
|
|
}}>Reporting</div>
|
|
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-muted)' }}>
|
|
Green is reserved for the page title + the lone primary action (Sync). Everything else is sky.
|
|
</div>
|
|
</div>
|
|
</KbCard>
|
|
<KbCard label="Surface accent" hover={false}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
<div style={{
|
|
padding: 10, borderRadius: 6,
|
|
background: 'linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)',
|
|
border: '1.5px solid rgba(14,165,233,0.35)',
|
|
fontFamily: 'var(--font-mono)', fontSize: 11, color: DC.skySoft,
|
|
}}>
|
|
KB card · sky border · 0.12 → 0.35 on hover
|
|
</div>
|
|
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-muted)' }}>
|
|
Same chrome for donuts, trend, and findings panel. No more colored left-rails.
|
|
</div>
|
|
</div>
|
|
</KbCard>
|
|
<KbCard label="Type" hover={false}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
|
|
Card label · 11 / 600 / 0.1em
|
|
</div>
|
|
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 14, color: 'var(--fg-1)' }}>JetBrains Mono · everywhere</div>
|
|
<div style={{ fontFamily: 'var(--font-display)', fontSize: 13, color: 'var(--fg-muted)' }}>Outfit · prose only (blurbs)</div>
|
|
</div>
|
|
</KbCard>
|
|
</div>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
/* ── Tokens ─────────────────────────────────────────────────────── */
|
|
function TokensSection() {
|
|
return (
|
|
<Section
|
|
id="tokens"
|
|
eyebrow="02 · Tokens"
|
|
title="Color roles, type, spacing"
|
|
blurb="Reporting uses the dashboard token set. These are the specific roles the page leans on."
|
|
>
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', gap: 14 }}>
|
|
<KbCard label="Color roles" hover={false}>
|
|
<SwatchRow name="--accent (sky-500)" value="#0EA5E9" role="Surfaces · pills · table headers · neutral btn" />
|
|
<SwatchRow name="--intel-success" value="#10B981" role="Page title glow · primary Sync button" />
|
|
<SwatchRow name="--intel-warning" value="#F59E0B" role="Filter active · anomaly · At-Risk SLA" />
|
|
<SwatchRow name="--intel-danger" value="#EF4444" role="Errors · Critical sev · Overdue SLA" />
|
|
<SwatchRow name="--text-disabled" value="#64748B" role="Card labels · meta text" />
|
|
<SwatchRow name="--text-faint" value="#475569" role="Subtitle · separator counts" />
|
|
</KbCard>
|
|
<KbCard label="Card chrome" hover={false}>
|
|
<Spec label="Background"><CodeChip>linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)</CodeChip></Spec>
|
|
<Spec label="Border (rest)"><CodeChip>1.5px solid rgba(14,165,233,0.12)</CodeChip></Spec>
|
|
<Spec label="Border (hover)"><CodeChip>1.5px solid rgba(14,165,233,0.35)</CodeChip></Spec>
|
|
<Spec label="Radius"><CodeChip>8px</CodeChip></Spec>
|
|
<Spec label="Padding"><CodeChip>16px (donuts) / 20px (panels)</CodeChip></Spec>
|
|
<Spec label="Label divider"><CodeChip>1px solid rgba(255,255,255,0.04)</CodeChip></Spec>
|
|
</KbCard>
|
|
<KbCard label="Type scale" hover={false}>
|
|
<Spec label="Page title">JetBrains Mono · 24 / 700 · 0.1em · uppercase · green glow</Spec>
|
|
<Spec label="Subtitle / meta">Mono · 12 / 400 · slate-muted</Spec>
|
|
<Spec label="Card label">Mono · 11 / 600 · 0.1em · uppercase · slate-disabled</Spec>
|
|
<Spec label="Toolbar label">Mono · 11 / 700 · 0.1em · uppercase · sky</Spec>
|
|
<Spec label="Button">Mono · 12 / 600 · 0.05em · uppercase</Spec>
|
|
<Spec label="Pill tab">Mono · 11 / 600 · 0.05em · uppercase</Spec>
|
|
<Spec label="Table cell">Mono · 11 / 400</Spec>
|
|
</KbCard>
|
|
<KbCard label="Spacing & motion" hover={false}>
|
|
<Spec label="Page gap"><CodeChip>20px</CodeChip> between major sections</Spec>
|
|
<Spec label="Donut grid"><CodeChip>repeat(auto-fill, minmax(220px, 1fr))</CodeChip> · gap 14</Spec>
|
|
<Spec label="Toolbar gap">8px between buttons · 6px subtle group</Spec>
|
|
<Spec label="Hover transition"><CodeChip>border-color 150ms cubic-bezier(0.4,0,0.2,1)</CodeChip></Spec>
|
|
<Spec label="Spinner"><CodeChip>1s linear infinite</CodeChip></Spec>
|
|
</KbCard>
|
|
</div>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
/* ── Components ─────────────────────────────────────────────────── */
|
|
function ComponentsSection() {
|
|
const [tab, setTab] = useDocsState('ivanti');
|
|
return (
|
|
<Section
|
|
id="components"
|
|
eyebrow="03 · Components"
|
|
title="Primitives"
|
|
blurb="Each component is a thin wrapper around the inline-style pattern used in ReportingPage.js. Drop into other pages that need to inherit the same vocabulary."
|
|
>
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: 14 }}>
|
|
{/* Buttons */}
|
|
<KbCard label="Buttons" hover={false}>
|
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', padding: '4px 0 12px' }}>
|
|
<RptButton variant="primary" icon={<DI.Refresh size={13} />}>Sync</RptButton>
|
|
<RptButton variant="neutral" icon={<DI.Atlas size={13} />}>Atlas</RptButton>
|
|
<RptButton variant="subtle" icon={<DI.Download size={12} />}>Export</RptButton>
|
|
<RptButton variant="danger" icon={<DI.AlertCircle size={12} />}>Reset</RptButton>
|
|
<RptButton variant="neutral" disabled icon={<DI.Loader size={13} />}>Disabled</RptButton>
|
|
</div>
|
|
<Spec label="primary">Green tinted-fill · the only primary on the page (Sync)</Spec>
|
|
<Spec label="neutral">Sky outlined · transparent · for Atlas, Prev/Next, etc.</Spec>
|
|
<Spec label="subtle">Sky tinted-fill · for in-toolbar actions (Export, Queue, Columns)</Spec>
|
|
<Spec label="danger">Red tinted-fill · destructive only</Spec>
|
|
</KbCard>
|
|
|
|
{/* Pill tabs */}
|
|
<KbCard label="Pill tabs (metric switcher)" hover={false}>
|
|
<div style={{ display: 'flex', gap: 5, alignItems: 'center', padding: '4px 0 12px' }}>
|
|
<DI.PieChart size={14} style={{ color: '#334155', marginRight: 4 }} />
|
|
<PillTab active={tab === 'ivanti'} onClick={() => setTab('ivanti')}>Ivanti Findings</PillTab>
|
|
<PillTab active={tab === 'atlas'} onClick={() => setTab('atlas')}>Atlas Coverage</PillTab>
|
|
<PillTab active={tab === 'sla'} onClick={() => setTab('sla')}>SLA</PillTab>
|
|
</div>
|
|
<Spec label="Active">sky border + sky-15% fill + sky text</Spec>
|
|
<Spec label="Hover (inactive)">subtle white-10% border, slate-300 text</Spec>
|
|
</KbCard>
|
|
|
|
{/* Filter chips */}
|
|
<KbCard label="Filter chips" hover={false}>
|
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', padding: '4px 0 12px' }}>
|
|
<FilterChip color={DC.amber}>Severity: Critical</FilterChip>
|
|
<FilterChip color={DC.sky}>Action: Patch</FilterChip>
|
|
<FilterChip color={DC.red}>SLA: Overdue</FilterChip>
|
|
</div>
|
|
<Spec label="Color">Tinted to the dimension being filtered</Spec>
|
|
<Spec label="Click">Clears the filter</Spec>
|
|
</KbCard>
|
|
|
|
{/* Status banners */}
|
|
<KbCard label="Status banners" hover={false}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, padding: '4px 0 12px' }}>
|
|
<StatusBanner tone="error">Atlas: connection refused — retry in 30s</StatusBanner>
|
|
<StatusBanner tone="warn">Sync stale (last success 4 hours ago)</StatusBanner>
|
|
<StatusBanner tone="info">12 findings reassigned to platform-team</StatusBanner>
|
|
</div>
|
|
<Spec label="Placement">Header-level for system errors; inline above target for action results</Spec>
|
|
</KbCard>
|
|
|
|
{/* Severity / SLA / Workflow badges */}
|
|
<KbCard label="Cell badges" hover={false}>
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 14, padding: '4px 0 12px' }}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
<SeverityDot level="Critical" />
|
|
<SeverityDot level="High" />
|
|
<SeverityDot level="Medium" />
|
|
<SeverityDot level="Low" />
|
|
</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
<SlaPill status="OVERDUE" />
|
|
<SlaPill status="AT_RISK" />
|
|
<SlaPill status="WITHIN_SLA" />
|
|
</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
<WorkflowBadge state="OPEN" />
|
|
<WorkflowBadge state="FP" />
|
|
<WorkflowBadge state="EXC" />
|
|
<WorkflowBadge state="REMEDIATED" />
|
|
</div>
|
|
</div>
|
|
<Spec label="Severity">Dot + glow + soft-text label · fixed semantic colors</Spec>
|
|
<Spec label="SLA">Pill · OVERDUE/AT_RISK/WITHIN_SLA</Spec>
|
|
<Spec label="Workflow">Tagged badge · OPEN/FP/EXC/REMEDIATED/ARCHIVED</Spec>
|
|
</KbCard>
|
|
|
|
{/* KB card itself */}
|
|
<KbCard label="KB Card" hover={false}>
|
|
<KbCard label="Open vs Closed" style={{ marginBottom: 10 }}>
|
|
<div style={{ display: 'flex', justifyContent: 'center', padding: '10px 0' }}>
|
|
<DonutSample
|
|
segments={[
|
|
{ label: 'Open', value: 184, color: DC.sky },
|
|
{ label: 'Closed', value: 712, color: DC.green },
|
|
]}
|
|
size={110}
|
|
centerLabel="TOTAL" centerValue="896" />
|
|
</div>
|
|
</KbCard>
|
|
<Spec label="Container">KB card chrome + label divider</Spec>
|
|
<Spec label="Body">Centered donut · 170 min-height · responsive auto-fill grid</Spec>
|
|
</KbCard>
|
|
</div>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
/* ── Assemblies ─────────────────────────────────────────────────── */
|
|
function AssembliesSection() {
|
|
return (
|
|
<Section
|
|
id="assemblies"
|
|
eyebrow="04 · Assemblies"
|
|
title="Page-level patterns"
|
|
blurb="Three combinations the Reporting page is built from. Reuse them as-is on related pages (e.g. dashboards, audit logs)."
|
|
>
|
|
{/* Header assembly */}
|
|
<KbCard label="① Page header + meta + actions" hover={false} style={{ marginBottom: 14 }}>
|
|
<div style={{ padding: '8px 0' }}>
|
|
<PageHeader
|
|
title="Reporting"
|
|
meta={
|
|
<>
|
|
Last sync: 2 minutes ago
|
|
<span style={{ marginLeft: 10, color: '#334155' }}>· 184 of 896 findings</span>
|
|
<span style={{ marginLeft: 8, color: DC.amber }}>(3 filters active)</span>
|
|
</>
|
|
}
|
|
>
|
|
<RptButton variant="neutral" icon={<DI.Atlas size={13} />}>Atlas</RptButton>
|
|
<RptButton variant="primary" icon={<DI.Refresh size={13} />}>Sync</RptButton>
|
|
</PageHeader>
|
|
</div>
|
|
<Spec label="Title">Mono uppercase · green glow · 24px</Spec>
|
|
<Spec label="Meta line">Sync timestamp → record count → active filter count (amber)</Spec>
|
|
<Spec label="Actions">Right-aligned · neutral secondaries → primary on far right</Spec>
|
|
</KbCard>
|
|
|
|
{/* Donut grid assembly */}
|
|
<KbCard label="② Metric tabs + donut grid" hover={false} style={{ marginBottom: 14 }}>
|
|
<div style={{ padding: '8px 0' }}>
|
|
<div style={{ display: 'flex', gap: 5, alignItems: 'center', marginBottom: 12 }}>
|
|
<DI.PieChart size={14} style={{ color: '#334155', marginRight: 4 }} />
|
|
<PillTab active onClick={() => {}}>Ivanti Findings</PillTab>
|
|
<PillTab active={false} onClick={() => {}}>Atlas Coverage</PillTab>
|
|
</div>
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: 12 }}>
|
|
{[
|
|
{ label: 'Open vs Closed', segs: [{ label: 'Open', value: 184, color: DC.sky }, { label: 'Closed', value: 712, color: DC.green }], cl: 'TOTAL', cv: '896' },
|
|
{ label: 'Action Coverage', segs: [{ label: 'Patch', value: 96, color: DC.sky }, { label: 'Mitigate', value: 42, color: DC.green }, { label: 'Accept', value: 28, color: '#A78BFA' }], cl: 'ASSIGNED', cv: '184' },
|
|
{ label: 'FP Status', segs: [{ label: 'Pending', value: 14, color: DC.amber }, { label: 'Approved', value: 31, color: DC.green }, { label: 'Rejected', value: 6, color: DC.red }], cl: 'FINDINGS', cv: '51' },
|
|
].map((d) => (
|
|
<KbCard key={d.label} label={d.label}>
|
|
<div style={{ display: 'flex', justifyContent: 'center', minHeight: 150 }}>
|
|
<DonutSample size={100} segments={d.segs} centerLabel={d.cl} centerValue={d.cv} />
|
|
</div>
|
|
</KbCard>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<Spec label="Tabs">Pill row sits above grid · scopes which donuts render</Spec>
|
|
<Spec label="Grid">Auto-fill, 220px min · each donut is its own KB card</Spec>
|
|
</KbCard>
|
|
|
|
{/* Findings panel chrome */}
|
|
<KbCard label="③ Findings panel chrome (toolbar + filters + table)" hover={false}>
|
|
<div style={{
|
|
background: 'linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)',
|
|
border: '1.5px solid rgba(14,165,233,0.12)', borderRadius: 8, padding: 16,
|
|
marginTop: 8,
|
|
}}>
|
|
<div style={{
|
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
|
paddingBottom: 10, marginBottom: 10,
|
|
borderBottom: '1px solid rgba(255,255,255,0.04)',
|
|
}}>
|
|
<ToolbarLabel count="184 of 896">Host Findings</ToolbarLabel>
|
|
<div style={{ display: 'flex', gap: 6 }}>
|
|
<RptButton variant="subtle" icon={<DI.Download size={12} />}>Export</RptButton>
|
|
<RptButton variant="subtle" icon={<DI.ListTodo size={12} />}>Queue</RptButton>
|
|
<RptButton variant="subtle" icon={<DI.Settings size={12} />}>Columns</RptButton>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
|
<FilterChip color={DC.amber}>Severity: Critical, High</FilterChip>
|
|
<FilterChip color={DC.sky}>Action: Patch</FilterChip>
|
|
<FilterChip color={DC.red}>SLA: Overdue</FilterChip>
|
|
</div>
|
|
</div>
|
|
<Spec label="Toolbar">Mono uppercase label + count · subtle action buttons right</Spec>
|
|
<Spec label="Filter row">Tinted chips, click-to-clear</Spec>
|
|
<Spec label="Header migration">Sync/Atlas no longer live here — they're in the page header</Spec>
|
|
</KbCard>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
/* ── Reference page ─────────────────────────────────────────────── */
|
|
function ReferenceSection() {
|
|
return (
|
|
<Section
|
|
id="reference"
|
|
eyebrow="05 · Reference page"
|
|
title="Full Reporting page"
|
|
blurb="Static mock of /reporting using only kit primitives. Use this to verify any change you make to a primitive flows through the page intact."
|
|
>
|
|
<div style={{
|
|
background: 'var(--bg-page)',
|
|
border: '1px solid rgba(14,165,233,0.12)',
|
|
borderRadius: 12,
|
|
overflow: 'hidden',
|
|
}}>
|
|
<ReportingPage />
|
|
</div>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
/* ── Top-level docs page ─────────────────────────────────────────── */
|
|
function KitDocs() {
|
|
const [active, setActive] = useDocsState('overview');
|
|
const handle = (id) => {
|
|
setActive(id);
|
|
const el = document.getElementById(id);
|
|
if (el) {
|
|
const top = el.getBoundingClientRect().top + window.scrollY - 80;
|
|
window.scrollTo({ top, behavior: 'smooth' });
|
|
}
|
|
};
|
|
|
|
// observe scroll position to update active tab
|
|
React.useEffect(() => {
|
|
const sections = ['overview', 'tokens', 'components', 'assemblies', 'reference']
|
|
.map((id) => document.getElementById(id))
|
|
.filter(Boolean);
|
|
const onScroll = () => {
|
|
const y = window.scrollY + 160;
|
|
let cur = sections[0]?.id;
|
|
for (const s of sections) {
|
|
if (s.offsetTop <= y) cur = s.id;
|
|
}
|
|
setActive(cur);
|
|
};
|
|
window.addEventListener('scroll', onScroll, { passive: true });
|
|
onScroll();
|
|
return () => window.removeEventListener('scroll', onScroll);
|
|
}, []);
|
|
|
|
return (
|
|
<div>
|
|
<TabNav active={active} onChange={handle} />
|
|
<div style={{ maxWidth: 1280, margin: '0 auto', padding: '0 24px 80px' }}>
|
|
<OverviewSection />
|
|
<TokensSection />
|
|
<ComponentsSection />
|
|
<AssembliesSection />
|
|
<ReferenceSection />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
window.RPT_DOCS = { KitDocs };
|