teste oua

import React, { useState, useEffect, useRef } from 'react'; // Import useRef import { initializeApp } from 'firebase/app'; import { getAuth, signInWithEmailAndPassword, createUserWithEmailAndPassword, onAuthStateChanged, signOut, signInAnonymously, signInWithCustomToken } from 'firebase/auth'; import { getFirestore, doc, getDoc, setDoc, onSnapshot, collection, query } from 'firebase/firestore'; // Global variables for Firebase configuration (provided by the Canvas environment) // DO NOT MODIFY THESE LINES const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'; const firebaseConfig = JSON.parse(typeof __firebase_config !== 'undefined' ? __firebase_config : '{}'); const initialAuthToken = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null; // Helper function to simulate vibration (for Japa Mala) const vibrate = (duration) => { if (navigator.vibrate) { navigator.vibrate(duration); } else { console.log(`Vibrating for ${duration}ms (simulated)`); } }; // --- UI Components --- // Shared Button Component const Button = ({ onClick, children, className = '', colorClass = 'bg-blue-500 hover:bg-blue-600' }) => ( ); // AuthScreen Component - Modified for Creator-Only Login const AuthScreen = ({ onAuthSuccess, auth, db }) => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); // --- ATENÇÃO: PARA DEMONSTRAÇÃO APENAS --- // Em um ambiente de produção real, NUNCA hardcode credenciais. // Use um sistema de gerenciamento de papéis ou autenticação de administrador segura. const CREATOR_EMAIL = "criador@exemplo.com"; // Substitua pelo seu e-mail de criador const CREATOR_PASSWORD = "senha_segura_criador"; // Substitua pela sua senha de criador // --- FIM DA ATENÇÃO --- const handleLogin = async () => { setLoading(true); setError(''); try { if (email === CREATOR_EMAIL && password === CREATOR_PASSWORD) { // Attempt to log in with Firebase Auth to establish the session await signInWithEmailAndPassword(auth, email, password); // onAuthStateChanged (in the App component) will handle navigation } else { setError('Invalid creator credentials.'); } } catch (e) { console.error("Creator login error:", e); if (e.code === 'auth/user-not-found' || e.code === 'auth/wrong-password' || e.code === 'auth/invalid-credential') { setError('Invalid creator credentials.'); } else if (e.code === 'auth/invalid-email') { setError('Invalid email format.'); } else { setError('Authentication error. Please try again.'); } } finally { setLoading(false); } }; return ( // Removed specific background from AuthScreen to use App's global background

Creator Login

