Initial commit - CMS Gov Bapenda Garut dengan EditorJS

This commit is contained in:
2026-01-05 06:47:36 +07:00
commit bd649bd5f2
634 changed files with 215640 additions and 0 deletions

View File

@@ -0,0 +1,319 @@
<?= $this->extend('admin/layout') ?>
<?= $this->section('content') ?>
<div class="space-y-5 sm:space-y-6">
<!-- Page Header -->
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<div>
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white/90">
Berita
</h2>
<p class="text-sm text-gray-500 dark:text-gray-400">
Kelola berita dan artikel
</p>
</div>
<a
href="<?= base_url('admin/news/create') ?>"
class="inline-flex items-center gap-2 rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white shadow-theme-xs hover:bg-brand-600"
>
<i class="fe fe-plus"></i>
Tambah Berita
</a>
</div>
<!-- Stats Cards -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div class="rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-white/[0.03]">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 dark:text-gray-400">Total Berita</p>
<p class="text-2xl font-bold text-gray-800 dark:text-white"><?= $stats['total'] ?></p>
</div>
<div class="w-12 h-12 rounded-full bg-brand-100 dark:bg-brand-900/20 flex items-center justify-center">
<i class="fe fe-file-text text-brand-600 dark:text-brand-400 text-xl"></i>
</div>
</div>
</div>
<div class="rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-white/[0.03]">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 dark:text-gray-400">Published</p>
<p class="text-2xl font-bold text-gray-800 dark:text-white"><?= $stats['published'] ?></p>
</div>
<div class="w-12 h-12 rounded-full bg-success-100 dark:bg-success-900/20 flex items-center justify-center">
<i class="fe fe-check-circle text-success-600 dark:text-success-400 text-xl"></i>
</div>
</div>
</div>
<div class="rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-white/[0.03]">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 dark:text-gray-400">Draft</p>
<p class="text-2xl font-bold text-gray-800 dark:text-white"><?= $stats['draft'] ?></p>
</div>
<div class="w-12 h-12 rounded-full bg-warning-100 dark:bg-warning-900/20 flex items-center justify-center">
<i class="fe fe-edit text-warning-600 dark:text-warning-400 text-xl"></i>
</div>
</div>
</div>
</div>
<!-- Filters and Search -->
<div class="rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-800 dark:bg-white/[0.03]">
<form method="get" action="<?= base_url('admin/news') ?>" class="flex flex-col gap-4 sm:flex-row sm:items-center">
<div class="flex-1">
<input
type="text"
name="search"
value="<?= esc($currentSearch ?? '') ?>"
placeholder="Cari berita..."
class="h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800"
/>
</div>
<div>
<select
name="status"
class="h-11 rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:focus:border-brand-800"
>
<option value="">Semua Status</option>
<option value="published" <?= ($currentStatus === 'published') ? 'selected' : '' ?>>Published</option>
<option value="draft" <?= ($currentStatus === 'draft') ? 'selected' : '' ?>>Draft</option>
</select>
</div>
<button
type="submit"
class="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white shadow-theme-xs hover:bg-brand-600"
>
<i class="fe fe-search"></i>
Cari
</button>
<?php if ($currentSearch || $currentStatus): ?>
<a
href="<?= base_url('admin/news') ?>"
class="inline-flex items-center justify-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03]"
>
<i class="fe fe-x"></i>
Reset
</a>
<?php endif; ?>
</form>
</div>
<!-- News Table -->
<div class="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
<div class="max-w-full overflow-x-auto">
<table class="min-w-full">
<thead>
<tr class="border-b border-gray-100 dark:border-gray-800">
<th class="px-5 py-3 sm:px-6">
<div class="flex items-center">
<p class="font-medium text-gray-500 text-xs dark:text-gray-400">
Judul
</p>
</div>
</th>
<th class="px-5 py-3 sm:px-6">
<div class="flex items-center">
<p class="font-medium text-gray-500 text-xs dark:text-gray-400">
Status
</p>
</div>
</th>
<th class="px-5 py-3 sm:px-6">
<div class="flex items-center">
<p class="font-medium text-gray-500 text-xs dark:text-gray-400">
Dibuat Oleh
</p>
</div>
</th>
<th class="px-5 py-3 sm:px-6">
<div class="flex items-center">
<p class="font-medium text-gray-500 text-xs dark:text-gray-400">
Tanggal Dibuat
</p>
</div>
</th>
<th class="px-5 py-3 sm:px-6">
<div class="flex items-center">
<p class="font-medium text-gray-500 text-xs dark:text-gray-400">
Aksi
</p>
</div>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-gray-800">
<?php if (empty($news)): ?>
<tr>
<td colspan="5" class="px-5 py-8 text-center sm:px-6">
<p class="text-gray-500 dark:text-gray-400">Tidak ada berita ditemukan.</p>
</td>
</tr>
<?php else: ?>
<?php foreach ($news as $item): ?>
<tr>
<td class="px-5 py-4 sm:px-6">
<div class="flex items-center">
<div>
<p class="font-medium text-gray-800 text-sm dark:text-white/90">
<?= esc($item['title']) ?>
</p>
<span class="text-gray-500 text-xs dark:text-gray-400">
<?= esc($item['slug']) ?>
</span>
</div>
</div>
</td>
<td class="px-5 py-4 sm:px-6">
<div class="flex items-center">
<?php if ($item['status'] === 'published'): ?>
<p class="rounded-full bg-success-50 px-2 py-0.5 text-xs font-medium text-success-700 dark:bg-success-500/15 dark:text-success-500">
Published
</p>
<?php else: ?>
<p class="rounded-full bg-warning-50 px-2 py-0.5 text-xs font-medium text-warning-700 dark:bg-warning-500/15 dark:text-warning-400">
Draft
</p>
<?php endif; ?>
</div>
</td>
<td class="px-5 py-4 sm:px-6">
<div class="flex items-center">
<p class="text-gray-500 text-sm dark:text-gray-400">
<?= esc($item['creator_name'] ?? 'Unknown') ?>
</p>
</div>
</td>
<td class="px-5 py-4 sm:px-6">
<div class="flex items-center">
<p class="text-gray-500 text-sm dark:text-gray-400">
<?= date('d M Y', strtotime($item['created_at'])) ?>
</p>
</div>
</td>
<td class="px-5 py-4 sm:px-6">
<div class="flex items-center gap-2">
<a
href="<?= base_url('admin/news/edit/' . $item['id']) ?>"
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03]"
title="Edit"
>
<i class="fe fe-edit text-sm"></i>
<span class="hidden sm:inline">Edit</span>
</a>
<button
type="button"
onclick="confirmDelete(<?= $item['id'] ?>)"
class="inline-flex items-center justify-center gap-1.5 rounded-lg border border-error-300 bg-white px-3 py-1.5 text-sm font-medium text-error-700 shadow-theme-xs hover:bg-error-50 dark:border-error-700 dark:bg-gray-800 dark:text-error-400 dark:hover:bg-error-900/20"
title="Hapus"
>
<i class="fe fe-trash-2 text-sm"></i>
<span class="hidden sm:inline">Hapus</span>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php if ($pager->hasMore() || $pager->getCurrentPage() > 1): ?>
<div class="flex items-center justify-between border-t border-gray-100 px-5 py-4 dark:border-gray-800 sm:px-6">
<div class="text-sm text-gray-500 dark:text-gray-400">
Menampilkan <?= count($news) ?> dari <?= $pager->getTotal() ?> berita
</div>
<div class="flex items-center gap-2">
<?= $pager->links() ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Confirmation Modal -->
<div id="confirmModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/50">
<div class="rounded-lg border border-gray-200 bg-white p-6 dark:border-gray-800 dark:bg-gray-900 w-full max-w-md">
<h3 class="text-lg font-semibold text-gray-800 dark:text-white mb-2" id="confirmModalTitle">
Hapus Berita
</h3>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4" id="confirmModalMessage">
Apakah Anda yakin ingin menghapus berita ini? Tindakan ini tidak dapat dibatalkan.
</p>
<div class="flex items-center gap-3 pt-4">
<button
type="button"
id="confirmModalButton"
class="inline-flex items-center justify-center gap-2 rounded-lg bg-error-500 px-4 py-2.5 text-sm font-medium text-white shadow-theme-xs hover:bg-error-600"
>
Ya, Hapus
</button>
<button
type="button"
onclick="closeConfirmModal()"
class="inline-flex items-center justify-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03]"
>
Batal
</button>
</div>
</div>
</div>
<!-- Delete Form -->
<form id="deleteForm" method="post" action="" style="display: none;">
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" />
</form>
<script>
let confirmCallback = null;
function showConfirmModal(title, message, buttonText, buttonClass, callback) {
document.getElementById('confirmModalTitle').textContent = title;
document.getElementById('confirmModalMessage').textContent = message;
const confirmBtn = document.getElementById('confirmModalButton');
confirmBtn.textContent = buttonText;
confirmBtn.className = `inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-sm font-medium text-white shadow-theme-xs ${buttonClass}`;
confirmCallback = callback;
document.getElementById('confirmModal').classList.remove('hidden');
document.getElementById('confirmModal').classList.add('flex');
}
function closeConfirmModal() {
document.getElementById('confirmModal').classList.add('hidden');
document.getElementById('confirmModal').classList.remove('flex');
confirmCallback = null;
}
function confirmDelete(id) {
showConfirmModal(
'Hapus Berita',
'Apakah Anda yakin ingin menghapus berita ini? Tindakan ini tidak dapat dibatalkan.',
'Ya, Hapus',
'bg-error-500 hover:bg-error-600',
function() {
const form = document.getElementById('deleteForm');
form.action = '<?= base_url('admin/news/delete/') ?>' + id;
form.submit();
}
);
}
// Handle confirm button click
document.getElementById('confirmModalButton').addEventListener('click', function() {
if (confirmCallback) {
confirmCallback();
closeConfirmModal();
}
});
// Close modal on outside click
document.getElementById('confirmModal')?.addEventListener('click', function(e) {
if (e.target === this) {
closeConfirmModal();
}
});
</script>
<?= $this->endSection() ?>