| Current Path : /home/seto/888mirror.pm/admin/ |
| Current File : /home/seto/888mirror.pm/admin/translations.php |
<?php
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/helpers.php';
auth_check();
$brand_id = (int)($_GET['brand_id'] ?? 0);
if (!$brand_id) { header('Location: /admin/brands.php'); exit(); }
$brand = db()->prepare("SELECT * FROM brands WHERE id = ?");
$brand->execute([$brand_id]);
$brand = $brand->fetch();
if (!$brand) { header('Location: /admin/brands.php'); exit(); }
$msg = '';
// Сохранение вручную
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_translations'])) {
$langs = $_POST['lang'] ?? [];
$db = db();
$check = $db->prepare("SELECT id FROM brand_translations WHERE brand_id=? AND language_code=?");
$insert = $db->prepare("
INSERT INTO brand_translations (brand_id, language_code, bonus, free_spins, games_count,
advantages, license, payment_methods, payment_methods_count, min_withdraw, max_withdraw, currencies)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
");
$update = $db->prepare("
UPDATE brand_translations SET bonus=?, free_spins=?, games_count=?, advantages=?,
license=?, payment_methods=?, payment_methods_count=?, min_withdraw=?, max_withdraw=?, currencies=?
WHERE brand_id=? AND language_code=?
");
foreach ($langs as $lang_code => $values) {
$bonus = $values['bonus'] ?? '';
$spins = $values['free_spins'] ?? '';
$games = $values['games_count'] ?? '';
$adv = $values['advantages'] ?? '';
$lic = $values['license'] ?? '';
$pm = $values['payment_methods'] ?? '';
$pmc = $values['payment_methods_count'] ?? '';
$minw = $values['min_withdraw'] ?? '';
$maxw = $values['max_withdraw'] ?? '';
$curr = $values['currencies'] ?? '';
$check->execute([$brand_id, $lang_code]);
if ($check->fetchColumn()) {
$update->execute([$bonus,$spins,$games,$adv,$lic,$pm,$pmc,$minw,$maxw,$curr,$brand_id,$lang_code]);
} else {
$insert->execute([$brand_id,$lang_code,$bonus,$spins,$games,$adv,$lic,$pm,$pmc,$minw,$maxw,$curr]);
}
}
$msg = 'Переводы сохранены.';
}
// Загрузить языки, отсортированные: EN → RU → по показам за 30 дней → алфавит
$since = date('Y-m-d H:i:s', strtotime('-30 days'));
$lang_stats = db()->prepare("
SELECT language, COUNT(*) as cnt
FROM stats
WHERE event_type = 'impression' AND created_at >= ?
GROUP BY language
");
$lang_stats->execute([$since]);
$stats_by_lang = array_column($lang_stats->fetchAll(), 'cnt', 'language');
$all_langs = db()->query("SELECT * FROM languages WHERE is_active = 1 ORDER BY code")->fetchAll();
$group_colors = [
'Core Markets' => 'primary',
'Nordics' => 'info',
'Balkans & SEE' => 'warning',
'Baltics' => 'success',
'Caucasus & CIS'=> 'secondary',
'Asia Growth' => 'danger',
];
usort($all_langs, function($a, $b) use ($stats_by_lang) {
$priority = ['en' => 0, 'ru' => 1];
$pa = $priority[$a['code']] ?? 2;
$pb = $priority[$b['code']] ?? 2;
if ($pa !== $pb) return $pa - $pb;
$ca = $stats_by_lang[$a['code']] ?? 0;
$cb = $stats_by_lang[$b['code']] ?? 0;
if ($ca !== $cb) return $cb - $ca;
return strcmp($a['code'], $b['code']);
});
$languages = $all_langs;
$trans_raw = db()->prepare("SELECT * FROM brand_translations WHERE brand_id = ?");
$trans_raw->execute([$brand_id]);
$translations = [];
foreach ($trans_raw->fetchAll() as $t) {
$translations[$t['language_code']] = $t;
}
$openai_key = setting('openai_key');
$fields_labels = [
'bonus' => 'Бонус',
'free_spins' => 'Фриспины',
'games_count' => 'Игр',
'advantages' => 'Преимущества (JSON или текст)',
'license' => 'Лицензия',
'payment_methods' => 'Способы оплаты (JSON или текст)',
'payment_methods_count' => 'Кол-во способов',
'min_withdraw' => 'Мин. вывод',
'max_withdraw' => 'Макс. вывод',
'currencies' => 'Валюты',
];
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Переводы — <?= h($brand['name']) ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.trans-table td, .trans-table th { vertical-align: middle; }
.trans-table textarea { font-size: 12px; min-height: 36px; resize: vertical; }
.lang-header { position: sticky; top: 0; background: #fff; z-index: 10; }
</style>
</head>
<body>
<?= admin_nav('brands') ?>
<div class="container-fluid px-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h5 class="mb-0">Переводы: <strong><?= h($brand['name']) ?></strong></h5>
<small class="text-muted">Slug: <?= h($brand['slug']) ?></small>
</div>
<div class="d-flex gap-2">
<?php if ($openai_key): ?>
<button class="btn btn-outline-info btn-sm" onclick="researchBrand()">
🔍 Исследовать бренд
</button>
<div class="dropdown">
<button class="btn btn-success btn-sm dropdown-toggle" data-bs-toggle="dropdown">
🤖 Сгенерировать через AI
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="generateAll(); return false">Все языки (чанками по 6)</a></li>
<li><hr class="dropdown-divider"></li>
<?php foreach ($languages as $lang): ?>
<li>
<a class="dropdown-item" href="#"
onclick="generateOne('<?= h($lang['code']) ?>','<?= h($lang['name']) ?>'); return false">
<?= h($lang['name']) ?> (<?= h($lang['code']) ?>)
</a>
</li>
<?php endforeach ?>
</ul>
</div>
<?php else: ?>
<a href="/admin/settings.php" class="btn btn-outline-secondary btn-sm">⚠ Добавьте OpenAI ключ</a>
<?php endif ?>
<a href="/admin/brands.php" class="btn btn-secondary btn-sm">← Назад</a>
</div>
</div>
<?php if ($msg): ?>
<div class="alert alert-success py-2"><?= h($msg) ?></div>
<?php endif ?>
<!-- Панель исследования бренда -->
<div id="research-panel" class="card mb-3 d-none border-info">
<div class="card-header bg-info bg-opacity-10 d-flex justify-content-between align-items-center py-2">
<span class="fw-semibold">🔍 Найденные факты о бренде</span>
<div class="d-flex gap-2">
<button class="btn btn-success btn-sm" onclick="generateAll()">
🤖 Использовать факты + Генерировать все языки
</button>
<button class="btn btn-outline-secondary btn-sm" onclick="document.getElementById('research-panel').classList.add('d-none')">✕</button>
</div>
</div>
<div class="card-body pb-2">
<div class="text-muted small mb-1">Проверьте и при необходимости отредактируйте перед генерацией:</div>
<textarea id="research-facts" class="form-control form-control-sm font-monospace" rows="10"></textarea>
</div>
</div>
<!-- AI прогресс -->
<div id="ai-progress" class="alert alert-info d-none">
<div class="d-flex align-items-center gap-2">
<div class="spinner-border spinner-border-sm"></div>
<span id="ai-progress-text">Генерация...</span>
</div>
</div>
<div id="ai-error" class="alert alert-danger d-none"></div>
<form method="post">
<div class="table-responsive">
<table class="table table-bordered table-sm trans-table">
<thead class="table-light lang-header">
<tr>
<th style="min-width:120px">Поле</th>
<?php foreach ($languages as $lang): ?>
<th style="min-width:140px">
<?= h($lang['name']) ?>
<br><small class="text-muted"><?= h($lang['code']) ?></small>
<br><span class="badge bg-<?= $group_colors[$lang['group_name'] ?? 'Core Markets'] ?? 'secondary' ?>" style="font-size:9px"><?= h($lang['group_name'] ?? '') ?></span>
<?php if (!empty($translations[$lang['code']]['generated_at'])): ?>
<br><small class="text-success" title="Дата генерации">
🤖 <?= substr($translations[$lang['code']]['generated_at'],0,10) ?>
</small>
<?php endif ?>
</th>
<?php endforeach ?>
</tr>
</thead>
<tbody>
<?php foreach ($fields_labels as $field => $label): ?>
<tr>
<td><strong><?= h($label) ?></strong></td>
<?php foreach ($languages as $lang): ?>
<td>
<textarea name="lang[<?= h($lang['code']) ?>][<?= h($field) ?>]"
class="form-control form-control-sm"
id="f_<?= h($lang['code']) ?>_<?= h($field) ?>"
rows="2"><?= h($translations[$lang['code']][$field] ?? '') ?></textarea>
</td>
<?php endforeach ?>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>
<div class="mt-3 mb-5">
<button type="submit" name="save_translations" class="btn btn-primary">Сохранить переводы</button>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
const brandId = <?= $brand_id ?>;
const allLangs = <?= json_encode(array_column($languages, 'code')) ?>;
const fieldKeys = <?= json_encode(array_keys($fields_labels)) ?>;
function showProgress(text) {
document.getElementById('ai-progress').classList.remove('d-none');
document.getElementById('ai-progress-text').textContent = text;
document.getElementById('ai-error').classList.add('d-none');
}
function hideProgress() {
document.getElementById('ai-progress').classList.add('d-none');
}
function showError(msg) {
hideProgress();
var el = document.getElementById('ai-error');
el.textContent = msg;
el.classList.remove('d-none');
}
function fillFields(data) {
for (var lang in data) {
var vals = data[lang];
for (var field in vals) {
var el = document.getElementById('f_' + lang + '_' + field);
if (el) {
var val = vals[field];
if (Array.isArray(val)) val = JSON.stringify(val, null, 2);
el.value = val || '';
}
}
}
}
async function researchBrand() {
showProgress('🔍 Поиск информации о бренде в интернете...');
try {
const r = await fetch('/admin/generate.php', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'brand_id=' + brandId + '&action=research'
}).then(r => r.json());
hideProgress();
if (r.error) { showError(r.error); return; }
document.getElementById('research-facts').value = r.facts;
document.getElementById('research-panel').classList.remove('d-none');
document.getElementById('research-panel').scrollIntoView({behavior:'smooth'});
} catch(e) { showError('Ошибка: ' + e.message); }
}
function getContext() {
var panel = document.getElementById('research-panel');
if (panel && !panel.classList.contains('d-none')) {
return document.getElementById('research-facts').value.trim();
}
return '';
}
async function generateAll() {
const context = getContext();
const chunkSize = 6;
const total = allLangs.length;
for (let i = 0; i < total; i += chunkSize) {
const chunk = allLangs.slice(i, i + chunkSize);
const from = i + 1;
const to = Math.min(i + chunkSize, total);
showProgress('🤖 Генерация языков ' + from + '–' + to + ' из ' + total + '...');
try {
const body = 'brand_id=' + brandId + '&langs=' + chunk.join(',')
+ (context ? '&context=' + encodeURIComponent(context) : '');
const r = await fetch('/admin/generate.php', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: body
}).then(r => r.json());
if (r.error) { showError(r.error); return; }
fillFields(r.data);
} catch(e) { showError('Ошибка: ' + e.message); return; }
}
hideProgress();
document.querySelector('button[name=save_translations]').click();
}
async function generateOne(lang, name) {
const context = getContext();
showProgress('🤖 Генерация: ' + name + '...');
try {
const body = 'brand_id=' + brandId + '&langs=' + lang
+ (context ? '&context=' + encodeURIComponent(context) : '');
const r = await fetch('/admin/generate.php', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: body
}).then(r => r.json());
hideProgress();
if (r.error) { showError(r.error); return; }
fillFields(r.data);
document.querySelector('button[name=save_translations]').click();
} catch(e) { showError('Ошибка: ' + e.message); }
}
</script>
</body>
</html>