feat: Apple-style donation-focused landing page + Azure OpenAI fixes

Major updates:
- Replace homepage with clean, minimalist Apple-style landing page
- Focus on donation messaging and mission statement
- Add comprehensive AI chat analysis documentation
- Fix Azure OpenAI configuration with correct endpoints
- Update embedding API to use text-embedding-ada-002 (1536 dims)

Landing Page Features:
- Hero section with tagline "Every Scripture. Every Language. Forever Free"
- Mission statement emphasizing free access
- Matthew 10:8 verse highlight
- 6 feature cards (Global Library, Multilingual, Prayer Wall, AI Chat, Privacy, Offline)
- Donation CTA sections with PayPal and card options
- "Why It Matters" section with dark background
- Clean footer with navigation links

Technical Changes:
- Updated .env.local with new Azure credentials
- Fixed vector-search.ts to support separate embed API version
- Integrated AuthModal into Bible reader and prayers page
- Made prayer filters collapsible and mobile-responsive
- Changed language picker to single-select

Documentation Created:
- AI_CHAT_FIX_PLAN.md - Comprehensive implementation plan
- AI_CHAT_VERIFICATION_FINDINGS.md - Database analysis
- AI_CHAT_ANALYSIS_SUMMARY.md - Executive summary
- AI_CHAT_STATUS_UPDATE.md - Current status and next steps
- logo.svg - App logo (MenuBook icon)

Build:  Successful (Next.js 15.5.3)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-10 22:38:19 +00:00
parent 71047c85cc
commit 79f1512f3a
17 changed files with 4066 additions and 666 deletions

View File

