feat: Add prayer chains, social sharing, and testimony wall
- Add PrayerChain component for visualizing linked prayers - Add SharePrayer component with social media integration (Twitter, Facebook, WhatsApp, copy link) - Create /testimonies page for public answered prayer stories - Update Firestore rules for public testimony access - Enhance Organization tab with prayer chains - Add Testimonies navigation tab - Fix Firebase Admin SDK integration for server-side operations - Improve email system with proper server-side authentication - Update prayer form to handle optional fields correctly - Deploy updated Firestore security rules
This commit is contained in:
1 parent
7214fdb89a
commit
d9784738e4
32 files changed
+3146
-3236
No files matched your search
+92
-83
@@ -31,7 +31,7 @@ export default function Profile() {
|
||||
const docSnap = await getDoc(docRef);
|
||||
if (docSnap.exists()) {
|
||||
setProfile(docSnap.data() as UserProfile);
|
||||
setNotificationsEnabled(docSnap.data().notificationsEnabled);
|
||||
setNotificationsEnabled(docSnap.data().notificationsEnabled ?? false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading profile:', error);
|
||||
@@ -44,13 +44,6 @@ export default function Profile() {
|
||||
}
|
||||
}, [user, loadProfile]);
|
||||
|
||||
// Load notification settings
|
||||
useEffect(() => {
|
||||
if (profile.notificationsEnabled !== undefined) {
|
||||
setNotificationsEnabled(profile.notificationsEnabled);
|
||||
}
|
||||
}, [profile.notificationsEnabled]);
|
||||
|
||||
// Listen for FCM messages
|
||||
useEffect(() => {
|
||||
if (notificationsEnabled) {
|
||||
@@ -146,88 +139,104 @@ export default function Profile() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto mb-8 p-6 bg-gray-800 rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-6">Profile</h2>
|
||||
|
||||
{/* Avatar */}
|
||||
<div className="mb-6">
|
||||
<label className="block mb-2 text-lg">Avatar</label>
|
||||
{profile.avatarUrl && (
|
||||
<Image src={profile.avatarUrl} alt="Avatar" width={80} height={80} className="w-20 h-20 rounded-full mb-2" />
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleAvatarUpload}
|
||||
disabled={uploading}
|
||||
className="w-full p-3 bg-gray-700 border border-gray-600 rounded"
|
||||
/>
|
||||
{uploading && <p className="text-sm text-gray-400 mt-1">Uploading...</p>}
|
||||
<div className="max-w-2xl mx-auto p-6 space-y-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl font-bold text-white mb-2">Profile Settings</h1>
|
||||
<p className="text-gray-400">Manage your account and preferences</p>
|
||||
</div>
|
||||
|
||||
{/* Theme */}
|
||||
<div className="mb-6">
|
||||
<label className="block mb-2 text-lg">Theme</label>
|
||||
<select
|
||||
value={theme}
|
||||
onChange={(e) => handleThemeChange(e.target.value)}
|
||||
className="w-full p-3 bg-gray-700 border border-gray-600 rounded"
|
||||
>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="system">System</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Notifications */}
|
||||
<div className="mb-6">
|
||||
<label className="block mb-2 text-lg">Notifications</label>
|
||||
<div className="flex items-center mb-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationsEnabled}
|
||||
onChange={(e) => handleNotificationToggle(e.target.checked)}
|
||||
className="w-6 h-6 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
||||
/>
|
||||
<label className="ml-3 text-base font-medium">
|
||||
Enable push notifications for prayer reminders
|
||||
</label>
|
||||
{/* Avatar Section */}
|
||||
<div className="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<h2 className="text-xl font-semibold mb-4 text-white">Profile Picture</h2>
|
||||
<div className="flex items-center space-x-4">
|
||||
{profile.avatarUrl ? (
|
||||
<Image src={profile.avatarUrl} alt="Avatar" width={80} height={80} className="w-20 h-20 rounded-full border-2 border-gray-600" />
|
||||
) : (
|
||||
<div className="w-20 h-20 rounded-full bg-gray-600 flex items-center justify-center text-gray-400">No Image</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleAvatarUpload}
|
||||
disabled={uploading}
|
||||
className="block w-full text-sm text-gray-300 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-600 file:text-white hover:file:bg-blue-700 disabled:opacity-50"
|
||||
/>
|
||||
{uploading && <p className="text-sm text-blue-400 mt-2">Uploading...</p>}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-gray-400">
|
||||
Get notified about your prayer schedule and when prayers are answered.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Categories */}
|
||||
<div className="mb-6">
|
||||
<label className="block mb-2 text-lg">Prayer Categories</label>
|
||||
<div className="flex gap-2 mb-2">
|
||||
<input
|
||||
type="text"
|
||||
value={newCategory}
|
||||
onChange={(e) => setNewCategory(e.target.value)}
|
||||
placeholder="Add category"
|
||||
className="flex-1 p-3 bg-gray-700 border border-gray-600 rounded"
|
||||
/>
|
||||
<button
|
||||
onClick={addCategory}
|
||||
className="px-4 py-3 bg-green-600 hover:bg-green-700 rounded"
|
||||
{/* Theme Section */}
|
||||
<div className="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<h2 className="text-xl font-semibold mb-4 text-white">Appearance</h2>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">Theme</label>
|
||||
<select
|
||||
value={theme}
|
||||
onChange={(e) => handleThemeChange(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"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="system">System</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(profile.categories || []).map(category => (
|
||||
<span key={category} className="bg-gray-700 px-3 py-1 rounded flex items-center gap-2">
|
||||
{category}
|
||||
<button
|
||||
onClick={() => removeCategory(category)}
|
||||
className="text-red-400 hover:text-red-300"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Notifications Section */}
|
||||
<div className="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<h2 className="text-xl font-semibold mb-4 text-white">Notifications</h2>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationsEnabled}
|
||||
onChange={(e) => handleNotificationToggle(e.target.checked)}
|
||||
className="w-5 h-5 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2"
|
||||
/>
|
||||
<label className="ml-3 text-sm font-medium text-gray-300">
|
||||
Enable push notifications for prayer reminders
|
||||
</label>
|
||||
</div>
|
||||
<p className="text-sm text-gray-400">
|
||||
Get notified about your prayer schedule and when prayers are answered.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Categories Section */}
|
||||
<div className="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<h2 className="text-xl font-semibold mb-4 text-white">Prayer Categories</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="flex space-x-2">
|
||||
<input
|
||||
type="text"
|
||||
value={newCategory}
|
||||
onChange={(e) => setNewCategory(e.target.value)}
|
||||
placeholder="Add a new category"
|
||||
className="flex-1 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"
|
||||
/>
|
||||
<button
|
||||
onClick={addCategory}
|
||||
className="px-6 py-3 bg-green-600 hover:bg-green-700 text-white rounded-lg font-medium transition-colors"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(profile.categories || []).map(category => (
|
||||
<span key={category} className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-700 text-gray-300">
|
||||
{category}
|
||||
<button
|
||||
onClick={() => removeCategory(category)}
|
||||
className="ml-2 text-red-400 hover:text-red-300 transition-colors"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user