feat: Fase 1 — Auth JWT+MFA, portales CRUD, UI completa

API:
- AuthModule: register, login, refresh, logout, MFA/TOTP setup+verify
- JwtStrategy + JwtAuthGuard + RolesGuard + CurrentUser decorator
- PackagesModule: CRUD paquetes + historial de estados
- PreAlertsModule: pre-alertas por usuario
- UsersModule: gestión de usuarios + roles + activación
- B2BModule: solicitudes de carga pesada/cotización
- ValidationPipe global + CORS configurado

Web (Next.js 15):
- globals.css completo (design system + utility classes)
- Layout raíz con WhatsApp flotante
- /login + /registro funcionales con JWT y redirección por rol
- /portal: dashboard, mi-casillero, mis-paquetes, pre-alerta, calculadora, perfil
- /admin: dashboard, usuarios (gestión roles/activación), tarifas, reportes, auditoría
- /bodega: dashboard, paquetes (crear+actualizar estado), verificación, despacho
- /tracking: tracking real con progreso visual + historial
- /calculadora: calculadora interactiva real (API SENAE §15)
- /como-funciona, /tarifas, /quienes-somos, /casillero
- /carga-pesada + /carga-pesada/cotizacion (formulario B2B)
- lib/api.ts: cliente HTTP con auto-refresh de token

Roles sincronizados con schema: SUPER_ADMIN, ADMIN_EMPRESA,
OPERADOR_BODEGA, AGENTE_ADUANERO, CLIENTE, SOPORTE
This commit is contained in:
Lizandro Guarnizo
2026-06-01 09:42:38 -05:00
parent 93db76897d
commit 4872053fd1
57 changed files with 4330 additions and 192 deletions
+65
View File
@@ -0,0 +1,65 @@
import Link from "next/link";
const TARIFAS = [
{ regime: "Mensajería Acelerada", limit: "Hasta $200 USD", taxes: "Sin impuestos", flete: "$8$15/lb", ideal: "Compras pequeñas · fast fashion · accesorios", badge: "Más popular" },
{ regime: "Courier", limit: "Hasta $400 USD", taxes: "Sin impuestos hasta $200", flete: "$8$15/lb", ideal: "Electrónica · ropa · zapatos", badge: "" },
{ regime: "Régimen 4×4", limit: "Hasta $2,000 USD", taxes: "FODINFA + Arancel + IVA", flete: "Según peso/volumen", ideal: "Equipos, repuestos, herramientas", badge: "" },
{ regime: "Carga pesada (FCL)", limit: "Sin límite", taxes: "Trámite formal", flete: "Cotización por m³", ideal: "Maquinaria · muebles · vehículos", badge: "" },
];
export default function TarifasPublicaPage() {
return (
<div style={{ minHeight: "100vh" }}>
<nav className="navbar">
<div className="container navbar-inner">
<Link href="/" className="navbar-logo">Moraworld<span>.</span></Link>
<div className="navbar-links">
<Link href="/como-funciona" className="navbar-link">Cómo funciona</Link>
<Link href="/calculadora" className="navbar-link">Calculadora</Link>
<Link href="/registro" className="btn btn-primary btn-sm">Crear casillero</Link>
</div>
</div>
</nav>
<div style={{ paddingTop: "var(--nav-h)" }}>
<div style={{ background: "linear-gradient(135deg, var(--dark) 0%, #0f2050 100%)", color: "white", padding: "4rem 0 3rem", textAlign: "center" }}>
<div className="container">
<h1 style={{ fontSize: "clamp(1.75rem,4vw,2.5rem)", fontWeight: 800, marginBottom: "1rem" }}>Tarifas y Costos</h1>
<p style={{ color: "rgba(255,255,255,.7)", maxWidth: 560, margin: "0 auto" }}>Precios transparentes, sin sorpresas en aduana.</p>
</div>
</div>
<div className="section-gray">
<div className="container">
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))", gap: "1.5rem" }}>
{TARIFAS.map(t => (
<div key={t.regime} className="card" style={{ padding: "1.5rem", position: "relative", borderTop: t.badge ? "4px solid var(--primary)" : "4px solid transparent" }}>
{t.badge && <span className="badge badge-blue" style={{ position: "absolute", top: "1rem", right: "1rem" }}>{t.badge}</span>}
<h3 style={{ fontWeight: 700, fontSize: "1.1rem", marginBottom: ".75rem" }}>{t.regime}</h3>
<div style={{ display: "flex", flexDirection: "column", gap: ".5rem", fontSize: ".9rem" }}>
<div><span style={{ color: "var(--gray-500)" }}>Límite: </span><strong>{t.limit}</strong></div>
<div><span style={{ color: "var(--gray-500)" }}>Impuestos: </span><strong>{t.taxes}</strong></div>
<div><span style={{ color: "var(--gray-500)" }}>Flete: </span><strong>{t.flete}</strong></div>
</div>
<div style={{ marginTop: "1rem", padding: ".75rem", background: "var(--gray-50)", borderRadius: "var(--radius)", fontSize: ".8rem", color: "var(--gray-500)" }}>
Ideal para: {t.ideal}
</div>
</div>
))}
</div>
<div style={{ textAlign: "center", marginTop: "3rem" }}>
<p style={{ color: "var(--gray-500)", marginBottom: "1rem" }}>¿Quieres saber exactamente cuánto pagarás?</p>
<Link href="/calculadora" className="btn btn-primary btn-lg">Usar la calculadora </Link>
</div>
<div className="alert alert-info" style={{ marginTop: "2rem" }}>
<strong>Impuestos SENAE:</strong> FODINFA 0.5% · Arancel 010% según partida arancelaria · IVA 15%.
Calculados sobre CIF (costo + seguro + flete). Los montos del régimen 4×4 se basan en facturas originales. §15 Reglamento COMEX.
</div>
</div>
</div>
</div>
</div>
);
}