39331-vm/index.php
Flatlogic Bot 522a55296c arsip_demo
2026-03-26 11:04:24 +00:00

433 lines
29 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/archive_bootstrap.php';
ensure_archive_table();
[$projectName, $projectDescription, $projectImageUrl] = project_meta();
$user = current_user();
$flash = pull_flash();
$folderFilter = isset($_GET['folder']) ? trim((string) $_GET['folder']) : null;
$folderInfo = $folderFilter ? normalize_folder_path($folderFilter) : null;
if ($folderFilter && !$folderInfo) {
$folderFilter = null;
}
$editRecord = null;
if ($user && isset($_GET['edit'])) {
$editRecord = fetch_record_by_id((int) $_GET['edit']);
if (!$editRecord || !can_edit_record($user, $editRecord)) {
$editRecord = null;
flash('error', 'Data yang dipilih tidak dapat diedit oleh akun ini.');
header('Location: index.php');
exit;
}
}
$selectedRecord = null;
if ($user && isset($_GET['record'])) {
$selectedRecord = fetch_record_by_id((int) $_GET['record']);
if (!$selectedRecord || !can_view_record($user, $selectedRecord)) {
$selectedRecord = null;
}
}
$monthNames = [
1 => 'Januari', 2 => 'Februari', 3 => 'Maret', 4 => 'April', 5 => 'Mei', 6 => 'Juni',
7 => 'Juli', 8 => 'Agustus', 9 => 'September', 10 => 'Oktober', 11 => 'November', 12 => 'Desember',
];
$yearOptions = range((int) date('Y') + 1, 2020);
$loginUsers = users_catalog();
?>
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= h($projectName) ?> — Arsip Internal</title>
<meta name="description" content="<?= h($projectDescription) ?>">
<meta property="og:title" content="<?= h($projectName) ?>">
<meta property="og:description" content="<?= h($projectDescription) ?>">
<meta property="twitter:title" content="<?= h($projectName) ?>">
<meta property="twitter:description" content="<?= h($projectDescription) ?>">
<?php if ($projectImageUrl): ?>
<meta property="og:image" content="<?= h($projectImageUrl) ?>">
<meta property="twitter:image" content="<?= h($projectImageUrl) ?>">
<?php endif; ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>">
</head>
<body>
<?php if (!$user): ?>
<main class="auth-shell">
<section class="container py-4 py-lg-5">
<div class="row justify-content-center">
<div class="col-12 col-xl-10">
<div class="row g-4 align-items-stretch">
<div class="col-lg-7">
<div class="panel panel-hero h-100">
<div class="eyebrow mb-3">Portal Arsip Internal KBRI Harare</div>
<h1 class="display-title mb-3">Arsip terstruktur, aman, dan siap dipakai untuk alur kerja harian.</h1>
<p class="lead-copy mb-4">Versi awal ini sudah menyiapkan login internal, struktur menu tree lengkap, input dokumen bertanggal, daftar arsip, detail, edit, print, dan unduh lampiran.</p>
<div class="row g-3">
<div class="col-sm-6">
<div class="metric-card h-100">
<div class="metric-label">Peran awal</div>
<div class="metric-value">14 akun</div>
<small>2 super admin, 7 kepala bagian, 5 staf</small>
</div>
</div>
<div class="col-sm-6">
<div class="metric-card h-100">
<div class="metric-label">Kontrol kerja</div>
<div class="metric-value">Tree + RBAC</div>
<small>Akses folder sesuai unit kerja</small>
</div>
</div>
</div>
<div class="callout mt-4">
<strong>Default keamanan tahap awal:</strong> semua akun demo memakai password <code>Kbri2026!</code>. Setelah alur disetujui, langkah paling penting berikutnya adalah password individual + audit trail lanjutan.
</div>
</div>
</div>
<div class="col-lg-5">
<div class="panel h-100">
<div class="d-flex justify-content-between align-items-start mb-4 gap-3 flex-wrap">
<div>
<div class="eyebrow">Masuk</div>
<h2 class="section-title mb-1">Akun internal</h2>
<p class="text-secondary mb-0">Gunakan salah satu username resmi di bawah.</p>
</div>
<span class="badge badge-soft">High security MVP</span>
</div>
<?php if ($flash): ?>
<div class="alert alert-<?= h(alert_class($flash['type'])) ?> mb-3" role="alert"><?= h($flash['message']) ?></div>
<?php endif; ?>
<form action="auth_login.php" method="post" class="mb-4">
<input type="hidden" name="csrf_token" value="<?= h(csrf_token()) ?>">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" placeholder="mis. super.admin1" required autocomplete="username">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="Password internal" required autocomplete="current-password">
</div>
<button type="submit" class="btn btn-dark w-100">Masuk ke dashboard arsip</button>
</form>
<div class="credentials-list">
<div class="d-flex justify-content-between align-items-center mb-2">
<h3 class="small-heading mb-0">Akun demo siap pakai</h3>
<span class="text-secondary small">password sama untuk semua</span>
</div>
<div class="table-responsive">
<table class="table table-sm align-middle mb-0 credential-table">
<thead>
<tr>
<th>Username</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<?php foreach ($loginUsers as $username => $account): ?>
<tr>
<td><code><?= h($username) ?></code></td>
<td><?= h(role_badge_label($account['role'])) ?> — <?= h($account['unit']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
<?php else: ?>
<?php
$counts = fetch_summary_counts($user);
$records = fetch_records($user, $folderFilter, 10);
$availableFolders = available_folder_paths_for_user($user);
$formFolder = $editRecord['folder_path'] ?? $folderFilter ?? ($availableFolders[0] ?? '');
$formFolderInfo = $formFolder ? normalize_folder_path($formFolder) : null;
$formMainMenu = $editRecord['main_menu'] ?? ($formFolderInfo['main_menu'] ?? ($user['allowed_menus'][0] ?? ''));
$todayUtc = gmdate('d M Y, H:i') . ' UTC';
?>
<div class="app-shell">
<header class="topbar border-bottom">
<div class="container-fluid px-3 px-lg-4 py-3">
<div class="d-flex align-items-center justify-content-between gap-3 flex-wrap">
<div>
<div class="eyebrow">Internal archive workspace</div>
<h1 class="app-title mb-0">KBRI Harare Archive Desk</h1>
</div>
<div class="d-flex align-items-center gap-2 flex-wrap">
<span class="badge badge-soft"><?= h(role_badge_label($user['role'])) ?></span>
<span class="badge badge-outline"><?= h($user['name']) ?> · <?= h($user['username']) ?></span>
<span class="text-secondary small">Sinkron UTC <?= h($todayUtc) ?></span>
<form action="logout.php" method="post" class="m-0">
<input type="hidden" name="csrf_token" value="<?= h(csrf_token()) ?>">
<button type="submit" class="btn btn-outline-secondary btn-sm">Keluar</button>
</form>
</div>
</div>
</div>
</header>
<main class="container-fluid px-3 px-lg-4 py-4">
<div class="row g-4 mb-4">
<div class="col-12 col-xl-3">
<div class="panel h-100">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<div class="eyebrow">Navigasi pohon</div>
<h2 class="section-title mb-0">Menu utama</h2>
</div>
<span class="badge badge-outline">+</span>
</div>
<p class="text-secondary small mb-3">Klik tanda + untuk membuka sub-menu dan pilih folder terdalam sebagai lokasi arsip.</p>
<nav class="tree-nav" aria-label="Struktur arsip KBRI">
<?= render_tree_nodes(archive_tree(), $folderFilter, $user) ?>
</nav>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="panel panel-hero mb-4">
<div class="d-flex flex-column flex-lg-row justify-content-between gap-3 align-items-start">
<div>
<div class="eyebrow">First delivery</div>
<h2 class="section-title mb-2">Workflow arsip harian dari input sampai detail.</h2>
<p class="text-secondary mb-0">Form di bawah terhubung ke database, mengikuti folder tree, menyimpan lampiran, dan menampilkan daftar arsip sesuai hak akses akun yang aktif.</p>
</div>
<div class="hero-actions d-flex gap-2 flex-wrap">
<a href="#arsip-form" class="btn btn-dark btn-sm">Input arsip</a>
<?php if ($selectedRecord): ?>
<a href="archive_detail.php?id=<?= (int) $selectedRecord['id'] ?>" class="btn btn-outline-secondary btn-sm">Lihat detail</a>
<?php endif; ?>
</div>
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-sm-4">
<div class="metric-card h-100">
<div class="metric-label">Total arsip</div>
<div class="metric-value"><?= (int) $counts['total_records'] ?></div>
<small><?= $user['role'] === 'staf' ? 'Hanya milik akun ini' : 'Seluruh arsip yang terlihat' ?></small>
</div>
</div>
<div class="col-sm-4">
<div class="metric-card h-100">
<div class="metric-label">Lampiran aktif</div>
<div class="metric-value"><?= (int) $counts['total_files'] ?></div>
<small>Siap diunduh dari detail arsip</small>
</div>
</div>
<div class="col-sm-4">
<div class="metric-card h-100">
<div class="metric-label">Masuk hari ini</div>
<div class="metric-value"><?= (int) $counts['created_today'] ?></div>
<small>Dokumen baru per <?= h($todayUtc) ?></small>
</div>
</div>
</div>
<div class="panel" id="arsip-form">
<div class="d-flex justify-content-between align-items-center gap-3 flex-wrap mb-3">
<div>
<div class="eyebrow">Form arsip</div>
<h2 class="section-title mb-1"><?= $editRecord ? 'Edit dokumen arsip' : 'Input dokumen baru' ?></h2>
<p class="text-secondary mb-0">Tanggal dipecah menjadi hari, bulan, dan tahun. Folder harus mengikuti menu kerja akun aktif.</p>
</div>
<?php if ($editRecord): ?>
<a href="index.php<?= $folderFilter ? '?folder=' . urlencode($folderFilter) : '' ?>#arsip-form" class="btn btn-outline-secondary btn-sm">Batal edit</a>
<?php endif; ?>
</div>
<?php if ($flash): ?>
<div class="toast align-items-center text-bg-<?= h(alert_class($flash['type'])) ?> border-0 show mb-3" role="status" aria-live="polite" aria-atomic="true">
<div class="d-flex">
<div class="toast-body"><?= h($flash['message']) ?></div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
<?php endif; ?>
<form action="archive_save.php" method="post" enctype="multipart/form-data" class="row g-3">
<input type="hidden" name="csrf_token" value="<?= h(csrf_token()) ?>">
<?php if ($editRecord): ?>
<input type="hidden" name="id" value="<?= (int) $editRecord['id'] ?>">
<?php endif; ?>
<div class="col-md-6">
<label class="form-label" for="reference_code">Nomor referensi</label>
<input class="form-control" id="reference_code" name="reference_code" maxlength="100" required value="<?= h($editRecord['reference_code'] ?? '') ?>" placeholder="mis. POL/HAR/ND/026/2026">
</div>
<div class="col-md-6">
<label class="form-label" for="title">Judul arsip</label>
<input class="form-control" id="title" name="title" maxlength="180" required value="<?= h($editRecord['title'] ?? '') ?>" placeholder="Judul dokumen atau peristiwa">
</div>
<div class="col-md-4">
<label class="form-label" for="main_menu">Menu utama</label>
<select class="form-select" id="main_menu" name="main_menu" required>
<?php foreach (main_menu_options() as $menu): ?>
<?php if (can_access_menu($user, $menu)): ?>
<option value="<?= h($menu) ?>" <?= $formMainMenu === $menu ? 'selected' : '' ?>><?= h($menu) ?></option>
<?php endif; ?>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-8">
<label class="form-label" for="folder_path">Folder nested</label>
<select class="form-select" id="folder_path" name="folder_path" required>
<option value="">Pilih folder terdalam</option>
<?php foreach ($availableFolders as $path): ?>
<option value="<?= h($path) ?>" <?= $formFolder === $path ? 'selected' : '' ?>><?= h($path) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label" for="country_tag">Negara / cakupan</label>
<select class="form-select" id="country_tag" name="country_tag" required>
<?php $countryValue = $editRecord['country_tag'] ?? 'Zimbabwe'; ?>
<?php foreach (['Zimbabwe', 'Zambia', 'Indonesia', 'Regional', 'Internal'] as $country): ?>
<option value="<?= h($country) ?>" <?= $countryValue === $country ? 'selected' : '' ?>><?= h($country) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label" for="record_day">Tanggal</label>
<select class="form-select" id="record_day" name="record_day" required>
<?php $selectedDay = (int) ($editRecord['record_day'] ?? date('j')); ?>
<?php foreach (range(1, 31) as $day): ?>
<option value="<?= $day ?>" <?= $selectedDay === $day ? 'selected' : '' ?>><?= $day ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label" for="record_month">Bulan</label>
<select class="form-select" id="record_month" name="record_month" required>
<?php $selectedMonth = (int) ($editRecord['record_month'] ?? date('n')); ?>
<?php foreach ($monthNames as $number => $label): ?>
<option value="<?= $number ?>" <?= $selectedMonth === $number ? 'selected' : '' ?>><?= h($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label" for="record_year">Tahun</label>
<select class="form-select" id="record_year" name="record_year" required>
<?php $selectedYear = (int) ($editRecord['record_year'] ?? date('Y')); ?>
<?php foreach ($yearOptions as $year): ?>
<option value="<?= $year ?>" <?= $selectedYear === (int) $year ? 'selected' : '' ?>><?= $year ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="confidentiality">Klasifikasi</label>
<select class="form-select" id="confidentiality" name="confidentiality" required>
<?php $classification = $editRecord['confidentiality'] ?? 'Terbuka'; ?>
<?php foreach (['Terbuka', 'Terbatas', 'Rahasia'] as $item): ?>
<option value="<?= h($item) ?>" <?= $classification === $item ? 'selected' : '' ?>><?= h($item) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="keywords">Kata kunci</label>
<input class="form-control" id="keywords" name="keywords" maxlength="255" value="<?= h($editRecord['keywords'] ?? '') ?>" placeholder="mis. bilateral, nota, visa, SOP">
</div>
<div class="col-12">
<label class="form-label" for="description">Ringkasan / catatan</label>
<textarea class="form-control" id="description" name="description" rows="5" required placeholder="Tuliskan isi pokok dokumen, konteks, dan tindak lanjut."><?= h($editRecord['description'] ?? '') ?></textarea>
</div>
<div class="col-12">
<label class="form-label" for="attachment">Lampiran file</label>
<input class="form-control" id="attachment" type="file" name="attachment" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.jpg,.jpeg,.png,.mp4,.zip">
<div class="form-text">Format umum dokumen, gambar, video, dan ZIP didukung. Maksimal 8 MB.</div>
<?php if (!empty($editRecord['attachment_name'])): ?>
<div class="inline-note mt-2">Lampiran aktif: <strong><?= h($editRecord['attachment_name']) ?></strong></div>
<?php endif; ?>
</div>
<div class="col-12 d-flex flex-wrap gap-2 align-items-center">
<button type="submit" class="btn btn-dark"><?= $editRecord ? 'Simpan perubahan' : 'Simpan arsip' ?></button>
<?php if ($formFolder): ?>
<a class="btn btn-outline-secondary" href="index.php?folder=<?= urlencode($formFolder) ?>">Lihat folder ini</a>
<?php endif; ?>
<span class="inline-note">Akun aktif: <?= h($user['name']) ?> · unit <?= h($user['unit']) ?></span>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-3">
<div class="panel h-100 d-flex flex-column gap-4">
<div>
<div class="d-flex justify-content-between align-items-center mb-3 gap-2 flex-wrap">
<div>
<div class="eyebrow">Daftar arsip</div>
<h2 class="section-title mb-0"><?= $folderFilter ? 'Folder aktif' : 'Arsip terbaru' ?></h2>
</div>
<?php if ($folderFilter): ?>
<a href="index.php" class="btn btn-outline-secondary btn-sm">Reset</a>
<?php endif; ?>
</div>
<?php if ($folderFilter): ?>
<div class="inline-note mb-3">Filter: <?= h($folderFilter) ?></div>
<?php endif; ?>
<?php if (!$records): ?>
<div class="empty-state">
<strong>Belum ada arsip.</strong>
<p class="mb-0">Pilih folder dari tree lalu input dokumen pertama untuk mulai membangun database arsip.</p>
</div>
<?php else: ?>
<div class="list-stack">
<?php foreach ($records as $record): ?>
<article class="record-card <?= $selectedRecord && (int) $selectedRecord['id'] === (int) $record['id'] ? 'active' : '' ?>">
<div class="d-flex justify-content-between align-items-start gap-2 mb-2">
<span class="badge <?= h(record_badge_class($record['confidentiality'])) ?>"><?= h($record['confidentiality']) ?></span>
<small class="text-secondary">#<?= (int) $record['id'] ?></small>
</div>
<h3 class="record-title"><?= h($record['title']) ?></h3>
<p class="record-meta mb-2"><?= h($record['reference_code']) ?> · <?= h($record['record_day']) ?>/<?= h($record['record_month']) ?>/<?= h($record['record_year']) ?></p>
<p class="record-path mb-3"><?= h($record['folder_path']) ?></p>
<div class="d-flex gap-2 flex-wrap">
<a href="archive_detail.php?id=<?= (int) $record['id'] ?>" class="btn btn-sm btn-dark">Detail</a>
<?php if (can_edit_record($user, $record)): ?>
<a href="index.php?edit=<?= (int) $record['id'] ?>#arsip-form" class="btn btn-sm btn-outline-secondary">Edit</a>
<?php endif; ?>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div>
<div class="eyebrow">Hak akses aktif</div>
<h2 class="section-title mb-3">Kontrol akun</h2>
<ul class="policy-list mb-0">
<?php if ($user['role'] === 'super_admin'): ?>
<li>Melihat, input, edit, unduh, dan hapus semua arsip.</li>
<li>Akses ke seluruh menu utama dan nested folder.</li>
<?php elseif ($user['role'] === 'kepala_bagian'): ?>
<li>Melihat seluruh database yang tersedia di dashboard.</li>
<li>Edit hanya arsip dari unit <?= h($user['unit']) ?>, tanpa fitur hapus.</li>
<?php else: ?>
<li>Input, edit, hapus, dan unduh arsip milik akun sendiri.</li>
<li>Folder input dibatasi ke unit <?= h($user['unit']) ?>.</li>
<?php endif; ?>
</ul>
</div>
</div>
</div>
</div>
</main>
</div>
<?php endif; ?>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="assets/js/main.js?v=<?= time() ?>"></script>
</body>
</html>