From a1b92be2a50c8d0aed08b46674acacff1741ff55 Mon Sep 17 00:00:00 2001 From: andupetcu <47487320+andupetcu@users.noreply.github.com> Date: Sat, 20 Sep 2025 18:10:50 +0300 Subject: [PATCH] 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. --- app/[locale]/search/page.tsx | 70 ++++++++++++++++++++-------------- app/api/search/verses/route.ts | 63 ++++++++++++++++++++++-------- 2 files changed, 89 insertions(+), 44 deletions(-) diff --git a/app/[locale]/search/page.tsx b/app/[locale]/search/page.tsx index 634de00..cabf100 100644 --- a/app/[locale]/search/page.tsx +++ b/app/[locale]/search/page.tsx @@ -35,7 +35,6 @@ import { } from '@mui/icons-material' import { useState, useEffect } from 'react' import { useTranslations, useLocale } from 'next-intl' -import { translateBookName } from '@/lib/book-translations' interface SearchResult { id: string @@ -48,10 +47,18 @@ interface SearchResult { interface SearchFilter { testament: 'all' | 'old' | 'new' - books: string[] + bookKeys: string[] exactMatch: boolean } +interface BookOption { + id: string + name: string + bookKey: string + orderNum: number + testament: string +} + export default function SearchPage() { const theme = useTheme() const t = useTranslations('pages.search') @@ -60,23 +67,11 @@ export default function SearchPage() { const [results, setResults] = useState([]) const [loading, setLoading] = useState(false) const [searchHistory, setSearchHistory] = useState([]) - const [filters, setFilters] = useState({ - testament: 'all', - books: [], - exactMatch: false, - }) + const [filters, setFilters] = useState({ testament: 'all', bookKeys: [], exactMatch: false }) + const [booksData, setBooksData] = useState([]) - 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 oldTestamentBooks = booksData.filter(b => b.orderNum <= 39) + const newTestamentBooks = booksData.filter(b => b.orderNum > 39) 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 () => { if (!searchQuery.trim()) return @@ -103,7 +115,8 @@ export default function SearchPage() { q: searchQuery, testament: filters.testament, exactMatch: filters.exactMatch.toString(), - books: filters.books.join(','), + bookKeys: filters.bookKeys.join(','), + locale, }) const response = await fetch(`/api/search/verses?${params}`) @@ -228,15 +241,16 @@ export default function SearchPage() { .concat(filters.testament === 'new' || filters.testament === 'all' ? newTestamentBooks : []) .map((book) => ( { - const newBooks = filters.books.includes(book) - ? filters.books.filter(b => b !== book) - : [...filters.books, book] - setFilters({ ...filters, books: newBooks }) + const exists = filters.bookKeys.includes(book.bookKey) + const newBookKeys = exists + ? filters.bookKeys.filter(b => b !== book.bookKey) + : [...filters.bookKeys, book.bookKey] + setFilters({ ...filters, bookKeys: newBookKeys }) }} sx={{ mb: 0.5, mr: 0.5 }} /> @@ -330,10 +344,10 @@ export default function SearchPage() { - {filters.books.length > 0 && ( + {filters.bookKeys.length > 0 && ( - {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(', ') })} )} diff --git a/app/api/search/verses/route.ts b/app/api/search/verses/route.ts index ed601ee..21b497f 100644 --- a/app/api/search/verses/route.ts +++ b/app/api/search/verses/route.ts @@ -10,6 +10,9 @@ export async function GET(request: NextRequest) { const testament = searchParams.get('testament') || 'all' const exactMatch = searchParams.get('exactMatch') === 'true' 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) { 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 const searchConditions: any = {} @@ -39,51 +64,57 @@ export async function GET(request: NextRequest) { } } - // Testament filter - let testamentFilter = {} + // Testament filter (by orderNum threshold) + let testamentFilter: any = {} if (testament === 'old') { - // Old Testament books (approximate book IDs 1-39) testamentFilter = { chapter: { book: { - orderNum: { - lte: 39 - } + versionId: bibleVersion.id, + orderNum: { lte: 39 } } } } } else if (testament === 'new') { - // New Testament books (approximate book IDs 40+) testamentFilter = { chapter: { book: { - orderNum: { - gt: 39 - } + versionId: bibleVersion.id, + orderNum: { gt: 39 } } } } } // Books filter - let booksFilter = {} - if (books.length > 0) { + let booksFilter: any = {} + if (bookKeys.length > 0) { booksFilter = { chapter: { book: { - name: { - in: books - } + versionId: bibleVersion.id, + bookKey: { in: bookKeys } + } + } + } + } else if (books.length > 0) { + booksFilter = { + chapter: { + book: { + versionId: bibleVersion.id, + name: { in: books } } } } } // Combine all filters + const baseVersionScope = { chapter: { book: { versionId: bibleVersion.id } } } const whereCondition = { ...searchConditions, + ...baseVersionScope, ...testamentFilter, - ...booksFilter + ...booksFilter, } // Search verses