Add clean URL support with book names like /book/Genesis/chapter/1

This commit is contained in:
Ryderjj89
2025-09-13 16:09:57 -04:00
parent 61280d6181
commit 0513f821bd

View File

@@ -11,21 +11,24 @@ interface BookData {
} }
// Component for the home page // Component for the home page
function HomePage({ books, formatBookName }: { books: string[], formatBookName: (name: string) => string }) { function HomePage({ books, formatBookName, getBookUrlName }: { books: string[], formatBookName: (name: string) => string, getBookUrlName: (name: string) => string }) {
const navigate = useNavigate(); const navigate = useNavigate();
const handleBookSelect = (book: string) => { const handleBookSelect = (book: string) => {
navigate(`/book/${book}`); const urlName = getBookUrlName(book);
navigate(`/book/${urlName}`);
}; };
return <BookSelector books={books} onBookSelect={handleBookSelect} formatBookName={formatBookName} />; return <BookSelector books={books} onBookSelect={handleBookSelect} formatBookName={formatBookName} />;
} }
// Component for book chapters page // Component for book chapters page
function BookPage({ books, formatBookName }: { books: string[], formatBookName: (name: string) => string }) { function BookPage({ books, formatBookName, getBookFromUrl }: { books: string[], formatBookName: (name: string) => string, getBookFromUrl: (urlName: string) => string }) {
const { bookName } = useParams<{ bookName: string }>(); const { bookName } = useParams<{ bookName: string }>();
const navigate = useNavigate(); const navigate = useNavigate();
const actualBookName = bookName ? getBookFromUrl(bookName) : '';
const handleChapterSelect = (chapter: string) => { const handleChapterSelect = (chapter: string) => {
navigate(`/book/${bookName}/chapter/${chapter}`); navigate(`/book/${bookName}/chapter/${chapter}`);
}; };
@@ -34,13 +37,13 @@ function BookPage({ books, formatBookName }: { books: string[], formatBookName:
navigate('/'); navigate('/');
}; };
if (!bookName || !books.includes(bookName)) { if (!bookName || !actualBookName || !books.includes(actualBookName)) {
return <div>Book not found</div>; return <div>Book not found</div>;
} }
return ( return (
<ChapterSelector <ChapterSelector
book={bookName} book={actualBookName}
onChapterSelect={handleChapterSelect} onChapterSelect={handleChapterSelect}
onBack={handleBack} onBack={handleBack}
formatBookName={formatBookName} formatBookName={formatBookName}
@@ -49,21 +52,23 @@ function BookPage({ books, formatBookName }: { books: string[], formatBookName:
} }
// Component for chapter reading page // Component for chapter reading page
function ChapterPage({ formatBookName }: { formatBookName: (name: string) => string }) { function ChapterPage({ formatBookName, getBookFromUrl }: { formatBookName: (name: string) => string, getBookFromUrl: (urlName: string) => string }) {
const { bookName, chapterNumber } = useParams<{ bookName: string, chapterNumber: string }>(); const { bookName, chapterNumber } = useParams<{ bookName: string, chapterNumber: string }>();
const navigate = useNavigate(); const navigate = useNavigate();
const actualBookName = bookName ? getBookFromUrl(bookName) : '';
const handleBack = () => { const handleBack = () => {
navigate(`/book/${bookName}`); navigate(`/book/${bookName}`);
}; };
if (!bookName || !chapterNumber) { if (!bookName || !chapterNumber || !actualBookName) {
return <div>Chapter not found</div>; return <div>Chapter not found</div>;
} }
return ( return (
<BibleReader <BibleReader
book={bookName} book={actualBookName}
chapter={chapterNumber} chapter={chapterNumber}
onBack={handleBack} onBack={handleBack}
formatBookName={formatBookName} formatBookName={formatBookName}
@@ -107,6 +112,26 @@ function App() {
return bookName.replace(/^\d+_/, '').replace(/_/g, ' '); return bookName.replace(/^\d+_/, '').replace(/_/g, ' ');
}; };
// Helper function to convert display name back to file name
const getBookFileName = (displayName: string): string => {
// Find the book that matches the display name
const book = books.find(b => formatBookName(b) === displayName);
return book || displayName;
};
// Helper function to convert book file name to URL-safe name
const getBookUrlName = (bookName: string): string => {
// Remove leading numbers and convert spaces to underscores for URL
return bookName.replace(/^\d+_/, '').replace(/ /g, '_');
};
// Helper function to convert URL name back to file name
const getBookFromUrl = (urlName: string): string => {
// Convert URL name back to display name, then find the file name
const displayName = urlName.replace(/_/g, ' ');
return getBookFileName(displayName);
};
const loadBooks = async () => { const loadBooks = async () => {
try { try {
console.log('Loading books from API...'); console.log('Loading books from API...');
@@ -218,9 +243,9 @@ function App() {
{/* Main Content */} {/* Main Content */}
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<Routes> <Routes>
<Route path="/" element={<HomePage books={books} formatBookName={formatBookName} />} /> <Route path="/" element={<HomePage books={books} formatBookName={formatBookName} getBookUrlName={getBookUrlName} />} />
<Route path="/book/:bookName" element={<BookPage books={books} formatBookName={formatBookName} />} /> <Route path="/book/:bookName" element={<BookPage books={books} formatBookName={formatBookName} getBookFromUrl={getBookFromUrl} />} />
<Route path="/book/:bookName/chapter/:chapterNumber" element={<ChapterPage formatBookName={formatBookName} />} /> <Route path="/book/:bookName/chapter/:chapterNumber" element={<ChapterPage formatBookName={formatBookName} getBookFromUrl={getBookFromUrl} />} />
</Routes> </Routes>
</main> </main>
</div> </div>