Commit c0f05e75 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(settings): edit reward_config, the table the game pays from

The settings page edited system_config, which nothing in the game reads.
It now edits reward_config: coins and XP per game and place (read by
complete_match() and domino-match), the seven-day daily reward ladder and
the chess Elo K-factor steps (El3ab includes/game-config.php). All 47 keys
have an Arabic label and group and say whether the game reads them today;
chess draw/loss and backgammon loss rows are marked unpaid because those
games report only the winner to complete_match(). Values are validated
whole numbers stored as plain text (complete_match casts value::int); only
changed rows are written and the change is audited.

system_config is shown read-only for reference, with where each value
really comes from. Its update no longer json_encode()s the value before
PostgREST encodes it again (the cause of elo_k_factor_master = "\"20\"",
repaired in the migration).
Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
parent 10af61a4
......@@ -186,6 +186,7 @@ return [
// Settings
'settings' => ['module' => 'settings', 'action' => 'index'],
'settings/update' => ['module' => 'settings', 'action' => 'update'],
'settings/rewards' => ['module' => 'settings', 'action' => 'updateRewards'],
// Branding
'branding' => ['module' => 'branding', 'action' => 'index'],
......
This diff is collapsed.
<div class="content-header">
<h1>إعدادات النظام</h1>
<h1>الإعدادات</h1>
</div>
<?php if (empty($grouped)): ?>
<div class="card"><div class="empty-state"><h3 class="empty-state-title">لا توجد إعدادات</h3><p class="empty-state-text">لم يتم تكوين أي إعدادات بعد</p></div></div>
<?php else: ?>
<div class="tabs mb-5">
<a href="/settings" class="tab <?= $tab === 'rewards' ? 'active' : '' ?>">المكافآت والتقييم</a>
<a href="/settings?tab=system" class="tab <?= $tab === 'system' ? 'active' : '' ?>">إعدادات قديمة (مرجع)</a>
</div>
<?php if ($tab === 'rewards'): ?>
<p class="text-sm text-muted mb-4">هذه القيم يقرؤها التطبيق مباشرة (جدول reward_config): العملات والخبرة لكل مباراة حسب المركز، المكافأة اليومية، ومعامل تقييم الشطرنج. القيم المعلّمة "لا يقرؤها التطبيق" محفوظة لكنها لا تغيّر شيئًا حاليًا.</p>
<?php if (empty($rewards)): ?>
<div class="card"><div class="empty-state"><h3 class="empty-state-title">لا توجد قيم</h3><p class="empty-state-text">جدول المكافآت فارغ أو تعذرت قراءته</p></div></div>
<?php endif; ?>
<?php foreach ($rewards as $group => $items): ?>
<div class="card mb-5">
<form method="POST" action="/settings/rewards">
<input type="hidden" name="_csrf" value="<?= Auth::csrfToken() ?>">
<div class="card-header flex justify-between items-center">
<h3 class="card-title"><?= View::e($group) ?></h3>
<button type="submit" class="btn btn-primary btn-sm">حفظ <?= View::e($group) ?></button>
</div>
<div class="flex flex-col">
<?php foreach ($items as $r): ?>
<div class="flex items-center justify-between p-4 gap-4 flex-wrap" style="border-bottom: 1px solid var(--border);">
<div class="flex-1" style="min-width: 220px;">
<div class="flex items-center gap-2">
<span class="font-medium text-sm"><?= View::e($r['label']) ?></span>
<?php if ($r['live']): ?>
<span class="badge badge-success badge-sm">يقرؤه التطبيق</span>
<?php else: ?>
<span class="badge badge-default badge-sm">لا يقرؤه التطبيق</span>
<?php endif; ?>
</div>
<div class="text-xs text-muted" dir="ltr"><?= View::e($r['key']) ?></div>
<?php if (!empty($r['note'])): ?>
<div class="text-xs text-secondary mt-1"><?= View::e($r['note']) ?></div>
<?php endif; ?>
</div>
<div class="flex items-center gap-2">
<input type="text" inputmode="<?= $r['kind'] === 'multiplier' ? 'decimal' : 'numeric' ?>" name="values[<?= View::e($r['key']) ?>]" class="form-input tabular-nums" style="width: 120px; padding: var(--space-2) var(--space-3);" value="<?= View::e($r['value']) ?>" dir="ltr" aria-label="<?= View::e($r['label']) ?>">
<span class="text-xs text-muted" style="min-width: 36px;"><?= View::e($r['unit']) ?></span>
</div>
</div>
<?php endforeach; ?>
</div>
</form>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($tab === 'system'): ?>
<div class="card mb-5 p-4">
<p class="text-sm">لا يقرأ التطبيق أي قيمة من هذا الجدول (system_config). تُعرض هنا للمرجع فقط، مع المكان الذي تُضبط منه القيمة الفعلية.</p>
</div>
<?php
$categoryLabels = [
'matchmaking' => 'التوفيق',
'matchmaking' => 'التوفيق بين اللاعبين',
'economy' => 'الاقتصاد',
'moderation' => 'الإشراف',
'platform' => 'المنصة',
'limits' => 'الحدود',
'rating' => 'التقييم',
'anticheat' => 'مكافحة الغش',
'tournaments' => 'البطولات',
'general' => 'عام',
];
$grouped = [];
foreach ($system as $s) $grouped[$s['category'] ?? 'general'][] = $s;
?>
<?php foreach ($grouped as $category => $settings): ?>
<div class="card mb-5">
<div class="card-header">
<h3 class="card-title"><?= View::e($categoryLabels[$category] ?? $category) ?></h3>
<span class="badge badge-default"><?= count($settings) ?> إعداد</span>
</div>
<div class="flex flex-col">
<table class="data-table">
<thead><tr><th>المفتاح</th><th>القيمة المحفوظة</th><th>القيمة الفعلية تُضبط من</th></tr></thead>
<tbody>
<?php foreach ($settings as $setting): ?>
<div class="flex items-center justify-between p-4" style="border-bottom: 1px solid var(--border);">
<div class="flex-1">
<div class="font-medium text-sm"><?= View::e($setting['label_ar'] ?? $setting['label'] ?? $setting['key']) ?></div>
<div class="text-xs text-muted" dir="ltr"><?= View::e($setting['key']) ?></div>
<?php if ($setting['description']): ?>
<div class="text-xs text-secondary mt-1"><?= View::e($setting['description']) ?></div>
<?php endif; ?>
</div>
<div class="flex items-center gap-3">
<?php
$val = is_array($setting['value']) ? json_encode($setting['value']) : (string)($setting['value'] ?? '');
$editable = !($setting['is_secret'] ?? false);
?>
<?php if ($val === 'true' || $val === 'false'): ?>
<form method="POST" action="/settings/update">
<input type="hidden" name="_csrf" value="<?= Auth::csrfToken() ?>">
<input type="hidden" name="key" value="<?= View::e($setting['key']) ?>">
<input type="hidden" name="value" value="<?= $val === 'true' ? 'false' : 'true' ?>">
<label class="toggle">
<input type="checkbox" <?= $val === 'true' ? 'checked' : '' ?> <?= !$editable ? 'disabled' : '' ?> onchange="this.closest('form').submit()">
<span class="toggle-track"></span>
</label>
</form>
<?php else: ?>
<form method="POST" action="/settings/update" class="flex items-center gap-2">
<input type="hidden" name="_csrf" value="<?= Auth::csrfToken() ?>">
<input type="hidden" name="key" value="<?= View::e($setting['key']) ?>">
<input type="text" name="value" class="form-input" style="width: 200px; padding: var(--space-2) var(--space-3);" value="<?= View::e($val) ?>" <?= !$editable ? 'disabled' : '' ?> dir="ltr">
<?php if ($editable): ?>
<button type="submit" class="btn btn-primary btn-sm">حفظ</button>
<?php endif; ?>
</form>
<?php endif; ?>
</div>
</div>
<?php $val = is_array($setting['value']) || is_bool($setting['value']) ? json_encode($setting['value'], JSON_UNESCAPED_UNICODE) : (string)($setting['value'] ?? ''); ?>
<tr>
<td><span class="text-xs" dir="ltr"><?= View::e($setting['key']) ?></span><?php if (!empty($setting['description'])): ?><div class="text-xs text-muted"><?= View::e($setting['description']) ?></div><?php endif; ?></td>
<td class="tabular-nums" dir="ltr"><?= ($setting['is_secret'] ?? false) ? '******' : View::e($val) ?></td>
<td class="text-sm text-secondary"><?= View::e(SettingsController::SYSTEM_SOURCES[$setting['key']] ?? 'غير مستخدم') ?></td>
</tr>
<?php endforeach; ?>
</div>
</tbody>
</table>
</div>
<?php endforeach; ?>
<?php endif; ?>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment