diff --git a/frontend/src/App-VivoBook.tsx b/frontend/src/App-VivoBook.tsx deleted file mode 100644 index ce44c5a4..00000000 --- a/frontend/src/App-VivoBook.tsx +++ /dev/null @@ -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([]); - const [loading, setLoading] = useState(true); - const [user, setUser] = useState(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(''); - const [showSearch, setShowSearch] = useState(false); - const [selectedVersion, setSelectedVersion] = useState(''); // Empty means no version selected yet - const [versionSelected, setVersionSelected] = useState(false); // Track if version has been chosen - const [availableVersions, setAvailableVersions] = useState([]); - 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 ( - 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 ( - 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
Book not found
; - } - - return ( - 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
Chapter not found
; - } - - return ( - setShowSearch(true)} - /> - ); - }; - - if (loading || !books) { - return ( -
-
- -

Loading The Bible...

-
-
- ); - } - - return ( -
- {/* Header */} -
-
-
-
- {!selectedVersion ? ( - - ) : ( -
- {`${selectedVersion.toUpperCase()} -
- )} -
- - {/* Version Selector */} - {selectedVersion ? ( - - ) : null} -
-
- - - - - - {/* User Authentication & Dark Mode */} -
- {/* Authentication Button */} - {authAvailable && ( -
- {user ? ( -
- - {user.name || user.email} - - -
- ) : ( - - )} -
- )} - - {/* Dark Mode Toggle */} - -
-
-
-
- - {/* Favorites Menu - Positioned below header for authenticated users */} - {user && ( -
-
- setUser((prev: any) => ({ ...prev }))} - /> -
-
- )} - - {/* Search Modal */} - {showSearch && ( - setShowSearch(false)} - isModal={true} - /> - )} - - {/* Main Content */} -
- - } /> - } /> - } /> - } /> - - } /> - -
-
- ); -} - -export default App;