Search filters now backed by DB per locale/version: fetch books from /api/bible/books; send bookKeys + locale to API; API constrains by versionId and bookKey/name; keeps testament filter.
This commit is contained in:
@@ -35,7 +35,6 @@ import {
|
|||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useTranslations, useLocale } from 'next-intl'
|
import { useTranslations, useLocale } from 'next-intl'
|
||||||
import { translateBookName } from '@/lib/book-translations'
|
|
||||||
|
|
||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
id: string
|
id: string
|
||||||
@@ -48,10 +47,18 @@ interface SearchResult {
|
|||||||
|
|
||||||
interface SearchFilter {
|
interface SearchFilter {
|
||||||
testament: 'all' | 'old' | 'new'
|
testament: 'all' | 'old' | 'new'
|
||||||
books: string[]
|
bookKeys: string[]
|
||||||
exactMatch: boolean
|
exactMatch: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BookOption {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
bookKey: string
|
||||||
|
orderNum: number
|
||||||
|
testament: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function SearchPage() {
|
export default function SearchPage() {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const t = useTranslations('pages.search')
|
const t = useTranslations('pages.search')
|
||||||
@@ -60,23 +67,11 @@ export default function SearchPage() {
|
|||||||
const [results, setResults] = useState<SearchResult[]>([])
|
const [results, setResults] = useState<SearchResult[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [searchHistory, setSearchHistory] = useState<string[]>([])
|
const [searchHistory, setSearchHistory] = useState<string[]>([])
|
||||||
const [filters, setFilters] = useState<SearchFilter>({
|
const [filters, setFilters] = useState<SearchFilter>({ testament: 'all', bookKeys: [], exactMatch: false })
|
||||||
testament: 'all',
|
const [booksData, setBooksData] = useState<BookOption[]>([])
|
||||||
books: [],
|
|
||||||
exactMatch: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const oldTestamentBooks = [
|
const oldTestamentBooks = booksData.filter(b => b.orderNum <= 39)
|
||||||
'Geneza', 'Exodul', 'Leviticul', 'Numerii', 'Deuteronomul',
|
const newTestamentBooks = booksData.filter(b => b.orderNum > 39)
|
||||||
'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: string[] = t.raw('popular.items')
|
const popularSearches: string[] = t.raw('popular.items')
|
||||||
|
|
||||||
@@ -88,6 +83,23 @@ export default function SearchPage() {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch available books for current locale/version
|
||||||
|
fetch(`/api/bible/books?locale=${locale}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
const mapped: BookOption[] = (data.books || []).map((b: any) => ({
|
||||||
|
id: b.id,
|
||||||
|
name: b.name,
|
||||||
|
bookKey: b.bookKey,
|
||||||
|
orderNum: b.orderNum,
|
||||||
|
testament: b.testament,
|
||||||
|
}))
|
||||||
|
setBooksData(mapped)
|
||||||
|
})
|
||||||
|
.catch(() => setBooksData([]))
|
||||||
|
}, [locale])
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
if (!searchQuery.trim()) return
|
if (!searchQuery.trim()) return
|
||||||
|
|
||||||
@@ -103,7 +115,8 @@ export default function SearchPage() {
|
|||||||
q: searchQuery,
|
q: searchQuery,
|
||||||
testament: filters.testament,
|
testament: filters.testament,
|
||||||
exactMatch: filters.exactMatch.toString(),
|
exactMatch: filters.exactMatch.toString(),
|
||||||
books: filters.books.join(','),
|
bookKeys: filters.bookKeys.join(','),
|
||||||
|
locale,
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await fetch(`/api/search/verses?${params}`)
|
const response = await fetch(`/api/search/verses?${params}`)
|
||||||
@@ -228,15 +241,16 @@ export default function SearchPage() {
|
|||||||
.concat(filters.testament === 'new' || filters.testament === 'all' ? newTestamentBooks : [])
|
.concat(filters.testament === 'new' || filters.testament === 'all' ? newTestamentBooks : [])
|
||||||
.map((book) => (
|
.map((book) => (
|
||||||
<Chip
|
<Chip
|
||||||
key={book}
|
key={book.bookKey}
|
||||||
label={translateBookName(book, locale)}
|
label={book.name}
|
||||||
size="small"
|
size="small"
|
||||||
variant={filters.books.includes(book) ? 'filled' : 'outlined'}
|
variant={filters.bookKeys.includes(book.bookKey) ? 'filled' : 'outlined'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newBooks = filters.books.includes(book)
|
const exists = filters.bookKeys.includes(book.bookKey)
|
||||||
? filters.books.filter(b => b !== book)
|
const newBookKeys = exists
|
||||||
: [...filters.books, book]
|
? filters.bookKeys.filter(b => b !== book.bookKey)
|
||||||
setFilters({ ...filters, books: newBooks })
|
: [...filters.bookKeys, book.bookKey]
|
||||||
|
setFilters({ ...filters, bookKeys: newBookKeys })
|
||||||
}}
|
}}
|
||||||
sx={{ mb: 0.5, mr: 0.5 }}
|
sx={{ mb: 0.5, mr: 0.5 }}
|
||||||
/>
|
/>
|
||||||
@@ -330,10 +344,10 @@ export default function SearchPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{filters.books.length > 0 && (
|
{filters.bookKeys.length > 0 && (
|
||||||
<Box sx={{ mt: 2 }}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
|
||||||
{t('searchIn', { books: filters.books.map(b => translateBookName(b, locale)).join(', ') })}
|
{t('searchIn', { books: filters.bookKeys.map(k => booksData.find(b => b.bookKey === k)?.name || k).join(', ') })}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ export async function GET(request: NextRequest) {
|
|||||||
const testament = searchParams.get('testament') || 'all'
|
const testament = searchParams.get('testament') || 'all'
|
||||||
const exactMatch = searchParams.get('exactMatch') === 'true'
|
const exactMatch = searchParams.get('exactMatch') === 'true'
|
||||||
const books = searchParams.get('books')?.split(',').filter(Boolean) || []
|
const books = searchParams.get('books')?.split(',').filter(Boolean) || []
|
||||||
|
const bookKeys = searchParams.get('bookKeys')?.split(',').filter(Boolean) || []
|
||||||
|
const locale = (searchParams.get('locale') || 'ro').toLowerCase()
|
||||||
|
const versionAbbr = searchParams.get('version') || undefined
|
||||||
|
|
||||||
if (!query) {
|
if (!query) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
@@ -22,6 +25,28 @@ export async function GET(request: NextRequest) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve Bible version to constrain search to selected language/version
|
||||||
|
let bibleVersion: any
|
||||||
|
if (versionAbbr) {
|
||||||
|
bibleVersion = await prisma.bibleVersion.findFirst({
|
||||||
|
where: { abbreviation: versionAbbr, language: { in: [locale, locale.toLowerCase(), locale.toUpperCase()] } }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bibleVersion = await prisma.bibleVersion.findFirst({
|
||||||
|
where: { language: { in: [locale, locale.toLowerCase(), locale.toUpperCase()] }, isDefault: true }
|
||||||
|
})
|
||||||
|
if (!bibleVersion) {
|
||||||
|
bibleVersion = await prisma.bibleVersion.findFirst({
|
||||||
|
where: { language: { in: [locale, locale.toLowerCase(), locale.toUpperCase()] } },
|
||||||
|
orderBy: { createdAt: 'asc' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bibleVersion) {
|
||||||
|
return NextResponse.json({ success: true, results: [], total: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
// Build search conditions
|
// Build search conditions
|
||||||
const searchConditions: any = {}
|
const searchConditions: any = {}
|
||||||
|
|
||||||
@@ -39,51 +64,57 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Testament filter
|
// Testament filter (by orderNum threshold)
|
||||||
let testamentFilter = {}
|
let testamentFilter: any = {}
|
||||||
if (testament === 'old') {
|
if (testament === 'old') {
|
||||||
// Old Testament books (approximate book IDs 1-39)
|
|
||||||
testamentFilter = {
|
testamentFilter = {
|
||||||
chapter: {
|
chapter: {
|
||||||
book: {
|
book: {
|
||||||
orderNum: {
|
versionId: bibleVersion.id,
|
||||||
lte: 39
|
orderNum: { lte: 39 }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (testament === 'new') {
|
} else if (testament === 'new') {
|
||||||
// New Testament books (approximate book IDs 40+)
|
|
||||||
testamentFilter = {
|
testamentFilter = {
|
||||||
chapter: {
|
chapter: {
|
||||||
book: {
|
book: {
|
||||||
orderNum: {
|
versionId: bibleVersion.id,
|
||||||
gt: 39
|
orderNum: { gt: 39 }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Books filter
|
// Books filter
|
||||||
let booksFilter = {}
|
let booksFilter: any = {}
|
||||||
if (books.length > 0) {
|
if (bookKeys.length > 0) {
|
||||||
booksFilter = {
|
booksFilter = {
|
||||||
chapter: {
|
chapter: {
|
||||||
book: {
|
book: {
|
||||||
name: {
|
versionId: bibleVersion.id,
|
||||||
in: books
|
bookKey: { in: bookKeys }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (books.length > 0) {
|
||||||
|
booksFilter = {
|
||||||
|
chapter: {
|
||||||
|
book: {
|
||||||
|
versionId: bibleVersion.id,
|
||||||
|
name: { in: books }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine all filters
|
// Combine all filters
|
||||||
|
const baseVersionScope = { chapter: { book: { versionId: bibleVersion.id } } }
|
||||||
const whereCondition = {
|
const whereCondition = {
|
||||||
...searchConditions,
|
...searchConditions,
|
||||||
|
...baseVersionScope,
|
||||||
...testamentFilter,
|
...testamentFilter,
|
||||||
...booksFilter
|
...booksFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search verses
|
// Search verses
|
||||||
|
|||||||
Reference in New Issue
Block a user