File size: 4,791 Bytes
52a0642
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
 * 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 (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
      <div className="bg-white max-w-3xl w-full max-h-[90vh] overflow-hidden shadow-xl">
        <div className="p-6 border-b border-[#eee]">
          <h2 className="text-xl font-bold text-[#333]">Welcome — Pick Your Favorites</h2>
          <p className="text-sm text-gray-500 mt-1">
            Select 3–5 books you like to get personalized recommendations.
          </p>
        </div>
        <div className="p-6 overflow-y-auto max-h-[50vh]">
          {loading && (
            <div className="text-center text-gray-400 py-8">Loading popular books...</div>
          )}
          {error && (
            <div className="text-center text-red-500 py-4 text-sm">{error}</div>
          )}
          {!loading && !error && (
            <div className="grid grid-cols-3 md:grid-cols-4 gap-4">
              {books.map((book) => {
                const isSelected = selected.has(book.isbn);
                return (
                  <button
                    key={book.isbn}
                    type="button"
                    onClick={() => toggle(book.isbn)}
                    className={`text-left border-2 transition-all p-2 ${
                      isSelected ? "border-[#b392ac] bg-[#faf5f7]" : "border-[#eee] hover:border-[#ddd]"
                    }`}
                  >
                    <div className="aspect-[3/4] bg-gray-100 mb-2 overflow-hidden">
                      <img
                        src={book.thumbnail || PLACEHOLDER_IMG}
                        alt={book.title}
                        className="w-full h-full object-cover"
                        onError={(e) => {
                          e.target.onerror = null;
                          e.target.src = PLACEHOLDER_IMG;
                        }}
                      />
                    </div>
                    <p className="text-[10px] font-bold text-[#555] truncate" title={book.title}>
                      {book.title}
                    </p>
                    {isSelected && (
                      <span className="text-[10px] text-[#b392ac] font-bold">✓ Selected</span>
                    )}
                  </button>
                );
              })}
            </div>
          )}
        </div>
        <div className="p-6 border-t border-[#eee] flex justify-between items-center">
          <span className="text-xs text-gray-500">
            {selected.size} selected (min {MIN_SELECT}, max {MAX_SELECT})
          </span>
          <div className="flex gap-2">
            {onSkip && (
              <button
                type="button"
                onClick={() => {
                  localStorage.setItem("onboarding_complete", "true");
                  onSkip();
                }}
                className="px-4 py-2 text-sm text-gray-500 hover:text-gray-700"
              >
                Skip for now
              </button>
            )}
          <button
            onClick={handleComplete}
            disabled={!canComplete}
            className={`px-6 py-2 text-sm font-bold ${
              canComplete ? "bg-[#b392ac] text-white" : "bg-gray-200 text-gray-400 cursor-not-allowed"
            }`}
          >
            Start Exploring
          </button>
          </div>
        </div>
      </div>
    </div>
  );
};

export default OnboardingModal;