Skip to content

Commit f3a4e3e

Browse files
authored
Merge pull request #4 from ArnauACR/development
feature(components): book components UI
2 parents e6cfa12 + ec16fa0 commit f3a4e3e

File tree

6 files changed

+417
-0
lines changed

6 files changed

+417
-0
lines changed

src/components/BookCard.jsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import styles from './BookCard.module.css';
2+
3+
const BookCard = ({ book, onEdit, onDelete }) => {
4+
const getStatusClass = (status) => {
5+
switch (status) {
6+
case 'pending':
7+
return styles['book-card__status--pending'];
8+
case 'in progress':
9+
return styles['book-card__status--in-progress'];
10+
case 'read':
11+
return styles['book-card__status--read'];
12+
default:
13+
return styles['book-card__status--pending'];
14+
}
15+
};
16+
17+
return (
18+
<div className={styles['book-card']}>
19+
<div className={styles['book-card__header']}>
20+
<h3 className={styles['book-card__title']}>{book.title}</h3>
21+
<span className={styles['book-card__year']}>{book.year}</span>
22+
</div>
23+
24+
<p className={styles['book-card__author']}>{book.author}</p>
25+
26+
<div className={styles['book-card__status-container']}>
27+
<span
28+
className={`${styles['book-card__status']} ${getStatusClass(book.status)}`}
29+
>
30+
{book.status}
31+
</span>
32+
</div>
33+
34+
<div className={styles['book-card__actions']}>
35+
<button
36+
className={`${styles['book-card__button']} ${styles['book-card__button--edit']}`}
37+
onClick={() => onEdit(book)}
38+
>
39+
Edit
40+
</button>
41+
<button
42+
className={`${styles['book-card__button']} ${styles['book-card__button--delete']}`}
43+
onClick={() => onDelete(book.id)}
44+
>
45+
Delete
46+
</button>
47+
</div>
48+
</div>
49+
);
50+
};
51+
52+
export default BookCard;

src/components/BookCard.module.css

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
.book-card {
2+
background: white;
3+
border: 1px solid #e9ecef;
4+
border-radius: 4px;
5+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
6+
padding: 1rem;
7+
transition: box-shadow 0.2s;
8+
}
9+
10+
.book-card:hover {
11+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
12+
}
13+
14+
.book-card__header {
15+
display: flex;
16+
justify-content: space-between;
17+
align-items: flex-start;
18+
margin-bottom: 0.5rem;
19+
gap: 1rem;
20+
}
21+
22+
.book-card__title {
23+
margin: 0;
24+
font-size: 1.125rem;
25+
font-weight: 700;
26+
color: #212529;
27+
line-height: 1.3;
28+
flex: 1;
29+
}
30+
31+
.book-card__year {
32+
background-color: #e9ecef;
33+
color: #6c757d;
34+
padding: 0.25rem 0.5rem;
35+
border-radius: 3px;
36+
font-size: 0.75rem;
37+
font-weight: 500;
38+
white-space: nowrap;
39+
}
40+
41+
.book-card__author {
42+
margin: 0 0 1rem 0;
43+
color: #6c757d;
44+
font-size: 0.875rem;
45+
}
46+
47+
.book-card__status-container {
48+
margin-bottom: 1rem;
49+
padding-bottom: 1rem;
50+
border-bottom: 1px solid #e9ecef;
51+
}
52+
53+
.book-card__status {
54+
display: inline-block;
55+
padding: 0.25rem 0.5rem;
56+
border-radius: 3px;
57+
font-size: 0.75rem;
58+
font-weight: 500;
59+
text-transform: lowercase;
60+
}
61+
62+
.book-card__status--pending {
63+
background-color: #fff5d9;
64+
color: #ec8a4d;
65+
}
66+
67+
.book-card__status--in-progress {
68+
background-color: #ddeffd;
69+
color: #2e5eab;
70+
}
71+
72+
.book-card__status--read {
73+
background-color: #d4edda;
74+
color: #42a646;
75+
}
76+
77+
.book-card__actions {
78+
display: flex;
79+
gap: 0.5rem;
80+
}
81+
82+
.book-card__button {
83+
flex: 1;
84+
padding: 0.5rem 1rem;
85+
border-radius: 3px;
86+
font-size: 0.875rem;
87+
cursor: pointer;
88+
transition: all 0.2s;
89+
border: 1px solid;
90+
font-weight: 400;
91+
}
92+
93+
.book-card__button--delete {
94+
background-color: #fde7e9;
95+
color: #ac3031;
96+
border-color: #fde7e9;
97+
}
98+
99+
.book-card__button--edit {
100+
background-color: #f8f9fa;
101+
color: #495057;
102+
border-color: #ced4da;
103+
}
104+
105+
@media (min-width: 768px) {
106+
.book-card {
107+
padding: 1.5rem;
108+
}
109+
110+
.book-card__title {
111+
font-size: 1.25rem;
112+
}
113+
114+
.book-card__author {
115+
font-size: 1rem;
116+
}
117+
}

