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:
nmemmert committed 2025-12-17 13:50:21 -05:00
1 parent 7214fdb89a
commit d9784738e4
32 files changed
+3146 -3236

No files matched your search

+82 -48
View File
@@ -9,15 +9,18 @@ export default function Login() {
const [confirmPassword, setConfirmPassword] = useState('');
const [isSignup, setIsSignup] = useState(false);
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const { login, signup } = useAuth();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);
if (isSignup) {
if (password !== confirmPassword) {
setError('Passwords do not match');
setLoading(false);
return;
}
try {
@@ -32,62 +35,93 @@ export default function Login() {
setError(err instanceof Error ? err.message : 'An error occurred');
}
}
setLoading(false);
};
return (
<div className="min-h-screen bg-black text-white flex items-center justify-center">
<form onSubmit={handleSubmit} className="bg-gray-800 p-8 rounded-lg w-full max-w-md">
<h1 className="text-2xl font-bold mb-6 text-center">
{isSignup ? 'Sign Up for Prayer App' : 'Login to Prayer App'}
</h1>
{error && <p className="text-red-500 mb-4">{error}</p>}
<div className="mb-4">
<label className="block mb-2">Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full p-2 bg-gray-700 border border-gray-600 rounded"
required
/>
<div className="min-h-screen bg-gradient-to-br from-gray-900 to-black text-white flex items-center justify-center p-4">
<div className="w-full max-w-md">
<div className="text-center mb-8">
<h1 className="text-4xl font-bold mb-2">Prayer App</h1>
<p className="text-gray-400">Manage your spiritual journey</p>
</div>
<div className="mb-4">
<label className="block mb-2">Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full p-2 bg-gray-700 border border-gray-600 rounded"
required
/>
</div>
{isSignup && (
<div className="mb-6">
<label className="block mb-2">Confirm Password</label>
<form onSubmit={handleSubmit} className="bg-gray-800 p-8 rounded-xl shadow-2xl space-y-6">
<h2 className="text-2xl font-semibold text-center">
{isSignup ? 'Create Account' : 'Welcome Back'}
</h2>
{error && (
<div className="bg-red-900 border border-red-700 text-red-200 px-4 py-3 rounded-lg">
{error}
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">Email</label>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="w-full p-2 bg-gray-700 border border-gray-600 rounded"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
autoFocus
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="Enter your email"
required
/>
</div>
)}
<button type="submit" className="w-full p-2 bg-blue-600 hover:bg-blue-700 rounded mb-4">
{isSignup ? 'Sign Up' : 'Login'}
</button>
<button
type="button"
onClick={() => {
setIsSignup(!isSignup);
setError('');
setConfirmPassword('');
}}
className="w-full p-2 bg-gray-600 hover:bg-gray-700 rounded"
>
{isSignup ? 'Already have an account? Login' : 'Need an account? Sign Up'}
</button>
</form>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(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="Enter your password"
required
/>
</div>
{isSignup && (
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">Confirm Password</label>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(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="Confirm your password"
required
/>
</div>
)}
<button
type="submit"
disabled={loading}
className="w-full p-3 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-800 disabled:cursor-not-allowed rounded-lg font-medium transition-colors flex items-center justify-center"
>
{loading ? (
<>
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Processing...
</>
) : (
isSignup ? 'Create Account' : 'Sign In'
)}
</button>
<div className="text-center">
<button
type="button"
onClick={() => {
setIsSignup(!isSignup);
setError('');
setConfirmPassword('');
}}
className="text-blue-400 hover:text-blue-300 transition-colors"
>
{isSignup ? 'Already have an account? Sign In' : 'Need an account? Sign Up'}
</button>
</div>
</form>
</div>
</div>
);
}