Files
biblical-guide.com/app/api/user/reading-progress/route.ts
Andrei 2a031cdf76 feat: implement reading progress tracking system
Database & API:
- Enhanced ReadingHistory model with versionId field and unique constraint per user/version
- Created /api/user/reading-progress endpoint (GET/POST) for saving and retrieving progress
- Upsert operation ensures one reading position per user per Bible version

Bible Reader Features:
- Auto-save reading position after 2 seconds of inactivity
- Auto-restore last reading position on page load (respects URL parameters)
- Visual progress bar showing completion percentage based on chapters read
- Calculate progress across entire Bible (current chapter / total chapters)
- Client-side only loading to prevent hydration mismatches

Bug Fixes:
- Remove 200 version limit when loading "all versions" - now loads ALL versions
- Fix version selection resetting to favorite when user manually selects different version
- Transform books API response to include chaptersCount property
- Update service worker cache version to force client updates
- Add comprehensive SEO URL logging for debugging 404 issues

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-30 11:42:39 +00:00

113 lines
3.0 KiB
TypeScript

import { NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
import jwt from 'jsonwebtoken'
export const runtime = 'nodejs'
// Get user's reading progress for a specific 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 { searchParams } = new URL(request.url)
const versionId = searchParams.get('versionId')
if (!versionId) {
// Get all reading progress for user
const allProgress = await prisma.readingHistory.findMany({
where: { userId: decoded.userId },
orderBy: { viewedAt: 'desc' }
})
return NextResponse.json({
success: true,
progress: allProgress
})
}
// Get reading progress for specific version
const progress = await prisma.readingHistory.findUnique({
where: {
userId_versionId: {
userId: decoded.userId,
versionId: versionId
}
}
})
return NextResponse.json({
success: true,
progress: progress || null
})
} catch (error) {
console.error('Error getting reading progress:', error)
return NextResponse.json(
{ success: false, error: 'Failed to get reading progress' },
{ status: 500 }
)
}
}
// Save/update user's reading progress
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, bookId, chapterNum, verseNum } = body
if (!versionId || !bookId || chapterNum === undefined) {
return NextResponse.json(
{ success: false, error: 'versionId, bookId, and chapterNum are required' },
{ status: 400 }
)
}
// Upsert reading progress (update if exists, create if not)
const progress = await prisma.readingHistory.upsert({
where: {
userId_versionId: {
userId: decoded.userId,
versionId: versionId
}
},
update: {
bookId,
chapterNum,
verseNum,
viewedAt: new Date()
},
create: {
userId: decoded.userId,
versionId,
bookId,
chapterNum,
verseNum
}
})
return NextResponse.json({
success: true,
message: 'Reading progress saved',
progress
})
} catch (error) {
console.error('Error saving reading progress:', error)
return NextResponse.json(
{ success: false, error: 'Failed to save reading progress' },
{ status: 500 }
)
}
}