277 lines
9.3 KiB
TypeScript
277 lines
9.3 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useAuth } from '@/context/AuthContext';
|
|
import Login from '@/components/Login';
|
|
import LoadingSkeleton from '@/components/LoadingSkeleton';
|
|
import BottomNav from '@/components/BottomNav';
|
|
import ReminderBanner from '@/components/ReminderBanner';
|
|
import DailyEntry from '@/components/DailyEntry';
|
|
import CalendarView from '@/components/CalendarView';
|
|
import ReflectionsList from '@/components/ReflectionsList';
|
|
import ThingsToPrayFor from '@/components/ThingsToPrayFor';
|
|
import AboutPage from '@/components/AboutPage';
|
|
import HomePage from '@/components/HomePage';
|
|
import AnsweredPrayers from '@/components/AnsweredPrayers';
|
|
import { db } from '@/lib/firebase';
|
|
import { collection, addDoc, getDocs, updateDoc, query, where, doc } from 'firebase/firestore';
|
|
import toast from 'react-hot-toast';
|
|
|
|
interface Prayer {
|
|
id: string;
|
|
userId: string;
|
|
date: string;
|
|
text: string;
|
|
prayFor?: string;
|
|
answered?: boolean;
|
|
answerDetails?: string;
|
|
prayFrequency?: number; // 1-7 times per week
|
|
lastPrayedFor?: string; // ISO date string
|
|
}
|
|
|
|
export default function Home() {
|
|
const [activeTab, setActiveTab] = useState<'home' | 'compose' | 'journal' | 'answered' | 'praying' | 'reflections' | 'about'>('home');
|
|
const [prayers, setPrayers] = useState<Prayer[]>([]);
|
|
const [editingPrayer, setEditingPrayer] = useState<Prayer | null>(null);
|
|
|
|
// Form state
|
|
const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
|
|
const [text, setText] = useState('');
|
|
const [prayFor, setPrayFor] = useState('');
|
|
const [answered, setAnswered] = useState(false);
|
|
const [answerDetails, setAnswerDetails] = useState('');
|
|
|
|
const { user, loading: authLoading } = useAuth();
|
|
|
|
// Load prayers from Firestore
|
|
useEffect(() => {
|
|
if (!user || !user.emailVerified) return;
|
|
|
|
const loadPrayers = async () => {
|
|
try {
|
|
const prayersRef = collection(db, 'prayers');
|
|
const q = query(prayersRef, where('userId', '==', user.uid));
|
|
const querySnapshot = await getDocs(q);
|
|
|
|
const loadedPrayers: Prayer[] = [];
|
|
querySnapshot.forEach((doc) => {
|
|
loadedPrayers.push({ id: doc.id, ...doc.data() } as Prayer);
|
|
});
|
|
|
|
setPrayers(loadedPrayers);
|
|
} catch (error) {
|
|
console.error('Error loading prayers:', error);
|
|
toast.error('Failed to load prayers');
|
|
}
|
|
};
|
|
|
|
loadPrayers();
|
|
}, [user]);
|
|
|
|
// Load draft from localStorage
|
|
useEffect(() => {
|
|
const draft = localStorage.getItem('prayerDraft');
|
|
if (draft) {
|
|
try {
|
|
const { date: draftDate, text: draftText, prayFor: draftPrayFor } = JSON.parse(draft);
|
|
setDate(draftDate || new Date().toISOString().split('T')[0]);
|
|
setText(draftText || '');
|
|
setPrayFor(draftPrayFor || '');
|
|
} catch (e) {
|
|
console.error('Error loading draft:', e);
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
// Save draft to localStorage
|
|
useEffect(() => {
|
|
localStorage.setItem('prayerDraft', JSON.stringify({ date, text, prayFor }));
|
|
}, [date, text, prayFor]);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!user) {
|
|
toast.error('Please log in to save prayers');
|
|
return;
|
|
}
|
|
|
|
if (!text.trim()) {
|
|
toast.error('Please write something');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const prayerData = {
|
|
userId: user.uid,
|
|
date,
|
|
text,
|
|
prayFor: prayFor || undefined,
|
|
answered,
|
|
answerDetails: answerDetails || undefined,
|
|
};
|
|
|
|
if (editingPrayer) {
|
|
// Update existing prayer
|
|
await updateDoc(doc(db, 'prayers', editingPrayer.id), prayerData);
|
|
setPrayers(prev =>
|
|
prev.map(p => (p.id === editingPrayer.id ? { ...p, ...prayerData } : p))
|
|
);
|
|
toast.success('Prayer updated! 🙏');
|
|
setEditingPrayer(null);
|
|
} else {
|
|
// Add new prayer
|
|
const docRef = await addDoc(collection(db, 'prayers'), prayerData);
|
|
const newPrayer: Prayer = { id: docRef.id, ...prayerData };
|
|
setPrayers(prev => [...prev, newPrayer]);
|
|
toast.success('Prayer saved! 🙏');
|
|
}
|
|
|
|
// Reset form
|
|
setDate(new Date().toISOString().split('T')[0]);
|
|
setText('');
|
|
setPrayFor('');
|
|
localStorage.removeItem('prayerDraft');
|
|
} catch (error) {
|
|
console.error('Error saving prayer:', error);
|
|
toast.error('Failed to save prayer');
|
|
}
|
|
};
|
|
|
|
const handleSelectPrayer = (prayer: Prayer) => {
|
|
setEditingPrayer(prayer);
|
|
setDate(prayer.date);
|
|
setText(prayer.text);
|
|
setPrayFor(prayer.prayFor || '');
|
|
setAnswered(prayer.answered || false);
|
|
setAnswerDetails(prayer.answerDetails || '');
|
|
setActiveTab('compose');
|
|
};
|
|
|
|
const handleDateClick = (clickedDate: string) => {
|
|
const dayPrayers = prayers.filter(p => p.date === clickedDate);
|
|
if (dayPrayers.length > 0) {
|
|
handleSelectPrayer(dayPrayers[0]);
|
|
} else {
|
|
setDate(clickedDate);
|
|
setActiveTab('home');
|
|
}
|
|
};
|
|
|
|
const handleMarkPrayed = async (prayerId: string) => {
|
|
if (!user) return;
|
|
|
|
try {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
await updateDoc(doc(db, 'prayers', prayerId), {
|
|
lastPrayedFor: today,
|
|
});
|
|
|
|
setPrayers(prev =>
|
|
prev.map(p => (p.id === prayerId ? { ...p, lastPrayedFor: today } : p))
|
|
);
|
|
toast.success('Prayed! 🙏');
|
|
} catch (error) {
|
|
console.error('Error marking prayed:', error);
|
|
toast.error('Failed to update');
|
|
}
|
|
};
|
|
|
|
if (authLoading) {
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-black to-slate-800 flex items-center justify-center p-4">
|
|
<div className="text-center">
|
|
<div className="mb-8 relative">
|
|
<div className="w-24 h-24 mx-auto relative">
|
|
<div className="absolute inset-0 border-4 border-blue-500/30 rounded-full animate-spin"></div>
|
|
<div
|
|
className="absolute inset-2 border-4 border-transparent border-t-blue-400 rounded-full animate-spin"
|
|
style={{ animationDirection: 'reverse', animationDuration: '1.5s' }}
|
|
></div>
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="text-3xl animate-pulse">🙏</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h1 className="text-2xl font-bold text-white mb-2 animate-pulse">Haver</h1>
|
|
<p className="text-gray-400 text-lg mb-4">Preparing your sacred space...</p>
|
|
|
|
<div className="max-w-xs mx-auto">
|
|
<blockquote className="text-gray-300 italic text-sm leading-relaxed">
|
|
"Be anxious for nothing, but in everything by prayer and supplication, with thanksgiving, let your requests be made known to God."
|
|
</blockquote>
|
|
<cite className="text-gray-500 text-xs mt-2 block">— Philippians 4:6</cite>
|
|
</div>
|
|
|
|
<div className="flex justify-center space-x-2 mt-8">
|
|
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '0s' }}></div>
|
|
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '0.2s' }}></div>
|
|
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '0.4s' }}></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!user) {
|
|
return <Login />;
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 pb-24 md:pb-0" style={{ paddingBottom: 'calc(6rem + env(safe-area-inset-bottom))' }}>
|
|
<ReminderBanner userEmail={user.email || undefined} />
|
|
<BottomNav activeTab={activeTab} onTabChange={setActiveTab} />
|
|
|
|
{activeTab === 'home' && (
|
|
<HomePage
|
|
prayers={prayers}
|
|
todayCount={prayers.filter(p => p.date === new Date().toISOString().split('T')[0]).length}
|
|
onStartPraying={() => setActiveTab('compose')}
|
|
onSelectPrayer={handleSelectPrayer}
|
|
userName={user.displayName || user.email || undefined}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'compose' && (
|
|
<DailyEntry
|
|
date={date}
|
|
onDateChange={setDate}
|
|
text={text}
|
|
onTextChange={setText}
|
|
prayFor={prayFor}
|
|
onPrayForChange={setPrayFor}
|
|
answered={answered}
|
|
onAnsweredChange={setAnswered}
|
|
answerDetails={answerDetails}
|
|
onAnswerDetailsChange={setAnswerDetails}
|
|
onSubmit={handleSubmit}
|
|
isEditing={!!editingPrayer}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'journal' && (
|
|
<CalendarView prayers={prayers} onDayClick={handleDateClick} />
|
|
)}
|
|
|
|
{activeTab === 'answered' && (
|
|
<AnsweredPrayers prayers={prayers} onEdit={handleSelectPrayer} />
|
|
)}
|
|
|
|
{activeTab === 'praying' && (
|
|
<ThingsToPrayFor prayers={prayers.filter(p => !p.answered)} onEdit={handleSelectPrayer} onMarkPrayed={handleMarkPrayed} />
|
|
)}
|
|
|
|
{activeTab === 'reflections' && (
|
|
<ReflectionsList
|
|
prayers={prayers}
|
|
onPrayerClick={handleSelectPrayer}
|
|
onEdit={handleSelectPrayer}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'about' && <AboutPage userEmail={user.email || undefined} />}
|
|
</div>
|
|
);
|
|
}
|