Start small MVC changes

This commit is contained in:
2025-11-24 16:26:13 +08:00
parent d7fe90a615
commit 24b3788493
51 changed files with 4012 additions and 4063 deletions
+58
View File
@@ -0,0 +1,58 @@
<?php
// views/series/create.php
include 'views/layouts/header.php';
?>
<h1>Создание новой серии</h1>
<?php if (isset($error) && $error): ?>
<div class="alert alert-error">
<?= e($error) ?>
</div>
<?php endif; ?>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div style="max-width: 100%; margin-bottom: 1rem;">
<label for="title" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Название серии *
</label>
<input type="text" id="title" name="title"
value="<?= e($_POST['title'] ?? '') ?>"
placeholder="Введите название серии"
style="width: 100%; margin-bottom: 1.5rem;"
required>
<label for="description" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Описание серии
</label>
<textarea id="description" name="description"
placeholder="Описание сюжета серии, общая концепция..."
rows="6"
style="width: 100%;"><?= e($_POST['description'] ?? '') ?></textarea>
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button type="submit" class="contrast">
📚 Создать серию
</button>
<a href="<?= SITE_URL ?>/series" role="button" class="secondary">
❌ Отмена
</a>
</div>
</form>
<div style="margin-top: 2rem; padding: 1rem; background: var(--card-background-color); border-radius: 5px;">
<h3>Что такое серия?</h3>
<p>Серия позволяет объединить несколько книг в одну тематическую коллекцию. Это полезно для:</p>
<ul>
<li>Циклов книг с общим сюжетом</li>
<li>Книг в одном мире или вселенной</li>
<li>Организации книг по темам или жанрам</li>
</ul>
<p>Вы сможете добавить книги в серию после её создания.</p>
</div>
<?php include 'views/layouts/footer.php'; ?>
+126
View File
@@ -0,0 +1,126 @@
<?php
// views/series/edit.php
include 'views/layouts/header.php';
?>
<h1>Редактирование серии: <?= e($series['title']) ?></h1>
<?php if (isset($error) && $error): ?>
<div class="alert alert-error">
<?= e($error) ?>
</div>
<?php endif; ?>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div style="max-width: 100%; margin-bottom: 1rem;">
<label for="title" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Название серии *
</label>
<input type="text" id="title" name="title"
value="<?= e($series['title']) ?>"
placeholder="Введите название серии"
style="width: 100%; margin-bottom: 1.5rem;"
required>
<label for="description" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Описание серии
</label>
<textarea id="description" name="description"
placeholder="Описание сюжета серии, общая концепция..."
rows="6"
style="width: 100%;"><?= e($series['description']) ?></textarea>
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button type="submit" class="contrast">
💾 Сохранить изменения
</button>
<a href="<?= SITE_URL ?>/series" role="button" class="secondary">
❌ Отмена
</a>
</div>
</form>
<?php if ($series): ?>
<div style="margin-top: 3rem;">
<h3>Книги в этой серии</h3>
<?php if (empty($books_in_series)): ?>
<div style="text-align: center; padding: 2rem; background: var(--card-background-color); border-radius: 5px;">
<p>В этой серии пока нет книг.</p>
<a href="<?= SITE_URL ?>/books" class="adaptive-button">📚 Добавить книги</a>
</div>
<?php else: ?>
<div style="overflow-x: auto;">
<table class="compact-table">
<thead>
<tr>
<th style="width: 10%;">Порядок</th>
<th style="width: 40%;">Название книги</th>
<th style="width: 20%;">Жанр</th>
<th style="width: 15%;">Статус</th>
<th style="width: 15%;">Действия</th>
</tr>
</thead>
<tbody>
<?php foreach ($books_in_series as $book): ?>
<tr>
<td><?= $book['sort_order_in_series'] ?></td>
<td>
<strong><?= e($book['title']) ?></strong>
<?php if ($book['description']): ?>
<br><small style="color: var(--muted-color);"><?= e(mb_strimwidth($book['description'], 0, 100, '...')) ?></small>
<?php endif; ?>
</td>
<td><?= e($book['genre']) ?></td>
<td>
<span style="color: <?= $book['published'] ? 'green' : 'orange' ?>">
<?= $book['published'] ? '✅ Опубликована' : '📝 Черновик' ?>
</span>
</td>
<td>
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/edit" class="compact-button secondary">
Редактировать
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
// Вычисляем общую статистику
$total_chapters = 0;
$total_words = 0;
foreach ($books_in_series as $book) {
$bookModel = new Book($pdo);
$stats = $bookModel->getBookStats($book['id']);
$total_chapters += $stats['chapter_count'] ?? 0;
$total_words += $stats['total_words'] ?? 0;
}
?>
<div style="margin-top: 1rem; padding: 0.5rem; background: var(--card-background-color); border-radius: 3px;">
<strong>Статистика серии:</strong>
Книг: <?= count($books_in_series) ?> |
Глав: <?= $total_chapters ?> |
Слов: <?= $total_words ?>
</div>
<?php endif; ?>
</div>
<div style="margin-top: 2rem; text-align: center;">
<form method="post" action="<?= SITE_URL ?>/series/<?= $series['id'] ?>/delete" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите удалить серию «<?= e($series['title']) ?>»? Книги останутся, но будут убраны из серии.');">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="button" style="background: #ff4444; border-color: #ff4444; color: white;">
🗑️ Удалить серию
</button>
</form>
</div>
<?php endif; ?>
<?php include 'views/layouts/footer.php'; ?>
+122
View File
@@ -0,0 +1,122 @@
<?php
// views/series/view_public.php
include 'views/layouts/header.php';
?>
<div class="container">
<article style="max-width: 800px; margin: 0 auto;">
<header style="text-align: center; margin-bottom: 2rem; border-bottom: 2px solid var(--muted-border-color); padding-bottom: 1rem;">
<h1 style="margin-bottom: 0.5rem;"><?= e($series['title']) ?></h1>
<p style="color: var(--muted-color); font-style: italic; margin-bottom: 0.5rem;">
Серия книг от
<a href="<?= SITE_URL ?>/author/<?= $author['id'] ?>"><?= e($author['display_name'] ?: $author['username']) ?></a>
</p>
<?php if ($series['description']): ?>
<div style="background: var(--card-background-color); padding: 1rem; border-radius: 5px; margin: 1rem 0; text-align: left;">
<?= $Parsedown->text($series['description']) ?>
</div>
<?php endif; ?>
<div style="display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; font-size: 0.9em; color: var(--muted-color);">
<span>Книг: <?= count($books) ?></span>
<span>Глав: <?= $total_chapters ?></span>
<span>Слов: <?= $total_words ?></span>
</div>
</header>
<?php if (empty($books)): ?>
<div style="text-align: center; padding: 3rem; background: var(--card-background-color); border-radius: 5px;">
<h3>В этой серии пока нет опубликованных книг</h3>
<p>Автор еще не опубликовал книги из этой серии</p>
</div>
<?php else: ?>
<div class="series-books">
<h2 style="text-align: center; margin-bottom: 2rem;">Книги серии</h2>
<?php foreach ($books as $book): ?>
<article style="display: flex; gap: 1rem; align-items: flex-start; margin-bottom: 2rem; padding: 1rem; background: var(--card-background-color); border-radius: 8px;">
<?php if ($book['cover_image']): ?>
<div style="flex-shrink: 0;">
<img src="<?= COVERS_URL . e($book['cover_image']) ?>"
alt="<?= e($book['title']) ?>"
style="max-width: 120px; height: auto; border-radius: 4px; border: 1px solid var(--border-color);"
onerror="this.style.display='none'">
</div>
<?php else: ?>
<div style="flex-shrink: 0;">
<div class="cover-placeholder" style="width: 120px; height: 160px; background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); border-radius: 4px; display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">
📚
</div>
</div>
<?php endif; ?>
<div style="flex: 1;">
<h3 style="margin-top: 0;">
<?php if ($book['sort_order_in_series']): ?>
<small style="color: var(--muted-color);">Книга <?= $book['sort_order_in_series'] ?></small><br>
<?php endif; ?>
<?= e($book['title']) ?>
</h3>
<?php if ($book['genre']): ?>
<p style="color: var(--muted-color); margin: 0.5rem 0;"><em><?= e($book['genre']) ?></em></p>
<?php endif; ?>
<?php if ($book['description']): ?>
<p style="margin-bottom: 1rem;"><?= nl2br(e($book['description'])) ?></p>
<?php endif; ?>
<div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center;">
<a href="<?= SITE_URL ?>/book/<?= e($book['share_token']) ?>" class="adaptive-button">
Читать
</a>
<?php
$bookModel = new Book($pdo);
$book_stats = $bookModel->getBookStats($book['id'], true);
?>
<small style="color: var(--muted-color);">
Глав: <?= $book_stats['chapter_count'] ?? 0 ?> | Слов: <?= $book_stats['total_words'] ?? 0 ?>
</small>
</div>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
<footer style="margin-top: 3rem; padding-top: 1rem; border-top: 2px solid var(--muted-border-color); text-align: center;">
<p style="color: var(--muted-color);">
Серия создана в <?= e(APP_NAME) ?> •
Автор: <a href="<?= SITE_URL ?>/author/<?= $author['id'] ?>"><?= e($author['display_name'] ?: $author['username']) ?></a>
</p>
</footer>
</article>
</div>
<style>
.series-books article {
transition: transform 0.2s ease, box-shadow 0.2s ease;
border: 1px solid var(--border-color);
}
.series-books article:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
@media (max-width: 768px) {
.series-books article {
flex-direction: column;
text-align: center;
}
.series-books .book-cover {
align-self: center;
}
}
</style>
<?php include 'views/layouts/footer.php'; ?>