removed vivobook files
This commit is contained in:
@@ -1,537 +0,0 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { Routes, Route, useNavigate, useParams, useLocation } from 'react-router-dom';
|
|
||||||
import { Book, ChevronRight, Moon, Sun, LogOut, Search, User } from 'lucide-react';
|
|
||||||
import BookSelector from './components/BookSelector';
|
|
||||||
import ChapterSelector from './components/ChapterSelector';
|
|
||||||
import BibleReader from './components/BibleReader';
|
|
||||||
import FavoritesMenu from './components/FavoritesMenu';
|
|
||||||
import SearchComponent from './components/SearchComponent';
|
|
||||||
import VersionSelector from './components/VersionSelector';
|
|
||||||
import { getBooks } from './services/api';
|
|
||||||
|
|
||||||
interface BookData {
|
|
||||||
books: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
const [books, setBooks] = useState<string[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [user, setUser] = useState<any>(null);
|
|
||||||
const [authAvailable, setAuthAvailable] = useState(false);
|
|
||||||
const [darkMode, setDarkMode] = useState(() => {
|
|
||||||
// Load dark mode preference from localStorage as fallback
|
|
||||||
const saved = localStorage.getItem('darkMode');
|
|
||||||
return saved ? JSON.parse(saved) : false;
|
|
||||||
});
|
|
||||||
const [error, setError] = useState<string>('');
|
|
||||||
const [showSearch, setShowSearch] = useState(false);
|
|
||||||
const [selectedVersion, setSelectedVersion] = useState<string>(''); // Empty means no version selected yet
|
|
||||||
const [versionSelected, setVersionSelected] = useState(false); // Track if version has been chosen
|
|
||||||
const [availableVersions, setAvailableVersions] = useState<any[]>([]);
|
|
||||||
const location = useLocation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
// Debug logging
|
|
||||||
console.log('App component rendered');
|
|
||||||
|
|
||||||
// Extract version from URL path on mount and when location changes
|
|
||||||
useEffect(() => {
|
|
||||||
const pathParts = location.pathname.split('/').filter(Boolean);
|
|
||||||
if (pathParts[0] === 'version' && (pathParts[1] === 'esv' || pathParts[1] === 'nkjv' || pathParts[1] === 'nlt')) {
|
|
||||||
setSelectedVersion(pathParts[1]);
|
|
||||||
} else if (location.pathname === '/') {
|
|
||||||
// At root path, no version is selected
|
|
||||||
setSelectedVersion('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scroll to top when navigating to new pages (unless there's verse auto-navigation active)
|
|
||||||
if (!window.location.hash.startsWith('#verse-')) {
|
|
||||||
window.scrollTo(0, 0);
|
|
||||||
}
|
|
||||||
}, [location.pathname]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('App useEffect triggered');
|
|
||||||
loadVersions();
|
|
||||||
loadBooks();
|
|
||||||
checkAuthStatus();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Load versions when version changes
|
|
||||||
useEffect(() => {
|
|
||||||
loadBooks();
|
|
||||||
|
|
||||||
// Update page title to reflect selected version
|
|
||||||
const versionTitle = selectedVersion ? selectedVersion.toUpperCase() : '';
|
|
||||||
document.title = versionTitle ? `${versionTitle} Bible` : 'The Bible';
|
|
||||||
}, [selectedVersion]);
|
|
||||||
|
|
||||||
// Load user preferences when user changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (user) {
|
|
||||||
loadUserPreferences();
|
|
||||||
}
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
// Load user preferences from database
|
|
||||||
const loadUserPreferences = async () => {
|
|
||||||
if (!user) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/preferences', {
|
|
||||||
credentials: 'include'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const preferences = await response.json();
|
|
||||||
console.log('Loaded user preferences:', preferences);
|
|
||||||
setDarkMode(preferences.dark_mode);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load user preferences:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Save user preferences to database
|
|
||||||
const saveUserPreferences = async (newDarkMode: boolean) => {
|
|
||||||
if (!user) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fetch('/api/preferences', {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
credentials: 'include',
|
|
||||||
body: JSON.stringify({
|
|
||||||
dark_mode: newDarkMode
|
|
||||||
})
|
|
||||||
});
|
|
||||||
console.log('Saved user preferences to database');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to save user preferences:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkAuthStatus = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/auth/user', {
|
|
||||||
credentials: 'include'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const userData = await response.json();
|
|
||||||
setUser(userData.user);
|
|
||||||
setAuthAvailable(true);
|
|
||||||
} else if (response.status === 501) {
|
|
||||||
// Authentication not configured
|
|
||||||
setAuthAvailable(false);
|
|
||||||
} else if (response.status === 401) {
|
|
||||||
// Authentication configured but user not logged in
|
|
||||||
setAuthAvailable(true);
|
|
||||||
setUser(null);
|
|
||||||
} else {
|
|
||||||
// Other error
|
|
||||||
setAuthAvailable(false);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Auth check failed:', error);
|
|
||||||
setAuthAvailable(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLogin = () => {
|
|
||||||
window.location.href = '/auth/login';
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLogout = async () => {
|
|
||||||
try {
|
|
||||||
await fetch('/auth/logout', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include'
|
|
||||||
});
|
|
||||||
setUser(null);
|
|
||||||
// Optionally reload the page to reset any user-specific state
|
|
||||||
window.location.reload();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Logout failed:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle dark mode toggle with hybrid storage
|
|
||||||
const handleDarkModeToggle = async () => {
|
|
||||||
const newDarkMode = !darkMode;
|
|
||||||
setDarkMode(newDarkMode);
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
// Save to database for authenticated users
|
|
||||||
await saveUserPreferences(newDarkMode);
|
|
||||||
} else {
|
|
||||||
// Save to localStorage for non-authenticated users
|
|
||||||
localStorage.setItem('darkMode', JSON.stringify(newDarkMode));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Apply dark mode
|
|
||||||
if (darkMode) {
|
|
||||||
document.documentElement.classList.add('dark');
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.remove('dark');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save to localStorage as backup (for non-authenticated users)
|
|
||||||
if (!user) {
|
|
||||||
localStorage.setItem('darkMode', JSON.stringify(darkMode));
|
|
||||||
}
|
|
||||||
}, [darkMode, user]);
|
|
||||||
|
|
||||||
// Helper function to format book names for display
|
|
||||||
const formatBookName = (bookName: string): string => {
|
|
||||||
// Handle both formats: numbered (01_Genesis) and regular (Genesis)
|
|
||||||
const cleanName = bookName.replace(/^\d+_/, ''); // Remove leading number and underscore
|
|
||||||
return cleanName.replace(/_/g, ' '); // Replace underscores with spaces
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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: string) => 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 () => {
|
|
||||||
try {
|
|
||||||
console.log('Loading books from API for version:', selectedVersion);
|
|
||||||
const response = await fetch(`/books?version=${selectedVersion}`);
|
|
||||||
const data: BookData = await response.json();
|
|
||||||
console.log('Books loaded:', data);
|
|
||||||
setBooks(data.books);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load books:', error);
|
|
||||||
setError('Failed to load books. Please check the console for details.');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadVersions = async () => {
|
|
||||||
try {
|
|
||||||
console.log('Loading available versions...');
|
|
||||||
const response = await fetch('/versions');
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
setAvailableVersions(data.versions);
|
|
||||||
console.log('Versions loaded:', data.versions);
|
|
||||||
} else {
|
|
||||||
console.error('Failed to load versions');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load versions:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get current navigation info from URL
|
|
||||||
const getCurrentNavInfo = () => {
|
|
||||||
const pathParts = location.pathname.split('/').filter(Boolean);
|
|
||||||
|
|
||||||
if (pathParts.length < 3 || pathParts[0] !== 'version' || pathParts[2] !== 'book') {
|
|
||||||
return { currentBook: null, currentChapter: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentBook = pathParts[3]; // book name from /version/:versionId/book/:bookName
|
|
||||||
const currentChapter = pathParts[4] === 'chapter' && pathParts[5] ? pathParts[5] : null;
|
|
||||||
return { currentBook, currentChapter };
|
|
||||||
};
|
|
||||||
|
|
||||||
const { currentBook, currentChapter } = getCurrentNavInfo();
|
|
||||||
|
|
||||||
// Component for the version selection page (root)
|
|
||||||
const VersionPage = () => {
|
|
||||||
const handleVersionSelect = (version: 'esv' | 'nkjv' | 'nlt') => {
|
|
||||||
navigate(`/version/${version}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<VersionSelector
|
|
||||||
onVersionSelect={handleVersionSelect}
|
|
||||||
onSearchClick={() => setShowSearch(true)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Component for book list page (version-specific)
|
|
||||||
const BookListPage = () => {
|
|
||||||
const { versionId } = useParams<{ versionId: string }>();
|
|
||||||
|
|
||||||
const handleBookSelect = (book: string) => {
|
|
||||||
const urlName = getBookUrlName(book);
|
|
||||||
navigate(`/version/${versionId}/book/${urlName}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBack = () => {
|
|
||||||
navigate('/');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFavoriteChange = () => {
|
|
||||||
// This will trigger a re-render of the FavoritesMenu
|
|
||||||
setUser((prev: any) => ({ ...prev }));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BookSelector
|
|
||||||
books={books}
|
|
||||||
onBookSelect={handleBookSelect}
|
|
||||||
formatBookName={formatBookName}
|
|
||||||
user={user}
|
|
||||||
onFavoriteChange={handleFavoriteChange}
|
|
||||||
version={versionId}
|
|
||||||
onBack={handleBack}
|
|
||||||
onSearchClick={() => setShowSearch(true)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Component for book chapters page
|
|
||||||
const BookPage = () => {
|
|
||||||
const { versionId, bookName } = useParams<{ versionId: string, bookName: string }>();
|
|
||||||
const actualBookName = bookName ? getBookFromUrl(bookName) : '';
|
|
||||||
|
|
||||||
const handleChapterSelect = (chapter: string) => {
|
|
||||||
navigate(`/version/${versionId}/book/${bookName}/chapter/${chapter}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBack = () => {
|
|
||||||
navigate(`/version/${versionId}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFavoriteChange = () => {
|
|
||||||
// This will trigger a re-render of the FavoritesMenu
|
|
||||||
setUser((prev: any) => ({ ...prev }));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!bookName || !actualBookName || !books.includes(actualBookName)) {
|
|
||||||
return <div>Book not found</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ChapterSelector
|
|
||||||
book={actualBookName}
|
|
||||||
onChapterSelect={handleChapterSelect}
|
|
||||||
onBack={handleBack}
|
|
||||||
formatBookName={formatBookName}
|
|
||||||
user={user}
|
|
||||||
onFavoriteChange={handleFavoriteChange}
|
|
||||||
version={versionId}
|
|
||||||
onSearchClick={() => setShowSearch(true)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Component for chapter reading page
|
|
||||||
const ChapterPage = () => {
|
|
||||||
const { versionId, bookName, chapterNumber } = useParams<{ versionId: string, bookName: string, chapterNumber: string }>();
|
|
||||||
const actualBookName = bookName ? getBookFromUrl(bookName) : '';
|
|
||||||
|
|
||||||
const handleBack = () => {
|
|
||||||
navigate(`/version/${versionId}/book/${bookName}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFavoriteChange = () => {
|
|
||||||
// This will trigger a re-render of the FavoritesMenu
|
|
||||||
setUser((prev: any) => ({ ...prev }));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!bookName || !chapterNumber || !actualBookName) {
|
|
||||||
return <div>Chapter not found</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BibleReader
|
|
||||||
book={actualBookName}
|
|
||||||
chapter={chapterNumber}
|
|
||||||
onBack={handleBack}
|
|
||||||
formatBookName={formatBookName}
|
|
||||||
user={user}
|
|
||||||
onFavoriteChange={handleFavoriteChange}
|
|
||||||
version={selectedVersion}
|
|
||||||
onSearchClick={() => setShowSearch(true)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading || !books) {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen flex items-center justify-center">
|
|
||||||
<div className="text-center">
|
|
||||||
<Book className="mx-auto h-12 w-12 text-blue-600 animate-pulse" />
|
|
||||||
<p className="mt-4 text-lg">Loading The Bible...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
|
||||||
{/* Header */}
|
|
||||||
<header className="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700">
|
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
||||||
<div className="flex justify-between items-center h-16">
|
|
||||||
<div className="flex items-center space-x-2 sm:space-x-4">
|
|
||||||
{!selectedVersion ? (
|
|
||||||
<Book className="h-6 w-6 sm:h-8 sm:w-8 text-blue-600" />
|
|
||||||
) : (
|
|
||||||
<div className="w-6 h-6 sm:w-8 sm:h-8 flex items-center justify-center">
|
|
||||||
<img
|
|
||||||
src={`/logos/${selectedVersion}-logo.png`}
|
|
||||||
alt={`${selectedVersion.toUpperCase()} Logo`}
|
|
||||||
className="max-w-full max-h-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
// Clear version selection and reload page to get fresh state
|
|
||||||
window.location.href = '/';
|
|
||||||
}}
|
|
||||||
className="text-lg sm:text-xl font-bold text-gray-900 dark:text-gray-100 hover:text-blue-600 dark:hover:text-blue-400"
|
|
||||||
>
|
|
||||||
The Bible
|
|
||||||
</button>
|
|
||||||
{/* Version Selector */}
|
|
||||||
{selectedVersion ? (
|
|
||||||
<select
|
|
||||||
value={selectedVersion}
|
|
||||||
onChange={(e) => {
|
|
||||||
const currentPath = location.pathname;
|
|
||||||
const newVersion = e.target.value;
|
|
||||||
// Replace current version in path with new version to keep same page
|
|
||||||
const newPath = currentPath.replace(`/version/${selectedVersion}`, `/version/${newVersion}`);
|
|
||||||
navigate(newPath);
|
|
||||||
}}
|
|
||||||
className="text-xs text-gray-600 dark:text-gray-400 bg-transparent border-none focus:outline-none cursor-pointer hover:text-blue-600 dark:hover:text-blue-400"
|
|
||||||
>
|
|
||||||
{availableVersions.map((version) => (
|
|
||||||
<option key={version.id} value={version.id}>
|
|
||||||
{version.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* User Authentication & Dark Mode */}
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
{/* Authentication Button */}
|
|
||||||
{authAvailable && (
|
|
||||||
<div>
|
|
||||||
{user ? (
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<span className="hidden sm:inline text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
{user.name || user.email}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
onClick={handleLogout}
|
|
||||||
className="text-sm px-3 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<span className="hidden sm:inline">Logout</span>
|
|
||||||
<LogOut className="sm:hidden h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
onClick={handleLogin}
|
|
||||||
className="text-sm px-3 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<span className="hidden sm:inline">Login</span>
|
|
||||||
<User className="sm:hidden h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Dark Mode Toggle */}
|
|
||||||
<button
|
|
||||||
onClick={handleDarkModeToggle}
|
|
||||||
className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
{darkMode ? (
|
|
||||||
<Sun className="h-4 w-4 sm:h-5 sm:w-5 text-yellow-500" />
|
|
||||||
) : (
|
|
||||||
<Moon className="h-4 w-4 sm:h-5 sm:w-5 text-gray-600" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{/* Favorites Menu - Positioned below header for authenticated users */}
|
|
||||||
{user && (
|
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2">
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<FavoritesMenu
|
|
||||||
user={user}
|
|
||||||
formatBookName={formatBookName}
|
|
||||||
getBookUrlName={getBookUrlName}
|
|
||||||
setSelectedVersion={setSelectedVersion}
|
|
||||||
onFavoriteChange={() => setUser((prev: any) => ({ ...prev }))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Search Modal */}
|
|
||||||
{showSearch && (
|
|
||||||
<SearchComponent
|
|
||||||
formatBookName={formatBookName}
|
|
||||||
getBookUrlName={getBookUrlName}
|
|
||||||
books={books}
|
|
||||||
selectedVersion={selectedVersion}
|
|
||||||
onClose={() => setShowSearch(false)}
|
|
||||||
isModal={true}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Main Content */}
|
|
||||||
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
||||||
<Routes>
|
|
||||||
<Route path="/" element={<VersionPage />} />
|
|
||||||
<Route path="/version/:versionId" element={<BookListPage />} />
|
|
||||||
<Route path="/version/:versionId/book/:bookName" element={<BookPage />} />
|
|
||||||
<Route path="/version/:versionId/book/:bookName/chapter/:chapterNumber" element={<ChapterPage />} />
|
|
||||||
<Route path="/search" element={
|
|
||||||
<SearchComponent
|
|
||||||
formatBookName={formatBookName}
|
|
||||||
getBookUrlName={getBookUrlName}
|
|
||||||
books={books}
|
|
||||||
selectedVersion={selectedVersion}
|
|
||||||
isModal={false}
|
|
||||||
/>
|
|
||||||
} />
|
|
||||||
</Routes>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
Reference in New Issue
Block a user