/* 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. */}
{/* Scrollable content */}
{/* Title block */}

{cfg.title}

Obtén tu radiografía

{cfg.subtitle}

{/* Upload area */}
{/* Supported banks */}
Bancos soportados: BBVA, Nu y Banorte
{/* Skipped-questions discrete note */} {skipped && (
Puedes completar tu perfil después desde la app
)} {/* 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.

{/* Buttons */}
); } Object.assign(window, { UploadScreen, ConfirmCardsSheet, TotaleroSingleStatementSheet, PROFILE_UPLOAD_COPY, });