diff --git a/bookbuddy/frontend/src/App.tsx b/bookbuddy/frontend/src/App.tsx index 280c07d4..e1dea554 100644 --- a/bookbuddy/frontend/src/App.tsx +++ b/bookbuddy/frontend/src/App.tsx @@ -8,6 +8,7 @@ import Library from "./Library"; import WishList from "./WishBook"; import Buddy from "./Buddy_Recommendation"; import Profile from "./Profile"; +import ProtectedRoutes from "./ProtectedRoutes"; export default function App() { const location = useLocation(); @@ -20,19 +21,22 @@ export default function App() { {/* Public routes */} } /> } /> - } /> + {/*} />*/} + {/* Routes inside ProtectedRoutes */} + }> {/* Routes inside Layout */} }> } /> } /> } /> - } /> + } /> } /> + {/* Catch-all redirect */} - } /> + } /> ); diff --git a/bookbuddy/frontend/src/Buddy_Recommendation.tsx b/bookbuddy/frontend/src/Buddy_Recommendation.tsx index e93cf879..75be1eb9 100644 --- a/bookbuddy/frontend/src/Buddy_Recommendation.tsx +++ b/bookbuddy/frontend/src/Buddy_Recommendation.tsx @@ -416,4 +416,4 @@ function Buddy() { ); } -export default Buddy; +export default Buddy; \ No newline at end of file diff --git a/bookbuddy/frontend/src/Profile.tsx b/bookbuddy/frontend/src/Profile.tsx index 41542c25..feacbdf6 100644 --- a/bookbuddy/frontend/src/Profile.tsx +++ b/bookbuddy/frontend/src/Profile.tsx @@ -1,13 +1,13 @@ -import {useEffect, useState} from "react"; -import {AccountDto} from "./types/AccountDto"; -import {getCurrentUser, getMyLibrary} from "./api"; -import {BookDto} from "./types/BookDto"; -import {useNavigate} from "react-router-dom"; +import { useEffect, useState } from "react"; +import { AccountDto } from "./types/AccountDto"; +import { getCurrentUser, getMyLibrary } from "./api"; +import { BookDto } from "./types/BookDto"; +import { useNavigate } from "react-router-dom"; import "./Styling/Profile.css"; - export default function Profile() { const navigate = useNavigate(); + const [account, setAccount] = useState(null); const [books, setBooks] = useState([]); const [loading, setLoading] = useState(true); @@ -16,26 +16,33 @@ export default function Profile() { const totalPages = books.reduce((sum, b) => sum + (b.pagecount || 0), 0); useEffect(() => { - (async () => { + async function loadData() { try { - const acct = await getCurrentUser(); - setAccount(acct); - - const lib = await getMyLibrary(); - setBooks(lib); - } catch (e: any) { - console.error(e); - navigate("/login"); + const userData = await getCurrentUser(); + const libraryData = await getMyLibrary(); + setAccount(userData); + setBooks(libraryData); + } catch (err) { + setError("Failed to load profile"); } finally { setLoading(false); } - })(); - }, [navigate]); + } + loadData(); + }, []); if (loading) { return ( -
-

Loading profile...

+
+
Loading...
+
+ ); + } + + if (error) { + return ( +
+
{error}
); } @@ -52,10 +59,14 @@ export default function Profile() {

AI Uses Remaining: {account?.aiLimit ?? 0}

- - );} \ No newline at end of file + ); +} diff --git a/bookbuddy/frontend/src/ProtectedRoutes.tsx b/bookbuddy/frontend/src/ProtectedRoutes.tsx new file mode 100644 index 00000000..17dac2a3 --- /dev/null +++ b/bookbuddy/frontend/src/ProtectedRoutes.tsx @@ -0,0 +1,14 @@ +import { + Outlet, + Navigate +} from 'react-router-dom' + +const ProtectedRoutes = () => { + return ( + localStorage.getItem("accountId") ? + : + + ) +} + +export default ProtectedRoutes \ No newline at end of file diff --git a/bookbuddy/frontend/src/Styling/AboutUS.css b/bookbuddy/frontend/src/Styling/AboutUS.css new file mode 100644 index 00000000..3784684c --- /dev/null +++ b/bookbuddy/frontend/src/Styling/AboutUS.css @@ -0,0 +1,208 @@ + +/* Existing button-bubble base styles (keep these, or adjust as needed) */ +.button-bubble { + padding: 10px; + border-radius: 7px; + font-size: 1.1rem; + font-weight: 700; + cursor: pointer; + transition: all 0.2s ease-in-out; + position: absolute; + background-color: #235789; + color: #ffffff; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); +} + +.button-bubble:hover { + background-color: #e2b4bd; + + transform: translateY(-3px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); +} + +.button-bubble:active { + background-color: #7175c0; + transform: translateY(0); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.button-bubble:disabled { + background-color: #cccccc; + color: #666666; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + + +.question-circle { + display: block; + background: #424b54; + color: #fff; + border-radius: 50%; + width: 40px; + height: 40px; + line-height: 4px; + text-align: center; + font-size: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transition: all 0.2s ease-in-out; +} +.question-circle:hover { + transform: translateY(-3px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); +} + +@keyframes slideDown { + 0% { + opacity: 0; + transform: translate(-50%, -150%); + } + 100% { + opacity: 1; + transform: translate(-50%, -50%); + } +} +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes slideUp { + /* Starts at the current centered position (100% of the original slideDown) */ + 0% { + opacity: 1; + transform: translate(-50%, -50%); + } + /* Ends off-screen at the top, fading out */ + 100% { + opacity: 0; + transform: translate(-50%, -150%); + } +} + +@keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +.popup { + /* Positioning & Layout */ + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); /* Final state/fallback */ + border-right: solid gray 15px; + z-index: 100; + + /* Appearance */ + background-color: #ffffff; + border-top-left-radius: 25px; + border-bottom-left-radius: 25px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); + + /* Size & Content */ + width: 90%; + max-width: 645px; + max-height: 80vh; + padding: 30px; + overflow-y: auto; + + /* Typography */ + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + color: #333; + + /* --- ANIMATION ADDED --- */ + animation: slideDown 0.5s ease-out forwards; + +} + +.popup h1 { + color: #4a90e2; /* A nice, friendly blue */ + font-size: 2.2em; + border-bottom: 3px solid #f0f0f0; /* Subtle separator */ + padding-bottom: 10px; + margin-top: 0; /* Remove default top margin */ + margin-bottom: 25px; +} + +.popup h2 { + color: #555; + font-size: 1.5em; + margin-top: 20px; + margin-bottom: 10px; +} + +.popup h3 { + color: #6a6a6a; /* A slightly softer gray than h2 */ + font-size: 1.2em; /* Smaller than h2 but larger than body text */ + font-weight: 600; /* Slightly bolder for emphasis */ + margin-top: 15px; /* Less space above than h2 */ + margin-bottom: 5px; /* Closer to the content below it (like a list of names) */ +} + +/* Styling for the Paragraphs */ +.popup p { + font-size: 1.05em; + line-height: 1.6; + margin-bottom: 20px; +} +.popup a { + + color: #0000EE; +} + +/* --- NEW EXIT CLASSES --- */ +.popup.closing { + /* The animation duration and timing should match the slideDown animation */ + animation: slideUp 0.5s ease-in forwards; +} + +.backdrop.closing { + /* The animation duration and timing should match the original backdrop animation */ + animation: fadeOut 0.7s ease forwards; +} + +.close { + position: absolute; + top: 15px; + right: 25px; + font-size: 30px; + font-weight: bold; + color: #aaa; + cursor: pointer; + transition: color 0.2s ease; + +} +p ul { + padding-left: 40px; + margin-top: 5px; +} + +/* Hover effect for the close button */ +.close:hover, +.close:focus { + color: #d9534f; /* Change to a warning color (red) on hover */ + text-decoration: none; +} + + +.backdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); /* Semi-transparent black */ + z-index: 99; /* Below the popup (100) but above everything else */ + animation: fadeIn 0.7s ease forwards; + +} \ No newline at end of file diff --git a/bookbuddy/frontend/src/Styling/Buddy_Recommendation.css b/bookbuddy/frontend/src/Styling/Buddy_Recommendation.css index fed099f3..c20c9981 100644 --- a/bookbuddy/frontend/src/Styling/Buddy_Recommendation.css +++ b/bookbuddy/frontend/src/Styling/Buddy_Recommendation.css @@ -1,3 +1,4 @@ + /*Form CSS*/ .pageBackground { width: 100vw; @@ -190,4 +191,4 @@ display: inline-block; width: 100%; text-align: left; -} \ No newline at end of file +} diff --git a/bookbuddy/frontend/src/Styling/Profile.css b/bookbuddy/frontend/src/Styling/Profile.css index 3be76ad8..b3670e5c 100644 --- a/bookbuddy/frontend/src/Styling/Profile.css +++ b/bookbuddy/frontend/src/Styling/Profile.css @@ -92,4 +92,4 @@ opacity: 1; transform: translateY(0); } -} +} \ No newline at end of file diff --git a/bookbuddy/frontend/src/Styling/signup.css b/bookbuddy/frontend/src/Styling/signup.css index 2c3698ee..f25123a7 100644 --- a/bookbuddy/frontend/src/Styling/signup.css +++ b/bookbuddy/frontend/src/Styling/signup.css @@ -1,3 +1,67 @@ +body { + /* Only prevents horizontal scrolling */ + overflow-x: hidden; +} + +/* ======================================= */ +/* Mobile Phone Breakpoint (e.g., iPhone/Android) */ +/* ======================================= */ +@media (max-width: 480px) { + + /* --- 1. BOOK SCALING --- */ + + /* Reduce the Base Size of the Book */ + .book-3d .page { + width: 200px; + height: 300px; + /* Re-calculate the overall 3D transformation scaling factor */ + transform: rotate(-45deg) skewX(10deg) scale(0.35); /* Apply smaller scale */ + box-shadow: -25px 25px 25px rgba(0, 0, 0, 0.4); + } + + /* Adjust the Spine/Side Edge */ + .book-3d .page:before { + width: 15px; + transform: skewY(-45deg) translate(-29px, -21.5px); + } + + /* Adjust the Bottom Edge */ + .book-3d .page:after { + height: 15px; + transform: skewX(-45deg) translate(-5.5px, 15px); + } + + /* --- 2. SCENE ADJUSTMENTS --- */ + + .book-scene { + /* Set a smaller initial size, relative to phone viewport */ + width: 100vw; + height: 50vh; + } + + /* Adjust the pan animation to be smaller and less frantic on phones */ + @keyframes book-pan { + 0% { + transform: translate3d(0vw, 0vh, 0); + } + 100% { + /* Reduce the pan distance significantly to match the smaller viewport */ + transform: translate3d(50vw, 50vh, 0); + } + } + + /* --- 3. SIDEBAR ADJUSTMENT --- */ + + .sidebar-open { + /* Set it to occupy most of the phone width */ + width: 90vw; + /* Re-center it */ + left: 50%; + transform: translateX(-50%); + } +} + + /* Moves the scene up and right to create the illusion of the camera panning down and left. */ @keyframes book-pan { @@ -7,30 +71,66 @@ } /* End position (Adjust 200vw/vh for more or less movement) */ 100% { - transform: translate3d(200vw, 200vh, 0); + transform: translate3d(100vw, 100vh, 0); } } + + +/* In your CSS file */ + +/* In your CSS file */ + +.sidebar-closed { + /* Hides the element off the screen to the right */ + transform: translateX(100vw); + left: 0; +} + +.sidebar-open { + /* **Crucial Change:** Centers the element horizontally. */ + /* 1. Start half the viewport width (50vw). */ + /* 2. Subtract half the element's width (224px, which is 448px / 2). */ + + transform: translateX(0); + + /* We use 50% here as the form is w-2/4 (50%) */ + left: calc(50vw - 224px); /* Assuming max-w-md is 448px, half is 224px */ +} + +.sidebar-transition { + /* Animate both 'left' and 'transform' */ + transition: left 300ms ease-in-out, transform 300ms ease-in-out; +} + .gradient-background-books { background: radial-gradient( circle at center, #F5F3F0 0%, /* Start: Brightest off-white in the center */ #EAE7E1 50%, /* Middle: Soft taupe */ - #c9c7c1 100% /* End: Same taupe on the edges */ + #000000 80% /* End: Same taupe on the edges */ ); } body { - font-family: 'Times New Roman', sans-serif; + font-family: Quicksand } +.To { + margin-left:75px; + +} +.welcome { + margin-left:10px; +} .wave-text span { margin-top: 100%; margin-bottom: -100%; display: flex; font-size: 3rem; - animation: wave 7s ease-in-out infinite; + + animation: wave 6s ease-in-out infinite; } .wave-text span:nth-child(1) { @@ -55,7 +155,6 @@ body { } } - .input { &:focus { @@ -63,7 +162,7 @@ body { box-shadow: 0 0 0 4px rgba(226, 180, 189, 0.7); /* A classic focus ring/glow */ border-color: #f1dade; /* A blue border */ } - } +} /* ======================================= */ /* 2. BOOK SCENE WRAPPER & ANIMATION */ @@ -72,7 +171,7 @@ body { .book-scene { width: 50vw; height: 30vh; - animation: book-pan 90s linear infinite alternate; /* 90s duration (adjust for speed) */ + animation: book-pan 100s linear infinite alternate; /* 90s duration (adjust for speed) */ } /* ======================================= */ @@ -88,7 +187,7 @@ body { .book-3d .page { width: 400px; height: 600px; - background-image: url('https://m.media-amazon.com/images/I/710K+kF6XfL._UF1000,1000_QL80_.jpg'); + background-image: url('https://m.media-amazon.com/images/I/41x0pn4T3fL._SY300_SX300_QL70_FMwebp_.jpg'); background-size: cover; background-position: center; background-repeat: no-repeat; @@ -138,21 +237,26 @@ body { /* ======================================= */ /* 4. CUSTOM POSITIONING CLASSES (SCATTER) */ -/* ======================================= */ +/* ======================================= + +x offset of 375 +vertical of 300 + +*/ /* These position the individual books across the 300vw x 300vh animated scene. */ /* Apply these to the .book-3d wrapper div. */ .book-1-pos { - top: -250px; - left: 0px; - transform: translate(-50%, -50%) rotate(5deg) scale(0.8); - } + top: -235px; + left: 95px; + transform: translate(-50%, -50%) rotate(5deg) scale(0.8); +} .book-cover-two .page { - background-image: url('https://m.media-amazon.com/images/I/71aQzvMHo0L._SY466_.jpg') !important; + background-image: url('https://m.media-amazon.com/images/I/41PSO6lQH8L._SY445_SX342_ControlCacheEqualizer_.jpg') !important; } .book-2-pos { - top: 0px; - left: -250px; + top: 50px; + left: -240px; transform: translate(-50%, -50%) rotate(5deg) scale(.8); } @@ -166,12 +270,13 @@ body { transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +/*default cover*/ .book-cover-four .page { - background-image: url('https://m.media-amazon.com/images/I/516Ii18gdWL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; + background-image: url('https://m.media-amazon.com/images/I/81sjORNm-fL._SX425_.jpg') !important; } .book-4-pos { - top: 10px; - left: 50px ; + top: 40px; + left: 100px ; transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } .book-cover-five .page { @@ -194,7 +299,7 @@ body { } .book-cover-seven .page { - background-image: url('https://m.media-amazon.com/images/I/41rrZplMctL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; + background-image: url('https://m.media-amazon.com/images/I/41b6U1QogSL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; } .book-7-pos { top: -250px; @@ -204,7 +309,7 @@ body { .book-cover-eight .page { - background-image: url('https://m.media-amazon.com/images/I/516Ii18gdWL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; + background-image: url('https://m.media-amazon.com/images/I/41qSPS2EDdL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; } .book-8-pos { top: 0px; @@ -213,7 +318,7 @@ body { } .book-cover-nine .page { - background-image: url('https://m.media-amazon.com/images/I/516Ii18gdWL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; + background-image: url('https://m.media-amazon.com/images/I/91eNqZHWLYL._SL1500_.jpg') !important; } .book-9-pos { top: -250px; @@ -221,6 +326,10 @@ body { transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-ten .page { + background-image: url('https://m.media-amazon.com/images/I/41ylo-bKSLL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} + /*releated to 8*/ .book-10-pos { top: 0px; @@ -228,12 +337,20 @@ body { transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-eleven .page { + background-image: url('https://m.media-amazon.com/images/I/51Ppi-8kISL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} + .book-11-pos { top: -250px; left: -1250px; transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-twelve .page { + background-image: url('https://m.media-amazon.com/images/I/418jD+Rsd5L._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} + /* --- Book 12 (N=12) --- */ /* top: 250px (even); left: -125 * 11 = -1375px */ .book-12-pos { @@ -242,6 +359,10 @@ body { transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-thirteen .page { + background-image: url('https://m.media-amazon.com/images/I/71H52+sSb4L._SY466_.jpg') !important; +} + /* --- Book 13 (N=13) --- */ /* top: -250px (odd); left: -125 * 12 = -1500px */ .book-13-pos { @@ -264,6 +385,10 @@ body { left: -1750px; transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } + +.book-cover-fourteen .page { + background-image: url('https://m.media-amazon.com/images/I/81kfMGiLweL._SY522_.jpg') !important; +} /* --- Book -1 (Higher, Staggered Right) --- */ /* To visually balance the Book 2 left-shift, we go right and higher. */ .book-minus-one-pos { @@ -272,14 +397,21 @@ body { transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-fifteen .page { + background-image: url('https://m.media-amazon.com/images/I/41+p7cyJqKL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} /* --- Book -2 (Even Higher, Continuing Stagger) --- */ /* To continue the staggered effect */ + .book-minus-two-pos { top: -520px; /* One level higher than Book 1's -250px */ left: -450px;; /* Offset right by 500px */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-sixteen .page { + background-image: url('https://m.media-amazon.com/images/I/51hJg4c3IsL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} /* --- Book -3 (Highest, Continuing Stagger) --- */ .book-minus-three-pos { top: -520px; /* One level higher than Book 1's -250px */ @@ -287,6 +419,13 @@ body { transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-seventeen .page { + background-image: url('https://m.media-amazon.com/images/I/41rrZplMctL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} + +.book-cover-eighteen .page { + background-image: url('https://m.media-amazon.com/images/I/41gcNhuGStL._SX342_SY445_ControlCacheEqualizer_.jpg') !important; +} /* --- Book -4 --- */ .book-minus-four-pos { top: -520px; /* One level higher than Book 1's -250px */ @@ -294,68 +433,134 @@ body { transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-nineteen .page { + background-image: url('https://m.media-amazon.com/images/I/7127ZROAw5L._SY466_.jpg') !important; + +} + .book-minus-five-pos { top: -520px; /* One level higher than Book 1's -250px */ left: -1575px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-twenty .page { + background-image: url('https://m.media-amazon.com/images/I/713e4Yk6brL._SY466_.jpg') !important; + +} + .book-minus-six-pos { top: -520px; /* One level higher than Book 1's -250px */ left: -1950px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } + +.book-cover-twenty-one .page { + background-image: url('https://m.media-amazon.com/images/I/416pTqzRwfL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} .book-minus-seven-pos { top: -520px; /* One level higher than Book 1's -250px */ left: -2325px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-twenty-two .page { + background-image: url('https://m.media-amazon.com/images/I/B1phDWaxHHL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} .book-plus-1-pos { top: -520px; /* One level higher than Book 1's -250px */ left: 300px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-twenty-three .page { + background-image: url('https://m.media-amazon.com/images/I/51hOOKXqM3L._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} + .book-plus-2-pos { top: -820px; /* One level higher than Book 1's -250px */ left: -100px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-twenty-four .page { + background-image: url('https://m.media-amazon.com/images/I/412aLelj05L._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} .book-plus-3-pos { top: -820px; /* One level higher than Book 1's -250px */ left: -475px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-twenty-five .page { + background-image: url('https://m.media-amazon.com/images/I/41D9uDfBjFL._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} .book-plus-4-pos { top: -820px; /* One level higher than Book 1's -250px */ left: -850px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.book-cover-twenty-six .page { + background-image: url('https://m.media-amazon.com/images/I/41tEO52oK7L._SY445_SX342_ControlCacheEqualizer_.jpg') !important; +} + .book-plus-5-pos { top: -820px; /* One level higher than Book 1's -250px */ left: -1225px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } + +.book-cover-twenty-seven .page { + background-image: url('') !important; +} .book-plus-6-pos { top: -820px; /* One level higher than Book 1's -250px */ left: -1600px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } + +.book-cover-twenty-eight .page { + background-image: url('') !important; +} + .book-plus-7-pos { top: -820px; /* One level higher than Book 1's -250px */ left: -1975px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } + +.book-cover-twenty-nine .page { + background-image: url('') !important; +} .book-plus-8-pos { top: -820px; /* One level higher than Book 1's -250px */ left: -2350px; /* Further right */ transform: translate(-50%, -50%) rotate(5deg) scale(0.8); } +.form-label-hover:hover { + color: #235789 !important; + /* Added this line: */ + transform: scale(1.05) translateY(-2px); +} + +.form-input-hover:hover { + background-color: rgb(240, 240, 240); + + /* Added this line: */ + transform: scale(1.05) translateY(-2px); +} + +.Cover_logo { + margin-left: 1300px; + margin-top: 550px; + + +} + + + /* #424b54 gray # 235789 blue #91abc4 fish diff --git a/bookbuddy/frontend/src/addBooksViaCSV.tsx b/bookbuddy/frontend/src/addBooksViaCSV.tsx index e187a42d..d6cff487 100644 --- a/bookbuddy/frontend/src/addBooksViaCSV.tsx +++ b/bookbuddy/frontend/src/addBooksViaCSV.tsx @@ -1,234 +1,184 @@ import React, { useEffect, useState } from "react"; -import { createPortal } from "react-dom"; import { searchBookViaTitle } from "./searchBookViaTitle"; import { addCSVBooks } from "./addCSVBooks"; import "./components/Book_loading.css"; import tempAddBook from "./logo/tempAddBook.png"; -const BASE = ""; +export default function CSVReader() { + const [columnData, setColumnData] = useState([]); + const [fileName, setFileName] = useState("No File Chosen!"); + const [isLoading, setIsLoading] = useState(false); -interface BookMessage { - title: string; - message: string; - success: boolean; -} - -async function delay(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} + interface BookMessage { + title: string; + message: string; + success: boolean; + } -const CSVReader: React.FC = () => { - const [columnData, setColumnData] = useState([]); - const [books, setBooks] = useState([]); // kept in case you use later - const [bookMessages, setBookMessages] = useState([]); - const [fileName, setFileName] = useState("No File Chosen!"); - const [isLoading, setIsLoading] = useState(false); - const [currentMessage, setCurrentMessage] = useState( - null - ); - - const handleFileUpload = (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (!file) { - setFileName("No File Chosen!"); - return; + // google rate limit helper + function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); } - setFileName(file.name); + const BASE = ""; - const reader = new FileReader(); - reader.onload = (e) => { - const text = e.target?.result as string; - if (!text) return; + const handleFileUpload = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; - const lines = text - .split(/\r?\n/) - .map((line) => line.trim()) - .filter((line) => line !== ""); + if (file) { + setFileName(file.name); + } else { + setFileName("No File Chosen!"); + return; + } - if (lines.length === 0) return; + const reader = new FileReader(); + reader.onload = (e) => { + const text = e.target?.result as string; + if (!text) return; - const csvSplitRegExp = /,(?=(?:[^"]*"[^"]*")*[^"]*$)/; + const lines = text + .split(/\r?\n/) + .filter((line) => line.trim() !== ""); - // Assume first row is header - const headerCells = lines[0].split(csvSplitRegExp); - let titleColIndex = -1; + // Extract second column & remove undefined + const firstColumn = lines + .map((line) => line.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/)[1]) + .filter((v): v is string => v !== undefined); - for (let i = 0; i < headerCells.length; i++) { - if (headerCells[i].replace(/"/g, "").trim().toLowerCase() === "title") { - titleColIndex = i; - break; - } - } - - // If we couldn't find the "title" column, bail - if (titleColIndex === -1) { - // Fallback: try first column as titles - const firstColumn = lines.map( - (line) => line.split(csvSplitRegExp)[0] ?? "" - ); - setColumnData(firstColumn); - return; - } - - const titleColumn: string[] = lines.map((line) => { - const cells = line.split(csvSplitRegExp); - return cells[titleColIndex] ?? ""; - }); - - setColumnData(titleColumn); - }; + setColumnData(firstColumn); + }; - reader.readAsText(file); - }; + reader.readAsText(file); + }; - useEffect(() => { - if (columnData.length === 0) return; + useEffect(() => { + async function updateUser(message: BookMessage) { + const textBox = document.getElementById("userUpdate"); + if (!textBox) return; - async function processBooks() { - // Copy so we don't mutate state directly - let titles = [...columnData]; + textBox.style.color = message.success ? "green" : "violet"; + textBox.innerHTML = `${message.title}: ${message.message}`; + } - // Skip header row - titles = titles.slice(1); + async function processBooks() { + if (columnData.length === 0) return; - // Limit to 25 books (plus header originally) - titles = titles.slice(0, 25); + // Limit rows & skip header + const limited = columnData.slice(1, Math.min(columnData.length, 25)); - if (titles.length === 0) return; + setIsLoading(true); - setIsLoading(true); + for (const title of limited) { + const titleClean = title.replaceAll("#", ""); - for (const title of titles) { - const titleClean = title.replaceAll("/", "").replaceAll("#", "").trim(); + // 1. Search Google Books + const found = await searchBookViaTitle(titleClean, BASE); - if (!titleClean) continue; + let message: BookMessage; - // 1. Search the book - const found = await searchBookViaTitle(titleClean, BASE); - if (!found) { - const msg: BookMessage = { - title: titleClean, - message: "No result found", - success: false, - }; - setCurrentMessage(msg); - setBookMessages((prev) => [...prev, msg]); - await delay(25); - continue; - } + if (!found) { + message = { + title: titleClean, + message: "No result found", + success: false, + }; + await updateUser(message); + await delay(25); + continue; + } - // 2. Add book to backend - const result = await addCSVBooks(found, BASE); + // 2. Add to backend + const result = await addCSVBooks(found, BASE); - const msg: BookMessage = { - title: found.bookname, - message: result.ok ? "Added successfully" : result.message || "Failed", - success: result.ok, - }; + message = { + title: found.bookname, + message: result.ok ? "Added successfully" : result.message || "Failed", + success: result.ok, + }; - setCurrentMessage(msg); - setBookMessages((prev) => [...prev, msg]); - await delay(1000); - } + await updateUser(message); + await delay(1000); + } - await delay(3000); - setIsLoading(false); - window.location.reload(); - } + await delay(3000); + setIsLoading(false); + window.location.reload(); + } - processBooks(); - }, [columnData]); - - return ( -
-
-
- ); -}; - -export default CSVReader; + {isLoading && ( +
+
+
+
+
+
+
+
+
+ +
+
+ )} +
+ ); +} diff --git a/bookbuddy/frontend/src/components/Sidebar.css b/bookbuddy/frontend/src/components/Sidebar.css index cf4a338c..26371165 100644 --- a/bookbuddy/frontend/src/components/Sidebar.css +++ b/bookbuddy/frontend/src/components/Sidebar.css @@ -1,116 +1,183 @@ :root{ - --bb-sidebar-width: 220px; - --bb-sidebar-bg: #ffffff; - --bb-sidebar-text: #000000; - --bb-accent: #3b82f6; - --bb-muted: #94a3b8; + --bb-sidebar-width: 220px; + --bb-sidebar-bg: #ffffff; + --bb-sidebar-text: #000000; + --bb-accent: #3b82f6; + --bb-muted: #94a3b8; } +.bookmark.icon { + /* Position & Base Size */ + position: absolute; + margin-left: -75px; + margin-top: 10px; + width: 50Px; + height: 150px; + rotate: -90deg; + + /* Core Shape with cutout */ + clip-path: polygon( + 0% 0%, + 100% 0%, + 100% 0%, + 100% 100%, + 50% 88%, + 0% 100% + ); + + /* --- REALISTIC TEXTURE & COLOR --- */ + background: + + repeating-linear-gradient( + 45deg, + rgba(0,0,0,0.05) 0%, + rgba(0,0,0,0.05) 1px, + transparent 1px, + transparent 2px + ), + repeating-linear-gradient( + -45deg, + rgba(0,0,0,0.05) 0%, + rgba(0,0,0,0.05) 1px, + transparent 1px, + transparent 2px + ), + linear-gradient( + 150deg, + #6f0000 0%, + #af0000 20%, + #c80000 40%, + #af0000 60%, + #8a0000 80%, + #6f0000 100% + ); + border-top: solid 6px #4d0000; + box-shadow: + inset 0 3px 5px rgba(0, 0, 0, 0.3), + 0 8px 18px rgba(0, 0, 0, 0.5), + 0 3px 6px rgba(0, 0, 0, 0.3); + + + transform: perspective(500px) rotateX(5deg) rotateY(-5deg); + transform-origin: top center; + transition: all 0.4s ease-in-out; +} + + +.bookmark.icon:hover { + transform: perspective(500px) rotateX(2deg) rotateY(-2deg) translateY(23px); + box-shadow: + inset 0 3px 5px rgba(0, 0, 0, 0.3), + 0 12px 25px rgba(0, 0, 0, 0.6), + 0 5px 10px rgba(0, 0, 0, 0.4); + +} .bb-sidebar_toggle{ - display: none; - position: fixed; - top: 12px; - left: 12px; - z-index: 60; - background: var(--bb-accent); - border: none; - color: white; - font-size: 20px; - padding: 6px 10px; - border-radius: 6px; + display: none; + position: fixed; + top: 12px; + left: 12px; + z-index: 60; + background: var(--bb-accent); + border: none; + color: white; + font-size: 20px; + padding: 6px 10px; + border-radius: 6px; } .bb-sidebar{ - width: var(--bb-sidebar-width); - min-width: var(--bb-sidebar-width); - background: var(--bb-sidebar-bg); - color: var(--bb-sidebar-text); - padding: 1rem; - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 1rem; - height: 100vh; - position: relative; - transition: transform .2s ease; + width: var(--bb-sidebar-width); + min-width: var(--bb-sidebar-width); + background: var(--bb-sidebar-bg); + color: var(--bb-sidebar-text); + padding: 1rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 1rem; + height: 100vh; + position: relative; + transition: transform .2s ease; } .bb-sidebar_brand{ - font-weight: 700; - font-size: 1.1rem; - letter-spacing: .5px; - margin-bottom: .25rem; + font-weight: 700; + font-size: 1.1rem; + letter-spacing: .5px; + margin-bottom: .25rem; } .bb-sidebar_links{ - list-style: none; - padding: 0; - margin: 0; - display: flex; - flex-direction: column; - gap: .5rem; - flex: 1 1 auto; + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: .5rem; + flex: 1 1 auto; } .bb-link{ - display: block; - padding: .5rem .75rem; - border-radius: 6px; - text-decoration: none; - color: var(--bb-sidebar-text); - font-weight: 500; + display: block; + padding: .5rem .75rem; + border-radius: 6px; + text-decoration: none; + color: var(--bb-sidebar-text); + font-weight: 500; } .bb-link:hover{ - background: rgba(255,255,255,0.03); + background: rgba(255,255,255,0.03); } .bb-link--active{ - background: linear-gradient(90deg, rgba(59,130,246,0.12), rgba(59,130,246,0.08)); - color: var(--bb-accent); - box-shadow: inset 3px 0 0 var(--bb-accent); + background: linear-gradient(90deg, rgba(59,130,246,0.12), rgba(59,130,246,0.08)); + color: var(--bb-accent); + box-shadow: inset 3px 0 0 var(--bb-accent); } - .bb-main { - flex: 2; - display: flex; - justify-content: center; - align-items: flex-start; - padding: 2rem; - box-sizing: border-box; +.bb-main { + flex: 2; + display: flex; + justify-content: center; + align-items: flex-start; + padding: 2rem; + box-sizing: border-box; } .bb-sidebar_footer{ - font-size: .85rem; - color: var(--bb-muted); - margin-top: 0.5rem; + font-size: .85rem; + color: var(--bb-muted); + margin-top: 0.5rem; } .bb-layout { - display: flex; + display: flex; } @media (max-width: 9000px) { - .bb-sidebar_toggle{ - display: block; - } + .bb-sidebar_toggle{ + display: block; + } - .bb-sidebar { - position: fixed; - top: 0; - left: 0; - transform: translateX(-100%); - z-index: 50; - width: 70%; - max-width: 320px; - height: 100vh; - padding-top: 3.5rem; /* leave space for toggle button */ - box-shadow: 0 6px 18px rgba(2,6,23,0.4); - } + .bb-sidebar { + position: fixed; + top: 0; + left: 0; + transform: translateX(-100%); + z-index: 50; + width: 70%; + max-width: 320px; + height: 100vh; + padding-top: 3.5rem; /* leave space for toggle button */ + box-shadow: 0 6px 18px rgba(2,6,23,0.4); + } - .bb-sidebar.open{ - transform: translateX(0); - } -} + .bb-sidebar.open{ + transform: translateX(0); + } + +} \ No newline at end of file diff --git a/bookbuddy/frontend/src/components/Sidebar.tsx b/bookbuddy/frontend/src/components/Sidebar.tsx index db704646..bdf058a5 100644 --- a/bookbuddy/frontend/src/components/Sidebar.tsx +++ b/bookbuddy/frontend/src/components/Sidebar.tsx @@ -5,54 +5,63 @@ import logo from "../logo/bookbuddy-logo-mywristhurts.png"; // Sidebar component with navigation links export default function Sidebar() { - // State to manage sidebar visibility if wanted - const [open, setOpen] = useState(false); - - //Defines the navigation links, can be added to later - const links: { to: string; label: string }[] = [ - { to: "/search", label: "Search" }, - { to: "/library", label: "Library" }, - { to: "/WishList", label: "WishList" }, - { to:"/Buddy_Recommendation", label: "Ask a buddy"}, - { to:"/Profile", label: "Profile"}, - { to: "/login", label: "Sign Out" } - - ]; - - // Render the sidebar with navigation links - return ( - <> - {/* // Button to toggle sidebar visibility */} - - - {/* // The sidebar navigation */} - - - ); -} + // State to manage sidebar visibility if wanted + const [open, setOpen] = useState(false); + + //Defines the navigation links, can be added to later + const links: { to: string; label: string }[] = [ + { to: "/search", label: "Search" }, + { to: "/library", label: "Library" }, + { to: "/WishBook", label: "WishList" }, + { to:"/Buddy_Recommendation", label: "Ask a buddy"}, + { to: "/profile", label: "Profile" }, + { to: "/", label: "Sign Out" } + + ]; + + // Render the sidebar with navigation links + + return ( + <> + {/* // Button to toggle sidebar visibility */} + + + {/* // The sidebar navigation */} + + + ); +} \ No newline at end of file diff --git a/bookbuddy/frontend/src/login.tsx b/bookbuddy/frontend/src/login.tsx index c2607321..a768d0ce 100644 --- a/bookbuddy/frontend/src/login.tsx +++ b/bookbuddy/frontend/src/login.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { useNavigate, Link } from "react-router-dom"; // ✅ add Link import +import { useNavigate, Link } from "react-router-dom"; // add Link import import { addLogin } from "./api"; import type { LoginDto } from "./types/LoginDto"; import logo from "./logo/bookbuddy-logo-mywristhurts.png"; @@ -27,8 +27,12 @@ const Login: React.FC = () => { const ok = await addLogin(body); if (ok) { - navigate("/search"); // ✅ login successful + console.log("✅ Login OK, about to set localStorage"); + localStorage.setItem("accountId", username); + console.log("✅ accountId now:", localStorage.getItem("accountId")); + navigate("/search"); // login successful } else { + console.log("❌ Login returned ok = false"); setMessage("Invalid username or password"); } } catch (error) { diff --git a/bookbuddy/frontend/src/searchBookViaTitle.tsx b/bookbuddy/frontend/src/searchBookViaTitle.tsx index 5e5fcfe9..44c489b3 100644 --- a/bookbuddy/frontend/src/searchBookViaTitle.tsx +++ b/bookbuddy/frontend/src/searchBookViaTitle.tsx @@ -1,9 +1,27 @@ import type { BookDto } from "./types/BookDto"; -export async function searchBookViaTitle(title: string, BASE: string): Promise { - const res = await fetch(`${BASE}/googlebooks/search/${encodeURIComponent(title)}`); - if (!res.ok) return; - const data = await res.json(); +/** + * Searches for a book by title and returns the **first result** + * Always returns BookDto OR null (never undefined) + */ +export async function searchBookViaTitle( + title: string, + BASE: string +): Promise { + try { + const res = await fetch( + `${BASE}/googlebooks/search/${encodeURIComponent(title)}` + ); - return (data.docs?.[0] as BookDto) ?? null; //returns the first book -} \ No newline at end of file + // If request fails → return null instead of undefined + if (!res.ok) return null; + + const data = await res.json(); + + // Guarantee output type: BookDto | null + return (data?.docs?.[0] as BookDto) ?? null; + } catch (err) { + console.error("searchBookViaTitle error:", err); + return null; + } +} diff --git a/bookbuddy/frontend/src/signup.tsx b/bookbuddy/frontend/src/signup.tsx index 337cb49d..c5bfaa9e 100644 --- a/bookbuddy/frontend/src/signup.tsx +++ b/bookbuddy/frontend/src/signup.tsx @@ -1,275 +1,595 @@ + import React, { useState } from "react"; import { useNavigate, Link } from "react-router-dom"; import { addAccount } from "./api"; +import { addLogin } from "./api"; import type { AccountDto } from "./types/AccountDto"; +import type { LoginDto } from "./types/LoginDto"; import logo from "./logo/bookbuddy-logo-mywristhurts.png"; -import "./Styling/signup.css" +import "./Styling/signup.css"; +import "./Styling/AboutUS.css"; + + + +//////////////////////////// SIGNUP CODE //////////////////////////// const Signup: React.FC = () => { - const navigate = useNavigate(); - const [form, setForm] = useState({ name: "", password: "" }); - const [confirm, setConfirm] = useState(""); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const onChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - if (name === "confirm") { - setConfirm(value); - } else { - setForm({ ...form, [name]: value }); + const navigate = useNavigate(); + const [form, setForm] = useState({ name: "", password: "" }); + const [confirm, setConfirm] = useState(""); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(null); + + const [seeSignup, setSeeSignup] = useState(false); + ////////////////////////////LOGIN CODE //////////////////////////// + const [formLN, setFormLN] = useState({ name: "", password: "" }); + const [loadingLN, setLoadingLN] = useState(false); + const [errorLN, setErrorLN] = useState(null); + const [seeLogin, setSeeLogin] = useState(false); + const [cycleStart, setCycleStart] = useState(false); + const [showinfo, setShowinfo] = useState(false); + + + const sleep = (ms: number | undefined) => { + return new Promise(resolve => setTimeout(resolve, ms)); } - }; - const onSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(null); + const onChangeLN = (e: React.ChangeEvent) => { + setFormLN({ ...formLN, [e.target.name]: e.target.value }); + }; - if (form.password.length < 6) { - setError("Password must be at least 6 characters."); - return; - } - if (form.password !== confirm) { - setError("Passwords do not match."); - return; - } + const logIn = async (e: React.FormEvent) => { + e.preventDefault(); + setErrorLN(null); + setLoadingLN(true); + + try { + const account = await addLogin({ + name: formLN.name.trim(), + password: formLN.password + }); + + if (!account) { + setErrorLN("Invalid username or password."); + return; + } + + localStorage.setItem("accountId", formLN.name.trim()); + navigate("/search"); + + } catch (err: any) { + setErrorLN(err?.message ?? "Login failed. Please check your credentials."); + } finally { + setLoadingLN(false); + } + }; +////////////////////////////LOGIN CODE //////////////////////////// + + + // state to track if the sidebar is open (true) or closed (false) + const [isOpen, setIsOpen] = useState(false); + + // functional update form ( gets around reacts batch updates + const toggleSidebar = () => { - setLoading(true); - try { - await addAccount({ name: form.name.trim(), password: form.password }); - navigate("/login"); - } catch (err: any) { - setError(err?.message ?? "Sign up failed. Please try again."); - } finally { - setLoading(false); + setIsOpen(prevIsOpen => !prevIsOpen); } - }; - return ( -
-
-
-

- Your collection all in one Place -

-
+ const onChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + if (name === "confirm") { + setConfirm(value); + } else { + setForm({ ...form, [name]: value }); + } + }; + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + + if (form.password.length < 6) { + setError("Password must be at least 6 characters."); + return; + } + if (form.password !== confirm) { + setError("Passwords do not match."); + return; + } + + setLoading(true); + try { + await addAccount({ name: form.name.trim(), password: form.password }); + setSuccess("Signup successful — proceed to login!"); + setForm({ name: "", password: "" }); + setConfirm(""); + + setTimeout(() => { + setSuccess(null); + }, 2000); + } catch (err: any) { + setError(err?.message ?? "Sign up failed. Please try again."); + } finally { + setLoading(false); + } + + }; + + //////////////////////////// SIGNUP CODE //////////////////////////// + + return ( + // Main container with relative positioning and high Z-index to establish a stacking context +
+ +
+

