import React, { useState, useEffect, useMemo } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInWithPopup, GoogleAuthProvider, onAuthStateChanged, signOut, signInWithCustomToken, signInAnonymously } from 'firebase/auth'; import { getFirestore, doc, setDoc, onSnapshot, collection } from 'firebase/firestore'; import { Chart as ChartJS, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement } from 'chart.js'; import { Doughnut, Bar } from 'react-chartjs-2'; import { PlusCircle, Trash2, Save, LogOut, TrendingUp, DollarSign, CreditCard, ShieldCheck, Edit3 } from 'lucide-react'; ChartJS.register(ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement); // --- CONFIGURACIÓN FIREBASE --- const firebaseConfig = { apiKey: "AIzaSyBVuBPoKpYL0Aiz7v8kl0QrTkDCiNp73K0", authDomain: "plan-financiero-ede7a.firebaseapp.com", projectId: "plan-financiero-ede7a", storageBucket: "plan-financiero-ede7a.firebasestorage.app", messagingSenderId: "934131384377", appId: "1:934131384377:web:5558073cd488364663cde8" }; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'eduardo-pro-2026'; // --- COMPONENTE PRINCIPAL --- export default function App() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [timeframe, setTimeframe] = useState('quincenal'); // 'quincenal' | 'mensual' const [view, setView] = useState('dashboard'); // 'dashboard' | 'admin' // Datos Financieros (Estado Global) const [finanzas, setFinanzas] = useState({ ingresoBruto: 97750, gastosFijos: [ { id: '1', nombre: "Crédito Camioneta", monto: 8600 }, { id: '2', nombre: "Escuela Tere", monto: 6600 }, { id: '3', nombre: "Súper (Efectivo)", monto: 6000 }, { id: '4', nombre: "Limpieza", monto: 5400 }, { id: '5', nombre: "Casa (Renta)", monto: 4731.20 }, { id: '6', nombre: "Gastos Médicos", monto: 4659 }, { id: '7', nombre: "Gasolina", monto: 4000 }, { id: '8', nombre: "Luz", monto: 3500 }, { id: '9', nombre: "Internet", monto: 1100 }, { id: '10', nombre: "Streaming", monto: 1042.66 }, { id: '11', nombre: "Agua", monto: 800 }, { id: '12', nombre: "Celular", monto: 500 }, { id: '13', nombre: "Jardinero", monto: 400 } ], metas: { deudaEspecial: { actual: 0, meta: 200000, pagoSugerido: 10000 }, fondoEmergencia: { actual: 0, meta: 142000, pagoSugerido: 5000 }, inversion: { actual: 0, meta: 100000, pagoSugerido: 3000 } } }); // --- AUTENTICACIÓN --- useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } } catch (e) { console.error("Auth init error:", e); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, (u) => { setUser(u); setLoading(false); }); return () => unsubscribe(); }, []); // --- PERSISTENCIA (FIRESTORE) --- useEffect(() => { if (!user) return; // Suscribirse a los datos del usuario const docRef = doc(db, 'artifacts', appId, 'users', user.uid, 'config', 'datos_principales'); const unsubscribe = onSnapshot(docRef, (docSnap) => { if (docSnap.exists()) { setFinanzas(docSnap.data()); } else { // Si no existen datos, guardar los iniciales setDoc(docRef, finanzas); } }, (err) => console.error("Firestore Error:", err)); return () => unsubscribe(); }, [user]); const saveToCloud = async (newData) => { if (!user) return; const docRef = doc(db, 'artifacts', appId, 'users', user.uid, 'config', 'datos_principales'); await setDoc(docRef, newData); }; // --- LÓGICA DE CÁLCULO --- const netoEfectivo = 71620; // Basado en tus cálculos previos de ISR 2026 const totalFijosMensual = useMemo(() => finanzas.gastosFijos.reduce((sum, g) => sum + g.monto, 0), [finanzas.gastosFijos]); const disponibleMensual = netoEfectivo - totalFijosMensual - finanzas.metas.deudaEspecial.pagoSugerido; const ahorroTotalMensual = finanzas.metas.fondoEmergencia.pagoSugerido + finanzas.metas.inversion.pagoSugerido; const gustosMensual = disponibleMensual - ahorroTotalMensual; const getMonto = (valor) => timeframe === 'mensual' ? valor : valor / 2; // --- ACCIONES --- const loginGoogle = () => { const provider = new GoogleAuthProvider(); signInWithPopup(auth, provider); }; const handleUpdateMeta = (key, delta) => { const newData = { ...finanzas }; newData.metas[key].actual = Math.max(0, Math.min(newData.metas[key].meta, newData.metas[key].actual + delta)); setFinanzas(newData); saveToCloud(newData); }; const handleAddGasto = () => { const nombre = prompt("Nombre del gasto:"); const monto = parseFloat(prompt("Monto mensual:")); if (nombre && !isNaN(monto)) { const newData = { ...finanzas, gastosFijos: [...finanzas.gastosFijos, { id: Date.now().toString(), nombre, monto }] }; setFinanzas(newData); saveToCloud(newData); } }; const handleDeleteGasto = (id) => { const newData = { ...finanzas, gastosFijos: finanzas.gastosFijos.filter(g => g.id !== id) }; setFinanzas(newData); saveToCloud(newData); }; const handleUpdatePagoMeta = (key, valor) => { const newData = { ...finanzas }; newData.metas[key].pagoSugerido = parseFloat(valor) || 0; setFinanzas(newData); saveToCloud(newData); }; if (loading) return (
); if (!user) return (

Bienvenido, Eduardo

Inicia sesión con tu cuenta de Google para gestionar tu plan financiero de forma privada y segura.

); return (
{/* Header */}

Plan Eduardo 2026

{user.displayName}

{view === 'dashboard' ? (
{/* KPIs */}
} color="blue" /> } color="slate" /> } color="red" /> } color="emerald" />
{/* Distribución */}

