From 0880eff86e8072ef6528c3c94ea6e576aa25a249 Mon Sep 17 00:00:00 2001 From: alessiadod Date: Thu, 17 Apr 2025 12:01:52 +0200 Subject: [PATCH 1/2] Add full assessment and answer questions in readme --- README.md | 24 ++++++++++++++ app/index.html | 15 +++++++++ app/src/App.css | 8 +++++ app/src/App.jsx | 35 +++++++++++++------- app/src/components/BookCard.jsx | 39 ++++++++++++++++++++++ app/src/components/Books.jsx | 41 ++++++++++++++++++++++++ app/src/components/Favourites.jsx | 27 ++++++++++++++++ app/src/components/FavouritesContext.jsx | 41 ++++++++++++++++++++++++ app/src/main.jsx | 19 ++++++----- 9 files changed, 229 insertions(+), 20 deletions(-) create mode 100644 app/src/components/BookCard.jsx create mode 100644 app/src/components/Books.jsx create mode 100644 app/src/components/Favourites.jsx create mode 100644 app/src/components/FavouritesContext.jsx diff --git a/README.md b/README.md index ec08486..bac1410 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,36 @@ 1. How long did you spend on the coding? + 2.5 hours + 2. What would you add to your solution if you had more time? + - Implement navigation to allow users to browse all the books in the JSON file, with controls to navigate + between pages. + - Add an option for the user to select how many books to dislay per page and display full JSON file. + - Add a search feature to filter books by author or title. + - Make the "READ MORE" button functional by displaying additional details from the JSON file, such as language, country, and year of publication. + 3. Share a code snippet that you are proud of and explain what it does + ```js +// Load initial state from localStorage or set to an empty array +const [favourites, setFavourites] = useState(() => { + const stored = localStorage.getItem("favourites"); + return stored ? JSON.parse(stored) : []; +}); + +// Sync favourites with localStorage whenever it changes +useEffect(() => { + localStorage.setItem("favourites", JSON.stringify(favourites)); +}, [favourites]); +``` + After implementing the core functionality, I noticed that the favourites list was reset on page reload. To address this, I used localStorage to persist the favourites state and ensure data remains available across sessions. + + 4. How would you track down a performance issue in production? Have you ever had to do this? + I have not but I'm eager to learn more about it! --- # Comments diff --git a/app/index.html b/app/index.html index 79c4701..94415e2 100644 --- a/app/index.html +++ b/app/index.html @@ -3,11 +3,26 @@ + + Vite + React
+ diff --git a/app/src/App.css b/app/src/App.css index e994711..e9b7b8c 100644 --- a/app/src/App.css +++ b/app/src/App.css @@ -1,3 +1,11 @@ .App { text-align: center; +} + +.custom-background { + background-color: #efeeeb; +} + +.custom-shadow { + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } \ No newline at end of file diff --git a/app/src/App.jsx b/app/src/App.jsx index a6aee20..daf1e86 100644 --- a/app/src/App.jsx +++ b/app/src/App.jsx @@ -1,17 +1,28 @@ -import './App.css' +import "./App.css"; +import Books from "./components/Books"; +import Favourites from "./components/Favourites"; + function App() { - return ( -
-

Hello CodeOper!

- -

- Welcome to your technical assigment. Please, read carefully the - README.md file and follow the instructions. -

- +
+
+
+
+

Books

+
+ +
+
+
+

Favourites

+
+ +
+
+
+
- ) + ); } -export default App +export default App; diff --git a/app/src/components/BookCard.jsx b/app/src/components/BookCard.jsx new file mode 100644 index 0000000..512b65f --- /dev/null +++ b/app/src/components/BookCard.jsx @@ -0,0 +1,39 @@ +import React from "react"; + +// Reusable card component for both Books and Favourites +const BookCard = ({ book, onAction, iconClass }) => { + return ( +
+
+ + {/* Top section */} +
+
{book.author}
+
{book.title}
+

{book.pages}

