/* Banyan Pharmacy — app shell, routing, chrome, the guided loop */
const { useState, useEffect, useRef, useMemo, createContext, useContext } = React;
const D = window.DATA;
const { cx, Icons, Glyph, TerraceLogo, Avatar, Drawer, useClickOutside } = window;
const AppCtx = createContext(null);
window.AppCtx = AppCtx;
const useApp = () => useContext(AppCtx);
window.useApp = useApp;
const SETTLED = 99; // settled (normal browsing) stage — everything flipped
/* shelf-health of a store at a given loop stage (Gariahat & Mukundapur rewind) */
function storeStatusAt(store, stage) {
if (store.loopStore === "gariahat") return stage < 1 ? "watch" : "atrisk";
if (store.loopStore === "mukundapur") return stage < 4 ? "watch" : "critical";
return store.status;
}
window.storeStatusAt = storeStatusAt;
/* alerts visible at a given stage, sorted by urgency */
function alertsAt(stage) {
return D.alerts.filter((a) => stage >= a.loopStage).sort((a, b) => a.sort - b.sort);
}
window.alertsAt = alertsAt;
/* ============ Chrome ============ */
function Header() {
const app = useApp();
const [menu, setMenu] = useState(false);
const ref = useRef();
useClickOutside(ref, () => setMenu(false));
return (
{menu && (
{D.SESSION.name}
{D.SESSION.email}
)}
);
}
const NAV = [
{ name: "today", label: "Dashboard" },
{ name: "stores", label: "Stores" },
{ name: "suppliers", label: "Suppliers" },
{ name: "alerts", label: "Alerts" },
{ name: "pilots", label: "Pilots" },
];
function Nav() {
const app = useApp();
const cur = app.route.name === "store" ? "stores" : app.route.name === "supplier" ? "suppliers" : app.route.name;
const alertCount = alertsAt(app.stage).length;
return (
);
}
/* ============ The Field, docked (the "same event, both levels" moment) ===== */
if (!document.getElementById("field-dock-css")) {
const _fds = document.createElement("style");
_fds.id = "field-dock-css";
_fds.textContent = `
.field-dock { position: fixed; right: 22px; bottom: 116px; z-index: 60; width: 272px;
display: flex; flex-direction: column; gap: 0;
animation: fd-in 420ms cubic-bezier(0.2, 0, 0, 1); }
@keyframes fd-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.field-dock .fd-head { display: flex; align-items: center; gap: 8px; padding: 0 4px 7px; }
.field-dock .fd-lbl { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.16em; color: var(--mute); }
.field-dock .fd-dot { width: 6px; height: 6px; border-radius: 999px; background: var(--clay); flex-shrink: 0; }
.field-dock .fd-x { margin-left: auto; width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;
background: transparent; border: 0; color: var(--mute); cursor: pointer; font-size: 15px; line-height: 1; border-radius: 4px; }
.field-dock .fd-x:hover { color: var(--ink); background: var(--stone-deep); }
.field-dock iframe { width: 272px; height: 583px; border: 0; display: block; background: transparent;
filter: drop-shadow(0 24px 50px rgba(26,26,26,0.35)); }
@media (max-height: 820px) { .field-dock iframe { height: 56vh; width: calc(56vh * 0.4667); } .field-dock { width: auto; } }
@media (max-width: 900px) { .field-dock { display: none; } }`;
document.head.appendChild(_fds);
}
function FieldDock() {
const app = useApp();
const dock = app.loopActive ? D.LOOP[app.loopIdx].dock : null;
const [hiddenFor, setHiddenFor] = useState(null);
if (!dock) return null;
const key = dock.thread + "·" + dock.state;
if (hiddenFor === key) return null;
return (
The Field · live
);
}
/* ============ The guided loop ============ */
function LoopBar() {
const app = useApp();
if (!app.loopActive) {
return (
);
}
const step = D.LOOP[app.loopIdx];
const last = app.loopIdx === D.LOOP.length - 1;
return (
{D.LOOP.map((_, i) => )}
{String(app.loopIdx + 1).padStart(2, "0")} / {String(D.LOOP.length).padStart(2, "0")}{step.kicker}
{step.title}
{step.body}
{step.field && (
Open Field
)}
{last
?
:
}
);
}
/* ============ App ============ */
function App() {
const [route, setRoute] = useState({ name: "today" });
const [loopActive, setLoopActive] = useState(false);
const [loopIdx, setLoopIdx] = useState(0);
const stage = loopActive ? D.LOOP[loopIdx].stage : SETTLED;
const loopFocus = loopActive ? D.LOOP[loopIdx].focus : null;
const goToStep = (idx) => {
const step = D.LOOP[idx];
setLoopIdx(idx);
setRoute(step.route);
window.scrollTo({ top: 0, behavior: "smooth" });
};
const app = {
route, stage, loopActive, loopIdx, loopFocus,
nav: (r) => { setRoute(r); window.scrollTo(0, 0); },
startLoop: () => { setLoopActive(true); setLoopIdx(0); setRoute(D.LOOP[0].route); window.scrollTo(0, 0); },
stopLoop: () => { setLoopActive(false); setRoute({ name: "today" }); window.scrollTo(0, 0); },
loopNext: () => goToStep(Math.min(loopIdx + 1, D.LOOP.length - 1)),
loopPrev: () => goToStep(Math.max(loopIdx - 1, 0)),
};
const Page = {
today: window.Pages.Today,
stores: window.Pages.Stores,
store: window.Pages.StoreDetail,
suppliers: window.Pages.Suppliers,
supplier: window.Pages.SupplierDetail,
alerts: window.Pages.Alerts,
pilots: window.Pages.Pilots,
}[route.name] || window.Pages.Today;
return (
);
}
window.__mountApp = () => ReactDOM.createRoot(document.getElementById("root")).render();