Change CSS framework from PicoCSS to Bootstrap 5.3

This commit is contained in:
2025-11-27 14:46:51 +08:00
parent e51768622f
commit 39cda7814f
245 changed files with 678223 additions and 2750 deletions
+61 -47
View File
@@ -3,56 +3,70 @@
include 'views/layouts/header.php';
?>
<h1>Создание новой серии</h1>
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h2">Создание новой серии</h1>
<a href="<?= SITE_URL ?>/series" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Назад к сериям
</a>
</div>
<?php if (isset($error) && $error): ?>
<div class="alert alert-error">
<?= e($error) ?>
</div>
<?php endif; ?>
<?php if (isset($error) && $error): ?>
<div class="alert alert-danger">
<?= 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 class="card">
<div class="card-body">
<form method="post">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div class="mb-3">
<label for="title" class="form-label">Название серии *</label>
<input type="text" class="form-control" id="title" name="title"
value="<?= e($_POST['title'] ?? '') ?>"
placeholder="Введите название серии" required>
</div>
<div class="mb-4">
<label for="description" class="form-label">Описание серии</label>
<textarea class="form-control" id="description" name="description"
placeholder="Описание сюжета серии, общая концепция..."
rows="6"><?= e($_POST['description'] ?? '') ?></textarea>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">
<i class="bi bi-collection"></i> Создать серию
</button>
<a href="<?= SITE_URL ?>/series" class="btn btn-outline-danger">
<i class="bi bi-x-circle"></i> Отмена
</a>
</div>
</form>
</div>
</div>
<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 class="card mt-4">
<div class="card-header">
<h5 class="card-title mb-0">Что такое серия?</h5>
</div>
<div class="card-body">
<p>Серия позволяет объединить несколько книг в одну тематическую коллекцию. Это полезно для:</p>
<ul>
<li>Циклов книг с общим сюжетом</li>
<li>Книг в одном мире или вселенной</li>
<li>Организации книг по темам или жанрам</li>
</ul>
<p>Вы сможете добавить книги в серию после её создания.</p>
</div>
</div>
</div>
</div>
</div>
<?php include 'views/layouts/footer.php'; ?>
+127 -124
View File
@@ -2,115 +2,147 @@
include 'views/layouts/header.php';
?>
<h1>Редактирование серии: <?= e($series['title']) ?></h1>
<article>
<h2>Основная информация</h2>
<form method="post" action="/series/<?= $series['id'] ?>/edit">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<label for="title">
Название серии *
<input type="text" id="title" name="title" value="<?= e($series['title']) ?>" required>
</label>
<label for="description">
Описание серии
<textarea id="description" name="description" rows="4"><?= e($series['description'] ?? '') ?></textarea>
</label>
<button type="submit" class="primary-btn">Сохранить изменения</button>
</form>
</article>
<div class="grid">
<div>
<article>
<h2>Добавить книгу в серию</h2>
<?php if (!empty($available_books)): ?>
<form method="post" action="/series/<?= $series['id'] ?>/add-book">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<label for="book_id">
Выберите книгу
<select id="book_id" name="book_id" required>
<option value="">-- Выберите книгу --</option>
<?php foreach ($available_books as $book): ?>
<option value="<?= $book['id'] ?>"><?= e($book['title']) ?></option>
<?php endforeach; ?>
</select>
</label>
<label for="sort_order">
Порядковый номер в серии
<input type="number" id="sort_order" name="sort_order" value="<?= count($books_in_series) + 1 ?>" min="1">
</label>
<button type="submit" class="secondary-btn">Добавить в серию</button>
</form>
<?php else: ?>
<p>Все ваши книги уже добавлены в эту серию или у вас нет доступных книг.</p>
<a href="/books/create" class="primary-btn" role="button">Создать новую книгу</a>
<?php endif; ?>
</article>
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h2">Редактирование серии: <?= e($series['title']) ?></h1>
<a href="<?= SITE_URL ?>/series" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Назад к сериям
</a>
</div>
<div>
<article>
<h2>Книги в серии (<?= count($books_in_series) ?>)</h2>
<?php if (!empty($books_in_series)): ?>
<div id="series-books-list">
<form id="reorder-form" method="post" action="/series/<?= $series['id'] ?>/update-order">
<div class="row">
<div class="col-lg-6">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title mb-0">Основная информация</h5>
</div>
<div class="card-body">
<form method="post" action="/series/<?= $series['id'] ?>/edit">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div class="books-list">
<?php foreach ($books_in_series as $index => $book): ?>
<div class="book-item" data-book-id="<?= $book['id'] ?>">
<div class="book-drag-handle" style="cursor: move;">☰</div>
<div class="book-info">
<strong><?= e($book['title']) ?></strong>
<small>Порядок: <?= $book['sort_order_in_series'] ?></small>
</div>
<div class="book-actions" style="display: grid; min-width: 2rem; margin-top: 1rem;">
<a href="/books/<?= $book['id'] ?>/edit" class="compact-button" role="button" style="margin-top: 0em;">✏️</a>
<form method="post" action="/series/<?= $series['id'] ?>/remove-book/<?= $book['id'] ?>"
style="display: inline;" onsubmit="return confirm('Удалить книгу из серии?')">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="compact-button red-btn" style="margin-top: 0em;">🗑️</button>
</form>
</div>
<input type="hidden" name="order[]" value="<?= $book['id'] ?>">
</div>
<?php endforeach; ?>
<div class="mb-3">
<label for="title" class="form-label">Название серии *</label>
<input type="text" class="form-control" id="title" name="title"
value="<?= e($series['title']) ?>" required>
</div>
<button type="submit" class="secondary-btn" id="save-order-btn" style="display: none;">
Сохранить порядок
<div class="mb-3">
<label for="description" class="form-label">Описание серии</label>
<textarea class="form-control" id="description" name="description"
rows="4"><?= e($series['description'] ?? '') ?></textarea>
</div>
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Сохранить изменения
</button>
</form>
</div>
<?php else: ?>
<p>В этой серии пока нет книг. Добавьте книги с помощью формы слева.</p>
<?php endif; ?>
</article>
</div>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Добавить книгу в серию</h5>
</div>
<div class="card-body">
<?php if (!empty($available_books)): ?>
<form method="post" action="/series/<?= $series['id'] ?>/add-book">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div class="mb-3">
<label for="book_id" class="form-label">Выберите книгу</label>
<select class="form-select" id="book_id" name="book_id" required>
<option value="">-- Выберите книгу --</option>
<?php foreach ($available_books as $book): ?>
<option value="<?= $book['id'] ?>"><?= e($book['title']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label for="sort_order" class="form-label">Порядковый номер в серии</label>
<input type="number" class="form-control" id="sort_order" name="sort_order"
value="<?= count($books_in_series) + 1 ?>" min="1">
</div>
<button type="submit" class="btn btn-outline-primary">
<i class="bi bi-plus-circle"></i> Добавить в серию
</button>
</form>
<?php else: ?>
<div class="text-center py-3">
<i class="bi bi-journal-bookmark fs-1 text-muted"></i>
<p class="text-muted mt-2">Все ваши книги уже добавлены в эту серию или у вас нет доступных книг.</p>
<a href="/books/create" class="btn btn-primary">Создать новую книгу</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Книги в серии (<?= count($books_in_series) ?>)</h5>
</div>
<div class="card-body">
<?php if (!empty($books_in_series)): ?>
<div id="series-books-list">
<form id="reorder-form" method="post" action="/series/<?= $series['id'] ?>/update-order">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div class="books-list">
<?php foreach ($books_in_series as $index => $book): ?>
<div class="book-item border rounded p-3 mb-2 d-flex align-items-center"
data-book-id="<?= $book['id'] ?>">
<div class="book-drag-handle text-muted me-3" style="cursor: move;">
<i class="bi bi-grip-vertical"></i>
</div>
<div class="book-info flex-grow-1">
<strong><?= e($book['title']) ?></strong>
<br>
<small class="text-muted">Порядок: <?= $book['sort_order_in_series'] ?></small>
</div>
<div class="book-actions ms-3">
<div class="btn-group btn-group-sm">
<a href="/books/<?= $book['id'] ?>/edit" class="btn btn-outline-primary" title="Редактировать">
<i class="bi bi-pencil"></i>
</a>
<form method="post" action="/series/<?= $series['id'] ?>/remove-book/<?= $book['id'] ?>"
onsubmit="return confirm('Удалить книгу из серии?')" class="d-inline">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="btn btn-outline-danger" title="Удалить из серии">
<i class="bi bi-trash"></i>
</button>
</form>
</div>
</div>
<input type="hidden" name="order[]" value="<?= $book['id'] ?>">
</div>
<?php endforeach; ?>
</div>
<button type="submit" class="btn btn-outline-success w-100 mt-3" id="save-order-btn" style="display: none;">
<i class="bi bi-check-circle"></i> Сохранить порядок
</button>
</form>
</div>
<?php else: ?>
<div class="text-center py-4">
<i class="bi bi-journal-bookmark fs-1 text-muted"></i>
<p class="text-muted mt-2">В этой серии пока нет книг.</p>
<p class="text-muted small">Добавьте книги с помощью формы слева.</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<style>
.books-list {
border: 1px solid #e0e0e0;
border-radius: 4px;
}
.book-item {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid #f0f0f0;
background: white;
transition: background-color 0.2s ease;
}
.book-item:last-child {
border-bottom: none;
background: white;
}
.book-item:hover {
@@ -126,30 +158,8 @@ include 'views/layouts/header.php';
}
.book-drag-handle {
padding: 0 10px;
color: #666;
font-size: 1.2rem;
}
.book-info {
flex: 1;
padding: 0 10px;
}
.book-info strong {
display: block;
margin-bottom: 2px;
}
.book-info small {
color: #666;
font-size: 0.8rem;
}
.book-actions {
display: flex;
gap: 5px;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
@@ -172,13 +182,6 @@ document.addEventListener('DOMContentLoaded', function() {
// Автосохранение порядка через 2 секунды после изменения
let saveTimeout;
saveOrderBtn.addEventListener('click', function(e) {
e.preventDefault();
clearTimeout(saveTimeout);
document.getElementById('reorder-form').submit();
});
// Автоматическое сохранение при изменении порядка
booksList.addEventListener('sortupdate', function() {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
+90 -75
View File
@@ -2,82 +2,97 @@
include 'views/layouts/header.php';
?>
<div style="display: block; justify-content: between; align-items: center; margin-bottom: 2rem; flex-wrap: wrap; gap: 1rem;">
<h1 style="margin: 0;">Мои серии книг</h1>
<a href="/series/create" class="action-button primary" role="button"> Создать серию</a>
</div>
<?php if (empty($series)): ?>
<article class="series-empty-state">
<div class="series-empty-icon">📚</div>
<h2>Пока нет серий</h2>
<p style="color: #666; margin-bottom: 2rem;">
Создайте свою первую серию, чтобы организовать книги в циклы и сериалы.
</p>
<div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
<a href="/series/create" class="action-button primary" role="button">Создать серию</a>
<a href="/books" class="action-button secondary">Перейти к книгам</a>
</div>
</article>
<?php else: ?>
<div class="series-grid">
<?php foreach ($series as $ser): ?>
<article class="series-card">
<div class="series-header">
<h3 class="series-title">
<a href="/series/<?= $ser['id'] ?>/edit"><?= e($ser['title']) ?></a>
</h3>
<div class="series-meta">
Создана <?= date('d.m.Y', strtotime($ser['created_at'])) ?>
<?php if ($ser['updated_at'] != $ser['created_at']): ?>
• Обновлена <?= date('d.m.Y', strtotime($ser['updated_at'])) ?>
<?php endif; ?>
</div>
</div>
<?php if (!empty($ser['description'])): ?>
<div class="series-description">
<?= e($ser['description']) ?>
</div>
<?php endif; ?>
<div class="series-stats-grid">
<div class="series-stat">
<span class="series-stat-number"><?= $ser['book_count'] ?? 0 ?></span>
<span class="series-stat-label">книг</span>
</div>
<div class="series-stat">
<span class="series-stat-number"><?= number_format($ser['total_words'] ?? 0) ?></span>
<span class="series-stat-label">слов</span>
</div>
<div class="series-stat">
<span class="series-stat-number">
<?php
$avg_words = $ser['book_count'] > 0 ? round($ser['total_words'] / $ser['book_count']) : 0;
echo number_format($avg_words);
?>
</span>
<span class="series-stat-label">слов/книга</span>
</div>
</div>
<div class="series-actions" style="display:grid;">
<a href="/series/<?= $ser['id'] ?>/edit" class="compact-button primary-btn" role="button">
✏️ Управление
</a>
<a href="/series/<?= $ser['id'] ?>/view" class="compact-button secondary-btn" target="_blank" role="button">
👁️ Публично
</a>
<form method="post" action="/series/<?= $ser['id'] ?>/delete"
onsubmit="return confirm('Удалить серию? Книги останутся, но будут удалены из серии.')">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="compact-button red-btn">🗑️ Удалить</button>
</form>
</div>
</article>
<?php endforeach; ?>
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h2">Мои серии книг</h1>
<a href="/series/create" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Создать серию
</a>
</div>
<?php endif; ?>
<?php if (empty($series)): ?>
<div class="text-center py-5">
<div class="mb-4">
<i class="bi bi-collection fs-1 text-muted"></i>
</div>
<h3 class="h4 text-muted">Пока нет серий</h3>
<p class="text-muted mb-4">
Создайте свою первую серию, чтобы организовать книги в циклы и сериалы.
</p>
<div class="d-flex gap-2 justify-content-center">
<a href="/series/create" class="btn btn-primary">Создать серию</a>
<a href="/books" class="btn btn-outline-secondary">Перейти к книгам</a>
</div>
</div>
<?php else: ?>
<div class="row g-4">
<?php foreach ($series as $ser): ?>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<h5 class="card-title">
<a href="/series/<?= $ser['id'] ?>/edit" class="text-decoration-none"><?= e($ser['title']) ?></a>
</h5>
<span class="badge bg-primary"><?= $ser['book_count'] ?? 0 ?> книг</span>
</div>
<div class="text-muted small mb-3">
Создана <?= date('d.m.Y', strtotime($ser['created_at'])) ?>
<?php if ($ser['updated_at'] != $ser['created_at']): ?>
• Обновлена <?= date('d.m.Y', strtotime($ser['updated_at'])) ?>
<?php endif; ?>
</div>
<?php if (!empty($ser['description'])): ?>
<p class="card-text"><?= e($ser['description']) ?></p>
<?php endif; ?>
<div class="row text-center mb-3">
<div class="col-4">
<div class="border-end">
<div class="fw-bold text-primary"><?= $ser['book_count'] ?? 0 ?></div>
<small class="text-muted">книг</small>
</div>
</div>
<div class="col-4">
<div class="border-end">
<div class="fw-bold text-success"><?= number_format($ser['total_words'] ?? 0) ?></div>
<small class="text-muted">слов</small>
</div>
</div>
<div class="col-4">
<div>
<div class="fw-bold text-info">
<?= $ser['book_count'] > 0 ? number_format(round($ser['total_words'] / $ser['book_count'])) : 0 ?>
</div>
<small class="text-muted">слов/книга</small>
</div>
</div>
</div>
<div class="d-grid gap-2">
<a href="/series/<?= $ser['id'] ?>/edit" class="btn btn-outline-primary btn-sm">
<i class="bi bi-pencil"></i> Управление
</a>
<a href="/series/<?= $ser['id'] ?>/view" class="btn btn-outline-success btn-sm" target="_blank">
<i class="bi bi-eye"></i> Публично
</a>
<form method="post" action="/series/<?= $ser['id'] ?>/delete"
onsubmit="return confirm('Удалить серию? Книги останутся, но будут удалены из серии.')">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="btn btn-outline-danger btn-sm">
<i class="bi bi-trash"></i> Удалить
</button>
</form>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php
include 'views/layouts/footer.php';
+116 -103
View File
@@ -3,119 +3,132 @@
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;">
<?= e($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 class="container py-4">
<div class="row justify-content-center">
<div class="col-lg-10">
<article class="card border-0 shadow-sm">
<div class="card-body p-4">
<header class="text-center mb-5">
<h1 class="display-5 mb-3"><?= e($series['title']) ?></h1>
<p class="lead text-muted mb-3">
Серия книг от
<a href="<?= SITE_URL ?>/author/<?= $author['id'] ?>" class="text-decoration-none">
<?= e($author['display_name'] ?: $author['username']) ?>
</a>
</p>
<?php if ($series['description']): ?>
<div class="bg-light p-4 rounded mb-4">
<?= e($series['description']) ?>
</div>
<?php endif; ?>
<div class="d-flex justify-content-center gap-4 flex-wrap">
<div class="text-center">
<div class="h4 text-primary mb-0"><?= count($books) ?></div>
<small class="text-muted">Книг</small>
</div>
<div class="text-center">
<div class="h4 text-success mb-0"><?= $total_chapters ?></div>
<small class="text-muted">Глав</small>
</div>
<div class="text-center">
<div class="h4 text-warning mb-0"><?= $total_words ?></div>
<small class="text-muted">Слов</small>
</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; ?>
</header>
<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>
<?php if (empty($books)): ?>
<div class="text-center py-5">
<i class="bi bi-collection fs-1 text-muted"></i>
<h3 class="h4 text-muted mt-3">В этой серии пока нет опубликованных книг</h3>
<p class="text-muted">Автор еще не опубликовал книги из этой серии</p>
</div>
<?php else: ?>
<div class="mb-5">
<h2 class="h3 text-center mb-4">
<i class="bi bi-book"></i> Книги серии
</h2>
<?php foreach ($books as $book): ?>
<div class="card mb-4">
<div class="card-body">
<div class="row align-items-start">
<?php if ($book['cover_image']): ?>
<div class="col-md-3 text-center mb-3 mb-md-0">
<img src="<?= COVERS_URL . e($book['cover_image']) ?>"
alt="<?= e($book['title']) ?>"
class="img-fluid rounded shadow"
style="max-height: 200px;"
onerror="this.style.display='none'">
</div>
<div class="col-md-9">
<?php else: ?>
<div class="col-12">
<?php endif; ?>
<div class="d-flex justify-content-between align-items-start mb-2">
<h3 class="h4">
<?php if ($book['sort_order_in_series']): ?>
<small class="text-muted">Книга <?= $book['sort_order_in_series'] ?></small><br>
<?php endif; ?>
<?= e($book['title']) ?>
</h3>
</div>
<?php if ($book['genre']): ?>
<p class="text-muted mb-2"><em><?= e($book['genre']) ?></em></p>
<?php endif; ?>
<?php if ($book['description']): ?>
<p class="mb-3"><?= nl2br(e($book['description'])) ?></p>
<?php endif; ?>
<div class="d-flex justify-content-between align-items-center">
<div>
<a href="<?= SITE_URL ?>/book/<?= e($book['share_token']) ?>" class="btn btn-primary">
<i class="bi bi-book"></i> Читать
</a>
</div>
<div class="text-end">
<?php
$book_stats = $book_model->getBookStats($book['id'], true);
?>
<small class="text-muted">
Глав: <?= $book_stats['chapter_count'] ?? 0 ?> |
Слов: <?= $book_stats['total_words'] ?? 0 ?>
</small>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<footer class="text-center mt-5 pt-4 border-top">
<p class="text-muted">
Серия создана в <?= e(APP_NAME) ?> •
Автор: <a href="<?= SITE_URL ?>/author/<?= $author['id'] ?>"><?= e($author['display_name'] ?: $author['username']) ?></a>
</p>
</footer>
</div>
</article>
</div>
</div>
</div>
<style>
.series-books article {
.card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
border: 1px solid var(--border-color);
}
.series-books article:hover {
.card: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;
}
box-shadow: 0 4px 12px rgba(0,0,0,0.1) !important;
}
</style>