{error &&

{error}

}
setEmail(e.target.value)} className="w-full p-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-[#A7C7E7] focus:border-transparent outline-none shadow-sm" /> setPassword(e.target.value)} className="w-full p-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-[#A7C7E7] focus:border-transparent outline-none shadow-sm" />
); }; // Dashboard Component const Dashboard = ({ onLogout, userId, setCurrentScreen }) => { const [dailyPhrase, setDailyPhrase] = useState(''); // List of daily phrases const phrases = [ "Mantra do dia: Eu honro minha trajetória.", "Mantra do dia: Eu sou minha melhor companhia.", "Mantra do dia: Eu inspiro paz, expiro tensão.", "Mantra do dia: Meu passado não define meu futuro.", "Mantra do dia: A cada caída, eu me levanto mais sábia.", "Mantra do dia: Tudo que busco fora está dentro de mim.", "Mantra do dia: Eu ajo mesmo com medo.", "Mantra do dia: Nada é mais urgente que minha paz.", "Mantra do dia: Ser imperfeita é ser real.", "Mantra do dia: A paz começa dentro de mim.", "Mantra do dia: Me escutar é meu primeiro passo.", "Mantra do dia: Eu sou feita de força e esperança.", "Mantra do dia: A quietude me fortalece.", "Mantra do dia: Eu enfrento o medo com coragem.", "Mantra do dia: Eu sou suficiente.", "Mantra do dia: Tudo o que preciso já existe em mim.", "Mantra do dia: Eu celebro minha evolução.", "Mantra do dia: Meu esforço vale a pena.", "Mantra do dia: A serenidade me encontra.", "Mantra do dia: Eu sou mais forte do que imagino.", "Mantra do dia: O impossível só dura até eu tentar.", "Mantra do dia: Eu me reinvento quando é preciso.", "Mantra do dia: Hoje é dia de me superar.", "Mantra do dia: Eu transformo medo em ação.", "Mantra do dia: Eu sou mais do que os obstáculos.", "Mantra do dia: Eu escolho me conhecer um pouco mais a cada dia.", "Mantra do dia: Eu solto o que não posso controlar.", "Mantra do dia: Eu descanso sem culpa.", "Mantra do dia: Cada respiração é um renascimento.", "Mantra do dia: Eu deixo ir o que pesa.", "Mantra do dia: Eu sou firme como a rocha e flexível como o vento.", "Mantra do dia: Tudo acontece no tempo certo.", "Mantra do dia: Eu confio na minha força interior.", "Mantra do dia: Nada externo pode roubar minha paz.", "Mantra do dia: Eu sou uma versão melhor de mim a cada dia.", "Mantra do dia: Eu sou movimento, não estagnação.", "Mantra do dia: Me amar é um ato de coragem.", "Mantra do dia: Eu sou o lar que procuro.", "Mantra do dia: Eu me abraço com amor e verdade.", "Mantra do dia: Cada parte de mim merece compaixão.", "Mantra do dia: Eu sou um canal de tranquilidade.", "Mantra do dia: Eu ajo com fé no caminho que escolhi.", "Mantra do dia: Eu sou mais do que os rótulos.", "Mantra do dia: Eu posso, eu consigo, eu realizo.", "Mantra do dia: Meus sentimentos merecem espaço.", "Mantra do dia: Eu sou guerreira do meu próprio caminho.", "Mantra do dia: Eu me escolho, todos os dias.", "Mantra do dia: Eu abro espaço para o silêncio.", "Mantra do dia: Eu valorizo minhas conquistas internas.", "Mantra do dia: Eu escolho a calma.", "Mantra do dia: Eu sou o que sou, sem precisar provar.", "Mantra do dia: Eu não preciso ter todas as respostas agora.", "Mantra do dia: Me permitir mudar é um presente.", "Mantra do dia: Ser é mais importante que fazer.", "Mantra do dia: Minha intuição é uma bússola segura.", "Mantra do dia: Eu não desisto de mim.", "Mantra do dia: Eu me perdoo por não saber antes.", "Mantra do dia: Hoje, escolho a calma.", "Mantra do dia: Minha mente se aquieta no presente.", "Mantra do dia: Eu sou mais do que os “e se”.", "Mantra do dia: Eu cultivo a harmonia interior.", "Mantra do dia: Eu me liberto dos “e se”.", "Mantra do dia: A coragem mora em mim.", "Mantra do dia: Eu deixo o medo passar, sem me prender.", "Mantra do dia: Eu abraço os riscos com confiança.", "Mantra do dia: Eu me acolho com amor.", "Mantra do dia: O agora é tudo que tenho.", "Mantra do dia: Eu sou a autora da minha própria história.", "Mantra do dia: Eu sou um mistério bonito em constante revelação.", "Mantra do dia: Eu descanso e permito que minha alma respire.", "Mantra do dia: Eu mereço viver com leveza.", "Mantra do dia: Eu reconheço meus limites e escolho crescer com sabedoria.", "Mantra do dia: A vida pode ser mais suave.", "Mantra do dia: Eu transformo medo em movimento.", "Mantra do dia: Eu sou luz, mesmo nos dias nublados.", "Mantra do dia: Eu me observo sem julgamento.", "Mantra do dia: Eu confio em mim mesma.", "Mantra do dia: Eu sou capaz de enfrentar o desconhecido.", "Mantra do dia: Eu sou um campo de tranquilidade.", "Mantra do dia: Eu abraço minhas imperfeições com gentileza.", "Mantra do dia: Hoje é um bom dia para desacelerar.", "Mantra do dia: Eu escolho respirar em paz.", "Mantra do dia: Eu sigo em frente, mesmo com incertezas.", "Mantra do dia: Eu sou mais do que minhas dúvidas.", "Mantra do dia: Eu me desligo da pressa.", "Mantra do dia: Silenciar também é sabedoria.", "Mantra do dia: Eu permito que minha alma descanse.", "Mantra do dia: Eu sou o presente.", "Mantra do dia: Eu me permito ser verdadeira comigo.", "Mantra do dia: Pensar positivo me fortalece, mesmo quando as coisas ficam difíceis.", ]; useEffect(() => { // Calculate the day of the year to pick a unique phrase for each day const now = new Date(); const startOfYear = new Date(now.getFullYear(), 0, 0); const diff = now.getTime() - startOfYear.getTime(); const oneDay = 1000 * 60 * 60 * 24; const dayOfYear = Math.floor(diff / oneDay); const phraseIndex = dayOfYear % phrases.length; setDailyPhrase(phrases[phraseIndex]); }, []); // Run once on component mount return ( // Removed specific background from Dashboard to use App's global background
{/* [Image of Logo Obrigada ao Universo] */}
Logo Obrigada ao Universo { e.target.onerror = null; e.target.src = "https://placehold.co/180x80/cccccc/ffffff?text=Logo"; }} />
{/* Título e Frase Diária */}

!Mantra do dia

{/* Título roxo claro */}

{dailyPhrase}

ID do Usuário (Criador): {userId}

); }; // --- Content Sections (Placeholders) --- const SectionHeader = ({ title, onBack }) => (

{title}

