batch 1 update style & dahsboard for rec.nurinda.co.id

This commit is contained in:
dirga committed 2026-10-14 13:52:12 +07:00
1 parent 9107ffb158
commit f39aa60384
18485 files changed
+843 -2881869

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
<?php
// Admin sessions are independent from candidate sessions.
if (PHP_SAPI !== 'cli') {
session_name('recruitment_admin');
session_set_cookie_params(['httponly' => true, 'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', 'samesite' => 'Lax', 'path' => '/']);
session_start();
header('Cache-Control: no-store');
header('X-Content-Type-Options: nosniff');
header("Content-Security-Policy: default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; base-uri 'self'");
if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(32));
}
date_default_timezone_set('Asia/Jakarta');
const CATEGORIES = [1 => 'Matematika', 2 => 'Sinonim', 3 => 'Antonim', 4 => 'Padanan kata', 5 => 'Kepribadian'];
const STAGES = [0 => 'Calon kandidat', 1 => 'Tidak dilanjutkan', 2 => 'Tes offline & interview', 3 => 'Diterima', 99 => 'Demo / arsip'];
function e($value) { return htmlspecialchars(str_replace("\0", '', (string)$value), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function redirect($url) { header('Location: ' . $url); exit; }
function csrf_field() { echo '<input type="hidden" name="csrf" value="' . e($_SESSION['csrf']) . '">'; }
function verify_csrf() { if (!hash_equals($_SESSION['csrf'], (string)($_POST['csrf'] ?? ''))) { http_response_code(403); exit('Sesi formulir tidak valid. Muat ulang halaman.'); } }
function require_admin() {
if (empty($_SESSION['admin_id']) || time() - ($_SESSION['admin_active'] ?? 0) > 3600) {
unset($_SESSION['admin_id']); redirect('login.php');
}
$_SESSION['admin_active'] = time();
}
function db() {
static $connection;
if ($connection) return $connection;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
require __DIR__ . '/../includes/conn.php';
$connection = $conn;
$connection->set_charset('utf8mb4');
return $connection;
}
function query($sql, $types = '', $params = []) {
$statement = db()->prepare($sql);
if ($types !== '') $statement->bind_param($types, ...$params);
$statement->execute();
return $statement;
}
function rows($sql, $types = '', $params = []) { return query($sql, $types, $params)->get_result()->fetch_all(MYSQLI_ASSOC); }
function one($sql, $types = '', $params = []) { return rows($sql, $types, $params)[0] ?? null; }
function threshold() { return max(0, (int)(getenv('REC_PASS_SCORE') ?: 300)); }
function fail_page($message, $status = 503) { http_response_code($status); page_start('Layanan belum siap'); echo '<section class="card empty"><h2>Layanan belum siap</h2><p>' . e($message) . '</p></section>'; page_end(); exit; }
set_exception_handler(function ($error) {
error_log('Recruitment admin: ' . $error->getMessage());
if (PHP_SAPI === 'cli') { fwrite(STDERR, "Operasi gagal. Periksa konfigurasi database dan migrasi admin.\n"); exit(1); }
fail_page('Periksa koneksi database dan jalankan migrasi tabel admin sesuai petunjuk instalasi.');
});
function page_start($title, $active = '') {
$GLOBALS['admin_layout'] = $active !== '';
?><!doctype html><html lang="id"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title><?=e($title)?> · Nurinda Recruitment</title><link rel="stylesheet" href="assets/admin.css"><script src="assets/admin.js" defer></script></head><body>
<?php if ($active !== ''): ?><aside class="sidebar"><a class="brand" href="index.php"><img class="brand-logo" src="assets/nurinda-logo.png" alt="Logo Nurinda"><span>NURINDA<small>Recruitment workspace</small></span></a><span class="nav-label">WORKSPACE</span><nav><a class="<?=$active === 'home' ? 'active' : ''?>" href="index.php"><span>◫</span> Ringkasan</a><a class="<?=$active === 'participants' ? 'active' : ''?>" href="idap.php"><span>◎</span> Daftar Peserta Psikotest</a><a class="<?=$active === 'questions' ? 'active' : ''?>" href="isoal.php"><span>▤</span> Bank Soal Psikotest</a></nav><div class="sidebar-bottom"><span class="avatar"><?=e(substr($_SESSION['admin_name'] ?? 'A', 0, 1))?></span><div><strong><?=e($_SESSION['admin_name'] ?? 'Admin')?></strong><small>Administrator</small></div><form method="post" action="logout.php"><?php csrf_field(); ?><button class="icon-button" title="Keluar" aria-label="Keluar">↗</button></form></div></aside><main class="main"><header class="topbar"><span>Human Capital <span class="muted">/ Recruitment</span></span><div class="topbar-actions"><span class="workspace-dot">Admin workspace</span><form method="post" action="logout.php"><?php csrf_field(); ?><button class="icon-button" title="Keluar" aria-label="Keluar">↗</button></form></div></header><div class="content"><div class="heading"><div><span class="eyebrow">RECRUITMENT MANAGEMENT</span><h1><?=e($title)?></h1></div><span class="date"><?=date('d M Y')?></span></div><?php else: ?><main class="auth-main"><?php endif; ?>
<?php if (!empty($_SESSION['flash'])): ?><div class="notice" role="status"><?=e($_SESSION['flash'])?></div><?php unset($_SESSION['flash']); endif;
}
function page_end() { echo (!empty($GLOBALS['admin_layout']) ? '</div>' : '') . '</main></body></html>'; }