Start small MVC changes
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
// views/chapters/create.php
|
||||
include 'views/layouts/header.php';
|
||||
?>
|
||||
|
||||
<h1>Новая глава для: <?= e($book['title']) ?></h1>
|
||||
|
||||
<?php if (isset($error) && $error): ?>
|
||||
<div class="alert alert-error">
|
||||
<?= e($error) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" id="chapter-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
|
||||
|
||||
<div style="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="content" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
|
||||
Содержание главы *
|
||||
</label>
|
||||
|
||||
<?php if (($book['editor_type'] ?? 'markdown') == 'html'): ?>
|
||||
<textarea id="content" name="content" class="html-editor"
|
||||
placeholder="Начните писать вашу главу..."
|
||||
rows="20"
|
||||
style="width: 100%;"><?= e($_POST['content'] ?? '') ?></textarea>
|
||||
<?php else: ?>
|
||||
<div style="margin-bottom: 1rem; padding: 0.5rem; background: var(--card-background-color); border-radius: 5px;">
|
||||
<div style="display: flex; gap: 3px; flex-wrap: nowrap; overflow-x: auto; padding: 5px 0;">
|
||||
<button type="button" onclick="insertMarkdown('**')" class="compact-button secondary" title="Жирный текст" style="white-space: nowrap; flex-shrink: 0;">**B**</button>
|
||||
<button type="button" onclick="insertMarkdown('*')" class="compact-button secondary" title="Курсив" style="white-space: nowrap; flex-shrink: 0;">*I*</button>
|
||||
<button type="button" onclick="insertMarkdown('~~')" class="compact-button secondary" title="Зачеркнутый" style="white-space: nowrap; flex-shrink: 0;">~~S~~</button>
|
||||
<button type="button" onclick="insertMarkdown('`')" class="compact-button secondary" title="Код" style="white-space: nowrap; flex-shrink: 0;">`code`</button>
|
||||
<button type="button" onclick="insertMarkdown('\n\n- ')" class="compact-button secondary" title="Список" style="white-space: nowrap; flex-shrink: 0;">- список</button>
|
||||
<button type="button" onclick="insertMarkdown('\n\n> ')" class="compact-button secondary" title="Цитата" style="white-space: nowrap; flex-shrink: 0;">> цитата</button>
|
||||
<button type="button" onclick="insertMarkdown('\n\n# ')" class="compact-button secondary" title="Заголовок" style="white-space: nowrap; flex-shrink: 0;"># Заголовок</button>
|
||||
<button type="button" onclick="insertMarkdown('\n— ')" class="compact-button secondary" title="Диалог" style="white-space: nowrap; flex-shrink: 0;">— диалог</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<textarea id="content" name="content"
|
||||
placeholder="Начните писать вашу главу... Поддерживается Markdown разметка."
|
||||
rows="20"
|
||||
style="width: 100%; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 14px; line-height: 1.5;"><?= e($_POST['content'] ?? '') ?></textarea>
|
||||
|
||||
<div style="margin-top: 0.5rem; font-size: 0.9em; color: var(--muted-color);">
|
||||
<strong>Подсказка:</strong> Используйте Markdown для форматирования.
|
||||
<a href="https://commonmark.org/help/" target="_blank">Справка по Markdown</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="margin-top: 1rem;">
|
||||
<label for="status" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
|
||||
Статус главы
|
||||
</label>
|
||||
<select id="status" name="status" style="width: 100%;">
|
||||
<option value="draft" <?= (($_POST['status'] ?? 'draft') == 'draft') ? 'selected' : '' ?>>📝 Черновик</option>
|
||||
<option value="published" <?= (($_POST['status'] ?? '') == 'published') ? 'selected' : '' ?>>✅ Опубликована</option>
|
||||
</select>
|
||||
<small style="color: var(--muted-color);">
|
||||
Опубликованные главы видны в публичном доступе
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-top: 1.5rem;">
|
||||
<button type="submit" class="contrast">
|
||||
💾 Сохранить главу
|
||||
</button>
|
||||
|
||||
<button type="button" onclick="previewChapter()" class="secondary">
|
||||
👁️ Предпросмотр
|
||||
</button>
|
||||
|
||||
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/chapters" role="button" class="secondary">
|
||||
❌ Отмена
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function previewChapter() {
|
||||
const form = document.getElementById('chapter-form');
|
||||
const formData = new FormData(form);
|
||||
|
||||
// Создаем временную форму для предпросмотра
|
||||
const tempForm = document.createElement('form');
|
||||
tempForm.method = 'POST';
|
||||
tempForm.action = '<?= SITE_URL ?>/chapters/preview';
|
||||
tempForm.target = '_blank';
|
||||
tempForm.style.display = 'none';
|
||||
|
||||
// Добавляем CSRF токен
|
||||
const csrfInput = document.createElement('input');
|
||||
csrfInput.name = 'csrf_token';
|
||||
csrfInput.value = '<?= generate_csrf_token() ?>';
|
||||
tempForm.appendChild(csrfInput);
|
||||
|
||||
// Добавляем контент
|
||||
const contentInput = document.createElement('input');
|
||||
contentInput.name = 'content';
|
||||
contentInput.value = document.getElementById('content').value;
|
||||
tempForm.appendChild(contentInput);
|
||||
|
||||
// Добавляем заголовок
|
||||
const titleInput = document.createElement('input');
|
||||
titleInput.name = 'title';
|
||||
titleInput.value = document.getElementById('title').value || 'Предпросмотр главы';
|
||||
tempForm.appendChild(titleInput);
|
||||
|
||||
// Добавляем тип редактора
|
||||
const editorTypeInput = document.createElement('input');
|
||||
editorTypeInput.name = 'editor_type';
|
||||
editorTypeInput.value = '<?= $book['editor_type'] ?? 'markdown' ?>';
|
||||
tempForm.appendChild(editorTypeInput);
|
||||
|
||||
document.body.appendChild(tempForm);
|
||||
tempForm.submit();
|
||||
document.body.removeChild(tempForm);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="/assets/js/markdown-editor.js"></script>
|
||||
<script src="/assets/js/autosave.js"></script>
|
||||
<?php include 'views/layouts/footer.php'; ?>
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
// views/chapters/edit.php
|
||||
include 'views/layouts/header.php';
|
||||
?>
|
||||
|
||||
<h1>Редактирование главы: <?= e($chapter['title']) ?></h1>
|
||||
|
||||
<?php if (isset($error) && $error): ?>
|
||||
<div class="alert alert-error">
|
||||
<?= e($error) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" id="chapter-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
|
||||
|
||||
<div style="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($chapter['title']) ?>"
|
||||
placeholder="Введите название главы"
|
||||
style="width: 100%; margin-bottom: 1.5rem;"
|
||||
required>
|
||||
|
||||
<label for="content" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
|
||||
Содержание главы *
|
||||
</label>
|
||||
|
||||
<?php if (($book['editor_type'] ?? 'markdown') == 'html'): ?>
|
||||
<textarea id="content" name="content" class="html-editor"
|
||||
placeholder="Начните писать вашу главу..."
|
||||
rows="20"
|
||||
style="width: 100%;"><?= e($chapter['content']) ?></textarea>
|
||||
<?php else: ?>
|
||||
<textarea id="content" name="content"
|
||||
placeholder="Начните писать вашу главу... Поддерживается Markdown разметка."
|
||||
rows="20"
|
||||
style="width: 100%; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 14px; line-height: 1.5;"><?= e($chapter['content']) ?></textarea>
|
||||
|
||||
<div style="margin-top: 0.5rem; font-size: 0.9em; color: var(--muted-color);">
|
||||
<strong>Подсказка:</strong> Используйте Markdown для форматирования.
|
||||
<a href="https://commonmark.org/help/" target="_blank">Справка по Markdown</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="margin-top: 1rem;">
|
||||
<label for="status" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
|
||||
Статус главы
|
||||
</label>
|
||||
<select id="status" name="status" style="width: 100%;">
|
||||
<option value="draft" <?= ($chapter['status'] == 'draft') ? 'selected' : '' ?>>📝 Черновик</option>
|
||||
<option value="published" <?= ($chapter['status'] == 'published') ? 'selected' : '' ?>>✅ Опубликована</option>
|
||||
</select>
|
||||
<small style="color: var(--muted-color);">
|
||||
Опубликованные главы видны в публичном доступе
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-top: 1.5rem;">
|
||||
<button type="submit" class="contrast">
|
||||
💾 Сохранить изменения
|
||||
</button>
|
||||
|
||||
<button type="button" onclick="previewChapter()" class="secondary">
|
||||
👁️ Предпросмотр
|
||||
</button>
|
||||
|
||||
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/chapters" 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><strong>Книга:</strong> <a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/edit"><?= e($book['title']) ?></a></p>
|
||||
<p><strong>Количество слов:</strong> <?= $chapter['word_count'] ?></p>
|
||||
<p><strong>Создана:</strong> <?= date('d.m.Y H:i', strtotime($chapter['created_at'])) ?></p>
|
||||
<p><strong>Обновлена:</strong> <?= date('d.m.Y H:i', strtotime($chapter['updated_at'])) ?></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function previewChapter() {
|
||||
const form = document.getElementById('chapter-form');
|
||||
const formData = new FormData(form);
|
||||
|
||||
const tempForm = document.createElement('form');
|
||||
tempForm.method = 'POST';
|
||||
tempForm.action = '<?= SITE_URL ?>/chapters/preview';
|
||||
tempForm.target = '_blank';
|
||||
tempForm.style.display = 'none';
|
||||
|
||||
const csrfInput = document.createElement('input');
|
||||
csrfInput.name = 'csrf_token';
|
||||
csrfInput.value = '<?= generate_csrf_token() ?>';
|
||||
tempForm.appendChild(csrfInput);
|
||||
|
||||
const contentInput = document.createElement('input');
|
||||
contentInput.name = 'content';
|
||||
contentInput.value = document.getElementById('content').value;
|
||||
tempForm.appendChild(contentInput);
|
||||
|
||||
const titleInput = document.createElement('input');
|
||||
titleInput.name = 'title';
|
||||
titleInput.value = document.getElementById('title').value || 'Предпросмотр главы';
|
||||
tempForm.appendChild(titleInput);
|
||||
|
||||
const editorTypeInput = document.createElement('input');
|
||||
editorTypeInput.name = 'editor_type';
|
||||
editorTypeInput.value = '<?= $book['editor_type'] ?? 'markdown' ?>';
|
||||
tempForm.appendChild(editorTypeInput);
|
||||
|
||||
document.body.appendChild(tempForm);
|
||||
tempForm.submit();
|
||||
document.body.removeChild(tempForm);
|
||||
}
|
||||
</script>
|
||||
<script src="/assets/js/markdown-editor.js"></script>
|
||||
<script src="/assets/js/autosave.js"></script>
|
||||
<?php include 'views/layouts/footer.php'; ?>
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
// views/chapters/index.php
|
||||
include 'views/layouts/header.php';
|
||||
?>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<h1 style="margin: 0 0 0.5rem 0; font-size: 1.5rem;">Главы книги: <?= e($book['title']) ?></h1>
|
||||
<div style="display: flex; gap: 5px; flex-wrap: wrap;">
|
||||
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/chapters/create" class="adaptive-button">➕ Новая глава</a>
|
||||
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/edit" class="adaptive-button secondary">✏️ Редактировать книгу</a>
|
||||
<a href="<?= SITE_URL ?>/book/<?= $book['share_token'] ?>" class="adaptive-button secondary" target="_blank">👁️ Просмотреть книгу</a>
|
||||
<a href="<?= SITE_URL ?>/books" class="adaptive-button secondary">📚 Все книги</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (empty($chapters)): ?>
|
||||
<div style="text-align: center; padding: 2rem; background: var(--card-background-color); border-radius: 5px; margin-top: 1rem;">
|
||||
<h3>В этой книге пока нет глав</h3>
|
||||
<p>Создайте первую главу для вашей книги</p>
|
||||
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/chapters/create" class="adaptive-button">📝 Создать первую главу</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div style="overflow-x: auto; margin-top: 1rem;">
|
||||
<table class="compact-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 5%;">№</th>
|
||||
<th style="width: 40%;">Название главы</th>
|
||||
<th style="width: 15%;">Статус</th>
|
||||
<th style="width: 10%;">Слов</th>
|
||||
<th style="width: 20%;">Обновлено</th>
|
||||
<th style="width: 10%;">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($chapters as $index => $chapter): ?>
|
||||
<tr>
|
||||
<td><?= $index + 1 ?></td>
|
||||
<td>
|
||||
<strong><?= e($chapter['title']) ?></strong>
|
||||
<?php if ($chapter['description']): ?>
|
||||
<br><small style="color: var(--muted-color);"><?= e(mb_strimwidth($chapter['description'], 0, 100, '...')) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<span style="color: <?= $chapter['status'] == 'published' ? 'green' : 'orange' ?>">
|
||||
<?= $chapter['status'] == 'published' ? '✅ Опубликована' : '📝 Черновик' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= $chapter['word_count'] ?></td>
|
||||
<td>
|
||||
<small><?= date('d.m.Y H:i', strtotime($chapter['updated_at'])) ?></small>
|
||||
</td>
|
||||
<td>
|
||||
<div style="display: flex; gap: 3px; flex-wrap: wrap;">
|
||||
<a href="<?= SITE_URL ?>/chapters/<?= $chapter['id'] ?>/edit" class="compact-button secondary" title="Редактировать">
|
||||
✏️
|
||||
</a>
|
||||
<form method="post" action="<?= SITE_URL ?>/chapters/<?= $chapter['id'] ?>/delete" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите удалить эту главу? Это действие нельзя отменить.');">
|
||||
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
|
||||
<button type="submit" class="compact-button secondary" style="background: #ff4444; border-color: #ff4444; color: white;" title="Удалить">
|
||||
🗑️
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 1rem; padding: 0.5rem; background: var(--card-background-color); border-radius: 3px;">
|
||||
<strong>Статистика:</strong>
|
||||
Всего глав: <?= count($chapters) ?> |
|
||||
Всего слов: <?= array_sum(array_column($chapters, 'word_count')) ?> |
|
||||
Опубликовано: <?= count(array_filter($chapters, function($ch) { return $ch['status'] == 'published'; })) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php include 'views/layouts/footer.php'; ?>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
// views/chapters/preview.php
|
||||
include 'views/layouts/header.php';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= e($page_title) ?></title>
|
||||
<link rel="stylesheet" href="<?= SITE_URL ?>/assets/css/pico.min.css">
|
||||
<link rel="stylesheet" href="<?= SITE_URL ?>/assets/css/style.css">
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
line-height: 1.6;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
.content {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
h1 { border-bottom: 2px solid var(--primary); padding-bottom: 0.3em; }
|
||||
h2 { border-bottom: 1px solid var(--border-color); padding-bottom: 0.3em; }
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
code {
|
||||
background: var(--card-background-color);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
pre {
|
||||
background: var(--card-background-color);
|
||||
padding: 1rem;
|
||||
border-radius: 5px;
|
||||
overflow-x: auto;
|
||||
border-left: 4px solid var(--primary);
|
||||
}
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 4px solid var(--border-color);
|
||||
padding-left: 1rem;
|
||||
margin-left: 0;
|
||||
color: var(--muted-color);
|
||||
font-style: italic;
|
||||
}
|
||||
strong { font-weight: bold; }
|
||||
em { font-style: italic; }
|
||||
u { text-decoration: underline; }
|
||||
del { text-decoration: line-through; }
|
||||
.dialogue {
|
||||
margin-left: 2rem;
|
||||
font-style: italic;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
th, td {
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background: var(--card-background-color);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1><?= e($title) ?></h1>
|
||||
<hr>
|
||||
</header>
|
||||
|
||||
<main class="content">
|
||||
<?= $content ?>
|
||||
</main>
|
||||
|
||||
<footer style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border-color);">
|
||||
<small>Сгенерировано <?= date('d.m.Y H:i') ?> | Markdown Preview</small>
|
||||
<br>
|
||||
<a href="javascript:window.close()" class="button secondary">Закрыть</a>
|
||||
<a href="javascript:window.print()" class="button">Печать</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user