// ================= Brand detail screen ================= const BrandDetailScreen = ({ brandCode, setView, tweaks, openContext, onMounted }) => { const brand = window.BRANDS.find(b => b.code === brandCode); const [tab, setTab] = useStateS("overview"); const [, setTick] = useStateS(0); const [loadingDetail, setLoadingDetail] = useStateS(false); useEffectS(() => { if (!brand) return; if ((brand.accounts || []).length > 0) return; if (!window.__fabcomLoadBrandDetail) return; setLoadingDetail(true); window.__fabcomLoadBrandDetail(brandCode) .then(() => { setLoadingDetail(false); setTick(x => x + 1); onMounted && onMounted(); }) .catch(() => setLoadingDetail(false)); }, [brandCode]); if (!brand) return
Brand not found or no access.
; const accounts = brand.accounts || []; const allCampaigns = accounts.flatMap(a => a.campaigns || []); const brandNonMediaAll = window.NON_MEDIA.filter(r => r.brand_code === brand.code); const nmClientTotal = brandNonMediaAll.reduce((s, r) => s + r.client_cost_with_ac, 0); const platSplit = useMemoS(() => { const map = {}; for (const a of accounts) { const spend = a.campaigns.reduce((s, c) => s + c.mtd_spend, 0); map[a.platform] = (map[a.platform] || 0) + spend; } const palette = { meta: "#1877f2", google: "#ea4335", dv360: "#34a853", tiktok: "#b5b8c0", ttd: "#ff6e00", youtube: "#ff0000" }; return Object.entries(map).map(([k, v]) => ({ label: k, value: v, color: palette[k] })); }, [brand.code]); const paceTone = brand.pace_status; return (
{/* Sticky brand header */}
{brand.code}

{brand.name}

{brand.industry} {brand.code}
AM {brand.am} Team Lead {brand.tl} Platforms {brand.platforms.map(p => )}
Media MTD
{window.fmt.hkdK(brand.mtd_spend)}
of {window.fmt.hkdK(brand.plan_mtd)}
Non-media MTD
{window.fmt.hkdK(nmClientTotal)}
{brandNonMediaAll.length} records
MTD pace
{(brand.pace * 100).toFixed(0)}%
Total {window.fmt.hkdK(brand.mtd_spend + nmClientTotal)}
{tab === "overview" && (() => { const brandNonMedia = window.NON_MEDIA.filter(r => r.brand_code === brand.code); const nmClient = brandNonMedia.reduce((s, r) => s + r.client_cost_with_ac, 0); const nmGP = brandNonMedia.reduce((s, r) => s + r.gross_profit, 0); const nmOverdue = brandNonMedia.filter(r => r.invoice_status === "Overdue").length; const totalInvestment = brand.mtd_spend + nmClient; return (

Spend by platform · MTD

{platSplit.map(it => (
{it.label} {window.fmt.hkdK(it.value)}
))}

Daily spend · last 30 days

brand.plan_mtd / 30) }, ]} />
{/* Media vs Non-media split */}

Media vs non-media · MTD

{window.fmt.hkdK(totalInvestment)} total client investment
Media {window.fmt.hkdK(brand.mtd_spend)} Non-media {window.fmt.hkdK(nmClient)}
Non-media GP{window.fmt.hkdK(nmGP)}
Records{brandNonMedia.length}
Overdue 0 ? "var(--danger)" : "var(--text)" }}>{nmOverdue}
{/* Recent non-media records */}

Recent non-media records

retainers · production · agency fees · vendor PO
{r.vendor_name} }, { key: "service_type", header: "Service", width: 130, render: r => {r.service_type} }, { key: "period", header: "Period", width: 160, render: r => {r.start_date} → {r.end_date} }, { key: "client_cost_with_ac", header: "Client cost", right: true, num: true, width: 110, render: r => window.fmt.hkdK(r.client_cost_with_ac) }, { key: "gross_profit", header: "GP", right: true, num: true, width: 90, render: r => {window.fmt.hkdK(r.gross_profit)} }, { key: "invoice_status", header: "Invoice", width: 100, render: r => {r.invoice_status} }, { key: "workflow_state", header: "Workflow", width: 130, render: r => {r.workflow_state} }, ]} rows={brandNonMedia.slice(0, 5)} striped={tweaks.tableStyle === "striped"} lined={tweaks.tableStyle === "lined"} />