+ WelcomeToBookBuddy +

+
-
+ {/* --- TEXT CONTAINER: Now without a background color --- */} +
+

+ Your collection all in one place +

+
- {/* --- Book Instances (The Reusable Components) --- */} - {/* Positioned using custom CSS classes (book-1-pos, etc.) */} -
-
-
+ {showinfo && ( +
+ +
+ setShowinfo(false)}>× +

Welcome to BookBuddy!

+ +

What is this place?

+

Book Buddy is a digital book tracking web application created to remove + the social aspect of other reading applications such as Goodreads or Hardcover. + All other book tracking applications focus so much on + “what others are saying” about a book. This website is not for finding the + hottest new books. It's about tracking the books YOU have or YOU want to read. + We here at BookBuddy believe that this allows you to feel less guilt for not following + the trends of the hottest books. Reading is all about consuming books that you find + interesting, and you should be able to read books that you want to without + feeling the fear of missing out or guilt that comes with not reading the hottest new books + people are talking about.

+

But what if I still want to find new books

+

No Problem! just ask a Buddy™

+

With our Ask a buddy feature, you can get personalized book recommendations + based not only on what you read but also on how you read. No two readers are the same, + and whether you read for the thrill of a good story, something to wind you down for + the night, or to learn something new, Our Buddy has you covered. +

+

But I already have a digital library elsewhere

+

Wonderful, bring it on over!

+

We love big libraries here. + Bring your digital library over with ease with our library uploader. + Simply bring over a copy of your library's CSV file and we will handle the rest +

+

What's a CSV file?

+

Don't worry about it!

+

A CSV is just a spreadsheet, and in this case one that contains your books. + Here are a few tutorials on how to obtain + your library CSV from other websites!

+ +

I HAVE A PROBLEM

+

Tell us about it

+

if you have any issues at all feel free to contact us bookbuddiesemu@gmail.com +

+ +

Credits

+

This project was created with love for the teams computer science Capstone project at Eastern michigan + university. The team was composed of 6 passionate students who either + are are readers themselves or have readers close to them.

+

Team members: +

    +
  • Ryan Cleary
  • +
  • Nicholas Hoshowski
  • +
  • Bear Kennedy
  • +
  • Ryan Retan
  • +
  • Noah Schaible
  • +
  • Benjamin Smith
  • +
+ Teacher/Project manager: +
    +
  • Siyuan Jiang
  • +
+ Artists: +
    +
  • Ben's Mom: BookBuddy character
  • +
  • Noah's friend: BookBuddy logo
  • +
+

+

We also want to thank everyone in this class who took the time to assist + us on this Journey. We also want to thank the teacher for her endless support, guidance and + patience with our team. Lastly we also would like to thank all of those who + were a part of beta testing, giving us input on how we can make this website the best it can + be.

+
+
+ BookBuddy +
+
)} + + + {/* ---------------------------- */} + + +
+ + + +
+ {/* ------------------------- */} + + + + + + {/*show info button */} + + + + + +
+ + + {/* --- Book Instances (The Reusable Components) --- */} + {/* Positioned using custom CSS classes (book-1-pos, etc.) */} +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+ + +
+
+
+ +
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- - - -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
- -
- -
- {/* Header / Branding */} -
- BookBuddy -