src/components/BookForm.jsx

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { useState, useEffect } from 'react';
2+
import styles from './BookForm.module.css';
3+
4+
const BookForm = ({ book, onSubmit, onCancel }) => {
5+
const [formData, setFormData] = useState({
6+
title: '',
7+
author: '',
8+
year: new Date().getFullYear(),
9+
status: 'pending',
10+
});
11+
12+
useEffect(() => {
13+
if (book) {
14+
setFormData({
15+
title: book.title,
16+
author: book.author,
17+
year: book.year,
18+
status: book.status,
19+
});
20+
}
21+
}, [book]);
22+
23+
const handleSubmit = (e) => {
24+
e.preventDefault();
25+
if (formData.title.trim() && formData.author.trim()) {
26+
onSubmit(formData);
27+
}
28+
};
29+
30+
const handleChange = (e) => {
31+
const { name, value } = e.target;
32+
setFormData((prev) => ({
33+
...prev,
34+
[name]: value,
35+
}));
36+
};
37+
38+
return (
39+
<div className={styles['book-form']}>
40+
<h2 className={styles['book-form__title']}>
41+
{book ? 'Edit Book' : 'Add New Book'}
42+
</h2>
43+
44+
<form onSubmit={handleSubmit} className={styles['book-form__form']}>
45+
<div className={styles['book-form__group']}>
46+
<label htmlFor="title" className={styles['book-form__label']}>
47+
Title
48+
</label>
49+
<input
50+
type="text"
51+
id="title"
52+
name="title"
53+
value={formData.title}
54+
onChange={handleChange}
55+
placeholder="Enter book title"
56+
className={styles['book-form__input']}
57+
required
58+
/>
59+
</div>
60+
61+
<div className={styles['book-form__group']}>
62+
<label htmlFor="author" className={styles['book-form__label']}>
63+
Autor
64+
</label>
65+
<input
66+
type="text"
67+
id="author"
68+
name="author"
69+
value={formData.author}
70+
onChange={handleChange}
71+
placeholder="Enter author name"
72+
className={styles['book-form__input']}
73+
required
74+
/>
75+
</div>
76+
77+
<div className={styles['book-form__row']}>
78+
<div className={styles['book-form__group']}>
79+
<label htmlFor="year" className={styles['book-form__label']}>
80+
Publication Year
81+
</label>
82+
<input
83+
type="number"
84+
id="year"
85+
name="year"
86+
value={formData.year}
87+
onChange={handleChange}
88+
className={styles['book-form__input']}
89+
min="1000"
90+
max={new Date().getFullYear() + 10}
91+
required
92+
/>
93+
</div>
94+
95+
<div className={styles['book-form__group']}>
96+
<label htmlFor="status" className={styles['book-form__label']}>
97+
Reading Status
98+
</label>
99+
<select
100+
id="status"
101+
name="status"
102+
value={formData.status}
103+
onChange={handleChange}
104+
className={styles['book-form__select']}
105+
>
106+
<option value="pending">Pending</option>
107+
<option value="in progress">In Progress</option>
108+
<option value="read">Read</option>
109+
</select>
110+
</div>
111+
</div>
112+
113+
<button type="submit" className={styles['book-form__button']}>
114+
{book ? 'Update Book' : 'Add Book'}
115+
</button>
116+
</form>
117+
</div>
118+
);
119+
};
120+
121+
export default BookForm;

src/components/BookForm.module.css

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
.book-form {
2+
background: white;
3+
margin: 1rem;
4+
padding: 1.5rem;
5+
border: 1px solid #e9ecef;
6+
border-radius: 4px;
7+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
8+
}
9+
10+
.book-form__title {
11+
margin: 0 0 1.5rem 0;
12+
font-size: 1.25rem;
13+
color: #212529;
14+
font-weight: 600;
15+
}
16+
17+
.book-form__form {
18+
display: flex;
19+
flex-direction: column;
20+
gap: 1rem;
21+
}
22+
23+
.book-form__group {
24+
display: flex;
25+
flex-direction: column;
26+
}
27+
28+
.book-form__row {
29+
display: grid;
30+
grid-template-columns: 1fr;
31+
gap: 1rem;
32+
}
33+
34+
.book-form__label {
35+
margin-bottom: 0.5rem;
36+
font-weight: 500;
37+
color: #212529;
38+
font-size: 0.875rem;
39+
}
40+
41+
.book-form__input,
42+
.book-form__select {
43+
padding: 0.5rem 0.75rem;
44+
border: 1px solid #ced4da;
45+
border-radius: 4px;
46+
font-size: 1rem;
47+
transition:
48+
border-color 0.2s,
49+
box-shadow 0.2s;
50+
background-color: white;
51+
}
52+
53+
.book-form__input:focus,
54+
.book-form__select:focus {
55+
outline: none;
56+
border-color: #80bdff;
57+
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
58+
}
59+
60+
.book-form__input::placeholder {
61+
color: #6c757d;
62+
}
63+
64+
.book-form__select {
65+
cursor: pointer;
66+
}
67+
68+
.book-form__button {
69+
background-color: #42a646;
70+
color: white;
71+
border: none;
72+
padding: 0.75rem 1rem;
73+
border-radius: 4px;
74+
font-size: 0.9rem;
75+
font-weight: 500;
76+
cursor: pointer;
77+
transition: background-color 0.2s;
78+
margin-top: 0.5rem;
79+
align-self: flex-end;
80+
}
81+
82+
@media (min-width: 768px) {
83+
.book-form {
84+
margin: 1rem auto;
85+
max-width: 600px;
86+
padding: 2rem;
87+
}
88+
89+
.book-form__title {
90+
font-size: 1.5rem;
91+
}
92+
93+
.book-form__row {
94+
grid-template-columns: 1fr 1fr;
95+
}
96+
97+
.book-form__button {
98+
padding: 0.75rem 1.5rem;
99+
font-size: 1rem;
100+
}
101+
}

0 commit comments

Comments
 (0)