GeoLite/categories.php

422 lines
18 KiB
PHP

<?php
require_once 'incl/Auth.php';
require_once 'incl/Database.php';
// Check if user is logged in and is admin
if (!isLoggedIn() || !isAdmin()) {
header('Location: login.php');
exit;
}
// Load brand settings
require_once 'incl/const.php';
require_once 'incl/db.php';
require_once 'incl/Settings.php';
$brand = [
'site_name' => 'GeoLite',
'logo_url' => null,
'primary_color' => '#667eea',
'hero_image' => null,
'footer_text' => '© ' . date('Y') . ' GeoLite'
];
// Load settings from database
$settingsService = new Settings($pdo, 'assets/brand', 'assets/brand');
$brand = array_merge($brand, $settingsService->load());
$message = '';
$error = '';
// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['action'])) {
switch ($_POST['action']) {
case 'create':
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$color = trim($_POST['color']);
$icon = trim($_POST['icon']);
if (empty($name)) {
$error = 'Category name is required.';
} else {
try {
createCategory($name, $description, $color, $icon);
$message = 'Category created successfully!';
} catch (Exception $e) {
$error = 'Error creating category: ' . $e->getMessage();
}
}
break;
case 'update':
$id = (int)$_POST['id'];
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$color = trim($_POST['color']);
$icon = trim($_POST['icon']);
if (empty($name)) {
$error = 'Category name is required.';
} else {
try {
if (updateCategory($id, $name, $description, $color, $icon)) {
$message = 'Category updated successfully!';
} else {
$error = 'Error updating category.';
}
} catch (Exception $e) {
$error = 'Error updating category: ' . $e->getMessage();
}
}
break;
case 'delete':
$id = (int)$_POST['id'];
try {
if (deleteCategory($id)) {
$message = 'Category deleted successfully!';
} else {
$error = 'Error deleting category.';
}
} catch (Exception $e) {
$error = 'Error deleting category: ' . $e->getMessage();
}
break;
}
}
}
// Get all categories
try {
$categories = getAllCategories();
} catch (Exception $e) {
$error = 'Error loading categories: ' . $e->getMessage();
$categories = [];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Categories - <?= htmlspecialchars($brand['site_name']) ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
:root {
--brand-primary: <?= htmlspecialchars($brand['primary_color']) ?>;
}
.brand-header {
background: linear-gradient(135deg, var(--brand-primary) 0%, #667eea 100%);
color: white;
padding: 1rem 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.brand-logo {
width: 32px;
height: 32px;
border-radius: 8px;
}
.brand-bar {
height: 4px;
background: linear-gradient(90deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.1) 100%);
}
.category-card {
border: 1px solid #e9ecef;
border-radius: 12px;
transition: all 0.3s ease;
overflow: hidden;
}
.category-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
}
.category-header {
padding: 1rem;
border-bottom: 1px solid #e9ecef;
background: #f8f9fa;
}
.category-icon {
width: 40px;
height: 40px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.2rem;
}
.color-preview {
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid #fff;
box-shadow: 0 0 0 1px #dee2e6;
}
.btn-category {
background: var(--brand-primary);
border: none;
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
transition: all 0.3s ease;
}
.btn-category:hover {
background: #5a67d8;
color: white;
transform: translateY(-1px);
}
.modal-header {
background: var(--brand-primary);
color: white;
}
.form-control:focus {
border-color: var(--brand-primary);
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
.btn-primary {
background: var(--brand-primary);
border-color: var(--brand-primary);
}
.btn-primary:hover {
background: #5a67d8;
border-color: #5a67d8;
}
.site-footer {
color: #6b7280;
background: rgba(255,255,255,0.85);
backdrop-filter: blur(4px);
}
</style>
</head>
<body>
<!-- Set header variables for the include -->
<?php
$headerTitle = 'Categories';
$headerSubtitle = 'Manage categories for organizing your maps and content';
$headerIcon = 'tags';
include 'incl/header.php';
?>
<div class="container-fluid" style="max-width: 98%; padding: 20px;">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2><i class="bi bi-tags"></i> Categories</h2>
<p class="text-muted">Manage categories for organizing your maps and content</p>
</div>
<button class="btn btn-category" data-bs-toggle="modal" data-bs-target="#categoryModal">
<i class="bi bi-plus-circle"></i> Add Category
</button>
</div>
<?php if ($message): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="bi bi-check-circle"></i> <?= htmlspecialchars($message) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle"></i> <?= htmlspecialchars($error) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="row">
<?php foreach ($categories as $category): ?>
<div class="col-md-6 col-lg-4 mb-4">
<div class="category-card">
<div class="category-header">
<div class="d-flex align-items-center">
<div class="category-icon me-3" style="background-color: <?= htmlspecialchars($category['color']) ?>">
<i class="<?= htmlspecialchars($category['icon']) ?>"></i>
</div>
<div class="flex-grow-1">
<h5 class="mb-1"><?= htmlspecialchars($category['name']) ?></h5>
<div class="d-flex align-items-center">
<div class="color-preview me-2" style="background-color: <?= htmlspecialchars($category['color']) ?>"></div>
<small class="text-muted"><?= htmlspecialchars($category['icon']) ?></small>
</div>
</div>
</div>
</div>
<div class="p-3">
<p class="text-muted mb-3"><?= htmlspecialchars($category['description']) ?></p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">
Created: <?= date('M j, Y', strtotime($category['created_at'])) ?>
</small>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="editCategory(<?= htmlspecialchars(json_encode($category)) ?>)">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-outline-danger" onclick="deleteCategory(<?= $category['id'] ?>, '<?= htmlspecialchars($category['name']) ?>')">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if (empty($categories)): ?>
<div class="text-center py-5">
<i class="bi bi-tags text-muted" style="font-size: 3rem;"></i>
<h4 class="text-muted mt-3">No categories found</h4>
<p class="text-muted">Create your first category to get started.</p>
<button class="btn btn-category" data-bs-toggle="modal" data-bs-target="#categoryModal">
<i class="bi bi-plus-circle"></i> Add Category
</button>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Category Modal -->
<div class="modal fade" id="categoryModal" tabindex="-1" aria-labelledby="categoryModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="categoryModalLabel">Add Category</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="POST" id="categoryForm">
<div class="modal-body">
<input type="hidden" name="action" id="formAction" value="create">
<input type="hidden" name="id" id="categoryId">
<div class="mb-3">
<label for="name" class="form-label">Category Name *</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="3"></textarea>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="color" class="form-label">Color</label>
<input type="color" class="form-control form-control-color" id="color" name="color" value="#667eea">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="icon" class="form-label">Icon</label>
<select class="form-select" id="icon" name="icon">
<option value="bi-tag">Tag</option>
<option value="bi-building">Building</option>
<option value="bi-car-front">Transportation</option>
<option value="bi-droplet">Water</option>
<option value="bi-tree">Land Use</option>
<option value="bi-mountain">Elevation</option>
<option value="bi-people">Population</option>
<option value="bi-flower1">Environment</option>
<option value="bi-lightning">Utilities</option>
<option value="bi-hospital">Emergency</option>
<option value="bi-tree-fill">Recreation</option>
<option value="bi-shop">Economic</option>
<option value="bi-cloud-sun">Weather</option>
<option value="bi-gear">Infrastructure</option>
<option value="bi-diagram-2">Boundaries</option>
</select>
</div>
</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 Category</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Confirm Delete</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete the category "<span id="deleteCategoryName"></span>"?</p>
<p class="text-muted">This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" id="deleteCategoryId">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</div>
</div>
</div>
<!-- Site Footer -->
<footer class="site-footer mt-5 py-4">
<div class="container text-center">
<p class="mb-0"><?= htmlspecialchars($brand['footer_text']) ?></p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function editCategory(category) {
document.getElementById('categoryModalLabel').textContent = 'Edit Category';
document.getElementById('formAction').value = 'update';
document.getElementById('categoryId').value = category.id;
document.getElementById('name').value = category.name;
document.getElementById('description').value = category.description || '';
document.getElementById('color').value = category.color;
document.getElementById('icon').value = category.icon;
new bootstrap.Modal(document.getElementById('categoryModal')).show();
}
function deleteCategory(id, name) {
document.getElementById('deleteCategoryId').value = id;
document.getElementById('deleteCategoryName').textContent = name;
new bootstrap.Modal(document.getElementById('deleteModal')).show();
}
// Reset form when modal is hidden
document.getElementById('categoryModal').addEventListener('hidden.bs.modal', function () {
document.getElementById('categoryForm').reset();
document.getElementById('categoryModalLabel').textContent = 'Add Category';
document.getElementById('formAction').value = 'create';
document.getElementById('categoryId').value = '';
});
</script>
</body>
</html>