diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 4be0dc5d..33145418 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -11,72 +11,6 @@ interface BookData {
books: string[];
}
-// Component for the home page
-function HomePage({ books, formatBookName, getBookUrlName }: { books: string[], formatBookName: (name: string) => string, getBookUrlName: (name: string) => string }) {
- const navigate = useNavigate();
-
- const handleBookSelect = (book: string) => {
- const urlName = getBookUrlName(book);
- navigate(`/book/${urlName}`);
- };
-
- return ;
-}
-
-// Component for book chapters page
-function BookPage({ books, formatBookName, getBookFromUrl }: { books: string[], formatBookName: (name: string) => string, getBookFromUrl: (urlName: string) => string }) {
- const { bookName } = useParams<{ bookName: string }>();
- const navigate = useNavigate();
-
- const actualBookName = bookName ? getBookFromUrl(bookName) : '';
-
- const handleChapterSelect = (chapter: string) => {
- navigate(`/book/${bookName}/chapter/${chapter}`);
- };
-
- const handleBack = () => {
- navigate('/');
- };
-
- if (!bookName || !actualBookName || !books.includes(actualBookName)) {
- return
Book not found
;
- }
-
- return (
-
- );
-}
-
-// Component for chapter reading page
-function ChapterPage({ formatBookName, getBookFromUrl }: { formatBookName: (name: string) => string, getBookFromUrl: (urlName: string) => string }) {
- const { bookName, chapterNumber } = useParams<{ bookName: string, chapterNumber: string }>();
- const navigate = useNavigate();
-
- const actualBookName = bookName ? getBookFromUrl(bookName) : '';
-
- const handleBack = () => {
- navigate(`/book/${bookName}`);
- };
-
- if (!bookName || !chapterNumber || !actualBookName) {
- return Chapter not found
;
- }
-
- return (
-
- );
-}
-
function App() {
const [books, setBooks] = useState([]);
const [loading, setLoading] = useState(true);
@@ -274,6 +208,75 @@ function App() {
const { currentBook, currentChapter } = getCurrentNavInfo();
+ // Component for the home page
+ const HomePage = () => {
+ const handleBookSelect = (book: string) => {
+ const urlName = getBookUrlName(book);
+ navigate(`/book/${urlName}`);
+ };
+
+ return (
+
+ );
+ };
+
+ // Component for book chapters page
+ const BookPage = () => {
+ const { bookName } = useParams<{ bookName: string }>();
+ const actualBookName = bookName ? getBookFromUrl(bookName) : '';
+
+ const handleChapterSelect = (chapter: string) => {
+ navigate(`/book/${bookName}/chapter/${chapter}`);
+ };
+
+ const handleBack = () => {
+ navigate('/');
+ };
+
+ if (!bookName || !actualBookName || !books.includes(actualBookName)) {
+ return Book not found
;
+ }
+
+ return (
+
+ );
+ };
+
+ // Component for chapter reading page
+ const ChapterPage = () => {
+ const { bookName, chapterNumber } = useParams<{ bookName: string, chapterNumber: string }>();
+ const actualBookName = bookName ? getBookFromUrl(bookName) : '';
+
+ const handleBack = () => {
+ navigate(`/book/${bookName}`);
+ };
+
+ if (!bookName || !chapterNumber || !actualBookName) {
+ return Chapter not found
;
+ }
+
+ return (
+
+ );
+ };
+
if (loading) {
return (
@@ -407,9 +410,9 @@ function App() {
{/* Main Content */}
- } />
- } />
- } />
+ } />
+ } />
+ } />
diff --git a/frontend/src/components/BookSelector.tsx b/frontend/src/components/BookSelector.tsx
index 9552fc6f..1edceef8 100644
--- a/frontend/src/components/BookSelector.tsx
+++ b/frontend/src/components/BookSelector.tsx
@@ -1,13 +1,102 @@
-import React from 'react';
-import { BookOpen } from 'lucide-react';
+import React, { useState, useEffect } from 'react';
+import { BookOpen, Star } from 'lucide-react';
interface BookSelectorProps {
books: string[];
onBookSelect: (book: string) => void;
formatBookName: (bookName: string) => string;
+ user?: any;
}
-const BookSelector: React.FC = ({ books, onBookSelect, formatBookName }) => {
+const BookSelector: React.FC = ({ books, onBookSelect, formatBookName, user }) => {
+ const [favorites, setFavorites] = useState>(new Set());
+ const [loading, setLoading] = useState(false);
+
+ // Load favorites when user is available
+ useEffect(() => {
+ if (user) {
+ loadFavorites();
+ }
+ }, [user]);
+
+ const loadFavorites = async () => {
+ if (!user) return;
+
+ setLoading(true);
+ try {
+ const response = await fetch('/api/favorites', {
+ credentials: 'include'
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ const bookFavorites = new Set(
+ data.favorites
+ .filter((fav: any) => !fav.chapter) // Only book-level favorites
+ .map((fav: any) => fav.book)
+ );
+ setFavorites(bookFavorites);
+ }
+ } catch (error) {
+ console.error('Failed to load favorites:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const toggleFavorite = async (book: string, event: React.MouseEvent) => {
+ event.stopPropagation(); // Prevent book selection when clicking star
+
+ if (!user) return;
+
+ const isFavorited = favorites.has(book);
+
+ try {
+ if (isFavorited) {
+ // Remove favorite
+ const response = await fetch('/api/favorites', {
+ credentials: 'include'
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ const bookFavorite = data.favorites.find((fav: any) => fav.book === book && !fav.chapter);
+
+ if (bookFavorite) {
+ await fetch(`/api/favorites/${bookFavorite.id}`, {
+ method: 'DELETE',
+ credentials: 'include'
+ });
+
+ setFavorites(prev => {
+ const newFavorites = new Set(prev);
+ newFavorites.delete(book);
+ return newFavorites;
+ });
+ }
+ }
+ } else {
+ // Add favorite
+ const response = await fetch('/api/favorites', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'include',
+ body: JSON.stringify({
+ book: book
+ })
+ });
+
+ if (response.ok) {
+ setFavorites(prev => new Set(prev).add(book));
+ }
+ }
+ } catch (error) {
+ console.error('Failed to toggle favorite:', error);
+ }
+ };
+
// Group books by testament
const oldTestament = books.slice(0, 39); // First 39 books
const newTestament = books.slice(39); // Remaining books
@@ -19,16 +108,34 @@ const BookSelector: React.FC = ({ books, onBookSelect, format
{books.map((book) => (
-
+
+
+
+ {/* Star button - only show for authenticated users */}
+ {user && (
+
+ )}
+
))}
@@ -43,6 +150,11 @@ const BookSelector: React.FC = ({ books, onBookSelect, format
Select a book to begin reading
+ {user && (
+
+ Click the ★ to add books to your favorites
+
+ )}