/** * P2: New-user onboarding — pick 3–5 books to seed preferences. * Shown when myCollection is empty and onboarding not completed. */ import React, { useState, useEffect } from "react"; import { getOnboardingBooks } from "../api"; const PLACEHOLDER_IMG = "/content/cover-not-found.jpg"; const MIN_SELECT = 3; const MAX_SELECT = 5; const OnboardingModal = ({ onComplete, onAddFavorite, onSkip }) => { const [books, setBooks] = useState([]); const [selected, setSelected] = useState(new Set()); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); useEffect(() => { getOnboardingBooks(24) .then(setBooks) .catch((e) => setError(e.message)) .finally(() => setLoading(false)); }, []); const toggle = (isbn) => { setSelected((prev) => { const next = new Set(prev); if (next.has(isbn)) { next.delete(isbn); } else if (next.size < MAX_SELECT) { next.add(isbn); } return next; }); }; const handleComplete = async () => { if (selected.size < MIN_SELECT) return; try { for (const isbn of selected) { await onAddFavorite(isbn); } localStorage.setItem("onboarding_complete", "true"); onComplete(); } catch (e) { setError(e.message); } }; const canComplete = selected.size >= MIN_SELECT; return (

Welcome — Pick Your Favorites

Select 3–5 books you like to get personalized recommendations.

{loading && (
Loading popular books...
)} {error && (
{error}
)} {!loading && !error && (
{books.map((book) => { const isSelected = selected.has(book.isbn); return ( ); })}
)}
{selected.size} selected (min {MIN_SELECT}, max {MAX_SELECT})
{onSkip && ( )}
); }; export default OnboardingModal;