/* global React, ReactDOM, DATA */ const { useState, useEffect, useRef, useMemo } = React; const NAV = [ { group: 'MANŞET', items: [ { id: 'briefing', label: 'Günlük Brifing', kbd: 'G' }, { id: 'overview', label: 'Genel Bakış', kbd: 'O' }, { id: 'reporting', label: 'Raporlama', kbd: 'R' }, { id: 'forecast', label: 'Tahmin', kbd: 'F' }, ]}, { group: 'GELİR MASASI', items: [ { id: 'pricing', label: 'Fiyatlama & Öneriler', kbd: 'P' }, { id: 'scenario', label: 'Senaryo Simülasyonu', kbd: 'S' }, { id: 'budget', label: 'Bütçe vs Gerçekleşen', kbd: 'B' }, { id: 'compset', label: 'Rakip Seti', kbd: 'C' }, ]}, { group: 'OPERASYON', items: [ { id: 'flights', label: 'Uçuşlar', kbd: 'L' }, { id: 'alerts', label: 'Uyarılar', kbd: 'A' }, { id: 'channels', label: 'Kanallar', kbd: 'M' }, ]}, ]; // flatten with running numbers const NAV_FLAT = (() => { let n = 0; return NAV.map(g => ({ ...g, items: g.items.map(it => { n += 1; return { ...it, num: String(n).padStart(2, '0') }; }) })); })(); const PAGES = { briefing: () => React.createElement(window.Briefing, null), overview: () => React.createElement(window.Overview, null), reporting: () => React.createElement(window.Reporting, null), pricing: () => React.createElement(window.Pricing, null), scenario: () => React.createElement(window.Scenario, null), forecast: () => React.createElement(window.Forecast, null), compset: () => React.createElement(window.Compset, null), flights: () => React.createElement(window.Flights, null), budget: () => React.createElement(window.Budget, null), alerts: () => React.createElement(window.Alerts, null), channels: () => React.createElement(window.Channels, null), }; const Sidebar = ({ page, setPage, alertCount }) => { return React.createElement('aside', { className: 'sidebar' }, React.createElement('div', { className: 'sidebar-brand' }, React.createElement('div', { className: 'sidebar-wordmark' }, 'Fun ', React.createElement('span', { style: { fontStyle: 'italic', fontWeight: 400 } }, '&'), ' Sun' ), React.createElement('div', { className: 'sidebar-tag' }, 'GELİR MASASI') ), React.createElement('div', { className: 'sidebar-edition' }, React.createElement('div', { className: 'sidebar-edition-label' }, 'Sayı'), React.createElement('div', { className: 'sidebar-edition-value' }, 'Cilt ', React.createElement('span', { style: { fontStyle: 'italic' } }, 'XII'), ' · No. 0407') ), React.createElement('nav', { className: 'sidebar-nav' }, NAV_FLAT.map((g, gi) => React.createElement('div', { key: gi, style: { padding: '14px 0 6px' } }, React.createElement('div', { style: { padding: '0 22px 8px', fontSize: 9, letterSpacing: '0.22em', textTransform: 'uppercase', fontWeight: 700, color: 'var(--text-on-ink-faint)' } }, g.group), g.items.map(it => React.createElement('div', { key: it.id, className: 'nav-item' + (page === it.id ? ' active' : ''), onClick: () => setPage(it.id) }, React.createElement('span', { className: 'nav-num' }, it.num), React.createElement('span', { className: 'nav-label' }, it.label), it.id === 'alerts' && alertCount ? React.createElement('span', { style: { fontFamily: 'var(--mono)', fontSize: 9.5, color: 'var(--red)', fontWeight: 600, letterSpacing: '0.04em' } }, alertCount) : null, React.createElement('span', { className: 'nav-shortcut' }, it.kbd) )) )) ), React.createElement('div', { className: 'sidebar-footer' }, React.createElement('div', { className: 'sidebar-footer-label', style: { marginBottom: 6 } }, 'Hat'), React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 } }, React.createElement('span', { className: 'dot dot-pos' }), React.createElement('span', { style: { color: 'var(--text-on-ink-dim)' } }, 'Ambar senkronize') ), React.createElement('div', { className: 'mono', style: { fontSize: 9.5, color: 'var(--text-on-ink-faint)' } }, '11 sn önce iletildi') ) ); }; const TopBar = ({ onSearch, copilotOpen, toggleCopilot }) => { return React.createElement('div', { className: 'topbar' }, React.createElement('div', { className: 'masthead' }, React.createElement('div', { className: 'masthead-issue' }, '05 Mayıs 2026 · Salı'), React.createElement('div', { style: { width: 1, height: 18, background: 'var(--rule-soft)' } }), React.createElement('div', { className: 'masthead-issue' }, 'FY 2025 ▾'), React.createElement('div', { className: 'masthead-issue' }, 'Tüm Pazarlar ▾'), React.createElement('div', { className: 'masthead-issue' }, 'Tüm Bölgeler ▾'), React.createElement('div', { className: 'masthead-divider' }), React.createElement('div', { className: 'masthead-live' }, React.createElement('span', { className: 'masthead-live-dot' }), React.createElement('span', null, 'Canlı'), React.createElement('em', null, '· Antalya 24°C, hafif rüzgâr') ) ), React.createElement('div', { className: 'topbar-actions' }, React.createElement('div', { className: 'topbar-search', onClick: onSearch }, React.createElement('span', null, 'Masada ara…'), React.createElement('span', { className: 'mono', style: { fontSize: 10, color: 'var(--text-faint)', marginLeft: 'auto' } }, '⌘K') ), React.createElement('button', { className: 'btn btn-sm btn-ghost' }, 'Dışa Aktar'), React.createElement('button', { className: 'btn btn-sm' + (copilotOpen ? ' btn-primary' : ''), onClick: toggleCopilot }, 'Copilot'), React.createElement('div', { className: 'avatar', title: 'Kaan Koç · Gelir Direktörü' }, 'K') ) ); }; const StatusBar = () => { return React.createElement('div', { className: 'statusbar' }, React.createElement('div', { className: 'statusbar-section' }, React.createElement('span', { className: 'dot dot-pos' }), React.createElement('span', null, 'BAĞLI'), React.createElement('span', { style: { color: 'var(--text-faint)' } }, 'ambar · s3-tr-pricing'), ), React.createElement('div', { className: 'statusbar-section' }, React.createElement('span', null, 'Gerçekleşen FY26'), React.createElement('span', { style: { color: 'var(--ink)', fontWeight: 600 } }, '€144.6M'), React.createElement('span', { style: { color: 'var(--text-faint)' } }, '·'), React.createElement('span', null, '+%12,4 YoY') ), React.createElement('div', { className: 'statusbar-section' }, React.createElement('span', null, 'Bekleyen öneri'), React.createElement('span', { style: { color: 'var(--amber)', fontWeight: 600 } }, '481'), React.createElement('span', { style: { color: 'var(--text-faint)' } }, '·'), React.createElement('span', null, 'Uyarı'), React.createElement('span', { style: { color: 'var(--red)', fontWeight: 600 } }, DATA.alerts.length) ), React.createElement('div', { className: 'statusbar-section', style: { marginLeft: 'auto' } }, React.createElement('span', { style: { color: 'var(--text-faint)' } }, 'v2.4.1'), React.createElement('span', { style: { color: 'var(--text-faint)' } }, 'gec 11ms'), React.createElement('span', { style: { color: 'var(--text-faint)' } }, 'otrm 02:14:21') ) ); }; const CommandPalette = ({ open, onClose, setPage }) => { const [q, setQ] = useState(''); useEffect(() => { if (open) setQ(''); }, [open]); if (!open) return null; const items = [ ...NAV_FLAT.flatMap(g => g.items.map(it => ({ type: 'sayfa', label: it.label + ' sayfasına git', action: () => { setPage(it.id); onClose(); } }))), ...DATA.hotel_index.slice(0, 50).map(h => ({ type: 'otel', label: h.HotelName + ' · ' + h.SubRegion + ' · ' + h.Stars + '★', action: () => { setPage('overview'); onClose(); } })), ]; const filtered = q ? items.filter(i => i.label.toLowerCase().includes(q.toLowerCase())).slice(0, 10) : items.slice(0, 10); return React.createElement('div', { className: 'modal-backdrop', onClick: onClose, style: { paddingTop: '15vh' } }, React.createElement('div', { onClick: e => e.stopPropagation(), style: { width: 620, maxWidth: '90vw', background: 'var(--paper)', border: '1px solid var(--ink)', overflow: 'hidden' } }, React.createElement('div', { style: { padding: '12px 16px 8px', borderBottom: '1px solid var(--ink)' } }, React.createElement('div', { className: 'eyebrow', style: { marginBottom: 4 } }, 'Endeks'), React.createElement('input', { autoFocus: true, placeholder: 'Komut yazın, gezin veya otel arayın…', value: q, onChange: e => setQ(e.target.value), style: { width: '100%', height: 36, border: 'none', outline: 'none', background: 'transparent', fontSize: 18, fontFamily: 'var(--serif)', color: 'var(--ink)' } }) ), React.createElement('div', { style: { maxHeight: 380, overflowY: 'auto', padding: 4 } }, filtered.length === 0 ? React.createElement('div', { style: { padding: 24, textAlign: 'center', color: 'var(--text-faint)', fontSize: 12, fontStyle: 'italic' } }, 'Bugünkü endekste eşleşme yok') : filtered.map((it, i) => React.createElement('div', { key: i, onClick: it.action, style: { padding: '9px 12px', fontSize: 12.5, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--rule-dim)' }, onMouseEnter: e => e.currentTarget.style.background = 'var(--paper-edge)', onMouseLeave: e => e.currentTarget.style.background = '' }, React.createElement('span', { style: { fontSize: 9, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--red)', fontWeight: 700, minWidth: 48 } }, it.type), React.createElement('span', null, it.label) )) ) ) ); }; class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { err: null }; } static getDerivedStateFromError(err) { return { err }; } componentDidCatch(err, info) { console.error('Page render error:', err, info); } render() { if (!this.state.err) return this.props.children; const e = this.state.err; return React.createElement('div', { style: { padding: 32, maxWidth: 880, margin: '0 auto', fontFamily: 'var(--serif)' } }, React.createElement('div', { className: 'eyebrow', style: { marginBottom: 6 } }, 'Sayfa Hatası'), React.createElement('h1', { className: 'headline', style: { fontSize: 36 } }, 'Bu bölüm yüklenemedi.'), React.createElement('div', { className: 'pullquote' }, e.message || String(e)), React.createElement('pre', { style: { fontFamily: 'var(--mono)', fontSize: 11, background: 'var(--paper-edge)', padding: 14, borderLeft: '3px solid var(--red)', overflow: 'auto', maxHeight: 360 } }, (e.stack || '').split('\n').slice(0, 12).join('\n')), React.createElement('button', { className: 'btn btn-primary', style: { marginTop: 18 }, onClick: () => this.setState({ err: null }) }, 'Yeniden Dene') ); } } const App = () => { const [authed, setAuthed] = useState(false); const [page, setPage] = useState('overview'); const [copilotOpen, setCopilotOpen] = useState(false); const [paletteOpen, setPaletteOpen] = useState(false); const [hotelOpen, setHotelOpen] = useState(null); const isMobile = (window.useIsMobile || (() => false))(); useEffect(() => { const handler = (e) => { if (!authed) return; if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); setPaletteOpen(true); } if ((e.metaKey || e.ctrlKey) && e.key === '/') { e.preventDefault(); setCopilotOpen(o => !o); } if (e.key === 'Escape') { setPaletteOpen(false); setHotelOpen(null); } if (e.altKey) { const map = { g: 'briefing', o: 'overview', r: 'reporting', f: 'forecast', p: 'pricing', s: 'scenario', b: 'budget', c: 'compset', l: 'flights', a: 'alerts', m: 'channels' }; if (map[e.key.toLowerCase()]) { e.preventDefault(); setPage(map[e.key.toLowerCase()]); } } }; window.addEventListener('keydown', handler); const openHotel = (e) => setHotelOpen(e.detail); const navigate = (e) => { if (e.detail && e.detail.page) setPage(e.detail.page); }; window.addEventListener('open-hotel', openHotel); window.addEventListener('navigate', navigate); return () => { window.removeEventListener('keydown', handler); window.removeEventListener('open-hotel', openHotel); window.removeEventListener('navigate', navigate); }; }, [authed]); // Label the cells of collapsed mobile table-cards (#5/#6/#10, and the // reporting "Otel" section whose filters re-render independently). Installed // once; the observer re-applies labels on any DOM change. useEffect(() => { if (window.observeTableLabels) window.observeTableLabels(document.body); return () => { if (window.__tblObserver) window.__tblObserver.disconnect(); }; }, []); if (!authed) return React.createElement(window.Login, { onLogin: () => setAuthed(true) }); return React.createElement(React.Fragment, null, React.createElement('div', { className: 'app' + (isMobile ? ' app-mobile' : '') }, // Desktop sidebar hidden on mobile (we use a bottom tab bar instead). !isMobile && React.createElement(Sidebar, { page, setPage, alertCount: DATA.alerts.length }), React.createElement('main', { className: 'main' }, !isMobile && React.createElement(TopBar, { onSearch: () => setPaletteOpen(true), copilotOpen, toggleCopilot: () => setCopilotOpen(o => !o) }), // Mobile: ultra-compact masthead + page title bar isMobile && React.createElement(window.MobileHeader, { page, alertCount: DATA.alerts.length, onSearch: () => setPaletteOpen(true) }), React.createElement('div', { className: 'workspace' }, React.createElement('div', { className: 'content', "data-screen-label": page }, React.createElement(ErrorBoundary, { key: page }, PAGES[page]()) ), React.createElement(window.Copilot, { open: copilotOpen, onClose: () => setCopilotOpen(false) }) ), !isMobile && React.createElement(StatusBar, null) ) ), // Mobile-only shell pieces. isMobile && React.createElement(window.MobileBottomNav, { page, setPage, alertCount: DATA.alerts.length }), isMobile && React.createElement(window.MobileCopilotFAB, { open: copilotOpen, onToggle: () => setCopilotOpen(o => !o) }), isMobile && React.createElement(window.MobileInstallHint, null), React.createElement(CommandPalette, { open: paletteOpen, onClose: () => setPaletteOpen(false), setPage }), hotelOpen && React.createElement(window.HotelDetailModal, { hotel: hotelOpen, onClose: () => setHotelOpen(null) }) ); }; function __mount() { ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App)); } if (window.DATA) __mount(); else window.addEventListener('__data_ready', __mount);