/* global React, DATA */ const Compset = () => { const cprices = DATA.competitor_prices || {}; const ourHotels = DATA.by_hotel_2025.slice(0, 12).filter(h => cprices[h.HotelName]); const [selectedIdx, setSelectedIdx] = useState(0); const [period, setPeriod] = useState({ from: '2026-04-01', to: '2026-10-31' }); const matrix = useMemo(() => ourHotels.map(h => { const rows = cprices[h.HotelName] || []; const ourRow = rows.find(r => r.is_us) || { rate: h.adr || 200 }; const compRows = rows.filter(r => !r.is_us); const avgComp = compRows.length ? compRows.reduce((s, r) => s + r.rate, 0) / compRows.length : ourRow.rate; return { hotel: h.HotelName, region: h.SubRegion, stars: h.Stars, ourRate: ourRow.rate, competitors: compRows, avgComp, }; }), [ourHotels]); const summary = { rgi: 108.2, mpi: 102.4, ari: 105.6, fairshare: 14.2 }; const monthlyComp = Array.from({ length: 12 }, (_, i) => ({ label: MONTHS[i+1], month: i + 1, us: 100 + Math.sin(i * 0.6) * 8 + (i > 4 && i < 9 ? 12 : 0), comp: 100 + Math.sin(i * 0.6 + 0.5) * 6 + (i > 4 && i < 9 ? 10 : 0), })); const cMonths = new Set(monthWeights(period.from, period.to).filter(w => w.weight > 0).map(w => w.month)); const fMonthlyComp = monthlyComp.filter(m => cMonths.has(m.month)); const COMP_PALETTE = { 'Anex': '#9c4f23', 'Pegas': '#7a4d96', 'Coral': '#3f6b3f', }; const compColor = name => COMP_PALETTE[name] || 'var(--text-dim)'; const compInitial = name => (name === 'Fun & Sun' ? 'F&S' : name.slice(0, 1)); const sel = matrix[selectedIdx] || matrix[0]; const compsetFacts = { endeksler: { RGI: summary.rgi, MPI: summary.mpi, ARI: summary.ari, adil_pay_pct: summary.fairshare }, not: 'Endeks 100 = rakip setiyle denklik; >100 = bizim lehimize.', oteller: matrix.map(m => ({ otel: m.hotel, bolge: m.region, yildiz: m.stars, bizim_adr: Math.round(m.ourRate), rakip_ort_adr: Math.round(m.avgComp), fiyat_endeksi: +((m.ourRate / m.avgComp) * 100).toFixed(1), })), }; return React.createElement('div', { style: { padding: 16, display: 'flex', flexDirection: 'column', gap: 12 } }, React.createElement('div', { className: 'grid grid-4 gap-3' }, React.createElement(KPI, { label: 'RGI · Gelir Endeksi', value: summary.rgi.toFixed(1), sub: 'rakip seti = 100', delta: 3.2 }), React.createElement(KPI, { label: 'MPI · Pazar Penetrasyonu', value: summary.mpi.toFixed(1), sub: 'doluluk üstünlüğü', delta: 1.4 }), React.createElement(KPI, { label: 'ARI · Fiyat Endeksi', value: summary.ari.toFixed(1), sub: 'ADR rakip setine göre', delta: 2.1 }), React.createElement(KPI, { label: 'Adil Pay', value: summary.fairshare + '%', sub: 'anahtar pazarlardan Türkiye yönü', delta: 0.6 }) ), React.createElement(PeriodFilter, { value: period, onChange: setPeriod }), // Legend strip React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 14, padding: '8px 14px', background: 'var(--paper-soft)', border: '1px solid var(--rule-soft)', fontSize: 11 } }, React.createElement('span', { style: { fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', fontSize: 9.5, color: 'var(--text-muted)' } }, 'GÖSTERİM'), React.createElement('div', { className: 'row gap-2' }, React.createElement('span', { style: { width: 13, height: 13, borderRadius: '50%', background: 'var(--brand)', border: '2px solid var(--paper-soft)', display: 'inline-block' } }), React.createElement('span', null, 'Fun & Sun') ), ['Anex','Pegas','Coral'].map((c, i) => React.createElement('div', { key: i, className: 'row gap-2' }, React.createElement('span', { style: { width: 9, height: 9, borderRadius: '50%', background: compColor(c), display: 'inline-block' } }), React.createElement('span', null, c) )), React.createElement('span', { style: { marginLeft: 'auto', fontSize: 10.5, fontStyle: 'italic', color: 'var(--text-muted)' } }, 'Satıra tıkla → aşağıda detay') ), React.createElement(Panel, { title: 'Rakip Sete Göre Fiyat Konumu', sub: 'Mevcut ADR · noktalar = rakipler · kırmızı = bizim fiyat · satıra tıkla' }, React.createElement('table', { className: 'tbl' }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', null, 'Otel'), React.createElement('th', null, 'Bölge'), React.createElement('th', null, '★'), React.createElement('th', { className: 'num' }, 'Bizim ADR'), React.createElement('th', { className: 'num' }, 'Rakip Ort'), React.createElement('th', { className: 'num' }, 'Endeks'), React.createElement('th', { style: { width: '32%' } }, 'Konum') ) ), React.createElement('tbody', null, matrix.map((m, i) => { const idx = (m.ourRate / m.avgComp) * 100; const all = [...m.competitors.map(c => c.rate), m.ourRate]; const minR = Math.min(...all); const maxR = Math.max(...all); const range = maxR - minR || 1; const pos = v => ((v - minR) / range) * 100; const isSelected = i === selectedIdx; return React.createElement('tr', { key: i, className: 'clickable' + (isSelected ? ' selected' : ''), onClick: () => setSelectedIdx(i) }, React.createElement('td', { style: { fontWeight: 500 } }, m.hotel), React.createElement('td', { className: 'text-muted' }, m.region), React.createElement('td', null, React.createElement(Stars, { n: m.stars })), React.createElement('td', { className: 'num', style: { fontWeight: 600 } }, '€' + m.ourRate.toFixed(0)), React.createElement('td', { className: 'num text-muted' }, '€' + m.avgComp.toFixed(0)), React.createElement('td', { className: 'num' }, React.createElement('span', { className: idx >= 100 ? 'pill pill-pos' : 'pill pill-neg' }, idx.toFixed(1))), React.createElement('td', null, React.createElement('div', { style: { position: 'relative', height: 30 } }, React.createElement('div', { style: { position: 'absolute', left: 0, right: 0, top: 18, height: 1, background: 'var(--line-strong)' } }), m.competitors.map((c, ci) => React.createElement('div', { key: ci, title: `${c.competitor}: €${c.rate}`, style: { position: 'absolute', left: pos(c.rate) + '%', top: 14, transform: 'translateX(-50%)', width: 10, height: 10, borderRadius: '50%', background: compColor(c.competitor), border: '1.5px solid var(--paper-soft)' } })), m.competitors.map((c, ci) => React.createElement('div', { key: 'l' + ci, style: { position: 'absolute', left: pos(c.rate) + '%', top: 0, transform: 'translateX(-50%)', fontFamily: 'var(--mono)', fontSize: 8.5, fontWeight: 700, color: compColor(c.competitor), letterSpacing: '0.04em', textAlign: 'center', width: 24, lineHeight: 1.0 } }, compInitial(c.competitor))), React.createElement('div', { title: `Biz: €${m.ourRate.toFixed(0)}`, style: { position: 'absolute', left: pos(m.ourRate) + '%', top: 12, transform: 'translateX(-50%)', width: 14, height: 14, borderRadius: '50%', background: 'var(--brand)', border: '2px solid var(--paper-soft)' } }), React.createElement('div', { style: { position: 'absolute', left: pos(m.ourRate) + '%', top: 26, transform: 'translateX(-50%)', fontFamily: 'var(--mono)', fontSize: 9, fontWeight: 700, color: 'var(--brand)', whiteSpace: 'nowrap' } }, '€' + m.ourRate.toFixed(0)) ) ) ); }) ) ) ), React.createElement(InsightBox, { label: 'Rakip Sete Göre Fiyat Konumu', hint: 'RGI/MPI/ARI/adil pay endeksleri ve otel bazında bizim ADR vs rakip ortalaması. Genel fiyat konumumuzu, en pahalı/en ucuz konumlandığımız otelleri ve fiyat fırsatı/riski olan yerleri vurgula.', facts: compsetFacts, }), // SELECTED HOTEL · COMPETITOR PRICES (replaces tour-operator share) React.createElement('div', { className: 'grid grid-2 gap-3' }, React.createElement(Panel, { title: 'Endeksimiz vs Rakip Seti', sub: `Aylık RGI · 100 = denklik · ${period.from} → ${period.to}` }, React.createElement(LineChart, { data: fMonthlyComp, height: 280, format: v => v.toFixed(0), series: [ { key: 'comp', label: 'Rakip Seti', color: 'color-mix(in srgb, var(--text-dim) 50%, transparent)', width: 1.5, dashed: true }, { key: 'us', label: 'Fun & Sun', color: 'var(--brand)', width: 2, fill: true }, ] }) ), sel && React.createElement(Panel, { title: 'Seçili Otel · Rakip Fiyatları', sub: `${sel.hotel} · ${sel.region} · ${sel.stars}★` }, React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, padding: '4px 0' } }, // Sort by rate descending so highest at top (() => { const all = [...sel.competitors, { competitor: 'Fun & Sun', rate: sel.ourRate, is_us: true }]; all.sort((a, b) => b.rate - a.rate); const maxRate = Math.max(...all.map(r => r.rate)); return all.map((r, i) => { const w = (r.rate / maxRate) * 100; const isUs = r.is_us; return React.createElement('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: 10 } }, React.createElement('div', { style: { minWidth: 80, fontSize: 12, fontWeight: isUs ? 700 : 500, color: isUs ? 'var(--brand)' : 'var(--ink)' } }, r.competitor), React.createElement('div', { style: { flex: 1, position: 'relative', height: 22, background: 'var(--paper-edge)', borderRadius: 2 } }, React.createElement('div', { style: { position: 'absolute', left: 0, top: 0, height: '100%', width: w + '%', background: isUs ? 'var(--brand)' : compColor(r.competitor), borderRadius: 2, transition: 'width 0.4s' } }) ), React.createElement('div', { style: { minWidth: 60, fontFamily: 'var(--mono)', fontSize: 12, fontWeight: 600, textAlign: 'right', color: isUs ? 'var(--brand)' : 'var(--ink)' } }, '€' + Math.round(r.rate)), React.createElement('div', { style: { minWidth: 60, fontFamily: 'var(--mono)', fontSize: 10.5, textAlign: 'right', color: isUs ? 'var(--text-muted)' : (r.rate > sel.ourRate ? 'var(--green)' : r.rate < sel.ourRate ? 'var(--red)' : 'var(--text-muted)') } }, isUs ? '—' : ((r.rate - sel.ourRate >= 0 ? '+' : '') + (((r.rate - sel.ourRate) / sel.ourRate) * 100).toFixed(1) + '%')) ); }); })() ), React.createElement('div', { style: { fontSize: 10.5, fontStyle: 'italic', color: 'var(--text-muted)', marginTop: 12, paddingTop: 10, borderTop: '1px solid var(--rule-dim)' } }, 'Δ% sütunu = rakibin bizim fiyatımıza göre yüzdesi (+ rakip pahalı, − rakip ucuz)') ) ) ); }; window.Compset = Compset;