import React, { useState, useEffect, useMemo } from 'react'; import { initializeApp } from 'firebase/app'; import { getFirestore, collection, doc, setDoc, getDoc, onSnapshot, addDoc, updateDoc, query, orderBy } from 'firebase/firestore'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar } from 'recharts'; import { CheckCircle, Circle, Utensils, Calendar, TrendingUp, Lock, User, Droplets, ShoppingCart, MessageCircle, ChevronRight, ChevronLeft, PlayCircle, ClipboardList, Sparkles, Brain, Search, Send, Info, XCircle, RefreshCw, Settings2, Wrench, RotateCcw, Edit3, ArrowLeft, Rocket, Check } from 'lucide-react'; // --- CONFIGURACIÓN FIREBASE --- const firebaseConfig = JSON.parse(__firebase_config); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'programa-21d-v2'; const apiKey = ""; // --- LISTA VERDE (SEGÚN PDF) --- const GREEN_LIST = { proteinas: ["Res", "Cerdo", "Cordero", "Chivo", "Hígado", "Corazón", "Riñones", "Pollo", "Gallina", "Pavo", "Pato", "Salmón", "Caballa", "Arenque", "Sardinas", "Anchoas", "Mariscos", "Huevos"], grasas: ["Ghee", "Mantequilla", "Crema de leche", "Aceite de coco", "Quesos curados", "Manteca de cerdo", "Sebo de res", "Aceitunas", "Aceite de oliva", "Aguacate", "Semillas de Chía", "Semillas de Lino", "Cáñamo"], vegetales: ["Espinaca", "Lechuga", "Acelga", "Rúcula", "Brócoli", "Coliflor", "Repollo", "Espárragos", "Pepino", "Champiñones", "Chucrut"], condimentos: ["Sal de mar", "Pimienta", "Ajo", "Cebolla en polvo", "Comino", "Cúrcuma", "Romero", "Tomillo", "Albahaca", "Vinagre de sidra", "Limón"] }; const WEEKLY_DATA = [ { week: 1, title: "Primera Semana 1/3", schedule: { breakfast: Array(7).fill("Desayuno"), lunch: Array(7).fill("Almuerzo"), dinner: ["Cena", "Caldo de Huesos", "Cena", "Cena", "Caldo de Huesos", "Cena", "Cena"] } }, { week: 2, title: "Segunda Semana 2/3", schedule: { breakfast: Array(7).fill("Desayuno"), lunch: Array(7).fill("Almuerzo"), dinner: ["Cena", "Caldo de Huesos", "Caldo de Huesos", "Cena", "Caldo de Huesos", "Caldo de Huesos", "Caldo de Huesos"] } }, { week: 3, title: "Tercera Semana 3/3", schedule: { breakfast: Array(7).fill("3 Opciones"), lunch: Array(7).fill("Almuerzo"), dinner: Array(7).fill("2 Opciones") } } ]; const PROGRAM_DAYS = Array.from({ length: 21 }, (_, i) => ({ day: i + 1, title: `Día ${i + 1}: ${i < 7 ? 'Adaptación' : i < 14 ? 'Cetosis Profunda' : 'Consolidación'}`, tasks: ["Agua con sal y limón", "15 min caminata", "Ayuno 14h"] })); const callGemini = async (prompt, systemInstruction = "") => { const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: prompt }] }], systemInstruction: systemInstruction ? { parts: [{ text: systemInstruction }] } : undefined, generationConfig: { responseMimeType: "application/json" } }; const fetchWithRetry = async (retries = 5, delay = 1000) => { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); return result.candidates?.[0]?.content?.parts?.[0]?.text; } catch (error) { if (retries > 0) { await new Promise(res => setTimeout(res, delay)); return fetchWithRetry(retries - 1, delay * 2); } throw error; } }; return await fetchWithRetry(); }; export default function App() { const [user, setUser] = useState(null); const [activeTab, setActiveTab] = useState('dashboard'); const [profile, setProfile] = useState({ startDate: null, demoDay: 0 }); const [progress, setProgress] = useState({}); const [measurements, setMeasurements] = useState([]); const [personalizedPlan, setPersonalizedPlan] = useState(null); // Ahora será { week1: {...}, week2: {...}, week3: {...} } const [shoppingLists, setShoppingLists] = useState({}); // { week1: {...}, week2: {...} } const [mealHistory, setMealHistory] = useState({}); const [excludedFoods, setExcludedFoods] = useState([]); const [loading, setLoading] = useState(true); const currentDay = useMemo(() => { if (profile.demoDay > 0) return profile.demoDay; if (!profile.startDate) return 0; const start = new Date(profile.startDate); const today = new Date(); const diffTime = Math.abs(today - start); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays > 21 ? 21 : diffDays; }, [profile]); useEffect(() => { const initAuth = async () => { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, (u) => { setUser(u); if (u) setLoading(false); }); return () => unsubscribe(); }, []); useEffect(() => { if (!user) return; const profileRef = doc(db, 'artifacts', appId, 'users', user.uid, 'data', 'profile'); const unsubProfile = onSnapshot(profileRef, (snap) => { if (snap.exists()) setProfile(snap.data()); }); const planRef = doc(db, 'artifacts', appId, 'users', user.uid, 'data', 'plan'); const unsubPlan = onSnapshot(planRef, (snap) => { if (snap.exists()) { const data = snap.data(); setPersonalizedPlan(data.meals || null); setExcludedFoods(data.excluded || []); setMealHistory(data.history || {}); setShoppingLists(data.shoppingLists || {}); } }); const progressRef = doc(db, 'artifacts', appId, 'users', user.uid, 'data', 'progress'); const unsubProgress = onSnapshot(progressRef, (snap) => { if (snap.exists()) setProgress(snap.data()); }); const measurementsRef = collection(db, 'artifacts', appId, 'users', user.uid, 'measurements'); const unsubMeasurements = onSnapshot(measurementsRef, (snap) => { const data = snap.docs.map(d => ({ id: d.id, ...d.data() })); setMeasurements(data.sort((a, b) => new Date(a.date) - new Date(b.date))); }); return () => { unsubProfile(); unsubPlan(); unsubProgress(); unsubMeasurements(); }; }, [user]); const startProgram = async () => { if (!user) return; const profileRef = doc(db, 'artifacts', appId, 'users', user.uid, 'data', 'profile'); await setDoc(profileRef, { startDate: new Date().toISOString(), demoDay: 0 }); }; const setDemoDay = async (day) => { const profileRef = doc(db, 'artifacts', appId, 'users', user.uid, 'data', 'profile'); await setDoc(profileRef, { ...profile, demoDay: day }, { merge: true }); }; const toggleDailyTask = async (day, taskIndex) => { if (!user) return; const currentDayData = progress[day] || { completedTasks: [] }; let newTasks = [...currentDayData.completedTasks]; if (newTasks.includes(taskIndex)) newTasks = newTasks.filter(i => i !== taskIndex); else newTasks.push(taskIndex); const progressRef = doc(db, 'artifacts', appId, 'users', user.uid, 'data', 'progress'); await setDoc(progressRef, { ...progress, [day]: { ...currentDayData, completedTasks: newTasks } }, { merge: true }); }; const savePlanData = async (meals, excluded, history = mealHistory, sLists = shoppingLists) => { if (!user) return; const planRef = doc(db, 'artifacts', appId, 'users', user.uid, 'data', 'plan'); await setDoc(planRef, { meals, excluded, history, shoppingLists: sLists, updatedAt: new Date().toISOString() }); }; if (loading) return
Estandarizando Glucosa...
; return (
{/* Sidebar */}
{currentDay === 0 ? (

Transforma tu
metabolismo hoy

Inicia tu conteo de 21 días y accede a tu plan inteligente basado en la zona verde.

) : ( <>

{activeTab === 'dashboard' && "Panel de Control"} {activeTab === 'strategy' && "Estrategia de Ayunos"} {activeTab === 'my-plan' && "Plan Personalizado"} {activeTab === 'journal' && "Coach Mental IA"} {activeTab === 'stats' && "Resultados"}

{activeTab === 'dashboard' && } {activeTab === 'strategy' && } {activeTab === 'my-plan' && } {activeTab === 'journal' && } {activeTab === 'stats' && { e.preventDefault(); const weight = e.target.weight.value; const waist = e.target.waist.value; addDoc(collection(db, 'artifacts', appId, 'users', user.uid, 'measurements'), { date: new Date().toISOString().split('T')[0], weight: parseFloat(weight), waist: parseFloat(waist) }); e.target.reset(); }} />}
)}
); } function NavItem({ icon, label, active, onClick }) { return ( ); } // --- PLAN PERSONALIZADO --- function PersonalizedPlanView({ plan, excluded, history, shoppingLists, onSave, currentDay }) { const currentWeekNum = Math.ceil(currentDay / 7); const [selectedWeek, setSelectedWeek] = useState(currentWeekNum > 3 ? 3 : currentWeekNum); const [step, setStep] = useState(plan?.[`week${selectedWeek}`] ? 'view' : 'setup'); const [tempExcluded, setTempExcluded] = useState(excluded); const [isGenerating, setIsGenerating] = useState(false); const [isSyncingList, setIsSyncingList] = useState(false); const [activeCellMenu, setActiveCellMenu] = useState(null); const [manualEdit, setManualEdit] = useState(''); const weekKey = `week${selectedWeek}`; const currentPlan = plan?.[weekKey] || null; const currentSList = shoppingLists?.[weekKey] || null; // Actualizar vista al cambiar de semana useEffect(() => { if (plan?.[weekKey]) setStep('view'); else setStep('setup'); }, [selectedWeek, plan]); const generateFullPlan = async () => { setIsGenerating(true); try { const systemPrompt = `Chef experto. Usa SOLO Zona Verde: ${JSON.stringify(GREEN_LIST)}. EXCLUYE: ${tempExcluded.join(', ')}. Genera platos coherentes para la semana ${selectedWeek}. JSON: { "meals": { "D1": { "breakfast": "Plato", "lunch": "Plato", "dinner": "Plato" }, ... } }. Responde SOLO JSON.`; const res = await callGemini(`Plan para semana ${selectedWeek}.`, systemPrompt); const cleaned = res.replace(/```json|```/g, '').trim(); const data = JSON.parse(cleaned); const newHistory = { ...history }; Object.keys(data.meals).forEach(day => { ['breakfast', 'lunch', 'dinner'].forEach(type => { newHistory[`${weekKey}-${day}-${type}`] = [data.meals[day][type]]; }); }); const updatedMeals = { ...plan, [weekKey]: data.meals }; await onSave(updatedMeals, tempExcluded, newHistory, shoppingLists); } catch (e) { console.error(e); } setIsGenerating(false); }; const syncShoppingList = async () => { if (!currentPlan) return; setIsSyncingList(true); try { const mealsStr = Object.keys(currentPlan).map(d => `${d}: ${currentPlan[d].breakfast}, ${currentPlan[d].lunch}, ${currentPlan[d].dinner}`).join(' | '); const res = await callGemini(`Lista compras categorizada semana ${selectedWeek}: ${mealsStr}. JSON: { "categories": { "Proteínas": [], "Vegetales": [], "Grasas": [], "Otros": [] } }. SOLO JSON.`, "Logística."); const cleaned = res.replace(/```json|```/g, '').trim(); const data = JSON.parse(cleaned); await onSave(plan, excluded, history, { ...shoppingLists, [weekKey]: data.categories }); } catch (e) { console.error(e); } setIsSyncingList(false); }; const regenerateMeal = async (day, type) => { setActiveCellMenu(null); setIsGenerating(true); try { const res = await callGemini(`Sustituye "${currentPlan[day][type]}". Zona Verde. JSON: { "newMeal": "" }.`, "Chef Keto."); const data = JSON.parse(res.replace(/```json|```/g, '').trim()); const key = `${weekKey}-${day}-${type}`; const newHistory = { ...history, [key]: [...(history[key] || []), data.newMeal] }; const updatedWeek = { ...currentPlan, [day]: { ...currentPlan[day], [type]: data.newMeal } }; await onSave({ ...plan, [weekKey]: updatedWeek }, excluded, newHistory, shoppingLists); } catch (e) { console.error(e); } setIsGenerating(false); }; if (step === 'setup') { return (
{[1, 2, 3].map(w => { const isLocked = w > currentWeekNum; return ; })}

