feat: add answered prayers tab with answer details tracking
This commit is contained in:
1 parent
159a7463cd
commit
12377187c8
4 files changed
+133
-4
No files matched your search
+19
-2
@@ -10,6 +10,7 @@ import CalendarView from '@/components/CalendarView';
|
|||||||
import ReflectionsList from '@/components/ReflectionsList';
|
import ReflectionsList from '@/components/ReflectionsList';
|
||||||
import SettingsPage from '@/components/SettingsPage';
|
import SettingsPage from '@/components/SettingsPage';
|
||||||
import HomePage from '@/components/HomePage';
|
import HomePage from '@/components/HomePage';
|
||||||
|
import AnsweredPrayers from '@/components/AnsweredPrayers';
|
||||||
import { db } from '@/lib/firebase';
|
import { db } from '@/lib/firebase';
|
||||||
import { collection, addDoc, getDocs, updateDoc, query, where, doc } from 'firebase/firestore';
|
import { collection, addDoc, getDocs, updateDoc, query, where, doc } from 'firebase/firestore';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
@@ -20,10 +21,12 @@ interface Prayer {
|
|||||||
date: string;
|
date: string;
|
||||||
text: string;
|
text: string;
|
||||||
prayFor?: string;
|
prayFor?: string;
|
||||||
|
answered?: boolean;
|
||||||
|
answerDetails?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [activeTab, setActiveTab] = useState<'home' | 'compose' | 'journal' | 'reflections' | 'settings'>('home');
|
const [activeTab, setActiveTab] = useState<'home' | 'compose' | 'journal' | 'answered' | 'reflections' | 'settings'>('home');
|
||||||
const [prayers, setPrayers] = useState<Prayer[]>([]);
|
const [prayers, setPrayers] = useState<Prayer[]>([]);
|
||||||
const [editingPrayer, setEditingPrayer] = useState<Prayer | null>(null);
|
const [editingPrayer, setEditingPrayer] = useState<Prayer | null>(null);
|
||||||
|
|
||||||
@@ -31,6 +34,8 @@ export default function Home() {
|
|||||||
const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
|
const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
|
||||||
const [text, setText] = useState('');
|
const [text, setText] = useState('');
|
||||||
const [prayFor, setPrayFor] = useState('');
|
const [prayFor, setPrayFor] = useState('');
|
||||||
|
const [answered, setAnswered] = useState(false);
|
||||||
|
const [answerDetails, setAnswerDetails] = useState('');
|
||||||
|
|
||||||
const { user, loading: authLoading } = useAuth();
|
const { user, loading: authLoading } = useAuth();
|
||||||
|
|
||||||
@@ -98,6 +103,8 @@ export default function Home() {
|
|||||||
date,
|
date,
|
||||||
text,
|
text,
|
||||||
prayFor: prayFor || undefined,
|
prayFor: prayFor || undefined,
|
||||||
|
answered,
|
||||||
|
answerDetails: answerDetails || undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (editingPrayer) {
|
if (editingPrayer) {
|
||||||
@@ -132,7 +139,9 @@ export default function Home() {
|
|||||||
setDate(prayer.date);
|
setDate(prayer.date);
|
||||||
setText(prayer.text);
|
setText(prayer.text);
|
||||||
setPrayFor(prayer.prayFor || '');
|
setPrayFor(prayer.prayFor || '');
|
||||||
setActiveTab('home');
|
setAnswered(prayer.answered || false);
|
||||||
|
setAnswerDetails(prayer.answerDetails || '');
|
||||||
|
setActiveTab('compose');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDateClick = (clickedDate: string) => {
|
const handleDateClick = (clickedDate: string) => {
|
||||||
@@ -206,6 +215,10 @@ export default function Home() {
|
|||||||
onTextChange={setText}
|
onTextChange={setText}
|
||||||
prayFor={prayFor}
|
prayFor={prayFor}
|
||||||
onPrayForChange={setPrayFor}
|
onPrayForChange={setPrayFor}
|
||||||
|
answered={answered}
|
||||||
|
onAnsweredChange={setAnswered}
|
||||||
|
answerDetails={answerDetails}
|
||||||
|
onAnswerDetailsChange={setAnswerDetails}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
isEditing={!!editingPrayer}
|
isEditing={!!editingPrayer}
|
||||||
/>
|
/>
|
||||||
@@ -215,6 +228,10 @@ export default function Home() {
|
|||||||
<CalendarView prayers={prayers} onDayClick={handleDateClick} />
|
<CalendarView prayers={prayers} onDayClick={handleDateClick} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{activeTab === 'answered' && (
|
||||||
|
<AnsweredPrayers prayers={prayers} onEdit={handleSelectPrayer} />
|
||||||
|
)}
|
||||||
|
|
||||||
{activeTab === 'reflections' && (
|
{activeTab === 'reflections' && (
|
||||||
<ReflectionsList
|
<ReflectionsList
|
||||||
prayers={prayers}
|
prayers={prayers}
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
interface Prayer {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
date: string;
|
||||||
|
text: string;
|
||||||
|
prayFor?: string;
|
||||||
|
answered?: boolean;
|
||||||
|
answerDetails?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AnsweredPrayersProps {
|
||||||
|
prayers: Prayer[];
|
||||||
|
onEdit?: (prayer: Prayer) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AnsweredPrayers({ prayers, onEdit }: AnsweredPrayersProps) {
|
||||||
|
const answeredPrayers = prayers.filter(p => p.answered);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 p-4 pb-32">
|
||||||
|
<div className="max-w-md mx-auto">
|
||||||
|
<h1 className="text-3xl font-bold text-slate-900 dark:text-white mb-2 mt-6">Answered Prayers</h1>
|
||||||
|
<p className="text-slate-600 dark:text-slate-400 mb-6">Celebrate prayers God has answered</p>
|
||||||
|
|
||||||
|
{answeredPrayers.length > 0 ? (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{answeredPrayers.map((prayer) => (
|
||||||
|
<div
|
||||||
|
key={prayer.id}
|
||||||
|
className="bg-white dark:bg-slate-800 rounded-lg p-4 shadow-sm border-l-4 border-green-500 hover:shadow-md transition-shadow cursor-pointer"
|
||||||
|
onClick={() => onEdit?.(prayer)}
|
||||||
|
>
|
||||||
|
<div className="flex items-start gap-2 mb-2">
|
||||||
|
<span className="text-2xl">✅</span>
|
||||||
|
<div className="flex-1">
|
||||||
|
{prayer.prayFor && (
|
||||||
|
<div className="text-xs font-semibold text-green-600 dark:text-green-400 mb-1 uppercase tracking-wide">
|
||||||
|
{prayer.prayFor}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="text-sm text-slate-700 dark:text-slate-300 line-clamp-2 font-medium">{prayer.text}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{prayer.answerDetails && (
|
||||||
|
<div className="mt-3 pt-3 border-t border-slate-200 dark:border-slate-700">
|
||||||
|
<p className="text-xs font-semibold text-slate-600 dark:text-slate-400 mb-1">How it was answered:</p>
|
||||||
|
<p className="text-sm text-slate-600 dark:text-slate-300 italic line-clamp-3">{prayer.answerDetails}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="text-xs text-slate-500 dark:text-slate-500 mt-2">
|
||||||
|
{new Date(prayer.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<div className="text-4xl mb-3">🙏</div>
|
||||||
|
<p className="text-slate-600 dark:text-slate-400 mb-2">No answered prayers yet</p>
|
||||||
|
<p className="text-sm text-slate-500 dark:text-slate-500">
|
||||||
|
Mark prayers as answered in the Compose tab to see them here
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,8 +4,8 @@ import React from 'react';
|
|||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
|
||||||
interface BottomNavProps {
|
interface BottomNavProps {
|
||||||
activeTab: 'home' | 'compose' | 'journal' | 'reflections' | 'settings';
|
activeTab: 'home' | 'compose' | 'journal' | 'answered' | 'reflections' | 'settings';
|
||||||
onTabChange: (tab: 'home' | 'compose' | 'journal' | 'reflections' | 'settings') => void;
|
onTabChange: (tab: 'home' | 'compose' | 'journal' | 'answered' | 'reflections' | 'settings') => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BottomNav({ activeTab, onTabChange }: BottomNavProps) {
|
export default function BottomNav({ activeTab, onTabChange }: BottomNavProps) {
|
||||||
@@ -15,6 +15,7 @@ export default function BottomNav({ activeTab, onTabChange }: BottomNavProps) {
|
|||||||
{ id: 'home' as const, label: 'Home', icon: '🏠' },
|
{ id: 'home' as const, label: 'Home', icon: '🏠' },
|
||||||
{ id: 'compose' as const, label: 'Compose', icon: '✍️' },
|
{ id: 'compose' as const, label: 'Compose', icon: '✍️' },
|
||||||
{ id: 'journal' as const, label: 'Journal', icon: '📅' },
|
{ id: 'journal' as const, label: 'Journal', icon: '📅' },
|
||||||
|
{ id: 'answered' as const, label: 'Answered', icon: '✅' },
|
||||||
{ id: 'reflections' as const, label: 'Reflections', icon: '📖' },
|
{ id: 'reflections' as const, label: 'Reflections', icon: '📖' },
|
||||||
{ id: 'settings' as const, label: 'Settings', icon: '⚙️' },
|
{ id: 'settings' as const, label: 'Settings', icon: '⚙️' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ interface DailyEntryProps {
|
|||||||
onTextChange: (text: string) => void;
|
onTextChange: (text: string) => void;
|
||||||
prayFor: string;
|
prayFor: string;
|
||||||
onPrayForChange: (prayFor: string) => void;
|
onPrayForChange: (prayFor: string) => void;
|
||||||
|
answered?: boolean;
|
||||||
|
onAnsweredChange?: (answered: boolean) => void;
|
||||||
|
answerDetails?: string;
|
||||||
|
onAnswerDetailsChange?: (details: string) => void;
|
||||||
onSubmit: (e: React.FormEvent) => void;
|
onSubmit: (e: React.FormEvent) => void;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
isEditing?: boolean;
|
isEditing?: boolean;
|
||||||
@@ -22,6 +26,10 @@ export default function DailyEntry({
|
|||||||
onTextChange,
|
onTextChange,
|
||||||
prayFor,
|
prayFor,
|
||||||
onPrayForChange,
|
onPrayForChange,
|
||||||
|
answered = false,
|
||||||
|
onAnsweredChange,
|
||||||
|
answerDetails = '',
|
||||||
|
onAnswerDetailsChange,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
loading = false,
|
loading = false,
|
||||||
isEditing = false,
|
isEditing = false,
|
||||||
@@ -91,6 +99,37 @@ export default function DailyEntry({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Answered Checkbox */}
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="answered"
|
||||||
|
checked={answered}
|
||||||
|
onChange={(e) => onAnsweredChange?.(e.target.checked)}
|
||||||
|
className="w-5 h-5 rounded border-slate-300 text-blue-600 focus:ring-blue-500 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<label htmlFor="answered" className="font-semibold text-slate-700 dark:text-slate-300 cursor-pointer">
|
||||||
|
Mark as answered
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Answer Details */}
|
||||||
|
{answered && (
|
||||||
|
<div>
|
||||||
|
<label htmlFor="answerDetails" className="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-2">
|
||||||
|
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-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder-slate-400 dark:placeholder-slate-500 focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all resize-none font-serif"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
Reference in new issue
Block a user