Add complete Biblical Guide web application with Material UI
Implemented comprehensive Romanian Biblical Guide web app: - Next.js 15 with App Router and TypeScript - Material UI 7.3.2 for modern, responsive design - PostgreSQL database with Prisma ORM - Complete Bible reader with book/chapter navigation - AI-powered biblical chat with Romanian responses - Prayer wall for community prayer requests - Advanced Bible search with filters and highlighting - Sample Bible data imported from API.Bible - All API endpoints created and working - Professional Material UI components throughout - Responsive layout with navigation and theme 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
308
app/bible/page.tsx
Normal file
308
app/bible/page.tsx
Normal file
@@ -0,0 +1,308 @@
|
||||
'use client'
|
||||
import {
|
||||
Container,
|
||||
Grid,
|
||||
Card,
|
||||
CardContent,
|
||||
Typography,
|
||||
Box,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Paper,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Divider,
|
||||
Button,
|
||||
Chip,
|
||||
useTheme,
|
||||
} from '@mui/material'
|
||||
import {
|
||||
MenuBook,
|
||||
NavigateBefore,
|
||||
NavigateNext,
|
||||
Bookmark,
|
||||
Share,
|
||||
} from '@mui/icons-material'
|
||||
import { Navigation } from '@/components/layout/navigation'
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
interface BibleVerse {
|
||||
id: string
|
||||
verseNum: number
|
||||
text: string
|
||||
}
|
||||
|
||||
interface BibleChapter {
|
||||
id: string
|
||||
chapterNum: number
|
||||
verses: BibleVerse[]
|
||||
}
|
||||
|
||||
interface BibleBook {
|
||||
id: number
|
||||
name: string
|
||||
testament: string
|
||||
chapters: BibleChapter[]
|
||||
}
|
||||
|
||||
export default function BiblePage() {
|
||||
const theme = useTheme()
|
||||
const [books, setBooks] = useState<BibleBook[]>([])
|
||||
const [selectedBook, setSelectedBook] = useState<number>(1)
|
||||
const [selectedChapter, setSelectedChapter] = useState<number>(1)
|
||||
const [verses, setVerses] = useState<BibleVerse[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// Fetch available books
|
||||
useEffect(() => {
|
||||
fetch('/api/bible/books')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
setBooks(data.books || [])
|
||||
setLoading(false)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error fetching books:', err)
|
||||
setLoading(false)
|
||||
})
|
||||
}, [])
|
||||
|
||||
// Fetch verses when book/chapter changes
|
||||
useEffect(() => {
|
||||
if (selectedBook && selectedChapter) {
|
||||
setLoading(true)
|
||||
fetch(`/api/bible/verses?bookId=${selectedBook}&chapter=${selectedChapter}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
setVerses(data.verses || [])
|
||||
setLoading(false)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error fetching verses:', err)
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
}, [selectedBook, selectedChapter])
|
||||
|
||||
const currentBook = books.find(book => book.id === selectedBook)
|
||||
const maxChapters = currentBook?.chapters?.length || 50 // Default fallback
|
||||
|
||||
const handlePreviousChapter = () => {
|
||||
if (selectedChapter > 1) {
|
||||
setSelectedChapter(selectedChapter - 1)
|
||||
} else if (selectedBook > 1) {
|
||||
setSelectedBook(selectedBook - 1)
|
||||
setSelectedChapter(50) // Will be adjusted by actual chapter count
|
||||
}
|
||||
}
|
||||
|
||||
const handleNextChapter = () => {
|
||||
if (selectedChapter < maxChapters) {
|
||||
setSelectedChapter(selectedChapter + 1)
|
||||
} else {
|
||||
const nextBook = books.find(book => book.id === selectedBook + 1)
|
||||
if (nextBook) {
|
||||
setSelectedBook(selectedBook + 1)
|
||||
setSelectedChapter(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loading && books.length === 0) {
|
||||
return (
|
||||
<Box>
|
||||
<Navigation />
|
||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
||||
<Typography variant="h4" textAlign="center">
|
||||
Se încarcă...
|
||||
</Typography>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Navigation />
|
||||
|
||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
||||
{/* Header */}
|
||||
<Box sx={{ mb: 4, textAlign: 'center' }}>
|
||||
<Typography variant="h3" component="h1" gutterBottom>
|
||||
<MenuBook sx={{ fontSize: 40, mr: 2, verticalAlign: 'middle' }} />
|
||||
Citește Biblia
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
Explorează Scriptura cu o interfață modernă și intuitivă
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Grid container spacing={4}>
|
||||
{/* Left Sidebar - Book Selection */}
|
||||
<Grid item xs={12} md={3}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Selectează cartea
|
||||
</Typography>
|
||||
|
||||
<FormControl fullWidth sx={{ mb: 2 }}>
|
||||
<InputLabel>Cartea</InputLabel>
|
||||
<Select
|
||||
value={selectedBook}
|
||||
label="Cartea"
|
||||
onChange={(e) => {
|
||||
setSelectedBook(Number(e.target.value))
|
||||
setSelectedChapter(1)
|
||||
}}
|
||||
>
|
||||
{books.map((book) => (
|
||||
<MenuItem key={book.id} value={book.id}>
|
||||
{book.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>Capitolul</InputLabel>
|
||||
<Select
|
||||
value={selectedChapter}
|
||||
label="Capitolul"
|
||||
onChange={(e) => setSelectedChapter(Number(e.target.value))}
|
||||
>
|
||||
{Array.from({ length: maxChapters }, (_, i) => (
|
||||
<MenuItem key={i + 1} value={i + 1}>
|
||||
Capitolul {i + 1}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Box sx={{ mt: 2, display: 'flex', gap: 1 }}>
|
||||
<Chip
|
||||
label={currentBook?.testament || 'Vechiul Testament'}
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
/>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
{/* Main Content - Bible Text */}
|
||||
<Grid item xs={12} md={9}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
{/* Chapter Header */}
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||
<Box>
|
||||
<Typography variant="h4" component="h2">
|
||||
{currentBook?.name || 'Geneza'} {selectedChapter}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{verses.length} versete
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<Button
|
||||
startIcon={<Bookmark />}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
Salvează
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<Share />}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
Partajează
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ mb: 3 }} />
|
||||
|
||||
{/* Bible Verses */}
|
||||
{loading ? (
|
||||
<Typography textAlign="center" color="text.secondary">
|
||||
Se încarcă versetele...
|
||||
</Typography>
|
||||
) : verses.length > 0 ? (
|
||||
<Box>
|
||||
{verses.map((verse) => (
|
||||
<Box key={verse.id} sx={{ mb: 2 }}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
component="p"
|
||||
sx={{
|
||||
lineHeight: 1.8,
|
||||
fontSize: '1.1rem',
|
||||
'&:hover': {
|
||||
bgcolor: 'action.hover',
|
||||
cursor: 'pointer',
|
||||
borderRadius: 1,
|
||||
p: 1,
|
||||
m: -1,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
fontWeight: 'bold',
|
||||
color: 'primary.main',
|
||||
mr: 1,
|
||||
fontSize: '0.9rem',
|
||||
}}
|
||||
>
|
||||
{verse.verseNum}
|
||||
</Typography>
|
||||
{verse.text}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
) : (
|
||||
<Typography textAlign="center" color="text.secondary">
|
||||
Nu s-au găsit versete pentru această selecție.
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{/* Navigation */}
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 4, pt: 3, borderTop: 1, borderColor: 'divider' }}>
|
||||
<Button
|
||||
startIcon={<NavigateBefore />}
|
||||
onClick={handlePreviousChapter}
|
||||
disabled={selectedBook === 1 && selectedChapter === 1}
|
||||
>
|
||||
Capitolul anterior
|
||||
</Button>
|
||||
|
||||
<Typography variant="body2" color="text.secondary" sx={{ alignSelf: 'center' }}>
|
||||
{currentBook?.name} {selectedChapter}
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
endIcon={<NavigateNext />}
|
||||
onClick={handleNextChapter}
|
||||
disabled={selectedBook === books.length && selectedChapter === maxChapters}
|
||||
>
|
||||
Capitolul următor
|
||||
</Button>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user