лаба + отчет
BIN
LabWork4Report.docx
Normal file
@@ -1,221 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Панкуха</title>
|
|
||||||
<!-- 1. Bootstrap -->
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- 2. Иконки -->
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="src/css/style.css">
|
|
||||||
</head>
|
|
||||||
<body class="d-flex flex-column min-vh-100">
|
|
||||||
|
|
||||||
<header class="sticky-top navbar navbar-expand-lg navbar-dark bg-black border-bottom border-punk px-0">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<!-- Логотип и название -->
|
|
||||||
<a href="index.html" class="navbar-brand d-flex align-items-center ms-3">
|
|
||||||
<img src="res/logo.png" alt="Панкуха" height="60" class="me-2">
|
|
||||||
<span class="text-punk fs-4 fw-bold">Панкуха</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Кнопка для мобильных -->
|
|
||||||
<button class="navbar-toggler me-3" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Навигация -->
|
|
||||||
<div class="collapse navbar-collapse bg-black" id="navbarContent">
|
|
||||||
<ul class="navbar-nav w-100 justify-content-end pe-4">
|
|
||||||
<li class="nav-item dropdown">
|
|
||||||
<a class="nav-link dropdown-toggle text-punk fw-bold" href="#" data-bs-toggle="dropdown">
|
|
||||||
<i class="bi bi-list-check me-1"></i>Страницы
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-dark border-punk">
|
|
||||||
<li><a class="dropdown-item text-punk" href="index.html"><i class="bi bi-house-door me-2"></i>Главная</a></li>
|
|
||||||
<li><hr class="dropdown-divider border-punk"></li>
|
|
||||||
<li><a class="dropdown-item text-punk" href="grob.html"><i class="bi bi-person-badge me-2"></i>Исполнитель</a></li>
|
|
||||||
<li><a class="dropdown-item text-punk" href="grobKaifIliBolshe.html"><i class="bi bi-music-note-beamed me-2"></i>Песня</a></li>
|
|
||||||
<li><a class="dropdown-item text-punk" href="catalog.html"><i class="bi bi-collection me-2"></i>Каталог</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link text-punk fw-bold" href="https://vk.com/kadyshevever">
|
|
||||||
<i class="bi bi-people-fill me-1"></i>Контакты
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="container my-5 flex-grow-1">
|
|
||||||
<div class="container py-4">
|
|
||||||
<div class="card bg-dark border-punk mb-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<h3 class="text-punk mb-3">
|
|
||||||
<i class="bi bi-people-fill"></i> Тут будет перечень исполнителей, принадлежащих жанру
|
|
||||||
</h3>
|
|
||||||
<p class="lead text-light">
|
|
||||||
Инфа будет +- такая
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul class="list-group list-group-flush">
|
|
||||||
<li class="list-group-item bg-dark text-punk border-punk">
|
|
||||||
<a href="grob.html" class="text-punk text-decoration-none d-flex align-items-center">
|
|
||||||
<i class="bi bi-person-badge me-2"></i> Гражданская Оборона
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item bg-dark text-punk border-punk">
|
|
||||||
<a href="#" class="text-punk text-decoration-none d-flex align-items-center">
|
|
||||||
<i class="bi bi-person-badge me-2"></i> Король и Шут
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item bg-dark text-punk border-punk">
|
|
||||||
<a href="#" class="text-punk text-decoration-none d-flex align-items-center">
|
|
||||||
<i class="bi bi-person-badge me-2"></i> Наив
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Форма добавления исполнителя -->
|
|
||||||
<div class="container my-5">
|
|
||||||
<div class="card bg-dark border-punk">
|
|
||||||
<div class="card-body">
|
|
||||||
<h3 class="text-punk mb-4"><i class="bi bi-person-plus"></i> Добавить исполнителя</h3>
|
|
||||||
|
|
||||||
<form id="artistForm">
|
|
||||||
<!-- Название группы -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label text-light">Название группы</label>
|
|
||||||
<input type="text" class="form-control bg-dark text-light border-punk" id="artistName" required>
|
|
||||||
</div>
|
|
||||||
<!-- Описание -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label text-light">Описание</label>
|
|
||||||
<input type="text" class="form-control bg-dark text-light border-punk" id="description" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Год основания -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label text-light">Год основания</label>
|
|
||||||
<input type="number" class="form-control bg-dark text-light border-punk" id="artistYear" min="1700" max="2025">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Страна -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label text-light">Страна</label>
|
|
||||||
<input type="text" class="form-control bg-dark text-light border-punk" id="artistCountry">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Кнопка добавления -->
|
|
||||||
<button type="submit" class="btn btn-punk mt-3">
|
|
||||||
<i class="bi bi-plus-circle"></i> Добавить исполнителя
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Контейнер для новых карточек -->
|
|
||||||
<div id="artistsContainer" class="row row-cols-1 row-cols-md-3 g-4 mt-4"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.getElementById('artistForm').addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// Получаем данные из формы
|
|
||||||
const name = document.getElementById('artistName').value;
|
|
||||||
const discription = document.getElementById('description').value;
|
|
||||||
const year = document.getElementById('artistYear').value;
|
|
||||||
const country = document.getElementById('artistCountry').value;
|
|
||||||
|
|
||||||
// Валидация
|
|
||||||
if (!name) {
|
|
||||||
alert('Заполните обязательные поля: Название группы!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Создаем новую карточку исполнителя
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'col';
|
|
||||||
card.innerHTML = `
|
|
||||||
<div class="card h-100 bg-dark border-punk artist-card">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title text-punk">${name}</h5>
|
|
||||||
<div class="card-text">
|
|
||||||
${year ? `<small class="text-light">Год основания: ${year}</small><br>` : ''}
|
|
||||||
${country ? `<small class="text-light">Страна: ${country}</small>` : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-outline-punk w-100" data-bs-toggle="modal" data-bs-target="#artistModal-${name.replace(/\s+/g, '-')}">
|
|
||||||
<i class="bi bi-music-note-list"></i> Описание
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Модальное окно для описания -->
|
|
||||||
<div class="modal fade" id="artistModal-${name.replace(/\s+/g, '-')}" tabindex="-1" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
|
||||||
<div class="modal-content bg-dark border-punk">
|
|
||||||
<div class="modal-header border-punk">
|
|
||||||
<h5 class="modal-title text-punk">${name}</h5>
|
|
||||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body text-light">
|
|
||||||
${year ? `<p><strong>Год основания:</strong> ${year}</p>` : ''}
|
|
||||||
${country ? `<p><strong>Страна:</strong> ${country}</p>` : ''}
|
|
||||||
${discription ? `<p><strong>Описание:</strong> ${discription}</p>`: ''}
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer border-punk">
|
|
||||||
<button type="button" class="btn btn-punk" data-bs-dismiss="modal">Закрыть</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Добавляем карточку в контейнер
|
|
||||||
document.getElementById('artistsContainer').appendChild(card);
|
|
||||||
|
|
||||||
// Анимация добавления
|
|
||||||
card.style.opacity = '0';
|
|
||||||
document.getElementById('artistsContainer').prepend(card);
|
|
||||||
setTimeout(() => { card.style.opacity = '1'; }, 10);
|
|
||||||
|
|
||||||
// Очищаем форму
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
// Фокус на первое поле
|
|
||||||
document.getElementById('artistName').focus();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Подвал -->
|
|
||||||
<footer class="bg-black py-3 border-top border-punk mt-auto">
|
|
||||||
<div class="container">
|
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
|
||||||
<p class="mb-0 text-punk">© 2025. Все права защищены.</p>
|
|
||||||
<nav class="d-flex align-items-center">
|
|
||||||
<a href="#" class="text-punk me-3">Политика конфиденциальности</a>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<!-- Bootstrap JS -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import './style.css'
|
|
||||||
import javascriptLogo from './javascript.svg'
|
|
||||||
import viteLogo from '/vite.svg'
|
|
||||||
import { setupCounter } from './counter.js'
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
||||||
import 'bootstrap-icons/font/bootstrap-icons.css';
|
|
||||||
import 'bootstrap';
|
|
||||||
|
|
||||||
document.querySelector('#app').innerHTML = `
|
|
||||||
<div>
|
|
||||||
<a href="https://vite.dev" target="_blank">
|
|
||||||
<img src="${viteLogo}" class="logo" alt="Vite logo" />
|
|
||||||
</a>
|
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank">
|
|
||||||
<img src="${javascriptLogo}" class="logo vanilla" alt="JavaScript logo" />
|
|
||||||
</a>
|
|
||||||
<h1>Hello Vite!</h1>
|
|
||||||
<div class="card">
|
|
||||||
<button id="counter" type="button"></button>
|
|
||||||
</div>
|
|
||||||
<p class="read-the-docs">
|
|
||||||
Click on the Vite logo to learn more
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
|
|
||||||
setupCounter(document.querySelector('#counter'))
|
|
||||||
82
lab4/my-project/db.json
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"id": "28d5",
|
||||||
|
"name": "что-то",
|
||||||
|
"description": "цуцй",
|
||||||
|
"epochId": 1,
|
||||||
|
"countryId": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9e30",
|
||||||
|
"name": "что-то",
|
||||||
|
"description": "бб",
|
||||||
|
"epochId": 2,
|
||||||
|
"countryId": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8ugr",
|
||||||
|
"name": "Летов Игорь",
|
||||||
|
"description": "Беспонтовый пирожок",
|
||||||
|
"epochId": 1,
|
||||||
|
"countryId": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0823",
|
||||||
|
"name": "киш",
|
||||||
|
"description": "бб",
|
||||||
|
"epochId": 1,
|
||||||
|
"countryId": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "94a1",
|
||||||
|
"name": "dfad",
|
||||||
|
"description": "dfas",
|
||||||
|
"epochId": 2,
|
||||||
|
"countryId": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ee1c",
|
||||||
|
"name": "Отчет1",
|
||||||
|
"description": "Очень крутой отчет",
|
||||||
|
"epochId": 3,
|
||||||
|
"countryId": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "Россия"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"name": "США"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"name": "ОАЭ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"name": "Тайга"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"epochs": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "1980-е"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"name": "1990-е"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"name": "2000-е"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"name": "2010-е"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
lab4/my-project/index.zip
Normal file
@@ -9,7 +9,8 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install",
|
||||||
|
"start:server": "json-server --watch db.json --port 3000"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"eslint-config-prettier": "^10.0.2",
|
"eslint-config-prettier": "^10.0.2",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"vite": "^5.1.0",
|
"vite": "^5.1.0",
|
||||||
"vite-plugin-html": "^3.2.0"
|
"vite-plugin-html": "^3.2.0",
|
||||||
|
"json-server": "^0.17.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
195
lab4/my-project/punkrock.html
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Панк-рок | Панкуха</title>
|
||||||
|
|
||||||
|
<!-- Подключаем стили в HEAD -->
|
||||||
|
<!-- 1. Bootstrap CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- 2. Иконки Bootstrap -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<!-- 3. Кастомные стили -->
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body class="d-flex flex-column min-vh-100">
|
||||||
|
|
||||||
|
<header class="sticky-top navbar navbar-expand-lg navbar-dark bg-black border-bottom border-punk px-0">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- Логотип и название -->
|
||||||
|
<a href="index.html" class="navbar-brand d-flex align-items-center ms-3">
|
||||||
|
<img src="res/logo.png" alt="Панкуха" height="60" class="me-2">
|
||||||
|
<span class="text-punk fs-4 fw-bold">Панкуха</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Кнопка для мобильных -->
|
||||||
|
<button class="navbar-toggler me-3" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Навигация -->
|
||||||
|
<div class="collapse navbar-collapse bg-black" id="navbarContent">
|
||||||
|
<ul class="navbar-nav w-100 justify-content-end pe-4">
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle text-punk fw-bold" href="#" data-bs-toggle="dropdown">
|
||||||
|
<i class="bi bi-list-check me-1"></i>Страницы
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-dark border-punk">
|
||||||
|
<li><a class="dropdown-item text-punk" href="index.html"><i class="bi bi-house-door me-2"></i>Главная</a></li>
|
||||||
|
<li><hr class="dropdown-divider border-punk"></li>
|
||||||
|
<li><a class="dropdown-item text-punk" href="grob.html"><i class="bi bi-person-badge me-2"></i>Исполнитель</a></li>
|
||||||
|
<li><a class="dropdown-item text-punk" href="grobKaifIliBolshe.html"><i class="bi bi-music-note-beamed me-2"></i>Песня</a></li>
|
||||||
|
<li><a class="dropdown-item text-punk" href="catalog.html"><i class="bi bi-collection me-2"></i>Каталог</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-punk fw-bold" href="https://vk.com/kadyshevever">
|
||||||
|
<i class="bi bi-people-fill me-1"></i>Контакты
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container my-5 flex-grow-1">
|
||||||
|
<div class="container py-4">
|
||||||
|
<div class="card bg-dark border-punk mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="text-punk mb-3">
|
||||||
|
<i class="bi bi-people-fill"></i> Перечень исполнителей панк-рока
|
||||||
|
</h3>
|
||||||
|
<p class="lead text-light">
|
||||||
|
Список культовых групп жанра
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<li class="list-group-item bg-dark text-punk border-punk">
|
||||||
|
<a href="grob.html" class="text-punk text-decoration-none d-flex align-items-center">
|
||||||
|
<i class="bi bi-person-badge me-2"></i> Гражданская Оборона
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item bg-dark text-punk border-punk">
|
||||||
|
<a href="#" class="text-punk text-decoration-none d-flex align-items-center">
|
||||||
|
<i class="bi bi-person-badge me-2"></i> Король и Шут
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item bg-dark text-punk border-punk">
|
||||||
|
<a href="#" class="text-punk text-decoration-none d-flex align-items-center">
|
||||||
|
<i class="bi bi-person-badge me-2"></i> Наив
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Форма добавления исполнителя -->
|
||||||
|
<div class="container my-5">
|
||||||
|
<div class="card bg-dark border-punk">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="text-punk mb-4"><i class="bi bi-person-plus"></i> Добавить исполнителя</h3>
|
||||||
|
|
||||||
|
<form id="artistForm">
|
||||||
|
<!-- Название группы -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Название группы</label>
|
||||||
|
<input type="text" class="form-control bg-dark text-light border-punk" id="artistName" required>
|
||||||
|
</div>
|
||||||
|
<!-- Описание -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Описание</label>
|
||||||
|
<textarea class="form-control bg-dark text-light border-punk" id="description" required></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Эпоха -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Эпоха</label>
|
||||||
|
<select class="form-select bg-dark text-light border-punk" id="artistEpoch" required>
|
||||||
|
<option value="" selected>Выберите эпоху</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Страна -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Страна</label>
|
||||||
|
<select class="form-select bg-dark text-light border-punk" id="artistCountry" required>
|
||||||
|
<option value="" selected>Выберите страну</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Кнопка добавления -->
|
||||||
|
<button type="submit" class="btn btn-punk mt-3">
|
||||||
|
<i class="bi bi-plus-circle"></i> Добавить исполнителя
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Контейнер для карточек исполнителей -->
|
||||||
|
<div id="artistsContainer" class="row row-cols-1 row-cols-md-3 g-4 mt-4"></div>
|
||||||
|
|
||||||
|
<!-- Модальное окно для редактирования исполнителя -->
|
||||||
|
<div class="modal fade" id="editArtistModal" tabindex="-1" aria-labelledby="editArtistModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content bg-dark border-punk">
|
||||||
|
<div class="modal-header border-punk">
|
||||||
|
<h5 class="modal-title text-punk" id="editArtistModalLabel"><i class="bi bi-pencil-square me-2"></i>Редактировать исполнителя</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="editArtistForm">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Название группы</label>
|
||||||
|
<input type="text" class="form-control bg-dark text-light border-punk" id="editArtistName" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Описание</label>
|
||||||
|
<textarea class="form-control bg-dark text-light border-punk" id="editDescription" required></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Эпоха</label>
|
||||||
|
<select class="form-select bg-dark text-light border-punk" id="editArtistEpoch" required>
|
||||||
|
<option value="" selected>Выберите эпоху</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-light">Страна</label>
|
||||||
|
<select class="form-select bg-dark text-light border-punk" id="editArtistCountry" required>
|
||||||
|
<option value="" selected>Выберите страну</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" id="editArtistId">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer border-punk">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||||
|
<button type="button" class="btn btn-punk" id="saveEditArtist"><i class="bi bi-save"></i> Сохранить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Подвал -->
|
||||||
|
<footer class="bg-black py-3 border-top border-punk mt-auto">
|
||||||
|
<div class="container">
|
||||||
|
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||||
|
<p class="mb-0 text-punk">© 2025. Все права защищены.</p>
|
||||||
|
<nav class="d-flex align-items-center">
|
||||||
|
<a href="#" class="text-punk me-3">Политика конфиденциальности</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Bootstrap JS -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Основной скрипт -->
|
||||||
|
<script type="module" src="src/js/main.js"></script>
|
||||||
|
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'93bfefb2de89bd0c',t:'MTc0NjYxMjcxOC4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 969 KiB After Width: | Height: | Size: 969 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B |
96
lab4/my-project/src/js/controller/ArtistController.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
export default class ArtistController {
|
||||||
|
constructor(model, view) {
|
||||||
|
this.model = model;
|
||||||
|
this.view = view;
|
||||||
|
|
||||||
|
// Привязка обработчиков
|
||||||
|
this.view.bindAddArtist(this.handleAddArtist);
|
||||||
|
this.view.bindDeleteArtist(this.handleDeleteArtist);
|
||||||
|
this.view.bindEditArtist(this.handleEditArtist);
|
||||||
|
this.view.bindSaveEditArtist(this.handleUpdateArtist);
|
||||||
|
|
||||||
|
// Инициализация приложения
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
const [artists, countries, epochs] = await Promise.all([
|
||||||
|
this.model.getAll(),
|
||||||
|
this.model.getCountries(),
|
||||||
|
this.model.getEpochs()
|
||||||
|
]);
|
||||||
|
console.log('Инициализация данных:', { artists, countries, epochs }); // Отладка
|
||||||
|
// Проверяем, есть ли epoch и country у артистов
|
||||||
|
if (artists.length > 0) {
|
||||||
|
console.log('Пример артиста:', artists[0]);
|
||||||
|
}
|
||||||
|
this.view.renderArtists(artists);
|
||||||
|
this.view.initCountries(countries);
|
||||||
|
this.view.initEpochs(epochs);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в init:', error);
|
||||||
|
alert('Ошибка загрузки данных: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAddArtist = async (artist) => {
|
||||||
|
try {
|
||||||
|
console.log('Добавление артиста:', artist); // Отладка
|
||||||
|
await this.model.create(artist);
|
||||||
|
await this.init();
|
||||||
|
alert('Исполнитель добавлен!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в handleAddArtist:', error);
|
||||||
|
alert('Ошибка: ' + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Z
|
||||||
|
handleDeleteArtist = async (id) => {
|
||||||
|
if (confirm('Удалить исполнителя?')) {
|
||||||
|
try {
|
||||||
|
await this.model.delete(id);
|
||||||
|
await this.init();
|
||||||
|
alert('Исполнитель удален!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в handleDeleteArtist:', error);
|
||||||
|
alert('Ошибка: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleEditArtist = async (id) => {
|
||||||
|
try {
|
||||||
|
const artist = await this.model.getById(id);
|
||||||
|
console.log('Редактирование артиста:', artist); // Отладка
|
||||||
|
if (!artist) {
|
||||||
|
throw new Error('Исполнитель не найден');
|
||||||
|
}
|
||||||
|
this.view.showEditForm(artist);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в handleEditArtist:', error);
|
||||||
|
alert('Ошибка: ' + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleUpdateArtist = async (artist) => {
|
||||||
|
try {
|
||||||
|
console.log('Обновление артиста:', artist); // Отладка
|
||||||
|
if (!artist.epochId || !artist.countryId) {
|
||||||
|
throw new Error('Эпоха и страна обязательны');
|
||||||
|
}
|
||||||
|
await this.model.update(artist.id, {
|
||||||
|
name: artist.name,
|
||||||
|
description: artist.description,
|
||||||
|
epochId: artist.epochId,
|
||||||
|
countryId: artist.countryId
|
||||||
|
});
|
||||||
|
await this.init();
|
||||||
|
alert('Изменения сохранены!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в handleUpdateArtist:', error);
|
||||||
|
alert('Ошибка: ' + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
19
lab4/my-project/src/js/main.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import '../css/style.css'
|
||||||
|
import 'bootstrap';
|
||||||
|
|
||||||
|
// Импорты компонентов MVC
|
||||||
|
import ArtistModel from './model/ArtistModel.js';
|
||||||
|
import ArtistView from './view/ArtistView.js';
|
||||||
|
import ArtistController from './controller/ArtistController.js';
|
||||||
|
|
||||||
|
// Инициализация приложения
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
try {
|
||||||
|
const model = new ArtistModel();
|
||||||
|
const view = new ArtistView();
|
||||||
|
new ArtistController(model, view);
|
||||||
|
console.log('Приложение инициализировано');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка инициализации:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
147
lab4/my-project/src/js/model/ArtistModel.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
export default class ArtistModel {
|
||||||
|
constructor() {
|
||||||
|
this.apiUrl = 'http://localhost:3000';
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll() {
|
||||||
|
try {
|
||||||
|
const [artistsResponse, countriesResponse, epochsResponse] = await Promise.all([
|
||||||
|
fetch(`${this.apiUrl}/artists`),
|
||||||
|
fetch(`${this.apiUrl}/countries`),
|
||||||
|
fetch(`${this.apiUrl}/epochs`)
|
||||||
|
]);
|
||||||
|
if (!artistsResponse.ok || !countriesResponse.ok || !epochsResponse.ok) {
|
||||||
|
throw new Error('Ошибка загрузки данных');
|
||||||
|
}
|
||||||
|
const artists = await artistsResponse.json();
|
||||||
|
const countries = await countriesResponse.json();
|
||||||
|
const epochs = await epochsResponse.json();
|
||||||
|
|
||||||
|
console.log('Сырые артисты:', artists);
|
||||||
|
console.log('Страны:', countries);
|
||||||
|
console.log('Эпохи:', epochs);
|
||||||
|
|
||||||
|
const artistsWithRelations = artists.map(artist => {
|
||||||
|
const countryId = Number(artist.countryId); // Приводим к числу
|
||||||
|
const epochId = Number(artist.epochId); // Приводим к числу
|
||||||
|
const country = countries.find(c => Number(c.id) === countryId) || { id: null, name: 'Не указана' };
|
||||||
|
const epoch = epochs.find(e => Number(e.id) === epochId) || { id: null, name: 'Не указана' };
|
||||||
|
console.log(`Артист ${artist.name}: countryId=${countryId}, country=${JSON.stringify(country)}, epochId=${epochId}, epoch=${JSON.stringify(epoch)}`);
|
||||||
|
return {
|
||||||
|
...artist,
|
||||||
|
country,
|
||||||
|
epoch
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Артисты с отношениями:', artistsWithRelations);
|
||||||
|
return artistsWithRelations;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в getAll:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getById(id) {
|
||||||
|
try {
|
||||||
|
const [artistResponse, countriesResponse, epochsResponse] = await Promise.all([
|
||||||
|
fetch(`${this.apiUrl}/artists/${id}`),
|
||||||
|
fetch(`${this.apiUrl}/countries`),
|
||||||
|
fetch(`${this.apiUrl}/epochs`)
|
||||||
|
]);
|
||||||
|
if (!artistResponse.ok || !countriesResponse.ok || !epochsResponse.ok) {
|
||||||
|
throw new Error('Ошибка загрузки данных');
|
||||||
|
}
|
||||||
|
const artist = await artistResponse.json();
|
||||||
|
const countries = await countriesResponse.json();
|
||||||
|
const epochs = await epochsResponse.json();
|
||||||
|
|
||||||
|
const countryId = Number(artist.countryId); // Приводим к числу
|
||||||
|
const epochId = Number(artist.epochId); // Приводим к числу
|
||||||
|
const country = countries.find(c => Number(c.id) === countryId) || { id: null, name: 'Не указана' };
|
||||||
|
const epoch = epochs.find(e => Number(e.id) === epochId) || { id: null, name: 'Не указана' };
|
||||||
|
console.log(`Артист ${artist.name}: countryId=${countryId}, country=${JSON.stringify(country)}, epochId=${epochId}, epoch=${JSON.stringify(epoch)}`);
|
||||||
|
|
||||||
|
const artistWithRelations = {
|
||||||
|
...artist,
|
||||||
|
country,
|
||||||
|
epoch
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Полученный артист с отношениями:', artistWithRelations);
|
||||||
|
return artistWithRelations;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в getById:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCountries() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiUrl}/countries`);
|
||||||
|
if (!response.ok) throw new Error('Ошибка загрузки стран');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в getCountries:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEpochs() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiUrl}/epochs`);
|
||||||
|
if (!response.ok) throw new Error('Ошибка загрузки эпох');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в getEpochs:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(artist) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiUrl}/artists`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(artist)
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Ошибка создания исполнителя');
|
||||||
|
const createdArtist = await response.json();
|
||||||
|
console.log('Созданный артист:', createdArtist);
|
||||||
|
return createdArtist;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в create:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id, artist) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiUrl}/artists/${id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(artist)
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Ошибка обновления исполнителя');
|
||||||
|
const updatedArtist = await response.json();
|
||||||
|
console.log('Обновленный артист:', updatedArtist);
|
||||||
|
return updatedArtist;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в update:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${this.apiUrl}/artists/${id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Ошибка удаления исполнителя');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в delete:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
148
lab4/my-project/src/js/view/ArtistView.js
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
export default class ArtistView {
|
||||||
|
constructor() {
|
||||||
|
this.artistsContainer = document.getElementById('artistsContainer');
|
||||||
|
this.artistForm = document.getElementById('artistForm');
|
||||||
|
this.editArtistModal = document.getElementById('editArtistModal');
|
||||||
|
this.saveEditArtistBtn = document.getElementById('saveEditArtist');
|
||||||
|
|
||||||
|
if (!this.artistsContainer || !this.artistForm || !this.editArtistModal || !this.saveEditArtistBtn) {
|
||||||
|
console.error('Не найдены необходимые элементы DOM');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderArtists(artists) {
|
||||||
|
console.log('Рендеринг артистов:', artists); // Отладка
|
||||||
|
this.artistsContainer.innerHTML = artists.map(artist => {
|
||||||
|
console.log('Обработка артиста:', artist); // Отладка
|
||||||
|
return `
|
||||||
|
<div class="col-md-4 mb-4">
|
||||||
|
<div class="card bg-dark border-punk">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title text-punk">${artist.name}</h5>
|
||||||
|
<p class="card-text text-light">${artist.description || 'Нет описания'}</p>
|
||||||
|
<p class="card-text text-light"><small>Эпоха: ${artist.epoch?.name || 'Не указана'}</small></p>
|
||||||
|
<p class="card-text text-light"><small>Страна: ${artist.country?.name || 'Не указана'}</small></p>
|
||||||
|
<button class="btn btn-outline-primary edit-btn me-2" data-id="${artist.id}">
|
||||||
|
<i class="bi bi-pencil-square"></i> Изменить
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-danger delete-btn" data-id="${artist.id}">
|
||||||
|
<i class="bi bi-trash"></i> Удалить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
initCountries(countries) {
|
||||||
|
const renderSelect = (selectId) => {
|
||||||
|
const select = document.getElementById(selectId);
|
||||||
|
select.innerHTML = '<option value="">Выберите страну</option>';
|
||||||
|
countries.forEach(country => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = country.id;
|
||||||
|
option.textContent = country.name;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderSelect('artistCountry');
|
||||||
|
renderSelect('editArtistCountry');
|
||||||
|
}
|
||||||
|
|
||||||
|
initEpochs(epochs) {
|
||||||
|
const renderSelect = (selectId) => {
|
||||||
|
const select = document.getElementById(selectId);
|
||||||
|
select.innerHTML = '<option value="">Выберите эпоху</option>';
|
||||||
|
epochs.forEach(epoch => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = epoch.id;
|
||||||
|
option.textContent = epoch.name;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderSelect('artistEpoch');
|
||||||
|
renderSelect('editArtistEpoch');
|
||||||
|
}
|
||||||
|
|
||||||
|
showEditForm(artist) {
|
||||||
|
console.log('Открытие формы редактирования:', artist); // Отладка
|
||||||
|
document.getElementById('editArtistName').value = artist.name || '';
|
||||||
|
document.getElementById('editDescription').value = artist.description || '';
|
||||||
|
document.getElementById('editArtistEpoch').value = artist.epoch?.id || artist.epochId || '';
|
||||||
|
document.getElementById('editArtistCountry').value = artist.country?.id || artist.countryId || '';
|
||||||
|
document.getElementById('editArtistId').value = artist.id;
|
||||||
|
|
||||||
|
new bootstrap.Modal(this.editArtistModal).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
bindAddArtist(handler) {
|
||||||
|
this.artistForm.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const artist = {
|
||||||
|
name: document.getElementById('artistName').value.trim(),
|
||||||
|
description: document.getElementById('description').value.trim(),
|
||||||
|
epochId: parseInt(document.getElementById('artistEpoch').value) || null,
|
||||||
|
countryId: parseInt(document.getElementById('artistCountry').value) || null
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Данные для добавления:', artist); // Отладка
|
||||||
|
if (!artist.name || !artist.description || !artist.epochId || !artist.countryId) {
|
||||||
|
alert('Все поля обязательны!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await handler(artist);
|
||||||
|
this.artistForm.reset();
|
||||||
|
} catch (error) {
|
||||||
|
alert('Ошибка при добавлении исполнителя: ' + error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bindDeleteArtist(handler) {
|
||||||
|
this.artistsContainer.addEventListener('click', (e) => {
|
||||||
|
if (e.target.closest('.delete-btn')) {
|
||||||
|
const id = e.target.closest('.delete-btn').dataset.id;
|
||||||
|
handler(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEditArtist(handler) {
|
||||||
|
this.artistsContainer.addEventListener('click', (e) => {
|
||||||
|
if (e.target.closest('.edit-btn')) {
|
||||||
|
const id = e.target.closest('.edit-btn').dataset.id;
|
||||||
|
handler(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bindSaveEditArtist(handler) {
|
||||||
|
this.saveEditArtistBtn.addEventListener('click', async () => {
|
||||||
|
const artist = {
|
||||||
|
id: document.getElementById('editArtistId').value,
|
||||||
|
name: document.getElementById('editArtistName').value.trim(),
|
||||||
|
description: document.getElementById('editDescription').value.trim(),
|
||||||
|
epochId: parseInt(document.getElementById('editArtistEpoch').value) || null,
|
||||||
|
countryId: parseInt(document.getElementById('editArtistCountry').value) || null
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Данные для обновления:', artist); // Отладка
|
||||||
|
if (!artist.name || !artist.description || !artist.epochId || !artist.countryId) {
|
||||||
|
alert('Все поля обязательны!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await handler(artist);
|
||||||
|
bootstrap.Modal.getInstance(this.editArtistModal).hide();
|
||||||
|
} catch (error) {
|
||||||
|
alert('Ошибка при обновлении исполнителя: ' + error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||