import { WEATHER } from '@meteo-exam/shared' import { h, mount } from '../dom' const SVG_NS = 'http://www.w3.org/2000/svg' function svgEl( tag: K, attrs: Record = {}, ): SVGElementTagNameMap[K] { const el = document.createElementNS(SVG_NS, tag) for (const [key, value] of Object.entries(attrs)) el.setAttribute(key, String(value)) return el } interface Region { id: string subject: string kind: keyof typeof WEATHER weight: number desc: string d: string labelX: number labelY: number emojiX: number emojiY: number } const REGIONS: Region[] = [ { id: 'maths', subject: 'Mathématiques', kind: 'tempete', weight: 4, d: 'M 150,40 Q 250,20 350,50 L 320,160 Q 220,180 160,150 Z', labelX: 230, labelY: 120, emojiX: 230, emojiY: 90, desc: 'Avis de forte tempête. Contrôle continu à fort coefficient programmé.' }, { id: 'svt', subject: 'SVT', kind: 'vent', weight: 2, d: 'M 350,50 Q 460,80 440,200 L 310,220 Q 300,160 320,160 Z', labelX: 375, labelY: 160, emojiX: 375, emojiY: 130, desc: 'Risque de petit QCM surprise en début d’heure sur le chapitre de la géologie.' }, { id: 'physique', subject: 'Physique-Chimie', kind: 'brouillard', weight: 2, d: 'M 160,150 Q 220,180 310,220 L 280,320 Q 180,310 140,240 Z', labelX: 220, labelY: 255, emojiX: 220, emojiY: 225, desc: 'Suspicion de TP évalué la semaine prochaine. Rumeurs soutenues par la classe.' }, { id: 'histoire', subject: 'Histoire-Géo', kind: 'soleil', weight: 1, d: 'M 150,40 L 160,150 Q 140,240 60,220 Q 30,120 150,40 Z', labelX: 90, labelY: 160, emojiX: 90, emojiY: 130, desc: 'Conditions idéales. Aucun contrôle prévu d’ici la fin de la semaine.' }, { id: 'francais', subject: 'Français', kind: 'soleil', weight: 1, d: 'M 140,240 Q 180,310 280,320 L 260,380 Q 120,390 60,220 Z', labelX: 170, labelY: 340, emojiX: 170, emojiY: 315, desc: 'Aucune perturbation à signaler. Cours magistral standard.' }, ] /** Carte SVG par matière — figée, alimentée par les données en phase 2. */ export function renderMap(root: HTMLElement, _rerender: () => void): void { let selected = 'maths' const detailTitle = h('h3', { class: 'text-base font-black text-slate-100' }) const detailStatus = h('p', { class: 'text-xs font-bold' }) const detailCoeff = h('span', { class: 'bg-slate-700 text-slate-200 text-xs font-black px-2.5 py-1 rounded-lg' }) const detailDesc = h('p', { class: 'text-xs text-slate-300 font-medium leading-relaxed mt-2 pt-2 border-t border-slate-700/60' }) const detailIcon = h('span', { class: 'text-3xl' }) const statusClasses: Record = { tempete: 'text-rose-400', orage: 'text-rose-400', vent: 'text-amber-400', brouillard: 'text-purple-400', soleil: 'text-emerald-400', } const updateDetail = (region: Region): void => { const meta = WEATHER[region.kind] detailIcon.textContent = meta.icon detailTitle.textContent = `Secteur : ${region.subject}` detailStatus.textContent = `${meta.title} • ${meta.hint}` detailStatus.className = `text-xs font-bold ${statusClasses[region.kind] ?? 'text-slate-400'}` detailCoeff.textContent = region.weight > 0 ? `Coeff ${region.weight.toFixed(1)}` : ' Calme' detailDesc.textContent = region.desc } const paths = new Map() const svg = svgEl('svg', { viewBox: '0 0 500 400' }) svg.setAttribute('class', 'w-full h-full drop-shadow-md') for (const region of REGIONS) { const meta = WEATHER[region.kind] const path = svgEl('path', { id: `region-${region.id}`, d: region.d, fill: meta.hex, }) path.setAttribute('class', 'map-region') path.setAttribute('style', 'transition:all 0.3s cubic-bezier(0.4,0,0.2,1);cursor:pointer;stroke:#0f172a;stroke-width:2.5') path.addEventListener('click', () => { selected = region.id for (const p of paths.values()) p.classList.remove('active-region') path.classList.add('active-region') updateDetail(region) }) paths.set(region.id, path) svg.append(path) const emoji = svgEl('text', { x: region.emojiX, y: region.emojiY, 'font-size': '22', 'pointer-events': 'none', }) emoji.textContent = meta.icon svg.append(emoji) const label = svgEl('text', { x: region.labelX, y: region.labelY, 'font-size': '12', 'font-weight': '900', fill: '#ffffff', 'text-anchor': 'middle', 'pointer-events': 'none', }) label.textContent = region.subject svg.append(label) } paths.get(selected)?.classList.add('active-region') updateDetail(REGIONS[0]!) mount( root, h( 'div', { class: 'space-y-4' }, h( 'div', { class: 'flex justify-between items-center' }, h('div', {}, h('h2', { class: 'text-lg font-black text-slate-100' }, 'Carte Météorologique'), h('p', { class: 'text-xs font-semibold text-slate-400' }, 'Secteurs géographiques par matière'), ), h('span', { class: 'px-2.5 py-1 rounded-full bg-sky-500/10 text-sky-400 text-xs font-bold border border-sky-500/20' }, 'Interactive'), ), h( 'div', { class: 'bg-slate-900 border border-slate-800 rounded-3xl p-3 shadow-lg relative overflow-hidden aspect-[4/3] flex items-center justify-center' }, svg, ), h( 'div', { class: 'bg-slate-800 border border-slate-700 rounded-2xl p-4' }, h('div', { class: 'flex justify-between items-center mb-2' }, h('div', { class: 'flex items-center gap-2.5' }, detailIcon, h('div', {}, detailTitle, detailStatus), ), detailCoeff, ), detailDesc, ), h( 'p', { class: 'text-[10px] text-slate-600 font-semibold text-center' }, 'Les secteurs seront alimentés par les données de la classe en phase 2.', ), ), ) }