diff --git a/package.json b/package.json index bfdf4d8..4fbf545 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prayer", - "version": "0.3.0", + "version": "0.4.0", "private": true, "type": "module", "scripts": { diff --git a/src/app/page.tsx b/src/app/page.tsx index 4717c52..6c62b27 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -30,6 +30,14 @@ interface Prayer { prayedDates?: string[]; // Array of dates when prayed for } +const getLocalISODate = () => { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +}; + export default function Home() { const [activeTab, setActiveTab] = useState<'home' | 'compose' | 'journal' | 'answered' | 'praying' | 'reflections' | 'about'>('home'); const [prayers, setPrayers] = useState([]); @@ -38,7 +46,7 @@ export default function Home() { const [mostPrayedFor, setMostPrayedFor] = useState(''); // Form state - const [date, setDate] = useState(new Date().toISOString().split('T')[0]); + const [date, setDate] = useState(getLocalISODate()); const [text, setText] = useState(''); const [prayFor, setPrayFor] = useState(''); const [answered, setAnswered] = useState(false); @@ -79,53 +87,6 @@ export default function Home() { fetchPrayerStatistics(); }, [user]); - // Load draft from Firebase - useEffect(() => { - if (!user) return; - - const loadDraft = async () => { - try { - const draftRef = doc(db, 'users', user.uid, 'drafts', 'currentPrayer'); - const draftSnap = await getDoc(draftRef); - - if (draftSnap.exists()) { - const draftData = draftSnap.data(); - setDate(draftData.date || new Date().toISOString().split('T')[0]); - setText(draftData.text || ''); - setPrayFor(draftData.prayFor || ''); - } - } catch (error: any) { - // Silently fail if offline - if (error?.code !== 'unavailable') { - console.error('Error loading draft:', error); - } - } - }; - - loadDraft(); - }, [user]); - - // Save draft to Firebase - useEffect(() => { - if (!user) return; - - const saveDraft = async () => { - try { - const draftRef = doc(db, 'users', user.uid, 'drafts', 'currentPrayer'); - await setDoc(draftRef, { date, text, prayFor, updatedAt: new Date().toISOString() }, { merge: true }); - } catch (error: any) { - // Silently fail if offline - will sync when connection is restored - if (error?.code !== 'unavailable') { - console.error('Error saving draft:', error); - } - } - }; - - // Debounce the save to avoid too many writes - const timeoutId = setTimeout(saveDraft, 1000); - return () => clearTimeout(timeoutId); - }, [date, text, prayFor, user]); - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -172,15 +133,11 @@ export default function Home() { } // Reset form - setDate(new Date().toISOString().split('T')[0]); + setDate(getLocalISODate()); setText(''); setPrayFor(''); - - // Clear draft from Firebase - if (user) { - const draftRef = doc(db, 'users', user.uid, 'drafts', 'currentPrayer'); - await setDoc(draftRef, { date: new Date().toISOString().split('T')[0], text: '', prayFor: '' }); - } + setAnswered(false); + setAnswerDetails(''); } catch (error) { console.error('Error saving prayer:', error); toast.error('Failed to save prayer'); @@ -262,18 +219,14 @@ export default function Home() { if (!user) return; try { - const today = new Date().toISOString().split('T')[0]; + const today = getLocalISODate(); const prayer = prayers.find(p => p.id === prayerId); if (!prayer) return; // Get existing prayed dates or initialize array const existingDates = prayer.prayedDates || []; - - // Add today's date if not already in the array - const updatedDates = existingDates.includes(today) - ? existingDates - : [...existingDates, today]; + const updatedDates = [...existingDates, today]; await updateDoc(doc(db, 'prayers', prayerId), { lastPrayedFor: today, @@ -377,9 +330,12 @@ export default function Home() { {activeTab === 'home' && ( p.date === new Date().toISOString().split('T')[0]).length} onStartPraying={() => setActiveTab('compose')} onSelectPrayer={handleSelectPrayer} + onGoToPraying={() => setActiveTab('praying')} + onGoToAnswered={() => setActiveTab('answered')} + onGoToJournal={() => setActiveTab('journal')} + onGoToReflections={() => setActiveTab('reflections')} userName={user.displayName || user.email || undefined} mostPrayedFor={mostPrayedFor} /> diff --git a/src/components/HomePage.tsx b/src/components/HomePage.tsx index 7a72ac7..cbeb97a 100644 --- a/src/components/HomePage.tsx +++ b/src/components/HomePage.tsx @@ -4,6 +4,14 @@ import { useState, useEffect } from 'react'; import Clock from './Clock'; import { HandHeart, CheckCircle2, PenSquare, BookOpen, Sparkles, Flame, BarChart3, MessageSquare } from 'lucide-react'; +const getLocalISODate = () => { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +}; + interface Prayer { id: string; userId: string; @@ -19,20 +27,47 @@ interface Prayer { interface HomePageProps { prayers: Prayer[]; - todayCount: number; onStartPraying: () => void; onSelectPrayer: (prayer: Prayer) => void; + onGoToPraying: () => void; + onGoToAnswered: () => void; + onGoToJournal: () => void; + onGoToReflections: () => void; userName?: string; mostPrayedFor?: string; } -export default function HomePage({ prayers, todayCount, onStartPraying, onSelectPrayer, userName, mostPrayedFor }: HomePageProps) { - const today = new Date().toISOString().split('T')[0]; - const todayPrayers = prayers.filter(p => p.date === today); +export default function HomePage({ prayers, onStartPraying, onSelectPrayer, onGoToPraying, onGoToAnswered, onGoToJournal, onGoToReflections, userName, mostPrayedFor }: HomePageProps) { + const today = getLocalISODate(); const recentPrayers = prayers.slice(0, 5); const totalPrayers = prayers.length; const uniqueDays = new Set(prayers.map(p => p.date)).size; const answeredPrayers = prayers.filter(p => p.answered).length; + + const prayedTodayDetails = prayers + .map(prayer => { + const dates = prayer.prayedDates && prayer.prayedDates.length > 0 + ? prayer.prayedDates + : prayer.lastPrayedFor + ? [prayer.lastPrayedFor] + : []; + + const todayOccurrences = dates.filter(d => d === today).length; + if (todayOccurrences === 0) return null; + + return { + id: prayer.id, + title: prayer.prayFor || prayer.text || 'Prayer', + count: todayOccurrences, + }; + }) + .filter(Boolean) as { id: string; title: string; count: number }[]; + + const todaysUniquePrayers = prayedTodayDetails.length; + const todaysTotalTaps = prayedTodayDetails.reduce((sum, item) => sum + item.count, 0); + const topTodayPrayers = [...prayedTodayDetails] + .sort((a, b) => b.count - a.count || a.title.localeCompare(b.title)) + .slice(0, 3); // Get greeting based on time of day const getGreeting = () => { @@ -57,12 +92,15 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect const activePrayerCount = prayers.filter(p => !p.answered).length; // Count how many active prayers were prayed for today - const prayedTodayCount = prayers.filter(p => - !p.answered && ( - p.prayedDates?.includes(today) || - p.lastPrayedFor === today - ) - ).length; + const prayedTodayCount = prayers.filter(p => { + if (p.answered) return false; + const dates = p.prayedDates && p.prayedDates.length > 0 + ? p.prayedDates + : p.lastPrayedFor + ? [p.lastPrayedFor] + : []; + return dates.some(d => d === today); + }).length; // Calculate actual streak - consecutive days with prayers const calculateStreak = () => { @@ -162,37 +200,99 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect
{/* Today's Prayers - Featured */}
-
+
-
Today's Prayers
-
{todayCount}
+
Today's Prayers
+
{todaysUniquePrayers}
+
unique prayed items
+
+
+
Total taps
+
{todaysTotalTaps}
-
+ + {topTodayPrayers.length > 0 ? ( +
+ {topTodayPrayers.map(item => ( +
+
+ +
{item.title}
+
+
{item.count} {item.count === 1 ? 'tap' : 'taps'}
+
+ ))} +
+ ) : ( +
+ Mark a prayer as prayed to see it here. +
+ )}
+ {/* Things to Pray For - Active Requests */} + {(() => { + const pendingPrayers = prayers.filter(p => !p.answered); + if (pendingPrayers.length > 0) { + return ( + + ); + } + return null; + })()} + {/* Main Stats Grid */}
{/* Total Prayers */} -
+
+
Go to Reflections →
+ {/* Days Prayed */} -
+
+
Open Journal →
+ {/* Answered Prayers */} -
+
+
See answers →
+ {/* Streak */}
@@ -240,69 +340,6 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect
- {/* Things to Pray For - Active Requests */} - {(() => { - const pendingPrayers = prayers.filter(p => !p.answered); - if (pendingPrayers.length > 0) { - return ( -
-

- Things to Pray For -

-
-
{pendingPrayers.length}
-
active prayer requests
- -
-
- ); - } - return null; - })()} - - {/* Recent Prayers */} - {recentPrayers.length > 0 && ( -
-

- Recent Prayers -

-
- {recentPrayers.map((prayer, idx) => ( - - ))} -
-
- )} - {/* Recent Answered Prayers */} {recentAnswered.length > 0 && (
diff --git a/src/lib/version.ts b/src/lib/version.ts index 1520842..c03bd2e 100644 --- a/src/lib/version.ts +++ b/src/lib/version.ts @@ -1,8 +1,8 @@ -export const APP_VERSION = '0.3.0'; +export const APP_VERSION = '0.4.0'; export const WHATS_NEW_BRIEF: string[] = [ - 'Firebase Migration: all data now syncs across devices', - 'Calendar: persistent prayer tracking with multi-day support', - 'Loading UX: smooth auth and data loading transitions', - 'Offline Support: IndexedDB persistence for reliability' + 'Dashboard Navigation: tap stat cards to jump to relevant sections', + 'Today\'s Prayers: shows unique prayers + total taps with details', + 'Fresh Compose: no draft persistence, clean slate every time', + 'Quick Access: Things to Pray For card now tappable' ];