Files
biblical-guide.com/app/[locale]/search/page.tsx
andupetcu 51248b24f7 Update color palette and improve UI aesthetics
- Apply new teal-based color palette (#009688, #00796B, #B2DFDB)
- Update secondary color to accent yellow (#FFC107)
- Replace Material-UI loading states with proper animations and skeletons
- Remove ugly gradient background, replace with clean white
- Update all gradients to use new color scheme
- Improve text colors for better readability (#212121, #757575)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-20 16:04:02 +03:00

438 lines
15 KiB
TypeScript

'use client'
import {
Container,
Grid,
Card,
CardContent,
Typography,
Box,
TextField,
Button,
Paper,
List,
ListItem,
ListItemText,
Chip,
InputAdornment,
FormControl,
InputLabel,
Select,
MenuItem,
Accordion,
AccordionSummary,
AccordionDetails,
useTheme,
CircularProgress,
Skeleton,
} from '@mui/material'
import {
Search,
FilterList,
ExpandMore,
MenuBook,
Close,
History,
} from '@mui/icons-material'
import { useState, useEffect } from 'react'
interface SearchResult {
id: string
book: string
chapter: number
verse: number
text: string
relevance: number
}
interface SearchFilter {
testament: 'all' | 'old' | 'new'
books: string[]
exactMatch: boolean
}
export default function SearchPage() {
const theme = useTheme()
const [searchQuery, setSearchQuery] = useState('')
const [results, setResults] = useState<SearchResult[]>([])
const [loading, setLoading] = useState(false)
const [searchHistory, setSearchHistory] = useState<string[]>([])
const [filters, setFilters] = useState<SearchFilter>({
testament: 'all',
books: [],
exactMatch: false,
})
const oldTestamentBooks = [
'Geneza', 'Exodul', 'Leviticul', 'Numerii', 'Deuteronomul',
'Iosua', 'Judecătorii', 'Rut', '1 Samuel', '2 Samuel',
'Psalmii', 'Proverbele', 'Isaia', 'Ieremia', 'Daniel'
]
const newTestamentBooks = [
'Matei', 'Marcu', 'Luca', 'Ioan', 'Faptele Apostolilor',
'Romani', '1 Corinteni', '2 Corinteni', 'Galateni', 'Efeseni',
'Filipeni', 'Coloseni', 'Evrei', 'Iacob', '1 Petru', 'Apocalipsa'
]
const popularSearches = [
'dragoste', 'credință', 'speranță', 'iertare', 'pace',
'rugăciune', 'înțelepciune', 'bucurie', 'răbdare', 'milostivire'
]
useEffect(() => {
// Load search history from localStorage
const saved = localStorage.getItem('searchHistory')
if (saved) {
setSearchHistory(JSON.parse(saved))
}
}, [])
const handleSearch = async () => {
if (!searchQuery.trim()) return
setLoading(true)
// Add to search history
const newHistory = [searchQuery, ...searchHistory.filter(s => s !== searchQuery)].slice(0, 10)
setSearchHistory(newHistory)
localStorage.setItem('searchHistory', JSON.stringify(newHistory))
try {
const params = new URLSearchParams({
q: searchQuery,
testament: filters.testament,
exactMatch: filters.exactMatch.toString(),
books: filters.books.join(','),
})
const response = await fetch(`/api/search/verses?${params}`)
if (!response.ok) {
throw new Error('Search failed')
}
const data = await response.json()
setResults(data.results || [])
} catch (error) {
console.error('Error searching:', error)
// Mock results for demo
setResults([
{
id: '1',
book: 'Ioan',
chapter: 3,
verse: 16,
text: 'Fiindcă atât de mult a iubit Dumnezeu lumea, că a dat pe singurul Său Fiu, pentru ca oricine crede în El să nu piară, ci să aibă viața veșnică.',
relevance: 0.95,
},
{
id: '2',
book: '1 Corinteni',
chapter: 13,
verse: 4,
text: 'Dragostea este îndelung răbdătoare, dragostea este binevoitoare; dragostea nu pizmuiește...',
relevance: 0.89,
},
])
} finally {
setLoading(false)
}
}
const handleKeyPress = (event: React.KeyboardEvent) => {
if (event.key === 'Enter') {
handleSearch()
}
}
const clearFilters = () => {
setFilters({
testament: 'all',
books: [],
exactMatch: false,
})
}
const highlightSearchTerm = (text: string, query: string) => {
if (!query) return text
const regex = new RegExp(`(${query})`, 'gi')
const parts = text.split(regex)
return parts.map((part, index) =>
regex.test(part) ? (
<Typography
key={index}
component="span"
sx={{ backgroundColor: 'yellow', fontWeight: 'bold' }}
>
{part}
</Typography>
) : (
part
)
)
}
return (
<Box>
<Container maxWidth="lg" sx={{ py: 4 }}>
{/* Header */}
<Box sx={{ mb: 4, textAlign: 'center' }}>
<Typography variant="h3" component="h1" gutterBottom>
<Search sx={{ fontSize: 40, mr: 2, verticalAlign: 'middle' }} />
Căutare în Scriptură
</Typography>
<Typography variant="body1" color="text.secondary">
Găsește rapid versete și pasaje din întreaga Biblie
</Typography>
</Box>
<Grid container spacing={4}>
{/* Search Sidebar */}
<Grid item xs={12} md={3}>
{/* Search Filters */}
<Card sx={{ mb: 3 }}>
<CardContent>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">
<FilterList sx={{ mr: 1, verticalAlign: 'middle' }} />
Filtre
</Typography>
<Button size="small" onClick={clearFilters}>
Șterge
</Button>
</Box>
<FormControl fullWidth sx={{ mb: 2 }}>
<InputLabel>Testament</InputLabel>
<Select
value={filters.testament}
label="Testament"
onChange={(e) => setFilters({ ...filters, testament: e.target.value as any })}
>
<MenuItem value="all">Toată Biblia</MenuItem>
<MenuItem value="old">Vechiul Testament</MenuItem>
<MenuItem value="new">Noul Testament</MenuItem>
</Select>
</FormControl>
<Accordion>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography variant="body2">Cărți specifice</Typography>
</AccordionSummary>
<AccordionDetails>
<Box sx={{ maxHeight: 200, overflow: 'auto' }}>
{(filters.testament === 'old' || filters.testament === 'all' ? oldTestamentBooks : [])
.concat(filters.testament === 'new' || filters.testament === 'all' ? newTestamentBooks : [])
.map((book) => (
<Chip
key={book}
label={book}
size="small"
variant={filters.books.includes(book) ? 'filled' : 'outlined'}
onClick={() => {
const newBooks = filters.books.includes(book)
? filters.books.filter(b => b !== book)
: [...filters.books, book]
setFilters({ ...filters, books: newBooks })
}}
sx={{ mb: 0.5, mr: 0.5 }}
/>
))}
</Box>
</AccordionDetails>
</Accordion>
</CardContent>
</Card>
{/* Search History */}
{searchHistory.length > 0 && (
<Card sx={{ mb: 3 }}>
<CardContent>
<Typography variant="h6" gutterBottom>
<History sx={{ mr: 1, verticalAlign: 'middle' }} />
Căutări recente
</Typography>
{searchHistory.slice(0, 5).map((query, index) => (
<Chip
key={index}
label={query}
size="small"
variant="outlined"
onClick={() => setSearchQuery(query)}
sx={{ mb: 0.5, mr: 0.5 }}
/>
))}
</CardContent>
</Card>
)}
{/* Popular Searches */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Căutări populare
</Typography>
{popularSearches.map((query, index) => (
<Chip
key={index}
label={query}
size="small"
variant="outlined"
onClick={() => setSearchQuery(query)}
sx={{ mb: 0.5, mr: 0.5 }}
/>
))}
</CardContent>
</Card>
</Grid>
{/* Main Search Area */}
<Grid item xs={12} md={9}>
{/* Search Input */}
<Card sx={{ mb: 3 }}>
<CardContent>
<Box sx={{ display: 'flex', gap: 2 }}>
<TextField
fullWidth
variant="outlined"
placeholder="Caută cuvinte, fraze sau referințe biblice..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyPress={handleKeyPress}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
endAdornment: searchQuery && (
<InputAdornment position="end">
<Button
size="small"
onClick={() => setSearchQuery('')}
>
<Close />
</Button>
</InputAdornment>
),
}}
/>
<Button
variant="contained"
onClick={handleSearch}
disabled={!searchQuery.trim() || loading}
sx={{ minWidth: 100 }}
>
{loading ? <CircularProgress size={20} color="inherit" /> : 'Caută'}
</Button>
</Box>
{filters.books.length > 0 && (
<Box sx={{ mt: 2 }}>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Căutare în: {filters.books.join(', ')}
</Typography>
</Box>
)}
</CardContent>
</Card>
{/* Search Results */}
{loading && searchQuery && (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Se caută...
</Typography>
<List>
{Array.from({ length: 3 }).map((_, index) => (
<ListItem key={index} divider>
<ListItemText
primary={
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
<Skeleton variant="text" width="40%" height={28} />
<Skeleton variant="rounded" width={100} height={24} />
</Box>
}
secondary={
<Box>
<Skeleton variant="text" width="100%" height={24} />
<Skeleton variant="text" width="90%" height={24} />
<Skeleton variant="text" width="95%" height={24} />
</Box>
}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
)}
{results.length > 0 && !loading && (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Rezultate ({results.length})
</Typography>
<List>
{results.map((result) => (
<ListItem key={result.id} divider>
<ListItemText
primary={
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
<Typography variant="subtitle1" color="primary">
{result.book} {result.chapter}:{result.verse}
</Typography>
<Chip
label={`${Math.round(result.relevance * 100)}% relevanță`}
size="small"
color="primary"
variant="outlined"
/>
</Box>
}
secondary={
<Typography variant="body1" sx={{ lineHeight: 1.6, mt: 1 }}>
{highlightSearchTerm(result.text, searchQuery)}
</Typography>
}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
)}
{!loading && searchQuery && results.length === 0 && (
<Paper sx={{ p: 4, textAlign: 'center' }}>
<Typography variant="h6" color="text.secondary" gutterBottom>
Nu s-au găsit rezultate
</Typography>
<Typography variant="body2" color="text.secondary">
Încearcă modifici termenul de căutare sau ajustezi filtrele.
</Typography>
</Paper>
)}
{!searchQuery && !loading && (
<Paper sx={{ p: 4, textAlign: 'center' }}>
<MenuBook sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
<Typography variant="h6" color="text.secondary" gutterBottom>
Începe cauți în Scriptură
</Typography>
<Typography variant="body2" color="text.secondary">
Introdu un cuvânt, o frază sau o referință biblică pentru a găsi versete relevante.
</Typography>
</Paper>
)}
</Grid>
</Grid>
</Container>
</Box>
)
}