2026-10-02 19:51:13 +02:00
import { h , mount } from '../dom'
import { api } from '../api'
2026-10-09 21:51:29 +02:00
import { getBaseUrl , setBaseUrl , DEFAULT_BASE_URL } from '../config'
2026-10-02 19:51:13 +02:00
import { loginAs } from '../store'
import { showToast } from '../toast'
type Mode = 'join' | 'create'
export function renderJoin ( root : HTMLElement , rerender : ( ) = > void ) : void {
let _mode : Mode = 'join'
const getMode = ( ) : Mode = > _mode
2026-10-09 21:51:29 +02:00
const nameInput = h ( 'input' , { class : 'app-input' , placeholder : 'Terminale S2' } )
const levelInput = h ( 'input' , { class : 'app-input' , placeholder : 'Lycée' } )
const codeInput = h ( 'input' , { class : 'app-input' , placeholder : 'TERMS2' , autocomplete : 'off' } )
const nicknameInput = h ( 'input' , { class : 'app-input' , placeholder : 'Votre pseudo' } )
2026-10-02 19:51:13 +02:00
const serverInput = h ( 'input' , {
2026-10-09 21:51:29 +02:00
class : 'app-input' ,
2026-10-02 19:51:13 +02:00
placeholder : DEFAULT_BASE_URL ,
value : getBaseUrl ( ) ,
autocomplete : 'off' ,
inputMode : 'url' ,
} )
2026-10-09 21:51:29 +02:00
// Wrappers pour toggle display
const codeWrap = h ( 'div' , { } )
const nameWrap = h ( 'div' , { } )
const levelWrap = h ( 'div' , { } )
const errorBox = h ( 'p' , {
style : 'display:none;font-size:12px;font-weight:700;color:#FB7185;background:color-mix(in srgb,var(--red) 10%,transparent);border:1px solid color-mix(in srgb,var(--red) 25%,transparent);border-radius:10px;padding:8px 12px' ,
2026-10-02 19:51:13 +02:00
} )
2026-10-09 21:51:29 +02:00
const submitBtn = h ( 'button' , { class : 'btn-primary' , style : 'width:100%' } )
2026-10-02 19:51:13 +02:00
const toggleBtn = h ( 'button' , {
2026-10-09 21:51:29 +02:00
style : 'width:100%;font-size:12px;font-weight:900;color:var(--blue2);padding:10px;background:none;border:0;cursor:pointer' ,
2026-10-02 19:51:13 +02:00
} )
const showError = ( message : string ) : void = > {
errorBox . textContent = message
2026-10-09 21:51:29 +02:00
errorBox . style . display = 'block'
2026-10-02 19:51:13 +02:00
}
const renderMode = ( ) : void = > {
const create = getMode ( ) === 'create'
2026-10-09 21:51:29 +02:00
submitBtn . replaceChildren ( create ? 'Créer la classe' : 'Rejoindre la classe' )
toggleBtn . replaceChildren ( create ? 'Déjà un code ? Rejoindre' : 'Créer une nouvelle classe' )
nameWrap . style . display = create ? '' : 'none'
levelWrap . style . display = create ? '' : 'none'
codeWrap . style . display = create ? 'none' : ''
errorBox . style . display = 'none'
2026-10-02 19:51:13 +02:00
}
const submit = async ( ) : Promise < void > = > {
const nickname = nicknameInput . value . trim ( )
2026-10-09 21:51:29 +02:00
if ( ! nickname ) { showError ( 'Choisissez un pseudo.' ) ; return }
setBaseUrl ( serverInput . value . trim ( ) . replace ( /\/+$/ , '' ) || DEFAULT_BASE_URL )
2026-10-02 19:51:13 +02:00
submitBtn . disabled = true
2026-10-09 21:51:29 +02:00
submitBtn . style . opacity = '0.6'
2026-10-02 19:51:13 +02:00
try {
if ( getMode ( ) === 'create' ) {
const name = nameInput . value . trim ( )
2026-10-09 21:51:29 +02:00
if ( name . length < 2 ) { showError ( 'Nom trop court.' ) ; submitBtn . disabled = false ; submitBtn . style . opacity = '1' ; return }
2026-10-02 19:51:13 +02:00
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 ( )
2026-10-09 21:51:29 +02:00
if ( ! code ) { showError ( 'Entrez le code.' ) ; submitBtn . disabled = false ; submitBtn . style . opacity = '1' ; return }
2026-10-02 19:51:13 +02:00
const result = await api . joinClass ( { code , nickname } )
await loginAs ( result . token , { member : result.member , klass : result.klass } )
}
rerender ( )
} catch ( error ) {
2026-10-09 21:51:29 +02:00
showError ( error instanceof Error ? error . message : 'Connexion impossible' )
2026-10-02 19:51:13 +02:00
submitBtn . disabled = false
2026-10-09 21:51:29 +02:00
submitBtn . style . opacity = '1'
2026-10-02 19:51:13 +02:00
}
}
submitBtn . addEventListener ( 'click' , submit )
2026-10-09 21:51:29 +02:00
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 ( ) } )
2026-10-02 19:51:13 +02:00
renderMode ( )
2026-10-09 21:51:29 +02:00
const field = ( label : string , control : HTMLElement , hint? : string ) : HTMLElement = >
h ( 'div' , { } ,
h ( 'span' , { class : 'app-label' } , label ) ,
control ,
hint ? h ( 'span' , { style : 'display:block;font-size:10px;color:var(--muted);font-weight:700;margin-top:4px' } , hint ) : null ,
)
// Remplir les wrappers
codeWrap . append ( field ( 'Code de classe' , codeInput , 'Demander le code à l’ admin.' ) )
nameWrap . append ( field ( 'Nom de la classe' , nameInput ) )
levelWrap . append ( field ( 'Niveau (optionnel)' , levelInput ) )
2026-10-02 19:51:13 +02:00
mount (
root ,
2026-10-09 21:51:29 +02:00
h ( 'div' , { style : 'display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:70vh;padding:0 24px' } ,
h ( 'div' , { class : 'app-mark paper-grid' , style : 'width:80px;height:80px;border-radius:24px;margin-bottom:24px' } ,
h ( 'div' , { style : 'font-family:\"Comic Sans MS\",\"Bradley Hand\",cursive;color:#EF4444;font-size:60px;font-weight:900;font-style:italic;line-height:80px;text-align:center;transform:rotate(-8deg)' } , '0' ) ,
2026-10-02 19:51:13 +02:00
) ,
2026-10-09 21:51:29 +02:00
h ( 'h1' , { style : 'font-size:24px;font-weight:700;color:var(--text);text-align:center;font-family:Fredoka,sans-serif' } , 'Météo Exam' ) ,
h ( 'p' , { style : 'font-size:12px;font-weight:700;color:var(--blue2);margin:6px 0 32px;text-align:center' } ,
'Le ciel de tes évaluations.' ) ,
h ( 'div' , { style : 'width:100%;display:grid;gap:16px' } ,
2026-10-02 19:51:13 +02:00
field ( 'Votre pseudo' , nicknameInput ) ,
2026-10-09 21:51:29 +02:00
codeWrap ,
nameWrap ,
levelWrap ,
field ( 'Serveur' , serverInput , 'Émulateur : 10.0.2.2:8787 · Réseau : IP du PC' ) ,
2026-10-02 19:51:13 +02:00
errorBox ,
submitBtn ,
toggleBtn ,
) ,
2026-10-09 21:51:29 +02:00
h ( 'p' , { style : 'font-size:10px;color:var(--muted);font-weight:800;margin-top:32px;text-align:center' } ,
'L’ admin crée le calendrier. Les élèves le consultent.' ) ,
2026-10-02 19:51:13 +02:00
) ,
)
}