Files
2026-10-20 13:16:38 +09:00

171 lines
7.8 KiB
PHP

<?php
declare(strict_types=1);
require __DIR__ . '/../includes/admin_actions.php';
define('SERUBI_ADMIN', true);
$me = require_admin();
$tabs = [
'parameter' => ['Parameter bank', 'sliders', 'GWM, kas, PUAB, suku bunga kredit, biaya, pajak, dan konvensi hari bunga.'],
'produk' => ['Rate tabungan & giro', 'wallet', 'Rate bunga SIMANJA, SIMPEDA, dan Giro Umum per kelompok nominal sesuai ALCO.'],
'deposito' => ['Rate deposito', 'grid', 'Rate deposito pembanding per kelompok nominal dan jangka waktu sesuai ALCO.'],
'cashback' => ['Porsi cashback', 'pie', 'Pembagian spread antara nasabah dan bank, serta jangka waktu yang mendapat cashback.'],
'pengguna' => ['Pengguna admin', 'users', 'Kelola akun yang dapat mengubah parameter.'],
'riwayat' => ['Riwayat perubahan', 'history', 'Siapa mengubah apa dan kapan.'],
];
$tab = input_str($_GET['tab'] ?? 'parameter');
if (!isset($tabs[$tab])) {
$tab = 'parameter';
}
// A reset or initial password must be replaced before anything else.
if ($me['must_change']) {
$tab = 'pengguna';
}
$failed = null; // ['errors' => [...], 'draft' => [...]] when a POST did not validate
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
csrf_check();
$action = input_str($_POST['action'] ?? '');
$handlers = [
'save_settings' => fn() => action_save_settings(),
'save_products' => fn() => action_save_products(),
'save_deposito' => fn() => action_save_deposito(),
'save_cashback' => fn() => action_save_cashback(),
'reset_defaults' => fn() => action_reset_defaults(),
'user_create' => fn() => action_user_create($me),
'user_toggle' => fn() => action_user_toggle($me),
'user_password' => fn() => action_user_password($me),
'user_role' => fn() => action_user_role($me),
'user_reset_otp' => fn() => action_user_reset_otp($me),
'own_password' => fn() => action_own_password($me),
];
if ($me['must_change'] && $action !== 'own_password') {
$action = '';
}
if (isset($handlers[$action])) {
$failed = $handlers[$action]();
}
}
$params = load_params();
$flash = flash();
$errors = $failed['errors'] ?? [];
$draft = $failed['draft'] ?? null;
$hasPreview = in_array($tab, ['parameter', 'produk', 'deposito', 'cashback'], true);
function field_error(array $errors, string $key): string
{
return isset($errors[$key]) ? '<p class="field-msg" role="alert">' . icon('alert') . '<span>' . e($errors[$key]) . '</span></p>' : '';
}
?>
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="color-scheme" content="light dark">
<meta name="robots" content="noindex">
<title><?= e($tabs[$tab][0]) ?> · Admin SERUBI</title>
<?= theme_boot_script() ?>
<link rel="icon" href="<?= e(asset('assets/favicon.svg')) ?>" type="image/svg+xml">
<link rel="stylesheet" href="<?= e(asset('assets/css/base.css')) ?>">
<link rel="stylesheet" href="<?= e(asset('assets/css/admin.css')) ?>">
<meta name="app-version" content="<?= e(app_version()) ?>">
</head>
<body class="page-admin">
<a class="skip-link" href="#konten">Lewati ke konten</a>
<div class="admin<?= $hasPreview ? ' has-preview' : '' ?>">
<aside class="side">
<div class="side-top">
<a class="brand" href="<?= e(base_url('admin/')) ?>" aria-label="Bank Papua, beranda admin"><?= bank_logo('white') ?></a>
<p class="side-tag"><span class="brand-prog">SERUBI</span><span class="side-sub">Admin parameter</span></p>
</div>
<nav class="side-nav" aria-label="Menu admin">
<?php foreach ($tabs as $key => [$label, $ico]): ?>
<a href="?tab=<?= e($key) ?>" class="side-link<?= $key === $tab ? ' is-active' : '' ?>" <?= $key === $tab ? 'aria-current="page"' : '' ?>>
<?= icon($ico) ?><span><?= e($label) ?></span>
</a>
<?php endforeach; ?>
</nav>
<div class="side-foot">
<a class="side-link" href="<?= e(base_url()) ?>" target="_blank" rel="noopener"><?= icon('calc') ?><span>Buka kalkulator</span></a>
<div class="side-user">
<span class="side-user-name"><?= e($me['name']) ?></span>
<span class="side-user-id">@<?= e($me['username']) ?></span>
</div>
<div class="side-actions">
<button type="button" class="icon-btn" data-theme-toggle aria-label="Ganti tema terang/gelap"><?= icon('moon', 'icon icon-moon') ?><?= icon('sun', 'icon icon-sun') ?></button>
<form method="post" action="<?= e(base_url('admin/logout.php')) ?>">
<?= csrf_field() ?>
<button type="submit" class="text-btn"><?= icon('logout') ?><span>Keluar</span></button>
</form>
</div>
</div>
</aside>
<main class="admin-main" id="konten" tabindex="-1">
<header class="page-head">
<h1><?= e($tabs[$tab][0]) ?></h1>
<p><?= e($tabs[$tab][2]) ?></p>
<?php if ($hasPreview): ?>
<p class="page-meta">Terakhir diubah <?= e(fmt_datetime($params['updatedAt'])) ?><?= $params['updatedBy'] ? ' oleh @' . e($params['updatedBy']) : '' ?></p>
<?php endif; ?>
</header>
<?php if ($me['must_change']): ?>
<p class="alert alert-info" role="status"><?= icon('key') ?><span>Password akun Anda baru dibuat atau di-reset. Ganti password di bawah sebelum memakai menu lain.</span></p>
<?php endif; ?>
<?php if ($flash): ?>
<p class="alert alert-<?= e($flash['type']) ?>" role="status"><?= icon($flash['type'] === 'ok' ? 'check' : 'info') ?><span><?= e($flash['message']) ?></span></p>
<?php endif; ?>
<?php if ($errors && !in_array($tab, ['pengguna'], true)): ?>
<p class="alert alert-error" role="alert"><?= icon('alert') ?><span>Perubahan belum disimpan. Periksa isian yang ditandai.</span></p>
<?php endif; ?>
<?php require __DIR__ . '/views/' . $tab . '.php'; ?>
</main>
<?php if ($hasPreview): ?>
<aside class="preview" aria-labelledby="pv-title">
<div class="preview-card">
<h2 id="pv-title">Pratinjau dampak</h2>
<p class="muted">Hasil kalkulator dengan nilai di formulir, sebelum disimpan.</p>
<div class="pv-inputs">
<div class="form-field">
<label for="pvNominal">Nominal</label>
<div class="input-affix"><span>Rp</span><input id="pvNominal" inputmode="numeric" value="55.000.000" autocomplete="off"></div>
</div>
<div class="form-field">
<label for="pvTenor">Jangka waktu</label>
<select id="pvTenor">
<?php foreach ($params['tenors'] as $t): ?><option value="<?= (int)$t ?>" <?= $t === 6 ? 'selected' : '' ?>><?= (int)$t ?> bulan</option><?php endforeach; ?>
</select>
</div>
</div>
<table class="pv-table">
<thead><tr><th scope="col"></th><th scope="col">Tersimpan</th><th scope="col">Formulir</th></tr></thead>
<tbody id="pvRows"></tbody>
</table>
<p class="hint-block" id="pvWarn" role="status" hidden><?= icon('alert') ?><span></span></p>
</div>
</aside>
<?php endif; ?>
</div>
<div class="savebar" id="savebar" hidden>
<span><?= icon('info') ?>Ada perubahan yang belum disimpan.</span>
<div>
<button type="button" class="btn btn-ghost" id="btnDiscard">Batalkan</button>
<button type="button" class="btn btn-primary" id="btnSave"><?= icon('save') ?><span>Simpan</span></button>
</div>
</div>
<script nonce="<?= e(csp_nonce()) ?>">window.SERUBI_PARAMS = <?= json_encode($params, JSON_HEX_TAG | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE) ?>;</script>
<script src="<?= e(asset('assets/js/engine.js')) ?>"></script>
<script src="<?= e(asset('assets/js/format.js')) ?>"></script>
<script src="<?= e(asset('assets/js/admin.js')) ?>"></script>
<script src="<?= e(asset('assets/js/theme.js')) ?>"></script>
<?php if ($draft !== null): ?><script nonce="<?= e(csp_nonce()) ?>">document.dispatchEvent(new Event('serubi:dirty'));</script><?php endif; ?>
</body>
</html>