Skip to content

Commit

Permalink
implemented bookList modal dialog with book details
Browse files Browse the repository at this point in the history
  • Loading branch information
Blockchain-Country committed Dec 24, 2024
1 parent 57e977f commit 3e0c306
Show file tree
Hide file tree
Showing 17 changed files with 299 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Login from './components/user/login/Login'
import Signup from './components/user/signup/Signup'
import Error from './components/error/Error'
import { syncLoadBook } from './redux/slices/booksSlice'
import { auth } from './services/firebaseConfig'
import { auth } from './api/services/firebaseConfig'
import './App.css'

function App() {
Expand Down
File renamed without changes.
39 changes: 38 additions & 1 deletion src/components/BookListSection/BookListSection.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { setError } from '../../redux/slices/errorSlice'
import Book from './book/Book'
Expand All @@ -13,24 +13,38 @@ import {
selectTitleFilter,
selectAuthorsFilter,
} from '../../redux/slices/filterSlice'
import BookListModal from './bookListModal/BookListModal'

const BookListSection = ({ 'data-testid': testId }) => {
const dispatch = useDispatch()
const books = useSelector(selectBook)
const titleFilter = useSelector(selectTitleFilter)
const authorsFilter = useSelector(selectAuthorsFilter)
const [isModalOpen, setIsModalOpen] = useState(false)
const [modalBook, setModalBook] = useState(null)

useEffect(() => {
if (process.env.NODE_ENV !== 'test') {
dispatch(syncLoadBook())
}
}, [dispatch])

useEffect(() => {
if (isModalOpen && modalBook) {
const updatedBookState = books.find((book) => book.id === modalBook.id)
if (updatedBookState) {
setModalBook(updatedBookState)
}
}
}, [books, isModalOpen, modalBook])

const handleDeleteBook = (id) => {
const book = books.find((book) => book.id === id)
if (book) {
if (!book.isFavorite) {
dispatch(syncDeleteBook(id))
setIsModalOpen(false)
setModalBook(null)
} else {
dispatch(setError("Can't Delete Favorite Book!"))
}
Expand All @@ -41,6 +55,19 @@ const BookListSection = ({ 'data-testid': testId }) => {
dispatch(syncToggleFavorite(id))
}

const handleOpenBookModal = (id) => {
const book = books.find((book) => book.id === id)
if (book) {
setIsModalOpen(true)
setModalBook(book)
}
}

const handleCloseBookModal = () => {
setIsModalOpen(false)
setModalBook(null)
}

const filteredBooksArr = books.filter((book) => {
return (
book.title.toLowerCase().includes(titleFilter.toLowerCase()) &&
Expand Down Expand Up @@ -81,11 +108,21 @@ const BookListSection = ({ 'data-testid': testId }) => {
bookAuthor={highlightFilterMatch(book.authors, authorsFilter)}
onHandleDeleteBook={handleDeleteBook}
onToggleFavoriteBook={toggleFavoriteBook}
onClick={() => handleOpenBookModal(book.id)}
data-testid={`bookList_item id=${book.id}`}
/>
))
)}
</ul>
{isModalOpen && (
<BookListModal
isOpen={isModalOpen}
onClose={handleCloseBookModal}
modalBook={modalBook}
onHandleDeleteBook={handleDeleteBook}
onToggleFavoriteBook={toggleFavoriteBook}
></BookListModal>
)}
</section>
)
}
Expand Down
40 changes: 29 additions & 11 deletions src/components/BookListSection/book/Book.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ li[data-testid^='bookList_item'] {
gap: 1rem;
align-items: center;
padding: 0.5rem 1rem;
cursor: pointer;
background-color: #fff;
border-bottom: 1px solid #ccc;
transition: transform 0.3s, background-color 0.3s ease;
}

li[data-testid*='bookList_item']:nth-child(even) {
background-color: #f2f2f2;
}

li[data-testid*='bookList_item']:hover {
background-color: #bcd0fcfe;
li[data-testid^='bookList_item']:hover {
background-color: #eaf3ff;
transform: scale(1.005);
}

/* ----- */

span[data-testid='book_index'] {
text-align: center;
min-width: 2rem;
Expand All @@ -35,8 +37,7 @@ div[data-testid='book_title_and_author_wrapper'] {
overflow: hidden;
}

/* -----title */

/* Title */
div[data-testid='book_title_wrapper'] {
white-space: nowrap;
overflow: hidden;
Expand All @@ -54,8 +55,7 @@ span[data-testid='book_title']::after {
content: '"';
}

/* -----author */

/* Author */
div[data-testid='book_author_wrapper'] {
white-space: nowrap;
overflow: hidden;
Expand All @@ -65,31 +65,49 @@ div[data-testid='book_author_wrapper'] {
span[data-testid='book_author'] {
padding-left: 0.5rem;
font-weight: bold;
transition: color 0.2s ease;
}

/* -----action buttons */
span[data-testid='book_author']:hover {
color: #007bff;
}

/* Action Buttons */
div[data-testid='book_actions'] {
display: flex;
justify-content: center;
align-items: center;
}

button[data-testid^='book_favorite_toggle_'] {
min-width: 0;
padding: 0.5rem;
border: 1px solid #fca510;
background-color: #fff;
transition: background-color 0.3s ease;
}

button[data-testid='book_favorite_toggle_false']:hover,
button[data-testid='book_favorite_toggle_true']:hover {
background-color: #f6dbabfd;
}

[data-testid='book_favorite_icon'],
[data-testid='book_nonFavorite_icon'] {
width: 2.5rem;
height: 2.5rem;
margin: 0rem 1rem;
cursor: pointer;
transition: color 0.3s, transform 0.3s ease-in-out;
color: #fca510;
transition: transform 0.2s, color 0.2s ease;
}

[data-testid='book_favorite_icon']:hover,
[data-testid='book_nonFavorite_icon']:hover {
transform: scale(1.3);
transform: scale(1.1);
color: #e69303;
}

/* Delete Button */
button[data-testid='delete_book_btn'] {
margin: 0 0.5rem;
background-color: #fff;
Expand Down
19 changes: 13 additions & 6 deletions src/components/BookListSection/book/Book.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TbStar } from 'react-icons/tb'
import { TbStarFilled } from 'react-icons/tb'
import './Book.css'
import Button from '../../common/button/Button'
import './Book.css'

const Book = ({
index,
Expand All @@ -10,10 +10,11 @@ const Book = ({
bookAuthor,
onHandleDeleteBook,
onToggleFavoriteBook,
onClick,
'data-testid': testId,
}) => {
return (
<li data-testid={testId}>
<li onClick={onClick} data-testid={testId}>
<span data-testid="book_index">{++index}.</span>
<div data-testid="book_title_and_author_wrapper">
<div data-testid="book_title_wrapper">
Expand All @@ -25,19 +26,25 @@ const Book = ({
</div>
</div>
<div data-testid="book_actions">
<span
onClick={() => onToggleFavoriteBook(book.id)}
<Button
onClick={(e) => {
e.stopPropagation()
onToggleFavoriteBook(book.id)
}}
data-testid={`book_favorite_toggle_${book.isFavorite}`}
>
{book.isFavorite ? (
<TbStarFilled data-testid="book_favorite_icon" />
) : (
<TbStar data-testid="book_nonFavorite_icon" />
)}
</span>
</Button>
<Button
text="Delete"
onClick={() => onHandleDeleteBook(book.id)}
onClick={(e) => {
e.stopPropagation()
onHandleDeleteBook(book.id)
}}
data-testid="delete_book_btn"
></Button>
</div>
Expand Down
107 changes: 107 additions & 0 deletions src/components/BookListSection/bookListModal/BookListModal.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
div[data-testid='bookListModal_body'] {
display: flex;
flex-direction: column;
padding: 1rem;
padding-bottom: 5rem;
overflow-y: auto;
gap: 1rem;
}

/* Book Item */
div[data-testid^='bookListModal_book_item'] {
display: flex;
flex-direction: column;
align-items: stretch;
border-bottom: 1px solid #ddd;
gap: 1rem;
padding: 1rem;
}

div[data-testid='bookListModal_book_content'] {
width: 100%;
display: flex;
gap: 1rem;
}

div[data-testid='bookListModal_left_content'] {
display: flex;
align-items: flex-start;
justify-content: center;
width: 30%;
}

img[data-testid='bookListModal_book_img'] {
min-width: 70%;
max-height: 100%;
padding-top: 3rem;
}

div[data-testid='bookListModal_right_content'] {
width: 70%;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 2rem;
padding: 2rem;
}

div[data-testid='bookListModal_add_book_wrapper'] {
flex-grow: 1;
display: flex;
align-items: flex-end;
justify-content: flex-start;
}

/* Actions Wrapper */
div[data-testid='bookListModal_actions_wrapper'] {
display: flex;
align-items: center;
column-gap: 1rem;
}

/* Favorite Toggle Button */
button[data-testid^='bookListModal_favorite_toggle_'] {
min-width: 0;
padding: 0.5rem;
border: 1px solid #fca510;
background-color: #fff;
/* transition: background-color 0.2s, transform 0.3s ease; */
}

button[data-testid^='bookListModal_favorite_toggle_false']:hover,
button[data-testid^='bookListModal_favorite_toggle_true']:hover {
background-color: #f6dbab;
}

/* Favorite Icon */
[data-testid='bookListModal_favorite_icon'],
[data-testid='bookListModal_nonFavorite_icon'] {
width: 2.5rem;
height: 2.5rem;
cursor: pointer;
transition: transform 0.2s, color 0.3s, ease;
color: #fca510;
}

[data-testid='bookListModal_favorite_icon']:hover,
[data-testid='bookListModal_nonFavorite_icon']:hover {
transform: scale(1.1);
color: #e69303;
}

button[data-testid='bookListModal_delete_btn'] {
margin: 0;
background-color: #fff;
color: red;
border: 1px solid red;
}

button[data-testid='bookListModal_delete_btn']:hover {
background-color: rgb(255, 204, 204);
}

button[data-testid='bookListModal_delete_btn']:disabled {
border: 1px solid darkgray;
color: fff;
background-color: darkgray;
}
Loading

0 comments on commit 3e0c306

Please sign in to comment.