39301-vm/admin.php
2026-03-25 08:05:46 +00:00

572 lines
26 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/layout.php';
library_bootstrap();
$errors = [];
$successMessage = '';
// Handle POST request
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
try {
if ($action === 'upload_document') {
$documentId = library_create_document($_POST, $_FILES['document_file'] ?? []);
library_set_flash('success', 'Document uploaded successfully.');
header('Location: /admin.php?created=' . $documentId . '#catalog-manager');
exit;
} elseif ($action === 'create_category') {
$nameEn = trim($_POST['name_en'] ?? '');
$nameAr = trim($_POST['name_ar'] ?? '');
if (!$nameEn || !$nameAr) {
throw new RuntimeException('Both English and Arabic names are required for Category.');
}
library_create_category($nameEn, $nameAr);
library_set_flash('success', 'Category created successfully.');
header('Location: /admin.php');
exit;
} elseif ($action === 'update_category') {
$nameEn = trim($_POST['name_en'] ?? '');
$nameAr = trim($_POST['name_ar'] ?? '');
if (!$id || !$nameEn || !$nameAr) {
throw new RuntimeException('ID, English name, and Arabic name are required.');
}
library_update_category($id, $nameEn, $nameAr);
library_set_flash('success', 'Category updated successfully.');
header('Location: /admin.php');
exit;
} elseif ($action === 'delete_category') {
if (!$id) {
throw new RuntimeException('Invalid Category ID.');
}
library_delete_category($id);
library_set_flash('success', 'Category deleted successfully.');
header('Location: /admin.php');
exit;
} elseif ($action === 'create_subcategory') {
$catId = (int)($_POST['category_id'] ?? 0);
$nameEn = trim($_POST['name_en'] ?? '');
$nameAr = trim($_POST['name_ar'] ?? '');
if (!$catId || !$nameEn || !$nameAr) {
throw new RuntimeException('Category, English name, and Arabic name are required.');
}
library_create_subcategory($catId, $nameEn, $nameAr);
library_set_flash('success', 'Subcategory created successfully.');
header('Location: /admin.php');
exit;
} elseif ($action === 'update_subcategory') {
$catId = (int)($_POST['category_id'] ?? 0);
$nameEn = trim($_POST['name_en'] ?? '');
$nameAr = trim($_POST['name_ar'] ?? '');
if (!$id || !$catId || !$nameEn || !$nameAr) {
throw new RuntimeException('ID, Category, English name, and Arabic name are required.');
}
library_update_subcategory($id, $catId, $nameEn, $nameAr);
library_set_flash('success', 'Subcategory updated successfully.');
header('Location: /admin.php');
exit;
} elseif ($action === 'delete_subcategory') {
if (!$id) {
throw new RuntimeException('Invalid Subcategory ID.');
}
library_delete_subcategory($id);
library_set_flash('success', 'Subcategory deleted successfully.');
header('Location: /admin.php');
exit;
}
} catch (Throwable $exception) {
$errors[] = $exception->getMessage();
}
}
$documents = library_fetch_documents(false, []);
$metrics = library_catalog_metrics();
$categories = library_get_categories();
// Fetch all subcategories to pass to JS for filtering
$allSubcategories = library_get_subcategories(null);
$flashes = library_get_flashes();
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Admin Studio · Nabd Library</title>
<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="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<style>
.admin-sidebar {
width: 260px;
height: 100vh;
position: sticky;
top: 0;
background: #fff;
border-right: 1px solid #dee2e6;
padding: 20px;
}
</style>
</head>
<body class="bg-light">
<div class="d-flex">
<aside class="admin-sidebar">
<h4 class="mb-4">Admin Dashboard</h4>
<nav class="nav flex-column gap-2">
<a class="nav-link text-dark bg-light rounded" href="/admin.php">Catalog Manager</a>
<a class="nav-link text-secondary" href="/index.php">Return to site</a>
</nav>
<hr class="my-4">
<h6 class="text-uppercase text-muted small">Metadata</h6>
<div class="d-grid gap-2">
<button type="button" class="btn btn-outline-secondary btn-sm text-start" onclick="openCreateCategoryModal()">
+ New Category
</button>
<button type="button" class="btn btn-outline-secondary btn-sm text-start" onclick="openCreateSubcategoryModal()">
+ New Subcategory
</button>
</div>
</aside>
<main class="flex-grow-1 p-4">
<h1>Catalog Manager</h1>
<p class="text-secondary">Upload manuscripts and manage permissions.</p>
<?php foreach ($flashes as $flash): ?>
<div class="alert alert-<?= $flash['type'] === 'error' ? 'danger' : 'success' ?> alert-dismissible fade show" role="alert">
<?= h($flash['message']) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endforeach; ?>
<?php if ($errors): ?>
<div class="alert alert-danger"><?= h(implode(' ', $errors)) ?></div>
<?php endif; ?>
<form method="post" action="/admin.php#catalog-manager" enctype="multipart/form-data" class="panel border p-4 bg-white rounded shadow-sm mb-5">
<input type="hidden" name="action" value="upload_document">
<h4 class="mb-3">Upload New Document</h4>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Title (English)</label>
<input class="form-control" name="title_en" type="text" required>
</div>
<div class="col-md-6">
<label class="form-label">Title (Arabic)</label>
<input class="form-control" name="title_ar" type="text" dir="rtl">
</div>
<div class="col-md-6">
<label class="form-label">Category</label>
<div class="input-group">
<select class="form-select" name="category_id" id="docCategorySelect" required>
<option value="">Select Category...</option>
<?php foreach ($categories as $cat): ?>
<option value="<?= $cat['id'] ?>">
<?= h($cat['name_en']) ?> / <?= h($cat['name_ar']) ?>
</option>
<?php endforeach; ?>
</select>
<button class="btn btn-outline-secondary" type="button" onclick="openCreateCategoryModal()">+</button>
</div>
</div>
<div class="col-md-6">
<label class="form-label">Sub Category</label>
<div class="input-group">
<select class="form-select" name="subcategory_id" id="docSubcategorySelect">
<option value="">Select Category First</option>
</select>
<button class="btn btn-outline-secondary" type="button" onclick="openCreateSubcategoryModal()">+</button>
</div>
</div>
<div class="col-md-12">
<label class="form-label">Visibility</label>
<select class="form-select" name="visibility">
<option value="public">Public</option>
<option value="private">Private</option>
</select>
</div>
<div class="col-12">
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="allow_download" value="1">
<label class="form-check-label">Allow Download</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="allow_print" value="1">
<label class="form-check-label">Allow Print</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="allow_copy" value="1">
<label class="form-check-label">Allow Copy</label>
</div>
</div>
<div class="col-12">
<label class="form-label">Document file</label>
<input class="form-control" name="document_file" type="file" required>
</div>
</div>
<button class="btn btn-primary mt-3" type="submit">Upload manuscript</button>
</form>
<h3 class="mb-3">Recent Documents</h3>
<div class="table-responsive bg-white border rounded mb-5">
<table class="table mb-0">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Title</th>
<th>Category</th>
<th>Visibility</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($documents)): ?>
<tr><td colspan="5" class="text-center py-4 text-muted">No documents found.</td></tr>
<?php else: ?>
<?php foreach (array_slice($documents, 0, 10) as $doc): ?>
<tr>
<td><?= $doc['id'] ?></td>
<td>
<?= h($doc['title_en']) ?><br>
<small class="text-muted"><?= h($doc['title_ar']) ?></small>
</td>
<td>
<?= h($doc['cat_en'] ?? $doc['category']) ?>
<?php if (!empty($doc['sub_en'])): ?>
<small class="text-muted"> > <?= h($doc['sub_en']) ?></small>
<?php elseif (!empty($doc['sub_category'])): ?>
<small class="text-muted"> > <?= h($doc['sub_category']) ?></small>
<?php endif; ?>
</td>
<td>
<span class="badge bg-<?= $doc['visibility'] === 'public' ? 'success' : 'secondary' ?>">
<?= h($doc['visibility']) ?>
</span>
</td>
<td>
<a href="/document.php?id=<?= $doc['id'] ?>" class="btn btn-sm btn-outline-primary" target="_blank">View</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="row">
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-3">
<h3>Categories</h3>
<button class="btn btn-sm btn-outline-primary" onclick="openCreateCategoryModal()">+ Add New</button>
</div>
<div class="table-responsive bg-white border rounded">
<table class="table mb-0">
<thead class="table-light">
<tr>
<th>Name</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($categories)): ?>
<tr><td colspan="2" class="text-center py-4 text-muted">No categories.</td></tr>
<?php else: ?>
<?php foreach ($categories as $cat): ?>
<tr>
<td>
<?= h($cat['name_en']) ?><br>
<small class="text-muted"><?= h($cat['name_ar']) ?></small>
</td>
<td class="text-end">
<button class="btn btn-sm btn-outline-secondary" onclick="openEditCategoryModal(<?= $cat['id'] ?>, '<?= h($cat['name_en']) ?>', '<?= h($cat['name_ar']) ?>')">Edit</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteCategory(<?= $cat['id'] ?>)">Delete</button>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-3">
<h3>Subcategories</h3>
<button class="btn btn-sm btn-outline-primary" onclick="openCreateSubcategoryModal()">+ Add New</button>
</div>
<div class="table-responsive bg-white border rounded">
<table class="table mb-0">
<thead class="table-light">
<tr>
<th>Name</th>
<th>Parent</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($allSubcategories)): ?>
<tr><td colspan="3" class="text-center py-4 text-muted">No subcategories.</td></tr>
<?php else: ?>
<?php foreach ($allSubcategories as $sub):
$parentName = 'Unknown';
foreach ($categories as $c) {
if ($c['id'] == $sub['category_id']) {
$parentName = $c['name_en'];
break;
}
}
?>
<tr>
<td>
<?= h($sub['name_en']) ?><br>
<small class="text-muted"><?= h($sub['name_ar']) ?></small>
</td>
<td><small><?= h($parentName) ?></small></td>
<td class="text-end">
<button class="btn btn-sm btn-outline-secondary" onclick="openEditSubcategoryModal(<?= $sub['id'] ?>, <?= $sub['category_id'] ?>, '<?= h($sub['name_en']) ?>', '<?= h($sub['name_ar']) ?>')">Edit</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteSubcategory(<?= $sub['id'] ?>)">Delete</button>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</main>
</div>
<!-- Category Modal -->
<div class="modal fade" id="categoryModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" action="/admin.php" id="categoryForm">
<input type="hidden" name="action" id="cat_action" value="create_category">
<input type="hidden" name="id" id="cat_id" value="">
<div class="modal-header">
<h5 class="modal-title" id="categoryModalTitle">Add New Category</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Name (English)</label>
<div class="input-group">
<input type="text" class="form-control" name="name_en" id="cat_name_en" required>
<button class="btn btn-outline-secondary" type="button" onclick="translateText('cat_name_en', 'cat_name_ar', 'Arabic')" title="Translate to Arabic">
<i class="bi bi-translate"></i>
</button>
</div>
</div>
<div class="mb-3">
<label class="form-label">Name (Arabic)</label>
<div class="input-group">
<input type="text" class="form-control" name="name_ar" id="cat_name_ar" dir="rtl" required>
<button class="btn btn-outline-secondary" type="button" onclick="translateText('cat_name_ar', 'cat_name_en', 'English')" title="Translate to English">
<i class="bi bi-translate"></i>
</button>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
<!-- Subcategory Modal -->
<div class="modal fade" id="subcategoryModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" action="/admin.php" id="subcategoryForm">
<input type="hidden" name="action" id="sub_action" value="create_subcategory">
<input type="hidden" name="id" id="sub_id" value="">
<div class="modal-header">
<h5 class="modal-title" id="subcategoryModalTitle">Add New Subcategory</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Parent Category</label>
<select class="form-select" name="category_id" id="sub_category_id" required>
<option value="">Select...</option>
<?php foreach ($categories as $cat): ?>
<option value="<?= $cat['id'] ?>"><?= h($cat['name_en']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Name (English)</label>
<div class="input-group">
<input type="text" class="form-control" name="name_en" id="sub_name_en" required>
<button class="btn btn-outline-secondary" type="button" onclick="translateText('sub_name_en', 'sub_name_ar', 'Arabic')" title="Translate to Arabic">
<i class="bi bi-translate"></i>
</button>
</div>
</div>
<div class="mb-3">
<label class="form-label">Name (Arabic)</label>
<div class="input-group">
<input type="text" class="form-control" name="name_ar" id="sub_name_ar" dir="rtl" required>
<button class="btn btn-outline-secondary" type="button" onclick="translateText('sub_name_ar', 'sub_name_en', 'English')" title="Translate to English">
<i class="bi bi-translate"></i>
</button>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Confirmation Form (Hidden) -->
<form method="post" action="/admin.php" id="deleteForm">
<input type="hidden" name="action" id="deleteAction" value="">
<input type="hidden" name="id" id="deleteId" value="">
</form>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Pass PHP data to JS safely
const allSubcategories = <?= json_encode($allSubcategories) ?>;
const catSelect = document.getElementById('docCategorySelect');
const subSelect = document.getElementById('docSubcategorySelect');
const categoryModal = new bootstrap.Modal(document.getElementById('categoryModal'));
const subcategoryModal = new bootstrap.Modal(document.getElementById('subcategoryModal'));
function updateSubcategories() {
const selectedCatId = catSelect.value;
subSelect.innerHTML = '<option value="">Select Subcategory...</option>';
if (!selectedCatId) {
return;
}
// Loose equality check for ID comparison
const filtered = allSubcategories.filter(sub => sub.category_id == selectedCatId);
filtered.forEach(sub => {
const option = document.createElement('option');
option.value = sub.id;
option.textContent = sub.name_en + ' / ' + sub.name_ar;
subSelect.appendChild(option);
});
}
if (catSelect && subSelect) {
catSelect.addEventListener('change', updateSubcategories);
updateSubcategories();
}
// Modal Helpers
function openCreateCategoryModal() {
document.getElementById('categoryModalTitle').innerText = 'Add New Category';
document.getElementById('cat_action').value = 'create_category';
document.getElementById('cat_id').value = '';
document.getElementById('cat_name_en').value = '';
document.getElementById('cat_name_ar').value = '';
categoryModal.show();
}
function openEditCategoryModal(id, nameEn, nameAr) {
document.getElementById('categoryModalTitle').innerText = 'Edit Category';
document.getElementById('cat_action').value = 'update_category';
document.getElementById('cat_id').value = id;
document.getElementById('cat_name_en').value = nameEn;
document.getElementById('cat_name_ar').value = nameAr;
categoryModal.show();
}
function deleteCategory(id) {
if (confirm('Are you sure you want to delete this category? All related subcategories will also be deleted.')) {
document.getElementById('deleteAction').value = 'delete_category';
document.getElementById('deleteId').value = id;
document.getElementById('deleteForm').submit();
}
}
function openCreateSubcategoryModal() {
document.getElementById('subcategoryModalTitle').innerText = 'Add New Subcategory';
document.getElementById('sub_action').value = 'create_subcategory';
document.getElementById('sub_id').value = '';
document.getElementById('sub_category_id').value = '';
document.getElementById('sub_name_en').value = '';
document.getElementById('sub_name_ar').value = '';
subcategoryModal.show();
}
function openEditSubcategoryModal(id, catId, nameEn, nameAr) {
document.getElementById('subcategoryModalTitle').innerText = 'Edit Subcategory';
document.getElementById('sub_action').value = 'update_subcategory';
document.getElementById('sub_id').value = id;
document.getElementById('sub_category_id').value = catId;
document.getElementById('sub_name_en').value = nameEn;
document.getElementById('sub_name_ar').value = nameAr;
subcategoryModal.show();
}
function deleteSubcategory(id) {
if (confirm('Are you sure you want to delete this subcategory?')) {
document.getElementById('deleteAction').value = 'delete_subcategory';
document.getElementById('deleteId').value = id;
document.getElementById('deleteForm').submit();
}
}
async function translateText(sourceId, targetId, lang) {
const source = document.getElementById(sourceId);
const target = document.getElementById(targetId);
const text = source.value.trim();
if (!text) {
alert('Please enter text to translate.');
return;
}
const originalPlaceholder = target.placeholder;
target.placeholder = 'Translating...';
const originalOpacity = target.style.opacity;
target.style.opacity = '0.7';
try {
const response = await fetch('/api/translate.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: text, target_lang: lang })
});
if (!response.ok) throw new Error('Translation failed');
const data = await response.json();
if (data.translation) {
target.value = data.translation;
} else if (data.error) {
alert('Translation error: ' + data.error);
}
} catch (e) {
console.error(e);
alert('Translation failed. Please try again.');
} finally {
target.placeholder = originalPlaceholder;
target.style.opacity = originalOpacity;
}
}
</script>
</body>
</html>