gestione ruoli e permessi

This commit is contained in:
Flatlogic Bot 2025-11-08 20:43:02 +00:00
parent 7c2c9d57f8
commit eca44a4bc7
33 changed files with 1476 additions and 67 deletions

View File

@ -4,16 +4,35 @@ require_once 'db/config.php';
require_once 'auth-check.php'; require_once 'auth-check.php';
require_once 'auth-helpers.php'; require_once 'auth-helpers.php';
if (!can($_SESSION['user_role'], 'asset', 'create')) { if (!can($_SESSION['user_role_id'], 'asset', 'create')) {
header('Location: index.php?error=access_denied'); header('Location: index.php?error=access_denied');
exit; exit;
} }
$allowed_fields_str = can($_SESSION['user_role'], 'asset', 'create'); $allowed_fields_str = can($_SESSION['user_role_id'], 'asset', 'create');
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'status', 'location', 'manufacturer', 'model', 'purchase_date'] : explode(',', $allowed_fields_str); $allowed_fields = ($allowed_fields_str === '*') ? ['name', 'status', 'location_id', 'manufacturer', 'model', 'purchase_date', 'category_id', 'assigned_to'] : explode(',', $allowed_fields_str);
$success_message = ''; $success_message = '';
$error_message = ''; $error_message = '';
$categories = [];
$locations = [];
$users = [];
try {
$pdo = db();
// Fetch categories for dropdown
$stmt = $pdo->query("SELECT id, name FROM categories ORDER BY name");
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch locations for dropdown
$stmt = $pdo->query("SELECT id, name FROM locations ORDER BY name");
$locations = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch users for dropdown
$stmt = $pdo->query("SELECT id, name FROM users ORDER BY name");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = []; $data = [];
@ -46,9 +65,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($error_message)) { if (empty($error_message)) {
foreach ($allowed_fields as $field) { foreach ($allowed_fields as $field) {
if (isset($_POST[$field])) { if (isset($_POST[$field])) {
$data[] = $_POST[$field]; $value = $_POST[$field];
if (($field === 'category_id' || $field === 'location_id' || $field === 'assigned_to') && $value === '') {
$value = null;
}
$data[] = $value;
$columns[] = $field; $columns[] = $field;
$placeholders[] = '?';
} }
} }
@ -79,6 +101,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<meta name="description" content="Add a new asset to the inventory."> <meta name="description" content="Add a new asset to the inventory.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>"> <link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"/>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@ -122,10 +145,43 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</select> </select>
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php if (in_array('location', $allowed_fields)): ?> <?php if (in_array('category_id', $allowed_fields)): ?>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label for="location" class="form-label">Location</label> <label for="category_id" class="form-label">Category</label>
<input type="text" class="form-control" id="location" name="location"> <select class="form-select" id="category_id" name="category_id">
<option value="">No Category</option>
<?php foreach ($categories as $category): ?>
<option value="<?php echo $category['id']; ?>">
<?php echo htmlspecialchars($category['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (in_array('location_id', $allowed_fields)): ?>
<div class="col-md-6 mb-3">
<label for="location_id" class="form-label">Location</label>
<select class="form-select" id="location_id" name="location_id">
<option value="">No Location</option>
<?php foreach ($locations as $location): ?>
<option value="<?php echo $location['id']; ?>">
<?php echo htmlspecialchars($location['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (in_array('assigned_to', $allowed_fields)): ?>
<div class="col-md-6 mb-3">
<label for="assigned_to" class="form-label">Assigned To</label>
<select class="form-select" id="assigned_to" name="assigned_to">
<option value="">Unassigned</option>
<?php foreach ($users as $user): ?>
<option value="<?php echo $user['id']; ?>">
<?php echo htmlspecialchars($user['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
@ -159,6 +215,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script> <script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
<script src="assets/js/choices.js?v=<?php echo time(); ?>"></script>
<script> <script>
feather.replace(); feather.replace();
</script> </script>

94
add-category.php Normal file
View File

@ -0,0 +1,94 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'category', 'create')) {
header('Location: index.php?error=access_denied');
exit;
}
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
if (empty($name)) {
$error_message = 'Please fill in the category name.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id FROM categories WHERE name = ?');
$stmt->execute([$name]);
if ($stmt->fetch()) {
$error_message = 'A category with this name already exists.';
} else {
$sql = "INSERT INTO categories (name) VALUES (?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name]);
header("Location: categories.php?success=category_added");
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add New Category - IC-Inventory</title>
<meta name="description" content="Add a new category.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Add New Category</h1>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
<div class="surface p-4">
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<form action="add-category.php" method="post">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Category Name*</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Add Category</button>
<a href="categories.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

94
add-location.php Normal file
View File

@ -0,0 +1,94 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'location', 'create')) {
header('Location: index.php?error=access_denied');
exit;
}
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
if (empty($name)) {
$error_message = 'Please fill in the location name.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id FROM locations WHERE name = ?');
$stmt->execute([$name]);
if ($stmt->fetch()) {
$error_message = 'A location with this name already exists.';
} else {
$sql = "INSERT INTO locations (name) VALUES (?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name]);
header("Location: locations.php?success=location_added");
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add New Location - IC-Inventory</title>
<meta name="description" content="Add a new location.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Add New Location</h1>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
<div class="surface p-4">
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<form action="add-location.php" method="post">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Location Name*</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Add Location</button>
<a href="locations.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

94
add-role.php Normal file
View File

@ -0,0 +1,94 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'role', 'create')) {
header('Location: index.php?error=access_denied');
exit;
}
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
if (empty($name)) {
$error_message = 'Please fill in the role name.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id FROM roles WHERE name = ?');
$stmt->execute([$name]);
if ($stmt->fetch()) {
$error_message = 'A role with this name already exists.';
} else {
$sql = "INSERT INTO roles (name) VALUES (?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name]);
header("Location: roles.php?success=role_added");
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add New Role - IC-Inventory</title>
<meta name="description" content="Add a new role.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Add New Role</h1>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
<div class="surface p-4">
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<form action="add-role.php" method="post">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Role Name*</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Add Role</button>
<a href="roles.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

View File

@ -4,7 +4,7 @@ require_once 'db/config.php';
require_once 'auth-check.php'; require_once 'auth-check.php';
require_once 'auth-helpers.php'; require_once 'auth-helpers.php';
if (!can($_SESSION['user_role'], 'user', 'create')) { if (!can($_SESSION['user_role_id'], 'user', 'create')) {
header('Location: index.php?error=access_denied'); header('Location: index.php?error=access_denied');
exit; exit;
} }

View File

@ -1,7 +1,21 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const assignedTo = document.getElementById('assigned_to'); const assignedTo = document.getElementById('assigned_to');
if (assignedTo) { if (assignedTo) {
const choices = new Choices(assignedTo, { new Choices(assignedTo, {
removeItemButton: true,
});
}
const category = document.getElementById('category_id');
if (category) {
new Choices(category, {
removeItemButton: true,
});
}
const location = document.getElementById('location_id');
if (location) {
new Choices(location, {
removeItemButton: true, removeItemButton: true,
}); });
} }

View File

@ -1,7 +1,7 @@
<?php <?php
require_once 'db/config.php'; require_once 'db/config.php';
function can($role, $resource, $action) { function can($role_id, $resource, $action) {
static $permissions = null; static $permissions = null;
if ($permissions === null) { if ($permissions === null) {
@ -11,7 +11,7 @@ function can($role, $resource, $action) {
$all_permissions = $stmt->fetchAll(PDO::FETCH_ASSOC); $all_permissions = $stmt->fetchAll(PDO::FETCH_ASSOC);
$permissions = []; $permissions = [];
foreach ($all_permissions as $p) { foreach ($all_permissions as $p) {
$permissions[$p['role']][$p['resource']][$p['action']] = $p['fields'] ?? '*'; $permissions[$p['role_id']][$p['resource']][$p['action']] = $p['fields'] ?? '*';
} }
} catch (PDOException $e) { } catch (PDOException $e) {
// Handle database errors, maybe return false or log the error // Handle database errors, maybe return false or log the error
@ -19,9 +19,9 @@ function can($role, $resource, $action) {
} }
} }
if (isset($permissions[$role][$resource][$action])) { if (isset($permissions[$role_id][$resource][$action])) {
if (in_array($action, ['read', 'update'])) { if (in_array($action, ['read', 'update', 'create'])) {
return $permissions[$role][$resource][$action]; return $permissions[$role_id][$resource][$action];
} }
return true; return true;
} }

118
categories.php Normal file
View File

@ -0,0 +1,118 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
// Permissions check
if (!can($_SESSION['user_role_id'], 'category', 'read')) {
header('Location: index.php?error=access_denied');
exit;
}
function get_categories() {
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM categories ORDER BY name ASC");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
return ['error' => 'Database error: ' . $e->getMessage()];
}
}
$categories = get_categories();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Category Management - IC-Inventory</title>
<meta name="description" content="Category management for IC-Inventory.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Category Management</h1>
<div>
<?php if (can($_SESSION['user_role_id'], 'category', 'create')): ?>
<a href="add-category.php" class="btn btn-primary">Add New Category</a>
<?php endif; ?>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success">
<?php
if ($_GET['success'] === 'category_added') echo 'Category successfully added!';
if ($_GET['success'] === 'category_updated') echo 'Category successfully updated!';
if ($_GET['success'] === 'category_deleted') echo 'Category successfully deleted!';
?>
</div>
<?php endif; ?>
<div class="surface p-4">
<?php if (isset($categories['error'])): ?>
<div class="alert alert-danger">
<?php echo htmlspecialchars($categories['error']); ?>
</div>
<?php elseif (empty($categories)): ?>
<div class="text-center p-5">
<h4>No categories found.</h4>
<?php if (can($_SESSION['user_role_id'], 'category', 'create')): ?>
<p>Get started by adding your first category.</p>
<a href="add-category.php" class="btn btn-primary">Add Category</a>
<?php endif; ?>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($categories as $category): ?>
<tr>
<td><?php echo htmlspecialchars($category['name']); ?></td>
<td>
<?php if (can($_SESSION['user_role_id'], 'category', 'update')): ?>
<a href="edit-category.php?id=<?php echo $category['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<?php endif; ?>
<?php if (can($_SESSION['user_role_id'], 'category', 'delete')): ?>
<a href="delete-category.php?id=<?php echo $category['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this category?');">Delete</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

View File

@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS `locations` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -0,0 +1 @@
ALTER TABLE `assets` ADD COLUMN `location_id` INT NULL, ADD FOREIGN KEY (`location_id`) REFERENCES `locations`(`id`) ON DELETE SET NULL;

View File

@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS `roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Seed with existing roles
INSERT INTO `roles` (`name`) VALUES
('Admin'),
('Asset Manager'),
('IT Technician'),
('Employee');

View File

@ -0,0 +1,16 @@
-- Add role_id column
ALTER TABLE `users` ADD COLUMN `role_id` INT(11) NULL AFTER `password`;
-- Update role_id from existing role name
UPDATE `users` u
JOIN `roles` r ON u.role = r.name
SET u.role_id = r.id;
-- Make role_id not nullable
ALTER TABLE `users` MODIFY `role_id` INT(11) NOT NULL;
-- Add foreign key constraint
ALTER TABLE `users` ADD CONSTRAINT `fk_user_role` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- Drop the old role column
ALTER TABLE `users` DROP COLUMN `role`;

View File

@ -0,0 +1,22 @@
-- Add role_id column
ALTER TABLE `role_permissions` ADD COLUMN `role_id` INT(11) NULL AFTER `id`;
-- Update role_id from existing role name
UPDATE `role_permissions` rp
JOIN `roles` r ON rp.role = r.name
SET rp.role_id = r.id;
-- Make role_id not nullable
ALTER TABLE `role_permissions` MODIFY `role_id` INT(11) NOT NULL;
-- Drop the old unique key
ALTER TABLE `role_permissions` DROP INDEX `role_resource_action`;
-- Drop the old role column
ALTER TABLE `role_permissions` DROP COLUMN `role`;
-- Add new unique key with role_id
ALTER TABLE `role_permissions` ADD UNIQUE KEY `role_resource_action` (`role_id`, `resource`, `action`);
-- Add foreign key to roles table
ALTER TABLE `role_permissions` ADD CONSTRAINT `fk_permission_role` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1 @@
INSERT INTO `role_permissions` (`role_id`, `resource`, `action`) VALUES (1, 'permission', 'update');

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS `permissions`;

View File

@ -0,0 +1,12 @@
-- Grant all permissions on roles and permissions to Super Admin (role_id = 1)
INSERT INTO `role_permissions` (`role_id`, `resource`, `action`) VALUES
-- Roles
(1, 'role', 'create'),
(1, 'role', 'read'),
(1, 'role', 'update'),
(1, 'role', 'delete'),
-- Permissions
(1, 'permission', 'read'),
(1, 'permission', 'update')
ON DUPLICATE KEY UPDATE `role_id` = `role_id`; -- Do nothing if the permission already exists

8
debug_tables.php Normal file
View File

@ -0,0 +1,8 @@
<?php
require_once 'db/config.php';
$pdo = db();
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
echo "<pre>";
print_r($tables);
echo "</pre>";

43
delete-category.php Normal file
View File

@ -0,0 +1,43 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'category', 'delete')) {
header('Location: index.php?error=access_denied');
exit;
}
$category_id = $_GET['id'] ?? null;
if (!$category_id) {
header('Location: categories.php');
exit;
}
try {
$pdo = db();
$pdo->beginTransaction();
// Set category_id to NULL for assets associated with this category
$stmt = $pdo->prepare('UPDATE assets SET category_id = NULL WHERE category_id = ?');
$stmt->execute([$category_id]);
// Delete the category
$stmt = $pdo->prepare('DELETE FROM categories WHERE id = ?');
$stmt->execute([$category_id]);
$pdo->commit();
header("Location: categories.php?success=category_deleted");
exit;
} catch (PDOException $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
// In a real app, log this error.
header("Location: categories.php?error=db_error");
exit;
}

43
delete-location.php Normal file
View File

@ -0,0 +1,43 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'location', 'delete')) {
header('Location: index.php?error=access_denied');
exit;
}
$location_id = $_GET['id'] ?? null;
if (!$location_id) {
header('Location: locations.php');
exit;
}
try {
$pdo = db();
$pdo->beginTransaction();
// Set location_id to NULL for assets associated with this location
$stmt = $pdo->prepare('UPDATE assets SET location_id = NULL WHERE location_id = ?');
$stmt->execute([$location_id]);
// Delete the location
$stmt = $pdo->prepare('DELETE FROM locations WHERE id = ?');
$stmt->execute([$location_id]);
$pdo->commit();
header("Location: locations.php?success=location_deleted");
exit;
} catch (PDOException $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
// In a real app, log this error.
header("Location: locations.php?error=db_error");
exit;
}

41
delete-role.php Normal file
View File

@ -0,0 +1,41 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'role', 'delete')) {
header('Location: index.php?error=access_denied');
exit;
}
$role_id = $_GET['id'] ?? null;
if (!$role_id) {
header('Location: roles.php');
exit;
}
try {
$pdo = db();
// Check if any user is assigned to this role
$stmt = $pdo->prepare('SELECT id FROM users WHERE role_id = ?');
$stmt->execute([$role_id]);
if ($stmt->fetch()) {
header("Location: roles.php?error=role_in_use");
exit;
}
// Delete the role
$stmt = $pdo->prepare('DELETE FROM roles WHERE id = ?');
$stmt->execute([$role_id]);
header("Location: roles.php?success=role_deleted");
exit;
} catch (PDOException $e) {
// In a real app, log this error.
header("Location: roles.php?error=db_error");
exit;
}

View File

@ -4,18 +4,20 @@ require_once 'db/config.php';
require_once 'auth-check.php'; require_once 'auth-check.php';
require_once 'auth-helpers.php'; require_once 'auth-helpers.php';
if (!can($_SESSION['user_role'], 'asset', 'update')) { if (!can($_SESSION['user_role_id'], 'asset', 'update')) {
header('Location: index.php?error=access_denied'); header('Location: index.php?error=access_denied');
exit; exit;
} }
$allowed_fields_str = can($_SESSION['user_role'], 'asset', 'update'); $allowed_fields_str = can($_SESSION['user_role_id'], 'asset', 'update');
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'asset_tag', 'status', 'location', 'manufacturer', 'model', 'purchase_date', 'assigned_to'] : explode(',', $allowed_fields_str); $allowed_fields = ($allowed_fields_str === '*') ? ['name', 'asset_tag', 'status', 'location_id', 'manufacturer', 'model', 'purchase_date', 'assigned_to', 'category_id'] : explode(',', $allowed_fields_str);
$success_message = ''; $success_message = '';
$error_message = ''; $error_message = '';
$asset = null; $asset = null;
$users = []; $users = [];
$categories = [];
$locations = [];
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) { if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header("Location: index.php"); header("Location: index.php");
@ -39,6 +41,14 @@ try {
$stmt = $pdo->query("SELECT id, name FROM users ORDER BY name"); $stmt = $pdo->query("SELECT id, name FROM users ORDER BY name");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC); $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch categories for dropdown
$stmt = $pdo->query("SELECT id, name FROM categories ORDER BY name");
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch locations for dropdown
$stmt = $pdo->query("SELECT id, name FROM locations ORDER BY name");
$locations = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) { } catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage(); $error_message = 'Database error: ' . $e->getMessage();
} }
@ -50,7 +60,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
foreach ($allowed_fields as $field) { foreach ($allowed_fields as $field) {
if (isset($_POST[$field])) { if (isset($_POST[$field])) {
$value = $_POST[$field]; $value = $_POST[$field];
if ($field === 'assigned_to' && $value === '') { if (($field === 'assigned_to' || $field === 'category_id' || $field === 'location_id') && $value === '') {
$value = null; $value = null;
} }
$data[] = $value; $data[] = $value;
@ -137,10 +147,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</select> </select>
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php if (in_array('location', $allowed_fields)): ?> <?php if (in_array('category_id', $allowed_fields)): ?>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label for="location" class="form-label">Location</label> <label for="category_id" class="form-label">Category</label>
<input type="text" class="form-control" id="location" name="location" value="<?php echo htmlspecialchars($asset['location']); ?>"> <select class="form-select" id="category_id" name="category_id">
<option value="">No Category</option>
<?php foreach ($categories as $category): ?>
<option value="<?php echo $category['id']; ?>" <?php if ($asset['category_id'] == $category['id']) echo 'selected'; ?>>
<?php echo htmlspecialchars($category['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (in_array('location_id', $allowed_fields)): ?>
<div class="col-md-6 mb-3">
<label for="location_id" class="form-label">Location</label>
<select class="form-select" id="location_id" name="location_id">
<option value="">No Location</option>
<?php foreach ($locations as $location): ?>
<option value="<?php echo $location['id']; ?>" <?php if ($asset['location_id'] == $location['id']) echo 'selected'; ?>>
<?php echo htmlspecialchars($location['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>

116
edit-category.php Normal file
View File

@ -0,0 +1,116 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'category', 'update')) {
header('Location: index.php?error=access_denied');
exit;
}
$error_message = '';
$category_id = $_GET['id'] ?? null;
if (!$category_id) {
header('Location: categories.php');
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id, name FROM categories WHERE id = ?');
$stmt->execute([$category_id]);
$category = $stmt->fetch();
if (!$category) {
header('Location: categories.php');
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
if (empty($name)) {
$error_message = 'Please fill in the category name.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id FROM categories WHERE name = ? AND id != ?');
$stmt->execute([$name, $category_id]);
if ($stmt->fetch()) {
$error_message = 'A category with this name already exists.';
} else {
$sql = "UPDATE categories SET name = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $category_id]);
header("Location: categories.php?success=category_updated");
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Category - IC-Inventory</title>
<meta name="description" content="Edit an existing category.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Edit Category</h1>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
<div class="surface p-4">
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<?php if ($category): ?>
<form action="edit-category.php?id=<?php echo $category['id']; ?>" method="post">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Category Name*</label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($category['name']); ?>" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Update Category</button>
<a href="categories.php" class="btn btn-secondary">Cancel</a>
</form>
<?php endif; ?>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

116
edit-location.php Normal file
View File

@ -0,0 +1,116 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'location', 'update')) {
header('Location: index.php?error=access_denied');
exit;
}
$error_message = '';
$location_id = $_GET['id'] ?? null;
if (!$location_id) {
header('Location: locations.php');
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id, name FROM locations WHERE id = ?');
$stmt->execute([$location_id]);
$location = $stmt->fetch();
if (!$location) {
header('Location: locations.php');
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
if (empty($name)) {
$error_message = 'Please fill in the location name.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id FROM locations WHERE name = ? AND id != ?');
$stmt->execute([$name, $location_id]);
if ($stmt->fetch()) {
$error_message = 'A location with this name already exists.';
} else {
$sql = "UPDATE locations SET name = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $location_id]);
header("Location: locations.php?success=location_updated");
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Location - IC-Inventory</title>
<meta name="description" content="Edit an existing location.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Edit Location</h1>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
<div class="surface p-4">
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<?php if ($location): ?>
<form action="edit-location.php?id=<?php echo $location['id']; ?>" method="post">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Location Name*</label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($location['name']); ?>" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Update Location</button>
<a href="locations.php" class="btn btn-secondary">Cancel</a>
</form>
<?php endif; ?>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

116
edit-role.php Normal file
View File

@ -0,0 +1,116 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
if (!can($_SESSION['user_role_id'], 'role', 'update')) {
header('Location: index.php?error=access_denied');
exit;
}
$error_message = '';
$role_id = $_GET['id'] ?? null;
if (!$role_id) {
header('Location: roles.php');
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id, name FROM roles WHERE id = ?');
$stmt->execute([$role_id]);
$role = $stmt->fetch();
if (!$role) {
header('Location: roles.php');
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
if (empty($name)) {
$error_message = 'Please fill in the role name.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare('SELECT id FROM roles WHERE name = ? AND id != ?');
$stmt->execute([$name, $role_id]);
if ($stmt->fetch()) {
$error_message = 'A role with this name already exists.';
} else {
$sql = "UPDATE roles SET name = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $role_id]);
header("Location: roles.php?success=role_updated");
exit;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Role - IC-Inventory</title>
<meta name="description" content="Edit an existing role.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Edit Role</h1>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
<div class="surface p-4">
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<?php if ($role): ?>
<form action="edit-role.php?id=<?php echo $role['id']; ?>" method="post">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Role Name*</label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($role['name']); ?>" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Update Role</button>
<a href="roles.php" class="btn btn-secondary">Cancel</a>
</form>
<?php endif; ?>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

View File

@ -4,12 +4,12 @@ require_once 'db/config.php';
require_once 'auth-check.php'; require_once 'auth-check.php';
require_once 'auth-helpers.php'; require_once 'auth-helpers.php';
if (!can($_SESSION['user_role'], 'user', 'update')) { if (!can($_SESSION['user_role_id'], 'user', 'update')) {
header('Location: index.php?error=access_denied'); header('Location: index.php?error=access_denied');
exit; exit;
} }
$allowed_fields_str = can($_SESSION['user_role'], 'user', 'update'); $allowed_fields_str = can($_SESSION['user_role_id'], 'user', 'update');
$allowed_fields = ($allowed_fields_str === '*') ? ['name', 'email', 'role'] : explode(',', $allowed_fields_str); $allowed_fields = ($allowed_fields_str === '*') ? ['name', 'email', 'role'] : explode(',', $allowed_fields_str);
$error_message = ''; $error_message = '';

View File

@ -5,7 +5,7 @@ require_once 'auth-check.php';
require_once 'auth-helpers.php'; require_once 'auth-helpers.php';
// Get allowed fields for the current user // Get allowed fields for the current user
$allowed_fields_str = can($_SESSION['user_role'], 'asset', 'read'); $allowed_fields_str = can($_SESSION['user_role_id'], 'asset', 'read');
$allowed_fields = []; $allowed_fields = [];
if ($allowed_fields_str === '*') { if ($allowed_fields_str === '*') {
// Wildcard means all fields // Wildcard means all fields
@ -189,7 +189,7 @@ function getStatusClass($status) {
<div class="header"> <div class="header">
<h1>Asset Dashboard</h1> <h1>Asset Dashboard</h1>
<div> <div>
<?php if (can($_SESSION['user_role'], 'asset', 'create')): ?> <?php if (can($_SESSION['user_role_id'], 'asset', 'create')): ?>
<a href="add-asset.php" class="btn btn-primary">Add New Asset</a> <a href="add-asset.php" class="btn btn-primary">Add New Asset</a>
<?php endif; ?> <?php endif; ?>
<div class="theme-switcher" id="theme-switcher"> <div class="theme-switcher" id="theme-switcher">
@ -233,7 +233,7 @@ function getStatusClass($status) {
<?php elseif (empty($assets)): ?> <?php elseif (empty($assets)): ?>
<div class="text-center p-5"> <div class="text-center p-5">
<h4>No assets found.</h4> <h4>No assets found.</h4>
<?php if (can($_SESSION['user_role'], 'asset', 'create')): ?> <?php if (can($_SESSION['user_role_id'], 'asset', 'create')): ?>
<p>Get started by adding your first company asset.</p> <p>Get started by adding your first company asset.</p>
<a href="add-asset.php" class="btn btn-primary">Add Asset</a> <a href="add-asset.php" class="btn btn-primary">Add Asset</a>
<?php endif; ?> <?php endif; ?>
@ -274,10 +274,10 @@ function getStatusClass($status) {
</td> </td>
<?php endforeach; ?> <?php endforeach; ?>
<td> <td>
<?php if (can($_SESSION['user_role'], 'asset', 'update')): ?> <?php if (can($_SESSION['user_role_id'], 'asset', 'update')): ?>
<a href="edit-asset.php?id=<?php echo $asset['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a> <a href="edit-asset.php?id=<?php echo $asset['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<?php endif; ?> <?php endif; ?>
<?php if (can($_SESSION['user_role'], 'asset', 'delete')): ?> <?php if (can($_SESSION['user_role_id'], 'asset', 'delete')): ?>
<a href="delete-asset.php?id=<?php echo $asset['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this asset?');">Delete</a> <a href="delete-asset.php?id=<?php echo $asset['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this asset?');">Delete</a>
<?php endif; ?> <?php endif; ?>
</td> </td>

118
locations.php Normal file
View File

@ -0,0 +1,118 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
// Permissions check
if (!can($_SESSION['user_role_id'], 'location', 'read')) {
header('Location: index.php?error=access_denied');
exit;
}
function get_locations() {
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM locations ORDER BY name ASC");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
return ['error' => 'Database error: ' . $e->getMessage()];
}
}
$locations = get_locations();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Location Management - IC-Inventory</title>
<meta name="description" content="Location management for IC-Inventory.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Location Management</h1>
<div>
<?php if (can($_SESSION['user_role_id'], 'location', 'create')): ?>
<a href="add-location.php" class="btn btn-primary">Add New Location</a>
<?php endif; ?>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success">
<?php
if ($_GET['success'] === 'location_added') echo 'Location successfully added!';
if ($_GET['success'] === 'location_updated') echo 'Location successfully updated!';
if ($_GET['success'] === 'location_deleted') echo 'Location successfully deleted!';
?>
</div>
<?php endif; ?>
<div class="surface p-4">
<?php if (isset($locations['error'])): ?>
<div class="alert alert-danger">
<?php echo htmlspecialchars($locations['error']); ?>
</div>
<?php elseif (empty($locations)): ?>
<div class="text-center p-5">
<h4>No locations found.</h4>
<?php if (can($_SESSION['user_role_id'], 'location', 'create')): ?>
<p>Get started by adding your first location.</p>
<a href="add-location.php" class="btn btn-primary">Add Location</a>
<?php endif; ?>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($locations as $location): ?>
<tr>
<td><?php echo htmlspecialchars($location['name']); ?></td>
<td>
<?php if (can($_SESSION['user_role_id'], 'location', 'update')): ?>
<a href="edit-location.php?id=<?php echo $location['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<?php endif; ?>
<?php if (can($_SESSION['user_role_id'], 'location', 'delete')): ?>
<a href="delete-location.php?id=<?php echo $location['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this location?');">Delete</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

View File

@ -19,14 +19,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} else { } else {
try { try {
$pdo = db(); $pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); $stmt = $pdo->prepare("SELECT u.*, r.name as role_name FROM users u JOIN roles r ON u.role_id = r.id WHERE u.email = ?");
$stmt->execute([$email]); $stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC); $user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) { if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id']; $_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name']; $_SESSION['user_name'] = $user['name'];
$_SESSION['user_role'] = $user['role']; $_SESSION['user_role_id'] = $user['role_id'];
$_SESSION['user_role_name'] = $user['role_name'];
// For backwards compatibility with old code expecting a role name
$_SESSION['user_role'] = $user['role_name'];
header("Location: index.php"); header("Location: index.php");
exit; exit;
} else { } else {

126
roles.php Normal file
View File

@ -0,0 +1,126 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'auth-check.php';
require_once 'auth-helpers.php';
// Permissions check
if (!can($_SESSION['user_role_id'], 'role', 'read')) {
// First, check if the user has permissions for this resource
$user_role_id = $_SESSION['user_role'];
$sql = "SELECT 1 FROM role_permissions WHERE role_id = :role_id AND resource = 'role' AND action = 'read'";
$stmt = db()->prepare($sql);
$stmt->execute(['role_id' => $user_role_id]);
if ($stmt->rowCount() == 0) {
// If no permissions, redirect
header('Location: index.php?error=access_denied');
exit;
}
}
function get_roles() {
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM roles ORDER BY name ASC");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
return ['error' => 'Database error: ' . $e->getMessage()];
}
}
$roles = get_roles();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Role Management - IC-Inventory</title>
<meta name="description" content="Role management for IC-Inventory.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<div class="wrapper">
<?php require_once 'templates/sidebar.php'; ?>
<main id="content">
<div class="header">
<h1>Role Management</h1>
<div>
<?php if (can($_SESSION['user_role_id'], 'role', 'create')): ?>
<a href="add-role.php" class="btn btn-primary">Add New Role</a>
<?php endif; ?>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success">
<?php
if ($_GET['success'] === 'role_added') echo 'Role successfully added!';
if ($_GET['success'] === 'role_updated') echo 'Role successfully updated!';
if ($_GET['success'] === 'role_deleted') echo 'Role successfully deleted!';
?>
</div>
<?php endif; ?>
<div class="surface p-4">
<?php if (isset($roles['error'])): ?>
<div class="alert alert-danger">
<?php echo htmlspecialchars($roles['error']); ?>
</div>
<?php elseif (empty($roles)): ?>
<div class="text-center p-5">
<h4>No roles found.</h4>
<?php if (can($_SESSION['user_role_id'], 'role', 'create')): ?>
<p>Get started by adding your first role.</p>
<a href="add-role.php" class="btn btn-primary">Add Role</a>
<?php endif; ?>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($roles as $role): ?>
<tr>
<td><?php echo htmlspecialchars($role['name']); ?></td>
<td>
<?php if (can($_SESSION['user_role_id'], 'role', 'update')): ?>
<a href="edit-role.php?id=<?php echo $role['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<?php endif; ?>
<?php if (can($_SESSION['user_role_id'], 'role', 'delete')): ?>
<a href="delete-role.php?id=<?php echo $role['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this role?');">Delete</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
feather.replace();
</script>
</body>
</html>

View File

@ -3,12 +3,74 @@ require_once 'db/config.php';
try { try {
$pdo = db(); $pdo = db();
$file = 'db/migrations/002_create_users_table.sql'; $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = file_get_contents($file);
$pdo->exec($sql);
echo "Successfully ran migration: $file\n";
echo "All migrations ran successfully.\n"; // 1. Create migrations table if it doesn't exist
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (
id INT AUTO_INCREMENT PRIMARY KEY,
migration_file VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY (migration_file)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
// 2. Pre-populate with migrations that are known to have run already
$known_migrations = [
'001_create_assets_table.sql',
'002_create_users_table.sql',
'003_create_permissions_table.sql',
'004_create_categories_table.sql',
'005_add_category_id_to_assets.sql'
];
$stmt = $pdo->prepare("INSERT IGNORE INTO migrations (migration_file) VALUES (?)");
foreach ($known_migrations as $migration) {
$stmt->execute([$migration]);
}
// 3. Get all migrations that have already been run
$ran_migrations_stmt = $pdo->query("SELECT migration_file FROM migrations");
$ran_migrations = $ran_migrations_stmt->fetchAll(PDO::FETCH_COLUMN);
// 4. Get all available migration files
$migration_files = glob('db/migrations/*.sql');
sort($migration_files);
// 5. Determine which migrations to run
$migrations_to_run = [];
foreach ($migration_files as $file) {
if (!in_array(basename($file), $ran_migrations)) {
$migrations_to_run[] = $file;
}
}
if (empty($migrations_to_run)) {
echo "Database is already up to date.\n";
} else {
// 6. Run the new migrations
foreach ($migrations_to_run as $file) {
try {
$sql = file_get_contents($file);
if (empty(trim($sql))) {
echo "Skipping empty migration file: $file\n";
$stmt = $pdo->prepare("INSERT INTO migrations (migration_file) VALUES (?)");
$stmt->execute([basename($file)]);
continue;
}
$pdo->exec($sql);
$stmt = $pdo->prepare("INSERT INTO migrations (migration_file) VALUES (?)");
$stmt->execute([basename($file)]);
echo "Successfully ran migration: " . basename($file) . "\n";
} catch (PDOException $e) {
echo "Error running migration " . basename($file) . ": " . $e->getMessage() . "\n";
exit(1);
}
}
echo "All new migrations ran successfully.\n";
}
} catch (PDOException $e) { } catch (PDOException $e) {
die("Database error: " . $e->getMessage()); die("Database error: " . $e->getMessage());

View File

@ -2,9 +2,9 @@
session_start(); session_start();
require_once 'db/config.php'; require_once 'db/config.php';
require_once 'auth-check.php'; require_once 'auth-check.php';
require_once 'auth-helpers.php';
// Only Admins can access this page if (!can($_SESSION['user_role_id'], 'permission', 'update')) {
if ($_SESSION['user_role'] !== 'Admin') {
header('Location: index.php?error=access_denied'); header('Location: index.php?error=access_denied');
exit; exit;
} }
@ -12,8 +12,16 @@ if ($_SESSION['user_role'] !== 'Admin') {
$success_message = ''; $success_message = '';
$error_message = ''; $error_message = '';
$roles = ['Admin', 'Asset Manager', 'IT Technician', 'Employee']; try {
$resources = ['asset', 'user']; $pdo = db();
$roles_stmt = $pdo->query('SELECT * FROM roles ORDER BY name');
$roles = $roles_stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$error_message = "Database error fetching roles: " . $e->getMessage();
$roles = [];
}
$resources = ['asset', 'user', 'category', 'location', 'role', 'permission'];
$actions = ['create', 'read', 'update', 'delete']; $actions = ['create', 'read', 'update', 'delete'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
@ -21,22 +29,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = db(); $pdo = db();
$pdo->beginTransaction(); $pdo->beginTransaction();
// Clear existing permissions // Clear existing permissions, but not for the super admin
$pdo->exec('TRUNCATE TABLE role_permissions'); $pdo->exec('DELETE FROM role_permissions WHERE role_id != 1');
$stmt = $pdo->prepare('INSERT INTO role_permissions (role, resource, action, fields) VALUES (?, ?, ?, ?)'); $stmt = $pdo->prepare('INSERT INTO role_permissions (role_id, resource, action, fields) VALUES (?, ?, ?, ?)');
$permissions = $_POST['permissions'] ?? []; $permissions = $_POST['permissions'] ?? [];
foreach ($roles as $role) { foreach ($roles as $role) {
if ($role['id'] == 1) continue; // Skip Admin role, its permissions are immutable
foreach ($resources as $resource) { foreach ($resources as $resource) {
foreach ($actions as $action) { foreach ($actions as $action) {
if (!empty($permissions[$role][$resource][$action]['enabled'])) { if (isset($permissions[$role['id']][$resource][$action]['enabled']) && $permissions[$role['id']][$resource][$action]['enabled'] == '1') {
$fields = $permissions[$role][$resource][$action]['fields'] ?? null; $fields = null;
if (in_array($action, ['read', 'update']) && empty($fields)) { if (in_array($action, ['read', 'update', 'create'])) {
$fields = '*'; // Default to all fields if not specified $fields = $permissions[$role['id']][$resource][$action]['fields'] ?? '*';
if (empty($fields)) {
$fields = '*';
}
} }
$stmt->execute([$role, $resource, $action, $fields]); $stmt->execute([$role['id'], $resource, $action, $fields]);
} }
} }
} }
@ -57,7 +69,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
function get_permissions() { function get_permissions() {
try { try {
$pdo = db(); $pdo = db();
$stmt = $pdo->query('SELECT * FROM role_permissions ORDER BY role, resource, action'); $stmt = $pdo->query('SELECT * FROM role_permissions ORDER BY role_id, resource, action');
return $stmt->fetchAll(PDO::FETCH_ASSOC); return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) { } catch (PDOException $e) {
return ['error' => 'Database error: ' . $e->getMessage()]; return ['error' => 'Database error: ' . $e->getMessage()];
@ -69,7 +81,7 @@ $permissions_from_db = get_permissions();
// Group permissions by role and resource for easier display // Group permissions by role and resource for easier display
$grouped_permissions = []; $grouped_permissions = [];
foreach ($permissions_from_db as $p) { foreach ($permissions_from_db as $p) {
$grouped_permissions[$p['role']][$p['resource']][$p['action']] = $p['fields']; $grouped_permissions[$p['role_id']][$p['resource']][$p['action']] = $p['fields'];
} }
?> ?>
@ -95,9 +107,6 @@ foreach ($permissions_from_db as $p) {
<main id="content"> <main id="content">
<div class="header"> <div class="header">
<h1>Settings - Role Permissions</h1> <h1>Settings - Role Permissions</h1>
<div class="theme-switcher" id="theme-switcher">
<i data-feather="moon"></i>
</div>
</div> </div>
<div class="surface p-4"> <div class="surface p-4">
@ -126,17 +135,25 @@ foreach ($permissions_from_db as $p) {
<?php foreach ($resources as $resource_idx => $resource): ?> <?php foreach ($resources as $resource_idx => $resource): ?>
<tr> <tr>
<?php if ($resource_idx === 0): ?> <?php if ($resource_idx === 0): ?>
<td rowspan="<?php echo count($resources); ?>" class="align-middle"><strong><?php echo $role; ?></strong></td> <td rowspan="<?php echo count($resources); ?>" class="align-middle">
<strong><?php echo htmlspecialchars($role['name']); ?></strong>
<?php if ($role['id'] == 1): ?>
<span class="badge bg-primary">Super Admin</span>
<?php endif; ?>
</td>
<?php endif; ?> <?php endif; ?>
<td class="align-middle"><?php echo ucfirst($resource); ?></td> <td class="align-middle"><?php echo ucfirst($resource); ?></td>
<?php foreach ($actions as $action): ?> <?php foreach ($actions as $action):
$is_disabled = ($role['id'] == 1); // Disable all checkboxes for Super Admin
$is_checked = isset($grouped_permissions[$role['id']][$resource]) && array_key_exists($action, $grouped_permissions[$role['id']][$resource]);
?>
<td class="align-middle"> <td class="align-middle">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" name="permissions[<?php echo $role; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][enabled]" value="1" <?php echo isset($grouped_permissions[$role][$resource][$action]) ? 'checked' : ''; ?>> <input class="form-check-input" type="checkbox" name="permissions[<?php echo $role['id']; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][enabled]" value="1" <?php echo $is_checked ? 'checked' : ''; ?> <?php echo $is_disabled ? 'disabled' : ''; ?>>
</div> </div>
<?php if (in_array($action, ['read', 'update'])): ?> <?php if (in_array($action, ['read', 'update', 'create'])): ?>
<input type="text" class="form-control form-control-sm mt-1" name="permissions[<?php echo $role; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][fields]" placeholder="* for all" value="<?php echo htmlspecialchars($grouped_permissions[$role][$resource][$action] ?? ''); ?>"> <input type="text" class="form-control form-control-sm mt-1" name="permissions[<?php echo $role['id']; ?>][<?php echo $resource; ?>][<?php echo $action; ?>][fields]" placeholder="* for all" value="<?php echo htmlspecialchars($grouped_permissions[$role['id']][$resource][$action] ?? '*'); ?>" <?php echo $is_disabled ? 'disabled' : ''; ?>>
<?php endif; ?> <?php endif; ?>
</td> </td>
<?php endforeach; ?> <?php endforeach; ?>

View File

@ -19,13 +19,39 @@ $current_page = basename($_SERVER['PHP_SELF']);
Assets Assets
</a> </a>
</li> </li>
<?php if ($_SESSION['user_role'] === 'Admin'): ?> <?php if (can($_SESSION['user_role_id'], 'user', 'read')): ?>
<li> <li>
<a href="users.php" class="nav-link <?php echo ($current_page === 'users.php' || $current_page === 'add-user.php' || $current_page === 'edit-user.php') ? 'active' : ''; ?>"> <a href="users.php" class="nav-link <?php echo ($current_page === 'users.php' || $current_page === 'add-user.php' || $current_page === 'edit-user.php') ? 'active' : ''; ?>">
<i data-feather="users" class="me-2"></i> <i data-feather="users" class="me-2"></i>
Users Users
</a> </a>
</li> </li>
<?php endif; ?>
<?php if (can($_SESSION['user_role_id'], 'category', 'read')): ?>
<li>
<a href="categories.php" class="nav-link <?php echo ($current_page === 'categories.php' || $current_page === 'add-category.php' || $current_page === 'edit-category.php') ? 'active' : ''; ?>">
<i data-feather="grid" class="me-2"></i>
Categories
</a>
</li>
<?php endif; ?>
<?php if (can($_SESSION['user_role_id'], 'location', 'read')): ?>
<li>
<a href="locations.php" class="nav-link <?php echo ($current_page === 'locations.php' || $current_page === 'add-location.php' || $current_page === 'edit-location.php') ? 'active' : ''; ?>">
<i data-feather="map-pin" class="me-2"></i>
Locations
</a>
</li>
<?php endif; ?>
<?php if (can($_SESSION['user_role_id'], 'role', 'read')): ?>
<li>
<a href="roles.php" class="nav-link <?php echo ($current_page === 'roles.php' || $current_page === 'add-role.php' || $current_page === 'edit-role.php') ? 'active' : ''; ?>">
<i data-feather="shield" class="me-2"></i>
Roles
</a>
</li>
<?php endif; ?>
<?php if (can($_SESSION['user_role_id'], 'permission', 'update')): ?>
<li> <li>
<a href="settings.php" class="nav-link <?php echo ($current_page === 'settings.php') ? 'active' : ''; ?>"> <a href="settings.php" class="nav-link <?php echo ($current_page === 'settings.php') ? 'active' : ''; ?>">
<i data-feather="settings" class="me-2"></i> <i data-feather="settings" class="me-2"></i>

View File

@ -7,13 +7,13 @@ require_once 'auth-helpers.php';
// Only Admins can access this page // Only Admins can access this page
if (!can($_SESSION['user_role'], 'user', 'read')) { if (!can($_SESSION['user_role_id'], 'user', 'read')) {
header('Location: index.php?error=access_denied'); header('Location: index.php?error=access_denied');
exit; exit;
} }
// Get allowed fields for the current user // Get allowed fields for the current user
$allowed_fields_str = can($_SESSION['user_role'], 'user', 'read'); $allowed_fields_str = can($_SESSION['user_role_id'], 'user', 'read');
$allowed_fields = ($allowed_fields_str && $allowed_fields_str !== '*') ? explode(',', $allowed_fields_str) : []; $allowed_fields = ($allowed_fields_str && $allowed_fields_str !== '*') ? explode(',', $allowed_fields_str) : [];
if ($allowed_fields_str === '*') { if ($allowed_fields_str === '*') {
@ -75,7 +75,7 @@ $users = get_users($allowed_fields);
<div class="header"> <div class="header">
<h1>User Management</h1> <h1>User Management</h1>
<div> <div>
<?php if (can($_SESSION['user_role'], 'user', 'create')): ?> <?php if (can($_SESSION['user_role_id'], 'user', 'create')): ?>
<a href="add-user.php" class="btn btn-primary">Add New User</a> <a href="add-user.php" class="btn btn-primary">Add New User</a>
<?php endif; ?> <?php endif; ?>
<div class="theme-switcher" id="theme-switcher"> <div class="theme-switcher" id="theme-switcher">
@ -102,7 +102,7 @@ $users = get_users($allowed_fields);
<?php elseif (empty($users)): ?> <?php elseif (empty($users)): ?>
<div class="text-center p-5"> <div class="text-center p-5">
<h4>No users found.</h4> <h4>No users found.</h4>
<?php if (can($_SESSION['user_role'], 'user', 'create')): ?> <?php if (can($_SESSION['user_role_id'], 'user', 'create')): ?>
<p>Get started by adding your first user.</p> <p>Get started by adding your first user.</p>
<a href="add-user.php" class="btn btn-primary">Add User</a> <a href="add-user.php" class="btn btn-primary">Add User</a>
<?php endif; ?> <?php endif; ?>
@ -125,10 +125,10 @@ $users = get_users($allowed_fields);
<td><?php echo htmlspecialchars($user[$field]); ?></td> <td><?php echo htmlspecialchars($user[$field]); ?></td>
<?php endforeach; ?> <?php endforeach; ?>
<td> <td>
<?php if (can($_SESSION['user_role'], 'user', 'update')): ?> <?php if (can($_SESSION['user_role_id'], 'user', 'update')): ?>
<a href="edit-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a> <a href="edit-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<?php endif; ?> <?php endif; ?>
<?php if (can($_SESSION['user_role'], 'user', 'delete')): ?> <?php if (can($_SESSION['user_role_id'], 'user', 'delete')): ?>
<a href="delete-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this user?');">Delete</a> <a href="delete-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this user?');">Delete</a>
<?php endif; ?> <?php endif; ?>
</td> </td>