import React, { useState, useEffect, useRef, useMemo } from 'react'; import { Home, Target, BookOpen, User, PlusCircle, CheckCircle2, Circle, Flame, Award, Settings, Brain, Heart, Moon, Sun, Droplets, Dumbbell, Book, Cross, ChevronRight, Activity, Smile, Frown, MessageSquare, Zap, BarChart3, ShieldCheck, Users, Trophy, Flag, LogOut, Mail, Lock, AlertCircle, Loader2 } from 'lucide-react'; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged, createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, updateProfile } from 'firebase/auth'; import { getFirestore, doc, getDoc, setDoc, updateDoc, onSnapshot, collection, query, addDoc, getDocs, deleteDoc, serverTimestamp, arrayUnion } from 'firebase/firestore'; // --- CONFIGURAÇÃO FIREBASE --- const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {}; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'transforma-app-v1'; const LEVELS = [ { name: 'Bronze', minXp: 0, color: 'text-orange-400', border: 'border-orange-400/50', bg: 'bg-orange-500/10' }, { name: 'Prata', minXp: 1000, color: 'text-slate-300', border: 'border-slate-300/50', bg: 'bg-slate-400/10' }, { name: 'Ouro', minXp: 2500, color: 'text-yellow-400', border: 'border-yellow-400/50', bg: 'bg-yellow-400/10' }, { name: 'Platina', minXp: 5000, color: 'text-cyan-400', border: 'border-cyan-400/50', bg: 'bg-cyan-400/10' }, { name: 'Diamante', minXp: 10000, color: 'text-purple-400', border: 'border-purple-400/50', bg: 'bg-purple-400/10' }, { name: 'Mestre', minXp: 20000, color: 'text-rose-500', border: 'border-rose-500/50', bg: 'bg-rose-500/10' }, { name: 'Lenda', minXp: 50000, color: 'text-amber-500', border: 'border-amber-500/50', bg: 'bg-amber-500/10' }, ]; const ICONS_MAP = { Sun, Cross, BookOpen, Droplets, Dumbbell, Book, ShieldCheck, Moon, Heart, Flame }; const INITIAL_HABITS = [ { id: 'h1', title: 'Check-in Matinal', xp: 20, iconName: 'Sun', category: 'mental', done: false }, { id: 'h2', title: 'Orar (10 min)', xp: 50, iconName: 'Cross', category: 'spiritual', done: false }, { id: 'h3', title: 'Ler Versículo do Dia', xp: 30, iconName: 'BookOpen', category: 'spiritual', done: false }, { id: 'h4', title: 'Beber 2L de Água', xp: 40, iconName: 'Droplets', category: 'physical', done: false }, { id: 'h5', title: 'Treino Fisico', xp: 100, iconName: 'Dumbbell', category: 'physical', done: false }, { id: 'h6', title: 'Leitura (20 pág)', xp: 30, iconName: 'Book', category: 'mental', done: false }, { id: 'h7', title: 'Zero Pornografia/Álcool', xp: 100, iconName: 'ShieldCheck', category: 'discipline', done: false }, { id: 'h8', title: 'Dormir antes das 23h', xp: 50, iconName: 'Moon', category: 'physical', done: false }, ]; const WEEKLY_CHART_DATA = [ { name: 'Seg', score: 65 }, { name: 'Ter', score: 72 }, { name: 'Qua', score: 68 }, { name: 'Qui', score: 85 }, { name: 'Sex', score: 90 }, { name: 'Sáb', score: 88 }, { name: 'Dom', score: 95 }, ]; export default function App() { // Navigation & UI State const [activeTab, setActiveTab] = useState('home'); const [showCheckIn, setShowCheckIn] = useState(false); const [showLevelUp, setShowLevelUp] = useState(false); const [loading, setLoading] = useState(true); const [authError, setAuthError] = useState(''); // Auth State const [user, setUser] = useState(null); const [isAuthScreen, setIsAuthScreen] = useState(false); const [authMode, setAuthMode] = useState('login'); // 'login' | 'register' // App Data State const [userData, setUserData] = useState(null); const [habits, setHabits] = useState([]); const [journalEntries, setJournalEntries] = useState([]); // Community State const [leaderboard, setLeaderboard] = useState([]); const [challenges, setChallenges] = useState([]); // Temp State const [journalText, setJournalText] = useState(''); const [aiFeedback, setAiFeedback] = useState(null); const [isAnalyzing, setIsAnalyzing] = useState(false); // Forms const emailRef = useRef(null); const passRef = useRef(null); const nameRef = useRef(null); const challengeTitleRef = useRef(null); const challengeRewardRef = useRef(null); useEffect(() => { // 1. Mandatory Auth Initialization const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { // Fallback to anonymous so we satisfy rules, but we'll show login UI if anonymous await signInAnonymously(auth); } } catch (err) { console.error("Auth Init Error", err); } }; initAuth(); // 2. Auth State Listener const unsubscribe = onAuthStateChanged(auth, (currentUser) => { setUser(currentUser); if (currentUser) { // If user is anonymous, we force them to the Auth screen for a premium experience if (currentUser.isAnonymous) { setIsAuthScreen(true); setLoading(false); } else { setIsAuthScreen(false); // Initial check-in logic const lastCheckIn = localStorage.getItem(`checkin_${currentUser.uid}`); const today = new Date().toDateString(); if (lastCheckIn !== today) setShowCheckIn(true); } } else { setIsAuthScreen(true); setLoading(false); } }); return () => unsubscribe(); }, []); useEffect(() => { if (!user || user.isAnonymous) return; setLoading(true); // 1. Listen to Private User Profile const profileRef = doc(db, 'artifacts', appId, 'users', user.uid, 'profile'); const unsubProfile = onSnapshot(profileRef, (docSnap) => { if (docSnap.exists()) { const data = docSnap.data(); // Handle Level Up Notification if (userData && data.levelIndex > userData.levelIndex) { setShowLevelUp(true); setTimeout(() => setShowLevelUp(false), 4000); } setUserData(data); } else { // Create initial profile const newProfile = { uid: user.uid, name: user.displayName || 'Guerreiro(a)', xp: 0, streak: 1, levelIndex: 0, lifeScore: 50, createdAt: serverTimestamp() }; setDoc(profileRef, newProfile); // Also add user to Public Leaderboard const publicUserRef = doc(db, 'artifacts', appId, 'public', 'data', 'users', user.uid); setDoc(publicUserRef, { uid: user.uid, name: newProfile.name, xp: newProfile.xp, levelIndex: newProfile.levelIndex, streak: newProfile.streak }); } setLoading(false); }, (err) => console.error(err)); // 2. Listen to Private Habits const habitsRef = collection(db, 'artifacts', appId, 'users', user.uid, 'habits'); const unsubHabits = onSnapshot(habitsRef, (snapshot) => { if (snapshot.empty && habits.length === 0) { // Initialize habits INITIAL_HABITS.forEach(h => { setDoc(doc(habitsRef, h.id), h); }); } else { const loadedHabits = []; snapshot.forEach(d => loadedHabits.push({ id: d.id, ...d.data() })); // Reset habits daily (simple client-side check for demo) const today = new Date().toDateString(); const storedDate = localStorage.getItem(`habits_date_${user.uid}`); if (storedDate !== today) { loadedHabits.forEach(h => { if (h.done) { updateDoc(doc(habitsRef, h.id), { done: false }); h.done = false; } }); localStorage.setItem(`habits_date_${user.uid}`, today); } setHabits(loadedHabits.sort((a,b) => a.id.localeCompare(b.id))); } }, (err) => console.error(err)); // 3. Listen to Public Community (Users & Challenges) const publicUsersRef = collection(db, 'artifacts', appId, 'public', 'data', 'users'); const unsubUsers = onSnapshot(publicUsersRef, (snapshot) => { const usersList = []; snapshot.forEach(d => usersList.push(d.data())); // Sort in memory (Rule 2) setLeaderboard(usersList.sort((a, b) => b.xp - a.xp)); }); const publicChallengesRef = collection(db, 'artifacts', appId, 'public', 'data', 'challenges'); const unsubChallenges = onSnapshot(publicChallengesRef, (snapshot) => { const chalList = []; snapshot.forEach(d => chalList.push({ id: d.id, ...d.data() })); setChallenges(chalList); }); return () => { unsubProfile(); unsubHabits(); unsubUsers(); unsubChallenges(); }; }, [user]); const updateXP = async (amount) => { if (!user || !userData) return; let newXp = userData.xp + amount; let newLifeScore = userData.lifeScore; // Simplistic Life Score math if (amount > 0) newLifeScore = Math.min(100, newLifeScore + 1); if (amount < 0) newLifeScore = Math.max(0, newLifeScore - 1); // Calculate level let newLevelIndex = userData.levelIndex; while (LEVELS[newLevelIndex + 1] && newXp >= LEVELS[newLevelIndex + 1].minXp) { newLevelIndex++; } const updates = { xp: newXp, levelIndex: newLevelIndex, lifeScore: newLifeScore }; // Update Private Profile await updateDoc(doc(db, 'artifacts', appId, 'users', user.uid, 'profile'), updates); // Update Public Leaderboard Profile await updateDoc(doc(db, 'artifacts', appId, 'public', 'data', 'users', user.uid), { xp: newXp, levelIndex: newLevelIndex }); }; const handleAuth = async (e) => { e.preventDefault(); setAuthError(''); setLoading(true); const email = emailRef.current?.value; const pass = passRef.current?.value; const name = nameRef.current?.value; try { if (authMode === 'register') { const cred = await createUserWithEmailAndPassword(auth, email, pass); await updateProfile(cred.user, { displayName: name }); } else { await signInWithEmailAndPassword(auth, email, pass); } } catch (err) { setAuthError(err.message); setLoading(false); } }; const handleLogout = async () => { await signOut(auth); // When signed out, the mandatory listener will sign them back in anonymously // but our UI catches isAnonymous and shows the auth screen. }; const toggleHabit = async (habit) => { if (!user) return; const isNowDone = !habit.done; // Optimistic UI Update setHabits(prev => prev.map(h => h.id === habit.id ? { ...h, done: isNowDone } : h)); // Backend Update await updateDoc(doc(db, 'artifacts', appId, 'users', user.uid, 'habits', habit.id), { done: isNowDone }); // Update XP await updateXP(isNowDone ? habit.xp : -habit.xp); }; const completeCheckIn = async () => { setShowCheckIn(false); localStorage.setItem(`checkin_${user.uid}`, new Date().toDateString()); await updateXP(50); // Find check-in habit and mark done const checkinHabit = habits.find(h => h.title.includes('Check-in')); if (checkinHabit && !checkinHabit.done) { await toggleHabit(checkinHabit); } }; const analyzeJournal = async () => { if (journalText.trim().length < 10 || !user) return; setIsAnalyzing(true); // Simulate AI delay setTimeout(async () => { const text = journalText.toLowerCase(); let feedback = { sentiment: 'Neutro', message: 'Continue firme no seu propósito. Deus está no controle e cada dia é uma página em branco.', verse: '"Tudo posso naquele que me fortalece." - Filipenses 4:13' }; if (text.includes('triste') || text.includes('ansioso') || text.includes('medo') || text.includes('difícil')) { feedback = { sentiment: 'Acolhimento', message: 'Percebo que hoje foi desafiador. Não se cobre tanto. A dor faz parte da jornada de restauração. Respire fundo, entregue essa ansiedade a Deus. Você não está sozinho.', verse: '"Lançando sobre ele toda a vossa ansiedade, porque ele tem cuidado de vós." - 1 Pedro 5:7' }; } else if (text.includes('grato') || text.includes('feliz') || text.includes('consegui') || text.includes('venci')) { feedback = { sentiment: 'Motivacional', message: 'Que alegria ler isso! Você está construindo uma mentalidade de vencedor. Cada pequena vitória gera dopamina e reforça seu novo eu.', verse: '"Este é o dia que o Senhor fez; regozijemo-nos e alegremo-nos nele." - Salmos 118:24' }; } setAiFeedback(feedback); setIsAnalyzing(false); await updateXP(50); // Save entry await addDoc(collection(db, 'artifacts', appId, 'users', user.uid, 'journal'), { text: journalText, feedback: feedback, createdAt: serverTimestamp() }); setJournalText(''); }, 1500); }; const handleCreateChallenge = async (e) => { e.preventDefault(); if (!user || !userData) return; const title = challengeTitleRef.current.value; const reward = parseInt(challengeRewardRef.current.value); if (!title || !reward) return; await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'challenges'), { title, reward, creatorId: user.uid, creatorName: userData.name, participants: [user.uid], createdAt: serverTimestamp() }); challengeTitleRef.current.value = ''; challengeRewardRef.current.value = ''; }; const joinChallenge = async (challengeId, currentParticipants) => { if (!user) return; if (currentParticipants.includes(user.uid)) return; // Already joined await updateDoc(doc(db, 'artifacts', appId, 'public', 'data', 'challenges', challengeId), { participants: [...currentParticipants, user.uid] }); // Give immediate tiny reward for joining await updateXP(10); }; if (loading && !isAuthScreen) { return (

