Загрузить файлы в «/»

This commit is contained in:
2026-04-30 08:09:46 +00:00
commit 08fe53fa5c
5 changed files with 1331 additions and 0 deletions
+735
View File
@@ -0,0 +1,735 @@
<?php
/**
* VH Posting System - Главная страница
* Управление фотографиями Flickr и публикация в соцсети
*/
session_start();
// Load configuration
$configFile = __DIR__ . '/config.php';
if (!file_exists($configFile)) {
die('Файл конфигурации не найден. Скопируйте config.example.php в config.php и настройте его.');
}
$config = require $configFile;
// Autoload classes
spl_autoload_register(function ($class) {
$file = __DIR__ . '/classes/' . $class . '.php';
if (file_exists($file)) {
require_once $file;
}
});
// Initialize Auth
$auth = new Auth();
// Handle setup if no users exist
if (!$auth->hasUsers()) {
header('Location: setup.php');
exit;
}
// Check authentication
if (!$auth->isAuthenticated()) {
header('Location: login.php');
exit;
}
// Get current user
$currentUser = $auth->getCurrentUser();
// Handle logout
if (isset($_GET['logout'])) {
$auth->logout();
header('Location: login.php');
exit;
}
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VH Posting System</title>
<link rel="icon" type="image/png" href="image.png">
<link rel="stylesheet" href="css/style.css?v=<?= filemtime(__DIR__ . '/css/style.css') ?>">
<script>
// Apply saved theme immediately to prevent flash
(function() {
const theme = localStorage.getItem('theme') || 'light';
if (theme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
}
})();
</script>
</head>
<body>
<div class="app-container">
<!-- Header -->
<header class="app-header">
<h1>VH Posting System</h1>
<div class="user-menu">
<button class="theme-toggle" id="theme-toggle" title="Переключить тему"></button>
<span class="username"><?= htmlspecialchars($currentUser) ?></span>
<a href="?logout=1" class="btn btn-small btn-secondary">Выход</a>
</div>
</header>
<!-- Main Navigation -->
<nav class="main-nav">
<button class="nav-btn" data-tab="gallery">Галерея</button>
<button class="nav-btn active" data-tab="posting">Публикация</button>
<button class="nav-btn" data-tab="converter">Конвертер</button>
<button class="nav-btn" data-tab="widget">Виджет</button>
<button class="nav-btn" data-tab="settings">Настройки</button>
</nav>
<!-- Tab: Flickr Gallery -->
<section id="tab-gallery" class="tab-content">
<div class="panel">
<!-- OAuth Status Banner -->
<div id="oauth-banner" class="oauth-banner hidden">
<div class="oauth-banner-content">
<span class="oauth-banner-icon">🔐</span>
<span class="oauth-banner-text">
<strong>Оригиналы недоступны</strong> — требуется авторизация Flickr
</span>
<a href="flickr_auth.php" class="btn btn-small btn-primary">Авторизовать</a>
</div>
</div>
<!-- Albums View (default) -->
<div id="albums-view" class="gallery-view">
<div class="gallery-header">
<h2>Альбомы Flickr</h2>
<div class="gallery-toolbar">
<button id="btn-load-albums" class="btn btn-secondary">
<span class="btn-icon-text">↻</span> Обновить
</button>
<div class="toolbar-search">
<input type="text" id="search-albums" placeholder="Поиск альбомов...">
</div>
</div>
</div>
<p class="drag-hint" id="drag-hint">💡 Перетащите альбомы для изменения порядка</p>
<div id="albums-grid" class="albums-grid">
<p class="placeholder">Загрузка альбомов...</p>
</div>
</div>
<!-- Photos View (when inside album) -->
<div id="photos-view" class="gallery-view hidden">
<div class="gallery-header">
<div class="breadcrumb">
<button id="btn-back-to-albums" class="btn btn-text">
← Назад к альбомам
</button>
<span class="breadcrumb-separator">/</span>
<span id="current-album-title" class="breadcrumb-current">Альбом</span>
</div>
<div class="gallery-toolbar">
<div class="toolbar-search">
<input type="text" id="search-photos" placeholder="Поиск фото...">
</div>
<span id="photos-count" class="photos-count"></span>
<button id="btn-download-album" class="btn btn-small" onclick="downloadAllPhotos()" title="Скачать все фото альбома">
↓ Скачать альбом
</button>
</div>
</div>
<!-- Photo Grid -->
<div id="photo-gallery" class="photo-gallery">
<p class="placeholder">Загрузка фотографий...</p>
</div>
<!-- Pagination -->
<div class="pagination">
<button id="btn-prev-page" class="btn btn-small" disabled>←</button>
<span id="page-info">1</span>
<button id="btn-next-page" class="btn btn-small" disabled>→</button>
</div>
</div>
</div>
<!-- Floating Action Bar (appears when photos selected) -->
<div id="selection-bar" class="floating-action-bar hidden">
<div class="action-bar-left">
<span id="selected-count" class="selection-count">0</span>
<span class="selection-label">выбрано</span>
</div>
<div class="action-bar-center">
<button id="btn-select-all" class="action-btn" title="Выбрать все">
<span class="action-icon">☑</span>
<span class="action-text">Все</span>
</button>
<button id="btn-deselect-all" class="action-btn" title="Снять выбор">
<span class="action-icon">☐</span>
<span class="action-text">Сброс</span>
</button>
</div>
<div class="action-bar-right">
<button id="btn-download-selected" class="action-btn" title="Скачать оригиналы">
<span class="action-icon">↓</span>
<span class="action-text">Скачать</span>
</button>
<button id="btn-convert-selected" class="action-btn action-secondary" title="Конвертировать в BBCode/HTML">
<span class="action-icon">{ }</span>
<span class="action-text">Код</span>
</button>
<button id="btn-telegram-selected" class="action-btn action-primary" title="Опубликовать в соцсети">
<span class="action-icon">↗</span>
<span class="action-text">Опубликовать</span>
</button>
</div>
</div>
</section>
<!-- Tab: Multi-Platform Posting -->
<section id="tab-posting" class="tab-content active">
<div class="panel">
<h2>Публикация в социальные сети</h2>
<p class="help-text">Выберите платформы и опубликуйте одним нажатием</p>
<!-- Photos Section -->
<div class="form-group">
<label>Фото и видео: <span id="photo-counter" class="photo-counter">0/9</span></label>
<div class="photo-source-buttons">
<button type="button" class="btn btn-secondary" id="btn-select-from-flickr">
<span class="btn-icon-text">🖼</span> Выбрать с Flickr
</button>
<input type="file" id="file-upload" multiple accept="image/*,video/*" style="display: none;">
<button type="button" class="btn btn-secondary" id="btn-upload-files">
<span class="btn-icon-text">📤</span> Загрузить
</button>
</div>
<div id="post-photos-preview" class="photos-preview combined-preview">
<p class="placeholder">Нажмите кнопку выше чтобы добавить фото</p>
</div>
</div>
<!-- Post Text -->
<div class="form-group">
<label for="post-text">Текст публикации:</label>
<div class="text-editor">
<div class="editor-toolbar">
<button type="button" class="toolbar-btn" data-format="bold" title="Жирный"><b>B</b></button>
<button type="button" class="toolbar-btn" data-format="italic" title="Курсив"><i>I</i></button>
<button type="button" class="toolbar-btn" data-format="underline" title="Подчёркнутый"><u>U</u></button>
<button type="button" class="toolbar-btn" data-format="strike" title="Зачёркнутый"><s>S</s></button>
<span class="toolbar-separator"></span>
<button type="button" class="toolbar-btn" data-format="link" title="Ссылка">🔗</button>
<button type="button" class="toolbar-btn" data-format="code" title="Код">&lt;/&gt;</button>
</div>
<textarea id="post-text" rows="4" placeholder="Введите текст для публикации..."></textarea>
</div>
</div>
<!-- Tags -->
<div class="form-group">
<label>Теги:</label>
<div class="tags-container" id="post-tags-container">
<div class="tags-list" id="post-tags-list"></div>
<div class="tags-input-wrapper">
<input type="text" id="post-tags-input" class="tags-input" placeholder="Добавить тег...">
<div class="tags-suggestions" id="post-tags-suggestions"></div>
</div>
</div>
<div class="tags-presets">
<span class="tags-presets-label">Быстрые теги:</span>
<div class="presets-list" id="post-presets-list"></div>
<button type="button" class="preset-add-btn" id="post-preset-add" title="Добавить пресет">+</button>
<button type="button" class="preset-manage-btn" id="post-preset-manage" title="Управление пресетами">⚙</button>
</div>
</div>
<!-- Platform Selection -->
<div class="form-group">
<label>Выберите платформы:</label>
<div class="platforms-grid">
<!-- Telegram -->
<div class="platform-card">
<div class="platform-header">
<label class="platform-checkbox">
<input type="checkbox" id="chk-telegram" checked>
<span class="checkmark"></span>
</label>
<div class="platform-info">
<span class="platform-name">Telegram</span>
<span id="tg-status-mini" class="status-mini">Не подключён</span>
</div>
</div>
<select id="tg-channel" class="platform-target">
<option value="">Выберите канал...</option>
</select>
</div>
<!-- VK -->
<div class="platform-card">
<div class="platform-header">
<label class="platform-checkbox">
<input type="checkbox" id="chk-vk" checked>
<span class="checkmark"></span>
</label>
<div class="platform-info">
<span class="platform-name">ВКонтакте</span>
<span id="vk-status-mini" class="status-mini">Не подключён</span>
</div>
</div>
<select id="vk-group" class="platform-target">
<option value="">Выберите группу...</option>
</select>
</div>
<!-- Instagram (info only) -->
<div class="platform-card platform-disabled">
<div class="platform-header">
<label class="platform-checkbox">
<input type="checkbox" id="chk-instagram" disabled>
<span class="checkmark"></span>
</label>
<div class="platform-info">
<span class="platform-name">Instagram</span>
<span class="status-mini">Требует настройки</span>
</div>
</div>
<p class="platform-note">Требуется Facebook Business API</p>
</div>
</div>
</div>
<!-- Post Options -->
<div class="post-options-grid">
<div class="form-group">
<label for="post-parse-mode">Формат:</label>
<select id="post-parse-mode">
<option value="HTML">HTML</option>
<option value="Markdown">Markdown</option>
<option value="">Текст</option>
</select>
</div>
<div class="form-group">
<label class="checkbox-label compact">
<input type="checkbox" id="chk-cross-promo" checked>
<span>Кросс-промо</span>
</label>
</div>
<div class="form-group schedule-toggle">
<label class="checkbox-label compact">
<input type="checkbox" id="chk-schedule">
<span>Отложить</span>
</label>
</div>
</div>
<!-- Schedule Options (hidden by default) -->
<div id="schedule-options" class="schedule-options hidden">
<label class="schedule-label">📅 Когда опубликовать:</label>
<div class="schedule-presets">
<button type="button" class="preset-btn" data-preset="1h">Через 1 час</button>
<button type="button" class="preset-btn" data-preset="3h">Через 3 часа</button>
<button type="button" class="preset-btn" data-preset="tomorrow-10">Завтра 10:00</button>
<button type="button" class="preset-btn" data-preset="tomorrow-18">Завтра 18:00</button>
</div>
<div class="schedule-custom">
<div class="schedule-date-row">
<div class="schedule-field">
<label for="schedule-date">Дата:</label>
<input type="date" id="schedule-date" class="schedule-input">
</div>
<div class="schedule-field">
<label for="schedule-time">Время:</label>
<input type="time" id="schedule-time" class="schedule-input">
</div>
</div>
<input type="hidden" id="scheduled-datetime">
</div>
</div>
<!-- Action Buttons -->
<div class="post-actions">
<button id="btn-send-post" class="btn btn-primary btn-large">
🚀 Опубликовать
</button>
</div>
<div id="post-result" class="result-message"></div>
<!-- Scheduled Posts List -->
<div class="scheduled-section">
<div class="scheduled-header">
<h3>📅 Отложенные публикации</h3>
<span id="scheduled-count" class="badge">0</span>
</div>
<div id="scheduled-posts-list" class="scheduled-posts-list">
<p class="placeholder">Нет запланированных постов</p>
</div>
</div>
<!-- Published Posts Archive -->
<div class="archive-section">
<div class="archive-header">
<h3>✓ Архив публикаций</h3>
<button type="button" class="btn btn-small btn-secondary" id="btn-refresh-archive">↻</button>
</div>
<div id="published-posts-list" class="published-posts-list">
<p class="placeholder">Загрузка...</p>
</div>
</div>
</div>
</section>
<!-- Tab: Link Converter -->
<section id="tab-converter" class="tab-content">
<div class="panel">
<h2>Конвертер ссылок Flickr</h2>
<p class="help-text">Преобразование ссылок в различные форматы для форумов и соцсетей</p>
<div class="converter-grid">
<!-- Left Column: Input -->
<div class="converter-input-section">
<div class="form-group">
<label for="input-urls">Ссылки Flickr:</label>
<textarea id="input-urls" rows="5" placeholder="https://www.flickr.com/photos/username/12345678901/
https://flic.kr/p/ABC123
https://live.staticflickr.com/65535/12345678901_abcdef1234_b.jpg"></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="image-size">Размер:</label>
<select id="image-size">
<option value="Large" selected>Большой (1024px)</option>
<option value="Large1600">1600px</option>
<option value="Large2048">2048px</option>
<option value="Original">Оригинал</option>
<option value="Medium640">640px</option>
</select>
</div>
<div class="form-group">
<label for="output-format">Формат:</label>
<select id="output-format">
<optgroup label="Кукольные форумы">
<option value="bjdclub">BJDClub.ru</option>
<option value="babiki">Babiki.ru</option>
<option value="babiki_simple">Babiki (простой)</option>
<option value="doll_forum">Универсальный</option>
</optgroup>
<optgroup label="BBCode">
<option value="bbcode">BBCode</option>
<option value="bbcode_linked">BBCode + ссылка</option>
</optgroup>
<optgroup label="HTML / Markdown">
<option value="html">HTML</option>
<option value="markdown">Markdown</option>
<option value="url_only">Только URL</option>
</optgroup>
</select>
</div>
</div>
<button id="btn-convert" class="btn btn-primary btn-block">Конвертировать</button>
</div>
<!-- Right Column: Text & Tags -->
<div class="converter-text-section">
<div class="form-group">
<label for="converter-title">Заголовок:</label>
<input type="text" id="converter-title" placeholder="Название поста...">
</div>
<div class="form-group">
<label for="converter-text">Текст к фотографиям:</label>
<div class="text-editor">
<div class="editor-toolbar">
<button type="button" class="toolbar-btn" data-format="bold" data-target="converter-text" title="Жирный"><b>B</b></button>
<button type="button" class="toolbar-btn" data-format="italic" data-target="converter-text" title="Курсив"><i>I</i></button>
<button type="button" class="toolbar-btn" data-format="underline" data-target="converter-text" title="Подчёркнутый"><u>U</u></button>
<span class="toolbar-separator"></span>
<button type="button" class="toolbar-btn" data-format="link" data-target="converter-text" title="Ссылка">🔗</button>
</div>
<textarea id="converter-text" rows="3" placeholder="Описание, комментарии к фото..."></textarea>
</div>
</div>
<div class="form-group">
<label>Теги:</label>
<div class="tags-container" id="converter-tags-container">
<div class="tags-list" id="converter-tags-list"></div>
<div class="tags-input-wrapper">
<input type="text" id="converter-tags-input" class="tags-input" placeholder="Добавить тег...">
<div class="tags-suggestions" id="converter-tags-suggestions"></div>
</div>
</div>
<div class="tags-presets">
<span class="tags-presets-label">Быстрые:</span>
<div class="presets-list" id="converter-presets-list"></div>
<button type="button" class="preset-add-btn" id="converter-preset-add" title="Добавить пресет">+</button>
<button type="button" class="preset-manage-btn" id="converter-preset-manage" title="Управление пресетами">⚙</button>
</div>
</div>
</div>
</div>
<!-- Output Section -->
<div class="converter-output-section">
<div class="form-group">
<label for="output-result">Результат:</label>
<textarea id="output-result" rows="6" readonly placeholder="Результат появится здесь после конвертации..."></textarea>
<div class="output-actions">
<button id="btn-copy" class="btn btn-secondary">📋 Скопировать</button>
<button id="btn-copy-with-tags" class="btn btn-secondary">📋 С тегами</button>
<span id="copy-status" class="copy-status"></span>
</div>
</div>
</div>
</div>
</section>
<!-- Tab: Widget Settings -->
<section id="tab-widget" class="tab-content">
<div class="panel">
<h2>Виджет для WordPress</h2>
<p class="help-text">Настройте мозаику фотографий для отображения на вашем сайте</p>
<!-- Widget Status -->
<div class="settings-section">
<h3>Статус виджета</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="widget-enabled" checked>
<span>Виджет включён</span>
</label>
</div>
<div class="form-group">
<label>API URL для WordPress:</label>
<input type="text" id="widget-api-url" readonly>
<button type="button" class="btn btn-small btn-secondary" onclick="copyWidgetUrl()">Копировать</button>
</div>
</div>
<!-- Album Selection -->
<div class="settings-section">
<h3>Выбор альбомов</h3>
<p class="help-text">Выберите альбомы для отображения в виджете. Если ничего не выбрано — показываются последние фото.</p>
<div class="form-group">
<button type="button" id="btn-load-widget-albums" class="btn btn-secondary">Загрузить альбомы</button>
</div>
<div id="widget-albums-list" class="widget-albums-grid">
<p class="placeholder">Нажмите «Загрузить альбомы» для выбора</p>
</div>
</div>
<!-- Widget Options -->
<div class="settings-section">
<h3>Параметры</h3>
<div class="form-row">
<div class="form-group">
<label for="widget-max-photos">Максимум фото:</label>
<input type="number" id="widget-max-photos" value="30" min="5" max="100">
</div>
<div class="form-group">
<label for="widget-cache-time">Кэш (секунд):</label>
<input type="number" id="widget-cache-time" value="3600" min="60" max="86400">
<span class="hint">3600 = 1 час</span>
</div>
</div>
</div>
<button id="btn-save-widget-settings" class="btn btn-primary btn-large">Сохранить настройки виджета</button>
<span id="widget-save-status" class="save-status"></span>
<!-- WordPress Installation -->
<div class="settings-section" style="margin-top: 32px;">
<h3>Установка на WordPress</h3>
<div class="code-block">
<p>1. Скачайте плагин:</p>
<a href="vh-flickr-mosaic.zip" class="btn btn-secondary" id="download-plugin-btn">Скачать плагин</a>
</div>
<div class="code-block">
<p>2. Установите плагин в WordPress (Плагины → Добавить новый → Загрузить)</p>
</div>
<div class="code-block">
<p>3. В настройках плагина укажите API URL:</p>
<code id="widget-api-url-code"></code>
</div>
</div>
</div>
</section>
<!-- Tab: Settings -->
<section id="tab-settings" class="tab-content">
<div class="panel">
<h2>Настройки</h2>
<!-- Flickr Settings -->
<div class="settings-section">
<h3>Flickr API</h3>
<div class="form-group">
<label>API ключ:</label>
<input type="text" value="<?= !empty($config['flickr']['api_key']) ? '••••••••' . substr($config['flickr']['api_key'], -4) : '' ?>" readonly>
<span class="status <?= !empty($config['flickr']['api_key']) ? 'connected' : 'disconnected' ?>">
<?= !empty($config['flickr']['api_key']) ? 'Настроен' : 'Не настроен' ?>
</span>
</div>
<div class="form-group">
<label>ID пользователя:</label>
<input type="text" value="<?= htmlspecialchars($config['flickr_user_id'] ?? '') ?>" readonly>
</div>
<div class="form-group">
<label>OAuth авторизация:</label>
<span id="flickr-oauth-status" class="status">Проверка...</span>
<a href="flickr_auth.php" id="flickr-oauth-btn" class="btn btn-small btn-primary" style="margin-left: 10px;">Авторизовать</a>
<span class="hint">Требуется для загрузки оригиналов</span>
</div>
</div>
<!-- Telegram Settings -->
<div class="settings-section">
<h3>Telegram</h3>
<div class="form-group">
<label>Статус бота:</label>
<span id="tg-bot-status" class="status">Проверка...</span>
</div>
<div class="form-group">
<label for="tg-channels-list">Каналы (по одному на строку):</label>
<textarea id="tg-channels-list" rows="3" placeholder="@channel_username
-1001234567890"><?php
$channels = $config['telegram']['channels'] ?? [];
foreach ($channels as $ch) {
echo htmlspecialchars($ch['id'] ?? $ch) . "\n";
}
?></textarea>
</div>
</div>
<!-- VK Settings -->
<div class="settings-section">
<h3>ВКонтакте</h3>
<div class="form-group">
<label>Статус:</label>
<span id="vk-status" class="status">Проверка...</span>
</div>
<div class="form-group">
<label for="vk-token-input">Access Token:</label>
<input type="password" id="vk-token-input" placeholder="Вставьте токен сюда..." value="<?= !empty($config['vk']['access_token']) ? $config['vk']['access_token'] : '' ?>">
<button id="btn-save-vk-token" class="btn btn-primary btn-small" style="margin-left: 10px;">Сохранить</button>
<button id="btn-toggle-vk-token" class="btn btn-secondary btn-small" style="margin-left: 5px;">👁</button>
</div>
<div class="form-group">
<span id="vk-token-save-status" class="save-status"></span>
</div>
<div class="form-group">
<details class="vk-help" open>
<summary>Как получить пользовательский токен (для загрузки фото)?</summary>
<ol style="margin: 10px 0; padding-left: 20px; font-size: 0.9em;">
<li>Перейдите на <a href="https://vkhost.github.io/" target="_blank">vkhost.github.io</a></li>
<li>Нажмите <strong>"VK Admin"</strong></li>
<li>Разрешите доступ приложению</li>
<li>Скопируйте <code>access_token</code> из адресной строки браузера</li>
<li>Вставьте токен в поле выше и нажмите "Сохранить"</li>
</ol>
<p style="font-size: 0.85em; color: var(--text-secondary); margin-top: 10px;">
<strong>Важно:</strong> Пользовательский токен позволяет загружать фото напрямую в VK.
Community-токен (ключ сообщества) может только постить текст и ссылки.
</p>
</details>
</div>
</div>
<!-- Cross-Promo Settings -->
<div class="settings-section">
<h3>Кросс-промо каналов</h3>
<p class="help-text">Ссылки на ваши каналы для автоматического добавления в посты</p>
<div class="form-group">
<label for="cross-promo-telegram">Ссылка на Telegram канал:</label>
<input type="text" id="cross-promo-telegram" placeholder="https://t.me/your_channel">
<span class="hint">Будет добавлена в посты VK</span>
</div>
<div class="form-group">
<label for="cross-promo-vk">Ссылка на ВКонтакте:</label>
<input type="text" id="cross-promo-vk" placeholder="https://vk.com/your_group">
<span class="hint">Будет добавлена в посты Telegram</span>
</div>
<div class="form-group">
<label for="cross-promo-text-tg">Текст для Telegram:</label>
<input type="text" id="cross-promo-text-tg" placeholder="Мой канал ВКонтакте" value="Мой канал ВКонтакте">
</div>
<div class="form-group">
<label for="cross-promo-text-vk">Текст для ВКонтакте:</label>
<input type="text" id="cross-promo-text-vk" placeholder="Мой канал в Telegram" value="Мой канал в Telegram">
</div>
<button id="btn-save-cross-promo" class="btn btn-primary">Сохранить настройки кросс-промо</button>
<span id="cross-promo-save-status" class="save-status"></span>
</div>
<!-- Theme -->
<div class="settings-section">
<h3>Оформление</h3>
<div class="form-group">
<label>Тема интерфейса:</label>
<button id="btn-toggle-theme" class="btn btn-secondary">Переключить тему</button>
</div>
</div>
<!-- Password Change -->
<div class="settings-section">
<h3>Смена пароля</h3>
<div class="form-group">
<label for="current-password">Текущий пароль:</label>
<input type="password" id="current-password">
</div>
<div class="form-group">
<label for="new-password">Новый пароль:</label>
<input type="password" id="new-password">
<span class="hint">Минимум 8 символов</span>
</div>
<div class="form-group">
<label for="confirm-password">Подтвердите пароль:</label>
<input type="password" id="confirm-password">
</div>
<button id="btn-change-password" class="btn btn-primary">Сменить пароль</button>
</div>
</div>
</section>
</div>
<!-- Preset Management Modal -->
<div id="preset-modal" class="modal-overlay" style="display: none;">
<div class="modal-content preset-modal">
<div class="modal-header">
<h3 id="preset-modal-title">Управление пресетами</h3>
<button type="button" class="modal-close" id="preset-modal-close">&times;</button>
</div>
<div class="modal-body">
<!-- Add/Edit Form -->
<div id="preset-form-section" style="display: none;">
<div class="form-group">
<label for="preset-name">Название пресета:</label>
<input type="text" id="preset-name" class="form-control" placeholder="Например: BJD">
</div>
<div class="form-group">
<label for="preset-tags">Теги (через запятую):</label>
<input type="text" id="preset-tags" class="form-control" placeholder="bjd, doll, куклы">
</div>
<div class="preset-form-actions">
<button type="button" id="preset-save" class="btn btn-primary">Сохранить</button>
<button type="button" id="preset-cancel" class="btn btn-secondary">Отмена</button>
</div>
</div>
<!-- Presets List -->
<div id="preset-list-section">
<div class="preset-manager-list" id="preset-manager-list"></div>
<button type="button" id="preset-add-new" class="btn btn-primary btn-block">+ Добавить пресет</button>
</div>
</div>
</div>
</div>
<script src="js/app.js?v=<?= filemtime(__DIR__ . '/js/app.js') ?>"></script>
</body>
</html>