Files
PIBD-23_Ivanov.D.A._Interne…/MyWebSite/components/BookModal.jsx
2025-12-12 15:34:40 +04:00

278 lines
8.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
import { Modal, Button, Form, Alert } from 'react-bootstrap';
import api from '../services/api';
const BookModal = ({ show, onHide, bookId, genres, onSave }) => {
const [formData, setFormData] = useState({
title: '',
author: '',
price: '',
description: '',
image: ''
});
const [selectedGenreIds, setSelectedGenreIds] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
useEffect(() => {
if (bookId && show) {
loadBookData();
} else {
resetForm();
}
}, [bookId, show]);
const loadBookData = async () => {
if (!bookId) return;
try {
setLoading(true);
setError('');
// Загружаем данные книги
const bookResponse = await api.fetchBook(bookId);
const book = bookResponse.data;
setFormData({
title: book.title || '',
author: book.author || '',
price: book.price || '',
description: book.description || '',
image: book.image || ''
});
// Загружаем жанры книги, если есть ID
if (bookId) {
try {
const genresResponse = await api.fetchBookGenres(bookId);
const bookGenres = genresResponse.data || [];
setSelectedGenreIds(bookGenres.map(gb => gb.genre.id));
} catch (genreError) {
console.error('Ошибка загрузки жанров книги:', genreError);
setSelectedGenreIds([]);
}
}
} catch (error) {
console.error('Ошибка загрузки книги:', error);
setError('Не удалось загрузить данные книги');
} finally {
setLoading(false);
}
};
const resetForm = () => {
setFormData({
title: '',
author: '',
price: '',
description: '',
image: ''
});
setSelectedGenreIds([]);
setError('');
};
const handleGenreChange = (genreId) => {
setSelectedGenreIds(prev => {
if (prev.includes(genreId)) {
return prev.filter(id => id !== genreId);
} else {
return [...prev, genreId];
}
});
};
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
try {
const bookData = {
title: formData.title,
author: formData.author,
price: Number(formData.price) || 0,
description: formData.description,
image: formData.image
};
let savedBook;
if (bookId) {
// Обновляем книгу
savedBook = await api.updateBook(bookId, bookData);
// Обновляем жанры книги
const currentGenresResponse = await api.fetchBookGenres(bookId);
const currentGenreIds = (currentGenresResponse.data || []).map(gb => gb.genre.id);
// Удаляем жанры, которые были убраны
for (const currentGenreId of currentGenreIds) {
if (!selectedGenreIds.includes(currentGenreId)) {
await api.deleteBookGenre(bookId, currentGenreId);
}
}
// Добавляем новые жанры
for (const newGenreId of selectedGenreIds) {
if (!currentGenreIds.includes(newGenreId)) {
await api.addBookGenre(bookId, {
genreId: newGenreId,
date: new Date().toISOString().split('T')[0] // Текущая дата в формате YYYY-MM-DD
});
}
}
} else {
// Создаем новую книгу
savedBook = await api.createBook(bookData);
const newBookId = savedBook.data.id;
// Добавляем жанры для новой книги
for (const genreId of selectedGenreIds) {
await api.addBookGenre(newBookId, {
genreId: genreId,
date: new Date().toISOString().split('T')[0]
});
}
}
onSave(bookData);
} catch (error) {
console.error('Ошибка сохранения книги:', error);
setError('Не удалось сохранить книгу');
}
};
const handleClose = () => {
resetForm();
onHide();
};
if (loading) {
return (
<Modal show={show} onHide={handleClose} size="lg">
<Modal.Header closeButton>
<Modal.Title>{bookId ? 'Редактировать книгу' : 'Добавить книгу'}</Modal.Title>
</Modal.Header>
<Modal.Body className="text-center">
<div className="py-4">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Загрузка...</span>
</div>
<p className="mt-2">Загрузка данных...</p>
</div>
</Modal.Body>
</Modal>
);
}
return (
<Modal show={show} onHide={handleClose} size="lg">
<Modal.Header closeButton>
<Modal.Title>{bookId ? 'Редактировать книгу' : 'Добавить книгу'}</Modal.Title>
</Modal.Header>
<Modal.Body>
{error && (
<Alert variant="danger" className="mb-3">
{error}
</Alert>
)}
<Form onSubmit={handleSubmit}>
<Form.Group className="mb-3">
<Form.Label>Название книги *</Form.Label>
<Form.Control
type="text"
value={formData.title}
onChange={(e) => setFormData({...formData, title: e.target.value})}
required
placeholder="Введите название книги"
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Автор *</Form.Label>
<Form.Control
type="text"
value={formData.author}
onChange={(e) => setFormData({...formData, author: e.target.value})}
required
placeholder="Введите автора"
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Жанры</Form.Label>
<div className="border rounded p-3" style={{ maxHeight: '200px', overflowY: 'auto' }}>
{genres.length === 0 ? (
<p className="text-muted mb-0">Нет доступных жанров</p>
) : (
genres.map(genre => (
<Form.Check
key={genre.id}
type="checkbox"
id={`genre-${genre.id}`}
label={genre.name}
checked={selectedGenreIds.includes(genre.id)}
onChange={() => handleGenreChange(genre.id)}
className="mb-2"
/>
))
)}
</div>
<Form.Text className="text-muted">
Можно выбрать несколько жанров
</Form.Text>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Цена *</Form.Label>
<Form.Control
type="number"
value={formData.price}
onChange={(e) => setFormData({...formData, price: e.target.value})}
required
min="0"
step="1"
placeholder="0"
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Описание</Form.Label>
<Form.Control
as="textarea"
rows={3}
value={formData.description}
onChange={(e) => setFormData({...formData, description: e.target.value})}
placeholder="Введите описание книги"
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Изображение</Form.Label>
<Form.Control
type="text"
value={formData.image}
onChange={(e) => setFormData({...formData, image: e.target.value})}
placeholder="URL изображения или путь к файлу"
/>
<Form.Text className="text-muted">
Можно указать полный URL (https://...) или относительный путь (images/book.jpg)
</Form.Text>
</Form.Group>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Отмена
</Button>
<Button variant="primary" type="submit">
{bookId ? 'Обновить' : 'Добавить'}
</Button>
</Modal.Footer>
</Form>
</Modal.Body>
</Modal>
);
};
export default BookModal;