/* global React, DATA */ /* Genel Bakış — yönetici panosu */ const Overview = () => { const k25 = DATA.kpi_2025; const k25y = DATA.kpi_2025_ytd; const k26y = DATA.kpi_2026_ytd; const k26 = DATA.kpi_2026_otb || DATA.kpi_2026_ytd_full || DATA.kpi_2026_ytd; const f25 = DATA.flights_kpi_2025; const f25y = DATA.flights_kpi_2025_ytd; const f26y = DATA.flights_kpi_2026_ytd; const yoy = (cur, ref) => (ref ? ((cur / ref) - 1) * 100 : 0); const monthly = useMemo(() => Array.from({ length: 12 }, (_, i) => { const m = i + 1; const r25 = DATA.by_month_2025.find(r => r.CheckInMonth === m); const r26 = DATA.by_month_2026.find(r => r.CheckInMonth === m); return { label: MONTHS[m], rev25: (r25?.revenue || 0) / 1e6, rev26: (r26?.revenue || 0) / 1e6, pax25: (r25?.pax || 0), pax26: (r26?.pax || 0), adr25: (r25?.adr || 0), adr26: (r26?.adr || 0), }; }), []); // YoY on a same-period (YTD) basis — comparing the 2026 forward book against // the 2025 FINAL would show meaningless -80% "declines". const revByMarket = DATA.by_market_2025.slice(0, 10).map(m => { const y25 = DATA.by_market_2025_ytd.find(x => x.SourceMarket === m.SourceMarket)?.revenue || 0; const y26 = DATA.by_market_2026_ytd.find(x => x.SourceMarket === m.SourceMarket)?.revenue || 0; return { label: m.SourceMarket, value: m.revenue / 1e6, pax: m.pax, yoy: y25 ? (y26 / y25 - 1) * 100 : 0 }; }); const sparkRev = monthly.map(m => m.rev25); const sparkPax = monthly.map(m => m.pax25); const stars = DATA.by_stars_2025.map(s => ({ label: `${s.Stars}★`, value: s.revenue/1e6, adr: s.adr })); const overviewFacts = { ytd_donem: `1 Oca → ${DATA.ytd_cutoff || '7 Nis'}`, ytd: { gelir_2026_M: +(k26y.revenue / 1e6).toFixed(1), gelir_2025_M: +(k25y.revenue / 1e6).toFixed(1), gelir_yoy_pct: +yoy(k26y.revenue, k25y.revenue).toFixed(1), yolcu_2026: k26y.pax, yolcu_2025: k25y.pax, yolcu_yoy_pct: +yoy(k26y.pax, k25y.pax).toFixed(1), adr_2026: Math.round(k26y.adr), adr_2025: Math.round(k25y.adr), adr_yoy_pct: +yoy(k26y.adr, k25y.adr).toFixed(1), marj_2026_M: +(k26y.margin / 1e6).toFixed(1), marj_2025_M: +(k25y.margin / 1e6).toFixed(1), ucus_lf_2026_pct: f26y.load_factor, ucus_lf_2025_pct: f25y.load_factor, }, not_2026_booking: '2026 aylık değerler mevcut BOOKING (bugüne kadar satılan) — gelecek/yaz ayları doğal olarak düşük görünür çünkü rezervasyon eğrisi henüz dolmadı. Bunu bir gelir AÇIĞI gibi yorumlama; YTD ve YoY metrikleri gerçek performans göstergesidir.', aylik_2025_gerceklesen_vs_2026_booking_M: monthly.map(m => ({ ay: m.label, gerceklesen_2025_M: +m.rev25.toFixed(1), booking_2026_M: +m.rev26.toFixed(1) })), en_iyi_pazarlar: revByMarket.slice(0, 6).map(m => ({ pazar: m.label, gelir_M: +m.value.toFixed(0), yoy_pct: +m.yoy.toFixed(1) })), yildiz_dagilimi: stars.map(s => ({ sinif: s.label, gelir_M: +s.value.toFixed(0), adr: Math.round(s.adr) })), }; return React.createElement('div', { style: { padding: 16, display: 'flex', flexDirection: 'column', gap: 14 } }, // YTD comparison strip (6 cards) — same calendar day, year-over-year React.createElement('div', { style: { fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase', color: 'var(--text-muted)', fontWeight: 700, marginBottom: -4 } }, `YTD KARŞILAŞTIRMA · 1 Oca → ${DATA.ytd_cutoff || '7 Nis'} · 2026 vs 2025`), React.createElement('div', { className: 'grid grid-6 gap-3' }, React.createElement(KPI, { label: 'Gelir YTD', value: fmtEurM(k26y.revenue), sub: `2025 YTD: ${fmtEurM(k25y.revenue)}`, delta: yoy(k26y.revenue, k25y.revenue), sparkline: sparkRev, sparklineColor: 'var(--brand)' }), React.createElement(KPI, { label: 'Yolcu YTD', value: fmtK(k26y.pax), sub: `2025 YTD: ${fmtK(k25y.pax)}`, delta: yoy(k26y.pax, k25y.pax), sparkline: sparkPax, sparklineColor: 'var(--info)' }), React.createElement(KPI, { label: 'ADR YTD', value: '€' + k26y.adr.toFixed(0), sub: `2025 YTD: €${k25y.adr.toFixed(0)}`, delta: yoy(k26y.adr, k25y.adr) }), React.createElement(KPI, { label: 'Rezervasyon YTD', value: fmtN(k26y.bookings), sub: `2025 YTD: ${fmtN(k25y.bookings)}`, delta: yoy(k26y.bookings, k25y.bookings) }), React.createElement(KPI, { label: 'Marj YTD', value: fmtEurM(k26y.margin), sub: `2025 YTD: ${fmtEurM(k25y.margin)}`, delta: yoy(k26y.margin, k25y.margin) }), React.createElement(KPI, { label: 'Uçuş LF YTD', value: '%' + f26y.load_factor, sub: `2025 YTD: %${f25y.load_factor}`, delta: yoy(f26y.load_factor, f25y.load_factor) }) ), // Forward-look strip (3 cards): full-year forward + 2025 close + ADR React.createElement('div', { style: { fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase', color: 'var(--text-muted)', fontWeight: 700, marginBottom: -4, marginTop: 4 } }, 'TAM YIL · 2026 GERÇEKLEŞEN İLERİ GÖRÜNÜM'), React.createElement('div', { className: 'grid grid-3 gap-3' }, React.createElement(KPI, { label: '2026 Gerçekleşen Tam Yıl', value: fmtEurM(k26.revenue), sub: '€1,32Mr hedef', delta: 12.4 }), React.createElement(KPI, { label: '2025 Tam Yıl', value: fmtEurM(k25.revenue), sub: `${fmtN(k25.bookings)} rez · marj %${(k25.margin/k25.revenue*100).toFixed(1)}` }), React.createElement(KPI, { label: '2025 ADR (Tam Yıl)', value: '€' + k25.adr.toFixed(0), sub: `2025 Yolcu: ${fmtK(k25.pax)}` }) ), React.createElement(Panel, { title: 'Aylık Gelir · 2025 Gerçekleşen vs 2026 Booking', sub: 'Giriş tarihi bazlı · € milyon · S3\'ten saatte bir yenilenir', actions: React.createElement('div', { className: 'row gap-2' }, React.createElement('div', { className: 'row gap-2', style: { marginRight: 8, fontSize: 10.5, color: 'var(--text-dim)' } }, React.createElement('span', { className: 'row gap-2', style: { alignItems: 'center' } }, React.createElement('span', { style: { width: 12, height: 10, background: 'color-mix(in srgb, var(--text-dim) 40%, transparent)' } }), '2025 Gerçekleşen'), React.createElement('span', { className: 'row gap-2', style: { alignItems: 'center' } }, React.createElement('span', { style: { width: 12, height: 10, background: 'var(--brand)' } }), '2026 Booking') ), React.createElement('div', { className: 'pill pill-outline' }, 'Giriş tarihi'), React.createElement('button', { className: 'btn btn-sm' }, 'Rezervasyon tarihi'), React.createElement('button', { className: 'btn btn-sm' }, 'CSV Dışa Aktar') ) }, React.createElement(BarChart, { data: monthly, height: 320, valueKey: 'rev25', secondaryKey: 'rev26', labelKey: 'label', format: v => '€' + v.toFixed(0) + 'M', color: 'color-mix(in srgb, var(--text-dim) 40%, transparent)', secondaryColor: 'var(--brand)', valueLabel: true, tooltipFormat: (d) => React.createElement('div', null, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6, color: 'var(--paper)' } }, d.label), React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, color: 'var(--paper)', marginBottom: 4 } }, React.createElement('span', { style: { width: 10, height: 3, background: 'color-mix(in srgb, var(--text-dim) 50%, transparent)', flexShrink: 0 } }), React.createElement('span', { style: { fontWeight: 600 } }, '2025 Gerçekleşen') ), React.createElement('div', { style: { color: 'var(--paper)', fontSize: 10.5, paddingLeft: 16, marginBottom: 6 } }, `Gelir: €${d.rev25.toFixed(1)}M · ADR: €${(d.adr25 || 0).toFixed(0)} · Yolcu: ${fmtN(d.pax25 || 0)}`), React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, color: 'var(--paper)', marginBottom: 4 } }, React.createElement('span', { style: { width: 10, height: 3, background: 'var(--brand)', flexShrink: 0 } }), React.createElement('span', { style: { fontWeight: 600 } }, '2026 Booking') ), React.createElement('div', { style: { color: 'var(--paper)', fontSize: 10.5, paddingLeft: 16 } }, `Gelir: €${d.rev26.toFixed(1)}M · ADR: €${(d.adr26 || 0).toFixed(0)} · Yolcu: ${fmtN(d.pax26 || 0)}`) ) }) ), React.createElement(InsightBox, { label: 'Genel Bakış · Yönetici Özeti', hint: 'Fun & Sun yönetici panosu. YTD momentumu (gelir/yolcu/ADR/marj/uçuş LF YoY) gerçek performansı gösterir — özeti buna dayandır. 2026 aylık seri mevcut BOOKING olduğundan gelecek ayların düşük görünmesi normaldir; bunu açık gibi sunma, en fazla "rezervasyon eğrisi henüz doluyor" diye nötr belirt. Lider kaynak pazarları ve yıldız sınıfı karmasını da vurgula.', facts: overviewFacts, }), React.createElement('div', { className: 'grid grid-2 gap-3' }, React.createElement(Panel, { title: 'En İyi 10 Kaynak Pazar', sub: '2025 gelir · YTD YoY' }, React.createElement('table', { className: 'tbl' }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', null, 'Pazar'), React.createElement('th', { className: 'num' }, 'Gelir'), React.createElement('th', { className: 'num' }, 'Yolcu'), React.createElement('th', { className: 'num' }, 'YoY') ) ), React.createElement('tbody', null, revByMarket.map((m, i) => React.createElement('tr', { key: i }, React.createElement('td', { style: { fontWeight: 500 } }, m.label), React.createElement('td', { className: 'num' }, '€' + m.value.toFixed(0) + 'M'), React.createElement('td', { className: 'num text-muted' }, fmtK(m.pax)), React.createElement('td', { className: 'num' }, React.createElement(Delta, { value: m.yoy, digits: 1 }) ) )) ) ) ), React.createElement(Panel, { title: 'Yıldız Sınıfı Dağılımı', sub: '2025 gelir · ADR' }, React.createElement('table', { className: 'tbl tbl-zebra' }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', null, 'Sınıf'), React.createElement('th', { className: 'num' }, 'Gelir'), React.createElement('th', { className: 'num' }, 'ADR'), React.createElement('th', { className: 'num' }, 'Pay') ) ), React.createElement('tbody', null, stars.map((s, i) => { const tot = stars.reduce((sum, x) => sum + x.value, 0); return React.createElement('tr', { key: i }, React.createElement('td', null, React.createElement(Stars, { n: parseInt(s.label) })), React.createElement('td', { className: 'num' }, '€' + s.value.toFixed(0) + 'M'), React.createElement('td', { className: 'num' }, '€' + s.adr.toFixed(0)), React.createElement('td', { className: 'num text-muted' }, ((s.value / tot) * 100).toFixed(1) + '%') ); }) ) ) ) ), React.createElement('div', { className: 'grid gap-3', style: { gridTemplateColumns: '2fr 1fr' } }, React.createElement(Panel, { title: 'Gelire Göre En İyi 12 Otel', sub: '2025 · ayrıntı için satıra tıkla', actions: React.createElement('button', { className: 'btn btn-sm' }, 'Tümünü gör (100)') }, React.createElement('table', { className: 'tbl' }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', null, '#'), React.createElement('th', null, 'Otel'), React.createElement('th', null, 'Bölge'), React.createElement('th', null, 'Sınıf'), React.createElement('th', { className: 'num' }, 'Gelir'), React.createElement('th', { className: 'num' }, 'Yolcu'), React.createElement('th', { className: 'num' }, 'ADR'), React.createElement('th', { className: 'num' }, 'Marj'), React.createElement('th', { className: 'num' }, 'Trend') ) ), React.createElement('tbody', null, DATA.by_hotel_2025.slice(0, 12).map((h, i) => { const adr = h.revenue / h.pax / (h.nights / h.bookings) || 0; const mp = h.margin / h.revenue * 100; const spark = Array.from({length: 8}, (_, j) => h.revenue * (0.6 + Math.random()*0.6)); return React.createElement('tr', { key: i, className: 'clickable', onClick: () => window.dispatchEvent(new CustomEvent('open-hotel', { detail: h })) }, React.createElement('td', { className: 'text-faint mono' }, String(i+1).padStart(2, '0')), React.createElement('td', { style: { fontWeight: 500 } }, h.HotelName), React.createElement('td', { className: 'text-muted' }, h.SubRegion), React.createElement('td', null, React.createElement(Stars, { n: h.Stars })), React.createElement('td', { className: 'num' }, fmtEurM(h.revenue)), React.createElement('td', { className: 'num text-muted' }, fmtN(h.pax)), React.createElement('td', { className: 'num' }, '€' + adr.toFixed(0)), React.createElement('td', { className: 'num text-pos' }, mp.toFixed(1) + '%'), React.createElement('td', { className: 'num' }, React.createElement(Sparkline, { data: spark, width: 60, height: 18, color: 'var(--brand)', fill: false }) ) ); }) ) ) ), React.createElement(Panel, { title: 'Aktif Uyarılar', sub: `${DATA.alerts.length} açık`, actions: React.createElement('button', { className: 'btn btn-sm' }, 'Tümünü gör') }, React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, maxHeight: 380, overflowY: 'auto' } }, DATA.alerts.slice(0, 6).map((a, i) => { const sev = a.severity; const sevTr = { high: 'YÜKSEK', medium: 'ORTA', low: 'DÜŞÜK' }[sev] || sev.toUpperCase(); const pillCls = sev === 'high' ? 'pill-neg' : sev === 'medium' ? 'pill-warn' : 'pill-info'; return React.createElement('div', { key: i, style: { padding: '10px 12px', background: 'var(--bg-1)', borderRadius: 4, borderLeft: `2px solid var(--${sev === 'high' ? 'neg' : sev === 'medium' ? 'warn' : 'info'})`, cursor: 'pointer' } }, React.createElement('div', { className: 'row between gap-2', style: { marginBottom: 4 } }, React.createElement('span', { className: `pill ${pillCls}` }, sevTr), React.createElement('span', { className: 'mono', style: { fontSize: 9.5, color: 'var(--text-faint)' } }, '12 dk önce') ), React.createElement('div', { style: { fontWeight: 600, fontSize: 11.5, marginBottom: 2 } }, a.title), React.createElement('div', { style: { fontSize: 10.5, color: 'var(--text-dim)', lineHeight: 1.45 } }, a.body.slice(0, 110) + (a.body.length > 110 ? '…' : '') ) ); }) ) ) ) ); }; window.Overview = Overview;