import { h, mount } from '../dom' import { api } from '../api' import { getBaseUrl, setBaseUrl } from '../config' import { loginAs } from '../store' import { showToast } from '../toast' import { DEFAULT_BASE_URL } from '../config' const inputClass = 'w-full bg-slate-900/80 border border-slate-700/80 rounded-xl px-3.5 py-3 text-sm text-slate-100 placeholder-slate-500 focus:outline-none focus:border-sky-500 transition-colors' const labelClass = 'block text-[10px] font-black uppercase tracking-wider text-slate-500 mb-1.5' type Mode = 'join' | 'create' export function renderJoin(root: HTMLElement, rerender: () => void): void { let _mode: Mode = 'join' const getMode = (): Mode => _mode const nameInput = h('input', { class: inputClass, placeholder: 'Terminale S2' }) const levelInput = h('input', { class: inputClass, placeholder: 'Lycée' }) const codeInput = h('input', { class: inputClass, placeholder: 'TERMS2', autocomplete: 'off' }) const nicknameInput = h('input', { class: inputClass, placeholder: 'Votre pseudo' }) const serverInput = h('input', { class: inputClass, placeholder: DEFAULT_BASE_URL, value: getBaseUrl(), autocomplete: 'off', inputMode: 'url', }) const errorBox = h('p', { class: 'hidden text-xs font-bold text-rose-300 bg-rose-500/10 border border-rose-500/25 rounded-xl px-3 py-2' }) const codeHint = h('p', { class: 'hidden text-xs text-slate-500 font-semibold' }) const submitBtn = h('button', { class: 'w-full bg-sky-500 text-slate-950 text-sm font-extrabold py-3.5 rounded-xl active:scale-[0.98] transition-transform', }) const toggleBtn = h('button', { class: 'w-full text-xs font-black text-sky-400 py-2.5 active:scale-95 transition-transform', }) const showError = (message: string): void => { errorBox.textContent = message errorBox.classList.remove('hidden') } const renderMode = (): void => { const create = getMode() === 'create' submitBtn.replaceChildren( create ? 'Créer la classe' : 'Rejoindre la classe', ) toggleBtn.replaceChildren( create ? 'Déjà un code ? Rejoindre une classe' : 'Créer une nouvelle classe', ) codeHint.classList.toggle('hidden', create) nameInput.parentElement?.classList.toggle('hidden', !create) levelInput.parentElement?.classList.toggle('hidden', !create) codeInput.parentElement?.classList.toggle('hidden', create) errorBox.classList.add('hidden') } const submit = async (): Promise => { const nickname = nicknameInput.value.trim() if (!nickname) { showError('Choisissez un pseudo.') return } const baseUrl = serverInput.value.trim().replace(/\/+$/, '') || DEFAULT_BASE_URL setBaseUrl(baseUrl) submitBtn.disabled = true submitBtn.classList.add('opacity-60') try { if (getMode() === 'create') { const name = nameInput.value.trim() if (name.length < 2) { showError('Nom de classe trop court.') submitBtn.disabled = false submitBtn.classList.remove('opacity-60') return } const result = await api.createClass({ name, level: levelInput.value.trim() || null, nickname }) showToast(`Classe créée — code ${result.code}`, 'success') await loginAs(result.token, { member: result.member, klass: result.klass }) } else { const code = codeInput.value.trim() if (!code) { showError('Entrez le code de classe.') submitBtn.disabled = false submitBtn.classList.remove('opacity-60') return } const result = await api.joinClass({ code, nickname }) await loginAs(result.token, { member: result.member, klass: result.klass }) } rerender() } catch (error) { const message = error instanceof Error ? error.message : 'Connexion impossible' showError(message) submitBtn.disabled = false submitBtn.classList.remove('opacity-60') } } submitBtn.addEventListener('click', submit) toggleBtn.addEventListener('click', () => { _mode = _mode === 'join' ? 'create' : 'join' renderMode() }) codeInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') submit() }) nicknameInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') submit() }) codeInput.addEventListener('input', () => { codeInput.value = codeInput.value.toUpperCase() }) renderMode() mount( root, h( 'div', { class: 'flex flex-col items-center justify-center min-h-[70vh] px-6' }, h( 'div', { class: 'w-20 h-20 rounded-[1.75rem] bg-gradient-to-tr from-sky-400 to-indigo-500 flex items-center justify-center text-white font-black text-3xl shadow-float mb-6' }, 'ME', ), h('h1', { class: 'text-2xl font-black text-white text-center tracking-tight' }, 'Météo Exam'), h('p', { class: 'text-xs font-bold text-sky-400 mt-1 mb-8 text-center' }, 'Le calendrier des évaluations, en bulletin météo.'), h( 'div', { class: 'w-full space-y-4' }, field('Votre pseudo', nicknameInput), getMode() === 'join' ? field('Code de classe', codeInput) : null, getMode() === 'join' ? h('p', { class: 'text-[11px] text-slate-500 font-semibold -mt-2' }, 'Demander le code à l’admin de la classe (ex. TERMS2).') : null, getMode() === 'create' ? field('Nom de la classe', nameInput) : null, getMode() === 'create' ? field('Niveau (optionnel)', levelInput) : null, field('Serveur', serverInput, 'Adresse de l’API. Emulateur Android : 10.0.2.2:8787'), errorBox, submitBtn, toggleBtn, codeHint, ), h( 'p', { class: 'text-[10px] text-slate-600 font-semibold mt-8 text-center' }, 'L’admin de la classe crée le calendrier. Les élèves le consultent et participent au radar.', ), ), ) function field(label: string, control: HTMLElement, hint?: string): HTMLElement { return h( 'label', { class: 'block' }, h('span', { class: labelClass }, label), control, hint ? h('span', { class: 'block text-[10px] text-slate-600 font-semibold mt-1' }, hint) : null, ) } }