import './styles.css' import { h, mount } from './dom' import { bootstrap, getState, isAdmin, subscribe } from './store' import { renderJoin } from './views/join' import { renderWeek } from './views/week' import { renderRadar } from './views/radar' import { renderMap } from './views/map' import { renderPlus } from './views/plus' import { getBaseUrl } from './config' import { checkForUpdate } from './update' import { showUpdateModal } from './update-modal' type Tab = 'meteo' | 'radar' | 'carte' | 'plus' const app = document.getElementById('app')! let activeTab: Tab = 'meteo' const TABS: { id: Tab; label: string; icon: string }[] = [ { id: 'meteo', label: 'Météo', icon: 'fa-cloud-sun' }, { id: 'radar', label: 'Radar', icon: 'fa-satellite-dish' }, { id: 'carte', label: 'La Carte', icon: 'fa-map-marked-alt' }, { id: 'plus', label: 'Plus', icon: 'fa-ellipsis-h' }, ] function renderApp(): void { const state = getState() // Écran d'accueil : pas de session valide. if (!state.session) { const container = h('div', { class: 'mobile-container' }) const main = h('main', { class: 'flex-1 overflow-y-auto no-scrollbar p-5' }) renderJoin(main, renderApp) container.append(main) mount(app, container) return } // App pleine : header + contenu + nav. const container = h('div', { class: 'mobile-container' }) const header = h( 'header', { class: 'pt-8 pb-3 px-6 bg-slate-900/80 backdrop-blur-md flex justify-between items-center z-20 border-b border-slate-800/60' }, h( 'div', { class: 'flex items-center gap-3' }, h('div', { class: 'w-9 h-9 rounded-2xl bg-gradient-to-tr from-sky-400 to-indigo-500 flex items-center justify-center text-white font-black shadow-md text-sm' }, 'ME'), h('div', {}, h('h1', { class: 'text-lg font-extrabold text-white tracking-tight leading-none' }, 'Météo Exam'), h('p', { class: 'text-xs font-bold text-sky-400 mt-0.5' }, `${state.session.klass.name}${state.session.klass.level ? ` · ${state.session.klass.level}` : ''}`), ), ), h( 'div', { class: 'flex items-center gap-2' }, isAdmin() ? h('span', { class: 'inline-flex items-center px-2.5 py-1 rounded-full text-xs font-extrabold bg-sky-500/10 text-sky-400 border border-sky-500/20' }, h('i', { class: 'fa-solid fa-user-shield mr-1' }), 'Admin') : null, h('span', { class: 'inline-flex items-center px-2.5 py-1 rounded-full text-xs font-extrabold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' }, h('span', { class: 'w-2 h-2 rounded-full bg-emerald-500 animate-ping mr-1.5' }), 'Direct'), ), ) const main = h('main', { class: 'flex-1 overflow-y-auto no-scrollbar p-5 pb-28 relative' }) const rerender = (): void => renderApp() switch (activeTab) { case 'meteo': renderWeek(main, rerender); break case 'radar': renderRadar(main, rerender); break case 'carte': renderMap(main, rerender); break case 'plus': renderPlus(main, rerender); break } const nav = h( 'nav', { class: 'absolute bottom-0 left-0 right-0 bg-slate-900/95 backdrop-blur-xl border-t border-slate-800 px-4 py-3 flex justify-around items-center z-30' }, ...TABS.map((tab) => { const active = tab.id === activeTab return h( 'button', { class: `flex flex-col items-center gap-1 transition-colors ${active ? 'text-sky-400' : 'text-slate-500'}`, onclick: () => { activeTab = tab.id renderApp() }, }, tab.id === 'radar' ? h('div', { class: 'relative' }, h('i', { class: `fa-solid ${tab.icon} text-xl` }), h('span', { class: 'absolute -top-1 -right-1 w-2 h-2 bg-rose-500 rounded-full' })) : h('i', { class: `fa-solid ${tab.icon} text-xl` }), h('span', { class: 'text-[10px] font-black tracking-wide' }, tab.label), ) }), ) container.append(header, main, nav) mount(app, container) } // --- Bootstrapping ----------------------------------------------------------- subscribe(renderApp) void bootstrap().then(() => { renderApp() console.log(`Météo Exam — v${__APP_VERSION__} — serveur : ${getBaseUrl()}`) // Vérification de mise à jour (Android natif uniquement). void checkForUpdate().then((update) => { if (update) { showUpdateModal(update, () => renderApp()) } }) })