/* Shared navigation for the live, advisory competitor-pricing workspace. */
function CompetitorPricingNav() {
const { screen, screenState = {}, setScreen } = window.useOffice();
const active = screen === "priceDetail" ? screenState.priceFrom || "pricing" : screen;
return ;
}
function CompetitorPricingHeader({copyRows} = {}) {
const { stores = [], activeStoreId } = window.useOffice();
const store = stores.find(s => s.id === activeStoreId);
const [capture, setCapture] = React.useState(false);
const [copying, setCopying] = React.useState(false);
const [message, setMessage] = React.useState(null);
const [copyText, setCopyText] = React.useState(null);
const refresh = () => window.dispatchEvent(new Event("office-pricing-refresh"));
const copy = async () => {
setCopying(true); setMessage(null);
try {
const data = copyRows?.length ? {rows:copyRows,generatedAt:new Date().toISOString()} : await window.OfficeAPI.pricingQueue();
const clean = v => String(v == null ? "" : v).replace(/[\t\r\n]/g," ");
const text = [["Advisory recommendations", "Generated", data.generatedAt], ["Product","SKU","Current ($)","Recommended ($)","Reason"], ...data.rows.map(r => [r.name,r.sku,r.currentCents == null ? "" : (r.currentCents/100).toFixed(2),r.recommendedCents == null ? "" : (r.recommendedCents/100).toFixed(2),r.reasonDetail])].map(row => row.map(clean).join("\t")).join("\n");
if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(text); setMessage("Copied " + data.rows.length + " advisory row" + (data.rows.length===1 ? "" : "s") + ". Shelf prices are unchanged."); }
else { setCopyText(text); }
} catch (e) { setMessage(e.message || "Couldn't copy the pricing list."); }
finally { setCopying(false); }
};
return <>
Observed prices in, recommendations out. Each store runs its own method, and every number carries the working behind it.Competitor pricing
Enter a price you have checked. The existing automated feed runs separately.