Files
moraworld/apps/web/src/app/calculadora/page.tsx
T
Lizandro Guarnizo 93db76897d feat: audit & sync with documentacion.html v1.1 — schema, tests, modules, web
Schema (packages/database/prisma/schema.prisma):
- Add SenaeCategory, B2BRequestStatus, NotificationChannel enums
- Add Package fields: lengthCm, widthCm, heightCm, hasDiscrepancy, photos[],
  senaeCategory, senaeAuthNumber, senaeDeclarationId
- Add userId field to PreAlert
- Add RefreshToken model (JWT auth — Fase 1)
- Add Tariff model (configurable rates: pricePerLb, insurancePct, etc.)
- Add B2BRequest model (heavy cargo imports — doc §13)
- Add Notification model (multi-channel: email, whatsapp, sms, push — doc §16)
- Add userAgent to AuditLog

API (apps/api):
- Add CalculatorModule: calculates SENAE cost breakdown (flete, seguro,
  FODINFA, arancel, IVA) per formulas in doc §15
- Add TrackingModule: public GET /api/tracking/:id endpoint (doc §07)
- Register both modules in AppModule
- Add Jest config + test scripts (test, test:watch, test:cov, test:ci)

Tests (80 tests, 97.45% coverage):
- calculator.util.spec.ts: 35 tests — SENAE formulas, volumetric weight,
  4x4 regime, tariff rates, error validation, breakdown
- tracking-id.util.spec.ts: 13 tests — format, uniqueness, validation
- suite-code.util.spec.ts: 13 tests — generation, parsing, address builder
- health.controller.spec.ts: 5 tests — ok/degraded/timestamp
- calculator.spec.ts: 8 tests — controller + service unit tests
- tracking.controller.spec.ts: 9 tests — public search, 404, no sensitive data

Web (apps/web):
- page.tsx: Full landing page matching documentation (Hero, Services,
  8 Modules, Tracking states, SENAE calculator preview, 6 Roles, Companies)
- /calculadora: SENAE categories + API usage guide
- /tracking: Tracking ID format + 11 states reference

Seed (packages/database/prisma/seed.ts):
- Creates all 6 roles (SUPER_ADMIN, ADMIN_EMPRESA, OPERADOR_BODEGA,
  AGENTE_ADUANERO, CLIENTE, SOPORTE)
- Assigns Suite EC-00001 to demo client
- Creates demo Package EC-20260506-000001 with status history
- Creates demo PreAlert and B2BRequest

.env.example: Added all 9 integrations from doc §18:
  Amazon SP-API, SENAE WebService, Stripe/PayPhone, WhatsApp Business API,
  SendGrid/SES, SMS/Twilio, FedEx/DHL/UPS couriers, S3/MinIO, Sentry

migration.sql: Updated to reflect all new models and fields
turbo.json + package.json: Added test, test:cov, test:ci tasks + db:seed script
2026-06-01 08:25:01 -05:00

87 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Calculadora de Envíos SENAE — Moraworld Imports",
description: "Calcula el costo total de tu envío desde EE.UU. a Ecuador incluyendo FODINFA, IVA, arancel y flete. Sin registro.",
};
export default function CalculadoraPage() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3001";
return (
<main style={{ minHeight: "100vh", background: "#F3F4F6", padding: "40px 20px" }}>
<div style={{ maxWidth: 700, margin: "0 auto" }}>
<div style={{ marginBottom: 32 }}>
<h1 style={{ fontSize: "1.8rem", fontWeight: 800, marginBottom: 8 }}>
Calculadora SENAE
</h1>
<p style={{ color: "#6B7280" }}>
Sin necesidad de registro. Conoce el costo total antes de comprar.
</p>
</div>
{/* Nota: el formulario interactivo se implementa en Fase 1 con client component */}
<div style={{
background: "#fff", borderRadius: 16, padding: 32,
border: "1px solid #E5E7EB", marginBottom: 24,
}}>
<h2 style={{ fontWeight: 700, marginBottom: 16, fontSize: "1rem" }}>
Ingresa los datos de tu paquete
</h2>
<p style={{ color: "#6B7280", fontSize: ".88rem", marginBottom: 20 }}>
El formulario interactivo se habilitará en la Fase 1. Por ahora puedes usar el
endpoint de la API directamente:
</p>
<div style={{
background: "#0D1117", borderRadius: 10, padding: "16px 20px",
fontFamily: "monospace", fontSize: ".8rem", color: "#A5B4FC",
lineHeight: 2,
}}>
<div style={{ color: "rgba(255,255,255,.3)", marginBottom: 8 }}>{`// GET /api/calculator`}</div>
<div>
<span style={{ color: "#6EE7B7" }}>GET</span>{" "}
{apiUrl}/api/calculator?<span style={{ color: "#FCD34D" }}>value</span>=150&
<span style={{ color: "#FCD34D" }}>weight</span>=2&
<span style={{ color: "#FCD34D" }}>category</span>=REGIMEN_4X4
</div>
<div style={{ marginTop: 12, color: "rgba(255,255,255,.3)" }}>
{`// Parámetros: value, weight, category, length, width, height, shipments`}
</div>
</div>
</div>
{/* Categorías SENAE informativas */}
<div style={{
background: "#fff", borderRadius: 16, padding: 32,
border: "1px solid #E5E7EB",
}}>
<h2 style={{ fontWeight: 700, marginBottom: 20, fontSize: "1rem" }}>
Categorías SENAE aplicables
</h2>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
{[
{ name: "Régimen 4×4", rate: "0%", color: "#10B981", bg: "#D1FAE5", desc: "Hasta $400 · 4 kg · 4 envíos/año" },
{ name: "Categoría B", rate: "10%", color: "#1D4ED8", bg: "#DBEAFE", desc: "Bienes generales" },
{ name: "Categoría C", rate: "20%", color: "#FF6B00", bg: "#FFEDD5", desc: "Textiles, calzado, hogar" },
{ name: "Categoría D", rate: "015%", color: "#8B5CF6", bg: "#EDE9FE", desc: "Electrónicos · puede requerir INEN" },
].map((c) => (
<div key={c.name} style={{
background: c.bg, borderRadius: 10, padding: 16, textAlign: "center",
}}>
<div style={{ fontWeight: 800, marginBottom: 4 }}>{c.name}</div>
<div style={{ fontSize: "1.8rem", fontWeight: 900, color: c.color, lineHeight: 1, marginBottom: 6 }}>{c.rate}</div>
<div style={{ fontSize: ".75rem", color: "#4B5563" }}>{c.desc}</div>
</div>
))}
</div>
<div style={{ marginTop: 20, padding: 16, background: "#F3F4F6", borderRadius: 8 }}>
<p style={{ fontSize: ".82rem", color: "#6B7280", lineHeight: 1.6 }}>
<strong>Fórmula:</strong> Flete (Peso × $3.50/lb) + Seguro (2%) + FODINFA (0.5%) + Arancel (% categoría) + IVA (15%)
</p>
</div>
</div>
</div>
</main>
);
}