@@ -18,6 +18,7 @@ import {
ListItemText,
MenuItem,
useTheme,
useMediaQuery,
CircularProgress,
Skeleton,
Alert,
@@ -29,6 +30,9 @@ import {
Checkbox,
SelectChangeEvent,
Switch,
Accordion,
AccordionSummary,
AccordionDetails,
} from '@mui/material'
import {
Favorite,
@@ -42,10 +46,12 @@ import {
AutoAwesome,
Edit,
Login,
ExpandMore,
} from '@mui/icons-material'
import { useState, useEffect, useMemo } from 'react'
import { useTranslations, useLocale, useFormatter } from 'next-intl'
import { useAuth } from '@/hooks/use-auth'
import { AuthModal } from '@/components/auth/auth-modal'
interface PrayerRequest {
id: string
@@ -68,6 +74,7 @@ export default function PrayersPage() {
const tc = useTranslations('common')
const f = useFormatter()
const { user } = useAuth()
const isMobile = useMediaQuery(theme.breakpoints.down('md'))
const [prayers, setPrayers] = useState<PrayerRequest[]>([])
const [selectedCategory, setSelectedCategory] = useState<string>('all')
const [openDialog, setOpenDialog] = useState(false)
@@ -82,12 +89,14 @@ export default function PrayersPage() {
const [isGenerating, setIsGenerating] = useState(false)
const [loading, setLoading] = useState(true)
const [viewMode, setViewMode] = useState<'private' | 'public'>(user ? 'private' : 'public')
const [selectedLanguages, setSelectedLanguages] = useState<string[]>([locale])
const [selectedLanguage, setSelectedLanguage] = useState<string>(locale)
const [authModalOpen, setAuthModalOpen] = useState(false)
const languagesKey = useMemo(() => selectedLanguages.slice().sort().join(','), [selectedLanguages])
const languageOptions = useMemo(() => ([
{ value: 'en', label: t('languageFilter.options.en') },
{ value: 'ro', label: t('languageFilter.options.ro') }
{ value: 'ro', label: t('languageFilter.options.ro') },
{ value: 'es', label: t('languageFilter.options.es') },
{ value: 'it', label: t('languageFilter.options.it') }
]), [t])
const languageLabelMap = useMemo(() => (
languageOptions.reduce((acc, option) => {
@@ -106,21 +115,10 @@ export default function PrayersPage() {
useEffect(() => {
if (viewMode === 'public') {
setSelectedLanguages(prev => {
if (prev.includes(locale)) {
return prev
}
return [...prev, locale]
})
setSelectedLanguage(locale)
}
}, [locale, viewMode])
useEffect(() => {
if (viewMode === 'public' && selectedLanguages.length === 0) {
setSelectedLanguages([locale])
}
}, [viewMode, selectedLanguages, locale])
const categories = [
{ value: 'personal', label: t('categories.personal'), color: 'primary' },
{ value: 'family', label: t('categories.family'), color: 'secondary' },
@@ -148,8 +146,7 @@ export default function PrayersPage() {
params.append('visibility', viewMode)
if (viewMode === 'public') {
const languagesToQuery = selectedLanguages.length > 0 ? selectedLanguages : [locale]
languagesToQuery.forEach(lang => params.append('languages', lang))
params.append('languages', selectedLanguage)
}
const headers: Record<string, string> = {}
@@ -185,7 +182,7 @@ export default function PrayersPage() {
useEffect(() => {
fetchPrayers()
}, [selectedCategory, user, viewMode, languagesKey])
}, [selectedCategory, user, viewMode, selectedLanguage])
const handleGenerateAIPrayer = async () => {
if (!aiPrompt.trim()) return
@@ -225,14 +222,9 @@ export default function PrayersPage() {
}
}
const handleLanguageChange = (event: SelectChangeEvent<string[]>) => {
const handleLanguageChange = (event: SelectChangeEvent<string>) => {
const value = event.target.value
const parsed = typeof value === 'string'
? value.split(',')
: (value as string[])
const uniqueValues = Array.from(new Set(parsed.filter(Boolean)))
setSelectedLanguages(uniqueValues)
setSelectedLanguage(value)
}
const handleSubmitPrayer = async () => {
@@ -273,12 +265,18 @@ export default function PrayersPage() {
const handleOpenDialog = () => {
if (!user) {
// Could redirect to login or show login modal
setAuthModalOpen(true)
return
}
setOpenDialog(true)
}
const handleAuthSuccess = () => {
setAuthModalOpen(false)
// After successful auth, open the add prayer dialog
setOpenDialog(true)
}
const handlePrayFor = async (prayerId: string) => {
try {
const headers: HeadersInit = {
@@ -371,80 +369,101 @@ export default function PrayersPage() {
fullWidth
variant="contained"
color="primary"
startIcon={<Login />}
onClick={() => {
// Could redirect to login page or show login modal
console.log('Please login to add prayers')
}}
startIcon={<Add />}
onClick={handleOpenDialog}
sx={{ mb: 3 }}
>
{locale === 'en' ? 'Login to Add Prayer' : 'Conectează-te pentru a adăuga'}
{t('addPrayer')}
</Button>
)}
<Typography variant="h6" gutterBottom>
{t('categories.title')}
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Chip
label={t('categories.all')}
color="default"
variant={selectedCategory === 'all' ? 'filled' : 'outlined'}
size="small"
onClick={() => setSelectedCategory('all')}
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
/>
{categories.map((category) => (
<Chip
key={category.value}
label={category.label}
color={category.color as any}
variant={selectedCategory === category.value ? 'filled' : 'outlined'}
size="small"
onClick={() => setSelectedCategory(category.value)}
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
/>
))}
</Box>
{/* Categories Accordion */}
<Accordion defaultExpanded={!isMobile}>
<AccordionSummary
expandIcon={<ExpandMore />}
aria-controls="categories-content"
id="categories-header"
>
<Typography variant="h6">
{t('categories.title')}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Chip
label={t('categories.all')}
color="default"
variant={selectedCategory === 'all' ? 'filled' : 'outlined'}
size="small"
onClick={() => setSelectedCategory('all')}
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
/>
{categories.map((category) => (
<Chip
key={category.value}
label={category.label}
color={category.color as any}
variant={selectedCategory === category.value ? 'filled' : 'outlined'}
size="small"
onClick={() => setSelectedCategory(category.value)}
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
/>
))}
</Box>
</AccordionDetails>
</Accordion>
{/* Language Filter Accordion */}
{viewMode === 'public' && (
<Box sx={{ mt: 3 }}>
<Typography variant="h6" sx={{ mb: 1 }}>
{t('languageFilter.title')}
</Typography>
<FormControl fullWidth size="small">
<Select
multiple
value={selectedLanguages}
onChange={handleLanguageChange}
renderValue={(selected) =>
(selected as string[])
.map(code => languageLabelMap[code] || code.toUpperCase())
.join(', ')
}
>
{languageOptions.map(option => (
<MenuItem key={option.value} value={option.value}>
<Checkbox checked={selectedLanguages.includes(option.value)} />
<ListItemText primary={option.label} />
</MenuItem>
))}
</Select>
</FormControl>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1 }}>
{t('languageFilter.helper')}
</Typography>
</Box>
<Accordion defaultExpanded={!isMobile} sx={{ mt: 2 }}>
<AccordionSummary
expandIcon={<ExpandMore />}
aria-controls="language-content"
id="language-header"
>
<Typography variant="h6">
{t('languageFilter.title')}
</Typography>
</AccordionSummary>
<AccordionDetails>
<FormControl fullWidth size="small">
<Select
value={selectedLanguage}
onChange={handleLanguageChange}
>
{languageOptions.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
{t('languageFilter.helper')}
</Typography>
</AccordionDetails>
</Accordion>
)}
<Typography variant="h6" sx={{ mt: 3, mb: 1 }}>
{t('stats.title')}
</Typography>
<Typography variant="body2" color="text.secondary">
{t('stats.activeRequests', { count: prayers.length })}<br />
{t('stats.totalPrayers', { count: prayers.reduce((sum, p) => sum + p.prayerCount, 0) })}<br />
{t('stats.youPrayed', { count: prayers.filter(p => p.isPrayedFor).length })}
</Typography>
{/* Stats Accordion */}
<Accordion defaultExpanded={!isMobile} sx={{ mt: 2 }}>
<AccordionSummary
expandIcon={<ExpandMore />}
aria-controls="stats-content"
id="stats-header"
>
<Typography variant="h6">
{t('stats.title')}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography variant="body2" color="text.secondary">
{t('stats.activeRequests', { count: prayers.length })}<br />
{t('stats.totalPrayers', { count: prayers.reduce((sum, p) => sum + p.prayerCount, 0) })}<br />
{t('stats.youPrayed', { count: prayers.filter(p => p.isPrayedFor).length })}
</Typography>
</AccordionDetails>
</Accordion>
</CardContent>
</Card>
</Box>
@@ -773,6 +792,15 @@ export default function PrayersPage() {
</Button>
</DialogActions>
</Dialog>
{/* Auth Modal */}
<AuthModal
open={authModalOpen}
onClose={() => setAuthModalOpen(false)}
onSuccess={handleAuthSuccess}
message={t('authRequired')}
defaultTab="login"
/>
</Container>
</Box>
)