lab4
This commit is contained in:
266
add-movie.html
266
add-movie.html
@@ -1,140 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Добавить фильм - Online Cinema Theater</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Добавить новый фильм - Online Cinema Theater</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<style>
|
||||
.preview-container img {
|
||||
max-height: 300px;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Навигационная панель -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark-custom fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center" href="index.html">
|
||||
<img src="resources/logo.webp" alt="Online Cinema Theater Logo" width="50" height="50" class="me-2">
|
||||
<span>Online Cinema Theater</span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.html">Главная</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="catalog.html">Каталог</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="reviews.html">Рецензии</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
|
||||
Что глянуть?
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark">
|
||||
<li><a class="dropdown-item" href="films.html">Фильмы</a></li>
|
||||
<li><a class="dropdown-item" href="seriales.html">Сериалы</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<body class="bg-dark text-light">
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.html">
|
||||
<i class="bi bi-film text-danger"></i> Online Cinema
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.html">Главная</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="catalog.html">Каталог</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="reviews.html">Рецензии</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Что глянуть?
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="films.html">Фильмы</a></li>
|
||||
<li><a class="dropdown-item" href="seriales.html">Сериалы</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Основной контент -->
|
||||
<main class="container py-5 mt-5">
|
||||
<section class="mb-5">
|
||||
<h2 class="text-orange mb-4"><i class="bi bi-plus-circle"></i> Добавить новый фильм</h2>
|
||||
<div class="row">
|
||||
<div class="col-lg-8 mx-auto">
|
||||
<form id="addMovieForm" class="bg-dark p-4 rounded">
|
||||
<div class="mb-3">
|
||||
<label for="movieTitle" class="form-label text-light">
|
||||
<i class="bi bi-film"></i> Название фильма
|
||||
</label>
|
||||
<input type="text" class="form-control" id="movieTitle" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieDirector" class="form-label text-light">
|
||||
<i class="bi bi-person-video3"></i> Режиссер
|
||||
</label>
|
||||
<input type="text" class="form-control" id="movieDirector" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieGenre" class="form-label text-light">
|
||||
<i class="bi bi-tags"></i> Жанр
|
||||
</label>
|
||||
<select class="form-select" id="movieGenre" multiple required>
|
||||
<option value="action">Боевик</option>
|
||||
<option value="comedy">Комедия</option>
|
||||
<option value="drama">Драма</option>
|
||||
<option value="thriller">Триллер</option>
|
||||
<option value="horror">Ужасы</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieYear" class="form-label text-light">
|
||||
<i class="bi bi-calendar3"></i> Год выпуска
|
||||
</label>
|
||||
<input type="number" class="form-control" id="movieYear" min="1900" max="2024" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieDescription" class="form-label text-light">
|
||||
<i class="bi bi-text-paragraph"></i> Описание
|
||||
</label>
|
||||
<textarea class="form-control" id="movieDescription" rows="3" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="moviePoster" class="form-label text-light">
|
||||
<i class="bi bi-image"></i> Постер фильма
|
||||
</label>
|
||||
<input type="file" class="form-control" id="moviePoster" accept="image/*" required>
|
||||
</div>
|
||||
<div class="preview-container mb-3 d-none">
|
||||
<img id="posterPreview" src="" alt="Preview" class="img-fluid rounded">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-orange w-100">
|
||||
<i class="bi bi-plus-circle-fill"></i> Добавить фильм
|
||||
</button>
|
||||
</form>
|
||||
<main class="container py-5 mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card bg-dark text-light border-secondary">
|
||||
<div class="card-header bg-dark border-secondary">
|
||||
<h2 class="text-orange mb-0">Добавить новый фильм</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="addMovieForm">
|
||||
<div class="mb-3">
|
||||
<label for="movieTitle" class="form-label">
|
||||
<i class="bi bi-film"></i> Название фильма
|
||||
</label>
|
||||
<input type="text" class="form-control" id="movieTitle" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieDirector" class="form-label">
|
||||
<i class="bi bi-person-video3"></i> Режиссер
|
||||
</label>
|
||||
<input type="text" class="form-control" id="movieDirector" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieGenre" class="form-label">
|
||||
<i class="bi bi-tags"></i> Жанр
|
||||
</label>
|
||||
<select class="form-select" id="movieGenre" multiple required>
|
||||
<option>Боевик</option>
|
||||
<option>Драма</option>
|
||||
<option>Комедия</option>
|
||||
<option>Триллер</option>
|
||||
<option>Ужасы</option>
|
||||
<option>Фантастика</option>
|
||||
<option>Приключения</option>
|
||||
<option>Мелодрама</option>
|
||||
<option>Детектив</option>
|
||||
<option>Криминал</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieYear" class="form-label">
|
||||
<i class="bi bi-calendar3"></i> Год выпуска
|
||||
</label>
|
||||
<input type="number" class="form-control" id="movieYear" min="1900" max="2099" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieDescription" class="form-label">
|
||||
<i class="bi bi-card-text"></i> Описание
|
||||
</label>
|
||||
<textarea class="form-control" id="movieDescription" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="moviePoster" class="form-label">
|
||||
<i class="bi bi-image"></i> Постер
|
||||
</label>
|
||||
<input type="file" class="form-control" id="moviePoster" accept="image/*">
|
||||
<div class="preview-container mt-3 d-none">
|
||||
<img id="posterPreview" class="img-fluid rounded" alt="Предпросмотр постера">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Футер -->
|
||||
<footer class="footer-custom py-4">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<h5 class="text-white mb-3">Контактная информация</h5>
|
||||
<p>Телефон: +7 (123) 456-78-90</p>
|
||||
<p>Email: info@cinema.com</p>
|
||||
<p>Адрес: ул. Примерная, 123, Москва, Россия</p>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<h5 class="text-white mb-3">Мы в соцсетях</h5>
|
||||
<div class="d-flex gap-3">
|
||||
<a href="#" class="text-light"><i class="bi bi-telegram fs-4"></i></a>
|
||||
<a href="#" class="text-light"><i class="bi bi-whatsapp fs-4"></i></a>
|
||||
<a href="#" class="text-light"><i class="bi bi-vk fs-4"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<h5 class="text-white mb-3">Время работы</h5>
|
||||
<p>Понедельник - Пятница: 10:00 - 22:00</p>
|
||||
<p>Суббота - Воскресенье: 12:00 - 24:00</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 text-center">
|
||||
<p class="mb-0">© 2022 Online Cinema Theater. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<a href="catalog.html" class="btn btn-secondary">Отмена</a>
|
||||
<button type="submit" class="btn btn-primary">Добавить фильм</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<footer class="bg-dark text-light py-4 mt-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h5>О нас</h5>
|
||||
<p>Online Cinema Theater - ваш проводник в мире кино. Мы предлагаем огромную коллекцию фильмов и сериалов различных жанров.</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h5>Контакты</h5>
|
||||
<p><i class="bi bi-envelope"></i> info@cinema.com</p>
|
||||
<p><i class="bi bi-telephone"></i> +7 (123) 456-78-90</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h5>Часы работы</h5>
|
||||
<p>Понедельник - Пятница: 9:00 - 22:00</p>
|
||||
<p>Суббота - Воскресенье: 12:00 - 24:00</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col text-center">
|
||||
<p class="mb-0">© 2022 Online Cinema Theater. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
109
catalog.html
109
catalog.html
@@ -5,6 +5,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Каталог - Online Cinema Theater</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<!-- Add CSS for consistent card sizes -->
|
||||
<style>
|
||||
.movie-card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.movie-card .card-body {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.movie-card img.card-img-top {
|
||||
height: 400px;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Навигационная панель -->
|
||||
@@ -85,23 +100,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Список фильмов -->
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
|
||||
<div class="col">
|
||||
<div class="card movie-card h-100 bg-dark">
|
||||
<img src="resources/movies/gruz.jpeg" class="card-img-top" alt="Movie Poster">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-white">Груз 200</h5>
|
||||
<p class="card-text text-white">Режиссер: Алексей Балабанов</p>
|
||||
<p class="card-text text-light">Жанр: триллер, драма, криминал</p>
|
||||
<p class="card-text text-light">Год выпуска: 2007</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0">
|
||||
<a href="about.html" class="btn btn-orange w-100">Смотреть сейчас</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Здесь можно добавить больше карточек фильмов -->
|
||||
<!-- Find the section with class "movie-catalog" and add this after the filters -->
|
||||
|
||||
<!-- Movie Container -->
|
||||
<div id="movieContainer" class="row row-cols-1 row-cols-md-3 g-4">
|
||||
<!-- Movies will be rendered here -->
|
||||
</div>
|
||||
|
||||
<!-- Пагинация -->
|
||||
@@ -154,5 +157,77 @@
|
||||
</footer>
|
||||
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
|
||||
<!-- Add this at the end of the file, just before the closing </body> tag -->
|
||||
|
||||
<!-- Modal for editing movies -->
|
||||
<div class="modal fade" id="editMovieModal" tabindex="-1" aria-labelledby="editMovieModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content bg-dark text-light">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editMovieModalLabel">Редактировать фильм</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="editMovieForm">
|
||||
<div class="mb-3">
|
||||
<label for="editMovieTitle" class="form-label">
|
||||
<i class="bi bi-film"></i> Название фильма
|
||||
</label>
|
||||
<input type="text" class="form-control" id="editMovieTitle" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editMovieDirector" class="form-label">
|
||||
<i class="bi bi-person-video3"></i> Режиссер
|
||||
</label>
|
||||
<input type="text" class="form-control" id="editMovieDirector" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editMovieGenre" class="form-label">
|
||||
<i class="bi bi-tags"></i> Жанр
|
||||
</label>
|
||||
<select class="form-select" id="editMovieGenre" multiple required>
|
||||
<option>Боевик</option>
|
||||
<option>Драма</option>
|
||||
<option>Комедия</option>
|
||||
<option>Триллер</option>
|
||||
<option>Ужасы</option>
|
||||
<option>Фантастика</option>
|
||||
<option>Приключения</option>
|
||||
<option>Мелодрама</option>
|
||||
<option>Детектив</option>
|
||||
<option>Криминал</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editMovieYear" class="form-label">
|
||||
<i class="bi bi-calendar3"></i> Год выпуска
|
||||
</label>
|
||||
<input type="number" class="form-control" id="editMovieYear" min="1900" max="2099" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editMovieDescription" class="form-label">
|
||||
<i class="bi bi-card-text"></i> Описание
|
||||
</label>
|
||||
<textarea class="form-control" id="editMovieDescription" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editMoviePoster" class="form-label">
|
||||
<i class="bi bi-image"></i> Постер
|
||||
</label>
|
||||
<input type="file" class="form-control" id="editMoviePoster" accept="image/*">
|
||||
<div class="preview-container mt-3 d-none">
|
||||
<img id="editPosterPreview" class="img-fluid rounded" alt="Предпросмотр постера">
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="submit" class="btn btn-primary">Сохранить изменения</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
242
edit-movie.html
Normal file
242
edit-movie.html
Normal file
@@ -0,0 +1,242 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Редактировать фильм - Online Cinema Theater</title>
|
||||
<!-- Add Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<!-- Add custom styles -->
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<style>
|
||||
.preview-container img {
|
||||
max-height: 300px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.text-orange {
|
||||
color: #fd7e14;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-dark text-light">
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.html">
|
||||
<i class="bi bi-film text-danger"></i> Online Cinema
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.html">Главная</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="catalog.html">Каталог</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="reviews.html">Рецензии</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Что глянуть?
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="films.html">Фильмы</a></li>
|
||||
<li><a class="dropdown-item" href="seriales.html">Сериалы</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container py-5 mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card bg-dark text-light border-secondary">
|
||||
<div class="card-header bg-dark border-secondary">
|
||||
<h2 class="text-orange mb-0">Редактировать фильм</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="editMovieForm">
|
||||
<input type="hidden" id="movieId">
|
||||
<div class="mb-3">
|
||||
<label for="movieTitle" class="form-label">
|
||||
<i class="bi bi-film"></i> Название фильма
|
||||
</label>
|
||||
<input type="text" class="form-control" id="movieTitle" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieDirector" class="form-label">
|
||||
<i class="bi bi-person-video3"></i> Режиссер
|
||||
</label>
|
||||
<input type="text" class="form-control" id="movieDirector" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieGenre" class="form-label">
|
||||
<i class="bi bi-tags"></i> Жанр
|
||||
</label>
|
||||
<select class="form-select" id="movieGenre" multiple required>
|
||||
<option>Боевик</option>
|
||||
<option>Драма</option>
|
||||
<option>Комедия</option>
|
||||
<option>Триллер</option>
|
||||
<option>Ужасы</option>
|
||||
<option>Фантастика</option>
|
||||
<option>Приключения</option>
|
||||
<option>Мелодрама</option>
|
||||
<option>Детектив</option>
|
||||
<option>Криминал</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieYear" class="form-label">
|
||||
<i class="bi bi-calendar3"></i> Год выпуска
|
||||
</label>
|
||||
<input type="number" class="form-control" id="movieYear" min="1900" max="2099" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="movieDescription" class="form-label">
|
||||
<i class="bi bi-card-text"></i> Описание
|
||||
</label>
|
||||
<textarea class="form-control" id="movieDescription" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="moviePoster" class="form-label">
|
||||
<i class="bi bi-image"></i> Постер
|
||||
</label>
|
||||
<input type="file" class="form-control" id="moviePoster" accept="image/*">
|
||||
<div class="preview-container mt-3">
|
||||
<img id="posterPreview" class="img-fluid rounded" alt="Предпросмотр постера">
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<a href="catalog.html" class="btn btn-secondary">Отмена</a>
|
||||
<button type="submit" class="btn btn-primary">Сохранить изменения</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="bg-dark text-light py-4 mt-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h5>О нас</h5>
|
||||
<p>Online Cinema Theater - ваш проводник в мире кино. Мы предлагаем огромную коллекцию фильмов и сериалов различных жанров.</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h5>Контакты</h5>
|
||||
<p><i class="bi bi-envelope"></i> info@cinema.com</p>
|
||||
<p><i class="bi bi-telephone"></i> +7 (123) 456-78-90</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h5>Часы работы</h5>
|
||||
<p>Понедельник - Пятница: 9:00 - 22:00</p>
|
||||
<p>Суббота - Воскресенье: 12:00 - 24:00</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col text-center">
|
||||
<p class="mb-0">© 2022 Online Cinema Theater. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="module">
|
||||
import { MovieModel } from './src/components/movie/MovieModel.js';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Get movie ID from URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const movieId = urlParams.get('id');
|
||||
|
||||
if (!movieId) {
|
||||
alert('Фильм не найден');
|
||||
window.location.href = 'catalog.html';
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize model and fetch movie data
|
||||
const movieModel = new MovieModel();
|
||||
const movie = await movieModel.getMovieById(movieId);
|
||||
|
||||
if (!movie) {
|
||||
alert('Фильм не найден');
|
||||
window.location.href = 'catalog.html';
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill form with movie data
|
||||
document.getElementById('movieId').value = movie.id;
|
||||
document.getElementById('movieTitle').value = movie.title;
|
||||
document.getElementById('movieDirector').value = movie.director;
|
||||
document.getElementById('movieYear').value = movie.year;
|
||||
document.getElementById('movieDescription').value = movie.description || '';
|
||||
|
||||
// Handle genres (multi-select)
|
||||
const genreSelect = document.getElementById('movieGenre');
|
||||
if (Array.isArray(movie.genres)) {
|
||||
Array.from(genreSelect.options).forEach(option => {
|
||||
option.selected = movie.genres.includes(option.text);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle poster preview
|
||||
const posterPreview = document.getElementById('posterPreview');
|
||||
if (movie.poster) {
|
||||
posterPreview.src = movie.poster;
|
||||
document.querySelector('.preview-container').classList.remove('d-none');
|
||||
}
|
||||
|
||||
// Handle poster file input change
|
||||
const moviePoster = document.getElementById('moviePoster');
|
||||
if (moviePoster) {
|
||||
moviePoster.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
posterPreview.src = e.target.result;
|
||||
document.querySelector('.preview-container').classList.remove('d-none');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
const editMovieForm = document.getElementById('editMovieForm');
|
||||
if (editMovieForm) {
|
||||
editMovieForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const updatedMovie = {
|
||||
id: movieId,
|
||||
title: document.getElementById('movieTitle').value,
|
||||
director: document.getElementById('movieDirector').value,
|
||||
genres: Array.from(document.getElementById('movieGenre').selectedOptions).map(option => option.text),
|
||||
year: document.getElementById('movieYear').value,
|
||||
description: document.getElementById('movieDescription').value,
|
||||
poster: document.getElementById('posterPreview').src
|
||||
};
|
||||
|
||||
await movieModel.updateMovie(movieId, updatedMovie);
|
||||
alert('Фильм успешно обновлен!');
|
||||
window.location.href = 'catalog.html';
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
65
films.html
65
films.html
@@ -5,7 +5,24 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Фильмы - Online Cinema Theater</title>
|
||||
<!-- Add Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<!-- Add CSS for consistent card sizes -->
|
||||
<style>
|
||||
.movie-card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.movie-card .card-body {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.movie-card img.card-img-top {
|
||||
height: 400px;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Навигационная панель -->
|
||||
@@ -46,46 +63,18 @@
|
||||
<!-- Основной контент -->
|
||||
<main class="container py-5 mt-5">
|
||||
<!-- Секция основных фильмов -->
|
||||
<!-- Find the section with the movie list and replace it with: -->
|
||||
|
||||
<section class="mb-5">
|
||||
<h2 class="text-orange mb-4">Основные фильмы</h2>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
|
||||
<div class="col">
|
||||
<div class="card movie-card h-100 bg-dark">
|
||||
<img src="resources/movies/gruz.jpeg" class="card-img-top" alt="Movie Poster">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-white"><i class="bi bi-film text-orange me-2"></i>Груз 200</h5>
|
||||
<p class="card-text text-white"><i class="bi bi-person-video3 me-2"></i>Режиссер: Алексей Балабанов</p>
|
||||
<p class="card-text text-light"><i class="bi bi-tags me-2"></i>Жанр: триллер, драма, криминал</p>
|
||||
<p class="card-text text-light"><i class="bi bi-calendar3 me-2"></i>Год выпуска: 2007</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0">
|
||||
<a href="about.html" class="btn btn-orange w-100"><i class="bi bi-play-circle me-2"></i>Смотреть сейчас</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-orange mb-4">Фильмы</h2>
|
||||
<div id="movieContainer" class="row row-cols-1 row-cols-md-3 g-4">
|
||||
<!-- Movies will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Секция предстоящих фильмов -->
|
||||
<section>
|
||||
<h2 class="text-orange mb-4">Предстоящие фильмы</h2>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
|
||||
<div class="col">
|
||||
<div class="card movie-card h-100 bg-dark">
|
||||
<img src="resources/movies/gruz.jpeg" class="card-img-top" alt="Movie Poster">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-white"><i class="bi bi-film text-orange me-2"></i>Груз 200</h5>
|
||||
<p class="card-text text-white"><i class="bi bi-person-video3 me-2"></i>Режиссер: Алексей Балабанов</p>
|
||||
<p class="card-text text-light"><i class="bi bi-tags me-2"></i>Жанр: триллер, драма, криминал</p>
|
||||
<p class="card-text text-light"><i class="bi bi-calendar3 me-2"></i>Год выпуска: 2007</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0">
|
||||
<a href="about.html" class="btn btn-orange w-100"><i class="bi bi-play-circle me-2"></i>Смотреть сейчас</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Add the edit modal at the end of the file, just before the closing </body> tag -->
|
||||
<!-- Same modal code as in catalog.html -->
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Футер -->
|
||||
@@ -120,6 +109,8 @@
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Add Bootstrap JS before the closing body tag -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
41
index.html
41
index.html
@@ -4,7 +4,24 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Online Cinema Theater</title>
|
||||
<!-- Add Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<!-- Add CSS for consistent card sizes -->
|
||||
<style>
|
||||
.movie-card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.movie-card .card-body {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.movie-card img.card-img-top {
|
||||
height: 400px;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Навигационная панель с использованием Bootstrap -->
|
||||
@@ -49,26 +66,16 @@
|
||||
<!-- Основное содержимое -->
|
||||
<main class="container py-5 mt-5">
|
||||
<div id="app">
|
||||
<!-- Find the section with popular movies and update it to use our component -->
|
||||
<section class="mb-5">
|
||||
<h2 class="text-orange mb-4">Популярные фильмы</h2>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
|
||||
<div class="col">
|
||||
<div class="card movie-card h-100 bg-dark">
|
||||
<img src="resources/movies/gruz.jpeg" class="card-img-top" alt="Movie Poster">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-white"><i class="bi bi-film text-orange me-2"></i>Груз 200</h5>
|
||||
<p class="card-text text-white"><i class="bi bi-person-video3 me-2"></i>Режиссер: Алексей Балабанов</p>
|
||||
<p class="card-text text-light"><i class="bi bi-tags me-2"></i>Жанр: триллер, драма, криминал</p>
|
||||
<p class="card-text text-light"><i class="bi bi-calendar3 me-2"></i>Год выпуска: 2007</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0">
|
||||
<a href="about.html" class="btn btn-orange w-100"><i class="bi bi-play-circle me-2"></i>Смотреть сейчас</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Здесь можно добавить больше карточек фильмов -->
|
||||
<div id="movieContainer" class="row row-cols-1 row-cols-md-3 g-4">
|
||||
<!-- Movies will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Add this at the end of your body tag, just before the closing </body> -->
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -106,6 +113,8 @@
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Add Bootstrap JS before the closing body tag -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1827
package-lock.json
generated
1827
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -8,7 +8,8 @@
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .js",
|
||||
"lint:fix": "eslint . --ext .js --fix",
|
||||
"format": "prettier --write \"**/*.{js,html,css,json}\""
|
||||
"format": "prettier --write \"**/*.{js,html,css,json}\"",
|
||||
"server": "json-server --watch db.json --port 3000"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,12 +22,13 @@
|
||||
"devDependencies": {
|
||||
"eslint": "^9.22.0",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-prettier": "^5.2.3",
|
||||
"prettier": "^3.5.3",
|
||||
"vite": "^6.2.2"
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"json-server": "^0.17.4",
|
||||
"prettier": "^3.2.5",
|
||||
"vite": "^5.2.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.2",
|
||||
"@popperjs/core": "^2.11.8"
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootstrap-icons": "^1.11.3"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
resources/movies/brat.webp
Normal file
BIN
resources/movies/brat.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
BIN
resources/movies/slonik.jpg
Normal file
BIN
resources/movies/slonik.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
@@ -4,7 +4,24 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Сериалы - Online Cinema Theater</title>
|
||||
<!-- Add Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<!-- Add this style section to ensure equal card heights -->
|
||||
<style>
|
||||
.movie-card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.movie-card .card-body {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.movie-card img.card-img-top {
|
||||
height: 400px;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Навигационная панель -->
|
||||
@@ -47,8 +64,9 @@
|
||||
<section class="featured-series">
|
||||
<h2 class="text-orange mb-4">Популярные сериалы</h2>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
|
||||
<!-- Add more cards with the same structure to test equal heights -->
|
||||
<div class="col">
|
||||
<div class="card movie-card h-100 bg-dark">
|
||||
<div class="card movie-card bg-dark">
|
||||
<img src="resources/series/960.webp" class="card-img-top" alt="Series Poster">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-white">Преступление и наказание</h5>
|
||||
@@ -129,6 +147,8 @@
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Add Bootstrap JS before the closing body tag -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
232
src/components/movie/MovieController.js
Normal file
232
src/components/movie/MovieController.js
Normal file
@@ -0,0 +1,232 @@
|
||||
export class MovieController {
|
||||
constructor(model, view) {
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
|
||||
// Initialize the controller based on the current page
|
||||
this.initializeController();
|
||||
}
|
||||
|
||||
initializeController() {
|
||||
// Check which page we're on and initialize accordingly
|
||||
const path = window.location.pathname;
|
||||
|
||||
if (path.includes('catalog.html')) {
|
||||
// Catalog page initialization
|
||||
this.initializeCatalogPage();
|
||||
} else if (path.includes('add-movie.html')) {
|
||||
// Add movie page initialization
|
||||
this.initializeAddMoviePage();
|
||||
} else if (path.includes('edit-movie.html')) {
|
||||
// Edit movie page initialization
|
||||
this.initializeEditMoviePage();
|
||||
} else if (path.endsWith('index.html') || path.endsWith('/')) {
|
||||
// Homepage initialization
|
||||
this.initializeHomepage();
|
||||
}
|
||||
}
|
||||
|
||||
initializeHomepage() {
|
||||
// Load and display featured movies on the homepage
|
||||
this.model.getMovies().then(movies => {
|
||||
// Sort movies by some criteria to get "featured" ones
|
||||
// For example, sort by year (newest first)
|
||||
const sortedMovies = [...movies].sort((a, b) => b.year - a.year);
|
||||
this.view.renderMovies(sortedMovies);
|
||||
|
||||
// Bind delete functionality on homepage too
|
||||
this.view.bindDeleteMovie(this.handleDeleteMovie.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
initializeCatalogPage() {
|
||||
// Load and display all movies
|
||||
this.model.getMovies().then(movies => {
|
||||
this.view.renderMovies(movies);
|
||||
});
|
||||
|
||||
// Bind event handlers for the catalog page
|
||||
this.view.bindDeleteMovie(this.handleDeleteMovie.bind(this));
|
||||
this.view.bindFilterMovies(this.handleFilterMovies.bind(this));
|
||||
}
|
||||
|
||||
initializeAddMoviePage() {
|
||||
// Bind the save new movie handler
|
||||
const addMovieForm = document.getElementById('addMovieForm');
|
||||
if (addMovieForm) {
|
||||
addMovieForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const newMovie = {
|
||||
title: document.getElementById('movieTitle').value,
|
||||
director: document.getElementById('movieDirector').value,
|
||||
genres: Array.from(document.getElementById('movieGenre').selectedOptions).map(option => option.text),
|
||||
year: document.getElementById('movieYear').value,
|
||||
description: document.getElementById('movieDescription').value,
|
||||
poster: document.getElementById('posterPreview')?.src || 'resources/movies/placeholder.jpg'
|
||||
};
|
||||
|
||||
this.handleSaveNewMovie(newMovie);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle poster preview
|
||||
const moviePoster = document.getElementById('moviePoster');
|
||||
const posterPreview = document.getElementById('posterPreview');
|
||||
const previewContainer = document.querySelector('.preview-container');
|
||||
|
||||
if (moviePoster && posterPreview && previewContainer) {
|
||||
moviePoster.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
posterPreview.src = e.target.result;
|
||||
previewContainer.classList.remove('d-none');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
initializeEditMoviePage() {
|
||||
// Get movie ID from URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const movieId = urlParams.get('id');
|
||||
|
||||
if (!movieId) {
|
||||
alert('Фильм не найден');
|
||||
window.location.href = 'catalog.html';
|
||||
return;
|
||||
}
|
||||
|
||||
// Load movie data and set up form
|
||||
this.model.getMovieById(movieId)
|
||||
.then(movie => {
|
||||
if (!movie) {
|
||||
alert('Фильм не найден');
|
||||
window.location.href = 'catalog.html';
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill form with movie data
|
||||
document.getElementById('movieId').value = movie.id;
|
||||
document.getElementById('movieTitle').value = movie.title;
|
||||
document.getElementById('movieDirector').value = movie.director;
|
||||
document.getElementById('movieYear').value = movie.year;
|
||||
document.getElementById('movieDescription').value = movie.description || '';
|
||||
|
||||
// Handle genres (multi-select)
|
||||
const genreSelect = document.getElementById('movieGenre');
|
||||
if (Array.isArray(movie.genres)) {
|
||||
Array.from(genreSelect.options).forEach(option => {
|
||||
option.selected = movie.genres.includes(option.text);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle poster preview
|
||||
const posterPreview = document.getElementById('posterPreview');
|
||||
if (movie.poster) {
|
||||
posterPreview.src = movie.poster;
|
||||
document.querySelector('.preview-container').classList.remove('d-none');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading movie:', error);
|
||||
alert('Произошла ошибка при загрузке фильма');
|
||||
window.location.href = 'catalog.html';
|
||||
});
|
||||
|
||||
// Handle poster file input change
|
||||
const moviePoster = document.getElementById('moviePoster');
|
||||
const posterPreview = document.getElementById('posterPreview');
|
||||
const previewContainer = document.querySelector('.preview-container');
|
||||
|
||||
if (moviePoster && posterPreview && previewContainer) {
|
||||
moviePoster.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
posterPreview.src = e.target.result;
|
||||
previewContainer.classList.remove('d-none');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
const editMovieForm = document.getElementById('editMovieForm');
|
||||
if (editMovieForm) {
|
||||
editMovieForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const updatedMovie = {
|
||||
id: movieId,
|
||||
title: document.getElementById('movieTitle').value,
|
||||
director: document.getElementById('movieDirector').value,
|
||||
genres: Array.from(document.getElementById('movieGenre').selectedOptions).map(option => option.text),
|
||||
year: document.getElementById('movieYear').value,
|
||||
description: document.getElementById('movieDescription').value,
|
||||
poster: document.getElementById('posterPreview').src
|
||||
};
|
||||
|
||||
this.model.updateMovie(movieId, updatedMovie)
|
||||
.then(() => {
|
||||
alert('Фильм успешно обновлен!');
|
||||
window.location.href = 'catalog.html';
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating movie:', error);
|
||||
alert('Произошла ошибка при обновлении фильма');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleSaveNewMovie(movieData) {
|
||||
this.model.addMovie(movieData)
|
||||
.then(() => {
|
||||
alert('Фильм успешно добавлен!');
|
||||
window.location.href = 'catalog.html';
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error adding movie:', error);
|
||||
alert('Произошла ошибка при добавлении фильма.');
|
||||
});
|
||||
}
|
||||
|
||||
handleDeleteMovie(movieId) {
|
||||
this.model.deleteMovie(movieId)
|
||||
.then(() => {
|
||||
return this.model.getMovies();
|
||||
})
|
||||
.then(movies => {
|
||||
// Check if we're on the homepage to sort movies again
|
||||
const isHomepage = window.location.pathname.endsWith('index.html') || window.location.pathname.endsWith('/');
|
||||
if (isHomepage) {
|
||||
const sortedMovies = [...movies].sort((a, b) => b.year - a.year);
|
||||
this.view.renderMovies(sortedMovies);
|
||||
} else {
|
||||
this.view.renderMovies(movies);
|
||||
}
|
||||
alert('Фильм успешно удален!');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error deleting movie:', error);
|
||||
alert('Произошла ошибка при удалении фильма.');
|
||||
});
|
||||
}
|
||||
|
||||
handleFilterMovies(filters) {
|
||||
this.model.getFilteredMovies(filters)
|
||||
.then(movies => {
|
||||
this.view.renderMovies(movies);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error filtering movies:', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
356
src/components/movie/MovieModel.js
Normal file
356
src/components/movie/MovieModel.js
Normal file
@@ -0,0 +1,356 @@
|
||||
export class MovieModel {
|
||||
constructor() {
|
||||
this.apiUrl = 'http://localhost:3000';
|
||||
this.movies = [];
|
||||
this.genres = [];
|
||||
this.directors = [];
|
||||
|
||||
// Initialize data
|
||||
this.initializeData();
|
||||
}
|
||||
|
||||
async initializeData() {
|
||||
try {
|
||||
await this.fetchAllData();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize from API, using localStorage:', error);
|
||||
|
||||
// Fallback to localStorage
|
||||
let movies = JSON.parse(localStorage.getItem('movies')) || [];
|
||||
|
||||
if (movies.length === 0) {
|
||||
movies = this.getSampleMovies();
|
||||
localStorage.setItem('movies', JSON.stringify(movies));
|
||||
}
|
||||
|
||||
this.movies = movies;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchAllData() {
|
||||
try {
|
||||
// Fetch all necessary data in parallel using Fetch API
|
||||
const [moviesResponse, genresResponse, directorsResponse] = await Promise.all([
|
||||
fetch(`${this.apiUrl}/movies`),
|
||||
fetch(`${this.apiUrl}/genres`),
|
||||
fetch(`${this.apiUrl}/directors`)
|
||||
]);
|
||||
|
||||
if (!moviesResponse.ok || !genresResponse.ok || !directorsResponse.ok) {
|
||||
throw new Error('Failed to fetch data from server');
|
||||
}
|
||||
|
||||
this.movies = await moviesResponse.json();
|
||||
this.genres = await genresResponse.json();
|
||||
this.directors = await directorsResponse.json();
|
||||
|
||||
// Process movies to include full genre and director objects
|
||||
this.movies = this.movies.map(movie => this.processMovie(movie));
|
||||
|
||||
return this.movies;
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
processMovie(movie) {
|
||||
// Convert genreIds to full genre names
|
||||
const genres = movie.genreIds?.map(id =>
|
||||
this.genres.find(genre => genre.id === id)?.name
|
||||
).filter(Boolean) || [];
|
||||
|
||||
// Get director name
|
||||
const director = this.directors.find(dir => dir.id === movie.directorId)?.name || 'Unknown Director';
|
||||
|
||||
return {
|
||||
...movie,
|
||||
genres,
|
||||
director
|
||||
};
|
||||
}
|
||||
|
||||
// Add this method to match what's being called in the controller
|
||||
async getMovies() {
|
||||
return this.getAllMovies();
|
||||
}
|
||||
|
||||
async getAllMovies() {
|
||||
if (this.movies.length === 0) {
|
||||
try {
|
||||
await this.fetchAllData();
|
||||
} catch (error) {
|
||||
console.error('Error fetching movies:', error);
|
||||
// Fallback to localStorage
|
||||
this.movies = JSON.parse(localStorage.getItem('movies')) || this.getSampleMovies();
|
||||
this.saveToStorage();
|
||||
}
|
||||
}
|
||||
return this.movies;
|
||||
}
|
||||
|
||||
async getMovieById(id) {
|
||||
try {
|
||||
const response = await fetch(`${this.apiUrl}/movies/${id}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch movie');
|
||||
}
|
||||
const movie = await response.json();
|
||||
return this.processMovie(movie);
|
||||
} catch (error) {
|
||||
console.error('Error fetching movie:', error);
|
||||
// Fallback to local cache
|
||||
return this.movies.find(movie => movie.id === id);
|
||||
}
|
||||
}
|
||||
|
||||
async addMovie(movie) {
|
||||
try {
|
||||
// Convert genre names to ids
|
||||
const genreIds = movie.genres.map(genreName => {
|
||||
const genre = this.genres.find(g => g.name === genreName);
|
||||
return genre ? genre.id : null;
|
||||
}).filter(Boolean);
|
||||
|
||||
// Find director id by name or create a new one
|
||||
let directorId;
|
||||
const directorObj = this.directors.find(d => d.name === movie.director);
|
||||
|
||||
if (directorObj) {
|
||||
directorId = directorObj.id;
|
||||
} else {
|
||||
// If director doesn't exist, create a new one
|
||||
const newDirector = {
|
||||
name: movie.director,
|
||||
birthYear: "Unknown",
|
||||
country: "Unknown"
|
||||
};
|
||||
|
||||
const dirResponse = await fetch(`${this.apiUrl}/directors`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(newDirector)
|
||||
});
|
||||
|
||||
if (!dirResponse.ok) {
|
||||
throw new Error('Failed to add new director');
|
||||
}
|
||||
|
||||
const createdDirector = await dirResponse.json();
|
||||
this.directors.push(createdDirector);
|
||||
directorId = createdDirector.id;
|
||||
}
|
||||
|
||||
const movieData = {
|
||||
title: movie.title,
|
||||
directorId,
|
||||
year: movie.year,
|
||||
description: movie.description || '',
|
||||
poster: movie.poster,
|
||||
genreIds
|
||||
};
|
||||
|
||||
const response = await fetch(`${this.apiUrl}/movies`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(movieData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to add movie');
|
||||
}
|
||||
|
||||
const newMovie = await response.json();
|
||||
const processedMovie = this.processMovie(newMovie);
|
||||
this.movies.push(processedMovie);
|
||||
|
||||
return processedMovie;
|
||||
} catch (error) {
|
||||
console.error('Error adding movie:', error);
|
||||
// Fallback to local storage
|
||||
return this.addMovieLocally(movie);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteMovie(id) {
|
||||
try {
|
||||
const response = await fetch(`${this.apiUrl}/movies/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete movie');
|
||||
}
|
||||
|
||||
this.movies = this.movies.filter(movie => movie.id !== id);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error deleting movie:', error);
|
||||
// Fallback to local storage
|
||||
return this.deleteMovieLocally(id);
|
||||
}
|
||||
}
|
||||
|
||||
async updateMovie(id, updatedMovie) {
|
||||
try {
|
||||
// Convert genre names to ids
|
||||
const genreIds = updatedMovie.genres.map(genreName => {
|
||||
const genre = this.genres.find(g => g.name === genreName);
|
||||
return genre ? genre.id : null;
|
||||
}).filter(Boolean);
|
||||
|
||||
// Find director id by name
|
||||
let directorId;
|
||||
const directorObj = this.directors.find(d => d.name === updatedMovie.director);
|
||||
|
||||
if (directorObj) {
|
||||
directorId = directorObj.id;
|
||||
} else {
|
||||
// If director doesn't exist, create a new one
|
||||
const newDirector = {
|
||||
name: updatedMovie.director,
|
||||
birthYear: "Unknown",
|
||||
country: "Unknown"
|
||||
};
|
||||
|
||||
const dirResponse = await fetch(`${this.apiUrl}/directors`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(newDirector)
|
||||
});
|
||||
|
||||
if (!dirResponse.ok) {
|
||||
throw new Error('Failed to add new director');
|
||||
}
|
||||
|
||||
const createdDirector = await dirResponse.json();
|
||||
this.directors.push(createdDirector);
|
||||
directorId = createdDirector.id;
|
||||
}
|
||||
|
||||
const movieData = {
|
||||
title: updatedMovie.title,
|
||||
directorId,
|
||||
year: updatedMovie.year,
|
||||
description: updatedMovie.description || '',
|
||||
poster: updatedMovie.poster,
|
||||
genreIds
|
||||
};
|
||||
|
||||
const response = await fetch(`${this.apiUrl}/movies/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(movieData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to update movie');
|
||||
}
|
||||
|
||||
const updatedMovieData = await response.json();
|
||||
const processedMovie = this.processMovie(updatedMovieData);
|
||||
|
||||
const index = this.movies.findIndex(movie => movie.id === id);
|
||||
if (index !== -1) {
|
||||
this.movies[index] = processedMovie;
|
||||
return processedMovie;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Error updating movie:', error);
|
||||
// Fallback to local storage
|
||||
const index = this.movies.findIndex(movie => movie.id === id);
|
||||
if (index !== -1) {
|
||||
this.movies[index] = { ...this.movies[index], ...updatedMovie };
|
||||
this.saveToStorage();
|
||||
return this.movies[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods for local storage fallback
|
||||
addMovieLocally(movie) {
|
||||
const newMovie = {
|
||||
id: Date.now().toString(),
|
||||
...movie
|
||||
};
|
||||
this.movies.push(newMovie);
|
||||
this.saveToStorage();
|
||||
return newMovie;
|
||||
}
|
||||
|
||||
deleteMovieLocally(id) {
|
||||
this.movies = this.movies.filter(movie => movie.id !== id);
|
||||
this.saveToStorage();
|
||||
return true;
|
||||
}
|
||||
|
||||
saveToStorage() {
|
||||
localStorage.setItem('movies', JSON.stringify(this.movies));
|
||||
}
|
||||
|
||||
filterMovies(filters = {}) {
|
||||
return this.movies.filter(movie => {
|
||||
let match = true;
|
||||
|
||||
if (filters.genre && filters.genre !== 'all') {
|
||||
match = match && movie.genres.includes(filters.genre);
|
||||
}
|
||||
|
||||
if (filters.year && filters.year !== 'all') {
|
||||
match = match && movie.year === filters.year;
|
||||
}
|
||||
|
||||
if (filters.search) {
|
||||
const searchLower = filters.search.toLowerCase();
|
||||
match = match && (
|
||||
movie.title.toLowerCase().includes(searchLower) ||
|
||||
movie.director.toLowerCase().includes(searchLower)
|
||||
);
|
||||
}
|
||||
|
||||
return match;
|
||||
});
|
||||
}
|
||||
|
||||
getSampleMovies() {
|
||||
return [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Груз 200',
|
||||
director: 'Алексей Балабанов',
|
||||
genres: ['Триллер', 'Драма', 'Криминал'],
|
||||
year: '2007',
|
||||
description: 'Действие фильма происходит в 1984 году в провинциальном городе. Молодая девушка оказывается в руках маньяка, который представляется сотрудником милиции.',
|
||||
poster: 'resources/movies/gruz.jpeg'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Брат',
|
||||
director: 'Алексей Балабанов',
|
||||
genres: ['Драма', 'Криминал', 'Боевик'],
|
||||
year: '1997',
|
||||
description: 'Демобилизовавшись, Данила Багров возвращается в родной городок. Но скучная жизнь провинциального городка не устраивает его, и он решает поехать в Петербург, где, по слухам, уже давно процветает его старший брат.',
|
||||
poster: 'resources/movies/brat.webp'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Зеленый слоник',
|
||||
director: 'Светлана Баскова',
|
||||
genres: ['Драма', 'Арт-хаус'],
|
||||
year: '1999',
|
||||
description: 'Два офицера, "Младший лейтенант" и "Капитан", сидят в одной камере на гауптвахте. Капитан — дослуживающий до пенсии армейский алкоголик, а Младший лейтенант — молодой офицер, мечтающий о карьере.',
|
||||
poster: 'resources/movies/slonik.jpg'
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
283
src/components/movie/MovieView.js
Normal file
283
src/components/movie/MovieView.js
Normal file
@@ -0,0 +1,283 @@
|
||||
// Import Bootstrap modal functionality
|
||||
import { Modal } from 'bootstrap';
|
||||
|
||||
export class MovieView {
|
||||
constructor() {
|
||||
this.movieContainer = document.getElementById('movieContainer');
|
||||
|
||||
// Create add movie button if we're on the catalog page
|
||||
if (window.location.pathname.includes('catalog.html')) {
|
||||
this.createAddMovieButton();
|
||||
}
|
||||
|
||||
// Initialize featured movies section if we're on the homepage
|
||||
if (window.location.pathname.endsWith('index.html') || window.location.pathname.endsWith('/')) {
|
||||
this.initializeHomepage();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize homepage elements
|
||||
initializeHomepage() {
|
||||
// We'll use the same movieContainer for homepage
|
||||
if (!this.movieContainer) {
|
||||
console.error('Movie container element not found on homepage');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a heading for the featured movies section if not already present
|
||||
const parentSection = this.movieContainer.closest('section');
|
||||
if (parentSection) {
|
||||
const heading = parentSection.querySelector('h2');
|
||||
if (heading) {
|
||||
heading.textContent = 'Популярные фильмы';
|
||||
heading.className = 'text-orange mb-4';
|
||||
}
|
||||
}
|
||||
|
||||
// Add a button to add movies on the homepage too
|
||||
this.createAddMovieButton();
|
||||
}
|
||||
|
||||
// Create "Add Movie" button and append it to the page
|
||||
createAddMovieButton() {
|
||||
// Find the appropriate container - either .mb-5 or the section containing movieContainer
|
||||
let container = document.querySelector('.mb-5');
|
||||
if (!container && this.movieContainer) {
|
||||
container = this.movieContainer.closest('section');
|
||||
}
|
||||
if (!container) return;
|
||||
|
||||
// Check if button already exists
|
||||
if (container.querySelector('.add-movie-btn')) return;
|
||||
|
||||
const buttonContainer = document.createElement('div');
|
||||
buttonContainer.className = 'mb-4 d-flex justify-content-end';
|
||||
|
||||
const addButton = document.createElement('a');
|
||||
addButton.className = 'btn btn-success add-movie-btn';
|
||||
addButton.href = 'add-movie.html';
|
||||
addButton.innerHTML = '<i class="bi bi-plus-circle me-2"></i>Добавить фильм';
|
||||
|
||||
buttonContainer.appendChild(addButton);
|
||||
container.insertBefore(buttonContainer, this.movieContainer);
|
||||
}
|
||||
|
||||
// Bind the save new movie form submit event
|
||||
bindSaveNewMovie(handler) {
|
||||
const addMovieForm = document.getElementById('addMovieForm');
|
||||
if (!addMovieForm) return;
|
||||
|
||||
addMovieForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const newMovie = {
|
||||
title: document.getElementById('movieTitle').value,
|
||||
director: document.getElementById('movieDirector').value,
|
||||
genres: Array.from(document.getElementById('movieGenre').selectedOptions).map(option => option.text),
|
||||
year: document.getElementById('movieYear').value,
|
||||
description: document.getElementById('movieDescription').value,
|
||||
poster: document.getElementById('posterPreview')?.src || 'resources/movies/placeholder.jpg'
|
||||
};
|
||||
|
||||
handler(newMovie);
|
||||
|
||||
// Redirect back to catalog after adding
|
||||
window.location.href = 'catalog.html';
|
||||
});
|
||||
}
|
||||
|
||||
createMovieElement(movie) {
|
||||
const movieCard = document.createElement('div');
|
||||
movieCard.className = 'card movie-card h-100 bg-dark';
|
||||
movieCard.dataset.movieId = movie.id;
|
||||
|
||||
movieCard.innerHTML = `
|
||||
<img src="${movie.poster}" class="card-img-top" alt="${movie.title} Poster">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-white">
|
||||
<i class="bi bi-film text-orange me-2"></i>${movie.title}
|
||||
</h5>
|
||||
<p class="card-text text-white">
|
||||
<i class="bi bi-person-video3 me-2"></i>Режиссер: ${movie.director}
|
||||
</p>
|
||||
<p class="card-text text-light">
|
||||
<i class="bi bi-tags me-2"></i>Жанр: ${Array.isArray(movie.genres) ? movie.genres.join(', ') : movie.genres}
|
||||
</p>
|
||||
<p class="card-text text-light">
|
||||
<i class="bi bi-calendar3 me-2"></i>Год выпуска: ${movie.year}
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0 d-flex justify-content-between">
|
||||
<a href="edit-movie.html?id=${movie.id}" class="btn btn-warning edit-movie btn-sm">
|
||||
<i class="bi bi-pencil"></i> Редактировать
|
||||
</a>
|
||||
<button class="btn btn-danger delete-movie btn-sm">
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return movieCard;
|
||||
}
|
||||
|
||||
renderMovies(movies) {
|
||||
// Make sure movies is an array before using forEach
|
||||
if (!Array.isArray(movies)) {
|
||||
console.error('Expected movies to be an array but got:', movies);
|
||||
movies = [];
|
||||
}
|
||||
|
||||
// Use movieContainer property instead of looking for 'movies-container'
|
||||
if (!this.movieContainer) {
|
||||
console.error('Movie container element not found. Make sure an element with id "movieContainer" exists in your HTML.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.movieContainer.innerHTML = '';
|
||||
|
||||
// If we're on the homepage, only show up to 6 featured movies
|
||||
const isHomepage = window.location.pathname.endsWith('index.html') || window.location.pathname.endsWith('/');
|
||||
const moviesToShow = isHomepage ? movies.slice(0, 6) : movies;
|
||||
|
||||
moviesToShow.forEach(movie => {
|
||||
const movieCard = this.createMovieElement(movie);
|
||||
this.movieContainer.appendChild(movieCard);
|
||||
});
|
||||
|
||||
// If we're on the homepage and there are more movies, add a "See All" button
|
||||
if (isHomepage && movies.length > 6) {
|
||||
const seeAllContainer = document.createElement('div');
|
||||
seeAllContainer.className = 'col-12 text-center mt-4';
|
||||
|
||||
const seeAllButton = document.createElement('a');
|
||||
seeAllButton.href = 'catalog.html';
|
||||
seeAllButton.className = 'btn btn-primary';
|
||||
seeAllButton.textContent = 'Смотреть все фильмы';
|
||||
|
||||
seeAllContainer.appendChild(seeAllButton);
|
||||
this.movieContainer.parentNode.appendChild(seeAllContainer);
|
||||
}
|
||||
}
|
||||
|
||||
fillEditModal(movie) {
|
||||
if (!this.editModal) return;
|
||||
|
||||
const titleInput = this.editModal.querySelector('#editMovieTitle');
|
||||
const directorInput = this.editModal.querySelector('#editMovieDirector');
|
||||
const genreSelect = this.editModal.querySelector('#editMovieGenre');
|
||||
const yearInput = this.editModal.querySelector('#editMovieYear');
|
||||
const descriptionInput = this.editModal.querySelector('#editMovieDescription');
|
||||
const posterPreview = this.editModal.querySelector('#editPosterPreview');
|
||||
|
||||
titleInput.value = movie.title;
|
||||
directorInput.value = movie.director;
|
||||
|
||||
// Handle genres (multi-select)
|
||||
if (Array.isArray(movie.genres)) {
|
||||
Array.from(genreSelect.options).forEach(option => {
|
||||
option.selected = movie.genres.includes(option.text);
|
||||
});
|
||||
}
|
||||
|
||||
yearInput.value = movie.year;
|
||||
descriptionInput.value = movie.description || '';
|
||||
|
||||
if (movie.poster) {
|
||||
posterPreview.src = movie.poster;
|
||||
this.editModal.querySelector('.preview-container').classList.remove('d-none');
|
||||
}
|
||||
|
||||
this.editModal.dataset.movieId = movie.id;
|
||||
}
|
||||
|
||||
bindDeleteMovie(handler) {
|
||||
if (!this.movieContainer) return;
|
||||
|
||||
// Use event delegation to handle delete button clicks
|
||||
// This works on any page that has the movieContainer
|
||||
this.movieContainer.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.delete-movie')) {
|
||||
const movieCard = e.target.closest('.movie-card');
|
||||
const movieId = movieCard.dataset.movieId;
|
||||
if (confirm('Вы уверены, что хотите удалить этот фильм?')) {
|
||||
handler(movieId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindEditMovie(handler) {
|
||||
if (!this.movieContainer) return;
|
||||
|
||||
this.movieContainer.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.edit-movie')) {
|
||||
const movieCard = e.target.closest('.movie-card');
|
||||
const movieId = movieCard.dataset.movieId;
|
||||
handler(movieId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindSaveEditedMovie(handler) {
|
||||
if (!this.editModal) return;
|
||||
|
||||
const form = this.editModal.querySelector('form');
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const movieId = this.editModal.dataset.movieId;
|
||||
const updatedMovie = {
|
||||
title: form.querySelector('#editMovieTitle').value,
|
||||
director: form.querySelector('#editMovieDirector').value,
|
||||
genres: Array.from(form.querySelector('#editMovieGenre').selectedOptions).map(option => option.text),
|
||||
year: form.querySelector('#editMovieYear').value,
|
||||
description: form.querySelector('#editMovieDescription').value,
|
||||
poster: form.querySelector('#editPosterPreview').src
|
||||
};
|
||||
|
||||
handler(movieId, updatedMovie);
|
||||
|
||||
// Close modal using Bootstrap
|
||||
bootstrap.Modal.getInstance(this.editModal).hide();
|
||||
});
|
||||
}
|
||||
|
||||
bindFilterMovies(handler) {
|
||||
const genreSelect = document.getElementById('genre-select');
|
||||
const yearSelect = document.getElementById('year-select');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
|
||||
if (genreSelect) {
|
||||
genreSelect.addEventListener('change', () => {
|
||||
const filters = {
|
||||
genre: genreSelect.value,
|
||||
year: yearSelect ? yearSelect.value : 'all',
|
||||
search: searchInput ? searchInput.value : ''
|
||||
};
|
||||
handler(filters);
|
||||
});
|
||||
}
|
||||
|
||||
if (yearSelect) {
|
||||
yearSelect.addEventListener('change', () => {
|
||||
const filters = {
|
||||
genre: genreSelect ? genreSelect.value : 'all',
|
||||
year: yearSelect.value,
|
||||
search: searchInput ? searchInput.value : ''
|
||||
};
|
||||
handler(filters);
|
||||
});
|
||||
}
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', () => {
|
||||
const filters = {
|
||||
genre: genreSelect ? genreSelect.value : 'all',
|
||||
year: yearSelect ? yearSelect.value : 'all',
|
||||
search: searchInput.value
|
||||
};
|
||||
handler(filters);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
295
src/main.js
295
src/main.js
@@ -1,14 +1,27 @@
|
||||
// Импортируем Bootstrap
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
|
||||
import * as bootstrap from 'bootstrap/dist/js/bootstrap.bundle.min.js';
|
||||
|
||||
// Make bootstrap available globally
|
||||
window.bootstrap = bootstrap;
|
||||
|
||||
// Импортируем собственные стили
|
||||
import '../style.css';
|
||||
|
||||
// Импортируем MVC компоненты
|
||||
import { MovieModel } from './components/movie/MovieModel.js';
|
||||
import { MovieView } from './components/movie/MovieView.js';
|
||||
import { MovieController } from './components/movie/MovieController.js';
|
||||
|
||||
// Инициализация общего функционала сайта
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('Сайт успешно загружен!');
|
||||
|
||||
// Инициализация MVC компонентов
|
||||
const movieModel = new MovieModel();
|
||||
const movieView = new MovieView();
|
||||
const movieController = new MovieController(movieModel, movieView);
|
||||
|
||||
// Инициализация выпадающего меню, если оно есть на странице
|
||||
const dropdownElements = document.querySelectorAll('.dropdown');
|
||||
if (dropdownElements.length > 0) {
|
||||
@@ -19,107 +32,205 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Добавление обработчиков для фильтров в каталоге
|
||||
const genreSelect = document.getElementById('genre-select');
|
||||
if (genreSelect) {
|
||||
genreSelect.addEventListener('change', () => {
|
||||
// Здесь будет логика фильтрации по жанрам
|
||||
console.log('Выбран жанр:', genreSelect.value);
|
||||
// Initialize all Bootstrap modals properly
|
||||
const modalElements = document.querySelectorAll('.modal');
|
||||
if (modalElements.length > 0) {
|
||||
modalElements.forEach(modalEl => {
|
||||
new bootstrap.Modal(modalEl, {
|
||||
keyboard: true,
|
||||
focus: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Form submission handler
|
||||
// Add event listeners for close and cancel buttons in modals
|
||||
const closeButtons = document.querySelectorAll('[data-bs-dismiss="modal"]');
|
||||
if (closeButtons.length > 0) {
|
||||
closeButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const modalElement = button.closest('.modal');
|
||||
if (modalElement) {
|
||||
const modalInstance = bootstrap.Modal.getInstance(modalElement);
|
||||
if (modalInstance) {
|
||||
modalInstance.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Обработка формы добавления фильма
|
||||
const addMovieForm = document.getElementById('addMovieForm');
|
||||
const movieContainer = document.getElementById('movieContainer');
|
||||
|
||||
if (addMovieForm && movieContainer) {
|
||||
if (addMovieForm) {
|
||||
// Предпросмотр постера
|
||||
const addMoviePoster = document.getElementById('addMoviePoster');
|
||||
const addPosterPreview = document.getElementById('addPosterPreview');
|
||||
const addPreviewContainer = document.querySelector('#addMovieModal .preview-container');
|
||||
|
||||
if (addMoviePoster && addPosterPreview && addPreviewContainer) {
|
||||
addMoviePoster.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
addPosterPreview.src = e.target.result;
|
||||
addPreviewContainer.classList.remove('d-none');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Отправка формы
|
||||
addMovieForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Get form values
|
||||
const title = document.getElementById('movieTitle').value;
|
||||
const director = document.getElementById('movieDirector').value;
|
||||
const genre = document.getElementById('movieGenre').value;
|
||||
const year = document.getElementById('movieYear').value;
|
||||
const poster = document.getElementById('moviePoster').value;
|
||||
|
||||
// Create movie card
|
||||
const movieCard = createMovieCard(title, director, genre, year, poster);
|
||||
|
||||
// Add card to container
|
||||
const colDiv = document.createElement('div');
|
||||
colDiv.className = 'col';
|
||||
colDiv.appendChild(movieCard);
|
||||
movieContainer.prepend(colDiv);
|
||||
|
||||
// Reset form
|
||||
const movieData = {
|
||||
title: document.getElementById('addMovieTitle').value,
|
||||
director: document.getElementById('addMovieDirector').value,
|
||||
genres: Array.from(document.getElementById('addMovieGenre').selectedOptions)
|
||||
.map(option => option.text),
|
||||
year: document.getElementById('addMovieYear').value,
|
||||
description: document.getElementById('addMovieDescription').value,
|
||||
poster: document.getElementById('addPosterPreview')?.src || ''
|
||||
};
|
||||
|
||||
movieController.handleSaveNewMovie(movieData);
|
||||
addMovieForm.reset();
|
||||
|
||||
if (addPreviewContainer) {
|
||||
addPreviewContainer.classList.add('d-none');
|
||||
}
|
||||
|
||||
// Close the modal window
|
||||
const addMovieModal = document.getElementById('addMovieModal');
|
||||
if (addMovieModal) {
|
||||
const modalInstance = bootstrap.Modal.getInstance(addMovieModal);
|
||||
if (modalInstance) {
|
||||
modalInstance.hide();
|
||||
}
|
||||
}
|
||||
|
||||
alert('Фильм успешно добавлен!');
|
||||
});
|
||||
|
||||
// Add cancel button handler
|
||||
const cancelButton = addMovieForm.querySelector('button[type="button"]');
|
||||
if (cancelButton) {
|
||||
cancelButton.addEventListener('click', () => {
|
||||
const modalElement = cancelButton.closest('.modal');
|
||||
if (modalElement) {
|
||||
const modalInstance = bootstrap.Modal.getInstance(modalElement);
|
||||
if (modalInstance) {
|
||||
modalInstance.hide();
|
||||
} else {
|
||||
// Fallback if modal instance isn't available
|
||||
modalElement.classList.remove('show');
|
||||
modalElement.style.display = 'none';
|
||||
document.body.classList.remove('modal-open');
|
||||
const backdrop = document.querySelector('.modal-backdrop');
|
||||
if (backdrop) {
|
||||
backdrop.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка формы редактирования фильма
|
||||
const editMovieForm = document.getElementById('editMovieForm');
|
||||
if (editMovieForm) {
|
||||
const editMoviePoster = document.getElementById('editMoviePoster');
|
||||
const editPosterPreview = document.getElementById('editPosterPreview');
|
||||
const editPreviewContainer = editMovieForm.querySelector('.preview-container');
|
||||
|
||||
if (editMoviePoster && editPosterPreview && editPreviewContainer) {
|
||||
editMoviePoster.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
editPosterPreview.src = e.target.result;
|
||||
editPreviewContainer.classList.remove('d-none');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Добавление кнопки для перехода на страницу добавления фильма
|
||||
const movieContainer = document.getElementById('movieContainer');
|
||||
if (movieContainer && window.location.pathname.includes('catalog.html')) {
|
||||
const addButtonContainer = document.createElement('div');
|
||||
addButtonContainer.className = 'col-12 mb-4 text-center';
|
||||
addButtonContainer.innerHTML = `
|
||||
<a href="add-movie.html" class="btn btn-success">
|
||||
<i class="bi bi-plus-circle"></i> Добавить новый фильм
|
||||
</a>
|
||||
`;
|
||||
movieContainer.parentNode.insertBefore(addButtonContainer, movieContainer);
|
||||
}
|
||||
|
||||
// Инициализация фильтров для каталога
|
||||
const genreSelect = document.getElementById('genre-select');
|
||||
const yearSelect = document.getElementById('year-select');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
|
||||
if (genreSelect || yearSelect || searchInput) {
|
||||
// Заполнение выпадающего списка годов
|
||||
if (yearSelect) {
|
||||
const currentYear = new Date().getFullYear();
|
||||
yearSelect.innerHTML = '<option value="all">Все годы</option>';
|
||||
for (let year = currentYear; year >= 1900; year--) {
|
||||
const option = document.createElement('option');
|
||||
option.value = year.toString();
|
||||
option.textContent = year.toString();
|
||||
yearSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Function to create movie card
|
||||
function createMovieCard(title, director, genre, year, poster) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card movie-card h-100 bg-dark';
|
||||
|
||||
card.innerHTML = `
|
||||
<img src="${poster}" class="card-img-top" alt="${title} Poster">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-white"><i class="bi bi-film text-orange me-2"></i>${title}</h5>
|
||||
<p class="card-text text-white"><i class="bi bi-person-video3 me-2"></i>Режиссер: ${director}</p>
|
||||
<p class="card-text text-light"><i class="bi bi-tags me-2"></i>Жанр: ${genre}</p>
|
||||
<p class="card-text text-light"><i class="bi bi-calendar3 me-2"></i>Год выпуска: ${year}</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0">
|
||||
<a href="about.html" class="btn btn-orange w-100"><i class="bi bi-play-circle me-2"></i>Смотреть сейчас</a>
|
||||
</div>
|
||||
`;
|
||||
// Add this to your main.js or in a script tag at the end of your HTML
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
// Handle movie form if it exists
|
||||
const addMovieForm = document.getElementById('addMovieForm');
|
||||
const posterPreview = document.getElementById('posterPreview');
|
||||
const previewContainer = document.querySelector('.preview-container');
|
||||
|
||||
if (addMovieForm) {
|
||||
// Handle poster preview
|
||||
const moviePoster = document.getElementById('moviePoster');
|
||||
moviePoster.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
posterPreview.src = e.target.result;
|
||||
previewContainer.classList.remove('d-none');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle form submission
|
||||
addMovieForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = {
|
||||
title: document.getElementById('movieTitle').value,
|
||||
director: document.getElementById('movieDirector').value,
|
||||
genres: Array.from(document.getElementById('movieGenre').selectedOptions).map(option => option.text),
|
||||
year: document.getElementById('movieYear').value,
|
||||
description: document.getElementById('movieDescription').value,
|
||||
poster: posterPreview.src
|
||||
};
|
||||
|
||||
// Here you can add code to save the movie data
|
||||
console.log('Movie data:', formData);
|
||||
|
||||
// Reset form and preview
|
||||
addMovieForm.reset();
|
||||
previewContainer.classList.add('d-none');
|
||||
|
||||
// Show success message
|
||||
alert('Фильм успешно добавлен!');
|
||||
});
|
||||
}
|
||||
// Handle poster image preview for Add Movie modal
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const addPosterInput = document.getElementById('addMoviePoster');
|
||||
const addPosterPreview = document.getElementById('addPosterPreview');
|
||||
const addPreviewContainer = document.querySelector('#addMovieModal .preview-container');
|
||||
|
||||
if (addPosterInput && addPosterPreview && addPreviewContainer) {
|
||||
addPosterInput.addEventListener('change', function() {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
addPosterPreview.src = e.target.result;
|
||||
addPreviewContainer.classList.remove('d-none');
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Similar code for edit modal if needed
|
||||
const editPosterInput = document.getElementById('editMoviePoster');
|
||||
const editPosterPreview = document.getElementById('editPosterPreview');
|
||||
const editPreviewContainer = document.querySelector('#editMovieModal .preview-container');
|
||||
|
||||
if (editPosterInput && editPosterPreview && editPreviewContainer) {
|
||||
editPosterInput.addEventListener('change', function() {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
editPosterPreview.src = e.target.result;
|
||||
editPreviewContainer.classList.remove('d-none');
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -9,7 +9,8 @@ const pages = [
|
||||
'seriales.html',
|
||||
'reviews.html',
|
||||
'about.html',
|
||||
'add-movie.html' // Add this line
|
||||
'add-movie.html',
|
||||
'edit-movie.html'
|
||||
];
|
||||
|
||||
// Создаем объект с входными точками для каждой страницы
|
||||
@@ -19,13 +20,16 @@ pages.forEach(page => {
|
||||
});
|
||||
|
||||
export default defineConfig({
|
||||
root: './',
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
rollupOptions: {
|
||||
input
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
open: '/index.html'
|
||||
}
|
||||
|
||||
BIN
Отчет.docx
BIN
Отчет.docx
Binary file not shown.
Reference in New Issue
Block a user