// ============ AcademIA8 · Consola — Reglas de material + Vista de publicación ============ // El editor de unidad clásico (fragmentos/checkpoints separados) fue reemplazado // por el organizador (organizer.jsx), coherente con el mapeo a pasos del backend. // Este archivo conserva las reglas de material (compartidas con el organizador) y // la vista de publicación, ahora cableada a los endpoints reales del backend // (GET /diff, GET /disclaimer) — sin diff local ni copia publicada en el front. // ---------- Reglas por tipo de material (contenido de las ayudas contextuales) ---------- const MEDIA_RULES = { video: { types:['MP4'], help:(<> Subida directa (hasta 500 MB): se envía como archivo por WhatsApp. Si pesa más de 16 MB se comprime automáticamente al subir (podés ver el progreso en el editor) — el alumno siempre recibe un MP4 dentro del límite de WhatsApp. Video largo por link: alternativa si preferís pegar un link (YouTube, Vimeo) en vez de subir el archivo. El alumno recibe el link, no el archivo. >) }, imagen: { types:['JPEG','PNG'], help:(Hasta 5 MB. Formatos: JPEG, PNG.) }, audio: { types:['AAC','MP3','OGG','Opus'], help:(Hasta 16 MB. Formatos: AAC, MP3, OGG, Opus.) }, documento: { types:['PDF','DOC','DOCX'], help:(Hasta 100 MB. Formatos: PDF, DOC, DOCX, etc.) }, }; /* =================================================================== VISTA DE PUBLICACIÓN (diff borrador vs publicado — backend Camino A) - estado derivado: solo-borrador / publicado-limpio / publicado-con-cambios - diff: unidades `editadas` y `nuevas` (no hay movida ni borrada, Camino A) - disclaimer de alcance: por unidad editada, cuántos alumnos ya la cursaron =================================================================== */ function PublishView({ course, actions }){ const [diff, setDiff] = useState(null); const [disc, setDisc] = useState({}); const [loading, setLoading] = useState(true); const [err, setErr] = useState(null); const [publishing, setPublishing] = useState(false); const estado = course.derivedEstado; const firstPub = estado === 'solo-borrador'; const clean = estado === 'publicado-limpio'; // mapa unidad_id → {title, pos} desde el borrador cargado const unitById = {}; (course.draft?.units || []).forEach((u,i)=>{ unitById[u.id] = { title:u.title, pos:i+1, u }; }); useEffect(()=>{ let alive = true; (async ()=>{ setLoading(true); setErr(null); try { const d = await A8Api.diff(course.id); let dc = {}; if (d.editadas && d.editadas.length){ try { dc = await A8Api.disclaimer(course.id); } catch(e){ dc = {}; } } if (alive){ setDiff(d); setDisc(dc || {}); } } catch(e){ if (alive) setErr(e.message || String(e)); } finally { if (alive) setLoading(false); } })(); return ()=>{ alive = false; }; }, [course.id]); async function doPublish(){ setPublishing(true); const ok = await actions.publish(course.id); // recarga + toast; devuelve bool setPublishing(false); if (ok) actions.openCourse(course.id); } const nuevas = diff ? diff.nuevas : []; const editadas = diff ? diff.editadas : []; const metaCambiada = diff ? !!diff.meta_cambiada : false; const totalChanges = nuevas.length + editadas.length + (metaCambiada ? 1 : 0); const affectedTotal = Object.values(disc).reduce((s,n)=>s+n, 0); return (
{firstPub ? 'Agregá unidades y contenido antes de publicar.' : 'El borrador coincide con lo publicado.'}