desvergue-de-alexandro
js 79 lines 5.32 KB
Raw
1 /**
2 * Círculos de estudio.
3 * Contrato esperado de GET /circulos:
4 * { sesiones, misGrupos } con sesiones [{ id, titulo, materia, fecha, enVivo,
5 * moderador, plataforma, descripcion, asistentes }] y misGrupos [{ id, nombre,
6 * descripcion, miembros }].
7 */
8 document.addEventListener('DOMContentLoaded', async () => {
9 Layout.init('circulos.html');
10 Layout.setPageTitle('Círculos de Estudio');
11
12 const content = document.getElementById('main-content');
13 if (!content) return;
14 content.innerHTML = '<div class="circulos-container"><p class="loading-message">Cargando círculos de estudio…</p></div>';
15 await loadCircles(content);
16 });
17
18 async function loadCircles(content, search = '') {
19 let response = {};
20 try {
21 response = await API.request(`/circulos${search ? `?busqueda=${encodeURIComponent(search)}` : ''}`);
22 } catch {
23 // El endpoint se conectará cuando el módulo esté disponible en backend.
24 }
25
26 const sessions = Array.isArray(response.sesiones) ? response.sesiones : [];
27 const groups = Array.isArray(response.misGrupos) ? response.misGrupos : [];
28 renderCircles(content, sessions, groups);
29 }
30
31 function renderCircles(content, sessions, groups) {
32 content.innerHTML = `
33 <div class="circulos-container">
34 <header class="module-heading"><h1>Círculos de estudio</h1><p>Encuentra sesiones de repaso y comunidades para aprender en conjunto.</p></header>
35 <form class="top-action-bar" id="circle-search-form">
36 <label class="visually-hidden" for="circle-search">Buscar círculo o sesión</label>
37 <input class="search-input" id="circle-search" name="busqueda" type="search" placeholder="Buscar círculo o sesión por materia...">
38 <button class="btn-solid" type="button" id="create-circle">+ Crear nuevo círculo</button>
39 </form>
40 <div class="circulos-grid">
41 <section><h2 class="section-title">Próximas sesiones de repaso</h2>${sessions.length ? sessions.map(renderSession).join('') : renderEmptySessions()}</section>
42 <aside><h2 class="section-title">Tus grupos permanentes</h2>${groups.length ? `<section class="card">${groups.map(renderGroup).join('')}</section>` : ''}<section class="card rules-card"><h2>Normas del círculo</h2><p>Los círculos son espacios colaborativos. Comparte material libre de plagio y mantén el respeto en las salas de videollamada.</p></section></aside>
43 </div>
44 </div>
45 `;
46
47 document.getElementById('circle-search-form')?.addEventListener('submit', event => {
48 event.preventDefault();
49 loadCircles(content, new FormData(event.currentTarget).get('busqueda')?.trim());
50 });
51 document.getElementById('create-circle')?.addEventListener('click', () => {
52 // La pantalla de creación se conectará cuando esté disponible el endpoint POST /circulos.
53 });
54 }
55
56 function renderSession(session) {
57 const live = Boolean(session.enVivo);
58 return `
59 <article class="card session-card ${live ? 'live-session' : ''}">
60 <div class="session-header"><div class="badges-wrap">${live ? '<span class="badge badge-live">● En vivo ahora</span>' : session.fecha ? `<span class="badge badge-time">${escapeHtml(session.fecha)}</span>` : ''}${session.materia ? `<span class="badge badge-materia">${escapeHtml(session.materia)}</span>` : ''}</div>${session.sala ? `<span class="attendees-count">${escapeHtml(session.sala)}</span>` : ''}</div>
61 <h2 class="session-title">${escapeHtml(session.titulo || 'Sesión de repaso')}</h2>
62 ${(session.moderador || session.plataforma) ? `<p class="session-info">${session.moderador ? `Moderador: <strong>${escapeHtml(session.moderador)}</strong>` : ''}${session.moderador && session.plataforma ? ' · ' : ''}${session.plataforma ? `Plataforma: <strong>${escapeHtml(session.plataforma)}</strong>` : ''}</p>` : ''}
63 ${session.descripcion ? `<p class="session-desc">${escapeHtml(session.descripcion)}</p>` : ''}
64 <div class="session-footer">${Number.isFinite(Number(session.asistentes)) ? `<span class="attendees-count">👥 ${Number(session.asistentes)} ${live ? 'estudiantes conectados' : 'confirmados'}</span>` : ''}<div class="session-actions">${session.materialDisponible ? '<button class="btn-clean" type="button">Ver material de apoyo</button>' : ''}<button class="btn-solid" type="button">${live ? 'Entrar a sala' : 'Confirmar asistencia'}</button></div></div>
65 </article>
66 `;
67 }
68
69 function renderGroup(group) {
70 return `<article class="group-item"><span class="group-name">${escapeHtml(group.nombre || 'Grupo de estudio')}</span>${group.descripcion ? `<span class="group-description">${escapeHtml(group.descripcion)}</span>` : ''}<div class="group-meta">${Number.isFinite(Number(group.miembros)) ? `<span>${Number(group.miembros)} miembros</span>` : '<span></span>'}<button class="group-link" type="button">Ver grupo →</button></div></article>`;
71 }
72
73 function renderEmptySessions() {
74 return '<section class="card empty-circles"><h2>Aún no hay sesiones programadas</h2><p>Las sesiones de repaso y los círculos disponibles aparecerán aquí cuando sean creados.</p></section>';
75 }
76
77 function escapeHtml(value) {
78 return String(value ?? '').replace(/[&<>'"]/g, character => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;' })[character]);
79 }