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 */}
{/* 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
| Concepto |
Mensual |
Quincenal |
Acciones |
{finanzas.gastosFijos.map(g => (
| {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]) => (
))}
)}
);
}
// --- 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 (
);
}
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);
}