feat: implement SEO-friendly URLs for Bible reader
- Add dynamic route structure /[locale]/bible/[version]/[book]/[chapter] - Convert UUID-based URLs to readable format (e.g., /en/bible/eng-kjv/genesis/1) - Implement automatic redirects from old URLs to new SEO-friendly format - Add SEO metadata generation with proper titles, descriptions, and OpenGraph tags - Create API endpoint for URL conversion between formats - Update navigation in search results, bookmarks, and internal links - Fix PWA manifest icons to correct dimensions (192x192, 512x512) - Resolve JavaScript parameter passing issues between server and client components - Maintain backward compatibility with existing bookmark and search functionality Benefits: - Improved SEO with descriptive URLs - Better user experience with readable URLs - Enhanced social media sharing - Maintained full backward compatibility
This commit is contained in:
134
app/[locale]/bible/[version]/[book]/[chapter]/page.tsx
Normal file
134
app/[locale]/bible/[version]/[book]/[chapter]/page.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { Suspense } from 'react'
|
||||
import { notFound } from 'next/navigation'
|
||||
import BibleReader from '../../../reader'
|
||||
import { prisma } from '@/lib/db'
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{
|
||||
locale: string
|
||||
version: string
|
||||
book: string
|
||||
chapter: string
|
||||
}>
|
||||
}
|
||||
|
||||
// Helper function to convert readable names to IDs
|
||||
async function getResourceIds(versionSlug: string, bookSlug: string, chapterNum: string) {
|
||||
try {
|
||||
// Find version by abbreviation (slug)
|
||||
const version = await prisma.bibleVersion.findFirst({
|
||||
where: {
|
||||
abbreviation: {
|
||||
equals: versionSlug,
|
||||
mode: 'insensitive'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!version) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Find book by bookKey (slug) within this version
|
||||
const book = await prisma.bibleBook.findFirst({
|
||||
where: {
|
||||
versionId: version.id,
|
||||
bookKey: {
|
||||
equals: bookSlug,
|
||||
mode: 'insensitive'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!book) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Validate chapter number
|
||||
const chapter = parseInt(chapterNum)
|
||||
if (isNaN(chapter) || chapter < 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Check if chapter exists
|
||||
const chapterRecord = await prisma.bibleChapter.findFirst({
|
||||
where: {
|
||||
bookId: book.id,
|
||||
chapterNum: chapter
|
||||
}
|
||||
})
|
||||
|
||||
if (!chapterRecord) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
versionId: version.id,
|
||||
bookId: book.id,
|
||||
chapter: chapter,
|
||||
version: version,
|
||||
book: book
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error resolving resource IDs:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Generate metadata for SEO
|
||||
export async function generateMetadata({ params }: PageProps) {
|
||||
const { version, book, chapter } = await params
|
||||
const resources = await getResourceIds(version, book, chapter)
|
||||
|
||||
if (!resources) {
|
||||
return {
|
||||
title: 'Bible Chapter Not Found',
|
||||
description: 'The requested Bible chapter could not be found.'
|
||||
}
|
||||
}
|
||||
|
||||
const title = `${resources.book.name} ${chapter} - ${resources.version.name} | Biblical Guide`
|
||||
const description = `Read ${resources.book.name} chapter ${chapter} in the ${resources.version.name} translation. Free online Bible study with AI chat and prayer community.`
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
type: 'article',
|
||||
url: `https://biblical-guide.com/${(await params).locale}/bible/${version}/${book}/${chapter}`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary',
|
||||
title,
|
||||
description,
|
||||
},
|
||||
alternates: {
|
||||
canonical: `https://biblical-guide.com/${(await params).locale}/bible/${version}/${book}/${chapter}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default async function BibleChapterPage({ params }: PageProps) {
|
||||
const { version, book, chapter } = await params
|
||||
const resources = await getResourceIds(version, book, chapter)
|
||||
|
||||
if (!resources) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
// Pass the parameters as props instead of URLSearchParams
|
||||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<BibleReader
|
||||
initialVersion={resources.versionId}
|
||||
initialBook={resources.bookId}
|
||||
initialChapter={chapter}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
// Note: generateStaticParams removed for now to avoid complexity
|
||||
// Can be added later for better performance with popular Bible chapters
|
||||
Reference in New Issue
Block a user