Implement stores and refills, improve testing
This commit is contained in:
parent
9f416903ef
commit
5536acd67d
137 changed files with 21218 additions and 221 deletions
144
docs/design/MeshiTrack.html
Normal file
144
docs/design/MeshiTrack.html
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>MeshiTrack</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600&family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"/>
|
||||
<link rel="stylesheet" href="styles/tokens.css"/>
|
||||
<link rel="stylesheet" href="styles/shell.css"/>
|
||||
<link rel="stylesheet" href="styles/cabinet.css"/>
|
||||
<link rel="stylesheet" href="styles/dashboard.css"/>
|
||||
<link rel="stylesheet" href="styles/schedule.css?v=2"/>
|
||||
<link rel="stylesheet" href="styles/other.css?v=2"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
||||
|
||||
<script type="text/babel" src="src/data.jsx"></script>
|
||||
<script type="text/babel" src="src/Icon.jsx"></script>
|
||||
<script type="text/babel" src="src/Shell.jsx"></script>
|
||||
<script type="text/babel" src="src/Cabinet.jsx"></script>
|
||||
<script type="text/babel" src="src/Dashboard.jsx"></script>
|
||||
<script type="text/babel" src="src/Schedule.jsx"></script>
|
||||
<script type="text/babel" src="src/OtherPages.jsx"></script>
|
||||
|
||||
<script type="text/babel">
|
||||
const { useState, useEffect } = React;
|
||||
|
||||
const TWEAKS = /*EDITMODE-BEGIN*/{
|
||||
"theme": "light",
|
||||
"accent": "sage"
|
||||
}/*EDITMODE-END*/;
|
||||
|
||||
const ACCENTS = {
|
||||
sage: { brand: '#2f6b4a', deep: '#1e4a32', soft: '#e6efe8' },
|
||||
cobalt: { brand: '#2e5aa8', deep: '#1d3d75', soft: '#e4eaf5' },
|
||||
terracotta: { brand: '#b55438', deep: '#7d3825', soft: '#f6e6de' },
|
||||
graphite: { brand: '#2c2c28', deep: '#000', soft: '#e8e6df' },
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
const [route, setRoute] = useState('dashboard');
|
||||
const [theme, setTheme] = useState(TWEAKS.theme || 'light');
|
||||
const [accent, setAccent] = useState(TWEAKS.accent || 'sage');
|
||||
const [tweaksOpen, setTweaksOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
const a = ACCENTS[accent] || ACCENTS.sage;
|
||||
document.documentElement.style.setProperty('--brand', a.brand);
|
||||
document.documentElement.style.setProperty('--brand-deep', a.deep);
|
||||
document.documentElement.style.setProperty('--brand-soft', a.soft);
|
||||
document.documentElement.style.setProperty('--brand-soft-ink', a.deep);
|
||||
document.documentElement.style.setProperty('--viz-1', a.brand);
|
||||
}, [theme, accent]);
|
||||
|
||||
useEffect(() => {
|
||||
const onMsg = (e) => {
|
||||
if (!e.data || typeof e.data !== 'object') return;
|
||||
if (e.data.type === '__activate_edit_mode') setTweaksOpen(true);
|
||||
if (e.data.type === '__deactivate_edit_mode') setTweaksOpen(false);
|
||||
};
|
||||
window.addEventListener('message', onMsg);
|
||||
window.parent.postMessage({ type: '__edit_mode_available' }, '*');
|
||||
return () => window.removeEventListener('message', onMsg);
|
||||
}, []);
|
||||
|
||||
const setTweak = (key, val) => {
|
||||
window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { [key]: val } }, '*');
|
||||
};
|
||||
|
||||
const { Sidebar, Topbar, NAV } = window.Shell;
|
||||
const current = NAV.find(n => n.id === route);
|
||||
|
||||
const pages = {
|
||||
dashboard: { title: 'Dashboard', subtitle: 'Household: Red Panda Den', Page: window.DashboardPage },
|
||||
cabinet: { title: 'Medicine Cabinet', subtitle: 'Everything on hand, with days of supply', Page: window.CabinetPage },
|
||||
schedule: { title: 'Schedule & Log', subtitle: 'Today and this week', Page: window.SchedulePage },
|
||||
regimens: { title: 'Regimens', subtitle: 'Daily medication schedules', crumbs: ['Medicines', 'Regimens'], Page: window.RegimensPage },
|
||||
organizer: { title: 'Pill Organizer', subtitle: 'Fill a week of pills at once', crumbs: ['Medicines', 'Organizer'], Page: window.OrganizerPage },
|
||||
activity: { title: 'Activity', subtitle: 'Spending and cabinet changes', crumbs: ['Medicines', 'Activity'], Page: window.ActivityPage },
|
||||
library: { title: 'Library', subtitle: 'All known medicines', crumbs: ['Medicines', 'Library'], Page: window.LibraryPage },
|
||||
refills: { title: 'Refills', subtitle: 'Running-low alerts and shopping lists', Page: window.RefillsPage },
|
||||
purchases: { title: 'Purchases', subtitle: 'Order history and pending arrivals', Page: window.PurchasesPage },
|
||||
prices: { title: 'Prices', subtitle: 'Track & compare across stores', Page: window.PricesPage },
|
||||
stores: { title: 'Stores', subtitle: 'Pharmacies and vendors', Page: window.StoresPage },
|
||||
settings: { title: 'Settings', subtitle: 'Household and account', Page: window.SettingsPage },
|
||||
};
|
||||
|
||||
const pg = pages[route];
|
||||
const Page = pg.Page;
|
||||
|
||||
return (
|
||||
<div className="mt-app">
|
||||
<Sidebar route={route} onNav={setRoute} />
|
||||
<div className="mt-main" data-screen-label={'MT / ' + pg.title}>
|
||||
<Topbar
|
||||
title={pg.title}
|
||||
subtitle={pg.subtitle}
|
||||
crumbs={pg.crumbs}
|
||||
theme={theme}
|
||||
onToggleTheme={() => { const t = theme === 'light' ? 'dark' : 'light'; setTheme(t); setTweak('theme', t); }}
|
||||
actions={route === 'cabinet' ? <button className="mt-btn mt-btn--primary"><span>+</span> Add to cabinet</button> : null}
|
||||
/>
|
||||
<Page onNav={setRoute} />
|
||||
</div>
|
||||
|
||||
<div className={'mt-tweaks' + (tweaksOpen ? ' is-open' : '')}>
|
||||
<div className="mt-tweaks__title">
|
||||
<span>Tweaks</span>
|
||||
<button className="mt-btn mt-btn--subtle" onClick={() => setTweaksOpen(false)} style={{padding:'2px 6px'}}>×</button>
|
||||
</div>
|
||||
<div className="mt-tweaks__row">
|
||||
<span>Theme</span>
|
||||
<div className="mt-seg">
|
||||
<button className={theme==='light'?'is-active':''} onClick={()=>{setTheme('light'); setTweak('theme','light');}}>Light</button>
|
||||
<button className={theme==='dark'?'is-active':''} onClick={()=>{setTheme('dark'); setTweak('theme','dark');}}>Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-tweaks__row">
|
||||
<span>Accent</span>
|
||||
<div className="mt-seg">
|
||||
{Object.keys(ACCENTS).map(k => (
|
||||
<button key={k} className={accent===k?'is-active':''} onClick={()=>{setAccent(k); setTweak('accent',k);}} style={{textTransform:'capitalize'}}>{k}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{fontSize: 10, color: 'var(--ink-faint)', marginTop: 8, lineHeight: 1.5}}>
|
||||
See alt-directions canvas for 3 novel cabinet layouts.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue