afe36fd347
Claude Design export for SD-0001 (login + create-storefront surfaces): wf-*/hf-* screens, design-system bundle, offline preview. First artifact in the ui/designs/ collection (ecomm#8). Also ignore .DS_Store. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
206 lines
12 KiB
React
206 lines
12 KiB
React
// hf-bootstrap.jsx — Bootstrap (PUC-10 / PUC-11) hi-fi. Terminals + runbook.
|
|
const { HF_H: B, HF_STARS: B_STARS, HF_DSC: BDS } = window;
|
|
const { Eyebrow: BEyebrow, Soul: BSoul, Callout: BCallout } = BDS;
|
|
const BMONO = 'ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace';
|
|
|
|
function BBay({ children, pad = 32 }) {
|
|
return <div className="hf" style={{ height: '100%', background: `${B_STARS}, ${B.sky}`, padding: pad, boxSizing: 'border-box', overflow: 'hidden', fontFamily: B.body, display: 'flex', flexDirection: 'column' }}>{children}</div>;
|
|
}
|
|
function BHead({ kicker, title, sub }) {
|
|
return (
|
|
<div style={{ marginBottom: 22 }}>
|
|
<BEyebrow style={{ margin: '0 0 9px' }}>{kicker}</BEyebrow>
|
|
<h2 style={{ fontFamily: B.disp, fontWeight: 700, letterSpacing: '-0.015em', fontSize: 23, color: B.star, margin: '0 0 7px', lineHeight: 1.1 }}>{title}</h2>
|
|
{sub && <p style={{ fontFamily: B.body, fontSize: 13.5, lineHeight: 1.5, color: B.soft, margin: 0, maxWidth: 580 }}>{sub}</p>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function BTerminal({ title, lines, style = {} }) {
|
|
return (
|
|
<div style={{ background: B.night, border: `1px solid ${B.hairCard}`, borderRadius: 12, overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: B.shadow, ...style }}>
|
|
<div style={{ flex: '0 0 auto', height: 42, padding: '0 15px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: `1px solid ${B.hair}`, background: 'rgba(155,140,255,.05)' }}>
|
|
<span style={{ display: 'flex', gap: 7 }}>
|
|
{['rgba(155,140,255,.55)', 'rgba(244,199,107,.55)', 'rgba(237,234,255,.32)'].map((c, i) => (
|
|
<span key={i} style={{ width: 10, height: 10, borderRadius: 5, background: c }} />
|
|
))}
|
|
</span>
|
|
<span style={{ fontFamily: BMONO, fontSize: 12, color: B.mute, marginLeft: 5 }}>{title}</span>
|
|
</div>
|
|
<div style={{ flex: 1, padding: '17px 19px', fontFamily: BMONO, fontSize: 12.5, lineHeight: 1.8, whiteSpace: 'pre-wrap', overflow: 'hidden' }}>
|
|
{lines.map((ln, i) => <BLine key={i} ln={ln} />)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
function BLine({ ln }) {
|
|
const [k, t] = ln;
|
|
if (k === 'sp') return <div style={{ height: 10 }} />;
|
|
if (k === 'cmd') return <div><span style={{ color: B.lilac }}>$ </span><span style={{ color: B.star }}>{t}</span></div>;
|
|
if (k === 'step') return <div><span style={{ color: B.lilac }}>→ </span><span style={{ color: B.soft }}>{t}</span></div>;
|
|
if (k === 'ok') return <div><span style={{ color: B.gold }}>✓ </span><span style={{ color: B.star }}>{t}</span></div>;
|
|
if (k === 'hl') return <div style={{ color: B.gold, background: 'rgba(244,199,107,.09)', margin: '0 -9px', padding: '2px 9px', borderRadius: 5 }}>{t}</div>;
|
|
if (k === 'note') return <div style={{ color: B.faint, fontStyle: 'italic' }}>{t}</div>;
|
|
return <div style={{ color: B.mute }}>{t}</div>;
|
|
}
|
|
|
|
// 1 · principle ----------------------------------------------------------------
|
|
function HFBootPrinciple() {
|
|
const steps = [
|
|
['Operator starts the app', 'scripts/dev.sh locally · flotilla deploy when deployed', 'lilac'],
|
|
['App migrates itself', 'pending .sql migrations apply in order, fail-stop (INV-7)', 'lilac'],
|
|
['/healthz goes green', 'process up · DB reachable · migrations current', 'gold'],
|
|
['First merchant walks the product', 'sign up → create storefront → admin (PUC-2 / 4 / 6)', 'gold'],
|
|
['First rows exist', 'created through the flows alone — nothing placed by hand', 'lilac'],
|
|
];
|
|
return (
|
|
<BBay>
|
|
<BHead kicker="INV-1 · the bootstrap property" title="Empty is a working state"
|
|
sub="No seed step, ever. A fresh, empty database is a first-class state the product brings to life itself — which is what makes launching anywhere an ordinary, rehearsed act." />
|
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
{steps.map(([t, d, c], i) => {
|
|
const col = c === 'gold' ? B.gold : B.lilac;
|
|
return (
|
|
<div key={i} style={{ display: 'flex', gap: 16, paddingBottom: i < steps.length - 1 ? 18 : 0 }}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flex: '0 0 auto' }}>
|
|
<span style={{ width: 28, height: 28, borderRadius: 14, border: `1.5px solid ${col}`, color: col, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: B.disp, fontWeight: 700, fontSize: 13.5 }}>{i + 1}</span>
|
|
{i < steps.length - 1 && <span style={{ width: 1.5, flex: 1, minHeight: 22, background: B.hair }} />}
|
|
</div>
|
|
<div style={{ paddingTop: 3 }}>
|
|
<div style={{ fontFamily: B.disp, fontWeight: 500, fontSize: 15, color: B.star }}>{t}</div>
|
|
<div style={{ fontFamily: BMONO, fontSize: 11.5, color: B.mute, marginTop: 3 }}>{d}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</BBay>
|
|
);
|
|
}
|
|
|
|
// 2 · runbook (Paper) ----------------------------------------------------------
|
|
function HFBootRunbook() {
|
|
const envs = [
|
|
['localhost', './scripts/dev.sh', 'Docker is the one prerequisite. Brings up Postgres, migrates, serves on :5173.'],
|
|
['pre-prod (PPE)', 'flotilla deploy ecomm --env ppe', 'Provisions Cloud SQL, resolves secrets, migrates at startup. Rehearse here first.'],
|
|
['production', 'flotilla deploy ecomm --env prod', 'The identical gesture. Day-one prod is simply the bootstrap state.'],
|
|
];
|
|
return (
|
|
<div style={{ height: '100%', background: 'var(--wv-paper)', padding: '36px 40px', boxSizing: 'border-box', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 18 }}>
|
|
<span style={{ fontFamily: BMONO, fontSize: 12, color: 'var(--wv-violet)', background: 'rgba(124,111,224,.12)', padding: '4px 11px', borderRadius: 999 }}>docs/BOOTSTRAP.md</span>
|
|
<span style={{ fontFamily: BMONO, fontSize: 11.5, color: '#8A7FB0' }}>the documented gesture</span>
|
|
</div>
|
|
<h2 style={{ fontFamily: B.disp, fontWeight: 700, letterSpacing: '-0.015em', fontSize: 27, color: 'var(--wv-ink)', margin: '0 0 8px', lineHeight: 1.08 }}>Bringing ecomm up from empty</h2>
|
|
<p style={{ fontFamily: B.body, fontSize: 14, lineHeight: 1.55, color: '#4B4170', margin: '0 0 24px', maxWidth: 560 }}>
|
|
One gesture, three environments. No hand-seeded data — the first merchant arrives through the product flows alone.
|
|
</p>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
{envs.map(([env, cmd, note]) => (
|
|
<div key={env} style={{ display: 'grid', gridTemplateColumns: '132px 1fr', gap: 18, alignItems: 'baseline' }}>
|
|
<span style={{ fontFamily: B.disp, fontWeight: 500, fontSize: 14, color: 'var(--wv-ink)' }}>{env}</span>
|
|
<div>
|
|
<div style={{ fontFamily: BMONO, fontSize: 12.5, color: 'var(--wv-ink)', background: '#ECE8F7', border: '1px solid #DCD5F0', borderRadius: 8, padding: '8px 12px', display: 'inline-block' }}>{cmd}</div>
|
|
<div style={{ fontFamily: B.body, fontSize: 12.5, lineHeight: 1.5, color: '#6A5F90', marginTop: 7 }}>{note}</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div style={{ flex: 1 }} />
|
|
<BCallout onLight>Empty is a working state — there is no seed.</BCallout>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 3 · localhost terminal -------------------------------------------------------
|
|
function HFBootDev() {
|
|
return (
|
|
<BBay>
|
|
<BHead kicker="PUC-10 · localhost" title="One command from clean checkout"
|
|
sub="A developer reaches “first merchant, first storefront” with Docker as the only prerequisite — the app migrates an empty database itself." />
|
|
<BTerminal title="scripts/dev.sh" style={{ flex: 1 }} lines={[
|
|
['cmd', 'git clone …/wiggleverse-ecomm && cd wiggleverse-ecomm'],
|
|
['cmd', './scripts/dev.sh'],
|
|
['sp'],
|
|
['step', 'datastore: starting postgres (docker compose up)…'],
|
|
['ok', 'postgres healthy · ecomm_dev · :5432'],
|
|
['step', 'migrations: applying pending…'],
|
|
['ok', '0001_init applied · schema current'],
|
|
['step', 'backend: uvicorn on :8000'],
|
|
['step', 'web: vite on :5173 (proxying /api)'],
|
|
['ok', '/healthz 200 {status:"ok", migrations:"current"}'],
|
|
['sp'],
|
|
['out', ' ecomm is up against an empty database.'],
|
|
['hl', ' open http://localhost:5173'],
|
|
['sp'],
|
|
['note', 'one-time codes print to this log (LogMailer) — no real mail in dev.'],
|
|
]} />
|
|
</BBay>
|
|
);
|
|
}
|
|
|
|
// 4 · the OTC channel ----------------------------------------------------------
|
|
function HFBootChannel() {
|
|
return (
|
|
<BBay>
|
|
<BHead kicker="PUC-10 / PUC-11 · the OTC channel" title="Where the one-time code lands"
|
|
sub="Same issue/verify path everywhere — only the mailer differs by configuration (INV-8)." />
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, flex: 1 }}>
|
|
<div>
|
|
<ChanLabel tone="lilac">Dev — the code prints to the backend log</ChanLabel>
|
|
<BTerminal title="backend · LogMailer" lines={[
|
|
['out', '[mailer] adapter=LogMailer'],
|
|
['out', ' to: ben@bensbets.com'],
|
|
['hl', ' subject: Your ecomm code: 419 204'],
|
|
['out', ' code valid 10m · single-use · 5 tries'],
|
|
]} />
|
|
</div>
|
|
<div>
|
|
<ChanLabel tone="gold">Deployed — the code arrives as real email</ChanLabel>
|
|
<div style={{ background: B.raised, border: `1px solid ${B.hairCard}`, borderRadius: 12, padding: 17 }}>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: B.body, fontSize: 12, color: B.mute, marginBottom: 9, borderBottom: `1px solid ${B.hair}`, paddingBottom: 9 }}>
|
|
<span>ecomm <no-reply@ecomm.wiggleverse.org></span><span>now</span>
|
|
</div>
|
|
<div style={{ fontFamily: B.disp, fontWeight: 500, fontSize: 15.5, color: B.star, marginBottom: 7 }}>Your ecomm code: 419 204</div>
|
|
<div style={{ fontFamily: B.body, fontSize: 12.5, lineHeight: 1.5, color: B.soft }}>
|
|
Enter this code to sign in. It's good for 10 minutes. If you didn't request it, you can ignore this email.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</BBay>
|
|
);
|
|
}
|
|
function ChanLabel({ children, tone }) {
|
|
const c = tone === 'gold' ? B.gold : B.lilac;
|
|
return <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10, fontFamily: B.body, fontSize: 12.5, color: B.soft }}>
|
|
<span aria-hidden="true" style={{ color: c }}>{tone === 'gold' ? '◆' : '◇'}</span>{children}
|
|
</div>;
|
|
}
|
|
|
|
// 5 · deploy terminal ----------------------------------------------------------
|
|
function HFBootDeploy() {
|
|
return (
|
|
<BBay>
|
|
<BHead kicker="PUC-11 · pre-prod, then prod" title="The same gesture, deployed"
|
|
sub="The operator runs flotilla; PPE rehearses Prod with real email (BUC-5a). No environment is “later.”" />
|
|
<BTerminal title="flotilla — operator front door" style={{ flex: 1 }} lines={[
|
|
['cmd', 'flotilla deploy ecomm --env ppe'],
|
|
['sp'],
|
|
['step', 'provision: Cloud SQL (postgres) · backups + PITR…'],
|
|
['ok', 'database ready · secrets resolved from Secret Manager'],
|
|
['step', 'deploy: image → single VM · fail-stop'],
|
|
['step', 'migrations: applying at startup…'],
|
|
['ok', '0001_init applied'],
|
|
['ok', '/healthz 200 — ppe live'],
|
|
['out', ' rehearsal: real sign-up (real email) → storefront → admin ✓'],
|
|
['sp'],
|
|
['cmd', 'flotilla deploy ecomm --env prod'],
|
|
['ok', 'same gesture — day-one prod is the bootstrap state'],
|
|
]} />
|
|
</BBay>
|
|
);
|
|
}
|
|
|
|
Object.assign(window, { HFBootPrinciple, HFBootRunbook, HFBootDev, HFBootChannel, HFBootDeploy });
|