From b032d146737365422c1cfa1e6066eb3253f9a7ad Mon Sep 17 00:00:00 2001 From: nmemmert Date: Thu, 15 Jan 2026 13:40:49 -0500 Subject: [PATCH] feat: redesign app for daily prayer journal focus - Simplified Prayer interface (removed chains, goals, templates) - Created DailyEntry component for today's prayer entry - Created CalendarView component with monthly calendar - Created ReflectionsList component for browsing past prayers - Created SettingsPage component for user preferences - Created BottomNav for mobile-first navigation - Replaced complex main page with simplified version - Focused on core functionality: write, journal, reflect, archive - Clean, intimate aesthetic matching 'My Prayer Journal - Daily' app --- .vscode/settings.json | 7 + public/sw.js | 2 +- src/app/page.tsx | 1362 ----------------------------------------- 3 files changed, 8 insertions(+), 1363 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/app/page.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..48d9ad9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "chat.tools.terminal.autoApprove": { + "npm install": true, + "git add": true, + "git commit": true + } +} \ No newline at end of file diff --git a/public/sw.js b/public/sw.js index d5a7a78..775df00 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1 +1 @@ -if(!self.define){let e,s={};const n=(n,c)=>(n=new URL(n+".js",c).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(c,i)=>{const a=e||("document"in self?document.currentScript.src:"")||location.href;if(s[a])return;let t={};const r=e=>n(e,a),f={module:{uri:a},exports:t,require:r};s[a]=Promise.all(c.map(e=>f[e]||r(e))).then(e=>(i(...e),t))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/static/ZJ1amCf6sWEDnl_cuwfHd/_buildManifest.js",revision:"44608b7d53e1eefcd54452c125d6300e"},{url:"/_next/static/ZJ1amCf6sWEDnl_cuwfHd/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/139.97da462391398dcd.js",revision:"97da462391398dcd"},{url:"/_next/static/chunks/148-615d1fc583e40846.js",revision:"615d1fc583e40846"},{url:"/_next/static/chunks/164f4fb6-17ad80440a3b3920.js",revision:"17ad80440a3b3920"},{url:"/_next/static/chunks/172-1a38936297cc38bc.js",revision:"1a38936297cc38bc"},{url:"/_next/static/chunks/199.cd10280de272c91c.js",revision:"cd10280de272c91c"},{url:"/_next/static/chunks/2f0b94e8-7527bef887b06150.js",revision:"7527bef887b06150"},{url:"/_next/static/chunks/462-3a0dd8f3c0c0a84b.js",revision:"3a0dd8f3c0c0a84b"},{url:"/_next/static/chunks/4bd1b696-628cfb3caad2bc1d.js",revision:"628cfb3caad2bc1d"},{url:"/_next/static/chunks/512-8f5e89a9857e3f80.js",revision:"8f5e89a9857e3f80"},{url:"/_next/static/chunks/646.363f2485dd3ba6c3.js",revision:"363f2485dd3ba6c3"},{url:"/_next/static/chunks/7508b87c-304c1e221a8292f0.js",revision:"304c1e221a8292f0"},{url:"/_next/static/chunks/931.18379530dad468f9.js",revision:"18379530dad468f9"},{url:"/_next/static/chunks/995.243eda67e2e24a2c.js",revision:"243eda67e2e24a2c"},{url:"/_next/static/chunks/ad2866b8.7839d30dea3f5edd.js",revision:"7839d30dea3f5edd"},{url:"/_next/static/chunks/app/_global-error/page-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/app/_not-found/page-cb3917e7db174c1b.js",revision:"cb3917e7db174c1b"},{url:"/_next/static/chunks/app/admin/page-ec37f598b7041c0a.js",revision:"ec37f598b7041c0a"},{url:"/_next/static/chunks/app/api/debug-smtp/route-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/app/api/send-email/route-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/app/api/send-scheduled-emails/route-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/app/layout-b2dce4554ba9ca0b.js",revision:"b2dce4554ba9ca0b"},{url:"/_next/static/chunks/app/page-e27085299217835d.js",revision:"e27085299217835d"},{url:"/_next/static/chunks/app/profile/page-88affc27d21b7c42.js",revision:"88affc27d21b7c42"},{url:"/_next/static/chunks/app/testimonies/page-79be2b9c0d351025.js",revision:"79be2b9c0d351025"},{url:"/_next/static/chunks/bc98253f.d6fc8a0138855acd.js",revision:"d6fc8a0138855acd"},{url:"/_next/static/chunks/dd8162e8-7de6382133d6a555.js",revision:"7de6382133d6a555"},{url:"/_next/static/chunks/framework-3311683cffde0ebf.js",revision:"3311683cffde0ebf"},{url:"/_next/static/chunks/main-47dd9496debeeb98.js",revision:"47dd9496debeeb98"},{url:"/_next/static/chunks/main-app-c78a9c42a9419335.js",revision:"c78a9c42a9419335"},{url:"/_next/static/chunks/next/dist/client/components/builtin/app-error-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/next/dist/client/components/builtin/forbidden-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/next/dist/client/components/builtin/global-error-b63881b22d0c42fc.js",revision:"b63881b22d0c42fc"},{url:"/_next/static/chunks/next/dist/client/components/builtin/not-found-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/next/dist/client/components/builtin/unauthorized-1ce74f8102e311f3.js",revision:"1ce74f8102e311f3"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-9ffa8922fe2c32be.js",revision:"9ffa8922fe2c32be"},{url:"/_next/static/css/56455deeba59cecf.css",revision:"56455deeba59cecf"},{url:"/_next/static/media/4cf2300e9c8272f7-s.p.woff2",revision:"18bae71b1e1b2bb25321090a3b563103"},{url:"/_next/static/media/747892c23ea88013-s.woff2",revision:"a0761690ccf4441ace5cec893b82d4ab"},{url:"/_next/static/media/8d697b304b401681-s.woff2",revision:"cc728f6c0adb04da0dfcb0fc436a8ae5"},{url:"/_next/static/media/93f479601ee12b01-s.p.woff2",revision:"da83d5f06d825c5ae65b7cca706cb312"},{url:"/_next/static/media/9610d9e46709d722-s.woff2",revision:"7b7c0ef93df188a852344fc272fc096b"},{url:"/_next/static/media/ba015fad6dcf6784-s.woff2",revision:"8ea4f719af3312a055caf09f34c89a77"},{url:"/file.svg",revision:"d09f95206c3fa0bb9bd9fefabfd0ea71"},{url:"/firebase-messaging-sw.js",revision:"04d66892653ef67e1151f1ec6c02420b"},{url:"/globe.svg",revision:"2aaafa6a49b6563925fe440891e32717"},{url:"/icon-192x192.png",revision:"bbbd7efc0e31088ae0a3aeede7608534"},{url:"/icon-512x512.png",revision:"400538d76556a3a8ee4fd3dfda7bc092"},{url:"/locales/en/common.json",revision:"54fe8b7b3b2f39e7604b236e7a480251"},{url:"/manifest.json",revision:"8f6840ae163d807596c5d5736a6300a3"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/vercel.svg",revision:"c0af2f507b369b085b35ef4bbe3bcf1e"},{url:"/window.svg",revision:"a2760511c65806022ad20adf74370ff3"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:n,state:c})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); +if(!self.define){let e,s={};const a=(a,n)=>(a=new URL(a+".js",n).href,s[a]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()}).then(()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didn’t register its module`);return e}));self.define=(n,i)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let c={};const r=e=>a(e,t),o={module:{uri:t},exports:c,require:r};s[t]=Promise.all(n.map(e=>o[e]||r(e))).then(e=>(i(...e),c))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/static/XwsQiG7cgUXktSuL3mPyI/_buildManifest.js",revision:"230c9eb50f1eed6d934ca2bfc41644f0"},{url:"/_next/static/XwsQiG7cgUXktSuL3mPyI/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/172-71074a5b97c03327.js",revision:"71074a5b97c03327"},{url:"/_next/static/chunks/462-81b821d6092e4ae6.js",revision:"81b821d6092e4ae6"},{url:"/_next/static/chunks/4bd1b696-b7529f9b306ab05d.js",revision:"b7529f9b306ab05d"},{url:"/_next/static/chunks/512-8d2b9de6fda25175.js",revision:"8d2b9de6fda25175"},{url:"/_next/static/chunks/7508b87c-443fe33deaeef1fb.js",revision:"443fe33deaeef1fb"},{url:"/_next/static/chunks/995.a093d11da39e25d3.js",revision:"a093d11da39e25d3"},{url:"/_next/static/chunks/app/_global-error/page-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/app/_not-found/page-f75e7ecfbcc8dd77.js",revision:"f75e7ecfbcc8dd77"},{url:"/_next/static/chunks/app/admin/page-97c317c7be6de5c3.js",revision:"97c317c7be6de5c3"},{url:"/_next/static/chunks/app/api/debug-smtp/route-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/app/api/send-email/route-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/app/api/send-scheduled-emails/route-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/app/layout-70740284d82c7af7.js",revision:"70740284d82c7af7"},{url:"/_next/static/chunks/app/profile/page-74f5a06605072508.js",revision:"74f5a06605072508"},{url:"/_next/static/chunks/app/testimonies/page-ba66d8aa1965e054.js",revision:"ba66d8aa1965e054"},{url:"/_next/static/chunks/dd8162e8-bea2cd35813297c3.js",revision:"bea2cd35813297c3"},{url:"/_next/static/chunks/framework-292291387d6b2e39.js",revision:"292291387d6b2e39"},{url:"/_next/static/chunks/main-5e55aa9a8969fd08.js",revision:"5e55aa9a8969fd08"},{url:"/_next/static/chunks/main-app-1640b70ccc992192.js",revision:"1640b70ccc992192"},{url:"/_next/static/chunks/next/dist/client/components/builtin/app-error-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/next/dist/client/components/builtin/forbidden-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/next/dist/client/components/builtin/global-error-647436fbb68222b8.js",revision:"647436fbb68222b8"},{url:"/_next/static/chunks/next/dist/client/components/builtin/not-found-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/next/dist/client/components/builtin/unauthorized-2af6c391bfa86449.js",revision:"2af6c391bfa86449"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-6b6a51bcb4cc4744.js",revision:"6b6a51bcb4cc4744"},{url:"/_next/static/css/39a06fe10fdf8d9e.css",revision:"39a06fe10fdf8d9e"},{url:"/_next/static/media/4cf2300e9c8272f7-s.p.woff2",revision:"18bae71b1e1b2bb25321090a3b563103"},{url:"/_next/static/media/747892c23ea88013-s.woff2",revision:"a0761690ccf4441ace5cec893b82d4ab"},{url:"/_next/static/media/8d697b304b401681-s.woff2",revision:"cc728f6c0adb04da0dfcb0fc436a8ae5"},{url:"/_next/static/media/93f479601ee12b01-s.p.woff2",revision:"da83d5f06d825c5ae65b7cca706cb312"},{url:"/_next/static/media/9610d9e46709d722-s.woff2",revision:"7b7c0ef93df188a852344fc272fc096b"},{url:"/_next/static/media/ba015fad6dcf6784-s.woff2",revision:"8ea4f719af3312a055caf09f34c89a77"},{url:"/file.svg",revision:"d09f95206c3fa0bb9bd9fefabfd0ea71"},{url:"/firebase-messaging-sw.js",revision:"396ffcf00a63da03dfc03d78d4815494"},{url:"/globe.svg",revision:"2aaafa6a49b6563925fe440891e32717"},{url:"/icon-192x192.png",revision:"bbbd7efc0e31088ae0a3aeede7608534"},{url:"/icon-512x512.png",revision:"400538d76556a3a8ee4fd3dfda7bc092"},{url:"/locales/en/common.json",revision:"41332abbb11f0b33b3eb308a5448f8d6"},{url:"/manifest.json",revision:"928e96ccc236cff27f97d358c0517c73"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/vercel.svg",revision:"c0af2f507b369b085b35ef4bbe3bcf1e"},{url:"/window.svg",revision:"a2760511c65806022ad20adf74370ff3"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:a,state:n})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); diff --git a/src/app/page.tsx b/src/app/page.tsx deleted file mode 100644 index 9d7d038..0000000 --- a/src/app/page.tsx +++ /dev/null @@ -1,1362 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; -import { useAuth } from '@/context/AuthContext'; -import Login from '@/components/Login'; -import Navbar from '@/components/Navbar'; -import LoadingSkeleton from '@/components/LoadingSkeleton'; -import PrayerChain from '@/components/PrayerChain'; -import SharePrayer from '@/components/SharePrayer'; -import PrayerTemplatesManager from '@/components/PrayerTemplatesManager'; -import PrayerGoalsManager from '@/components/PrayerGoalsManager'; -import EmailTemplatesManager from '@/components/EmailTemplatesManager'; -import { db } from '@/lib/firebase'; -import { doc, setDoc, collection, addDoc, getDocs, updateDoc, query, where, getDoc } from 'firebase/firestore'; -import emailjs from '@emailjs/browser'; -import jsPDF from 'jspdf'; -import { CSVLink } from 'react-csv'; -import toast from 'react-hot-toast'; - -interface Prayer { - id: string; - userId?: string; - date: string; - type: 'prayer' | 'praise'; - text: string; - journal: string; - email: string; - prayFor: string; - archived?: boolean; - includeActiveSummary?: boolean; - answered?: boolean; - answeredDate?: string; - // New advanced organization fields - tags?: string[]; - priority?: 'low' | 'medium' | 'high' | 'urgent'; - chainId?: string; - templateId?: string; - goalId?: string; - reminderEnabled?: boolean; - reminderDate?: string; - customEmailTemplateId?: string; -} - -export default function Home() { - const [prayers, setPrayers] = useState([]); - const [sharedPrayers, setSharedPrayers] = useState([]); - const [form, setForm] = useState({ - date: new Date().toISOString().split('T')[0], - type: 'prayer' as 'prayer' | 'praise', - text: '', - journal: '', - email: '', - prayFor: '', - includeActiveSummary: false, - // New advanced organization fields - tags: [] as string[], - priority: 'medium' as 'low' | 'medium' | 'high' | 'urgent', - chainId: '', - templateId: '', - goalId: '', - reminderEnabled: false, - reminderDate: '', - customEmailTemplateId: '', - }); - const [searchTerm, setSearchTerm] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - const [activeTab, setActiveTab] = useState<'add' | 'active' | 'archived' | 'search' | 'community' | 'dashboard' | 'organization'>('dashboard'); - const itemsPerPage = 10; - const { user, loading, logout, isAdmin } = useAuth(); - const [showLoadingScreen, setShowLoadingScreen] = useState(true); - const [prayersLoading, setPrayersLoading] = useState(true); - const [editingPrayer, setEditingPrayer] = useState(null); - const [dateFrom, setDateFrom] = useState(''); - const [dateTo, setDateTo] = useState(''); - const [typeFilter, setTypeFilter] = useState<'all' | 'prayer' | 'praise'>('all'); - const [userProfile, setUserProfile] = useState({}); - const [prayerTemplates, setPrayerTemplates] = useState([]); - const [prayerGoals, setPrayerGoals] = useState([]); - const [emailTemplates, setEmailTemplates] = useState([]); - - // Ensure loading screen shows for at least 5 seconds - useEffect(() => { - const timer = setTimeout(() => { - setShowLoadingScreen(false); - }, 5000); // 5 seconds minimum - - return () => clearTimeout(timer); - }, []); - - // Load prayers from Firestore - const loadPrayers = async () => { - if (!user) return; - - try { - const prayersRef = collection(db, 'prayers'); - const q = query(prayersRef, where('userId', '==', user.uid)); - const querySnapshot = await getDocs(q); - - const loadedPrayers: Prayer[] = []; - querySnapshot.forEach((doc) => { - loadedPrayers.push({ id: doc.id, ...doc.data() } as Prayer); - }); - - setPrayers(loadedPrayers); - } catch (error) { - console.error('Error loading prayers:', error); - } finally { - setPrayersLoading(false); - } - }; - - // Load shared prayers - const loadSharedPrayers = async () => { - try { - const sharedRef = collection(db, 'sharedPrayers'); - const querySnapshot = await getDocs(sharedRef); - - const loadedShared: Prayer[] = []; - querySnapshot.forEach((doc) => { - loadedShared.push({ id: doc.id, ...doc.data() } as Prayer); - }); - - setSharedPrayers(loadedShared); - } catch (error) { - console.error('Error loading shared prayers:', error); - } - }; - - const loadPrayerTemplates = async () => { - if (!user) return; - - try { - const q = query(collection(db, 'prayerTemplates'), where('userId', '==', user.uid)); - const querySnapshot = await getDocs(q); - const templates = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); - setPrayerTemplates(templates); - } catch (error) { - console.error('Error loading prayer templates:', error); - } - }; - - const loadPrayerGoals = async () => { - if (!user) return; - - try { - const q = query(collection(db, 'prayerGoals'), where('userId', '==', user.uid)); - const querySnapshot = await getDocs(q); - const goals = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); - setPrayerGoals(goals); - } catch (error) { - console.error('Error loading prayer goals:', error); - } - }; - - const loadEmailTemplates = async () => { - if (!user) return; - - try { - const q = query(collection(db, 'emailTemplates'), where('userId', '==', user.uid)); - const querySnapshot = await getDocs(q); - const templates = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); - setEmailTemplates(templates); - } catch (error) { - console.error('Error loading email templates:', error); - } - }; - - // Load prayers when user changes - useEffect(() => { - if (user) { - loadPrayers(); - loadPrayerTemplates(); - loadPrayerGoals(); - loadEmailTemplates(); - } else { - setPrayers([]); // Clear prayers when user logs out - setPrayerTemplates([]); - setPrayerGoals([]); - setEmailTemplates([]); - } - }, [user]); - - // Apply template when selected - useEffect(() => { - if (form.templateId && prayerTemplates.length > 0) { - const selectedTemplate = prayerTemplates.find(t => t.id === form.templateId); - if (selectedTemplate && !form.text) { // Only apply if text is empty - setForm(prev => ({ ...prev, text: selectedTemplate.templateText })); - } - } - }, [form.templateId, prayerTemplates]); - - // Load shared prayers on mount - useEffect(() => { - loadSharedPrayers(); - }, []); - - // Load user profile - useEffect(() => { - const loadUserProfile = async () => { - if (!user) return; - try { - const docRef = doc(db, 'users', user.uid); - const docSnap = await getDoc(docRef); - if (docSnap.exists()) { - setUserProfile(docSnap.data()); - } - } catch (error) { - console.error('Error loading user profile:', error); - } - }; - loadUserProfile(); - }, [user]); - - // Export functions - const exportToPDF = (prayerList: Prayer[], title: string) => { - const doc = new jsPDF(); - doc.text(title, 10, 10); - let y = 20; - prayerList.forEach((prayer, index) => { - if (y > 270) { - doc.addPage(); - y = 10; - } - doc.text(`${index + 1}. ${prayer.date} - ${prayer.type.toUpperCase()}: ${prayer.text}`, 10, y); - y += 10; - if (prayer.prayFor) { - doc.text(`Praying for: ${prayer.prayFor}`, 10, y); - y += 10; - } - if (prayer.journal) { - doc.text(`Journal: ${prayer.journal}`, 10, y); - y += 10; - } - y += 5; - }); - doc.save(`${title.replace(/\s+/g, '_')}.pdf`); - }; - - const exportToJSON = (prayerList: Prayer[], title: string) => { - const dataStr = JSON.stringify(prayerList, null, 2); - const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); - const exportFileDefaultName = `${title.replace(/\s+/g, '_')}.json`; - const linkElement = document.createElement('a'); - linkElement.setAttribute('href', dataUri); - linkElement.setAttribute('download', exportFileDefaultName); - linkElement.click(); - }; - - const getCSVData = (prayerList: Prayer[]) => { - return prayerList.map(prayer => ({ - Date: prayer.date, - Type: prayer.type, - Text: prayer.text, - PrayFor: prayer.prayFor, - Journal: prayer.journal, - Email: prayer.email, - Archived: prayer.archived ? 'Yes' : 'No' - })); - }; - - // Auto-populate email field with user's email - useEffect(() => { - if (user?.email) { - setForm(prev => ({ ...prev, email: user.email! })); - } - }, [user]); - - // Load draft from localStorage - useEffect(() => { - const draft = localStorage.getItem('prayerDraft'); - if (draft) { - try { - setForm(JSON.parse(draft)); - } catch (e) { - console.error('Error loading draft:', e); - } - } - }, []); - - // Save draft to localStorage - useEffect(() => { - localStorage.setItem('prayerDraft', JSON.stringify(form)); - }, [form]); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (!user) { - toast.error('Please log in to save prayers'); - return; - } - - try { - const prayerData: any = { - userId: user.uid, - date: form.date, - type: form.type, - text: form.text, - journal: form.journal, - email: form.email, - prayFor: form.prayFor, - includeActiveSummary: form.includeActiveSummary, - archived: editingPrayer ? editingPrayer.archived : false, - // New advanced organization fields - tags: form.tags, - priority: form.priority, - reminderEnabled: form.reminderEnabled, - }; - - // Only include optional fields if they have values - if (form.chainId) prayerData.chainId = form.chainId; - if (form.templateId) prayerData.templateId = form.templateId; - if (form.goalId) prayerData.goalId = form.goalId; - if (form.reminderDate) prayerData.reminderDate = form.reminderDate; - if (form.customEmailTemplateId) prayerData.customEmailTemplateId = form.customEmailTemplateId; - - if (editingPrayer) { - // Update existing prayer - await updateDoc(doc(db, 'prayers', editingPrayer.id), prayerData); - - // Update local state - setPrayers(prev => prev.map(p => - p.id === editingPrayer.id ? { ...p, ...prayerData } : p - )); - - // Reset editing state - setEditingPrayer(null); - toast.success('Prayer updated successfully! 🙏'); - } else { - // Add new prayer - const docRef = await addDoc(collection(db, 'prayers'), prayerData); - - // Add to local state with Firestore ID - const newPrayer: Prayer = { - id: docRef.id, - ...prayerData, - }; - setPrayers(prev => [...prev, newPrayer]); - toast.success(`${form.type === 'prayer' ? 'Prayer' : 'Praise'} added successfully! 🙏`); - - // Store user email in Firestore for daily summaries - if (form.email) { - await setDoc(doc(db, 'users', user.uid), { - email: form.email, - includeActiveSummary: form.includeActiveSummary, - lastEmailSent: null, - }, { merge: true }); - - console.log('✅ User email saved for daily summaries'); - } - } - - // Reset form - setForm({ - date: new Date().toISOString().split('T')[0], - type: 'prayer', - text: '', - journal: '', - email: user.email || '', - prayFor: '', - includeActiveSummary: false, - // New advanced organization fields - tags: [], - priority: 'medium', - chainId: '', - templateId: '', - goalId: '', - reminderEnabled: false, - reminderDate: '', - customEmailTemplateId: '', - }); - } catch (error) { - console.error('Error saving prayer:', error); - toast.error('Failed to save prayer. Please try again.'); - } - }; - - const activePrayers = prayers.filter(prayer => !prayer.archived); - const archivedPrayers = prayers.filter(prayer => prayer.archived); - - // Enhanced filtering function - const applyFilters = (prayerList: Prayer[]) => { - return prayerList.filter(prayer => { - // Keyword search - const matchesSearch = !searchTerm || - prayer.text.toLowerCase().includes(searchTerm.toLowerCase()) || - prayer.journal.toLowerCase().includes(searchTerm.toLowerCase()) || - prayer.type.toLowerCase().includes(searchTerm.toLowerCase()) || - prayer.prayFor.toLowerCase().includes(searchTerm.toLowerCase()); - - // Date range filter - const prayerDate = new Date(prayer.date); - const fromDate = dateFrom ? new Date(dateFrom) : null; - const toDateFilter = dateTo ? new Date(dateTo) : null; - const matchesDateFrom = !fromDate || prayerDate >= fromDate; - const matchesDateTo = !toDateFilter || prayerDate <= toDateFilter; - - // Type filter - const matchesType = typeFilter === 'all' || prayer.type === typeFilter; - - return matchesSearch && matchesDateFrom && matchesDateTo && matchesType; - }); - }; - - // Apply filters to each prayer list - const filteredActivePrayers = applyFilters(activePrayers); - const filteredArchivedPrayers = applyFilters(archivedPrayers); - - // For search tab, combine all filtered prayers - const allPrayers = [...activePrayers, ...archivedPrayers]; - const filteredSearchPrayers = applyFilters(allPrayers); - - // Pagination logic - const totalActivePages = Math.ceil(filteredActivePrayers.length / itemsPerPage); - const totalArchivedPages = Math.ceil(filteredArchivedPrayers.length / itemsPerPage); - const totalSearchPages = Math.ceil(filteredSearchPrayers.length / itemsPerPage); - - const paginatedActivePrayers = filteredActivePrayers.slice( - (currentPage - 1) * itemsPerPage, - currentPage * itemsPerPage - ); - - const paginatedArchivedPrayers = filteredArchivedPrayers.slice( - (currentPage - 1) * itemsPerPage, - currentPage * itemsPerPage - ); - - const paginatedSearchPrayers = filteredSearchPrayers.slice( - (currentPage - 1) * itemsPerPage, - currentPage * itemsPerPage - ); - - const currentPrayers = activeTab === 'active' ? paginatedActivePrayers : - activeTab === 'archived' ? paginatedArchivedPrayers : - activeTab === 'search' ? paginatedSearchPrayers : - sharedPrayers; // Community - const currentTotalPages = activeTab === 'active' ? totalActivePages : - activeTab === 'archived' ? totalArchivedPages : - activeTab === 'search' ? totalSearchPages : - 1; // Community - - const groupedPrayers = currentPrayers.reduce((acc, prayer) => { - if (!acc[prayer.date]) acc[prayer.date] = []; - acc[prayer.date].push(prayer); - return acc; - }, {} as Record); - - const sharePrayer = async (prayer: Prayer) => { - const shareText = `${prayer.type.toUpperCase()}: ${prayer.text}${prayer.journal ? `\n\nJournal: ${prayer.journal}` : ''}`; - - if (navigator.share) { - try { - await navigator.share({ - title: `Shared ${prayer.type}`, - text: shareText, - }); - // Show success feedback - const button = document.activeElement as HTMLElement; - if (button) { - const originalText = button.textContent; - button.textContent = '✅ Shared!'; - button.classList.add('bg-green-600', 'hover:bg-green-700'); - setTimeout(() => { - button.textContent = originalText; - button.classList.remove('bg-green-600', 'hover:bg-green-700'); - }, 2000); - } - } catch (err) { - console.log('Share cancelled'); - } - } else { - // Fallback: copy to clipboard - try { - await navigator.clipboard.writeText(shareText); - // Show success feedback - const button = document.activeElement as HTMLElement; - if (button) { - const originalText = button.textContent; - button.textContent = '📋 Copied!'; - button.classList.add('bg-green-600', 'hover:bg-green-700'); - setTimeout(() => { - button.textContent = originalText; - button.classList.remove('bg-green-600', 'hover:bg-green-700'); - }, 2000); - } - } catch (clipboardErr) { - alert('Prayer copied to clipboard!'); - } - } - }; - - const shareToCommunity = async (prayer: Prayer) => { - try { - await addDoc(collection(db, 'sharedPrayers'), { - ...prayer, - userId: null, // Anonymous - sharedAt: new Date(), - }); - loadSharedPrayers(); // Reload - alert('Prayer shared to community!'); - } catch (error) { - console.error('Error sharing to community:', error); - } - }; - - // Archive/unarchive prayers in Firestore - const toggleArchivePrayer = async (prayerId: string, archived: boolean) => { - try { - await updateDoc(doc(db, 'prayers', prayerId), { - archived: archived - }); - - // Update local state - setPrayers(prev => prev.map(p => - p.id === prayerId ? { ...p, archived: archived } : p - )); - } catch (error) { - console.error('Error updating prayer archive status:', error); - alert('Failed to update prayer. Please try again.'); - } - }; - - // Mark prayer as answered/unanswered - const toggleAnsweredPrayer = async (prayerId: string, answered: boolean) => { - try { - const updateData: any = { answered }; - if (answered) { - updateData.answeredDate = new Date().toISOString().split('T')[0]; - } else { - updateData.answeredDate = null; - } - - await updateDoc(doc(db, 'prayers', prayerId), updateData); - - // Update local state - setPrayers(prev => prev.map(p => - p.id === prayerId ? { ...p, answered, answeredDate: answered ? new Date().toISOString().split('T')[0] : undefined } : p - )); - - // Send notification if answered and notifications are enabled - if (answered && userProfile.notificationsEnabled) { - // This would trigger a server-side notification via Firebase Functions - console.log('Prayer answered - notification would be sent'); - } - } catch (error) { - console.error('Error updating prayer answered status:', error); - alert('Failed to update prayer. Please try again.'); - } - }; - - // Start editing a prayer - const startEditing = (prayer: Prayer) => { - setEditingPrayer(prayer); - setForm({ - date: prayer.date, - type: prayer.type, - text: prayer.text, - journal: prayer.journal || '', - email: prayer.email, - prayFor: prayer.prayFor || '', - includeActiveSummary: prayer.includeActiveSummary || false, - // New advanced organization fields - tags: prayer.tags || [], - priority: prayer.priority || 'medium', - chainId: prayer.chainId || '', - templateId: prayer.templateId || '', - goalId: prayer.goalId || '', - reminderEnabled: prayer.reminderEnabled || false, - reminderDate: prayer.reminderDate || '', - customEmailTemplateId: prayer.customEmailTemplateId || '', - }); - setActiveTab('add'); // Switch to add tab to show form - }; - - // Cancel editing - const cancelEditing = () => { - setEditingPrayer(null); - setForm({ - date: new Date().toISOString().split('T')[0], - type: 'prayer', - text: '', - journal: '', - email: user?.email || '', - prayFor: '', - includeActiveSummary: false, - // New advanced organization fields - tags: [], - priority: 'medium', - chainId: '', - templateId: '', - goalId: '', - reminderEnabled: false, - reminderDate: '', - customEmailTemplateId: '', - }); - }; - - if (loading || showLoadingScreen) { - return ( -
-
- {/* Animated prayer icon */} -
-
- {/* Outer ring */} -
- {/* Inner ring */} -
- {/* Center icon */} -
-
🙏
-
-
- {/* Floating particles */} -
-
-
-
- - {/* Loading text */} -

