main
js 273 lines 13.7 KB
Raw
1 (function () {
2 let rolActual = null;
3
4 document.addEventListener('DOMContentLoaded', () => {
5 if (!Auth.requireAuth()) return;
6
7 const user = Auth.getUser();
8 rolActual = user?.rol || '';
9 document.getElementById('ono-titulo').textContent =
10 rolActual === 'PROFESOR' ? 'Perfil del Maestro' : 'Bienvenido, estudiante';
11
12 if (rolActual === 'ESTUDIANTE') {
13 document.getElementById('ono-subtitulo').textContent =
14 'Cuéntanos tu carrera y semestre para mostrar tus materias y empezar a navegar.';
15 renderFormularioEstudiante();
16 } else if (rolActual === 'PROFESOR') {
17 document.getElementById('ono-subtitulo').textContent =
18 'Define tu asignación académica y tus datos de asesoría para aparecer en el directorio.';
19 renderFormularioProfesor();
20 } else {
21 window.location.href = Auth.resolvePath('pages/inicio.html');
22 }
23 });
24
25 function cargarOpciones(url, textoVacio) {
26 return API.request(url).then(res => res || []);
27 }
28
29 /* ------------------------- ESTUDIANTE ------------------------- */
30
31 async function renderFormularioEstudiante() {
32 const cont = document.getElementById('ono-contenido');
33 const matricula = sessionStorage.getItem('pending-matricula') || '';
34 cont.innerHTML = `
35 <form id="ono-form-estudiante">
36 <div class="form-group">
37 <label for="ono-matricula">Matrícula</label>
38 <input type="text" id="ono-matricula" class="form-control" required
39 value="${escapeHtml(matricula)}" placeholder="A12345678">
40 </div>
41 <div class="form-group">
42 <label for="ono-carrera">Carrera</label>
43 <select id="ono-carrera" class="form-control" required>
44 <option value="">Selecciona tu carrera...</option>
45 </select>
46 </div>
47 <div class="form-group">
48 <label>Semestre</label>
49 <div class="ono-grid" id="ono-semestres"></div>
50 </div>
51 <div class="ono-section">
52 <h3>Materias de tu semestre</h3>
53 <div id="ono-materias">${spinnerHtml()}</div>
54 </div>
55 <div class="form-group" style="margin-top:1.25rem">
56 <button type="submit" class="btn btn-primary" style="width:100%">Confirmar y empezar a navegar</button>
57 </div>
58 </form>`;
59
60 const selCarrera = document.getElementById('ono-carrera');
61 const selSemestre = document.getElementById('ono-semestres');
62
63 try {
64 const carreras = await cargarOpciones('/carreras', 'Sin carreras');
65 selCarrera.innerHTML =
66 '<option value="">Selecciona tu carrera...</option>' +
67 carreras.map(c => `<option value="${c.id}">${escapeHtml(c.nombre)}</option>`).join('');
68
69 selSemestre.innerHTML = Array.from({ length: 12 }, (_, i) => i + 1).map(s =>
70 `<label class="${s === 1 ? 'sem-activo' : ''}">
71 <input type="radio" name="ono-semestre" value="${s}" ${s === 1 ? 'checked' : ''}>
72 ${s}°
73 </label>`).join('');
74
75 selSemestre.addEventListener('change', (e) => {
76 if (e.target.name === 'ono-semestre') {
77 selSemestre.querySelectorAll('label').forEach(l => l.classList.remove('sem-activo'));
78 e.target.closest('label').classList.add('sem-activo');
79 cargarMateriasEstudiante();
80 }
81 });
82 selCarrera.addEventListener('change', cargarMateriasEstudiante);
83
84 if (carreras.length) cargarMateriasEstudiante();
85
86 document.getElementById('ono-form-estudiante').addEventListener('submit', async (ev) => {
87 ev.preventDefault();
88 Utils.clearAlert('alert-container');
89 const btn = ev.target.querySelector('button[type="submit"]');
90
91 if (!selCarrera.value) {
92 Utils.showAlert('alert-container', 'Selecciona tu carrera.');
93 return;
94 }
95 const semestre = Number(document.querySelector('input[name="ono-semestre"]:checked')?.value || 1);
96
97 try {
98 btn.disabled = true;
99 btn.textContent = 'Guardando...';
100 const materias = [...document.querySelectorAll('#ono-materias input[type="checkbox"]:checked')]
101 .map(cb => Number(cb.value));
102 await API.request('/auth/onboarding/estudiante', {
103 method: 'POST',
104 body: JSON.stringify({
105 matricula: document.getElementById('ono-matricula').value.trim(),
106 idCarrera: Number(selCarrera.value),
107 semestre,
108 materias
109 })
110 });
111 sessionStorage.removeItem('pending-matricula');
112 window.location.href = Auth.resolvePath('pages/inicio.html');
113 } catch (error) {
114 Utils.showAlert('alert-container', error.message || 'No se pudo completar el perfil.');
115 btn.disabled = false;
116 btn.textContent = 'Confirmar y empezar a navegar';
117 }
118 });
119 } catch (error) {
120 cont.innerHTML = `<div class="alert alert-error">Error al cargar el catálogo: ${escapeHtml(error.message)}</div>`;
121 }
122 }
123
124 async function cargarMateriasEstudiante() {
125 const idCarrera = document.getElementById('ono-carrera')?.value;
126 const semestre = Number(document.querySelector('input[name="ono-semestre"]:checked')?.value || 1);
127 const contMaterias = document.getElementById('ono-materias');
128 if (!idCarrera) {
129 contMaterias.innerHTML = '<div class="ono-espera">Selecciona tu carrera para ver las materias.</div>';
130 return;
131 }
132 contMaterias.innerHTML = spinnerHtml();
133 try {
134 const materias = await cargarOpciones(`/materias/plan/${idCarrera}/${semestre}`, 'Sin materias');
135 contMaterias.innerHTML = materias.length
136 ? `<div class="ono-grid">${materias.map(m => `
137 <label>
138 <input type="checkbox" name="ono-materia" value="${m.id}"> ${escapeHtml(m.nombre)}
139 </label>`).join('')}
140 </div>
141 <div class="form-hint">Marca las materias que cursas este semestre.</div>`
142 : '<div class="ono-espera">No hay materias registradas para ese semestre.</div>';
143 } catch (error) {
144 contMaterias.innerHTML = `<div class="alert alert-error">${escapeHtml(error.message)}</div>`;
145 }
146 }
147
148 /* ------------------------- PROFESOR ------------------------- */
149
150 async function renderFormularioProfesor() {
151 const cont = document.getElementById('ono-contenido');
152 const nomina = sessionStorage.getItem('pending-numeroNomina') || '';
153 cont.innerHTML = `
154 <form id="ono-form-profesor">
155 <div class="form-group">
156 <label for="ono-nomina">Nómina / Matrícula Docente</label>
157 <input type="text" id="ono-nomina" class="form-control" required
158 value="${escapeHtml(nomina)}" placeholder="L01234567">
159 </div>
160 <div class="ono-section">
161 <h3>Asignación Académica y Especialidad (Ruta MAPS)</h3>
162 <div class="form-group">
163 <label for="ono-area">Área / Carrera principal</label>
164 <select id="ono-area" class="form-control">
165 <option value="">Selecciona el departamento...</option>
166 </select>
167 </div>
168 <div class="form-group">
169 <label>Certificados a cargo</label>
170 <div class="ono-grid" id="ono-certificados"><div class="ono-espera">${spinnerHtml()}</div></div>
171 </div>
172 <div class="form-group">
173 <label>Materias que imparte</label>
174 <div class="ono-grid" id="ono-materias-prof"><div class="ono-espera">${spinnerHtml()}</div></div>
175 </div>
176 <div class="form-group">
177 <label for="ono-semestres">Semestres asignados</label>
178 <input type="text" id="ono-semestres" class="form-control" placeholder="ej. 4°, 5°, 6°">
179 <div class="form-hint">Ciclos académicos donde tiene grupos activos.</div>
180 </div>
181 </div>
182 <div class="ono-section">
183 <h3>Datos de Asesoría y Contacto</h3>
184 <div class="form-group">
185 <label for="ono-horario">Horario de asesoría semanal</label>
186 <input type="text" id="ono-horario" class="form-control" placeholder="ej. Lunes y Miércoles 4:00 PM - 6:00 PM">
187 </div>
188 <div class="form-group">
189 <label for="ono-enlace">Enlace de sala virtual</label>
190 <input type="url" id="ono-enlace" class="form-control" placeholder="https://teams.microsoft.com/...">
191 </div>
192 <div class="form-group">
193 <label for="ono-semblanza">Semblanza / Especialidad técnica</label>
194 <textarea id="ono-semblanza" class="form-control" rows="3"
195 placeholder="ej. Especialista en arquitectura backend Java y modelado relacional en MySQL."></textarea>
196 </div>
197 </div>
198 <div class="form-group" style="margin-top:1.25rem">
199 <button type="submit" class="btn btn-primary" style="width:100%">Guardar y empezar a navegar</button>
200 </div>
201 </form>`;
202
203 const selArea = document.getElementById('ono-area');
204 const contCert = document.getElementById('ono-certificados');
205 const contMat = document.getElementById('ono-materias-prof');
206
207 const [carreras, certificados, materias] = await Promise.all([
208 cargarOpciones('/carreras', 'Sin carreras'),
209 cargarOpciones('/certificados', 'Sin certificados'),
210 cargarOpciones('/materias', 'Sin materias')
211 ]);
212
213 selArea.innerHTML =
214 '<option value="">Selecciona el departamento...</option>' +
215 carreras.map(c => `<option value="${escapeHtml(c.nombre)}">${escapeHtml(c.nombre)}</option>`).join('');
216
217 contCert.innerHTML = certificados.length
218 ? certificados.map(c => `<label>
219 <input type="checkbox" name="ono-certificado" value="${c.id}"> ${escapeHtml(c.nombre)}
220 </label>`).join('')
221 : `<div class="ono-espera" style="grid-column:1/-1">Aún no hay certificados registrados en el catálogo.</div>`;
222
223 contMat.innerHTML = materias.length
224 ? materias.map(m => `<label>
225 <input type="checkbox" name="ono-materia-prof" value="${m.id}"> ${escapeHtml(m.nombre)}
226 </label>`).join('')
227 : `<div class="ono-espera" style="grid-column:1/-1">Aún no hay materias en el catálogo.</div>`;
228
229 document.getElementById('ono-form-profesor').addEventListener('submit', async (ev) => {
230 ev.preventDefault();
231 Utils.clearAlert('alert-container');
232 const btn = ev.target.querySelector('button[type="submit"]');
233 const certIds = [...document.querySelectorAll('input[name="ono-certificado"]:checked')].map(i => Number(i.value));
234 const matIds = [...document.querySelectorAll('input[name="ono-materia-prof"]:checked')].map(i => Number(i.value));
235
236 try {
237 btn.disabled = true;
238 btn.textContent = 'Guardando...';
239 await API.request('/auth/onboarding/profesor', {
240 method: 'POST',
241 body: JSON.stringify({
242 numeroNomina: document.getElementById('ono-nomina').value.trim(),
243 areaEspecialidad: selArea.value,
244 semestresAsignados: document.getElementById('ono-semestres').value.trim(),
245 horarioAsesorias: document.getElementById('ono-horario').value.trim(),
246 enlaceSalaVirtual: document.getElementById('ono-enlace').value.trim(),
247 semblanza: document.getElementById('ono-semblanza').value.trim(),
248 certificados: certIds,
249 materias: matIds
250 })
251 });
252 sessionStorage.removeItem('pending-numeroNomina');
253 window.location.href = Auth.resolvePath('pages/inicio.html');
254 } catch (error) {
255 Utils.showAlert('alert-container', error.message || 'No se pudo completar el perfil.');
256 btn.disabled = false;
257 btn.textContent = 'Guardar y empezar a navegar';
258 }
259 });
260 }
261
262 /* ------------------------- UTILIDADES ------------------------- */
263
264 function escapeHtml(texto) {
265 return String(texto || '').replace(/[&<>"']/g, c => ({
266 '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
267 }[c]));
268 }
269
270 function spinnerHtml() {
271 return '<div class="ono-espera">Cargando...</div>';
272 }
273 })();