diff --git a/public/audio/sonido_notificacion.mp3 b/public/audio/sonido_notificacion.mp3 new file mode 100644 index 0000000..1182e17 Binary files /dev/null and b/public/audio/sonido_notificacion.mp3 differ diff --git a/src/components/admin/BadgeSolicitudes.tsx b/src/components/admin/BadgeSolicitudes.tsx index 5531bf0..6a0a47b 100644 --- a/src/components/admin/BadgeSolicitudes.tsx +++ b/src/components/admin/BadgeSolicitudes.tsx @@ -38,6 +38,8 @@ export default function BadgeSolicitudes() { () => { setBadge(readBadge() + 1); toast({ title: 'Nueva solicitud pendiente', kind: 'info' }); + // Autoplay puede fallar (política del navegador antes de interacción); ignorar. + new Audio('/audio/sonido_notificacion.mp3').play().catch(() => {}); }, ) .on( diff --git a/src/components/admin/maestros/MaestroForm.tsx b/src/components/admin/maestros/MaestroForm.tsx index 00b4c4a..85cbe8e 100644 --- a/src/components/admin/maestros/MaestroForm.tsx +++ b/src/components/admin/maestros/MaestroForm.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'; type Props = { mode: 'create' | 'edit'; - maestro?: { id: number; nombre: string; activo: boolean }; + maestro?: { id: number; nombre: string; activo: boolean; es_tutor: boolean }; }; export default function MaestroForm({ mode, maestro }: Props) { @@ -11,6 +11,7 @@ export default function MaestroForm({ mode, maestro }: Props) { const errorRef = useRef(null); const [nombre, setNombre] = useState(maestro?.nombre ?? ''); const [activo, setActivo] = useState(maestro?.activo ?? true); + const [esTutor, setEsTutor] = useState(maestro?.es_tutor ?? false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -21,6 +22,7 @@ export default function MaestroForm({ mode, maestro }: Props) { if (mode === 'create') { setNombre(''); setActivo(true); + setEsTutor(false); } dialogRef.current?.showModal(); queueMicrotask(() => firstFieldRef.current?.focus()); @@ -60,7 +62,7 @@ export default function MaestroForm({ mode, maestro }: Props) { const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ nombre: trimmed, activo }), + body: JSON.stringify({ nombre: trimmed, activo, es_tutor: esTutor }), }); const json = await res.json().catch(() => ({})); if (!res.ok) throw new Error(json?.error ?? 'No se pudo guardar'); @@ -126,7 +128,17 @@ export default function MaestroForm({ mode, maestro }: Props) { checked={activo} onChange={(e) => setActivo(e.target.checked)} /> - Activo (visible en la lista de tutores) + Activo (visible en el catálogo de maestros) + + + {error && ( diff --git a/src/components/admin/solicitudes/AccionesSolicitud.tsx b/src/components/admin/solicitudes/AccionesSolicitud.tsx index 8ca17bb..7e0e573 100644 --- a/src/components/admin/solicitudes/AccionesSolicitud.tsx +++ b/src/components/admin/solicitudes/AccionesSolicitud.tsx @@ -41,7 +41,7 @@ function useDialog() { function AprobarDialog({ prestamo }: { prestamo: Prestamo }) { const dlg = useDialog(); - const [fecha, setFecha] = useState(enDias(7)); + const [fecha, setFecha] = useState(enDias(1)); const [notas, setNotas] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); diff --git a/src/components/admin/solicitudes/VerDetalles.tsx b/src/components/admin/solicitudes/VerDetalles.tsx index c70e550..dd4ecc4 100644 --- a/src/components/admin/solicitudes/VerDetalles.tsx +++ b/src/components/admin/solicitudes/VerDetalles.tsx @@ -197,7 +197,7 @@ export default function VerDetalles({ prestamoId }: { prestamoId: number }) {
Alumno
{data.prestamo.alumno?.nombre ?? data.prestamo.alumno?.email}
Maestro
-
{data.prestamo.maestro_responsable}
+
{data.prestamo.maestro_responsable}
Materiales
    diff --git a/src/components/alumno/SolicitudCart.tsx b/src/components/alumno/SolicitudCart.tsx index df21b37..8a4f38f 100644 --- a/src/components/alumno/SolicitudCart.tsx +++ b/src/components/alumno/SolicitudCart.tsx @@ -39,19 +39,25 @@ export function useCart() { const clamp = (n: number, max: number) => Math.max(1, Math.min(max, n)); +type MaestroOpt = { id: number; nombre: string }; + export default function CartProvider({ materiales, tutorNombre = null, perfilCompleto = true, + esDocente = false, + maestros = [], }: { materiales: Material[]; tutorNombre?: string | null; perfilCompleto?: boolean; + esDocente?: boolean; + maestros?: MaestroOpt[]; }) { const [cart, setCart] = useState>(new Map()); const dialogRef = useRef(null); - const firstFieldRef = useRef(null); - const [maestroResponsable, setMaestroResponsable] = useState(''); + const firstFieldRef = useRef(null); + const [maestroId, setMaestroId] = useState(''); const [notas, setNotas] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -141,11 +147,12 @@ export default function CartProvider({ setLoading(true); setError(null); try { + const maestroIdNum = maestroId ? Number(maestroId) : null; const res = await fetch('/api/solicitudes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - maestro_responsable: maestroResponsable, + maestro_id: esDocente ? null : maestroIdNum, notas: notas || undefined, items: items.map((i) => ({ material_id: i.material_id, @@ -159,7 +166,7 @@ export default function CartProvider({ throw new Error(json?.error ?? 'No se pudo enviar la solicitud'); } setOk(true); - const usedFallback = maestroResponsable.trim() === '' && !!tutorNombre; + const usedFallback = !esDocente && !maestroIdNum && !!tutorNombre; toastAfterReload({ kind: 'success', title: 'Solicitud enviada', @@ -317,30 +324,31 @@ export default function CartProvider({
)} -
- -