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.
364 lines
20 KiB
JavaScript
364 lines
20 KiB
JavaScript
// KitDocs.jsx — browseable docs page for the Compliance kit.
|
||
|
||
const { useState: useDocsCompState } = React;
|
||
const {
|
||
COLORS: DCC, statusColor: dStatus, pctDisplay: dPct, cAlpha: dA,
|
||
CompPageHeader: DHeader, CompButton: DBtn, TeamTabs: DTabs,
|
||
VariantPill: DVPill, StatusRibbon: DRibbon, MetricHealthCard: DMHC,
|
||
MetricBadge: DMB, SeenBadge: DSB,
|
||
DeviceTable: DDT, DeviceTableToolbar: DDTT, DeviceTableHeader: DDTH, DeviceRow: DDR,
|
||
CompEmpty: DEmpty, ChartCard: DChart, ChartLegend: DLegend,
|
||
DefinitionTooltip: DTip, RollbackDialog: DRoll, RollbackToast: DToast,
|
||
CompIcon: DIcon,
|
||
} = window.COMP;
|
||
const { CompliancePage: DPage } = window.COMP_PAGE;
|
||
|
||
function CSection({ 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: DCC.teal, 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: 660, margin: '8px 0 0 0',
|
||
}}>{blurb}</p>
|
||
)}
|
||
</div>
|
||
{children}
|
||
</section>
|
||
);
|
||
}
|
||
|
||
function CCode({ children }) {
|
||
return (
|
||
<code style={{
|
||
display: 'inline-block', padding: '2px 6px', borderRadius: 3,
|
||
background: dA(DCC.teal, 0.10), border: `1px solid ${dA(DCC.teal, 0.18)}`,
|
||
fontFamily: 'var(--font-mono)', fontSize: 11, color: DCC.tealMid,
|
||
}}>{children}</code>
|
||
);
|
||
}
|
||
|
||
function CSwatch({ 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>
|
||
<CCode>{value}</CCode>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function CSpec({ 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 CSpecimen({ children, padding = 24 }) {
|
||
return (
|
||
<div style={{
|
||
padding,
|
||
background: 'rgba(15,23,42,0.5)',
|
||
border: '1px solid rgba(255,255,255,0.05)', borderRadius: 8,
|
||
}}>{children}</div>
|
||
);
|
||
}
|
||
|
||
const TABS = [
|
||
{ id: 'overview', label: 'Overview' },
|
||
{ id: 'tokens', label: 'Tokens' },
|
||
{ id: 'components', label: 'Components' },
|
||
{ id: 'assemblies', label: 'Assemblies' },
|
||
{ id: 'reference', label: 'Reference Page' },
|
||
];
|
||
|
||
const subhead = {
|
||
margin: '32px 0 6px 0',
|
||
fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 700,
|
||
color: 'var(--fg-1)', textTransform: 'uppercase', letterSpacing: '0.08em',
|
||
};
|
||
const subblurb = {
|
||
margin: '0 0 12px 0',
|
||
fontFamily: 'var(--font-display)', fontSize: 13, lineHeight: 1.55,
|
||
color: 'var(--fg-muted)', maxWidth: 720,
|
||
};
|
||
|
||
const SAMPLE_FAMILY_BAD = {
|
||
metricId: 'VM-CRITICAL', category: 'Vulnerability Management', target: 0.95, worstStatus: 'Below 15% of Target',
|
||
entries: [
|
||
{ metric_id: 'VM-CRITICAL', priority: 'P1', compliance_pct: 0.74, status: 'Below 15% of Target' },
|
||
{ metric_id: 'VM-CRITICAL', priority: 'P2', compliance_pct: 0.91, status: 'Within 15% of Target' },
|
||
],
|
||
};
|
||
const SAMPLE_FAMILY_OK = {
|
||
metricId: 'EDR-DEPLOY', category: 'Endpoint Protection', target: 0.95, worstStatus: 'Meets/Exceeds Target',
|
||
entries: [{ metric_id: 'EDR-DEPLOY', compliance_pct: 0.96, status: 'Meets/Exceeds Target' }],
|
||
};
|
||
|
||
function CKitDocs() {
|
||
const [active, setActive] = useDocsCompState('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' });
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div style={{ minHeight: '100vh', background: 'var(--bg-page)' }}>
|
||
<header style={{ padding: '40px 48px 0 48px', maxWidth: 1280, margin: '0 auto' }}>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, fontWeight: 700, color: DCC.teal, textTransform: 'uppercase', letterSpacing: '0.18em', marginBottom: 8 }}>
|
||
STEAM Security · UI Kit
|
||
</div>
|
||
<h1 style={{
|
||
margin: 0, fontFamily: 'var(--font-mono)', fontSize: 36, fontWeight: 700,
|
||
color: DCC.teal, textTransform: 'uppercase', letterSpacing: '0.08em',
|
||
textShadow: `0 0 24px ${dA(DCC.teal, 0.30)}`,
|
||
}}>Compliance</h1>
|
||
<p style={{
|
||
margin: '12px 0 0 0', maxWidth: 720, fontSize: 15, lineHeight: 1.6,
|
||
color: 'var(--fg-muted)', fontFamily: 'var(--font-display)',
|
||
}}>
|
||
The AEO Compliance view: per-team metric health, six trend charts, and a non-compliant device
|
||
drilldown. Identity color is teal — distinct from the green-titled CVE pages — with status colors
|
||
that map green/amber/red onto target adherence.
|
||
</p>
|
||
</header>
|
||
|
||
<nav style={{
|
||
position: 'sticky', top: 0, zIndex: 10, marginTop: 28,
|
||
background: 'rgba(2,6,23,0.85)', backdropFilter: 'blur(8px)',
|
||
borderBottom: `1px solid ${dA(DCC.teal, 0.15)}`,
|
||
}}>
|
||
<div style={{ maxWidth: 1280, margin: '0 auto', padding: '0 48px', display: 'flex', gap: 4 }}>
|
||
{TABS.map(t => {
|
||
const on = active === t.id;
|
||
return (
|
||
<button key={t.id} onClick={() => handle(t.id)} style={{
|
||
padding: '14px 16px', background: 'transparent', border: 'none',
|
||
borderBottom: `2px solid ${on ? DCC.teal : 'transparent'}`,
|
||
color: on ? DCC.teal : 'var(--fg-2)',
|
||
fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 600,
|
||
textTransform: 'uppercase', letterSpacing: '0.08em',
|
||
cursor: 'pointer', transition: 'all 160ms ease',
|
||
}}>{t.label}</button>
|
||
);
|
||
})}
|
||
</div>
|
||
</nav>
|
||
|
||
<main style={{ maxWidth: 1280, margin: '0 auto', padding: '0 48px 96px 48px' }}>
|
||
|
||
<CSection id="overview" eyebrow="01 — Overview" title="Why this kit exists" blurb="Compliance has its own visual identity inside the suite — teal page title, status colors driven by target adherence, and a metric-card pattern that does double duty as a filter. This kit captures the vocabulary so other audit-style views can reuse it.">
|
||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
|
||
<CSpecimen>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: DCC.teal, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 12, fontWeight: 600 }}>Identity</div>
|
||
<p style={{ margin: 0, fontSize: 13, color: 'var(--fg-1)', lineHeight: 1.6, fontFamily: 'var(--font-display)' }}>
|
||
Teal owns the page header, the active team tab, the upload CTA, the active device row,
|
||
and any "neutral compliance signal" surface. Status colors (green/amber/red) own
|
||
everything that represents target adherence — never decorative.
|
||
</p>
|
||
</CSpecimen>
|
||
<CSpecimen>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: DCC.teal, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 12, fontWeight: 600 }}>Layout</div>
|
||
<p style={{ margin: 0, fontSize: 13, color: 'var(--fg-1)', lineHeight: 1.6, fontFamily: 'var(--font-display)' }}>
|
||
Page header → team tabs → metric health row (one card per metric family) →
|
||
3×2 chart grid → device table with active/resolved tabs and hostname search.
|
||
Selecting a metric card filters the table; selecting a row opens a detail panel.
|
||
</p>
|
||
</CSpecimen>
|
||
</div>
|
||
</CSection>
|
||
|
||
<CSection id="tokens" eyebrow="02 — Tokens" title="Status, category, and identity color" blurb="Status colors are reserved for target adherence. Category colors tag failing-metric badges by program area so a host's failure mix is scannable.">
|
||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32 }}>
|
||
<div>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8, fontWeight: 600 }}>Status (target adherence)</div>
|
||
<CSwatch name="green" value={DCC.green} role="Meets/Exceeds Target · success" />
|
||
<CSwatch name="amber" value={DCC.amber} role="Within 15% of Target · attention" />
|
||
<CSwatch name="red" value={DCC.red} role="Below 15% of Target · critical" />
|
||
<div style={{ marginTop: 24, fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8, fontWeight: 600 }}>Identity</div>
|
||
<CSwatch name="teal" value={DCC.teal} role="Page title · CTA · selected row" />
|
||
</div>
|
||
<div>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8, fontWeight: 600 }}>Category</div>
|
||
<CSwatch name="red" value={DCC.red} role="Vulnerability Management" />
|
||
<CSwatch name="amber" value={DCC.amber} role="Access & MFA" />
|
||
<CSwatch name="purple" value={DCC.purple} role="Logging & Monitoring" />
|
||
<CSwatch name="orange" value={DCC.orange} role="End-of-Life OS · Endpoint Protection" />
|
||
<CSwatch name="sky" value={DCC.sky} role="Application Security" />
|
||
<CSwatch name="slate" value={DCC.slate} role="Asset Data Quality · Decommissioned" />
|
||
</div>
|
||
</div>
|
||
<div style={{ marginTop: 32 }}>
|
||
<CSpec label="Card chrome">background <CCode>linear-gradient(135deg, rgba(30,41,59,.95), rgba(15,23,42,.98))</CCode></CSpec>
|
||
<CSpec label="Metric card border">resting <CCode>1.5px solid {`{statusColor}`} @ 0.25</CCode> · hover <CCode>0.50</CCode> · active <CCode>1.0</CCode> + 15% bg fill</CSpec>
|
||
<CSpec label="Title type"><CCode>var(--font-mono)</CCode> · 24 / 700 · uppercase · 0.1em tracking · 16px text-shadow glow</CSpec>
|
||
<CSpec label="Worst-status logic">A family's <CCode>worstStatus</CCode> is the lowest-severity entry across all variants — drives card border + ribbon</CSpec>
|
||
</div>
|
||
</CSection>
|
||
|
||
<CSection id="components" eyebrow="03 — Components" title="The pieces" blurb="Every primitive used by the page assembly. All are exported on window.COMP.">
|
||
<h3 style={subhead}>CompPageHeader</h3>
|
||
<p style={subblurb}>Teal title with glow, last-report meta + optional rollback button, network/vertical scores, and a refresh + upload CTA on the right.</p>
|
||
<CSpecimen>
|
||
<DHeader lastReport="2026-04-21" networkScore="88%" verticalScore="84%" isAdmin onRollback={() => {}} />
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>TeamTabs</h3>
|
||
<p style={subblurb}>Two-team toggle pinned above the metric strip. The active tab fills with teal at 18% alpha.</p>
|
||
<CSpecimen>
|
||
<DTabs teams={['STEAM', 'ACCESS-ENG']} active="STEAM" onChange={() => {}} />
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>CompButton</h3>
|
||
<p style={subblurb}>Four variants. Primary is the lone teal CTA (Upload Report). Danger fronts the rollback flow.</p>
|
||
<CSpecimen>
|
||
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
|
||
<DBtn variant="primary" icon="upload">Upload Report</DBtn>
|
||
<DBtn variant="neutral" icon="refresh">Refresh</DBtn>
|
||
<DBtn variant="danger" icon="rotate">Rollback</DBtn>
|
||
<DBtn variant="ghost">Cancel</DBtn>
|
||
</div>
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>MetricHealthCard</h3>
|
||
<p style={subblurb}>The big clickable card in the metric strip. Border + ID color follow the family's <em>worst</em> status, so a single bad variant turns the whole family red. Click filters the device table; the info "i" opens a definition panel.</p>
|
||
<CSpecimen>
|
||
<div style={{ display: 'flex', gap: 12 }}>
|
||
<div style={{ flex: 1 }}><DMHC family={SAMPLE_FAMILY_BAD} active={false} onClick={() => {}} onInfoClick={() => {}} /></div>
|
||
<div style={{ flex: 1 }}><DMHC family={SAMPLE_FAMILY_BAD} active={true} onClick={() => {}} onInfoClick={() => {}} /></div>
|
||
<div style={{ flex: 1 }}><DMHC family={SAMPLE_FAMILY_OK} active={false} onClick={() => {}} onInfoClick={() => {}} /></div>
|
||
</div>
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>VariantPill · StatusRibbon</h3>
|
||
<p style={subblurb}>Atoms inside MetricHealthCard. VariantPill = one priority's % readout. StatusRibbon = the bottom lozenge.</p>
|
||
<CSpecimen>
|
||
<div style={{ display: 'flex', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>
|
||
<DVPill status="Meets/Exceeds Target" pct={0.97} />
|
||
<DVPill status="Within 15% of Target" pct={0.91} label="P2" />
|
||
<DVPill status="Below 15% of Target" pct={0.74} label="P1" />
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||
<DRibbon status="Meets/Exceeds Target" />
|
||
<DRibbon status="Within 15% of Target" />
|
||
<DRibbon status="Below 15% of Target" />
|
||
</div>
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>MetricBadge · SeenBadge</h3>
|
||
<p style={subblurb}>Row-level chips in the device table. MetricBadge tints by category; SeenBadge escalates slate→amber→red as repeat-failure count grows.</p>
|
||
<CSpecimen>
|
||
<div style={{ display: 'flex', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>
|
||
<DMB metricId="VM-CRITICAL" category="Vulnerability Management" />
|
||
<DMB metricId="AUTH-MFA" category="Access & MFA" />
|
||
<DMB metricId="LOG-COVERAGE" category="Logging & Monitoring" />
|
||
<DMB metricId="EOL-OS" category="End-of-Life OS" />
|
||
<DMB metricId="EDR-DEPLOY" category="Endpoint Protection" />
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<DSB count={1} /><DSB count={3} /><DSB count={5} /><DSB count={7} />
|
||
</div>
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>DeviceRow</h3>
|
||
<p style={subblurb}>One non-compliant host per row. Selected state shifts the left border + hostname color to teal.</p>
|
||
<CSpecimen padding={0}>
|
||
<DDT>
|
||
<DDTH />
|
||
<DDR hostname="app-prod-04.steam.internal" ip="10.42.18.4" type="Linux server" failingMetrics={[{ metric_id: 'VM-CRITICAL', category: 'Vulnerability Management' }, { metric_id: 'EOL-OS', category: 'End-of-Life OS' }]} seenCount={5} hasNotes={true} selected={true} onClick={() => {}} />
|
||
<DDR hostname="db-staging-01.steam.internal" ip="10.42.20.11" type="Linux server" failingMetrics={[{ metric_id: 'VM-CRITICAL', category: 'Vulnerability Management' }]} seenCount={2} hasNotes={false} onClick={() => {}} />
|
||
</DDT>
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>ChartCard</h3>
|
||
<p style={subblurb}>Wrapper for any of the six trend charts. Title in mono uppercase, optional subtitle in disabled grey, 240px chart well by default.</p>
|
||
<CSpecimen>
|
||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
|
||
<DChart title="Network Compliance" subtitle="Trailing 7 days" height={120}>
|
||
<div style={{ height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--fg-disabled)', fontFamily: 'var(--font-mono)', fontSize: 11 }}>chart well</div>
|
||
</DChart>
|
||
<DChart title="Status Distribution" subtitle="Last 4 cycles" height={120}>
|
||
<DLegend items={[{ label: 'Meets', color: DCC.green }, { label: 'Within 15%', color: DCC.amber }, { label: 'Below 15%', color: DCC.red }]} />
|
||
</DChart>
|
||
</div>
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>DefinitionTooltip</h3>
|
||
<p style={subblurb}>Hover popover used to surface a metric's title, business justification, and data sources.</p>
|
||
<CSpecimen>
|
||
<DTip title="VM-CRITICAL — Critical Vulnerabilities Patched" justification="Track the percentage of critical CVEs patched within the SLA window. Below-target performance creates exploitable risk on production assets." sources="Tenable, Atlas, JIRA" />
|
||
</CSpecimen>
|
||
</CSection>
|
||
|
||
<CSection id="assemblies" eyebrow="04 — Assemblies" title="How the parts compose">
|
||
<h3 style={subhead}>Metric health row</h3>
|
||
<p style={subblurb}>One MetricHealthCard per family, flexed evenly. Click a card to filter the device table to only its IDs; an "× clear filter" button appears in the section label when active.</p>
|
||
<CSpecimen>
|
||
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 600, color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 10 }}>
|
||
Metric Health — click to filter
|
||
<span style={{ marginLeft: 12, color: DCC.teal }}>× clear filter</span>
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 10 }}>
|
||
<div style={{ flex: 1 }}><DMHC family={SAMPLE_FAMILY_BAD} active={true} onClick={() => {}} onInfoClick={() => {}} /></div>
|
||
<div style={{ flex: 1 }}><DMHC family={SAMPLE_FAMILY_OK} active={false} onClick={() => {}} onInfoClick={() => {}} /></div>
|
||
</div>
|
||
</CSpecimen>
|
||
|
||
<h3 style={subhead}>Device table</h3>
|
||
<p style={subblurb}>Toolbar (active/resolved tabs + hostname search) → header row → DeviceRows. Empty/loading/error states are centered messages inside the same chrome.</p>
|
||
<CSpecimen padding={0}>
|
||
<DDT>
|
||
<DDTT tab="active" onTabChange={() => {}} count={3} search="" onSearchChange={() => {}} />
|
||
<DDTH />
|
||
<DDR hostname="app-prod-04.steam.internal" ip="10.42.18.4" type="Linux server" failingMetrics={[{ metric_id: 'VM-CRITICAL', category: 'Vulnerability Management' }, { metric_id: 'EOL-OS', category: 'End-of-Life OS' }]} seenCount={5} hasNotes={true} onClick={() => {}} />
|
||
<DDR hostname="jumpbox-east.steam.internal" ip="10.42.4.7" type="Linux server" failingMetrics={[{ metric_id: 'AUTH-MFA', category: 'Access & MFA' }]} seenCount={4} hasNotes={true} onClick={() => {}} />
|
||
<DDR hostname="legacy-billing.steam.internal" ip="10.42.8.18" type="Windows server" failingMetrics={[{ metric_id: 'EOL-OS', category: 'End-of-Life OS' }]} seenCount={7} hasNotes={false} onClick={() => {}} />
|
||
</DDT>
|
||
</CSpecimen>
|
||
</CSection>
|
||
|
||
<CSection id="reference" eyebrow="05 — Reference" title="Full Compliance page" blurb="Every primitive composed exactly as CompliancePage.js renders. The frame below is scrollable.">
|
||
<div className="sample-frame" style={{
|
||
border: `1px solid ${dA(DCC.teal, 0.20)}`, borderRadius: 12,
|
||
overflow: 'hidden', maxHeight: 900, overflowY: 'auto',
|
||
background: 'var(--bg-page)',
|
||
}}>
|
||
<DPage />
|
||
</div>
|
||
</CSection>
|
||
</main>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
window.COMP_DOCS = { CKitDocs };
|