e159e8d297
Perfil de usuario: - /perfil con avatar, matrícula, semestre, tutor, foto de perfil - /onboarding opcional (skippeable) para primer login - Avatar con prioridad: foto propia > Google OAuth > iniciales sobre color HSL - Endpoint PATCH /api/profile con validación y guard self-only - Middleware carga semestre, tutor_id, foto_path - Sidebar footer con Avatar + link a perfil CRUD admin de maestros: - /admin/maestros con tabla desktop / cards mobile - MaestroForm + EliminarMaestro (mismo patrón que categorias) - Endpoints POST/PATCH/DELETE con guard admin + 23503 traducido - Subnav de inventario ahora incluye tab Maestros Home reformulado: - Alumno: saludo por hora + CTA "¿Qué vas a pedir hoy?" + card de último préstamo activo - Admin: saludo + 2 KPIs de alerta (Pendientes, Vencidos) + últimas 2 pendientes + actividad reciente (tabla desktop, cards mobile — fix del bug 6) - Helper saludoHora() y fmtFechaRelativa() en date.ts con offset MX Checkout con tutor auto: - SolicitudCart: input maestro → textarea con placeholder = nombre del tutor - Guard perfil incompleto: CTA "Completa tu perfil" si !matricula || !tutor_id - RPC crear_solicitud: fallback automático al nombre del tutor si textarea vacío Fixes: - Bug upload imagen: Dockerfile ARG + docker-compose build.args pasan PUBLIC_* al bundle client de Vite - Grid inventario mobile: acciones en grid 2-col con prop compact en botones - UnidadesManager mobile: tabla reemplazada por cards, select estado con min-width 130px - Chart donut: Legend horizontal debajo (sin labels internos cortadas) - Chart barras: YAxis width fijo 100 + tickFormatter que trunca >14 chars - Panel admin mobile: tabla ahora tiene versión cards mobile (parte del rediseño home)
47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
import { defineMiddleware } from 'astro:middleware';
|
|
import { serverClient } from '@/lib/supabase';
|
|
|
|
const UABC_DOMAIN = '@uabc.edu.mx';
|
|
const PUBLIC_ROUTES = ['/login', '/api/auth/signin', '/api/auth/callback', '/api/auth/signout'];
|
|
|
|
export const onRequest = defineMiddleware(async (context, next) => {
|
|
const supabase = serverClient(context.cookies);
|
|
context.locals.supabase = supabase;
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
|
|
if (user && !user.email?.toLowerCase().endsWith(UABC_DOMAIN)) {
|
|
await supabase.auth.signOut();
|
|
return context.redirect('/login?error=dominio');
|
|
}
|
|
|
|
context.locals.user = user;
|
|
context.locals.profile = null;
|
|
|
|
if (user) {
|
|
const { data: profile } = await supabase
|
|
.from('profiles')
|
|
.select('id, email, nombre, matricula, rol, semestre, tutor_id, foto_path')
|
|
.eq('id', user.id)
|
|
.maybeSingle();
|
|
context.locals.profile = profile ?? null;
|
|
}
|
|
|
|
const { pathname } = context.url;
|
|
const isPublic = PUBLIC_ROUTES.includes(pathname);
|
|
|
|
if (!user && !isPublic) {
|
|
return context.redirect('/login');
|
|
}
|
|
|
|
if (user && pathname === '/login') {
|
|
return context.redirect('/');
|
|
}
|
|
|
|
if (pathname.startsWith('/admin') && context.locals.profile?.rol !== 'admin') {
|
|
return context.rewrite('/403');
|
|
}
|
|
|
|
return next();
|
|
});
|