{/* Placeholder for alignment */}
); const VideosSection = ({ onBack }) => { const allVideos = [ { id: '1', title: 'Video Teste 1', youtubeId: 'FsYdjlqU_zk' }, { id: '2', title: 'Video Teste 2', youtubeId: 'oG6-MQmEgx4' }, { id: '3', title: 'Video Teste 3', youtubeId: '4DuqSxe5wR0' }, { id: '4', title: 'Video Teste 4', youtubeId: 'r7dYs5hgGJ8' }, ]; const [selectedVideoId, setSelectedVideoId] = useState(null); // Stores the ID of the video to play const [showVideoPlayer, setShowVideoPlayer] = useState(false); // State to toggle video player view const handlePlayVideo = (youtubeId) => { setSelectedVideoId(youtubeId); setShowVideoPlayer(true); }; const handleBackToList = () => { setSelectedVideoId(null); setShowVideoPlayer(false); }; const youTubeChannelLink = "https://www.youtube.com/@ObrigadaaoUniversomusic"; // Your YouTube channel link return (

Aqui você poderá acessar os links dos seus vídeos exclusivos do YouTube.

{showVideoPlayer && selectedVideoId ? ( // Detailed Video Player View

{allVideos.find(video => video.youtubeId === selectedVideoId)?.title || "Vídeo"}

Ir para o canal no YouTube
) : ( // Video List View
    {allVideos.map((video) => (
  • handlePlayVideo(video.youtubeId)} className="flex items-center p-3 bg-gray-100 rounded-lg shadow-sm hover:shadow-md transition-all duration-200 cursor-pointer" > {`Capa { e.target.onerror = null; e.target.src = "https://placehold.co/80x45/cccccc/ffffff?text=Vídeo"; }} /> {video.title}
  • ))}
)}
); }; const MusicSection = ({ onBack }) => { const allMusicTracks = [ { id: '1', name: 'Canção teste', url: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/Audio-do-WhatsApp-de-2025-06-24-as-18.50.12_1f6835f6.mp3', category: 'Mantra', imageUrl: 'https://placehold.co/100x100/A7C7E7/FFFFFF?text=Mantra', lyrics: "Essa é a letra da Canção teste. \nÉ um mantra suave e calmante. \nRepita comigo para mais paz." }, { id: '2', name: 'Outra canção', url: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/Audio-do-WhatsApp-de-2025-06-24-as-18.50.12_f1b6180f.mp3', category: 'Amor Próprio', imageUrl: 'https://placehold.co/100x100/CBAACB/FFFFFF?text=Amor', lyrics: "Letra da Outra canção. \nSobre se amar e se valorizar. \nVocê é suficiente." }, { id: '3', name: 'Cantar ela', url: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/Audio-do-WhatsApp-de-2025-06-24-as-18.50.13_394ad6cb.mp3', category: 'Casa', imageUrl: 'https://placehold.co/100x100/F7C6C7/FFFFFF?text=Casa', lyrics: "Essa é a letra de Cantar ela. \nUma canção para o lar. \nCom muito amor e aconchego." }, ]; const [filteredTracks, setFilteredTracks] = useState(allMusicTracks); const [selectedCategory, setSelectedCategory] = useState('Todas'); const [currentTrackIndex, setCurrentTrackIndex] = useState(-1); // -1 means no track is selected/playing const [isPlaying, setIsPlaying] = useState(false); const [showDetailView, setShowDetailView] = useState(false); // New state to toggle detail view const audioRef = useRef(null); // Use useRef for the Audio object // Update filtered tracks when category changes useEffect(() => { if (selectedCategory === 'Todas') { setFilteredTracks(allMusicTracks); } else { setFilteredTracks(allMusicTracks.filter(track => track.category === selectedCategory)); } // Reset playback when filter changes setIsPlaying(false); setCurrentTrackIndex(-1); if (audioRef.current) { audioRef.current.pause(); audioRef.current.src = ''; } setShowDetailView(false); // Hide detail view when filter changes }, [selectedCategory]); // Handle audio playback logic and cleanup useEffect(() => { if (audioRef.current) { // Clean up previous event listener audioRef.current.onended = null; audioRef.current.pause(); audioRef.current.src = ''; // Clear source to release resources } if (isPlaying && currentTrackIndex !== -1 && filteredTracks.length > 0) { const trackToPlay = filteredTracks[currentTrackIndex]; if (trackToPlay && trackToPlay.url) { audioRef.current = new Audio(trackToPlay.url); audioRef.current.play().catch(e => console.error("Erro ao tocar música:", e)); // Set up onended event to play next song, looping the playlist audioRef.current.onended = () => { // If not in loop mode, go to next song or stop if at the end of the filtered list if (!audioRef.current.loop) { // Check audioRef.current.loop const nextIndex = (currentTrackIndex + 1); if (nextIndex < filteredTracks.length) { setCurrentTrackIndex(nextIndex); } else { setIsPlaying(false); setCurrentTrackIndex(-1); // Stop if at the end of the list and not looping } } else { // If loop is true, the `audioRef.current.loop` property will handle continuous looping. // We just ensure it plays the current song again. audioRef.current.play(); } }; } } else { if (audioRef.current) { audioRef.current.pause(); } } // Cleanup function for useEffect return () => { if (audioRef.current) { audioRef.current.onended = null; audioRef.current.pause(); audioRef.current.src = ''; } }; }, [isPlaying, currentTrackIndex, filteredTracks]); // Dependencies const handlePlayMusic = () => { // Only attempt to play if there's a track selected or we need to select the first one if (filteredTracks.length === 0) return; if (currentTrackIndex === -1) { // If no track is selected, start from the first one setCurrentTrackIndex(0); } setIsPlaying(true); setShowDetailView(true); // Ensure detail view is shown }; const handlePauseMusic = () => { if (audioRef.current) { audioRef.current.pause(); setIsPlaying(false); } }; const handleRepeatToggle = () => { if (audioRef.current) { audioRef.current.loop = !audioRef.current.loop; console.log('Looping:', audioRef.current.loop); // Optionally provide user feedback that loop state changed } }; const handleSelectAndPlayTrack = (index) => { setCurrentTrackIndex(index); setIsPlaying(true); setShowDetailView(true); // Show detail view when a track is selected }; const handleNextTrack = () => { if (filteredTracks.length === 0) return; const nextIndex = (currentTrackIndex + 1) % filteredTracks.length; setCurrentTrackIndex(nextIndex); setIsPlaying(true); // Ensure playback continues }; const handlePreviousTrack = () => { if (filteredTracks.length === 0) return; const prevIndex = (currentTrackIndex - 1 + filteredTracks.length) % filteredTracks.length; setCurrentTrackIndex(prevIndex); setIsPlaying(true); // Ensure playback continues }; const currentPlayingTrack = filteredTracks[currentTrackIndex]; const uniqueCategories = Array.from(new Set(allMusicTracks.map(track => track.category))); return ( // Removed specific background from MusicSection to use App's global background

Sua playlist de músicas exclusivas estará disponível aqui.

{showDetailView && currentPlayingTrack ? ( // Detailed Music View
{currentPlayingTrack.name} { e.target.onerror = null; e.target.src = "https://placehold.co/200x200/A7C7E7/FFFFFF?text=Capa+da+Música"; }} />

{currentPlayingTrack.name}

{currentPlayingTrack.category}

{/* Music Controls */}
{/* Added items-center */} {/* Previous Button */} {/* Pause Button */} {/* Play Button */} {/* Next Button */} {/* Repeat Toggle (optional to move, keeping it separate for now) */}

Letra da Música:

{currentPlayingTrack.lyrics}
) : ( // Music List View <>

Filtrar por Categoria:

Minhas Músicas:

{filteredTracks.length === 0 ? (

Nenhuma música encontrada nesta categoria.

) : (
    {filteredTracks.map((track, index) => (
  • handleSelectAndPlayTrack(index)} // Click anywhere on the item to play and show detail className={`flex items-center p-3 rounded-lg shadow-sm justify-between cursor-pointer transition-all duration-200 ${currentTrackIndex === index && isPlaying ? 'bg-[#CBAACB] bg-opacity-30 border border-[#CBAACB]' : 'bg-gray-100'}`} > {track.name} { e.target.onerror = null; e.target.src = "https://placehold.co/40x40/A7C7E7/FFFFFF?text=🎶"; }} />

    {track.name}

    {track.category}

    {currentTrackIndex === index && isPlaying && ( Tocando... )}
  • ))}
)} )} {/* Always show platform links */}

Conferir em Outras Plataformas:

); }; const BooksSection = ({ onBack, userId, db }) => { // Book content definitions const initialBooksData = [ { id: '1', title: 'Os 4 Níveis da Lei da Atração', // Updated title category: 'Lei da Atração', coverImageUrl: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/Obrigada-ao-universo.jpg', // Updated cover image synopsis: 'Descubra os 4 níveis da Lei da Atração — pensamento, decreto, emoção e vibração — e aprenda a alinhar mente, palavra e energia para atrair o que deseja com consciência e poder.', // Updated synopsis chapters: [ { title: 'CAPÍTULO 1 – Introdução', contentBlocks: [ { type: 'section-title', content: 'Os 4 Níveis da Lei da Atração' }, { type: 'text', content: 'Antes de aprender como usar a Lei da Atração, é importante entender que ela se manifesta em quatro níveis complementares.\n\nVocê está prestes a conhecer cada um deles — e ao alinhar todos, sua capacidade de atrair o que deseja será muito mais poderosa.\nEste modelo dos quatro níveis é uma forma prática de organizar os ensinamentos populares sobre a Lei da Atração, inspirados principalmente nas ideias divulgadas por Rhonda Byrne (autora de “O Segredo”) e nos ensinamentos de Abraham-Hicks.' }, ], }, { title: 'CAPÍTULO 2 – Pensamento', contentBlocks: [ { type: 'section-title', content: '1º Nível – O Poder do Pensar Positivo' }, { type: 'text', content: 'Esse é o nível mais conhecido: pensar positivo.\nMas atenção: só pensar positivo não é suficiente. O pensamento é o início, mas não o caminho completo.\nPensamentos são como sementes. Se você pensa constantemente em problemas, escassez ou medo, estará plantando exatamente isso.\n\nPor outro lado, pensar com frequência em coisas boas — conquistas, alegrias, sonhos — fortalece o seu campo de atração.\nA chave está na repetição consciente.\nPensamentos negativos vão surgir, é natural. Mas você pode escolher o que vai nutrir.\n\nSempre que perceber um pensamento limitante, redirecione:\nPense em algo que deseja atrair.\nVisualize lugares, momentos felizes.\nLembre-se do que te faz sentir gratidão.\nPensamento direcionado é o primeiro passo da manifestação.', }, ], }, { title: 'CAPÍTULO 3 – Decreto', contentBlocks: [ { type: 'section-title', content: '2º Nível – O Que Você Fala, Você Atrai' }, { type: 'text', content: 'Tudo o que você fala, você fortalece.\nQuando você declara algo em voz alta, está reforçando o que pensa — e enviando uma mensagem mais intensa ao universo.\nExemplo:\n\nAo dizer “Hoje eu termino esse trabalho”, seu cérebro registra isso como um compromisso.\n\nO mesmo vale para frases negativas como “Eu sou um fracasso”. Ao repeti-las, você se sabota.\nFale com consciência.\n\nSubstitua frases negativas por versões fortalecedoras:\nEm vez de “Eu não consigo”, diga “Estou aprendendo”.\nEm vez de “Isso é impossível”, diga “Ainda não sei como, mas vai dar certo”.\nPrática de decreto:\nEscolha um desejo e diga em voz alta:\n\n“Eu posso…”\n“Eu mereço…”\n“Eu consigo…”\n“Eu sou…” (abençoada, saudável, próspera, amada…)\nInclua também uma frase positiva sobre alguém que você ama. Espalhar energia positiva cria um ciclo de retorno.', }, ], }, { title: 'CAPÍTULO 4 – Emoção', contentBlocks: [ { type: 'section-title', content: '3º Nível – Sentir é o Segredo' }, { type: 'text', content: 'A emoção é a ponte entre o que você pensa e o que você atrai.\nPensar e decretar são importantes, mas sem sentimento, não há energia suficiente para manifestar.\nAo declarar seus desejos, sinta como se já fossem reais.\n\nImagine a alegria de viver aquilo, sinta no corpo, sorria, feche os olhos, vibre.\nNão se trata de fingir, mas de ativar em você o sentimento verdadeiro da conquista.\nSentir é o que magnetiza o seu desejo.\n\nQuanto mais forte for sua emoção, mais clara será sua vibração.', }, ], }, { title: 'CAPÍTULO 5 – Vibração', contentBlocks: [ { type: 'section-title', content: '4º Nível – Sua Frequência Atrai o Que Você Vibra' }, { type: 'text', content: 'A vibração é a frequência energética que você emite.\n\nÉ o frio na barriga, o arrepio, a expectativa boa. É quando seu corpo e mente acreditam que algo incrível está por vir.\nMuita gente vibra na ansiedade, no medo ou na dúvida. Mas você pode escolher vibrar na confiança, na leveza e na certeza.\n\nPara elevar sua vibração:\nVisualize-se vivendo aquilo que deseja.\nSinta a emoção como se já tivesse acontecido.\nPergunte-se:\nO que vou fazer quando isso se realizar?\nComo vou comemorar?\nQuem estará comigo?\nEsse tipo de vibração acelera os caminhos e te coloca em sintonia com o que você busca.', }, ], }, { title: 'CAPÍTULO 6 – Conclusão', contentBlocks: [ { type: 'section-title', content: 'Atração Consciente: O Poder Está em Você' }, { type: 'text', content: 'A Lei da Atração funciona melhor quando você alinha:\n\nPensamento – A intenção clara.\nDecreto – A palavra que afirma.\nEmoção – O sentimento verdadeiro.\nVibração – A energia que você transmite.\nVocê é um campo magnético de possibilidades.\n\nAgora que conhece os quatro níveis, pratique com consciência, constância e coração aberto.\nA mudança começa dentro.\n\nVocê atrai aquilo que acredita, sente e vive.\nVocê é a chave.', }, ], }, ], }, { id: '2', title: 'Caderno da Gratidão', category: 'Bônus', coverImageUrl: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/capa-2.png', synopsis: 'Um guia prático com exercícios diários para cultivar a gratidão e transformar sua perspectiva de vida. Ideal para reflexão e bem-estar, este caderno ajuda a focar nas coisas boas e a desenvolver uma mentalidade mais positiva e resiliente. Perfeito para iniciar ou aprofundar sua prática de gratidão.', chapters: [ { type: 'chapter-title', content: 'Introdução ao Caderno da Gratidão' }, { type: 'text', content: 'Este é um espaço para registrar diariamente suas gratidões e observar a transformação em sua vida.' }, { type: 'section-title', content: 'Exercício 1: Três Coisas Boas' }, { type: 'text', content: 'Todos os dias, antes de dormir, anote três coisas pelas quais você é grato(a).' } ] }, { id: '3', title: 'Afirmações de Amor', category: 'Afirmações', coverImageUrl: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/capa-1.png', synopsis: 'Coleção de afirmações poderosas focadas no amor próprio e nos relacionamentos, para fortalecer sua autoestima e atrair mais amor. Com frases cuidadosamente elaboradas, este livro é um recurso diário para nutrir a compaixão por si mesmo e pelos outros, promovendo harmonia e conexões profundas.', chapters: [ { type: 'chapter-title', content: 'Afirmações de Amor Próprio' }, { type: 'text', content: 'Eu sou digno(a) de amor e respeito. Eu me aceito e me amo incondicionalmente.' }, { type: 'section-title', content: 'Afirmações para Relacionamentos' }, { type: 'text', content: 'Eu atraio relacionamentos saudáveis e amorosos para a minha vida.' } ] }, ]; const [books, setBooks] = useState(initialBooksData); const [selectedBook, setSelectedBook] = useState(null); const [showDetailView, setShowDetailView] = useState(false); const [userFavorites, setUserFavorites] = useState({}); // { bookId: true/false } const [selectedCategory, setSelectedCategory] = useState('Todas'); // New state for category filter // Filtered books based on selected category and favorites const filteredBooks = React.useMemo(() => { let currentBooks = books; if (selectedCategory === 'Favoritos') { currentBooks = currentBooks.filter(book => userFavorites[book.id]); } else if (selectedCategory !== 'Todas') { currentBooks = currentBooks.filter(book => book.category === selectedCategory); } return currentBooks; }, [books, selectedCategory, userFavorites]); // Generate unique categories for the dropdown const uniqueCategories = React.useMemo(() => { const categories = Array.from(new Set(initialBooksData.map(book => book.category))); return ['Todas', 'Favoritos', ...categories]; }, [initialBooksData]); // Fetch user's favorite books from Firestore useEffect(() => { if (db && userId) { const userBooksRef = collection(db, `artifacts/${appId}/users/${userId}/books`); const unsubscribe = onSnapshot(userBooksRef, (snapshot) => { const favorites = {}; snapshot.docs.forEach(doc => { favorites[doc.id] = doc.data().isFavorite; }); setUserFavorites(favorites); }, (error) => { // Corrected console.error syntax console.error(`Erro ao carregar favoritos: ${error}`); }); return () => unsubscribe(); } }, [db, userId, appId]); // Update a book's favorite status in Firestore const handleToggleFavorite = async (bookId, currentStatus) => { if (!db || !userId) return; const bookDocRef = doc(db, `artifacts/${appId}/users/${userId}/books/${bookId}`); try { await setDoc(bookDocRef, { isFavorite: !currentStatus }, { merge: true }); // UI will update via onSnapshot listener } catch (error) { console.error("Erro ao atualizar favorito:", error); } }; const handleSelectBook = (book) => { setSelectedBook(book); setShowDetailView(true); }; const handleBackToShelf = () => { setSelectedBook(null); setShowDetailView(false); }; const [readingBook, setReadingBook] = useState(null); // State for the book currently being read const handleReadBook = (book) => { setReadingBook(book); }; if (readingBook) { return setReadingBook(null)} userId={userId} db={db} />; } return (

Leia seus livros digitais favoritos diretamente no aplicativo.

{showDetailView && selectedBook ? ( // Detailed Book View
{`Capa { e.target.onerror = null; e.target.src = "https://placehold.co/200x260/A0E7E5/000?text=Capa+Livro"; }} />

{selectedBook.title}

Categoria: {selectedBook.category}

{/* Favorite Star */}

Sinopse:

{selectedBook.synopsis}

) : ( // Book Shelf View <>

Filtrar por Categoria:

{filteredBooks.length === 0 ? (

Nenhum livro encontrado nesta categoria.

) : (
{/* Two columns */} {filteredBooks.map((book) => (
{`Capa handleSelectBook(book)} onError={(e) => { e.target.onerror = null; e.target.src = "https://placehold.co/100x150/A0E7E5/000?text=Capa"; }} />

{book.title}

{book.category}

))}
)} )}
); }; // New BookReaderView Component const BookReaderView = ({ book, onBack, userId, db }) => { const contentRef = useRef(null); const [currentChapterIndex, setCurrentChapterIndex] = useState(0); const [readerFontSize, setReaderFontSize] = useState('text-base'); // Default font size const fontSizes = ['text-sm', 'text-base', 'text-lg', 'text-xl']; const speechUtteranceRef = useRef(null); // Ref for SpeechSynthesisUtterance // Load reading progress from Firestore useEffect(() => { if (db && userId && book.id) { const readingProgressDocRef = doc(db, `artifacts/${appId}/users/${userId}/readingProgress/${book.id}`); const unsubscribe = onSnapshot(readingProgressDocRef, (docSnap) => { if (docSnap.exists()) { const data = docSnap.data(); setCurrentChapterIndex(data.chapterIndex || 0); setReaderFontSize(data.fontSize || 'text-base'); // Ensure contentRef.current is available before setting scroll position if (contentRef.current) { contentRef.current.scrollTop = data.scrollPosition || 0; } } }, (error) => { console.error(`Erro ao carregar progresso de leitura: ${error}`); // Corrected console.error syntax }); return () => unsubscribe(); } }, [db, userId, book.id, appId]); // Added appId to dependencies // Save reading progress to Firestore (debounced for performance) useEffect(() => { const handler = setTimeout(() => { saveReadingProgress(); }, 1000); // Save every 1 second after scroll/chapter change return () => { clearTimeout(handler); }; }, [currentChapterIndex, readerFontSize, contentRef.current?.scrollTop]); // Depend on relevant states const saveReadingProgress = async () => { if (!db || !userId || !book.id || !contentRef.current) return; const readingProgressDocRef = doc(db, `artifacts/${appId}/users/${userId}/readingProgress/${book.id}`); try { await setDoc(readingProgressDocRef, { chapterIndex: currentChapterIndex, fontSize: readerFontSize, scrollPosition: contentRef.current.scrollTop, lastRead: new Date(), }, { merge: true }); // console.log("Progresso de leitura salvo!"); } catch (error) { console.error("Erro ao salvar progresso de leitura:", error); } }; const handleFontSizeChange = (direction) => { const currentIndex = fontSizes.indexOf(readerFontSize); let newIndex = currentIndex; if (direction === 'increase' && currentIndex < fontSizes.length - 1) { newIndex++; } else if (direction === 'decrease' && currentIndex > 0) { newIndex--; } setReaderFontSize(fontSizes[newIndex]); }; const handleNextChapter = () => { if (currentChapterIndex < book.chapters.length - 1) { setCurrentChapterIndex(prevIndex => prevIndex + 1); if (contentRef.current) { contentRef.current.scrollTop = 0; // Scroll to top of new chapter } stopAudiobook(); // Stop any ongoing narration } }; const handlePreviousChapter = () => { if (currentChapterIndex > 0) { setCurrentChapterIndex(prevIndex => prevIndex - 1); if (contentRef.current) { contentRef.current.scrollTop = 0; // Scroll to top of new chapter } stopAudiobook(); // Stop any ongoing narration } }; // Audiobook functionality const startAudiobook = () => { if ('speechSynthesis' in window) { // If there's an utterance and it's paused, resume it. if (speechUtteranceRef.current && window.speechSynthesis.paused) { window.speechSynthesis.resume(); return; } // If not paused or no utterance, start new narration const textToSpeak = book.chapters[currentChapterIndex].contentBlocks .filter(block => block.type === 'text' || block.type === 'section-title' || block.type === 'chapter-title') .map(block => block.content) .join('\n\n'); if (!textToSpeak) { alert('Não há texto para ler neste capítulo.'); return; } const utterance = new SpeechSynthesisUtterance(textToSpeak); // Optionally set language, voice, pitch, rate utterance.lang = 'pt-BR'; // Set language to Portuguese (Brazil) // You can find available voices using window.speechSynthesis.getVoices() // utterance.voice = window.speechSynthesis.getVoices().find(voice => voice.lang === 'pt-BR'); utterance.rate = 1.0; // Speed of speech utterance.pitch = 1.0; // Pitch of speech utterance.onend = () => { console.log('Leitura finalizada.'); speechUtteranceRef.current = null; // Clear ref when finished }; utterance.onerror = (event) => { console.error('Erro na síntese de fala:', event.error); alert('Erro ao tentar ler o livro em voz alta. Seu navegador pode não suportar ou há um problema com a voz.'); }; window.speechSynthesis.cancel(); // Stop any previous speech window.speechSynthesis.speak(utterance); speechUtteranceRef.current = utterance; // Store reference to current utterance } else { alert('Seu navegador não suporta a leitura de texto em voz alta (Speech Synthesis).'); } }; const pauseAudiobook = () => { if ('speechSynthesis' in window && speechUtteranceRef.current) { window.speechSynthesis.pause(); } }; const stopAudiobook = () => { if ('speechSynthesis' in window) { window.speechSynthesis.cancel(); speechUtteranceRef.current = null; // Clear ref to ensure new start on next play } }; const currentChapter = book.chapters[currentChapterIndex]; return (
{/* Header with back button and title */}

{book.title}

{/* Top Controls: Font Size and Mark Page */}
{/* Audiobook Controls: Play, Pause, Stop (Icons) */}
{/* Chapter Navigation */}

Capítulo {currentChapterIndex + 1} de {book.chapters.length}

{/* Book Content Area */}
{currentChapter && (
{/* Render the chapter title */}

{currentChapter.title}

{/* Render content blocks within the chapter */} {currentChapter.contentBlocks.map((block, blockIndex) => ( {block.type === 'section-title' && (

{block.content}

)} {block.type === 'text' && (

{block.content}

)}
))}
)}
); }; // JapaMalaSection Component - Reconstruído do Zero (corrigindo problemas de escopo/definição) const JapaMalaSection = ({ onBack, userId, db }) => { const [count, setCount] = useState(0); const [targetCount, setTargetCount] = useState(108); // Default target count const [inputTarget, setInputTarget] = useState('108'); // For the input field const [selectedMandala, setSelectedMandala] = useState('mandala1'); // Default to the first mandala const [music, setMusic] = useState('none'); const [currentAudio, setCurrentAudio] = useState(null); // State to hold the current Audio object const lastClickTime = useRef(0); // To track last click time for double tap // Background options with both icon and main mandala URLs const mandalaOptions = { mandala1: { icon: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/icone-mandala-01-100.jpg) center/cover no-repeat', main: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/mandala-01.png) center/contain no-repeat' }, mandala2: { icon: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/icone-mandala-02-100.jpg) center/cover no-repeat', main: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/mandala-02.png) center/contain no-repeat' }, mandala3: { icon: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/icone-mandala-03-100.jpg) center/cover no-repeat', main: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/mandala-03.png) center/contain no-repeat' }, mandala4: { icon: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/icone-mandala-04-100.jpg) center/cover no-repeat', main: 'url(https://obrigadaaouniverso.com/wp-content/uploads/2025/06/mandala-04.png) center/contain no-repeat' }, }; // Music options const musicOptions = { none: { name: 'Nenhuma', url: '' }, // Ensure 'none' has an empty URL 'cancao-teste': { name: 'Canção teste', url: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/Audio-do-WhatsApp-de-2025-06-24-as-18.50.12_1f6835f6.mp3' }, 'outra-cancao': { name: 'Outra canção', url: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/Audio-do-WhatsApp-de-2025-06-24-as-18.50.12_f1b6180f.mp3' }, 'cantar-ela': { name: 'Cantar ela', url: 'https://obrigadaaouniverso.com/wp-content/uploads/2025/06/Audio-do-WhatsApp-de-2025-06-24-as-18.50.13_394ad6cb.mp3' }, }; // Effect to load user's Japa Mala settings from Firestore useEffect(() => { if (db && userId) { const userDocRef = doc(db, `artifacts/${appId}/users/${userId}/settings/japaMala`); const unsubscribe = onSnapshot(userDocRef, (docSnap) => { if (docSnap.exists()) { const data = docSnap.data(); setTargetCount(data.targetCount || 108); setInputTarget(String(data.targetCount || 108)); setSelectedMandala(data.selectedMandala || 'mandala1'); setMusic(data.music || 'none'); setCount(data.currentCount || 0); } }, (error) => { console.error(`Erro ao carregar configurações do Japa Mala: ${error}`); }); return () => unsubscribe(); } }, [db, userId, appId]); // Effect to save Japa Mala settings to Firestore (debounced) useEffect(() => { const handler = setTimeout(() => { if (db && userId) { const userDocRef = doc(db, `artifacts/${appId}/users/${userId}/settings/japaMala`); setDoc(userDocRef, { targetCount: targetCount, selectedMandala: selectedMandala, music: music, currentCount: count, lastUpdated: new Date(), }, { merge: true }).catch(error => { console.error("Erro ao salvar configurações do Japa Mala:", error); }); } }, 1000); // Save every 1 second after a state change return () => { clearTimeout(handler); }; }, [count, targetCount, selectedMandala, music, db, userId, appId]); // Dependencies for saving // Effect to manage current audio playback useEffect(() => { if (currentAudio) { currentAudio.pause(); currentAudio.src = ''; currentAudio.onended = null; // Clear event listener } const selectedMusicUrl = musicOptions[music]?.url; if (selectedMusicUrl) { const newAudio = new Audio(selectedMusicUrl); newAudio.loop = false; // Default to no loop, controlled by button newAudio.volume = 0.5; // Default volume setCurrentAudio(newAudio); // Listen for when audio ends to potentially play next newAudio.onended = () => { // Here, if you wanted the Japa Mala music to loop indefinitely, you'd set newAudio.loop = true earlier // For now, it just ends. If you want a playlist in JapaMala, we'd need more complex state. }; } else { setCurrentAudio(null); } return () => { if (currentAudio) { currentAudio.pause(); currentAudio.src = ''; currentAudio.onended = null; } }; }, [music]); // Only re-run when 'music' selection changes const handlePlayMusic = () => { if (currentAudio) { currentAudio.play().catch(e => console.error("Erro ao tocar música:", e)); } }; const handlePauseMusic = () => { if (currentAudio) { currentAudio.pause(); } }; const handleStopMusic = () => { if (currentAudio) { currentAudio.pause(); currentAudio.currentTime = 0; // Reset to start } }; const handleRepeatToggle = () => { if (currentAudio) { currentAudio.loop = !currentAudio.loop; console.log('Looping Japa Mala Music:', currentAudio.loop); } }; const handleIncrement = () => { const now = Date.now(); const DOUBLE_TAP_THRESHOLD = 300; // milliseconds // If count is at or above target, and it's a double tap, then reset if (count >= targetCount && (now - lastClickTime.current < DOUBLE_TAP_THRESHOLD)) { setCount(0); vibrate(500); // Strong vibrate for reset lastClickTime.current = 0; // Reset for next sequence return; // Exit, as reset handled } // If count is less than target, increment normally if (count < targetCount) { const newCount = count + 1; setCount(newCount); vibrate(50); // Small vibration on each tap if (newCount === targetCount) { vibrate(500); // Stronger vibration at target } } // If count is >= targetCount and it's not a double tap, do nothing (don't increment further) lastClickTime.current = now; // Update last click time for double tap detection }; const handleReset = () => { setCount(0); vibrate(100); // Small vibration on reset }; const handleTargetChange = (e) => { setInputTarget(e.target.value); const value = parseInt(e.target.value, 10); if (!isNaN(value) && value >= 0 && value <= 200) { setTargetCount(value); } }; return (
{/* Header with back button and title */}
{/* More delicate title with light font */}

Digital Japa Mala

{/* Placeholder for alignment */}
{/* Target Count Option - at the top */}
{/* Central Japa Mala Number - larger clickable area */}
{count}
{/* Reset Count Button */} {/* Background and Music Options - at the bottom */}

Escolha o Fundo:

{/* Mandala selection buttons - removed background and shadow-inner */}
{Object.keys(mandalaOptions).map((key) => { const option = mandalaOptions[key]; let previewStyle = { background: option.icon, // Use the icon URL for the preview circle backgroundSize: 'cover', // Ensure icons cover the circle backgroundPosition: 'center', }; return (
setSelectedMandala(key)} // Set selectedMandala className={`w-12 h-12 rounded-full cursor-pointer border-2 ${selectedMandala === key ? 'border-[#A7C7E7] ring-2 ring-[#CBAACB]' : 'border-gray-200'} transition-all duration-200 flex items-center justify-center text-xs text-white overflow-hidden`} style={previewStyle} > {/* No text here, relying on visual preview */}
); })}

Escolha a Música:

{/* Music Selection Dropdown */} {/* Music Control Buttons - Reordered and updated icons */}
{/* Pause Button */} {/* Play Button */} {/* Repeat Button */}
); }; // Main App Component const App = () => { const [isAuthenticated, setIsAuthenticated] = useState(false); const [userId, setUserId] = useState(null); const [currentScreen, setCurrentScreen] = useState('auth'); // 'auth', 'dashboard', 'videos', 'music', 'books', 'japa-mala' const [db, setDb] = useState(null); const [auth, setAuth] = useState(null); // Initialize Firebase and set up authentication listener useEffect(() => { try { const app = initializeApp(firebaseConfig); const firestoreDb = getFirestore(app); const firebaseAuth = getAuth(app); setDb(firestoreDb); setAuth(firebaseAuth); const unsubscribeAuth = onAuthStateChanged(firebaseAuth, async (user) => { if (user) { setIsAuthenticated(true); setUserId(user.uid); // If auth success, navigate to dashboard unless already in a content screen if (currentScreen === 'auth' || currentScreen === 'loading') { setCurrentScreen('dashboard'); } } else { setIsAuthenticated(false); setUserId(null); setCurrentScreen('auth'); // Sign in anonymously if initialAuthToken is provided, or just keep at auth screen if (initialAuthToken) { try { await signInWithCustomToken(firebaseAuth, initialAuthToken); } catch (error) { console.error("Error trying to log in with initial token:", error); // Fallback to anonymous or just stay on auth screen // We keep signInAnonymously here to allow Firestore rules to work await signInAnonymously(firebaseAuth); } } else { // Ensure there's always an anonymous user for Firestore access rules await signInAnonymously(firebaseAuth); } } }); return () => unsubscribeAuth(); } catch (error) { console.error("Error initializing Firebase:", error); // Potentially show an error message to the user } }, [currentScreen, initialAuthToken]); // Added initialAuthToken to dependencies const handleAuthSuccess = (uid) => { setIsAuthenticated(true); setUserId(uid); setCurrentScreen('dashboard'); }; const handleLogout = async () => { if (auth) { try { await signOut(auth); // onAuthStateChanged will handle setting isAuthenticated to false and navigating to 'auth' } catch (error) { console.error("Error logging out:", error); } } }; // Render the appropriate screen based on isAuthenticated and currentScreen state let content; if (!isAuthenticated) { content = ; } else { switch (currentScreen) { case 'dashboard': content = ; break; case 'videos': content = setCurrentScreen('dashboard')} />; break; case 'music': content = setCurrentScreen('dashboard')} />; break; case 'books': content = setCurrentScreen('dashboard')} userId={userId} db={db} />; // Pass userId and db break; case 'japa-mala': // JapaMalaSection is now defined above App, so it's in scope. content = setCurrentScreen('dashboard')} userId={userId} db={db} />; break; default: content = ; } } return (
{content} {/* Tailwind CSS Script - Always include this in HTML/React apps */} {/* Configure Tailwind to use JIT mode for faster compilation in development */} {/* Using dangerouslySetInnerHTML for the script content to avoid JSX parsing issues */}