// KPI Detail Screen — full page with deep detail per agreed KPI const KPI_DETAIL_DATA = { bookings: { history: [ { date: 'Apr 1', actual: 14, target: 16 }, { date: 'Apr 8', actual: 22, target: 18 }, { date: 'Apr 15', actual: 19, target: 20 }, { date: 'Apr 22', actual: 38, target: 22 }, { date: 'Apr 29', actual: 96, target: 24 }, { date: 'May 6', actual: 31, target: 26 }, { date: 'May 13', actual: 28, target: 28 }, { date: 'May 20', actual: 34, target: 30 }, { date: 'May 27', actual: 36, target: 30 }, { date: 'Jun 3', actual: 41, target: 32 }, { date: 'Jun 10', actual: 53, target: 32 }, { date: 'Jun 17', actual: 0, target: 32, forecast: 36 }, { date: 'Jun 24', actual: 0, target: 30, forecast: 38 }, ], breakdown: [ { label: 'Direct (organic + brand)', value: 168, share: 41, color: '#10b981' }, { label: 'Google Ads', value: 122, share: 30, color: '#2563eb' }, { label: 'Sykes', value: 78, share: 19, color: '#d97706' }, { label: 'Hoseasons', value: 32, share: 8, color: '#7c3aed' }, { label: 'Other OTAs', value: 12, share: 3, color: '#6b7280' }, ], forecast: { value: 494, vsTarget: '+14 nights', tone: 'positive', confidence: 'High · 87%' }, activity: [ { date: 'Apr 29', body: 'Easter + bank holiday delivered 96 nights vs forecast 78. Knock-on into May expected.', who: 'Joe Marshall' }, { date: 'May 12', body: 'Performance Max budget reallocated from Hosp Display → Lodge Search. Bookings cost dropped £4.20.', who: 'Matt Jones' }, { date: 'Jun 8', body: 'Late availability email sent to 14k subscribers — 38 incremental nights booked in 72h.', who: 'Hayley Roberts' }, ], actions: [ 'Approve June half-term promotional spend lift (+£4k)', 'Review whether to re-baseline Q3 target upwards', ], }, revenue: { history: [ { date: 'Apr 1', actual: 5400, target: 6100 }, { date: 'Apr 8', actual: 8800, target: 6900 }, { date: 'Apr 15', actual: 7600, target: 7700 }, { date: 'Apr 22', actual: 14600, target: 8400 }, { date: 'Apr 29', actual: 36800, target: 9200 }, { date: 'May 6', actual: 11900, target: 10000 }, { date: 'May 13', actual: 10800, target: 10800 }, { date: 'May 20', actual: 13100, target: 11500 }, { date: 'May 27', actual: 13900, target: 11500 }, { date: 'Jun 3', actual: 15800, target: 12300 }, { date: 'Jun 10', actual: 19720, target: 12300 }, ], breakdown: [ { label: 'Direct', value: 64720, share: 41, color: '#10b981' }, { label: 'Google Ads', value: 47260, share: 30, color: '#2563eb' }, { label: 'Sykes', value: 29960, share: 19, color: '#d97706' }, { label: 'Hoseasons', value: 12480, share: 8, color: '#7c3aed' }, { label: 'Other', value: 4000, share: 2, color: '#6b7280' }, ], forecast: { value: 189200, vsTarget: '+£5.2k', tone: 'positive', confidence: 'High · 84%' }, activity: [ { date: 'May 3', body: 'AOV up £41 vs LY thanks to longer mid-week stays in 3-bed lodges.', who: 'Joe Marshall' }, { date: 'May 18', body: 'Cancellation rate dropped to 6.1% (was 8.7% Q1). Tighter deposit terms working.', who: 'Hayley Roberts' }, { date: 'Jun 5', body: 'Two large group bookings (£8.4k combined) for September — booked early via brand search.', who: 'Matt Jones' }, ], actions: [ 'Confirm September pricing tier — early demand suggests +5% feasible', 'Brief on AOV growth tactics for Q3 plan', ], }, cpb: { history: [ { date: 'Apr 1', actual: 38, target: 38 }, { date: 'Apr 8', actual: 36, target: 38 }, { date: 'Apr 15', actual: 34, target: 38 }, { date: 'Apr 22', actual: 33, target: 38 }, { date: 'Apr 29', actual: 29, target: 38 }, { date: 'May 6', actual: 31, target: 38 }, { date: 'May 13', actual: 32, target: 38 }, { date: 'May 20', actual: 31, target: 38 }, { date: 'May 27', actual: 30, target: 38 }, { date: 'Jun 3', actual: 32, target: 38 }, { date: 'Jun 10', actual: 31, target: 38 }, ], breakdown: [ { label: 'Brand Search', value: 8.40, share: 4, color: '#10b981' }, { label: 'Generic Search', value: 28.10, share: 38, color: '#2563eb' }, { label: 'Performance Max', value: 41.20, share: 36, color: '#d97706' }, { label: 'Display + YouTube', value: 62.90, share: 14, color: '#7c3aed' }, { label: 'Sykes commission', value: 78.50, share: 8, color: '#6b7280' }, ], forecast: { value: 30.80, vsTarget: '−£7.20 vs target', tone: 'positive', confidence: 'High · 91%' }, activity: [ { date: 'Apr 19', body: 'Branded auctions softened — Booking.com pulled brand bids. CPB on brand dropped to £6.20.', who: 'Matt Jones' }, { date: 'May 8', body: 'Reallocated 22% of Display budget into Generic Search. Generic CPB up £3, blended down £2.', who: 'Matt Jones' }, { date: 'Jun 1', body: 'Sykes commission rate review — accounting flagged 17% effective rate vs 15% contract.', who: 'Hayley Roberts' }, ], actions: [ 'Resolve Sykes commission discrepancy (open ticket #4081)', 'Decide whether to invest the CPB headroom in volume', ], }, roas: { history: [ { date: 'Apr 1', actual: 6.2, target: 6.5 }, { date: 'Apr 8', actual: 6.8, target: 6.5 }, { date: 'Apr 15', actual: 7.1, target: 6.5 }, { date: 'Apr 22', actual: 7.4, target: 6.5 }, { date: 'Apr 29', actual: 8.9, target: 6.5 }, { date: 'May 6', actual: 7.6, target: 6.5 }, { date: 'May 13', actual: 7.3, target: 6.5 }, { date: 'May 20', actual: 7.5, target: 6.5 }, { date: 'May 27', actual: 7.4, target: 6.5 }, { date: 'Jun 3', actual: 7.5, target: 6.5 }, { date: 'Jun 10', actual: 7.6, target: 6.5 }, ], breakdown: [ { label: 'Brand Search', value: 18.4, share: 35, color: '#10b981' }, { label: 'Generic Search', value: 6.2, share: 28, color: '#2563eb' }, { label: 'Performance Max', value: 4.1, share: 25, color: '#d97706' }, { label: 'Display + YouTube', value: 1.8, share: 8, color: '#7c3aed' }, { label: 'Email', value: 22.3, share: 4, color: '#0891b2' }, ], forecast: { value: 7.5, vsTarget: '+1.0x vs target', tone: 'positive', confidence: 'Medium · 76%' }, activity: [ { date: 'Apr 25', body: 'PMax holding 4.1x ROAS — within target band. Search remains the engine at 11.2x blended.', who: 'Matt Jones' }, { date: 'May 18', body: 'Display ROAS dipped to 1.8x. Recommend shift to YouTube view-through campaigns.', who: 'Matt Jones' }, { date: 'Jun 4', body: 'Email automation series shipped — driving 22.3x ROAS on new welcome flow.', who: 'Hayley Roberts' }, ], actions: [ 'Approve Display → YouTube reallocation (£1.2k/month)', 'Set Q3 ROAS target — current trajectory supports 7.0x', ], }, directshare: { history: [ { date: 'Apr 1', actual: 68, target: 70 }, { date: 'Apr 8', actual: 67, target: 70 }, { date: 'Apr 15', actual: 66, target: 70 }, { date: 'Apr 22', actual: 64, target: 70 }, { date: 'Apr 29', actual: 62, target: 70 }, { date: 'May 6', actual: 63, target: 70 }, { date: 'May 13', actual: 64, target: 70 }, { date: 'May 20', actual: 64, target: 70 }, { date: 'May 27', actual: 65, target: 70 }, { date: 'Jun 3', actual: 64, target: 70 }, { date: 'Jun 10', actual: 64, target: 70 }, ], breakdown: [ { label: 'Direct (brand + organic)', value: 64, share: 64, color: '#10b981' }, { label: 'Sykes', value: 19, share: 19, color: '#d97706' }, { label: 'Hoseasons', value: 8, share: 8, color: '#7c3aed' }, { label: 'Booking.com', value: 6, share: 6, color: '#1d4ed8' }, { label: 'Airbnb + other', value: 3, share: 3, color: '#6b7280' }, ], forecast: { value: 67, vsTarget: '−3pp vs target', tone: 'warning', confidence: 'Medium · 72%' }, activity: [ { date: 'Apr 30', body: 'Sykes commissions ticking up — share of bookings rose from 14% to 19% over 8 weeks.', who: 'Joe Marshall' }, { date: 'May 11', body: 'Brand search investment scheduled for May 14 — projected to recapture 4-5pp direct share.', who: 'Matt Jones' }, { date: 'Jun 7', body: 'Direct share holding at 64%; brand campaign delayed to Jun 18 due to creative review.', who: 'Hayley Roberts' }, ], actions: [ 'Unblock brand creative review — biggest lever on this CPT', 'Audit OTA contracts — Sykes margin review with Joe', ], }, ownership: { history: [ { date: 'Apr 1', actual: 4, target: 5 }, { date: 'Apr 8', actual: 5, target: 5 }, { date: 'Apr 15', actual: 6, target: 5 }, { date: 'Apr 22', actual: 4, target: 5 }, { date: 'Apr 29', actual: 5, target: 5 }, { date: 'May 6', actual: 6, target: 5 }, { date: 'May 13', actual: 4, target: 5 }, { date: 'May 20', actual: 5, target: 5 }, { date: 'May 27', actual: 5, target: 5 }, { date: 'Jun 3', actual: 4, target: 5 }, { date: 'Jun 10', actual: 4, target: 5 }, ], breakdown: [ { label: 'Google Ads (ownership)', value: 21, share: 45, color: '#2563eb' }, { label: 'Direct enquiry form', value: 14, share: 30, color: '#10b981' }, { label: 'Open day events', value: 8, share: 17, color: '#d97706' }, { label: 'Referrals', value: 4, share: 8, color: '#7c3aed' }, ], forecast: { value: 58, vsTarget: '−2 leads', tone: 'warning', confidence: 'Medium · 68%' }, activity: [ { date: 'Apr 22', body: 'Qualified-lead rate now 38% (was 29% in Q1). Quality up even though volume is flat.', who: 'Hayley Roberts' }, { date: 'May 9', body: 'New ownership landing page published — bounce rate 41% (was 67%). Conversion +24%.', who: 'Joe Marshall' }, { date: 'Jun 2', body: 'Open day on June 22 — capacity 30 visitors, 21 confirmed. Expecting 6-8 enquiries.', who: 'Hayley Roberts' }, ], actions: [ 'Confirm spend uplift on ownership Search (target: +12 leads in Q3)', 'Schedule Q3 open day before peak season ends', ], }, }; const KpiDetailScreen = ({ kpiKey, onBack, setActive }) => { const kpi = window.AGREED_KPIS.find(k => k.key === kpiKey) || window.AGREED_KPIS[0]; const detail = KPI_DETAIL_DATA[kpi.key] || KPI_DETAIL_DATA.bookings; const tone = STATUS_TONES[kpi.status] || STATUS_TONES['on-track']; const { LineChart, Line, XAxis, YAxis, Tooltip, CartesianGrid, ResponsiveContainer, ReferenceLine, Legend } = window.Recharts; const [noteOpen, setNoteOpen] = React.useState(false); const [note, setNote] = React.useState(''); const [noteSent, setNoteSent] = React.useState(false); const fmt = (v) => formatKpi(v, kpi.unit); const deltaColor = kpi.deltaTone === 'warning' ? 'var(--warning)' : kpi.deltaTone === 'negative' ? 'var(--negative)' : 'var(--positive)'; return (