TRANSFORMA

); } if (isAuthScreen) { return (

Transforma

Seu mentor, psicólogo e treinador em um só lugar.

{authError && (
{authError}
)} {authMode === 'register' && (
)}
); } // Ensure userData exists before rendering main app if (!userData) return null; const currentLevelInfo = LEVELS[userData.levelIndex] || LEVELS[LEVELS.length - 1]; const nextLevelInfo = LEVELS[userData.levelIndex + 1] || currentLevelInfo; const xpProgress = nextLevelInfo.minXp > currentLevelInfo.minXp ? ((userData.xp - currentLevelInfo.minXp) / (nextLevelInfo.minXp - currentLevelInfo.minXp)) * 100 : 100; const ProgressBar = ({ progress, color = 'bg-indigo-500' }) => (
); const ScoreRing = ({ score }) => { const radius = 38; const circumference = 2 * Math.PI * radius; const strokeDashoffset = circumference - (score / 100) * circumference; const color = score > 80 ? 'text-emerald-400' : score > 50 ? 'text-indigo-400' : 'text-rose-400'; return (
{score}% Vida
); }; const MorningCheckInModal = () => { if (!showCheckIn) return null; return (

Bom dia, {userData.name}!

Hoje Deus lhe deu mais uma oportunidade. Como você está começando o dia?

{['Péssimo', 'Ruim', 'Médio', 'Bom', 'Ótimo'].map((opt) => ( ))}
Fraca Inabalável
); }; const renderDashboard = () => (
{/* Header Info */}

Olá, {userData.name.split(' ')[0]} 👋

Pronto para evoluir hoje?

{userData.streak} dias
{/* Main Score Card */}

Score Geral da Vida

Baseado nos seus hábitos e saúde emocional.

Nível {currentLevelInfo.name}
XP Total {userData.xp.toLocaleString()}
{/* Verse of the Day */}
Versículo do Dia

"Pois Deus não nos deu um espírito de covardia, mas de poder, de amor e de equilíbrio."

2 Timóteo 1:7

{/* Quick Missions Overview */}

Missões Prioritárias

{habits.filter(h => !h.done).slice(0, 3).map(habit => { const Icon = ICONS_MAP[habit.iconName] || Circle; return (
toggleHabit(habit)} className="bg-slate-900 border border-slate-800 rounded-xl p-4 flex items-center justify-between cursor-pointer hover:border-slate-700 active:scale-[0.98] transition-all">

{habit.title}

+{habit.xp} XP
); })} {habits.filter(h => !h.done).length === 0 && (

Todas missões concluídas hoje!

)}
); const renderMissions = () => { const doneCount = habits.filter(h => h.done).length; const progress = habits.length > 0 ? (doneCount / habits.length) * 100 : 0; return (

Hábitos Diários

Construa disciplina, ganhe recompensas.

Progresso Diário {doneCount}/{habits.length}
{Math.round(progress)}% Concluído
{habits.map(habit => { const Icon = ICONS_MAP[habit.iconName] || Circle; return (
toggleHabit(habit)} className={`rounded-2xl p-4 flex items-center justify-between cursor-pointer transition-all duration-300 ${habit.done ? 'bg-indigo-900/20 border border-indigo-500/30 shadow-[0_0_15px_rgba(99,102,241,0.1)]' : 'bg-slate-900 border border-slate-800 hover:border-slate-700'}`} >

{habit.title}

{habit.done ? 'Concluído' : `+${habit.xp} XP`}
{habit.done ? ( ) : ( )}
); })}
); }; const renderCommunity = () => (

Arena

Cresça em comunidade, participe de desafios.

{/* Leaderboard */}

Ranking Global

{leaderboard.slice(0, 5).map((u, i) => { const isMe = u.uid === user.uid; const lvl = LEVELS[u.levelIndex] || LEVELS[0]; return (
#{i + 1}

{u.name} {isMe && '(Você)'}

{lvl.name}
{u.xp} XP 🔥 {u.streak} dias
); })}
{/* Challenges */}

Desafios Ativos

{/* Create Challenge Form */}

Criar Novo Desafio

{challenges.map(chal => { const isParticipating = chal.participants.includes(user.uid); return (

{chal.title}

Criado por {chal.creatorName}

+{chal.reward} XP
{chal.participants.length} Guerreiros
); })} {challenges.length === 0 &&

Nenhum desafio ativo. Crie o primeiro!

}
); const renderJournal = () => (

Santuário

Mentoria guiada por IA. Desabafe e evolua.