Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="app">
<h1 className="title">React Forms! 📝</h1>
<AddBook />
<BookList />
<AddBook handleAddBook={handleAddBook} />
<BookList books={books} />
<div>
<button></button>
</div>
</div>
);
};
Expand Down
80 changes: 77 additions & 3 deletions src/components/AddBook.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";

/**
* A book should have the following fields:
Expand All @@ -13,8 +13,82 @@ import React from "react";
* - isFavorite (boolean, default false)
*/

const AddBook = () => {
return <div>AddBook</div>;
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 (
<form onSubmit={handleSubmit}>
<h2>Add a Book</h2>

<div>
<label>Title:</label>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
</div>

<div>
<label>Author:</label>
<input value={author} onChange={(e) => setAuthor(e.target.value)} />
</div>
<div>
<label>Rating (1–5):</label>
<input
value={rating}
onChange={(e) => setRating(e.target.value)}
type="number"
step="0.1"
/>
</div>
<div>
<label>Release Date:</label>
<input
value={releaseDate}
onChange={(e) => setReleaseDate(e.target.value)}
type="text"
placeholder="YYYY-MM-DD"
/>
</div>

{error && <p style={{ color: "red" }}>{error}</p>}

<button type="submit" className="black-text">
Add Book
</button>
</form>
);
};

export default AddBook;
51 changes: 49 additions & 2 deletions src/components/BookCard.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,54 @@
import React from "react";

const BookCard = () => {
return <div>BookCard</div>;
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 (
<div style={cardStyling}>
{/* Not sure about adding an image, this would then need to also be included in state
<img src="" alt="" />
Adding dummy image for now*/}
<img
style={imgStyling}
src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fstatic.vecteezy.com%2Fsystem%2Fresources%2Fpreviews%2F021%2F644%2F208%2Flarge_2x%2Fbooks-stacked-on-top-of-each-other-created-with-generative-ai-photo.jpg&f=1&nofb=1&ipt=7321c649c45350e5606f62cc761a8e8237dc7019672a6c710f8604faf3d5f728"
alt="Image of Book"
/>
<h1 style={textStyling}>{title}</h1>
<p style={textStyling}>{author}</p>
<p style={ratingStyling}>Ratings: {ratingsAverage}</p>
<p style={releaseDateStyling}>Release: {releaseDate}</p>
</div>
);
};

export default BookCard;
52 changes: 49 additions & 3 deletions src/components/BookList.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,53 @@
import React from "react";
import React, { useState } from "react";
import BookCard from "./BookCard";

const BookList = () => {
return <div>BookList</div>;
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 (
<>
<div>Book List</div>
<form onSubmit={handleSubmit}>
<input name="title" type="text" required placeholder="Title" />
<button className="buttom" type="submit">
search
</button>
</form>
{searchedBook.length !== 0
? searchedBook.map((book) => {
return (
<BookCard
title={book.title}
author={book.author}
ratings={book.ratings}
releaseDate={book.releaseDate}
/>
);
})
: books.map((book) => {
return (
<BookCard
title={book.title}
author={book.author}
ratings={book.ratings}
releaseDate={book.releaseDate}
/>
);
})}
</>
);
};

export default BookList;
7 changes: 7 additions & 0 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

body {
background-color: #160f29;
color: #160f29;
}

.title {
Expand Down Expand Up @@ -40,3 +41,9 @@ body {
.gif-card img {
width: 100%;
}
input {
color: black !important;
}
.black-text {
color: black;
}