diff --git a/app/[locale]/bible/reader.tsx b/app/[locale]/bible/reader.tsx index f6f3003..1b0e2e5 100644 --- a/app/[locale]/bible/reader.tsx +++ b/app/[locale]/bible/reader.tsx @@ -77,7 +77,9 @@ import { CloudDownload, WifiOff, Storage, - MoreVert + MoreVert, + Star, + StarBorder } from '@mui/icons-material' interface BibleVerse { @@ -329,32 +331,65 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha // Fetch versions based on showAllVersions state and locale useEffect(() => { - setVersionsLoading(true) - const url = showAllVersions - ? '/api/bible/versions?all=true&limit=200' // Limit to first 200 for performance - : `/api/bible/versions?language=${locale}` + const loadVersions = async () => { + setVersionsLoading(true) + const url = showAllVersions + ? '/api/bible/versions?all=true&limit=200' // Limit to first 200 for performance + : `/api/bible/versions?language=${locale}` + + try { + const res = await fetch(url) + const data = await res.json() - fetch(url) - .then(res => res.json()) - .then(data => { if (data.success && data.versions) { setVersions(data.versions) + // Keep current selection if it exists in new list, otherwise select default/first const currentVersionExists = data.versions.some((v: BibleVersion) => v.id === selectedVersion) if (!currentVersionExists || !selectedVersion) { - const defaultVersion = data.versions.find((v: BibleVersion) => v.isDefault) || data.versions[0] - if (defaultVersion) { - setSelectedVersion(defaultVersion.id) + // Try to load user's favorite version first + let versionToSelect = null + + if (user) { + const token = localStorage.getItem('authToken') + if (token) { + try { + const favoriteRes = await fetch('/api/user/favorite-version', { + headers: { 'Authorization': `Bearer ${token}` } + }) + const favoriteData = await favoriteRes.json() + + if (favoriteData.success && favoriteData.favoriteBibleVersion) { + const favoriteVersion = data.versions.find((v: BibleVersion) => v.id === favoriteData.favoriteBibleVersion) + if (favoriteVersion) { + versionToSelect = favoriteVersion + } + } + } catch (error) { + console.error('Error loading favorite version:', error) + } + } + } + + // Fall back to default version or first version + if (!versionToSelect) { + versionToSelect = data.versions.find((v: BibleVersion) => v.isDefault) || data.versions[0] + } + + if (versionToSelect) { + setSelectedVersion(versionToSelect.id) } } } setVersionsLoading(false) - }) - .catch(err => { + } catch (err) { console.error('Error fetching versions:', err) setVersionsLoading(false) - }) - }, [locale, showAllVersions, selectedVersion]) + } + } + + loadVersions() + }, [locale, showAllVersions, selectedVersion, user]) // Handle URL parameters for bookmark navigation useEffect(() => { @@ -1000,6 +1035,40 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha } } + const handleSetFavoriteVersion = async () => { + if (!user) { + router.push(`/${locale}/login?redirect=${encodeURIComponent(`/${locale}/bible?version=${selectedVersion}&book=${selectedBook}&chapter=${selectedChapter}`)}`) + return + } + + const token = localStorage.getItem('authToken') + if (!token) return + + try { + const response = await fetch('/api/user/favorite-version', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ versionId: selectedVersion }) + }) + + const data = await response.json() + if (data.success) { + setCopyFeedback({ + open: true, + message: 'This version has been set as your default' + }) + } else { + alert('Failed to set favorite version') + } + } catch (error) { + console.error('Error setting favorite version:', error) + alert('Failed to set favorite version') + } + } + const getThemeStyles = () => { switch (preferences.theme) { case 'dark': @@ -1190,6 +1259,15 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha style: { maxHeight: 400 } }} /> + + + + + ([]) + const [favoriteBibleVersion, setFavoriteBibleVersion] = useState(null) + const [loadingVersions, setLoadingVersions] = useState(true) + + useEffect(() => { + const loadBiblePreferences = async () => { + try { + // Load available versions (no limit to ensure we get all versions including the favorite) + const versionsRes = await fetch('/api/bible/versions?all=true') + const versionsData = await versionsRes.json() + if (versionsData.success) { + console.log('[Settings] Loaded versions:', versionsData.versions.length) + setBibleVersions(versionsData.versions) + } + + // Load user's favorite version + const token = localStorage.getItem('authToken') + if (token) { + const favoriteRes = await fetch('/api/user/favorite-version', { + headers: { 'Authorization': `Bearer ${token}` } + }) + const favoriteData = await favoriteRes.json() + console.log('[Settings] Favorite version data:', favoriteData) + if (favoriteData.success && favoriteData.favoriteBibleVersion) { + console.log('[Settings] Setting favorite version:', favoriteData.favoriteBibleVersion) + console.log('[Settings] Type of favorite version:', typeof favoriteData.favoriteBibleVersion) + setFavoriteBibleVersion(favoriteData.favoriteBibleVersion) + } + } + } catch (error) { + console.error('Error loading Bible preferences:', error) + } finally { + setLoadingVersions(false) + } + } + + if (user) { + loadBiblePreferences() + } else { + setLoadingVersions(false) + } + }, [user]) const handleSettingChange = (setting: string, value: any) => { setSettings(prev => ({ @@ -51,6 +95,33 @@ export default function SettingsPage() { })) } + const handleFavoriteVersionChange = async (versionId: string) => { + const token = localStorage.getItem('authToken') + if (!token) return + + try { + const response = await fetch('/api/user/favorite-version', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ versionId }) + }) + + const data = await response.json() + if (data.success) { + setFavoriteBibleVersion(versionId) + setMessage('Favorite Bible version updated successfully') + } else { + setMessage('Failed to update favorite version') + } + } catch (error) { + console.error('Error updating favorite version:', error) + setMessage('Failed to update favorite version') + } + } + const handleSave = async () => { try { // TODO: Implement settings update API @@ -176,6 +247,69 @@ export default function SettingsPage() { + {/* Bible Preferences */} + + + + + + + Bible Preferences + + + + + Select your preferred Bible version. This will be loaded automatically when you open the Bible reader. + + + {loadingVersions ? ( + + + + ) : ( + + + Favorite Bible Version + + + {favoriteBibleVersion && ( + + ✓ This version will be loaded when you open the Bible reader + + )} + + )} + + + + {/* Security Settings */} diff --git a/app/api/user/favorite-version/route.ts b/app/api/user/favorite-version/route.ts new file mode 100644 index 0000000..f0ac2e5 --- /dev/null +++ b/app/api/user/favorite-version/route.ts @@ -0,0 +1,85 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/db' +import jwt from 'jsonwebtoken' + +export const runtime = 'nodejs' + +// Get user's favorite Bible version +export async function GET(request: Request) { + try { + const authHeader = request.headers.get('authorization') + if (!authHeader?.startsWith('Bearer ')) { + return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 }) + } + + const token = authHeader.substring(7) + const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: string } + + const user = await prisma.user.findUnique({ + where: { id: decoded.userId }, + select: { favoriteBibleVersion: true } + }) + + if (!user) { + return NextResponse.json({ success: false, error: 'User not found' }, { status: 404 }) + } + + return NextResponse.json({ + success: true, + favoriteBibleVersion: user.favoriteBibleVersion + }) + } catch (error) { + console.error('Error getting favorite version:', error) + return NextResponse.json( + { success: false, error: 'Failed to get favorite version' }, + { status: 500 } + ) + } +} + +// Set user's favorite Bible version +export async function POST(request: Request) { + try { + const authHeader = request.headers.get('authorization') + if (!authHeader?.startsWith('Bearer ')) { + return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 }) + } + + const token = authHeader.substring(7) + const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: string } + + const body = await request.json() + const { versionId } = body + + if (!versionId) { + return NextResponse.json({ success: false, error: 'Version ID is required' }, { status: 400 }) + } + + // Verify that the version exists + const version = await prisma.bibleVersion.findUnique({ + where: { id: versionId } + }) + + if (!version) { + return NextResponse.json({ success: false, error: 'Bible version not found' }, { status: 404 }) + } + + // Update user's favorite version + await prisma.user.update({ + where: { id: decoded.userId }, + data: { favoriteBibleVersion: versionId } + }) + + return NextResponse.json({ + success: true, + message: 'Favorite Bible version updated', + favoriteBibleVersion: versionId + }) + } catch (error) { + console.error('Error setting favorite version:', error) + return NextResponse.json( + { success: false, error: 'Failed to set favorite version' }, + { status: 500 } + ) + } +} \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5acd01c..1a4ec24 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,16 +8,17 @@ datasource db { } model User { - id String @id @default(uuid()) - email String @unique - passwordHash String - name String? - role String @default("user") // "user", "admin", "moderator" - theme String @default("light") - fontSize String @default("medium") - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - lastLoginAt DateTime? + id String @id @default(uuid()) + email String @unique + passwordHash String + name String? + role String @default("user") // "user", "admin", "moderator" + theme String @default("light") + fontSize String @default("medium") + favoriteBibleVersion String? // User's preferred Bible version ID + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + lastLoginAt DateTime? sessions Session[] bookmarks Bookmark[]