144 lines
6.9 KiB
HTML
144 lines
6.9 KiB
HTML
<!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>
|