Météo Exam v0.1.0 — calendrier des évaluations en bulletin météo

- API Express + SQLite : classes, évaluations, SSE, auth Bearer
- Front Vite + TS + Tailwind : frise 7 jours, Exam Cards, modales
- Capacitor Android : APK debug, CapacitorHttp natif
- Seed : classe Terminale S2 (code TERMS2)
This commit is contained in:
Mathis 2026-10-02 19:51:13 +02:00
commit 6d154ed5fb
92 changed files with 9574 additions and 0 deletions

39
web/src/toast.ts Normal file
View file

@ -0,0 +1,39 @@
type ToastKind = 'success' | 'reject' | 'info'
let container: HTMLElement | null = null
let hideTimer: number | undefined
function ensureContainer(): HTMLElement {
if (container && document.body.contains(container)) return container
container = document.createElement('div')
container.className =
'fixed top-20 left-1/2 -translate-x-1/2 z-[100] flex items-center gap-2 px-4 py-2 rounded-full text-xs font-bold shadow-2xl border border-slate-700 bg-slate-800 text-slate-100 transition-all duration-300 opacity-0 pointer-events-none'
document.body.append(container)
return container
}
const ICONS: Record<ToastKind, string> = {
success: 'fa-solid fa-circle-check text-emerald-400',
reject: 'fa-solid fa-circle-xmark text-rose-400',
info: 'fa-solid fa-circle-info text-sky-400',
}
export function showToast(message: string, kind: ToastKind = 'success'): void {
const el = ensureContainer()
el.replaceChildren()
const icon = document.createElement('i')
icon.className = ICONS[kind]
const text = document.createElement('span')
text.textContent = message
el.append(icon, text)
el.classList.remove('opacity-0', '-translate-y-2', 'pointer-events-none')
el.classList.add('opacity-100', 'translate-y-0')
window.clearTimeout(hideTimer)
hideTimer = window.setTimeout(() => {
el.classList.remove('opacity-100', 'translate-y-0')
el.classList.add('opacity-0', '-translate-y-2', 'pointer-events-none')
}, 2600)
}