Semana {selectedWeek}

Configura tus gustos para esta etapa

setTempExcluded(p => p.includes(i) ? p.filter(x => x !== i) : [...p, i])} color="indigo" /> setTempExcluded(p => p.includes(i) ? p.filter(x => x !== i) : [...p, i])} color="amber" />
); } return (
{[1, 2, 3].map(w => { const isLocked = w > currentWeekNum; const isCompleted = w < currentWeekNum; return ( ); })}
{selectedWeek < currentWeekNum ? ( Semana Completada ) : selectedWeek === currentWeekNum ? ( Semana en Curso ) : ( Próxima Etapa )}
{Object.keys(currentPlan).map(day => )} {['breakfast', 'lunch', 'dinner'].map((type, rowIndex) => ( {Object.keys(currentPlan).map(day => { const key = `${weekKey}-${day}-${type}`; const isMenuOpen = activeCellMenu === key; const canGoBack = history[key]?.length > 1; return ( ); })} ))}
MOMENTO{day.replace('D', 'Día ')}
{type === 'breakfast' ? 'MAÑANA' : type === 'lunch' ? 'TARDE' : 'NOCHE'}

{currentPlan[day][type]}

{isMenuOpen && (
{canGoBack && }
setManualEdit(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && manualEdit) { onSave({ ...plan, [weekKey]: { ...currentPlan, [day]: { ...currentPlan[day], [type]: manualEdit } } }, excluded, { ...history, [key]: [...(history[key] || []), manualEdit] }, shoppingLists); setManualEdit(''); setActiveCellMenu(null); } }} />
)}
{/* CARRITO SEMANAL */}

