<?php
// Include database configuration
require_once '../admin/database/db_config.php';
// Check if icon_class column exists in the categories table
$iconClassExists = false;
$result = $conn->query("SHOW COLUMNS FROM categories");
if ($result) {
while ($row = $result->fetch_assoc()) {
if ($row['Field'] === 'icon_class') {
$iconClassExists = true;
break;
}
}
// Add icon_class column if it doesn't exist
if (!$iconClassExists) {
$alterResult = $conn->query("ALTER TABLE categories ADD COLUMN icon_class VARCHAR(50) DEFAULT 'fas fa-folder' AFTER description");
if (!$alterResult) {
die("Failed to add icon_class column to categories table: " . $conn->error);
}
}
}
// Include header
include_once 'includes/header.php';
// Define actions
$action = isset($_GET['action']) ? $_GET['action'] : 'list';
$category_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$msg = '';
$error = '';
// Process form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
// Process category creation
if (isset($_POST['add_category'])) {
$name = $conn->real_escape_string($_POST['name']);
$description = $conn->real_escape_string($_POST['description']);
// Check if category name already exists
$check_query = $conn->prepare("SELECT id FROM categories WHERE name = ?");
$check_query->bind_param("s", $name);
$check_query->execute();
$result = $check_query->get_result();
if ($result->num_rows > 0) {
$error = "A category with this name already exists!";
} else {
// Handle icon upload
$icon_class = $conn->real_escape_string($_POST['icon_class']);
// Insert category data
$stmt = $conn->prepare("INSERT INTO categories (name, description, icon_class, created_at) VALUES (?, ?, ?, NOW())");
$stmt->bind_param("sss", $name, $description, $icon_class);
if ($stmt->execute()) {
$msg = "Category added successfully!";
$action = 'list'; // Redirect to list view
} else {
$error = "Error adding category: " . $stmt->error;
}
$stmt->close();
}
}
// Process category update
elseif (isset($_POST['update_category'])) {
$id = (int)$_POST['id'];
$name = $conn->real_escape_string($_POST['name']);
$description = $conn->real_escape_string($_POST['description']);
$icon_class = $conn->real_escape_string($_POST['icon_class']);
// Check if category name already exists (excluding current category)
$check_query = $conn->prepare("SELECT id FROM categories WHERE name = ? AND id != ?");
$check_query->bind_param("si", $name, $id);
$check_query->execute();
$result = $check_query->get_result();
if ($result->num_rows > 0) {
$error = "A category with this name already exists!";
} else {
// Update category data
$stmt = $conn->prepare("UPDATE categories SET name = ?, description = ?, icon_class = ?, updated_at = NOW() WHERE id = ?");
$stmt->bind_param("sssi", $name, $description, $icon_class, $id);
if ($stmt->execute()) {
$msg = "Category updated successfully!";
$action = 'list'; // Redirect to list view
} else {
$error = "Error updating category: " . $stmt->error;
}
$stmt->close();
}
}
// Process category deletion
elseif (isset($_POST['delete_category'])) {
$id = (int)$_POST['id'];
// Check if category has courses
$check_query = $conn->prepare("SELECT COUNT(*) as course_count FROM courses WHERE category = ?");
$check_query->bind_param("i", $id);
$check_query->execute();
$result = $check_query->get_result();
$row = $result->fetch_assoc();
if ($row['course_count'] > 0) {
$error = "Cannot delete this category because it has courses assigned to it. Please reassign the courses first.";
} else {
// Delete the category
if ($conn->query("DELETE FROM categories WHERE id = $id")) {
$msg = "Category deleted successfully!";
} else {
$error = "Error deleting category: " . $conn->error;
}
}
}
} catch (Exception $e) {
$error = "An error occurred: " . $e->getMessage();
}
}
?>
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="index.php">Dashboard</a></li>
<li class="breadcrumb-item active">Categories</li>
<?php if ($action === 'add'): ?>
<li class="breadcrumb-item active">Add New Category</li>
<?php elseif ($action === 'edit'): ?>
<li class="breadcrumb-item active">Edit Category</li>
<?php endif; ?>
</ol>
</nav>
<!-- Page Title and Action Buttons -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0 text-gray-800">
<?php
if ($action === 'add') echo 'Add New Category';
elseif ($action === 'edit') echo 'Edit Category';
else echo 'Category Management';
?>
</h1>
<?php if ($action === 'list'): ?>
<a href="?action=add" class="btn btn-primary">
<i class="fas fa-plus me-1"></i> Add New Category
</a>
<?php endif; ?>
</div>
<!-- Alerts for success and error messages -->
<?php if ($msg): ?>
<div class="alert alert-success alert-dismissible fade show mb-4 animate__animated animate__fadeIn" role="alert">
<?php echo $msg; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger alert-dismissible fade show mb-4 animate__animated animate__fadeIn" role="alert">
<?php echo $error; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<!-- Main Content -->
<?php if ($action === 'list'): ?>
<!-- Category List View -->
<div class="card mb-4 shadow-sm">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Categories</h5>
<form class="d-flex" method="get">
<input type="hidden" name="action" value="list">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search categories..." name="search"
value="<?php echo isset($_GET['search']) ? htmlspecialchars($_GET['search']) : ''; ?>">
<button class="btn btn-outline-primary" type="submit">
<i class="fas fa-search"></i>
</button>
</div>
</form>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th width="60">ID</th>
<th width="60">Icon</th>
<th>Name</th>
<th>Description</th>
<th>Courses</th>
<th width="150">Actions</th>
</tr>
</thead>
<tbody>
<?php
// Build the query with search functionality
$search_condition = '';
if (isset($_GET['search']) && !empty($_GET['search'])) {
$search = $conn->real_escape_string($_GET['search']);
$search_condition = " WHERE c.name LIKE '%$search%' OR c.description LIKE '%$search%'";
}
$sql = "SELECT c.*, COUNT(co.id) as course_count
FROM categories c
LEFT JOIN courses co ON c.id = co.category
$search_condition
GROUP BY c.id
ORDER BY c.name ASC";
$result = $conn->query($sql);
if ($result && $result->num_rows > 0) {
while ($category = $result->fetch_assoc()) {
?>
<tr>
<td><?php echo $category['id']; ?></td>
<td>
<div class="category-icon">
<i class="<?php echo $category['icon_class']; ?>"></i>
</div>
</td>
<td>
<strong><?php echo htmlspecialchars($category['name']); ?></strong>
</td>
<td>
<?php
echo (strlen($category['description']) > 100)
? htmlspecialchars(substr($category['description'], 0, 100)) . '...'
: htmlspecialchars($category['description']);
?>
</td>
<td>
<span class="badge rounded-pill bg-primary"><?php echo $category['course_count']; ?></span>
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="?action=edit&id=<?php echo $category['id']; ?>" class="btn btn-primary" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-danger delete-category-btn"
data-id="<?php echo $category['id']; ?>"
data-name="<?php echo htmlspecialchars($category['name']); ?>"
data-courses="<?php echo $category['course_count']; ?>"
title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="6" class="text-center py-4">
<div class="d-flex flex-column align-items-center">
<i class="fas fa-folder fa-3x text-muted mb-3"></i>
<h5>No categories found</h5>
<p class="text-muted">Start by adding your first category</p>
<a href="?action=add" class="btn btn-primary btn-sm mt-2">
<i class="fas fa-plus me-1"></i> Add New Category
</a>
</div>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Delete Category Modal -->
<div class="modal fade" id="deleteCategoryModal" tabindex="-1" aria-labelledby="deleteCategoryModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteCategoryModalLabel">Confirm Deletion</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="categoryToDelete"></span>?</p>
<div id="categoryHasCourses" class="alert alert-warning d-none">
<i class="fas fa-exclamation-triangle me-2"></i>
This category has <span id="courseCount"></span> courses. You cannot delete it until you reassign these courses to another category.
</div>
<p id="deleteCategoryWarning" class="text-danger">This action cannot be undone.</p>
</div>
<div class="modal-footer">
<form method="post">
<input type="hidden" name="id" id="deleteCategoryId">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" name="delete_category" class="btn btn-danger" id="deleteCategoryBtn">Delete Category</button>
</form>
</div>
</div>
</div>
</div>
<?php elseif ($action === 'add' || $action === 'edit'): ?>
<!-- Add/Edit Category Form -->
<?php
$category = [
'id' => '',
'name' => '',
'description' => '',
'icon_class' => 'fas fa-folder'
];
if ($action === 'edit' && $category_id > 0) {
$result = $conn->query("SELECT * FROM categories WHERE id = $category_id");
if ($result->num_rows > 0) {
$category = $result->fetch_assoc();
} else {
echo '<div class="alert alert-danger">Category not found!</div>';
exit;
}
}
?>
<div class="card shadow-sm">
<div class="card-header">
<h5 class="mb-0"><?php echo ($action === 'add') ? 'Add New Category' : 'Edit Category'; ?></h5>
</div>
<div class="card-body">
<form method="post" class="needs-validation" novalidate>
<?php if ($action === 'edit'): ?>
<input type="hidden" name="id" value="<?php echo $category['id']; ?>">
<?php endif; ?>
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label for="name" class="form-label">Category Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($category['name']); ?>" required>
<div class="invalid-feedback">Please enter a category name.</div>
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="4"><?php echo htmlspecialchars($category['description']); ?></textarea>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="icon_class" class="form-label">Icon Class <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-text"><i id="iconPreview" class="<?php echo $category['icon_class']; ?>"></i></span>
<input type="text" class="form-control" id="icon_class" name="icon_class" value="<?php echo htmlspecialchars($category['icon_class']); ?>" placeholder="fas fa-folder" required>
<div class="invalid-feedback">Please enter an icon class.</div>
</div>
<div class="mt-3">
<p class="mb-2"><small>Choose an icon:</small></p>
<div class="d-flex flex-wrap gap-2 icon-picker">
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-book"><i class="fas fa-book"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-code"><i class="fas fa-code"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-laptop"><i class="fas fa-laptop"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-paint-brush"><i class="fas fa-paint-brush"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-camera"><i class="fas fa-camera"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-chart-bar"><i class="fas fa-chart-bar"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-music"><i class="fas fa-music"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-language"><i class="fas fa-language"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-calculator"><i class="fas fa-calculator"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-heartbeat"><i class="fas fa-heartbeat"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-microscope"><i class="fas fa-microscope"></i></button>
<button type="button" class="btn btn-outline-secondary icon-btn" data-icon="fas fa-globe"><i class="fas fa-globe"></i></button>
</div>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between border-top pt-3 mt-3">
<a href="categories.php" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i> Cancel
</a>
<button type="submit" name="<?php echo ($action === 'add') ? 'add_category' : 'update_category'; ?>" class="btn btn-primary">
<?php echo ($action === 'add') ? '<i class="fas fa-plus me-1"></i> Add Category' : '<i class="fas fa-save me-1"></i> Update Category'; ?>
</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
<style>
.category-icon {
width: 40px;
height: 40px;
background-color: var(--bs-light);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
color: var(--bs-primary);
}
.icon-picker .icon-btn {
width: 40px;
height: 40px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
}
.icon-picker .icon-btn.active {
background-color: var(--bs-primary);
color: white;
border-color: var(--bs-primary);
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Delete category modal functionality
const deleteBtns = document.querySelectorAll('.delete-category-btn');
const deleteCategoryModal = document.getElementById('deleteCategoryModal');
const categoryToDelete = document.getElementById('categoryToDelete');
const deleteCategoryId = document.getElementById('deleteCategoryId');
const categoryHasCourses = document.getElementById('categoryHasCourses');
const courseCount = document.getElementById('courseCount');
const deleteCategoryBtn = document.getElementById('deleteCategoryBtn');
if (deleteBtns.length > 0) {
deleteBtns.forEach(btn => {
btn.addEventListener('click', function() {
const categoryId = this.getAttribute('data-id');
const categoryName = this.getAttribute('data-name');
const courses = parseInt(this.getAttribute('data-courses'));
categoryToDelete.textContent = categoryName;
deleteCategoryId.value = categoryId;
// Disable delete button if category has courses
if (courses > 0) {
categoryHasCourses.classList.remove('d-none');
courseCount.textContent = courses;
deleteCategoryBtn.disabled = true;
} else {
categoryHasCourses.classList.add('d-none');
deleteCategoryBtn.disabled = false;
}
const modal = new bootstrap.Modal(deleteCategoryModal);
modal.show();
});
});
}
// Icon preview and selection
const iconInput = document.getElementById('icon_class');
const iconPreview = document.getElementById('iconPreview');
const iconBtns = document.querySelectorAll('.icon-btn');
if (iconInput && iconPreview) {
iconInput.addEventListener('input', function() {
iconPreview.className = this.value;
});
iconBtns.forEach(btn => {
btn.addEventListener('click', function() {
const iconClass = this.getAttribute('data-icon');
iconInput.value = iconClass;
iconPreview.className = iconClass;
// Update active state
iconBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
});
// Set active state for current icon
if (iconInput.value === btn.getAttribute('data-icon')) {
btn.classList.add('active');
}
});
}
// Form validation
const forms = document.querySelectorAll('.needs-validation');
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
});
</script>
<?php
// Include footer
include_once 'includes/footer.php';
?>