Top campaigns by MTD spend

click a row to drill
}, { key: "name", header: "Campaign", render: r => {r.name} }, { key: "status", header: "", width: 80, render: r => }, { key: "mtd_spend", header: "MTD spend", right: true, num: true, width: 110, render: r => window.fmt.hkdK(r.mtd_spend) }, { key: "impr", header: "Impr.", right: true, num: true, width: 100, render: r => window.fmt.int(r.impressions) }, { key: "ctr", header: "CTR", right: true, num: true, width: 70, render: r => window.fmt.pct(r.ctr, 2) }, { key: "cpc", header: "CPC", right: true, num: true, width: 70, render: r => window.fmt.money2(r.cpc) }, { key: "spark", header: "Trend", width: 100, render: r => }, ]} rows={allCampaigns.slice().sort((a,b) => b.mtd_spend - a.mtd_spend).slice(0, 8)} striped={tweaks.tableStyle === "striped"} lined={tweaks.tableStyle === "lined"} onRowClick={(r) => setView({ name: "campaign", brand: brand.code, account: r.account_id, campaign: r.id })} onRowContext={(r, e) => openContext(e, r, "campaign")} /> ); })()} {tab === "accounts" && (
}, { key: "name", header: "Account", render: r =>
{r.name}
{r.id}
}, { key: "daily_budget_hkd", header: "Daily budget", right: true, num: true, width: 110, render: r => window.fmt.hkd(r.daily_budget_hkd) }, { key: "today_spend", header: "Today", right: true, num: true, width: 100, render: r => window.fmt.hkdK(r.today_spend) }, { key: "spend_7d", header: "7d", right: true, num: true, width: 100, render: r => window.fmt.hkdK(r.spend_7d) }, { key: "spend_30d", header: "30d", right: true, num: true, width: 100, render: r => window.fmt.hkdK(r.spend_30d) }, { key: "ctr", header: "CTR", right: true, num: true, width: 70, render: r => window.fmt.pct(r.ctr, 2) }, { key: "cpc", header: "CPC", right: true, num: true, width: 70, render: r => window.fmt.money2(r.cpc) }, { key: "cpa", header: "CPA", right: true, num: true, width: 80, render: r => window.fmt.money2(r.cpa) }, { key: "roas", header: "ROAS", right: true, num: true, width: 70, render: r => r.roas.toFixed(2) + "x" }, ]} rows={accounts} onRowClick={(r) => setView({ name: "account", brand: brand.code, account: r.id })} onRowContext={(r, e) => openContext(e, r, "account")} striped={tweaks.tableStyle === "striped"} lined={tweaks.tableStyle === "lined"} /> )} {tab === "campaigns" && (
}, { key: "name", header: "Campaign", render: r => {r.name} }, { key: "status", header: "Status", width: 90, render: r => }, { key: "daily_budget", header: "Daily", right: true, num: true, width: 90, render: r => window.fmt.hkd(r.daily_budget) }, { key: "mtd_spend", header: "MTD", right: true, num: true, width: 100, render: r => window.fmt.hkdK(r.mtd_spend) }, { key: "impressions", header: "Impr.", right: true, num: true, width: 100, render: r => window.fmt.int(r.impressions) }, { key: "clicks", header: "Clicks", right: true, num: true, width: 90, render: r => window.fmt.int(r.clicks) }, { key: "ctr", header: "CTR", right: true, num: true, width: 70, render: r => window.fmt.pct(r.ctr, 2) }, { key: "cpm", header: "CPM", right: true, num: true, width: 80, render: r => window.fmt.money2(r.cpm) }, { key: "cpc", header: "CPC", right: true, num: true, width: 70, render: r => window.fmt.money2(r.cpc) }, { key: "conv", header: "Conv.", right: true, num: true, width: 70, render: r => window.fmt.int(r.conv) }, { key: "cpa", header: "CPA", right: true, num: true, width: 80, render: r => window.fmt.money2(r.cpa) }, ]} rows={allCampaigns} onRowClick={(r) => setView({ name: "campaign", brand: brand.code, account: r.account_id, campaign: r.id })} onRowContext={(r, e) => openContext(e, r, "campaign")} striped={tweaks.tableStyle === "striped"} lined={tweaks.tableStyle === "lined"} /> )} {tab === "pacing" && (
}, { key: "name", header: "Campaign", render: r => {r.name} }, { key: "flight", header: "Flight", width: 110, render: r => {r.days_elapsed}/{r.flight_days}d }, { key: "plan", header: "Plan", right: true, num: true, width: 110, render: r => window.fmt.hkdK(r.plan_budget) }, { key: "spend", header: "Spent", right: true, num: true, width: 110, render: r => window.fmt.hkdK(r.spend) }, { key: "pace", header: "Pace", width: 170, render: r => { const p = r.spend / Math.max(1, r.plan_budget * (r.days_elapsed / r.flight_days)); const t = p > 1.2 ? "danger" : p < 0.7 ? "warn" : "success"; return
{(p * 100).toFixed(0)}%
; } }, { key: "forecast", header: "EOM forecast", right: true, num: true, width: 120, render: r => window.fmt.hkdK(r.spend / Math.max(1, r.days_elapsed) * r.flight_days) }, { key: "var", header: "Variance", right: true, num: true, width: 90, render: r => { const f = r.spend / Math.max(1, r.days_elapsed) * r.flight_days; const v = (f - r.plan_budget) / Math.max(1, r.plan_budget); return 0.2 ? "var(--danger)" : v < -0.2 ? "var(--warn)" : "var(--text-2)" }}>{window.fmt.pctDelta(v)}; } }, ]} rows={allCampaigns} onRowClick={(r) => setView({ name: "campaign", brand: brand.code, account: r.account_id, campaign: r.id })} onRowContext={(r, e) => openContext(e, r, "campaign")} striped={tweaks.tableStyle === "striped"} lined={tweaks.tableStyle === "lined"} /> )} {tab === "nonmedia" && (
r.vendor_name }, { key: "service_type", header: "Service", render: r => {r.service_type} }, { key: "campaign_name", header: "Campaign", render: r => {r.campaign_name} }, { key: "period", header: "Period", width: 170, render: r => {r.start_date} → {r.end_date} }, { key: "net_vendor_cost", header: "Net vendor", right: true, num: true, width: 110, render: r => window.fmt.hkdK(r.net_vendor_cost) }, { key: "client_cost_with_ac", header: "Client cost", right: true, num: true, width: 110, render: r => window.fmt.hkdK(r.client_cost_with_ac) }, { key: "gross_profit", header: "GP", right: true, num: true, width: 100, render: r => {window.fmt.hkdK(r.gross_profit)} }, { key: "invoice_status", header: "Invoice", width: 110, render: r => {r.invoice_status} }, { key: "workflow_state", header: "Workflow", width: 130, render: r => {r.workflow_state} }, ]} rows={window.NON_MEDIA.filter(r => r.brand_code === brand.code)} striped={tweaks.tableStyle === "striped"} lined={tweaks.tableStyle === "lined"} /> )} {tab === "changes" && (
{window.CHANGES.filter(c => c.brand_code === brand.code).slice(0, 24).map(c => (
{c.actor.slice(0, 2).toUpperCase()}
{c.actor} {c.action} {c.campaign}
{c.prev}{c.next} · {c.platform.toUpperCase()} · {c.account}
{window.fmt.timeAgo(c.hours_ago)}
))}
)} {tab === "creatives" && (
{Array.from({ length: 18 }).map((_, i) => { const c = allCampaigns[i % allCampaigns.length]; const palette = ["#1a2340", "#0ea5a0", "#e5e8ee", "#ff6e00", "#8b5cf6", "#ec4899"]; const bg = palette[i % palette.length]; return (
{brand.code}_AD_{String(i+1).padStart(3, "0")}
Spend{window.fmt.hkdK(randCr(i))}
CTR{(0.8 + (i%10)*0.15).toFixed(2)}%
ROAS{(1.2 + (i%9)*0.3).toFixed(2)}x
); })}
)}
); }; const randCr = (i) => 3000 + (i * 2213) % 48000; window.BrandDetailScreen = BrandDetailScreen;