/* Shared primitives for fake-app mocks inside the critique doc. */
const { useEffect, useRef, useState } = React;
/* --- Scaled frame: renders a 1280px-wide fake-app and scales to fit its box --- */
function Scaled({ width = 1280, height = 720, children }) {
const ref = useRef(null);
const [scale, setScale] = useState(1);
useEffect(() => {
const el = ref.current;
if (!el) return;
const ro = new ResizeObserver(() => {
const w = el.clientWidth;
setScale(w / width);
});
ro.observe(el);
return () => ro.disconnect();
}, [width]);
return (
);
}
/* --- Before/After stage --- */
function Stage({ beforeLabel = 'Current', afterLabel = 'Proposed', before, after, delta }) {
return (
<>
Before · {beforeLabel}
as shipped
{before}
After · {afterLabel}
proposed
{after}
{delta && (
What's wrong
{delta.wrong.map((d, i) => - {d}
)}
What changed
{delta.change.map((d, i) => - {d}
)}
)}
>
);
}
/* --- Fake Sidebar (before = current Bootstrap) --- */
function SidebarBefore() {
const items = [
['Main Menu', null],
['bi-house-door', 'Dashboard', true],
['Operations', null],
['bi-people', 'Customers'],
['bi-file-text', 'Quotes'],
['bi-briefcase', 'Jobs'],
['bi-receipt', 'Invoices'],
['bi-calendar-event', 'Appointments'],
['bi-clipboard2-check', 'Daily Board'],
['Inventory & Purchasing', null],
['bi-book', 'Product Catalog'],
['bi-box-seam', 'Inventory'],
['bi-truck', 'Vendors'],
];
return (
);
}
/* --- Fake Sidebar (after) --- */
function SidebarAfter() {
const sections = [
{ title: 'Today',
items: [
{ icon: 'bi-house-door', label: 'Overview', active: true, badge: null },
{ icon: 'bi-clipboard2-check', label: 'Daily board', badge: '12' },
{ icon: 'bi-fire', label: 'Oven schedule' },
]
},
{ title: 'Pipeline',
items: [
{ icon: 'bi-people', label: 'Customers' },
{ icon: 'bi-file-text', label: 'Quotes', badge: '3' },
{ icon: 'bi-briefcase', label: 'Jobs', badge: '47' },
{ icon: 'bi-receipt', label: 'Invoices' },
{ icon: 'bi-calendar-event', label: 'Appointments' },
]
},
{ title: 'Stock',
items: [
{ icon: 'bi-book', label: 'Catalog' },
{ icon: 'bi-box-seam', label: 'Inventory' },
{ icon: 'bi-truck', label: 'Vendors' },
]
},
];
return (
);
}
/* --- Fake topbar (before) --- */
function TopbarBefore({ title = 'Dashboard' }) {
return (
);
}
/* --- Fake topbar (after) --- */
function TopbarAfter({ title = 'Overview', breadcrumbs = ['Today', 'Overview'] }) {
return (
{breadcrumbs.map((b, i) => (
{i > 0 && /}
{b}
))}
{title}
);
}
const topBtn = {
display: 'flex', alignItems: 'center', gap: 6,
padding: '7px 12px', borderRadius: 6, border: 'none',
background: 'var(--ink)', color: 'var(--paper)',
fontSize: 13, fontWeight: 500, cursor: 'pointer',
};
const ghostBtn = {
width: 32, height: 32, borderRadius: 6, border: '1px solid var(--rule)',
background: 'var(--card)', color: 'var(--slate)',
display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
};
Object.assign(window, {
Scaled, Stage,
SidebarBefore, SidebarAfter,
TopbarBefore, TopbarAfter,
});