Implement complete backend subscription system that limits free users to 10 AI conversations per month and offers Premium tier ($10/month or $100/year) with unlimited conversations. Changes: - Add User subscription fields (tier, status, limits, counters) - Create Subscription model to track Stripe subscriptions - Implement conversation limit enforcement in chat API - Add subscription checkout and customer portal APIs - Update Stripe webhook to handle subscription events - Add subscription utility functions (limit checks, tier management) - Add comprehensive subscription translations (en, ro, es, it) - Update environment variables for Stripe price IDs - Update footer "Sponsor Us" link to point to /donate - Add "Sponsor Us" button to home page hero section Database: - User model: subscriptionTier, subscriptionStatus, conversationLimit, conversationCount, limitResetDate, stripeCustomerId, stripeSubscriptionId - Subscription model: tracks Stripe subscription details, periods, status - SubscriptionStatus enum: ACTIVE, CANCELLED, PAST_DUE, TRIALING, etc. API Routes: - POST /api/subscriptions/checkout - Create Stripe checkout session - POST /api/subscriptions/portal - Get customer portal link - Webhook handlers for: customer.subscription.created/updated/deleted, invoice.payment_succeeded/failed Features: - Free tier: 10 conversations/month with automatic monthly reset - Premium tier: Unlimited conversations - Automatic limit enforcement before conversation creation - Returns LIMIT_REACHED error with upgrade URL when limit hit - Stripe Customer Portal integration for subscription management - Automatic tier upgrade/downgrade via webhooks Documentation: - SUBSCRIPTION_IMPLEMENTATION_PLAN.md - Complete implementation plan - SUBSCRIPTION_IMPLEMENTATION_STATUS.md - Current status and next steps Frontend UI still needed: subscription page, upgrade modal, usage display 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
903 lines
27 KiB
TypeScript
903 lines
27 KiB
TypeScript
'use client'
|
|
import {
|
|
Container,
|
|
Typography,
|
|
Box,
|
|
Button,
|
|
Paper,
|
|
useTheme,
|
|
Divider,
|
|
Card,
|
|
CardContent,
|
|
List,
|
|
ListItem,
|
|
ListItemText,
|
|
TextField,
|
|
Checkbox,
|
|
FormControlLabel,
|
|
ToggleButton,
|
|
ToggleButtonGroup,
|
|
CircularProgress,
|
|
Alert,
|
|
} from '@mui/material'
|
|
import {
|
|
MenuBook,
|
|
Chat,
|
|
Favorite,
|
|
Search,
|
|
Language,
|
|
CloudOff,
|
|
Security,
|
|
AutoStories,
|
|
Public,
|
|
VolunteerActivism,
|
|
CheckCircle,
|
|
} from '@mui/icons-material'
|
|
import { useRouter } from 'next/navigation'
|
|
import { useLocale, useTranslations } from 'next-intl'
|
|
import { useState } from 'react'
|
|
import { DONATION_PRESETS } from '@/lib/stripe'
|
|
|
|
export default function DonatePage() {
|
|
const theme = useTheme()
|
|
const router = useRouter()
|
|
const locale = useLocale()
|
|
const t = useTranslations('donate')
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [success, setSuccess] = useState(false)
|
|
|
|
// Form state
|
|
const [selectedAmount, setSelectedAmount] = useState<number | null>(25)
|
|
const [customAmount, setCustomAmount] = useState('')
|
|
const [email, setEmail] = useState('')
|
|
const [name, setName] = useState('')
|
|
const [message, setMessage] = useState('')
|
|
const [isAnonymous, setIsAnonymous] = useState(false)
|
|
const [isRecurring, setIsRecurring] = useState(false)
|
|
const [recurringInterval, setRecurringInterval] = useState<'month' | 'year'>('month')
|
|
|
|
const handleAmountSelect = (amount: number | null) => {
|
|
setSelectedAmount(amount)
|
|
setCustomAmount('')
|
|
}
|
|
|
|
const handleCustomAmountChange = (value: string) => {
|
|
setCustomAmount(value)
|
|
setSelectedAmount(null)
|
|
}
|
|
|
|
const getAmount = (): number | null => {
|
|
if (customAmount) {
|
|
const parsed = parseFloat(customAmount)
|
|
return isNaN(parsed) ? null : parsed
|
|
}
|
|
return selectedAmount
|
|
}
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setError(null)
|
|
|
|
const amount = getAmount()
|
|
|
|
// Validation
|
|
if (!amount || amount < 1) {
|
|
setError(t('form.errors.invalidAmount'))
|
|
return
|
|
}
|
|
|
|
if (!email || !email.includes('@')) {
|
|
setError(t('form.errors.invalidEmail'))
|
|
return
|
|
}
|
|
|
|
setLoading(true)
|
|
|
|
try {
|
|
// Create checkout session
|
|
const response = await fetch('/api/stripe/checkout', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
amount,
|
|
email,
|
|
name: isAnonymous ? 'Anonymous' : name,
|
|
message,
|
|
isAnonymous,
|
|
isRecurring,
|
|
recurringInterval,
|
|
locale,
|
|
}),
|
|
})
|
|
|
|
const data = await response.json()
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.error || t('form.errors.checkoutFailed'))
|
|
}
|
|
|
|
// Redirect to Stripe Checkout
|
|
if (data.url) {
|
|
window.location.href = data.url
|
|
}
|
|
} catch (err) {
|
|
console.error('Donation error:', err)
|
|
setError(err instanceof Error ? err.message : t('form.errors.generic'))
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const features = [
|
|
{
|
|
icon: <Public sx={{ fontSize: 48 }} />,
|
|
title: t('features.globalLibrary.title'),
|
|
description: t('features.globalLibrary.description'),
|
|
},
|
|
{
|
|
icon: <Language sx={{ fontSize: 48 }} />,
|
|
title: t('features.multilingual.title'),
|
|
description: t('features.multilingual.description'),
|
|
},
|
|
{
|
|
icon: <Favorite sx={{ fontSize: 48 }} />,
|
|
title: t('features.prayerWall.title'),
|
|
description: t('features.prayerWall.description'),
|
|
},
|
|
{
|
|
icon: <Chat sx={{ fontSize: 48 }} />,
|
|
title: t('features.aiChat.title'),
|
|
description: t('features.aiChat.description'),
|
|
},
|
|
{
|
|
icon: <Security sx={{ fontSize: 48 }} />,
|
|
title: t('features.privacy.title'),
|
|
description: t('features.privacy.description'),
|
|
},
|
|
{
|
|
icon: <CloudOff sx={{ fontSize: 48 }} />,
|
|
title: t('features.offline.title'),
|
|
description: t('features.offline.description'),
|
|
},
|
|
]
|
|
|
|
return (
|
|
<Box>
|
|
{/* Hero Section */}
|
|
<Box
|
|
sx={{
|
|
background: 'linear-gradient(135deg, #009688 0%, #00796B 100%)',
|
|
color: 'white',
|
|
py: 6.25,
|
|
textAlign: 'center',
|
|
position: 'relative',
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
<Container maxWidth="md">
|
|
<Typography
|
|
variant="h1"
|
|
sx={{
|
|
fontSize: { xs: '2.5rem', sm: '3.5rem', md: '4.5rem' },
|
|
fontWeight: 700,
|
|
mb: 3,
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
{t('hero.title')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h4"
|
|
sx={{
|
|
fontSize: { xs: '1.25rem', sm: '1.75rem', md: '2rem' },
|
|
fontWeight: 400,
|
|
mb: 6,
|
|
opacity: 0.95,
|
|
letterSpacing: '-0.01em',
|
|
}}
|
|
>
|
|
{t('hero.subtitle')}
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center', flexWrap: 'wrap' }}>
|
|
<Button
|
|
variant="contained"
|
|
size="large"
|
|
sx={{
|
|
bgcolor: 'white',
|
|
color: 'primary.main',
|
|
px: 4,
|
|
py: 1.5,
|
|
fontSize: '1.1rem',
|
|
fontWeight: 600,
|
|
'&:hover': { bgcolor: 'grey.100' },
|
|
textTransform: 'none',
|
|
}}
|
|
startIcon={<AutoStories />}
|
|
onClick={() => router.push(`/${locale}/bible`)}
|
|
>
|
|
{t('hero.cta.readBible')}
|
|
</Button>
|
|
<Button
|
|
variant="outlined"
|
|
size="large"
|
|
sx={{
|
|
borderColor: 'white',
|
|
color: 'white',
|
|
px: 4,
|
|
py: 1.5,
|
|
fontSize: '1.1rem',
|
|
fontWeight: 600,
|
|
'&:hover': {
|
|
borderColor: 'white',
|
|
bgcolor: 'rgba(255,255,255,0.1)',
|
|
},
|
|
textTransform: 'none',
|
|
}}
|
|
onClick={() => window.scrollTo({ top: document.getElementById('donate-form')?.offsetTop || 0, behavior: 'smooth' })}
|
|
>
|
|
{t('hero.cta.supportMission')}
|
|
</Button>
|
|
</Box>
|
|
</Container>
|
|
</Box>
|
|
|
|
{/* Mission Section */}
|
|
<Container maxWidth="md" sx={{ pt: { xs: 10, md: 16 }, pb: 0, textAlign: 'center' }}>
|
|
<Typography
|
|
variant="h2"
|
|
sx={{
|
|
fontSize: { xs: '2rem', md: '3rem' },
|
|
fontWeight: 700,
|
|
mb: 4,
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
{t('mission.title')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.6,
|
|
color: 'text.secondary',
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
{t('mission.description1')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 600,
|
|
lineHeight: 1.6,
|
|
mt: 3,
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
{t('mission.different')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.6,
|
|
mt: 2,
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
{t('mission.description2')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.6,
|
|
mt: 2,
|
|
color: 'text.secondary',
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
{t('mission.description3')}
|
|
</Typography>
|
|
</Container>
|
|
|
|
<Divider sx={{ maxWidth: 200, mx: 'auto', borderColor: 'grey.300' }} />
|
|
|
|
{/* Donation Pitch Section */}
|
|
<Container maxWidth="md" sx={{ pt: { xs: 10, md: 16 }, pb: 0, textAlign: 'center' }}>
|
|
<Typography
|
|
variant="h2"
|
|
sx={{
|
|
fontSize: { xs: '2rem', md: '3rem' },
|
|
fontWeight: 700,
|
|
mb: 4,
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
{t('pitch.title')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.8,
|
|
color: 'text.secondary',
|
|
mb: 5,
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
{t('pitch.description1')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.8,
|
|
color: 'text.secondary',
|
|
mb: 5,
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
{t('pitch.description2')}
|
|
</Typography>
|
|
<Paper
|
|
elevation={0}
|
|
sx={{
|
|
bgcolor: 'primary.light',
|
|
color: 'white',
|
|
py: 4,
|
|
px: 3,
|
|
borderRadius: 3,
|
|
maxWidth: 600,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.3rem' },
|
|
fontWeight: 500,
|
|
fontStyle: 'italic',
|
|
lineHeight: 1.6,
|
|
}}
|
|
>
|
|
{t('pitch.verse.text')}
|
|
</Typography>
|
|
<Typography variant="h6" sx={{ mt: 2, fontWeight: 600 }}>
|
|
{t('pitch.verse.reference')}
|
|
</Typography>
|
|
</Paper>
|
|
</Container>
|
|
|
|
{/* Features Section */}
|
|
<Box sx={{ bgcolor: 'grey.50', pt: { xs: 10, md: 16 }, pb: 0 }}>
|
|
<Container maxWidth="lg">
|
|
<Typography
|
|
variant="h2"
|
|
sx={{
|
|
fontSize: { xs: '2rem', md: '3rem' },
|
|
fontWeight: 700,
|
|
mb: 3,
|
|
textAlign: 'center',
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
{t('features.title')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h6"
|
|
sx={{
|
|
textAlign: 'center',
|
|
color: 'text.secondary',
|
|
mb: 8,
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
}}
|
|
>
|
|
{t('features.subtitle')}
|
|
</Typography>
|
|
|
|
<Box sx={{ display: 'flex', gap: 4, flexWrap: 'wrap', justifyContent: 'center' }}>
|
|
{features.map((feature, index) => (
|
|
<Box key={index} sx={{ flex: { xs: '1 1 100%', md: '1 1 calc(33.33% - 24px)' }, maxWidth: { xs: '100%', md: 400 } }}>
|
|
<Card
|
|
elevation={0}
|
|
sx={{
|
|
height: '100%',
|
|
textAlign: 'center',
|
|
bgcolor: 'white',
|
|
border: '1px solid',
|
|
borderColor: 'grey.200',
|
|
transition: 'transform 0.2s, box-shadow 0.2s',
|
|
'&:hover': {
|
|
transform: 'translateY(-4px)',
|
|
boxShadow: 2,
|
|
},
|
|
}}
|
|
>
|
|
<CardContent sx={{ p: 4 }}>
|
|
<Box sx={{ color: 'primary.main', mb: 2 }}>
|
|
{feature.icon}
|
|
</Box>
|
|
<Typography variant="h6" sx={{ fontWeight: 600, mb: 2 }}>
|
|
{feature.title}
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary" sx={{ lineHeight: 1.6 }}>
|
|
{feature.description}
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
</Box>
|
|
))}
|
|
</Box>
|
|
</Container>
|
|
</Box>
|
|
|
|
{/* Donation Form Section */}
|
|
<Container id="donate-form" maxWidth="lg" sx={{ pt: { xs: 10, md: 16 }, pb: 0 }}>
|
|
<Typography
|
|
variant="h2"
|
|
sx={{
|
|
fontSize: { xs: '2rem', md: '3rem' },
|
|
fontWeight: 700,
|
|
mb: 6,
|
|
textAlign: 'center',
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
{t('form.title')}
|
|
</Typography>
|
|
|
|
<Box sx={{ display: 'flex', gap: 4, flexDirection: { xs: 'column', md: 'row' } }}>
|
|
{/* Donation Form */}
|
|
<Box sx={{ flex: { xs: '1 1 100%', md: '1 1 58%' } }}>
|
|
<Paper elevation={2} sx={{ p: 4 }}>
|
|
<Typography variant="h5" sx={{ fontWeight: 600, mb: 3 }}>
|
|
{t('form.makedonation')}
|
|
</Typography>
|
|
|
|
{error && (
|
|
<Alert severity="error" sx={{ mb: 3 }}>
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
|
|
{success && (
|
|
<Alert severity="success" sx={{ mb: 3 }}>
|
|
{t('form.success')}
|
|
</Alert>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
{/* Recurring Donation Toggle */}
|
|
<Box sx={{ mb: 3 }}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={isRecurring}
|
|
onChange={(e) => setIsRecurring(e.target.checked)}
|
|
/>
|
|
}
|
|
label={t('form.recurring.label')}
|
|
/>
|
|
{isRecurring && (
|
|
<ToggleButtonGroup
|
|
value={recurringInterval}
|
|
exclusive
|
|
onChange={(_, value) => value && setRecurringInterval(value)}
|
|
sx={{ mt: 2, width: '100%' }}
|
|
>
|
|
<ToggleButton value="month" sx={{ flex: 1 }}>
|
|
{t('form.recurring.monthly')}
|
|
</ToggleButton>
|
|
<ToggleButton value="year" sx={{ flex: 1 }}>
|
|
{t('form.recurring.yearly')}
|
|
</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
)}
|
|
</Box>
|
|
|
|
{/* Amount Selection */}
|
|
<Box sx={{ mb: 3 }}>
|
|
<Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 2 }}>
|
|
{t('form.amount.label')}
|
|
</Typography>
|
|
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 2 }}>
|
|
{DONATION_PRESETS.map((preset) => (
|
|
<Button
|
|
key={preset.amount}
|
|
fullWidth
|
|
variant={selectedAmount === preset.amount ? 'contained' : 'outlined'}
|
|
onClick={() => handleAmountSelect(preset.amount)}
|
|
sx={{
|
|
py: 2,
|
|
fontSize: '1.1rem',
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
{preset.label}
|
|
</Button>
|
|
))}
|
|
</Box>
|
|
|
|
<TextField
|
|
fullWidth
|
|
label={t('form.amount.custom')}
|
|
type="number"
|
|
value={customAmount}
|
|
onChange={(e) => handleCustomAmountChange(e.target.value)}
|
|
sx={{ mt: 2 }}
|
|
InputProps={{
|
|
startAdornment: <Typography sx={{ mr: 1 }}>$</Typography>,
|
|
}}
|
|
inputProps={{ min: 1, step: 0.01 }}
|
|
/>
|
|
</Box>
|
|
|
|
<Divider sx={{ my: 3 }} />
|
|
|
|
{/* Contact Information */}
|
|
<Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 2 }}>
|
|
{t('form.info.title')}
|
|
</Typography>
|
|
|
|
<TextField
|
|
fullWidth
|
|
label={t('form.info.email')}
|
|
type="email"
|
|
required
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
sx={{ mb: 2 }}
|
|
/>
|
|
|
|
{!isAnonymous && (
|
|
<TextField
|
|
fullWidth
|
|
label={t('form.info.name')}
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
sx={{ mb: 2 }}
|
|
/>
|
|
)}
|
|
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={isAnonymous}
|
|
onChange={(e) => setIsAnonymous(e.target.checked)}
|
|
/>
|
|
}
|
|
label={t('form.info.anonymous')}
|
|
sx={{ mb: 2 }}
|
|
/>
|
|
|
|
<TextField
|
|
fullWidth
|
|
label={t('form.info.message')}
|
|
multiline
|
|
rows={3}
|
|
value={message}
|
|
onChange={(e) => setMessage(e.target.value)}
|
|
placeholder={t('form.info.messagePlaceholder')}
|
|
sx={{ mb: 3 }}
|
|
/>
|
|
|
|
{/* Submit Button */}
|
|
<Button
|
|
type="submit"
|
|
variant="contained"
|
|
size="large"
|
|
fullWidth
|
|
disabled={loading}
|
|
sx={{
|
|
py: 2,
|
|
fontSize: '1.1rem',
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
{loading ? (
|
|
<CircularProgress size={24} color="inherit" />
|
|
) : (
|
|
`${t('form.submit')} ${getAmount() ? `$${getAmount()}` : ''}`
|
|
)}
|
|
</Button>
|
|
|
|
<Typography
|
|
variant="body2"
|
|
color="text.secondary"
|
|
sx={{ mt: 2, textAlign: 'center' }}
|
|
>
|
|
{t('form.secure')}
|
|
</Typography>
|
|
</form>
|
|
|
|
{/* Alternative Donation Methods */}
|
|
<Box sx={{ mt: 4 }}>
|
|
<Divider sx={{ mb: 3 }} />
|
|
<Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 2, textAlign: 'center' }}>
|
|
{t('alternatives.title')}
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<Button
|
|
variant="outlined"
|
|
size="large"
|
|
fullWidth
|
|
sx={{ py: 1.5, textTransform: 'none' }}
|
|
startIcon={<span style={{ fontSize: '1.5rem' }}>💳</span>}
|
|
href="https://paypal.me/andupetcu"
|
|
target="_blank"
|
|
>
|
|
{t('alternatives.paypal')}
|
|
</Button>
|
|
<Typography variant="body2" color="text.secondary" textAlign="center">
|
|
<span style={{ fontSize: '1.5rem' }}>🎯</span> {t('alternatives.kickstarter')}
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
</Paper>
|
|
</Box>
|
|
|
|
{/* Impact Section */}
|
|
<Box sx={{ flex: { xs: '1 1 100%', md: '1 1 42%' } }}>
|
|
<Paper elevation={2} sx={{ p: 4, mb: 3, bgcolor: 'primary.light', color: 'white' }}>
|
|
<Typography variant="h6" sx={{ fontWeight: 600, mb: 2 }}>
|
|
{t('impact.title')}
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ mb: 3, lineHeight: 1.8 }}>
|
|
{t('impact.description')}
|
|
</Typography>
|
|
<List>
|
|
{features.slice(0, 4).map((feature, index) => (
|
|
<ListItem key={index} sx={{ px: 0 }}>
|
|
<CheckCircle sx={{ mr: 2 }} />
|
|
<ListItemText primary={feature.title} />
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
</Paper>
|
|
|
|
<Paper elevation={2} sx={{ p: 4 }}>
|
|
<Typography variant="h6" sx={{ fontWeight: 600, mb: 2 }}>
|
|
{t('why.title')}
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2, lineHeight: 1.8 }}>
|
|
{t('why.description1')}
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ lineHeight: 1.8 }}>
|
|
{t('why.description2')}
|
|
</Typography>
|
|
</Paper>
|
|
</Box>
|
|
</Box>
|
|
</Container>
|
|
|
|
{/* Why It Matters Section */}
|
|
<Box sx={{ bgcolor: 'grey.900', color: 'white', pt: { xs: 10, md: 16 }, pb: 0 }}>
|
|
<Container maxWidth="md" sx={{ textAlign: 'center' }}>
|
|
<Typography
|
|
variant="h2"
|
|
sx={{
|
|
fontSize: { xs: '2rem', md: '3rem' },
|
|
fontWeight: 700,
|
|
mb: 6,
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
{t('matters.title')}
|
|
</Typography>
|
|
|
|
<List sx={{ maxWidth: 700, mx: 'auto' }}>
|
|
<ListItem sx={{ py: 2, flexDirection: 'column', alignItems: 'flex-start' }}>
|
|
<Typography variant="h6" sx={{ fontSize: '1.2rem', mb: 1, lineHeight: 1.6 }}>
|
|
{t('matters.point1')}
|
|
</Typography>
|
|
</ListItem>
|
|
<ListItem sx={{ py: 2, flexDirection: 'column', alignItems: 'flex-start' }}>
|
|
<Typography variant="h6" sx={{ fontSize: '1.2rem', mb: 1, lineHeight: 1.6 }}>
|
|
{t('matters.point2')}
|
|
</Typography>
|
|
</ListItem>
|
|
<ListItem sx={{ py: 2, flexDirection: 'column', alignItems: 'flex-start' }}>
|
|
<Typography variant="h6" sx={{ fontSize: '1.2rem', mb: 1, lineHeight: 1.6 }}>
|
|
{t('matters.point3')}
|
|
</Typography>
|
|
</ListItem>
|
|
</List>
|
|
|
|
<Typography
|
|
variant="h4"
|
|
sx={{
|
|
fontSize: { xs: '1.5rem', md: '2rem' },
|
|
fontWeight: 700,
|
|
mt: 6,
|
|
mb: 3,
|
|
}}
|
|
>
|
|
{t('matters.together')}
|
|
</Typography>
|
|
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.3rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.6,
|
|
opacity: 0.9,
|
|
}}
|
|
>
|
|
{t('matters.conclusion')}
|
|
</Typography>
|
|
</Container>
|
|
</Box>
|
|
|
|
{/* Join the Mission Section */}
|
|
<Container maxWidth="md" sx={{ pt: { xs: 10, md: 16 }, pb: 0, textAlign: 'center' }}>
|
|
<Typography
|
|
variant="h2"
|
|
sx={{
|
|
fontSize: { xs: '2rem', md: '3rem' },
|
|
fontWeight: 700,
|
|
mb: 4,
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
{t('join.title')}
|
|
</Typography>
|
|
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.8,
|
|
color: 'text.secondary',
|
|
mb: 2,
|
|
}}
|
|
>
|
|
{t('join.description1')}
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
lineHeight: 1.8,
|
|
color: 'text.secondary',
|
|
mb: 6,
|
|
}}
|
|
>
|
|
{t('join.description2')}
|
|
</Typography>
|
|
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 600,
|
|
lineHeight: 1.8,
|
|
mb: 6,
|
|
}}
|
|
>
|
|
{t('join.callToAction')}
|
|
</Typography>
|
|
|
|
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '0.95rem', fontStyle: 'italic' }}>
|
|
{t('join.closing')}
|
|
</Typography>
|
|
</Container>
|
|
|
|
{/* Footer CTA */}
|
|
<Box
|
|
sx={{
|
|
background: 'linear-gradient(135deg, #009688 0%, #00796B 100%)',
|
|
color: 'white',
|
|
py: 6.25,
|
|
textAlign: 'center',
|
|
}}
|
|
>
|
|
<Container maxWidth="md">
|
|
<Typography
|
|
variant="h3"
|
|
sx={{
|
|
fontSize: { xs: '1.75rem', md: '2.5rem' },
|
|
fontWeight: 700,
|
|
mb: 2,
|
|
letterSpacing: '-0.02em',
|
|
}}
|
|
>
|
|
Biblical-Guide.com
|
|
</Typography>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontSize: { xs: '1.1rem', md: '1.5rem' },
|
|
fontWeight: 400,
|
|
opacity: 0.95,
|
|
}}
|
|
>
|
|
{t('footer.tagline')}
|
|
</Typography>
|
|
|
|
<Box sx={{ display: 'flex', gap: 3, justifyContent: 'center', mt: 6, flexWrap: 'wrap' }}>
|
|
<Button
|
|
variant="outlined"
|
|
sx={{
|
|
borderColor: 'white',
|
|
color: 'white',
|
|
'&:hover': {
|
|
borderColor: 'white',
|
|
bgcolor: 'rgba(255,255,255,0.1)',
|
|
},
|
|
textTransform: 'none',
|
|
}}
|
|
onClick={() => router.push(`/${locale}/bible`)}
|
|
>
|
|
{t('footer.links.readBible')}
|
|
</Button>
|
|
<Button
|
|
variant="outlined"
|
|
sx={{
|
|
borderColor: 'white',
|
|
color: 'white',
|
|
'&:hover': {
|
|
borderColor: 'white',
|
|
bgcolor: 'rgba(255,255,255,0.1)',
|
|
},
|
|
textTransform: 'none',
|
|
}}
|
|
onClick={() => router.push(`/${locale}/prayers`)}
|
|
>
|
|
{t('footer.links.prayerWall')}
|
|
</Button>
|
|
<Button
|
|
variant="outlined"
|
|
sx={{
|
|
borderColor: 'white',
|
|
color: 'white',
|
|
'&:hover': {
|
|
borderColor: 'white',
|
|
bgcolor: 'rgba(255,255,255,0.1)',
|
|
},
|
|
textTransform: 'none',
|
|
}}
|
|
onClick={() => window.dispatchEvent(new CustomEvent('floating-chat:open', { detail: { fullscreen: true } }))}
|
|
>
|
|
{t('footer.links.aiChat')}
|
|
</Button>
|
|
<Button
|
|
variant="outlined"
|
|
sx={{
|
|
borderColor: 'white',
|
|
color: 'white',
|
|
'&:hover': {
|
|
borderColor: 'white',
|
|
bgcolor: 'rgba(255,255,255,0.1)',
|
|
},
|
|
textTransform: 'none',
|
|
}}
|
|
onClick={() => router.push(`/${locale}/contact`)}
|
|
>
|
|
{t('footer.links.contact')}
|
|
</Button>
|
|
</Box>
|
|
</Container>
|
|
</Box>
|
|
</Box>
|
|
)
|
|
}
|