Skip to content
Merged
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
52 changes: 52 additions & 0 deletions src/components/BookCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import styles from './BookCard.module.css';

const BookCard = ({ book, onEdit, onDelete }) => {
const getStatusClass = (status) => {
switch (status) {
case 'pending':
return styles['book-card__status--pending'];
case 'in progress':
return styles['book-card__status--in-progress'];
case 'read':
return styles['book-card__status--read'];
default:
return styles['book-card__status--pending'];
}
};

return (
<div className={styles['book-card']}>
<div className={styles['book-card__header']}>
<h3 className={styles['book-card__title']}>{book.title}</h3>
<span className={styles['book-card__year']}>{book.year}</span>
</div>

<p className={styles['book-card__author']}>{book.author}</p>

<div className={styles['book-card__status-container']}>
<span
className={`${styles['book-card__status']} ${getStatusClass(book.status)}`}
>
{book.status}
</span>
</div>

<div className={styles['book-card__actions']}>
<button
className={`${styles['book-card__button']} ${styles['book-card__button--edit']}`}
onClick={() => onEdit(book)}
>
Edit
</button>
<button
className={`${styles['book-card__button']} ${styles['book-card__button--delete']}`}
onClick={() => onDelete(book.id)}
>
Delete
</button>
</div>
</div>
);
};

export default BookCard;
117 changes: 117 additions & 0 deletions src/components/BookCard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
.book-card {
background: white;
border: 1px solid #e9ecef;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
padding: 1rem;
transition: box-shadow 0.2s;
}

.book-card:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.book-card__header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.5rem;
gap: 1rem;
}

.book-card__title {
margin: 0;
font-size: 1.125rem;
font-weight: 700;
color: #212529;
line-height: 1.3;
flex: 1;
}

.book-card__year {
background-color: #e9ecef;
color: #6c757d;
padding: 0.25rem 0.5rem;
border-radius: 3px;
font-size: 0.75rem;
font-weight: 500;
white-space: nowrap;
}

.book-card__author {
margin: 0 0 1rem 0;
color: #6c757d;
font-size: 0.875rem;
}

.book-card__status-container {
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e9ecef;
}

.book-card__status {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 3px;
font-size: 0.75rem;
font-weight: 500;
text-transform: lowercase;
}

.book-card__status--pending {
background-color: #fff5d9;
color: #ec8a4d;
}

.book-card__status--in-progress {
background-color: #ddeffd;
color: #2e5eab;
}

.book-card__status--read {
background-color: #d4edda;
color: #42a646;
}

.book-card__actions {
display: flex;
gap: 0.5rem;
}

.book-card__button {
flex: 1;
padding: 0.5rem 1rem;
border-radius: 3px;
font-size: 0.875rem;
cursor: pointer;
transition: all 0.2s;
border: 1px solid;
font-weight: 400;
}

.book-card__button--delete {
background-color: #fde7e9;
color: #ac3031;
border-color: #fde7e9;
}

.book-card__button--edit {
background-color: #f8f9fa;
color: #495057;
border-color: #ced4da;
}

@media (min-width: 768px) {
.book-card {
padding: 1.5rem;
}

.book-card__title {
font-size: 1.25rem;
}

.book-card__author {
font-size: 1rem;
}
}
121 changes: 121 additions & 0 deletions src/components/BookForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useState, useEffect } from 'react';
import styles from './BookForm.module.css';

const BookForm = ({ book, onSubmit, onCancel }) => {
const [formData, setFormData] = useState({
title: '',
author: '',
year: new Date().getFullYear(),
status: 'pending',
});

useEffect(() => {
if (book) {
setFormData({
title: book.title,
author: book.author,
year: book.year,
status: book.status,
});
}
}, [book]);

const handleSubmit = (e) => {
e.preventDefault();
if (formData.title.trim() && formData.author.trim()) {
onSubmit(formData);
}
};

const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};

return (
<div className={styles['book-form']}>
<h2 className={styles['book-form__title']}>
{book ? 'Edit Book' : 'Add New Book'}
</h2>

<form onSubmit={handleSubmit} className={styles['book-form__form']}>
<div className={styles['book-form__group']}>
<label htmlFor="title" className={styles['book-form__label']}>
Title
</label>
<input
type="text"
id="title"
name="title"
value={formData.title}
onChange={handleChange}
placeholder="Enter book title"
className={styles['book-form__input']}
required
/>
</div>

<div className={styles['book-form__group']}>
<label htmlFor="author" className={styles['book-form__label']}>
Autor
</label>
<input
type="text"
id="author"
name="author"
value={formData.author}
onChange={handleChange}
placeholder="Enter author name"
className={styles['book-form__input']}
required
/>
</div>

<div className={styles['book-form__row']}>
<div className={styles['book-form__group']}>
<label htmlFor="year" className={styles['book-form__label']}>
Publication Year
</label>
<input
type="number"
id="year"
name="year"
value={formData.year}
onChange={handleChange}
className={styles['book-form__input']}
min="1000"
max={new Date().getFullYear() + 10}
required
/>
</div>

<div className={styles['book-form__group']}>
<label htmlFor="status" className={styles['book-form__label']}>
Reading Status
</label>
<select
id="status"
name="status"
value={formData.status}
onChange={handleChange}
className={styles['book-form__select']}
>
<option value="pending">Pending</option>
<option value="in progress">In Progress</option>
<option value="read">Read</option>
</select>
</div>
</div>

<button type="submit" className={styles['book-form__button']}>
{book ? 'Update Book' : 'Add Book'}
</button>
</form>
</div>
);
};

export default BookForm;
101 changes: 101 additions & 0 deletions src/components/BookForm.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
.book-form {
background: white;
margin: 1rem;
padding: 1.5rem;
border: 1px solid #e9ecef;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.book-form__title {
margin: 0 0 1.5rem 0;
font-size: 1.25rem;
color: #212529;
font-weight: 600;
}

.book-form__form {
display: flex;
flex-direction: column;
gap: 1rem;
}

.book-form__group {
display: flex;
flex-direction: column;
}

.book-form__row {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}

.book-form__label {
margin-bottom: 0.5rem;
font-weight: 500;
color: #212529;
font-size: 0.875rem;
}

.book-form__input,
.book-form__select {
padding: 0.5rem 0.75rem;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 1rem;
transition:
border-color 0.2s,
box-shadow 0.2s;
background-color: white;
}

.book-form__input:focus,
.book-form__select:focus {
outline: none;
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}

.book-form__input::placeholder {
color: #6c757d;
}

.book-form__select {
cursor: pointer;
}

.book-form__button {
background-color: #42a646;
color: white;
border: none;
padding: 0.75rem 1rem;
border-radius: 4px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
margin-top: 0.5rem;
align-self: flex-end;
}

@media (min-width: 768px) {
.book-form {
margin: 1rem auto;
max-width: 600px;
padding: 2rem;
}

.book-form__title {
font-size: 1.5rem;
}

.book-form__row {
grid-template-columns: 1fr 1fr;
}

.book-form__button {
padding: 0.75rem 1.5rem;
font-size: 1rem;
}
}
Loading