- Create your account -

-

- Join BookBuddy today -

-
- -
- {error && ( -
- {error} -
- )} - -
-
- - -
+
+
+
-
- - -
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
-
- - +
+
+
+ +
+
+
+ +
+
+
+ +
- - - -

- Already have an account?{" "} - - Sign in - -

+ <> +
+ + {seeSignup && ( + + +
+ + {/* Header / Branding */} +
+ BookBuddy +

+ Create your account +

+

+ Join BookBuddy today +

+
+ +
+ {error && ( +
+ {error} +
+ )} + {success && ( +
+ {success} +
+ )} +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+
)} + + {seeLogin && ( + +
+ {/* Header / Branding */} +
+ BookBuddy +

+ Log into your account +

+ +
+ +
+ {errorLN && ( +
+ {errorLN} +
+ )} + +
+
+ + +
+ +
+ + +
+ + + +
+ + +
+
)} +
+ +
-
-
- ); + + + ); }; -export default Signup; +export default Signup; \ No newline at end of file diff --git a/bookbuddy/frontend/src/types/AccountDto.ts b/bookbuddy/frontend/src/types/AccountDto.ts index 9ffa5a3a..7f07723d 100644 --- a/bookbuddy/frontend/src/types/AccountDto.ts +++ b/bookbuddy/frontend/src/types/AccountDto.ts @@ -1 +1 @@ -export type AccountDto = { accountId?: number; name: string, password: string} \ No newline at end of file +export type AccountDto = { accountId?: number; name: string, password: string, aiLimit?:number} \ No newline at end of file diff --git a/bookbuddy/src/main/resources/static/Questions.txt b/bookbuddy/src/main/resources/static/Questions.txt new file mode 100644 index 00000000..495d3bf5 --- /dev/null +++ b/bookbuddy/src/main/resources/static/Questions.txt @@ -0,0 +1,166 @@ +Do you want a series or standalone? +What mood are you in? +What inspires you most? +Do you want something familiar or new? +Favorite setting? +Favorite sport? +Do you like to stay at home, go out, or have a mix of the two? +Do you want something written in the past 5 years? +Favorite author? +Which TV show or movie do you wish had a book version? +What is your dream job? +Do you enjoy stories set in the past, present, or future? +If your life were a novel, what genre would it be right now? +If you could be a holiday, which one would you be? +What is your ideal first date activity? +Favorite color? +About how old are you? +Do you prefer worlds set in a dystopia or utopia—or neither? +Hero or villain? +Past or future? +Love or mystery? +City or wilderness? +Real or imaginary settings? +Do you want an educational book? +What age range should the book be targeted at? +What weather do you like to read in? +Do you use emojis when you text? +Apple or Windows computer? +Quickest route or most scenic route? +Do you like being able to relate to your book’s characters? +Are you more a fan of supervised learning or unsupervised learning? +Coffee, tea, or no caffeine? +Would you rather follow a character who changes the world or one who quietly understands it better? +What emotion do you want a book to leave you with when you finish the last page? +What topic, time period, or mystery are you curious about but haven’t explored through reading? +Do you prefer stories told in first, second, or third person? +How important is a satisfying, neat ending versus an ambiguous one? +Do you prefer ensemble casts or a single deeply explored protagonist? +What fictional concept (magic system, technology, etc.) would you most like to see fully explained? +Do you gravitate toward character-driven or plot-driven narratives? +Are you drawn more to stories about creating something or destroying something? +Do you read the last page first or never? +Would you rather read about a “chosen one” who succeeds or a normal person who defies expectations? +How important are accurate historical details in historical fiction? +Do you prefer personal stakes or global stakes? +Do you prefer books with maps or illustrations, or none at all? +Which sense do you associate most with reading (smell, touch, sight, etc.)? +Do you like stark, minimalist cover designs or busy, illustrative ones? +What’s your go-to background noise while reading (silence, music, nature sounds)? +Are you a rereader, or always looking for the next new book? +Do you prefer bright and airy reading locations or dim and cozy ones? +When watching a film adaptation, do you prefer it faithful or creatively different? +Which time of day gives you the best focus (morning, afternoon, evening, night)? +Do you prefer crisp, formal language or casual, conversational prose? +If you had a theme song, what tempo would it be? +Do you prefer muted/earth tones or vibrant/high-contrast colors? +What’s your favorite handwriting style (cursive, print, calligraphy)? +Do you trust institutions (governments, schools, etc.) in fiction? +What belief have you had challenged by something you read? +Do you prefer stories with clear moral lessons or moral ambiguity? +Would you rather master a complex skill or have effortless intuition in everything? +Which fictional character’s philosophy do you align with most? +Do you believe in fate or free will in stories? +What universal truth do you wish more stories acknowledged? +What is the best ending for a memoir (triumphant, reflective, still evolving)? +How do you organize bookshelves (color, author, genre)? +Do you dog-ear pages or use bookmarks? +If you could only use one font forever, what would it be? +Do you listen to audiobooks while doing chores, or only when focused, or not at all? +What’s your preferred reading snack (salty, sweet, savory)? +Do you look up unfamiliar references immediately or keep reading? +Is it better to be the most knowledgeable or the most creative person in the room? +Do you prefer physical books, e-readers, or tablets? +Do you prefer protagonists who start strong or grow significantly? +Are you drawn to magical realism or clearly defined fantasy rules? +Do you like stories that end with major change or quiet resolution? +How much do you enjoy unreliable history or unreliable technology in fiction? +Do you prefer high magic/tech worlds or grounded realism? +What fictional landscape feels most like home (forest, desert, ocean, city)? +Do you prefer stories about building something new or preserving something old? +How do you feel about stories told entirely through documents (letters, emails, reports)? +How do you make decisions: pros/cons list or gut feeling? +Do you like books about isolation or community? +Which historical era do you find most visually interesting? +Do you like it when narrators break the fourth wall? +Are you more interested in why a character acts or what their actions lead to? +Do you prefer tight single-plot narratives or many interwoven subplots? +What fictional object would you want to own? +Do you like characters who are intensely passionate even if it causes problems? +Do you enjoy books that challenge your worldview? +How important is a strong prologue to you? +Do you like witty, quick jokes or long, situational humor? +If you could only listen to one music genre for a year, what would it be? +Do you prefer detailed magical systems or mysterious ones? +Do you like time travel paradox stories? +Do you prefer mystery/suspense or immediate action? +Do you like stories set in extreme weather? +Do you prefer hard sci-fi or soft sci-fi? +Do you prefer reading in the morning, afternoon, or night? +What kind of lighting do you prefer while reading? +Do you like books with a glossary or appendix? +If a character is flawed, do you want to see them redeem themselves? +Are you more interested in beginnings or aftermaths of major events? +Do you like to see characters achieve goals perfectly or imperfectly? +Do you prefer near-future or far-future settings? +Do you like natural-leader protagonists or reluctant followers? +What emotion do you most want a book to evoke? +Do you prefer politics of fictional worlds or daily life of citizens? +Do you prefer complex grammar or simple communication? +Do you prefer emotional impact or intellectual stimulation? +Do you like subtle sequel hints in standalones? +Do you prefer large casts or intimate casts? +What type of non-fiction do you want to read more of? +Do you prefer experts or passionate amateurs as main characters? +Are you drawn to adventure or home-centered stories? +Do you like biased or misleading narrators? +Should stories always teach something new? +What fictional vehicle would you most want to pilot? +Do you prefer interpersonal conflicts or large-scale conflicts? +How important is a book’s title to you? +Do you prefer reading during travel or at home? +Do you like overly optimistic characters or pessimistic ones? +What is your favorite time of year to read? +Do you judge books by their cover? +Do you prefer clear villains or morally gray antagonists? +Do you like long descriptive passages? +Are you drawn to rebellion or order? +Do you prefer older or younger authors? +What is your preferred reading speed? +Do you enjoy experimental page layouts or traditional ones? +Is intricate world-building or atmospheric world-building more important? +Do you like solving mysteries alongside the protagonist? +Do you prefer utopias or dystopias? +Which fictional mentor do you admire most? +Do you like magic that is innate or learned? +Are you drawn to family secrets or societal secrets? +Do you prefer closed endings or ones that imply ongoing struggle? +How do you feel about books with heavy mythological elements? +Do you prefer reading on long trips or short daily sessions? +What’s your ideal drink temperature while reading? +Do you like high-tech futures or de-industrialized societies? +Is motivation or success more important in a character? +Do you prefer long novels or short ones? +Do you enjoy non-linear timelines? +What’s your favorite weather for indoor reading? +Do you prefer royalty/nobility stories or common-folk stories? +Do you like books that teach a skill? +Are you interested in time-travel science or emotional impact? +Do you prefer exceptionally skilled characters or average ones? +Do you prefer fiction close to reality or far from it? +How do you feel about revenge-driven protagonists? +Do you like footnotes or endnotes? +Do you prefer high-stakes/low-emotion books or the reverse? +What fictional historical event would you like to witness? +Do you prefer slow-burn romances or fast-developing ones? +Are you drawn to betrayal or loyalty stories? +Do you like settings that feel like a character themselves? +Which color do you find most appealing? +High-concept sci-fi or contemporary realistic settings? +Do you like invented slang? +Is it more important that a character is brave or kind? +Conflict resolved through dialogue or confrontation? +What fictional museum or library would you want to visit? +Do you like characters who hide secret lives? +Do you prefer characters who challenge norms or uphold them? +What is your favorite non-musical sound? \ No newline at end of file diff --git a/witch main b/witch main new file mode 100644 index 00000000..acbab36f --- /dev/null +++ b/witch main @@ -0,0 +1,12 @@ + 39-finalize-api-selection + 41-connect-book-and-account-table + Wish +* aifix + aiimprovement + comments + demo + main + new-deploy + profilepage + searchpage-css + sprint2-deploy-v1