+
+ {book.title} +
+
+ + {/* Bottom row with buttons */} +
+ + +
+
+
+ ); +}; + +export default BookCard; diff --git a/app/src/components/Books.jsx b/app/src/components/Books.jsx new file mode 100644 index 0000000..535af05 --- /dev/null +++ b/app/src/components/Books.jsx @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from "react"; +import BookCard from "./BookCard"; +import { useFavourites } from "./FavouritesContext"; + +// Books component to fetch and display the first 20 books +const Books = () => { + const [books, setBooks] = useState([]); + const { addToFavourites } = useFavourites(); + +// Fetch books from the JSON file + useEffect(() => { + const fetchBooks = async () => { + try { + const response = await fetch("/books.json"); + const data = await response.json(); + setBooks(data.slice(0, 20)); // Get first 6 books for the moment + } catch (error) { + console.error("Error fetching books:", error); + setBooks([]); + } + }; + + fetchBooks(); + }, []); + + return ( +
+ {books.map((book, index) => ( +
+ +
+ ))} +
+ ); +}; + +export default Books; diff --git a/app/src/components/Favourites.jsx b/app/src/components/Favourites.jsx new file mode 100644 index 0000000..dda9652 --- /dev/null +++ b/app/src/components/Favourites.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import BookCard from "./BookCard"; +import { useFavourites } from "../components/FavouritesContext.jsx"; + +// Favourites component to display the list of favourite books +const Favourites = () => { + const { favourites, removeFromFavourites } = useFavourites(); + return ( +
+ {favourites.length === 0 ? ( +

No favourites yet.

+ ) : ( + favourites.map((book, index) => ( +
+ +
+ )) + )} +
+ ); +}; + +export default Favourites; diff --git a/app/src/components/FavouritesContext.jsx b/app/src/components/FavouritesContext.jsx new file mode 100644 index 0000000..f42d44b --- /dev/null +++ b/app/src/components/FavouritesContext.jsx @@ -0,0 +1,41 @@ +import { createContext, useContext, useState, useEffect} from "react"; + +// Create the context +const FavouritesContext = createContext(); + +// Custom hook to access the context in components +export const useFavourites = () => useContext(FavouritesContext); + +// Provider component +export const FavouritesProvider = ({ children }) => { + // Load initial state from localStorage or set to an empty array + const [favourites, setFavourites] = useState(() => { + const stored = localStorage.getItem("favourites"); + return stored ? JSON.parse(stored) : []; + }); + + // Synch favourites with localStorage whenever it changes + useEffect(() => { + localStorage.setItem("favourites", JSON.stringify(favourites)); + }, [favourites]); + + // Add a book to favourites, if it's not already there + const addToFavourites = (book) => { + if (!favourites.some((fav) => fav.title === book.title)) { + setFavourites([...favourites, book]); + } + }; + + // Remove a book from favourites, checking if it exists + const removeFromFavourites = (bookToRemove) => { + setFavourites(favourites.filter((book) => book.title !== bookToRemove.title)); + }; + + return ( + + {children} + + ); +}; diff --git a/app/src/main.jsx b/app/src/main.jsx index 54b39dd..0469838 100644 --- a/app/src/main.jsx +++ b/app/src/main.jsx @@ -1,10 +1,13 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.jsx' -import './index.css' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; +import "./index.css"; +import { FavouritesProvider } from "./components/FavouritesContext.jsx"; -ReactDOM.createRoot(document.getElementById('root')).render( +ReactDOM.createRoot(document.getElementById("root")).render( - - , -) + + + + +); From 11ea61b54df07a795de7e35d3670aa8c22ce2501 Mon Sep 17 00:00:00 2001 From: alessiadod Date: Thu, 17 Apr 2025 12:11:23 +0200 Subject: [PATCH 2/2] Final version of the App, comments, and answers --- app/src/components/Books.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/components/Books.jsx b/app/src/components/Books.jsx index 535af05..7076792 100644 --- a/app/src/components/Books.jsx +++ b/app/src/components/Books.jsx @@ -13,7 +13,7 @@ const Books = () => { try { const response = await fetch("/books.json"); const data = await response.json(); - setBooks(data.slice(0, 20)); // Get first 6 books for the moment + setBooks(data.slice(0, 20)); } catch (error) { console.error("Error fetching books:", error); setBooks([]);