MeshiTrack/docs/design/src/OtherPages.jsx

518 lines
28 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Medium-fi placeholder pages for the remaining medicine + purchasing sections.
const { MED: M2, MEDICINES: MEDS2, CABINET: C2, REGIMENS: REG2, STORES: ST2, PRICES: PR2, PURCHASES: PU2, ACTIVITY: AC2 } = window.MESHI;
// ---- Library ----
const LibraryPage = () => {
const [q, setQ] = useState('');
const rows = MEDS2.filter(m => m.name.toLowerCase().includes(q.toLowerCase()));
return (
<div className="mt-page">
<div className="sub-toolbar">
<div className="cab-filters__search" style={{flex:1, maxWidth: 320}}>
<Icon name="search" size={13}/>
<input placeholder="Search medicines…" value={q} onChange={e=>setQ(e.target.value)} style={{width:'100%'}}/>
</div>
<div style={{display:'flex', gap: 8}}>
<button className="mt-btn mt-btn--ghost">All categories <Icon name="chevDown" size={12}/></button>
<button className="mt-btn mt-btn--ghost">All forms <Icon name="chevDown" size={12}/></button>
<button className="mt-btn mt-btn--primary"><Icon name="plus" size={12}/> Add medicine</button>
</div>
</div>
<div className="mt-card" style={{overflow:'hidden'}}>
{rows.map((m, i) => (
<div key={m.id} className="lib-row">
<div className="cab-card__swatch cab-card__swatch--sm" style={{'--c':'var(--viz-'+m.color+')'}}>
<Icon name={m.form === 'Injection' ? 'injection' : m.form === 'Capsule' ? 'capsule' : 'pill'} size={12}/>
</div>
<div style={{flex:1}}>
<div style={{fontWeight:500, fontSize:13, color:'var(--ink-strong)'}}>{m.name}</div>
<div style={{fontSize:11, color:'var(--ink-muted)'}}>{m.strength} · {m.form}</div>
</div>
<span className={'mt-pill mt-pill--' + (m.category === 'Prescription' ? 'info' : 'ghost')}>{m.category}</span>
<button className="mt-btn mt-btn--subtle"><Icon name="edit" size={12}/></button>
<button className="mt-btn mt-btn--subtle"><Icon name="trash" size={12}/></button>
</div>
))}
</div>
</div>
);
};
// ---- Regimens ----
const RegimensPage = () => (
<div className="mt-page">
<div className="sub-toolbar">
<div style={{display:'flex', gap:8}}>
<button className="mt-btn mt-btn--ghost">All regimens <Icon name="chevDown" size={12}/></button>
<button className="mt-btn mt-btn--ghost"><Icon name="trend" size={12}/> Burn rate</button>
</div>
<button className="mt-btn mt-btn--primary"><Icon name="plus" size={12}/> New regimen</button>
</div>
<div className="mt-col">
{REG2.map(r => (
<div key={r.id} className="mt-card" style={{padding: 18}}>
<div style={{display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap: 16}}>
<div>
<div style={{display:'flex', alignItems:'center', gap: 8, marginBottom: 4}}>
<span style={{fontFamily:'var(--font-display)', fontSize: 18, fontWeight: 500, color:'var(--ink-strong)'}}>Aeri's {r.name}</span>
<span className="mt-pill mt-pill--ok">Active</span>
</div>
<div style={{fontSize:12, color:'var(--ink-muted)'}}>{r.items.length} medication{r.items.length>1?'s':''} · {r.time} · {r.freq}</div>
</div>
<div style={{display:'flex', gap: 6}}>
<button className="mt-btn mt-btn--ghost">Deactivate</button>
<button className="mt-btn mt-btn--subtle"><Icon name="edit" size={12}/></button>
<button className="mt-btn mt-btn--subtle"><Icon name="trash" size={12}/></button>
</div>
</div>
<div style={{display:'flex', flexWrap:'wrap', gap: 6, marginTop: 14}}>
{r.items.map(it => {
const m = M2[it.medId];
return (
<span key={it.medId} className="mt-pill mt-pill--outline" style={{padding: '4px 10px', fontSize: 11}}>
<span style={{background:'var(--viz-'+m.color+')', width:6, height:6, borderRadius:3, display:'inline-block', marginRight: 4}}/>
<strong style={{fontWeight: 500, color:'var(--ink-strong)'}}>{m.name}</strong>
<span style={{marginLeft:4, color:'var(--ink-muted)'}}>{it.qty} {it.unit}</span>
</span>
);
})}
</div>
</div>
))}
</div>
</div>
);
// ---- Organizer ----
const OrganizerPage = () => {
const [regimenId, setRegimenId] = useState(REG2[1].id);
const [days, setDays] = useState(7);
const regimen = REG2.find(r => r.id === regimenId);
// Compute inventory impact: for each item in regimen, how many units will be consumed?
const impact = regimen.items.map(it => {
const m = M2[it.medId];
const cabEntry = C2.find(c => c.medId === it.medId);
const onHand = cabEntry ? cabEntry.qty : 0;
const needed = it.qty * days;
const after = onHand - needed;
const perDay = cabEntry ? (cabEntry.perDose || 1) / (cabEntry.daysPerUnit || 1) : 1;
const daysAfter = perDay > 0 ? Math.floor(Math.max(0, after) / perDay) : 0;
const status = after < 0 ? 'short' : after < needed * 0.5 ? 'low' : 'ok';
return { m, it, onHand, needed, after, daysAfter, status, cabEntry };
});
const anyShort = impact.some(i => i.status === 'short');
const anyLow = impact.some(i => i.status === 'low');
return (
<div className="mt-page">
<div className="org-hero">
<div className="org-hero__controls">
<div>
<div style={{fontSize:10, color:'var(--ink-faint)', textTransform:'uppercase', letterSpacing:'.08em', fontWeight:500}}>Fill from regimen</div>
<select className="org-hero__select" value={regimenId} onChange={e => setRegimenId(e.target.value)}>
{REG2.map(r => <option key={r.id} value={r.id}>Aeri's {r.name}</option>)}
</select>
<div style={{fontSize:12, color:'var(--ink-muted)', marginTop: 4}}>{regimen.time} · {regimen.items.length} medicines · {regimen.freq}</div>
</div>
<div className="org-hero__divider"/>
<div>
<div style={{fontSize:10, color:'var(--ink-faint)', textTransform:'uppercase', letterSpacing:'.08em', fontWeight:500, marginBottom:8}}>Days to fill</div>
<div className="org-hero__stepper">
<button onClick={() => setDays(Math.max(1, days - 1))}></button>
<span className="num">{days}</span>
<button onClick={() => setDays(Math.min(30, days + 1))}>+</button>
</div>
<div className="org-hero__presets">
{[3, 7, 14, 30].map(d => (
<button key={d} className={'org-hero__preset' + (days === d ? ' is-active' : '')} onClick={() => setDays(d)}>{d}d</button>
))}
</div>
</div>
</div>
<div className="org-hero__summary">
<div className="org-hero__sumRow">
<div style={{fontSize:10, color:'var(--ink-faint)', textTransform:'uppercase', letterSpacing:'.08em', fontWeight:500}}>This fill will dispense</div>
<div style={{fontSize: 22, fontWeight: 600, color:'var(--ink-strong)', letterSpacing:'-0.01em'}} className="num">
{impact.reduce((a,b) => a + b.needed, 0)} <span style={{fontSize:13, fontWeight:400, color:'var(--ink-muted)'}}>units across {impact.length} medicines</span>
</div>
</div>
{anyShort ? (
<div className="org-alert org-alert--danger">
<Icon name="alert" size={14}/>
<div>
<strong>Not enough stock.</strong> {impact.filter(i => i.status === 'short').length} medicine{impact.filter(i => i.status === 'short').length > 1 ? 's are' : ' is'} short for this fill.
</div>
<button className="mt-btn mt-btn--primary">Add to shopping list</button>
</div>
) : anyLow ? (
<div className="org-alert org-alert--warn">
<Icon name="alert" size={14}/>
<div><strong>Low after fill.</strong> Some meds will drop below 50% headroom.</div>
</div>
) : (
<div className="org-alert org-alert--ok">
<Icon name="check" size={14}/>
<div><strong>Ready to dispense.</strong> Cabinet has enough stock with comfortable headroom.</div>
</div>
)}
<div style={{display:'flex', gap: 8}}>
<button className="mt-btn mt-btn--primary" disabled={anyShort}><Icon name="zap" size={12}/> Dispense {days}-day fill</button>
<button className="mt-btn mt-btn--ghost">Print label</button>
<div style={{flex:1}}/>
<button className="mt-btn mt-btn--ghost">Fill history</button>
</div>
</div>
</div>
{/* Inventory impact table */}
<div className="mt-card" style={{marginTop: 16}}>
<div className="mt-card__head">
<div>
<div className="mt-card__title">Inventory impact</div>
<div className="mt-card__sub">What this fill will take from the cabinet</div>
</div>
</div>
<div className="org-impact">
<div className="org-impact__head">
<div>Medicine</div>
<div style={{textAlign:'right'}}>On hand</div>
<div style={{textAlign:'right'}}>Needed</div>
<div>After fill</div>
<div style={{textAlign:'right'}}>Days left</div>
</div>
{impact.map(row => {
const m = row.m;
const pct = row.onHand > 0 ? Math.max(0, Math.min(100, (row.after / row.onHand) * 100)) : 0;
return (
<div key={m.id} className={'org-impact__row org-impact__row--' + row.status}>
<div style={{display:'flex', alignItems:'center', gap: 10}}>
<div className="cab-card__swatch cab-card__swatch--sm" style={{'--c':'var(--viz-'+m.color+')'}}>
<Icon name={m.form === 'Injection' ? 'injection' : m.form === 'Capsule' ? 'capsule' : 'pill'} size={12}/>
</div>
<div>
<div style={{fontWeight:500, fontSize:13, color:'var(--ink-strong)'}}>{m.name}</div>
<div style={{fontSize:11, color:'var(--ink-muted)'}}>{row.it.qty} × {days} days = {row.needed} {m.form.toLowerCase()}</div>
</div>
</div>
<div style={{textAlign:'right'}}><span className="num" style={{fontSize:14, fontWeight:600}}>{row.onHand}</span></div>
<div style={{textAlign:'right'}}><span className="num" style={{fontSize:14, color:'var(--ink-muted)'}}>{row.needed}</span></div>
<div style={{display:'flex', alignItems:'center', gap: 8}}>
<div className="org-impact__bar">
<div className="org-impact__barBg"/>
<div className="org-impact__barFill" style={{width: Math.max(0, pct) + '%', background: row.status === 'short' ? 'var(--danger)' : row.status === 'low' ? 'var(--warn)' : 'var(--ok)'}}/>
</div>
<span className="num" style={{fontSize:13, fontWeight:600, color: row.after < 0 ? 'var(--danger)' : 'var(--ink-strong)', minWidth: 38, textAlign:'right'}}>{row.after < 0 ? row.after : row.after}</span>
</div>
<div style={{textAlign:'right'}}>
{row.status === 'short'
? <span className="mt-pill mt-pill--danger">Short</span>
: <span className="num" style={{fontSize:13, color: row.status === 'low' ? 'var(--warn)' : 'var(--ink-muted)'}}>{row.daysAfter}d</span>}
</div>
</div>
);
})}
</div>
</div>
</div>
);
};
// ---- Activity ----
const ActivityPage = () => {
const { SPENDING } = window.MESHI;
return (
<div className="mt-page">
<div className="mt-card" style={{marginBottom: 16}}>
<div className="mt-card__head">
<div>
<div className="mt-card__title">Spending summary</div>
<div className="mt-card__sub">Last 6 months</div>
</div>
<div className="mt-row" style={{gap:6}}>
<button className="mt-btn mt-btn--ghost">This year <Icon name="chevDown" size={12}/></button>
<button className="mt-btn mt-btn--ghost">All medicines <Icon name="chevDown" size={12}/></button>
</div>
</div>
<div style={{padding:'18px', display:'grid', gridTemplateColumns:'2fr 1fr', gap: 20}}>
<svg viewBox="0 0 360 180" width="100%" height="180">
{SPENDING.map((s, i) => {
const max = Math.max(...SPENDING.map(x=>x.amount));
const h = (s.amount / max) * 140;
const x = 20 + i * 56;
return (
<g key={s.month}>
<rect x={x} y={160 - h} width="36" height={h} rx="3" fill="var(--brand)" opacity={0.3 + (i/SPENDING.length)*0.7}/>
<text x={x+18} y="175" textAnchor="middle" fontSize="9" fill="var(--ink-faint)">{s.month.slice(5)}</text>
</g>
);
})}
</svg>
<div className="mt-col">
<div><div style={{fontSize:10, color:'var(--ink-faint)', textTransform:'uppercase', letterSpacing:'.06em'}}>Total spent</div><div style={{fontFamily:'var(--font-display)', fontSize:28, fontWeight:500, color:'var(--ink-strong)'}} className="num">{fmtJPY(SPENDING.reduce((a,b)=>a+b.amount,0))}</div></div>
<div><div style={{fontSize:10, color:'var(--ink-faint)', textTransform:'uppercase', letterSpacing:'.06em'}}>Monthly average</div><div className="num" style={{fontSize:18, fontWeight:500}}>{fmtJPY(Math.round(SPENDING.reduce((a,b)=>a+b.amount,0)/SPENDING.length))}</div></div>
<div><div style={{fontSize:10, color:'var(--ink-faint)', textTransform:'uppercase', letterSpacing:'.06em'}}>Top medicine</div><div style={{fontSize:14, fontWeight:500}}>Semaglutide <span className="num" style={{color:'var(--ink-muted)'}}>{fmtJPY(56062)}</span></div></div>
</div>
</div>
</div>
<div className="mt-card">
<div className="mt-card__head">
<div>
<div className="mt-card__title">Cabinet activity</div>
<div className="mt-card__sub">{AC2.length} events</div>
</div>
<div className="mt-row" style={{gap:6}}>
<button className="mt-btn mt-btn--ghost">All types <Icon name="chevDown" size={12}/></button>
<button className="mt-btn mt-btn--ghost">All medicines <Icon name="chevDown" size={12}/></button>
</div>
</div>
<div style={{padding:'4px 18px 16px'}}>
{AC2.map(a => {
const m = M2[a.medId];
return (
<div key={a.id} className="act-row">
<span className="act-rail"><span className={'act-dot act-dot--'+a.type}/></span>
<span className={'mt-pill mt-pill--' + (a.type==='consumed'?'info':a.type==='added'?'ok':'warn')} style={{minWidth: 76, justifyContent:'center'}}>{a.type}</span>
<strong style={{fontSize:13, fontWeight:500, color:'var(--ink-strong)'}}>{m.name}</strong>
<span className="num" style={{color: a.delta < 0 ? 'var(--ink-muted)' : 'var(--ok)', fontSize: 13, fontWeight:500}}>{a.delta > 0?'+':''}{a.delta}</span>
<span className="num" style={{color:'var(--ink-faint)', fontSize:12}}>{a.before}{a.after}</span>
<span style={{flex:1}}/>
<span className="mono" style={{fontSize:11, color:'var(--ink-muted)'}}>{a.at}</span>
</div>
);
})}
</div>
</div>
</div>
);
};
// ---- Stores ----
const StoresPage = () => (
<div className="mt-page">
<div className="sub-toolbar">
<div style={{display:'flex', gap:8, alignItems:'center'}}>
<div className="cab-filters__search"><Icon name="search" size={13}/><input placeholder="Search stores…"/></div>
<button className="mt-btn mt-btn--ghost">All tags <Icon name="chevDown" size={12}/></button>
<label style={{display:'flex', gap:6, alignItems:'center', fontSize:12, color:'var(--ink-muted)'}}><input type="checkbox"/> Show inactive</label>
</div>
<button className="mt-btn mt-btn--primary"><Icon name="plus" size={12}/> Add store</button>
</div>
<div className="mt-grid" style={{gridTemplateColumns:'repeat(auto-fill, minmax(280px, 1fr))'}}>
{ST2.map(s => (
<div key={s.id} className="mt-card" style={{padding: 16}}>
<div style={{display:'flex', justifyContent:'space-between', alignItems:'flex-start'}}>
<div>
<div style={{fontWeight:500, fontSize:14, color:'var(--ink-strong)'}}>{s.name}</div>
<div style={{fontSize:11, color:'var(--brand)', marginTop:2}}>{s.url}</div>
</div>
<div style={{display:'flex', gap:4}}>
<button className="mt-btn mt-btn--subtle"><Icon name="edit" size={12}/></button>
</div>
</div>
<div style={{display:'flex', flexWrap:'wrap', gap: 4, marginTop: 10}}>
{s.tags.map(t => <span key={t} className="mt-pill mt-pill--ghost" style={{fontSize:10}}>{t}</span>)}
</div>
<div className="mt-divider"/>
<div style={{display:'flex', justifyContent:'space-between', fontSize:11, color:'var(--ink-muted)'}}>
<span>{PR2.filter(p=>p.store===s.id).length} prices tracked</span>
<span>{PU2.filter(p=>p.store===s.id).length} orders</span>
</div>
</div>
))}
</div>
</div>
);
// ---- Prices ----
const PricesPage = () => (
<div className="mt-page">
<div className="sub-toolbar">
<div style={{display:'flex', gap: 8}}>
<button className="mt-btn mt-btn--ghost">Semaglutide (14mg) <Icon name="chevDown" size={12}/></button>
<button className="mt-btn mt-btn--ghost">All stores <Icon name="chevDown" size={12}/></button>
</div>
<button className="mt-btn mt-btn--primary"><Icon name="plus" size={12}/> Record price</button>
</div>
<div className="mt-card" style={{marginBottom: 16}}>
<div className="mt-card__head"><div><div className="mt-card__title">Store comparison</div><div className="mt-card__sub">Best price highlighted</div></div></div>
<div className="prices-table">
<div className="prices-table__head">
<div>Store</div><div className="num">Price</div><div className="num">Per unit</div><div>Date</div>
</div>
{PR2.filter(p=>p.medId==='semaglutide').map(p => {
const s = ST2.find(s=>s.id===p.store);
const isBest = p.price/p.pkg === Math.min(...PR2.filter(x=>x.medId===p.medId).map(x=>x.price/x.pkg));
return (
<div key={p.store+p.date} className={'prices-table__row' + (isBest?' is-best':'')}>
<div style={{display:'flex', alignItems:'center', gap:8}}>
<span style={{fontWeight:500}}>{s?.name}</span>
{isBest && <span className="mt-pill mt-pill--ok" style={{fontSize:10}}>Cheapest</span>}
</div>
<div className="num">{fmtJPY(p.price)}</div>
<div className="num" style={{color:'var(--ink-muted)'}}>{fmtJPY(Math.round(p.price/p.pkg))}</div>
<div className="num" style={{color:'var(--ink-muted)'}}>{p.date}</div>
</div>
);
})}
</div>
</div>
<div className="mt-card">
<div className="mt-card__head"><div><div className="mt-card__title">All price records</div><div className="mt-card__sub">{PR2.length} entries</div></div></div>
<div className="prices-table">
<div className="prices-table__head" style={{gridTemplateColumns:'1.5fr 1fr 0.7fr 0.8fr 0.8fr 0.8fr'}}>
<div>Medicine</div><div>Store</div><div className="num">Qty</div><div className="num">Price</div><div className="num">Per unit</div><div>Date</div>
</div>
{PR2.map(p => {
const s = ST2.find(s=>s.id===p.store);
const m = M2[p.medId];
return (
<div key={p.medId+p.store+p.date} className="prices-table__row" style={{gridTemplateColumns:'1.5fr 1fr 0.7fr 0.8fr 0.8fr 0.8fr'}}>
<div style={{fontWeight:500}}>{m.name}</div>
<div>{s?.name}</div>
<div className="num">{p.pkg} {p.unit}</div>
<div className="num">{fmtJPY(p.price)}</div>
<div className="num" style={{color:'var(--ink-muted)'}}>{fmtJPY(Math.round(p.price/p.pkg))}</div>
<div className="num" style={{color:'var(--ink-muted)'}}>{p.date}</div>
</div>
);
})}
</div>
</div>
</div>
);
// ---- Refills ----
const RefillsPage = () => {
const low = C2.map(r => ({r, s: cabinetStatus(r)})).filter(x => x.s.level !== 'ok');
return (
<div className="mt-page">
<div className="mt-card" style={{marginBottom: 16}}>
<div className="mt-card__head">
<div>
<div className="mt-card__title">Refill alerts</div>
<div className="mt-card__sub">Threshold: 14 days</div>
</div>
<button className="mt-btn mt-btn--primary"><Icon name="plus" size={12}/> Generate refill list</button>
</div>
<div style={{padding:'4px 18px 16px'}}>
{low.map(({r, s}) => {
const m = M2[r.medId];
const bestPrice = PR2.filter(p=>p.medId===r.medId).sort((a,b)=> a.price/a.pkg - b.price/b.pkg)[0];
const bestStore = bestPrice && ST2.find(st=>st.id===bestPrice.store);
return (
<div key={r.medId} className="refill-row">
<div className="cab-card__swatch cab-card__swatch--sm" style={{'--c':'var(--viz-'+m.color+')'}}>
<Icon name={m.form === 'Injection' ? 'injection' : m.form === 'Capsule' ? 'capsule' : 'pill'} size={12}/>
</div>
<div style={{flex:1}}>
<div style={{fontWeight:500, color:'var(--ink-strong)'}}>{m.name} <span style={{color:'var(--ink-muted)', fontWeight:400}}>{m.strength}</span></div>
<div style={{fontSize:11, color: s.level==='critical' ? 'var(--danger)':'var(--warn)', fontWeight:500}}>{s.days} days left · {r.qty} {r.unit} in cabinet</div>
</div>
<div style={{textAlign:'right'}}>
<div style={{fontSize:11, color:'var(--ink-faint)'}}>Suggested</div>
<div className="num" style={{fontWeight:500}}>{bestPrice?.pkg || 30} units</div>
</div>
<div style={{textAlign:'right', minWidth: 140}}>
<div style={{fontSize:11, color:'var(--ink-faint)'}}>Best price</div>
<div style={{fontSize:12, fontWeight:500, color:'var(--brand)'}}>{bestStore?.name} · <span className="num">{bestPrice ? fmtJPY(bestPrice.price) : '—'}</span></div>
</div>
<button className="mt-btn mt-btn--primary">Add to list</button>
</div>
);
})}
</div>
</div>
<div className="mt-card">
<div className="mt-card__head">
<div><div className="mt-card__title">Refill lists</div><div className="mt-card__sub">Create from alerts or start blank</div></div>
<button className="mt-btn mt-btn--ghost"><Icon name="plus" size={12}/> New list</button>
</div>
<div style={{padding:'20px 18px', color:'var(--ink-muted)', fontSize:12, textAlign:'center'}}>No refill lists yet. Create one above or generate from alerts.</div>
</div>
</div>
);
};
// ---- Purchases ----
const PurchasesPage = () => (
<div className="mt-page">
<div className="sub-toolbar">
<div style={{display:'flex', gap:8}}>
<button className="mt-btn mt-btn--ghost">All statuses <Icon name="chevDown" size={12}/></button>
</div>
<button className="mt-btn mt-btn--primary"><Icon name="plus" size={12}/> Record purchase</button>
</div>
{['pending', 'received'].map(status => {
const items = PU2.filter(p => p.status === status);
if (!items.length) return null;
return (
<div key={status} style={{marginBottom: 20}}>
<div style={{fontSize:11, textTransform:'uppercase', letterSpacing:'.08em', color:'var(--ink-muted)', fontWeight:600, marginBottom: 8}}>{status === 'pending' ? 'Pending arrival' : 'Received'}</div>
<div className="mt-col">
{items.map(p => {
const s = ST2.find(s => s.id === p.store);
const total = p.items.reduce((a,i) => a + i.price, 0);
return (
<div key={p.id} className="mt-card" style={{padding: 16}}>
<div style={{display:'flex', justifyContent:'space-between', alignItems:'flex-start', marginBottom: 10}}>
<div>
<div style={{fontWeight:500, fontSize:14, color:'var(--ink-strong)'}}>{s?.name}</div>
<div className="num" style={{fontSize:11, color:'var(--ink-muted)'}}>{p.date}</div>
</div>
<span className={'mt-pill mt-pill--' + (status==='pending'?'warn':'ok')}>{status}</span>
</div>
<div style={{display:'flex', flexDirection:'column', gap:6, padding: '10px 0', borderTop:'1px dashed var(--border)'}}>
{p.items.map(i => {
const m = M2[i.medId];
return (
<div key={i.medId} style={{display:'flex', justifyContent:'space-between', fontSize:12}}>
<span>{m.name}</span>
<span style={{color:'var(--ink-muted)'}} className="num">{i.qty} {i.unit} · {fmtJPY(i.price)}</span>
</div>
);
})}
</div>
<div style={{display:'flex', justifyContent:'space-between', alignItems:'center', paddingTop: 10, borderTop:'1px solid var(--border)'}}>
<div className="num" style={{fontSize:15, fontWeight:600}}>{fmtJPY(total)}</div>
{status === 'pending' && <div style={{display:'flex', gap:6}}>
<button className="mt-btn mt-btn--primary">Mark as received</button>
<button className="mt-btn mt-btn--ghost">Cancel</button>
</div>}
</div>
</div>
);
})}
</div>
</div>
);
})}
</div>
);
// ---- Settings ----
const SettingsPage = () => (
<div className="mt-page" style={{maxWidth: 720}}>
<div className="mt-card" style={{marginBottom: 16}}>
<div className="mt-card__head"><div className="mt-card__title">Household</div></div>
<div style={{padding: 18}}>
<div className="settings-row"><span>Name</span><span style={{fontWeight:500}}>Red Panda Den</span><button className="mt-btn mt-btn--ghost">Edit</button></div>
<div className="settings-row"><span>Invite code</span><span className="mono">2496F-7CF</span><button className="mt-btn mt-btn--ghost">Regenerate</button></div>
<div className="settings-row"><span>Members</span><span>1</span><button className="mt-btn mt-btn--ghost">Invite</button></div>
</div>
</div>
<div className="mt-card" style={{marginBottom: 16}}>
<div className="mt-card__head"><div className="mt-card__title">Account</div></div>
<div style={{padding: 18}}>
<p style={{fontSize:12, color:'var(--ink-muted)'}}>Account settings are managed through Keycloak.</p>
<button className="mt-btn mt-btn--ghost">Manage Keycloak account</button>
</div>
</div>
</div>
);
Object.assign(window, { LibraryPage, RegimensPage, OrganizerPage, ActivityPage, StoresPage, PricesPage, RefillsPage, PurchasesPage, SettingsPage });