feat: enhance dashboard with real streak calculation, answered prayers section, and more stats
This commit is contained in:
1 parent
1d9e2a5d38
commit
55076bbc0e
1 file changed
+110
-7
+110
-7
@@ -10,6 +10,7 @@ interface Prayer {
|
||||
text: string;
|
||||
prayFor?: string;
|
||||
answered?: boolean;
|
||||
answerDetails?: string;
|
||||
}
|
||||
|
||||
interface HomePageProps {
|
||||
@@ -26,7 +27,53 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect
|
||||
const totalPrayers = prayers.length;
|
||||
const uniqueDays = new Set(prayers.map(p => p.date)).size;
|
||||
const answeredPrayers = prayers.filter(p => p.answered).length;
|
||||
const streak = Math.floor(Math.random() * 30) + 1; // Calculate actual streak
|
||||
|
||||
// Calculate actual streak - consecutive days with prayers
|
||||
const calculateStreak = () => {
|
||||
if (prayers.length === 0) return 0;
|
||||
|
||||
const prayerDates = Array.from(new Set(prayers.map(p => p.date))).sort().reverse();
|
||||
let streak = 0;
|
||||
let checkDate = new Date(today);
|
||||
|
||||
for (let i = 0; i < 365; i++) {
|
||||
const dateStr = checkDate.toISOString().split('T')[0];
|
||||
if (prayerDates.includes(dateStr)) {
|
||||
streak++;
|
||||
} else if (i === 0) {
|
||||
// If today has no prayer, streak is broken (unless we haven't prayed today yet, which is ok)
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
checkDate.setDate(checkDate.getDate() - 1);
|
||||
}
|
||||
|
||||
return streak;
|
||||
};
|
||||
|
||||
const streak = calculateStreak();
|
||||
|
||||
// Calculate more stats
|
||||
const avgPrayerLength = totalPrayers > 0
|
||||
? Math.round(prayers.reduce((sum, p) => sum + p.text.length, 0) / totalPrayers)
|
||||
: 0;
|
||||
|
||||
const answeredPercentage = totalPrayers > 0
|
||||
? Math.round((answeredPrayers / totalPrayers) * 100)
|
||||
: 0;
|
||||
|
||||
// Get most prayed for person/topic
|
||||
const prayerForCounts: Record<string, number> = {};
|
||||
prayers.forEach(p => {
|
||||
if (p.prayFor) {
|
||||
prayerForCounts[p.prayFor] = (prayerForCounts[p.prayFor] || 0) + 1;
|
||||
}
|
||||
});
|
||||
const mostPrayedFor = Object.entries(prayerForCounts).sort((a, b) => b[1] - a[1])[0];
|
||||
|
||||
// Recent answered prayers
|
||||
const recentAnswered = prayers.filter(p => p.answered).slice(0, 3);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 px-4 pb-32">
|
||||
@@ -67,7 +114,7 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Grid */}
|
||||
{/* Main Stats Grid */}
|
||||
<div className="grid grid-cols-2 gap-4 mb-8">
|
||||
{/* Total Prayers */}
|
||||
<div className="bg-slate-800/60 backdrop-blur-xl rounded-xl p-4 border border-slate-700/50 hover:border-slate-600 transition-all">
|
||||
@@ -83,20 +130,39 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect
|
||||
<div className="text-xs text-slate-500 mt-2">Consistent</div>
|
||||
</div>
|
||||
|
||||
{/* Answered */}
|
||||
{/* Answered Prayers */}
|
||||
<div className="bg-slate-800/60 backdrop-blur-xl rounded-xl p-4 border border-slate-700/50 hover:border-slate-600 transition-all">
|
||||
<div className="text-slate-400 text-xs font-semibold mb-2">✅ Answered</div>
|
||||
<div className="text-3xl font-bold text-green-400">{answeredPrayers}</div>
|
||||
<div className="text-xs text-slate-500 mt-2">Blessings</div>
|
||||
<div className="text-xs text-slate-500 mt-2">{answeredPercentage}% answered</div>
|
||||
</div>
|
||||
|
||||
{/* Streak */}
|
||||
<div className="bg-slate-800/60 backdrop-blur-xl rounded-xl p-4 border border-slate-700/50 hover:border-slate-600 transition-all">
|
||||
<div className="text-slate-400 text-xs font-semibold mb-2">🔥 Streak</div>
|
||||
<div className="text-3xl font-bold text-orange-400">{streak} days</div>
|
||||
<div className="text-xs text-slate-500 mt-2">Keep going!</div>
|
||||
<div className="text-3xl font-bold text-orange-400">{streak}</div>
|
||||
<div className="text-xs text-slate-500 mt-2">consecutive days</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Secondary Stats */}
|
||||
<div className="grid grid-cols-2 gap-4 mb-8">
|
||||
{/* Avg Prayer Length */}
|
||||
<div className="bg-purple-900/20 backdrop-blur-xl rounded-xl p-4 border border-purple-500/30">
|
||||
<div className="text-purple-300 text-xs font-semibold mb-2">📊 Avg Length</div>
|
||||
<div className="text-2xl font-bold text-purple-200">{avgPrayerLength}</div>
|
||||
<div className="text-xs text-purple-400 mt-2">characters</div>
|
||||
</div>
|
||||
|
||||
{/* Most Prayed For */}
|
||||
{mostPrayedFor && (
|
||||
<div className="bg-pink-900/20 backdrop-blur-xl rounded-xl p-4 border border-pink-500/30">
|
||||
<div className="text-pink-300 text-xs font-semibold mb-2">💝 Most Prayed For</div>
|
||||
<div className="text-sm font-bold text-pink-200 line-clamp-1">{mostPrayedFor[0]}</div>
|
||||
<div className="text-xs text-pink-400 mt-2">{mostPrayedFor[1]} times</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
@@ -112,7 +178,7 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect
|
||||
|
||||
{/* Recent Prayers */}
|
||||
{recentPrayers.length > 0 && (
|
||||
<div className="max-w-md mx-auto">
|
||||
<div className="max-w-md mx-auto mb-12">
|
||||
<h2 className="text-lg font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span>📖</span> Recent Prayers
|
||||
</h2>
|
||||
@@ -144,6 +210,43 @@ export default function HomePage({ prayers, todayCount, onStartPraying, onSelect
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Recent Answered Prayers */}
|
||||
{recentAnswered.length > 0 && (
|
||||
<div className="max-w-md mx-auto mb-12">
|
||||
<h2 className="text-lg font-bold text-green-400 mb-4 flex items-center gap-2">
|
||||
<span>✨</span> Recently Answered
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{recentAnswered.map((prayer) => (
|
||||
<div
|
||||
key={prayer.id}
|
||||
className="bg-green-900/20 backdrop-blur-xl rounded-xl p-4 border border-green-500/30 hover:border-green-400/50 transition-all"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="text-2xl">✅</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
{prayer.prayFor && (
|
||||
<div className="text-xs font-bold text-green-300 mb-1 uppercase tracking-wider">
|
||||
🙏 {prayer.prayFor}
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-slate-300 line-clamp-2">{prayer.text}</p>
|
||||
{prayer.answerDetails && (
|
||||
<p className="text-xs text-green-400 mt-2 italic">
|
||||
💬 "{prayer.answerDetails.substring(0, 60)}..."
|
||||
</p>
|
||||
)}
|
||||
<div className="text-xs text-slate-500 mt-2">
|
||||
{new Date(prayer.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Empty State */}
|
||||
{prayers.length === 0 && (
|
||||
<div className="max-w-md mx-auto text-center py-12">
|
||||
|
||||
Reference in new issue
Block a user