import React, { useState, useMemo } from 'react'; import { usePrayers } from './usePrayers'; import { useSettings } from './useSettings'; import { Prayer, PrayedEntry, AppSettings } from './types'; import './App.css'; type Filter = 'active' | 'answered' | 'all'; type Tab = 'prayers' | 'calendar'; const DOT_PALETTE = [ '#c07a3a', '#3d6b52', '#6b4545', '#456b8a', '#7a5c8a', '#8a7a3a', '#3a6b6b', '#8a4a6b', ]; function prayerColor(id: string): string { let hash = 0; for (let i = 0; i < id.length; i++) hash = (hash * 31 + id.charCodeAt(i)) & 0xffffffff; return DOT_PALETTE[Math.abs(hash) % DOT_PALETTE.length]; } function normalize(s: string) { return s.toLowerCase().trim(); } function matches(prayer: Prayer, query: string) { const q = normalize(query); if (!q) return true; return ( normalize(prayer.person).includes(q) || normalize(prayer.request).includes(q) || normalize(prayer.answer).includes(q) || prayer.tags.some(t => normalize(t).includes(q)) ); } const DAYS = ['sun','mon','tue','wed','thu','fri','sat'] as const; const DAY_LABELS: Record = { sun:'Su', mon:'Mo', tue:'Tu', wed:'We', thu:'Th', fri:'Fr', sat:'Sa' }; export default function App() { const { prayers, loading, error: apiError, addPrayer, updateAnswer, markAnswered, markActive, archivePrayer, deletePrayer, updateRequest, updateNotes, logPrayed, updateReminderDays, } = usePrayers(); const { settings, saveSettings, testNotification } = useSettings(); const [showSettings, setShowSettings] = useState(false); const [tab, setTab] = useState('prayers'); const [filter, setFilter] = useState('active'); const [activeTag, setActiveTag] = useState(null); const [search, setSearch] = useState(''); const [newPerson, setNewPerson] = useState(''); const [newRequest, setNewRequest] = useState(''); const [newTags, setNewTags] = useState(''); const [expandedId, setExpandedId] = useState(null); const [editingAnswer, setEditingAnswer] = useState<{ id: string; text: string } | null>(null); const [writeError, setWriteError] = useState(null); const allTags = useMemo(() => { const set = new Set(); prayers.filter(p => p.status !== 'archived').forEach(p => p.tags.forEach(t => set.add(t))); return Array.from(set).sort(); }, [prayers]); const filtered = useMemo(() => prayers.filter(p => { const statusMatch = filter === 'active' ? p.status === 'active' : filter === 'answered' ? p.status === 'answered' : p.status !== 'archived'; const tagMatch = activeTag ? p.tags.includes(activeTag) : true; const searchMatch = matches(p, search); return statusMatch && tagMatch && searchMatch; }), [prayers, filter, activeTag, search]); const counts = { active: prayers.filter(p => p.status === 'active').length, answered: prayers.filter(p => p.status === 'answered').length, }; async function handleAdd(e: React.FormEvent) { e.preventDefault(); if (!newRequest.trim()) return; const tags = newTags.split(',').map(t => t.trim()).filter(Boolean); try { await addPrayer(newPerson.trim(), newRequest.trim(), tags); setNewPerson(''); setNewRequest(''); setNewTags(''); setWriteError(null); } catch (err: any) { setWriteError(err.message); } } async function handleSaveAnswer(id: string) { if (!editingAnswer) return; try { await updateAnswer(id, editingAnswer.text); await markAnswered(id); setEditingAnswer(null); setExpandedId(null); setWriteError(null); } catch (err: any) { setWriteError(err.message); } } function formatDate(iso: string) { return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric', }); } function handleTagClick(tag: string) { setActiveTag(prev => prev === tag ? null : tag); } function handleExport() { const params = new URLSearchParams(); params.set('status', filter); if (activeTag) params.set('tag', activeTag); window.location.href = `/api/export?${params.toString()}`; } const emptyMessage = search ? `No results for "${search}".` : activeTag ? `No ${filter === 'all' ? '' : filter + ' '}prayers tagged "${activeTag}".` : filter === 'active' ? 'No active prayers. Add one above.' : 'Nothing here yet.'; return (

Prayer Journal

Bring your requests. Record His faithfulness.

{showSettings && ( )}
{tab === 'prayers' && ( <>
setNewPerson(e.target.value)} />