| Current Path : /home/seto/888mirror.pm/admin/ |
| Current File : /home/seto/888mirror.pm/admin/landing_edit.php |
<?php
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/helpers.php';
auth_check();
$id = (int)($_GET['id'] ?? 0);
$clone = (int)($_GET['clone'] ?? 0);
$errors = [];
$lnd = null;
$templates = [
'dark-premium' => 'Dark Premium (карточки)',
'light-minimal' => 'Light Minimal (карточки)',
'neon' => 'Neon Contrast (карточки)',
'classic' => 'Classic Casino (карточки)',
'table-dark' => 'Table Dark (таблица)',
'table-light' => 'Table Light (таблица)',
'row-dark' => 'Row Dark (горизонтальные)',
'row-light' => 'Row Light (горизонтальные)',
];
// Загрузка или клонирование
$source_id = $id ?: $clone;
if ($source_id) {
$stmt = db()->prepare("SELECT * FROM landings WHERE id=?");
$stmt->execute([$source_id]);
$lnd = $stmt->fetch();
if (!$lnd) { header('Location: /admin/landings.php'); exit(); }
if ($clone && !$id) {
$lnd['name'] = $lnd['name'] . ' (copy)';
$lnd['slug'] = $lnd['slug'] . '-copy';
$lnd['status'] = 'draft';
$id = 0;
}
}
// Активные языки для AI-модала
$active_langs = db()->query("SELECT code, name FROM languages WHERE is_active=1 ORDER BY code")->fetchAll();
// Список стран для GEO
$countries_raw = db()->query("
SELECT DISTINCT bc.country_code FROM brand_countries bc
JOIN brands b ON b.id=bc.brand_id
WHERE b.status='active'
ORDER BY bc.country_code
")->fetchAll(PDO::FETCH_COLUMN);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name'] ?? '');
$slug = trim($_POST['slug'] ?? '');
$status = $_POST['status'] ?? 'draft';
$template = $_POST['template'] ?? 'dark-premium';
$geo = trim($_POST['geo'] ?? '');
$brand_lim = (int)($_POST['brand_limit'] ?? 0);
$h1 = trim($_POST['h1'] ?? '');
$subtitle = trim($_POST['subtitle'] ?? '');
$cta_text = trim($_POST['cta_text'] ?? 'Get Bonus →');
$body_text = trim($_POST['body_text'] ?? '');
$footer_txt = trim($_POST['footer_text']?? '');
$meta_title = trim($_POST['meta_title'] ?? '');
$meta_desc = trim($_POST['meta_description'] ?? '');
if (!$name) $errors[] = 'Название обязательно.';
if (!in_array($status, ['active','inactive','draft','archived'])) $status = 'draft';
if (!array_key_exists($template, $templates)) $template = 'dark-premium';
if (!$slug) $slug = slugify($name);
$slug = preg_replace('/[^a-z0-9-]/', '', strtolower($slug));
// Уникальность slug
if (!$errors) {
$chk = db()->prepare("SELECT id FROM landings WHERE slug=? AND id!=?");
$chk->execute([$slug, $id]);
if ($chk->fetchColumn()) $errors[] = 'Такой slug уже существует.';
}
if (!$errors) {
$db = db();
if ($id) {
$db->prepare("UPDATE landings SET name=?,slug=?,status=?,template=?,geo=?,brand_limit=?,
h1=?,subtitle=?,cta_text=?,body_text=?,footer_text=?,meta_title=?,meta_description=?,
updated_at=datetime('now') WHERE id=?")
->execute([$name,$slug,$status,$template,$geo,$brand_lim,$h1,$subtitle,$cta_text,$body_text,$footer_txt,$meta_title,$meta_desc,$id]);
} else {
$db->prepare("INSERT INTO landings (name,slug,status,template,geo,brand_limit,h1,subtitle,cta_text,body_text,footer_text,meta_title,meta_description)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")
->execute([$name,$slug,$status,$template,$geo,$brand_lim,$h1,$subtitle,$cta_text,$body_text,$footer_txt,$meta_title,$meta_desc]);
$id = $db->lastInsertId();
}
header('Location: /admin/landings.php?msg=' . urlencode('Лендинг сохранён'));
exit();
}
$lnd = compact('name','slug','status','template','geo','brand_lim','h1','subtitle','cta_text','body_text','footer_txt','meta_title','meta_desc');
$lnd['brand_limit'] = $brand_lim;
$lnd['footer_text'] = $footer_txt;
$lnd['meta_description'] = $meta_desc;
}
$title = $id ? 'Редактировать лендинг' : ($clone ? 'Дублировать лендинг' : 'Новый лендинг');
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><?= h($title) ?> — Admin</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
</head>
<body>
<?= admin_nav('landings') ?>
<div class="container" style="max-width:800px">
<div class="d-flex align-items-center gap-2 mb-4">
<a href="/admin/landings.php" class="btn btn-outline-secondary btn-sm">← Назад</a>
<h5 class="mb-0"><?= h($title) ?></h5>
<?php if ($id): ?>
<a href="/<?= h($lnd['slug'] ?? '') ?>?preview=1" target="_blank" class="btn btn-outline-secondary btn-sm ms-auto"><i class="bi bi-eye me-1"></i>Превью</a>
<?php endif ?>
</div>
<?php foreach ($errors as $e): ?>
<div class="alert alert-danger py-2"><?= h($e) ?></div>
<?php endforeach ?>
<form method="post">
<!-- Основное -->
<div class="card shadow-sm mb-3">
<div class="card-header fw-semibold">Основное</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-7">
<label class="form-label">Название *</label>
<input type="text" name="name" class="form-control" required
value="<?= h($lnd['name'] ?? '') ?>"
oninput="autoSlug(this.value)">
</div>
<div class="col-md-5">
<label class="form-label">Slug (URL: /slug)</label>
<input type="text" name="slug" id="slug" class="form-control"
value="<?= h($lnd['slug'] ?? '') ?>" placeholder="авто из названия">
<div class="form-text">888mirror.pm/<strong id="slug-preview"><?= h($lnd['slug'] ?? '...') ?></strong></div>
</div>
<div class="col-md-4">
<label class="form-label">Статус</label>
<select name="status" class="form-select">
<?php foreach (['active'=>'Активен','inactive'=>'Неактивен','draft'=>'Черновик','archived'=>'Архив'] as $val=>$lbl): ?>
<option value="<?= $val ?>" <?= ($lnd['status']??'draft')===$val?'selected':'' ?>><?= $lbl ?></option>
<?php endforeach ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Шаблон</label>
<select name="template" class="form-select">
<?php foreach ($templates as $val => $lbl): ?>
<option value="<?= $val ?>" <?= ($lnd['template']??'dark-premium')===$val?'selected':'' ?>><?= $lbl ?></option>
<?php endforeach ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label">GEO фильтр брендов</label>
<select name="geo" class="form-select">
<option value="">— все бренды —</option>
<option value="crypto" <?= ($lnd['geo']??'')==='crypto'?'selected':'' ?>>💎 Crypto</option>
<option value="en" <?= ($lnd['geo']??'')==='en'?'selected':'' ?>>🌐 Global</option>
<optgroup label="Страны">
<?php foreach ($countries_raw as $cc): if (strlen($cc)!==2) continue; ?>
<option value="<?= h($cc) ?>" <?= ($lnd['geo']??'')===$cc?'selected':'' ?>><?= h(strtoupper($cc)) ?></option>
<?php endforeach ?>
</optgroup>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Макс. брендов (0 = все)</label>
<input type="number" name="brand_limit" class="form-control" min="0"
value="<?= (int)($lnd['brand_limit'] ?? 0) ?>">
</div>
</div>
</div>
</div>
<!-- Контент -->
<div class="card shadow-sm mb-3">
<div class="card-header fw-semibold d-flex justify-content-between align-items-center">
Контент страницы
<?php if ($id): ?>
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#aiModal">
<i class="bi bi-stars me-1"></i>AI Content
</button>
<?php endif ?>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label">H1 заголовок</label>
<input type="text" name="h1" class="form-control"
value="<?= h($lnd['h1'] ?? '') ?>" placeholder="Best Casino Bonuses">
</div>
<div class="col-12">
<label class="form-label">Подзаголовок</label>
<input type="text" name="subtitle" class="form-control"
value="<?= h($lnd['subtitle'] ?? '') ?>" placeholder="Exclusive offers for...">
</div>
<div class="col-md-6">
<label class="form-label">Текст CTA кнопки</label>
<input type="text" name="cta_text" class="form-control"
value="<?= h($lnd['cta_text'] ?? 'Get Bonus →') ?>">
</div>
<div class="col-md-6">
<label class="form-label">Footer текст</label>
<input type="text" name="footer_text" class="form-control"
value="<?= h($lnd['footer_text'] ?? '') ?>" placeholder="© 2025 ...">
</div>
<div class="col-12">
<label class="form-label">Текст под таблицей брендов <small class="text-muted">(plain text, каждый абзац — отдельная строка)</small></label>
<textarea name="body_text" class="form-control" rows="4"
placeholder="Дополнительный текст для SEO..."><?= h($lnd['body_text'] ?? '') ?></textarea>
</div>
</div>
</div>
</div>
<!-- SEO -->
<div class="card shadow-sm mb-4">
<div class="card-header fw-semibold">SEO</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label">Meta title <small class="text-muted">(если пусто — используется H1)</small></label>
<input type="text" name="meta_title" class="form-control"
value="<?= h($lnd['meta_title'] ?? '') ?>" placeholder="Best Casino Bonuses 2025">
</div>
<div class="col-12">
<label class="form-label">Meta description</label>
<textarea name="meta_description" class="form-control" rows="2"
placeholder="Find the best casino bonuses..."><?= h($lnd['meta_description'] ?? '') ?></textarea>
</div>
</div>
</div>
</div>
<div class="d-flex gap-2 mb-5">
<button type="submit" class="btn btn-primary">Сохранить</button>
<a href="/admin/landings.php" class="btn btn-secondary">Отмена</a>
<?php if ($id): ?>
<a href="/admin/landing_edit.php?clone=<?= $id ?>" class="btn btn-outline-success ms-auto"><i class="bi bi-copy me-1"></i>Дублировать</a>
<?php endif ?>
</div>
</form>
</div>
<?php if ($id): ?>
<!-- AI Content Modal -->
<div class="modal fade" id="aiModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-stars me-2"></i>AI Content Generation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label fw-semibold">Language</label>
<select id="aiLang" class="form-select">
<option value="__all__">All active languages (<?= count($active_langs) ?>)</option>
<?php foreach ($active_langs as $l): ?>
<option value="<?= h($l['code']) ?>"><?= h(strtoupper($l['code'])) ?> — <?= h($l['name']) ?></option>
<?php endforeach ?>
</select>
</div>
<div class="col-md-6 d-flex align-items-end">
<button type="button" id="aiGenerateBtn" class="btn btn-primary w-100">
<i class="bi bi-stars me-1"></i>Generate
</button>
</div>
</div>
<div id="aiStatus" class="mb-3" style="display:none"></div>
<div id="aiFields">
<div class="row g-2">
<div class="col-12">
<label class="form-label form-label-sm">H1</label>
<input type="text" id="aiH1" class="form-control form-control-sm">
</div>
<div class="col-12">
<label class="form-label form-label-sm">Subtitle</label>
<input type="text" id="aiSubtitle" class="form-control form-control-sm">
</div>
<div class="col-md-6">
<label class="form-label form-label-sm">CTA text</label>
<input type="text" id="aiCta" class="form-control form-control-sm">
</div>
<div class="col-md-6">
<label class="form-label form-label-sm">Footer text</label>
<input type="text" id="aiFooter" class="form-control form-control-sm">
</div>
<div class="col-12">
<label class="form-label form-label-sm">Body text <small class="text-muted">(paragraphs separated by newlines)</small></label>
<textarea id="aiBody" class="form-control form-control-sm" rows="5"></textarea>
</div>
<div class="col-12">
<label class="form-label form-label-sm">Meta title</label>
<input type="text" id="aiMetaTitle" class="form-control form-control-sm">
</div>
<div class="col-12">
<label class="form-label form-label-sm">Meta description</label>
<textarea id="aiMetaDesc" class="form-control form-control-sm" rows="2"></textarea>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" id="aiSaveBtn" class="btn btn-success" disabled>
<i class="bi bi-floppy me-1"></i>Save translation
</button>
</div>
</div>
</div>
</div>
<?php endif ?>
<script>
function autoSlug(val) {
var slug = document.getElementById('slug');
if (slug.dataset.manual) return;
var map = {'а':'a','б':'b','в':'v','г':'g','д':'d','е':'e','ё':'yo','ж':'zh','з':'z','и':'i','й':'j','к':'k','л':'l','м':'m','н':'n','о':'o','п':'p','р':'r','с':'s','т':'t','у':'u','ф':'f','х':'h','ц':'ts','ч':'ch','ш':'sh','щ':'sch','ъ':'','ы':'y','ь':'','э':'e','ю':'yu','я':'ya'};
var s = val.toLowerCase().split('').map(c => map[c]!==undefined?map[c]:c).join('');
slug.value = s.replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'');
document.getElementById('slug-preview').textContent = slug.value || '...';
}
document.getElementById('slug').addEventListener('input', function() {
this.dataset.manual = '1';
document.getElementById('slug-preview').textContent = this.value || '...';
});
</script>
<?php if ($id): ?>
<script>
(function() {
var landingId = <?= (int)$id ?>;
var langSel = document.getElementById('aiLang');
var genBtn = document.getElementById('aiGenerateBtn');
var saveBtn = document.getElementById('aiSaveBtn');
var statusEl = document.getElementById('aiStatus');
var fieldsEl = document.getElementById('aiFields');
function setStatus(html, type) {
statusEl.innerHTML = '<div class="alert alert-'+type+' py-2 mb-0">'+html+'</div>';
statusEl.style.display = '';
}
function clearStatus() { statusEl.style.display = 'none'; }
function fillFields(t) {
document.getElementById('aiH1').value = t.h1 || '';
document.getElementById('aiSubtitle').value = t.subtitle || '';
document.getElementById('aiCta').value = t.cta_text || '';
document.getElementById('aiFooter').value = t.footer_text || '';
document.getElementById('aiBody').value = t.body_text || '';
document.getElementById('aiMetaTitle').value = t.meta_title || '';
document.getElementById('aiMetaDesc').value = t.meta_description || '';
saveBtn.disabled = false;
}
function clearFields() {
['aiH1','aiSubtitle','aiCta','aiFooter','aiBody','aiMetaTitle','aiMetaDesc']
.forEach(function(id){ document.getElementById(id).value = ''; });
saveBtn.disabled = true;
}
function isAllMode() { return langSel.value === '__all__'; }
function setAllMode(yes) {
fieldsEl.style.display = yes ? 'none' : '';
saveBtn.style.display = yes ? 'none' : '';
}
langSel.addEventListener('change', function() {
clearStatus();
if (isAllMode()) {
setAllMode(true);
clearFields();
return;
}
setAllMode(false);
clearFields();
// Load existing translation
var fd = new FormData();
fd.append('action', 'load');
fd.append('landing_id', landingId);
fd.append('lang', langSel.value);
fetch('/admin/landing_generate.php', {method:'POST', body:fd})
.then(function(r){ return r.json(); })
.then(function(d){
if (d.success && d.translation) {
fillFields(d.translation);
setStatus('Loaded existing translation.', 'secondary');
}
});
});
genBtn.addEventListener('click', function() {
var langs = isAllMode()
? <?= json_encode(array_column($active_langs, 'code')) ?>
: [langSel.value];
genBtn.disabled = true;
genBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Generating...';
clearStatus();
var fd = new FormData();
fd.append('action', 'generate');
fd.append('landing_id', landingId);
fd.append('langs', langs.join(','));
fetch('/admin/landing_generate.php', {method:'POST', body:fd})
.then(function(r){ return r.json(); })
.then(function(d){
genBtn.disabled = false;
genBtn.innerHTML = '<i class="bi bi-stars me-1"></i>Generate';
if (d.error) {
setStatus(d.error, 'danger');
return;
}
if (isAllMode()) {
var count = Object.keys(d.data || {}).length;
setStatus('Generated and saved for <strong>'+count+' languages</strong>.', 'success');
} else {
var t = d.data[langSel.value];
if (t) { fillFields(t); }
setStatus('Generated successfully.', 'success');
}
})
.catch(function(e){
genBtn.disabled = false;
genBtn.innerHTML = '<i class="bi bi-stars me-1"></i>Generate';
setStatus('Request failed: '+e.message, 'danger');
});
});
saveBtn.addEventListener('click', function() {
if (isAllMode()) return;
saveBtn.disabled = true;
var fd = new FormData();
fd.append('action', 'save');
fd.append('landing_id', landingId);
fd.append('lang', langSel.value);
fd.append('h1', document.getElementById('aiH1').value);
fd.append('subtitle', document.getElementById('aiSubtitle').value);
fd.append('cta_text', document.getElementById('aiCta').value);
fd.append('footer_text', document.getElementById('aiFooter').value);
fd.append('body_text', document.getElementById('aiBody').value);
fd.append('meta_title', document.getElementById('aiMetaTitle').value);
fd.append('meta_description', document.getElementById('aiMetaDesc').value);
fetch('/admin/landing_generate.php', {method:'POST', body:fd})
.then(function(r){ return r.json(); })
.then(function(d){
saveBtn.disabled = false;
if (d.error) { setStatus(d.error, 'danger'); }
else { setStatus('Translation saved for <strong>'+langSel.options[langSel.selectedIndex].text+'</strong>.', 'success'); }
});
});
// Init on modal open
document.getElementById('aiModal').addEventListener('show.bs.modal', function() {
clearStatus();
setAllMode(isAllMode());
clearFields();
});
})();
</script>
<?php endif ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>