Distribución de Ingresos

{/* Guía Automática */}

Hoja de Ruta {timeframe === 'mensual' ? 'Mensual' : 'Quincenal'}

{/* Rastreadores de Metas */}
handleUpdateMeta('deudaEspecial', getMonto(finanzas.metas.deudaEspecial.pagoSugerido))} /> handleUpdateMeta('fondoEmergencia', getMonto(finanzas.metas.fondoEmergencia.pagoSugerido))} /> handleUpdateMeta('inversion', getMonto(finanzas.metas.inversion.pagoSugerido))} />
) : (
{/* Gestión de Gastos */}

Gastos Fijos Mensuales

{finanzas.gastosFijos.map(g => ( ))}
Concepto Mensual Quincenal Acciones
{g.nombre} {formatCurrency(g.monto)} {formatCurrency(g.monto / 2)}
{/* Ajuste de Metas */}

Configuración de Cuotas y Metas

{Object.entries(finanzas.metas).map(([key, m]) => (

{key.replace(/([A-Z])/g, ' $1')}

handleUpdatePagoMeta(key, e.target.value)} className="w-full bg-white border border-slate-200 rounded-xl px-4 py-2 font-bold focus:ring-2 focus:ring-blue-500 outline-none" />
{ const newData = { ...finanzas }; newData.metas[key].meta = parseFloat(e.target.value) || 0; setFinanzas(newData); saveToCloud(newData); }} className="w-full bg-white border border-slate-200 rounded-xl px-4 py-2 font-bold focus:ring-2 focus:ring-blue-500 outline-none" />
))}
)}
); } // --- SUB-COMPONENTES --- function StatCard({ label, val, icon, color }) { const colors = { blue: "bg-blue-50 text-blue-600", red: "bg-red-50 text-red-600", emerald: "bg-emerald-50 text-emerald-600", slate: "bg-slate-100 text-slate-600" }; return (
{icon}

{label}

{formatCurrency(val)}

); } function GuideItem({ label, sub, monto, color }) { const colors = { blue: "bg-blue-50 text-blue-700 border-blue-100", red: "bg-red-50 text-red-700 border-red-100", emerald: "bg-emerald-50 text-emerald-700 border-emerald-100", slate: "bg-slate-50 text-slate-700 border-slate-100" }; return (

{label}

{sub}

{formatCurrency(monto)}

); } function GoalTracker({ title, goal, color, onAdd }) { const perc = (goal.actual / goal.meta) * 100; const barColors = { red: "bg-red-500", emerald: "bg-emerald-500", blue: "bg-blue-500" }; const textColors = { red: "text-red-700", emerald: "text-emerald-700", blue: "text-blue-700" }; return (

{title}

{perc.toFixed(1)}%

Acumulado

{formatCurrency(goal.actual)}

Faltan {formatCurrency(goal.meta - goal.actual)}

); } function formatCurrency(val) { return new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN' }).format(val); }