/* global React, PrimaryButton */
// (PrimaryButton is sourced from Shared.jsx — variant="accent" + variant="outlined")
// Yomee — Upload Screen
// Final step of the post-result onboarding flow. Profile-specific title,
// CTA color, and copy. Includes a discrete "skipped questions" note when
// the user chose to skip the per-profile questions, plus 2 demo file rows
// showing what an uploaded statement looks like.
const { useState: useStateUp, useEffect: useEffectUp } = React;
const PROFILE_UPLOAD_COPY = {
totalero: {
title: "Llegó la hora",
subtitle:
"Carga tu primer estado de cuenta y descubre cómo mejorar tus finanzas",
cta: "Obtén tu radiografía",
accent: "var(--yo-mint-700)",
},
optimizador: {
title: "Tu plan está casi listo",
subtitle: "Solo necesitamos tu estado de cuenta para completarlo",
cta: "Subir mi estado de cuenta",
// Unified mint accent across all archetypes — matches the Totalero
// "Obtén tu radiografía" button so the primary CTA on the upload
// screen has one consistent visual language.
accent: "var(--yo-mint-700)",
},
escalador: {
title: "Hora de ver la verdad",
subtitle:
"Tu estado de cuenta revela exactamente cuánto te está costando",
cta: "Quiero ver mi radiografía",
accent: "var(--yo-mint-700)",
},
reiniciador: {
title: "El primer paso ya lo diste",
subtitle: "Sube tu estado de cuenta y nosotros hacemos el resto",
cta: "Ver mi situación real",
accent: "var(--yo-mint-700)",
},
};
// Bank metadata for the file row badges.
const BANKS = {
bbva: { color: "#004A9F", letter: "B" },
nu: { color: "#7B2D8B", letter: "N" },
banorte: { color: "#E30613", letter: "B" },
};
// Simulated upload sequence — each tap on the upload area appends the next file.
const UPLOAD_SEQUENCE = [
{ name: "Estado_cta_BBVA_enero.pdf", bank: "bbva" },
{ name: "Estado_cta_NU_enero.pdf", bank: "nu" },
{ name: "Estado_cta_Banorte_enero.pdf", bank: "banorte" },
];
// ────────────────────────────────────────────────
// Bank circular badge
// ────────────────────────────────────────────────
function BankBadge({ bank }) {
const meta = BANKS[bank] || BANKS.bbva;
return (
{meta.letter}
);
}
// ────────────────────────────────────────────────
// Filled green circle checkmark — design-system "success" indicator.
// ────────────────────────────────────────────────
function SuccessCheck() {
return (
);
}
// ────────────────────────────────────────────────
// File row card — animates in with fade + slide down (200ms ease-out)
// ────────────────────────────────────────────────
function FileRow({ name, bank, onDelete }) {
const [shown, setShown] = useStateUp(false);
useEffectUp(() => {
const id = requestAnimationFrame(() => setShown(true));
return () => cancelAnimationFrame(id);
}, []);
return (
{name}
{onDelete && (
)}
);
}
// ────────────────────────────────────────────────
// Upload area — solid border, centered tray icon + text.
// During simulated loading, replaces icon+text with a spinner.
// When inactive (all files uploaded), reduces opacity to 0.5.
// ────────────────────────────────────────────────
function UploadArea({ accent, loading, disabled, onTap }) {
// Border + arrow icon use the same ink color as the "Ver radiografía demo"
// outlined button below — keeps the upload area visually grouped with the
// secondary action and lets the profile accent live only on the primary CTA.
const inkBorder = "var(--yo-ink)";
return (
);
}
// ────────────────────────────────────────────────
// Main screen
// profile — internal key (totalero | optimizador | escalador | reiniciador)
// skipped — boolean, true if user skipped questions to reach this screen
// onContinue — primary CTA tap (begin processing)
// onDemo — secondary CTA tap (see demo radiografía)
// ────────────────────────────────────────────────
function UploadScreen({
profile = "optimizador",
skipped = false,
cardsAnswer = null,
files: filesProp,
setFiles: setFilesProp,
onContinue,
onDemo,
}) {
const cfg = PROFILE_UPLOAD_COPY[profile] || PROFILE_UPLOAD_COPY.optimizador;
// Controlled mode: files state is owned by the parent (App) so it
// persists across navigation to Radiografía and back. Falls back to
// local state if no parent setter is wired in.
const [filesLocal, setFilesLocal] = useStateUp([]);
const files = filesProp != null ? filesProp : filesLocal;
const setFiles = setFilesProp || setFilesLocal;
const [loading, setLoading] = useStateUp(false);
const [showSheet, setShowSheet] = useStateUp(false);
const [sheetIn, setSheetIn] = useStateUp(false); // animation state
// Map declared cards answer to required minimum file count.
// null / "1 tarjeta" → 1 (modal never triggers)
// "2 tarjetas" → 2
// "3 o más" → 3
const declaredCount =
cardsAnswer === "2 tarjetas"
? 2
: cardsAnswer === "3 o más"
? 3
: 1;
const allUploaded = files.length >= UPLOAD_SEQUENCE.length;
// CTA enables as soon as at least 1 file has been uploaded.
const ctaEnabled = files.length >= 1;
const proceedToRadiografia = () => {
onContinue && onContinue(profile);
};
const handlePrimaryTap = () => {
if (!ctaEnabled) return;
// Condition A1 — Totalero: only 1 statement uploaded → show "¿Solo un
// estado de cuenta?" modal inviting the user to upload more.
if (profile === "totalero" && files.length === 1) {
setShowSheet(true);
setTimeout(() => setSheetIn(true), 16);
return;
}
// Condition A2 — user declared 2+ cards but uploaded fewer files.
if (declaredCount >= 2 && files.length < declaredCount) {
setShowSheet(true);
// Defer flipping sheetIn so the initial render commits with
// translateY(100%) + opacity 0; then the transition runs.
setTimeout(() => setSheetIn(true), 16);
return;
}
// Condition B — proceed.
proceedToRadiografia();
};
const closeSheet = () => {
setSheetIn(false);
setTimeout(() => setShowSheet(false), 200);
};
const handleUploadTap = () => {
if (allUploaded || loading) return;
setLoading(true);
setTimeout(() => {
setFiles((prev) => {
const next = UPLOAD_SEQUENCE[prev.length];
return next ? [...prev, next] : prev;
});
setLoading(false);
}, 300);
};
const handleDelete = (idx) => {
setFiles((prev) => prev.filter((_, i) => i !== idx));
};
return (
{/* Topbar — no progress, no back, only help. Sits in same spot as others. */}
)}
{/* Uploaded files — only render when at least one file is loaded */}
{files.length > 0 && (
Archivos cargados
{files.map((f, i) => (
handleDelete(i)}
/>
))}
{/* Privacy note */}
Tus archivos se eliminarán de forma permanente una vez procesados
)}
{/* Sticky CTA stack */}
{/* Primary — matches the Main Screen CTA: default ink primary
variant with trailing arrow. Uniform across all archetypes. */}
{cfg.cta}
{/* Secondary — outlined demo (PrimaryButton variant="outlined") */}
onDemo && onDemo(profile)}
variant="outlined"
>
Ver radiografía demo
{/* Confirmation bottom sheet — shown when user taps CTA but uploaded
fewer files than declared cards count. Totalero gets the
"¿Solo un estado de cuenta?" variant. */}
{showSheet && profile === "totalero" && (
{
closeSheet();
setTimeout(() => proceedToRadiografia(), 220);
}}
onBackdrop={closeSheet}
/>
)}
{showSheet && profile !== "totalero" && (
{
closeSheet();
// Navigate after sheet exit animation finishes
setTimeout(() => proceedToRadiografia(), 220);
}}
onBackdrop={closeSheet}
/>
)}
);
}
// ────────────────────────────────────────────────
// Confirmation bottom sheet
// Slides up from bottom (200ms ease-out) with 40% black backdrop.
// Title + body + file-count note + 2 stacked buttons + tertiary link.
// ────────────────────────────────────────────────
function ConfirmCardsSheet({
declaredCount,
uploadedCount,
visible,
onAddMore,
onContinueAnyway,
onBackdrop,
}) {
const declaredLabel =
declaredCount === 3 ? "3 o más tarjetas" : "2 tarjetas";
const title = `Mencionaste que tienes ${declaredLabel}`;
// File count note phrasing — singular/plural for the uploaded count.
const fileWord = uploadedCount === 1 ? "estado de cuenta" : "estados de cuenta";
const fileCountNote = `Cargaste ${uploadedCount} de ${declaredCount} estados de cuenta.`;
const continueLabel = `CONTINUAR CON ${uploadedCount} ${fileWord.toUpperCase()}`;
return (
{/* Backdrop */}
{/* Sheet */}
{/* Handle bar */}
{/* Mascot */}
{/* Title */}
{title}
{/* Body */}
Tu radiografía será más completa y precisa con todos tus estados de
cuenta.
{/* File count note */}
{fileCountNote}
{/* Buttons */}
);
}
// ────────────────────────────────────────────────
// Totalero — single statement confirmation sheet
// Same visual pattern as ConfirmCardsSheet but with copy specific to the
// "only 1 estado de cuenta uploaded" case.
// ────────────────────────────────────────────────
function TotaleroSingleStatementSheet({
visible,
onAddMore,
onContinueAnyway,
onBackdrop,
}) {
return (
{/* Backdrop */}
{/* Sheet */}
{/* Handle bar */}
{/* Mascot */}
{/* Title */}
¿Solo un estado de cuenta?
{/* Body */}
Entre más estados de cuenta cargues, más completa será tu radiografía y más oportunidades podemos encontrar para optimizar tus tarjetas.