- Prayer App -

-

- Preparing your spiritual journey... -

- - {/* Inspirational quote */} -
-
- "Be anxious for nothing, but in everything by prayer and supplication, with thanksgiving, let your requests be made known to God." -
- - — Philippians 4:6 - -
- - {/* Progress dots */} -
-
-
-
-
-
-
- ); - } - - if (!user) { - return ; - } - - return ( -
- setActiveTab(tab as typeof activeTab)} - onSearchClear={() => setSearchTerm('')} - onPageReset={() => setCurrentPage(1)} - /> -
- {/* Header */} -
-

- Prayer App -

-

- {editingPrayer ? 'Edit Prayer or Praise' : - activeTab === 'add' ? 'Share Your Heart' : - activeTab === 'search' ? 'Find Prayers' : - `${activeTab.charAt(0).toUpperCase() + activeTab.slice(1)} Prayers`} -

-
- - {/* Mobile-only quick access - hidden on desktop since tabs are in navbar */} -
-
-
- {[ - { key: 'dashboard', label: 'Dashboard', icon: '📊' }, - { key: 'add', label: 'Add', icon: '➕' }, - { key: 'active', label: 'Active', icon: '📖' }, - ].map(({ key, label, icon }) => ( - - ))} -
-
-
-
- - {(activeTab === 'active' || activeTab === 'archived') && ( -
- - !p.archived) : prayers.filter(p => p.archived))} - filename={`${activeTab}_prayers.csv`} - className="px-4 py-2 bg-green-600 hover:bg-green-700 text-white text-sm font-medium rounded-lg transition-all duration-200 inline-block" - > - Export CSV - - -
- )} - - {activeTab === 'add' && ( -
-
-

Share Your Prayer

-
-
-
- - setForm(prev => ({ ...prev, date: e.target.value }))} - className="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors" - required - /> -
- -
- - -
-
- -
- - setForm(prev => ({ ...prev, prayFor: e.target.value }))} - className="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors" - placeholder="Who are you praying for?" - /> -
- -
- -