Files
prayer-app/src/components/DailyEntry.tsx
T
nmemmert 67c5ff1b35 Major improvements: Firebase data migration, enhanced loading screens, multi-day prayer tracking
- Migrated all localStorage data to Firebase for cross-device sync
  - Prayer drafts now stored in users/{userId}/drafts
  - Reminder settings in users/{userId}/settings
  - Prayer statistics in users/{userId}/statistics
- Implemented persistent 'most prayed for' tracking in Firebase
- Enhanced loading screen with animated backgrounds and gradients
- Fixed auth session restoration on hard refresh
- Added prayedDates array for multi-day prayer tracking
- Calendar now accurately shows all days a prayer was prayed for
- Enabled Firestore offline persistence for offline-first experience
- Updated Firestore security rules for new data structure
- Improved clock display (removed leading zero from hours)
- Moved clock below date on homepage with appropriate sizing
- Fixed data loading race conditions between auth and Firestore
2026-01-19 10:11:57 -05:00

181 lines
7.8 KiB
TypeScript

'use client';
import React, { useState } from 'react';
import toast from 'react-hot-toast';
import { PenSquare, Calendar as CalendarIcon, HandHeart, CheckCircle2, Sparkles, Save, Loader2, ChevronDown } from 'lucide-react';
interface DailyEntryProps {
date: string;
onDateChange: (date: string) => void;
text: string;
onTextChange: (text: string) => void;
prayFor: string;
onPrayForChange: (prayFor: string) => void;
prayForOptions?: string[];
answered?: boolean;
onAnsweredChange?: (answered: boolean) => void;
answerDetails?: string;
onAnswerDetailsChange?: (details: string) => void;
onSubmit: (e: React.FormEvent) => void;
loading?: boolean;
isEditing?: boolean;
}
export default function DailyEntry({
date,
onDateChange,
text,
onTextChange,
prayFor,
onPrayForChange,
prayForOptions = [],
answered = false,
onAnsweredChange,
answerDetails = '',
onAnswerDetailsChange,
onSubmit,
loading = false,
isEditing = false,
}: DailyEntryProps) {
const today = new Date().toISOString().split('T')[0];
const isToday = date === today;
const wordCount = text.split(/\s+/).filter(w => w).length;
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 pt-24 px-4 pb-8">
{/* Animated Background */}
<div className="fixed inset-0 -z-10">
<div className="absolute top-40 right-20 w-80 h-80 bg-blue-500/10 rounded-full blur-3xl"></div>
<div className="absolute bottom-40 left-20 w-80 h-80 bg-purple-500/10 rounded-full blur-3xl"></div>
</div>
<div className="max-w-2xl mx-auto">
{/* Header */}
<div className="text-center mb-12">
<img src="/icon-192x192.png" alt="Haver icon" className="w-12 h-12 mx-auto mb-3 rounded" />
<h1 className="text-4xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent mb-2">
<span className="inline-flex items-center gap-2 justify-center">
<PenSquare className="w-7 h-7 text-blue-400" />
{isToday ? "Today's Prayer" : 'Prayer Entry'}
</span>
</h1>
<p className="text-slate-400 text-lg">
{new Date(date).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</p>
</div>
{/* Entry Form */}
<form onSubmit={onSubmit} className="space-y-6">
{/* Date Input */}
<div>
<label className="block text-sm font-semibold text-slate-300 mb-3 inline-flex items-center gap-1.5"><CalendarIcon className="w-4 h-4" /> Date</label>
<input
type="date"
value={date}
onChange={(e) => onDateChange(e.target.value)}
className="w-full px-4 py-3 rounded-xl bg-slate-800/60 backdrop-blur-xl border border-slate-700/50 text-white placeholder-slate-500 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all"
/>
</div>
{/* Pray For */}
<div>
<label className="block text-sm font-semibold text-slate-300 mb-3 inline-flex items-center gap-1.5"><HandHeart className="w-4 h-4" /> Praying For (Optional)</label>
<div className="relative">
<input
type="text"
value={prayFor}
onChange={(e) => onPrayForChange(e.target.value)}
placeholder="Person, situation, or concern..."
list="prayfor-options"
className="w-full px-4 py-3.5 pr-12 rounded-xl bg-slate-800/60 backdrop-blur-xl border border-slate-700/50 text-white placeholder-slate-500 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all"
/>
{prayForOptions.length > 0 && (
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-6 h-6 text-slate-400 pointer-events-none" />
)}
</div>
{prayForOptions.length > 0 && (
<datalist id="prayfor-options">
{prayForOptions.map((opt) => (
<option key={opt} value={opt} />
))}
</datalist>
)}
</div>
{/* Prayer Text */}
<div>
<div className="flex items-center justify-between mb-3">
<label className="block text-sm font-semibold text-slate-300 inline-flex items-center gap-1.5"><PenSquare className="w-4 h-4" /> Your Prayer</label>
<span className="text-xs text-slate-500">{wordCount} words</span>
</div>
<textarea
value={text}
onChange={(e) => onTextChange(e.target.value)}
placeholder="Write your thoughts, feelings, and prayers here..."
rows={10}
required
className="w-full px-4 py-3 rounded-xl bg-slate-800/60 backdrop-blur-xl border border-slate-700/50 text-white placeholder-slate-500 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all resize-none font-serif"
/>
</div>
{/* Answered Checkbox */}
<div className="bg-slate-800/40 backdrop-blur-xl rounded-xl p-4 border border-slate-700/50">
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={answered}
onChange={(e) => onAnsweredChange?.(e.target.checked)}
className="w-5 h-5 rounded border-slate-600 bg-slate-700 text-green-500 focus:ring-green-500 cursor-pointer"
/>
<span className="font-semibold text-slate-300 inline-flex items-center gap-1.5"><CheckCircle2 className="w-4 h-4" /> Mark as answered</span>
</label>
</div>
{/* Answer Details */}
{answered && (
<div className="bg-green-900/20 backdrop-blur-xl rounded-xl p-4 border border-green-500/30 animate-in fade-in-50 duration-300">
<label htmlFor="answerDetails" className="block text-sm font-semibold text-green-400 mb-3 inline-flex items-center gap-1.5">
<Sparkles className="w-4 h-4" /> How was it answered?
</label>
<textarea
id="answerDetails"
value={answerDetails}
onChange={(e) => onAnswerDetailsChange?.(e.target.value)}
placeholder="Share how God answered this prayer..."
rows={4}
className="w-full px-4 py-3 rounded-xl bg-slate-800/60 backdrop-blur-xl border border-green-500/30 text-white placeholder-slate-500 focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all resize-none font-serif"
/>
</div>
)}
{/* Submit Button */}
<button
type="submit"
disabled={loading}
className="w-full bg-gradient-to-r from-blue-600 via-blue-500 to-purple-600 hover:from-blue-700 hover:via-blue-600 hover:to-purple-700 disabled:from-slate-600 disabled:to-slate-600 text-white font-bold py-4 px-6 rounded-xl transition-all duration-200 disabled:cursor-not-allowed shadow-lg hover:shadow-2xl transform hover:scale-105 border border-blue-400/30 disabled:border-slate-400/30"
>
{loading ? (
<span className="inline-flex items-center gap-2">
<Loader2 className="w-5 h-5 animate-spin" /> Saving...
</span>
) : isEditing ? (
<span className="inline-flex items-center gap-2">
<PenSquare className="w-5 h-5" /> Update Prayer
</span>
) : (
<span className="inline-flex items-center gap-2">
<Save className="w-5 h-5" /> Save Prayer
</span>
)}
</button>
</form>
</div>
</div>
);
}