Mi Carrito Semana {selectedWeek}

Sincronizado con tus platos actuales

{currentSList ?
{Object.keys(currentSList).map(cat =>

{cat}

{currentSList[cat].map((item, i) =>
{item}
)}
)}
:

Presiona sincronizar para generar tu lista de la semana {selectedWeek}.

}

Mi Carrito 21D

Enviar directamente a tu WhatsApp

); } function IngredientSection({ title, list, tempExcluded, toggleExclude, color }) { const colorMap = { indigo: "border-indigo-100 bg-indigo-50/20 text-indigo-950 hover:border-indigo-300 shadow-indigo-50/50", amber: "border-amber-100 bg-amber-50/20 text-amber-950 hover:border-amber-300 shadow-amber-50/50", green: "border-green-100 bg-green-50/20 text-green-950 hover:border-green-300 shadow-green-50/50" }; return (

{title}

{list.map(item => { const isEx = tempExcluded.includes(item); return ; })}
); } function DashboardView({ progress, toggleTask, currentDay }) { const dayData = PROGRAM_DAYS[currentDay - 1] || PROGRAM_DAYS[0]; return (

Tareas Día #{currentDay}

{dayData.tasks.map((t, i) => { const isComp = progress[currentDay]?.completedTasks?.includes(i); return ; })}

MOTOR METABÓLICO ACTIVADO

ADAPTACIÓN
CELULAR

{21 - currentDay}Días Restantes
); } function StrategyView({ currentMaxDay }) { const currentWeek = Math.ceil(currentMaxDay / 7); const [selectedWeek, setSelectedWeek] = useState(currentWeek > 3 ? 3 : currentWeek); const daysLabels = ["D1 - L", "D2 - M", "D3 - M", "D4 - J", "D5 - V", "D6 - S", "D7 - D"]; return (
{[1, 2, 3].map(w => { const isLocked = w > currentWeek; return ; })}

MATRIZ ESTRATÉGICA

{WEEKLY_DATA[selectedWeek-1].title}

{daysLabels.map((l, i) => )}{WEEKLY_DATA[selectedWeek-1].schedule.breakfast.map((item, idx) => )}{WEEKLY_DATA[selectedWeek-1].schedule.lunch.map((item, idx) => )}{WEEKLY_DATA[selectedWeek-1].schedule.dinner.map((item, idx) => )}
PERIODO SOLAR{l}
Mañana7 AM{item}
Tarde12 - 3 PM{item}
Noche6 PM{item}
); } function JournalView() { return

SOPORTE COGNITIVO

"Tu cuerpo no pide comida, pide nutrientes. Aprende a distinguir el hambre real de la emocional."

; } function StatsView({ measurements, onAdd }) { return

Métricas

; }