diff --git a/src/App.jsx b/src/App.jsx index c5c0b50..26bc023 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,15 +1,36 @@ -import React from "react"; +import React, { useState } from "react"; import { createRoot } from "react-dom/client"; import "./style.css"; import AddBook from "./components/AddBook"; import BookList from "./components/BookList"; const App = () => { + const [books, setBooks] = useState([ + { + title: "Book1", + author: "author1", + ratings: [2.5, 5, 3, 3], + releaseDate: "December 2025", + }, + { + title: "Book2", + author: "author2", + ratings: [3.5, 4, 3.5, 3], + releaseDate: "November 2025", + }, + ]); + const handleAddBook = (newBook) => { + setBooks([...books, newBook]); + }; + return (

React Forms! 📝

- - + + +
+ +
); }; diff --git a/src/components/AddBook.jsx b/src/components/AddBook.jsx index 9f115e7..f413072 100644 --- a/src/components/AddBook.jsx +++ b/src/components/AddBook.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; /** * A book should have the following fields: @@ -13,8 +13,82 @@ import React from "react"; * - isFavorite (boolean, default false) */ -const AddBook = () => { - return
AddBook
; +const AddBook = ({ handleAddBook }) => { + //states + const [title, setTitle] = useState(""); + const [author, setAuthor] = useState(""); + const [rating, setRating] = useState(""); + const [releaseDate, setReleaseDate] = useState(""); + const [error, setError] = useState(""); + + const handleSubmit = (e) => { + e.preventDefault(); + //add validation + + if (!title.trim() || !author.trim() || !rating.trim()) { + setError("Title, author, and rating are required."); + return; + } + + const numericRating = parseFloat(rating); + if (isNaN(numericRating) || numericRating < 1 || numericRating > 5) { + setError("Rating must be a number between 1 and 5."); + return; + } + + const newBook = { + title: title, + author: author, + ratings: [parseFloat(rating)], + releaseDate: releaseDate, + }; + handleAddBook(newBook); + setTitle(""); + setAuthor(""); + setRating(""); + setReleaseDate(""); + setError(""); + }; + + return ( +
+

Add a Book

+ +
+ + setTitle(e.target.value)} /> +
+ +
+ + setAuthor(e.target.value)} /> +
+
+ + setRating(e.target.value)} + type="number" + step="0.1" + /> +
+
+ + setReleaseDate(e.target.value)} + type="text" + placeholder="YYYY-MM-DD" + /> +
+ + {error &&

{error}

} + + +
+ ); }; export default AddBook; diff --git a/src/components/BookCard.jsx b/src/components/BookCard.jsx index b3bbda7..20300c3 100644 --- a/src/components/BookCard.jsx +++ b/src/components/BookCard.jsx @@ -1,7 +1,54 @@ import React from "react"; -const BookCard = () => { - return
BookCard
; +const cardStyling = { + position: "relative", + padding: "10px", + display: "flex", + flexDirection: "column", + alignItems: "center", + backgroundColor: "white", + border: "1px solid red", + margin: "10px", +}; +const imgStyling = { + width: "180px", +}; +const textStyling = { + color: "black", + alignSelf: "flex-start", +}; +const releaseDateStyling = { + position: "absolute", + fontSize: "10px", + fontStyle: "italic", + color: "darkgray", + bottom: 0, + right: 0, +}; +const ratingStyling = { + color: "black", +}; + +const BookCard = ({ title, author, ratings, releaseDate }) => { + const ratingsAverage = Math.round( + ratings.reduce((acc, rating) => acc + rating, 0) / ratings.length, + ); + return ( +
+ {/* Not sure about adding an image, this would then need to also be included in state + + Adding dummy image for now*/} + Image of Book +

{title}

+

{author}

+

Ratings: {ratingsAverage}

+

Release: {releaseDate}

+
+ ); }; export default BookCard; diff --git a/src/components/BookList.jsx b/src/components/BookList.jsx index 0a87a3f..023464d 100644 --- a/src/components/BookList.jsx +++ b/src/components/BookList.jsx @@ -1,7 +1,53 @@ -import React from "react"; +import React, { useState } from "react"; +import BookCard from "./BookCard"; -const BookList = () => { - return
BookList
; +const BookList = ({ books }) => { + const [searchedBook, setSearchedBook] = useState([]); + const handleSubmit = (event) => { + event.preventDefault(); + const titleInput = event.target[0].value; + const filteredBooks = []; + + for (let i = 0; i < books.length; i++) { + if (books[i].title.toLowerCase().includes(titleInput.toLowerCase())) { + filteredBooks.push(books[i]); + } + } + setSearchedBook(filteredBooks); + }; + + return ( + <> +
Book List
+
+ + +
+ {searchedBook.length !== 0 + ? searchedBook.map((book) => { + return ( + + ); + }) + : books.map((book) => { + return ( + + ); + })} + + ); }; export default BookList; diff --git a/src/style.css b/src/style.css index e1b4f2c..75a4605 100644 --- a/src/style.css +++ b/src/style.css @@ -7,6 +7,7 @@ body { background-color: #160f29; + color: #160f29; } .title { @@ -40,3 +41,9 @@ body { .gif-card img { width: 100%; } +input { + color: black !important; +} +.black-text { + color: black; +}