257 lines
9.8 KiB
PHP
257 lines
9.8 KiB
PHP
<?php
|
|
session_start();
|
|
require 'db/config.php';
|
|
$db = db();
|
|
|
|
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
|
|
header('Location: index.php');
|
|
exit;
|
|
}
|
|
|
|
// Handle User Creation/Update
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_user'])) {
|
|
$id = $_POST['user_id'] ?? null;
|
|
$name = $_POST['name'];
|
|
$role = $_POST['role'];
|
|
$processes = json_encode($_POST['processes'] ?? []);
|
|
$pin = $_POST['pin'] ?? '';
|
|
|
|
if ($id) {
|
|
// Update
|
|
$sql = "UPDATE users SET name = ?, role = ?, assigned_processes = ? WHERE id = ?";
|
|
$params = [$name, $role, $processes, $id];
|
|
$db->prepare($sql)->execute($params);
|
|
|
|
if ($role === 'admin' && !empty($pin)) {
|
|
$pinHash = password_hash($pin, PASSWORD_BCRYPT);
|
|
$db->prepare("UPDATE users SET pin_hash = ? WHERE id = ?")->execute([$pinHash, $id]);
|
|
}
|
|
} else {
|
|
// Create
|
|
$pinHash = ($role === 'admin' && !empty($pin)) ? password_hash($pin, PASSWORD_BCRYPT) : null;
|
|
$db->prepare("INSERT INTO users (name, role, assigned_processes, pin_hash) VALUES (?, ?, ?, ?)")
|
|
->execute([$name, $role, $processes, $pinHash]);
|
|
}
|
|
header("Location: users.php?success=1");
|
|
exit;
|
|
}
|
|
|
|
$users = $db->query("SELECT * FROM users ORDER BY role ASC, name ASC")->fetchAll();
|
|
$processTypes = ['cutting', 'welding', 'bending', 'assembly', 'inspection'];
|
|
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>M-TRACK | User Management</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--sidebar-width: 240px;
|
|
--bg: #f8fafc;
|
|
--primary: #1e293b;
|
|
--accent: #3b82f6;
|
|
--text: #334155;
|
|
--border: #e2e8f0;
|
|
}
|
|
body {
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
background-color: var(--bg);
|
|
color: var(--text);
|
|
}
|
|
.sidebar {
|
|
width: var(--sidebar-width);
|
|
position: fixed;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
background-color: var(--primary);
|
|
color: white;
|
|
z-index: 1000;
|
|
padding: 1.5rem 1rem;
|
|
}
|
|
.main-content {
|
|
margin-left: var(--sidebar-width);
|
|
padding: 2.5rem;
|
|
}
|
|
.card {
|
|
background: white;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.badge-process {
|
|
background: #e2e8f0;
|
|
color: #475569;
|
|
font-weight: 600;
|
|
font-size: 0.75rem;
|
|
padding: 0.25rem 0.625rem;
|
|
border-radius: 4px;
|
|
margin-right: 0.25rem;
|
|
margin-bottom: 0.25rem;
|
|
display: inline-block;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="sidebar">
|
|
<h2>M-TRACK</h2>
|
|
<nav class="nav nav-pills flex-column">
|
|
<a class="nav-link" href="dashboard.php"><i class="bi bi-grid-fill me-2"></i> Dashboard</a>
|
|
<a class="nav-link" href="jobs.php"><i class="bi bi-briefcase me-2"></i> Jobs</a>
|
|
<a class="nav-link" href="shop_floor.php"><i class="bi bi-kanban me-2"></i> Shop Floor</a>
|
|
<a class="nav-link" href="inventory.php"><i class="bi bi-boxes me-2"></i> Inventory</a>
|
|
<a class="nav-link active" href="users.php"><i class="bi bi-people me-2"></i> Users</a>
|
|
<hr class="my-4 border-secondary opacity-25">
|
|
<a class="nav-link text-danger" href="logout.php"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
|
|
</nav>
|
|
</div>
|
|
|
|
<div class="main-content">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1>User Management</h1>
|
|
<button class="btn btn-primary" onclick="resetUserModal()" data-bs-toggle="modal" data-bs-target="#userModal">
|
|
<i class="bi bi-person-plus-fill me-1"></i> New User
|
|
</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0 align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Role</th>
|
|
<th>Assigned Processes</th>
|
|
<th class="text-end">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($users as $user): ?>
|
|
<?php $procs = json_decode($user['assigned_processes'] ?? '[]', true); ?>
|
|
<tr>
|
|
<td><div class="fw-bold"><?= htmlspecialchars($user['name']) ?></div></td>
|
|
<td><span class="badge bg-<?= $user['role'] === 'admin' ? 'dark' : 'light text-dark border' ?> text-uppercase" style="font-size: 0.7rem;"><?= $user['role'] ?></span></td>
|
|
<td>
|
|
<?php if ($user['role'] === 'worker'): ?>
|
|
<?php foreach ($procs as $p): ?>
|
|
<span class="badge-process"><?= strtoupper($p) ?></span>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($procs)): ?>
|
|
<span class="text-muted small">None assigned</span>
|
|
<?php endif; ?>
|
|
<?php else: ?>
|
|
<span class="text-muted small">—</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-outline-primary" onclick='editUser(<?= json_encode($user) ?>)' data-bs-toggle="modal" data-bs-target="#userModal">Edit</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Modal -->
|
|
<div class="modal fade" id="userModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<form method="POST" class="modal-content" id="userForm">
|
|
<input type="hidden" name="user_id" id="user_id">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="modalTitle">New User</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Full Name</label>
|
|
<input type="text" name="name" id="user_name" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Role</label>
|
|
<select name="role" id="user_role" class="form-select" onchange="toggleRoleFields()">
|
|
<option value="worker">Worker</option>
|
|
<option value="admin">Admin</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="worker_fields">
|
|
<label class="form-label d-block">Assigned Processes</label>
|
|
<div class="row">
|
|
<?php foreach ($processTypes as $pt): ?>
|
|
<div class="col-6 mb-2">
|
|
<div class="form-check">
|
|
<input class="form-check-input proc-checkbox" type="checkbox" name="processes[]" value="<?= $pt ?>" id="proc_<?= $pt ?>">
|
|
<label class="form-check-label text-capitalize" for="proc_<?= $pt ?>">
|
|
<?= $pt ?>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="admin_fields" style="display:none;">
|
|
<div class="mb-3">
|
|
<label class="form-label">PIN (4-6 digits)</label>
|
|
<input type="password" name="pin" id="user_pin" class="form-control" placeholder="Leave blank to keep current PIN if editing">
|
|
<small class="text-muted">Admins must enter this PIN to log in.</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" name="save_user" class="btn btn-primary">Save User</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
function toggleRoleFields() {
|
|
const role = document.getElementById('user_role').value;
|
|
document.getElementById('worker_fields').style.display = role === 'worker' ? 'block' : 'none';
|
|
document.getElementById('admin_fields').style.display = role === 'admin' ? 'block' : 'none';
|
|
}
|
|
|
|
function resetUserModal() {
|
|
document.getElementById('userForm').reset();
|
|
document.getElementById('user_id').value = '';
|
|
document.getElementById('modalTitle').innerText = 'New User';
|
|
toggleRoleFields();
|
|
}
|
|
|
|
function editUser(user) {
|
|
document.getElementById('user_id').value = user.id;
|
|
document.getElementById('user_name').value = user.name;
|
|
document.getElementById('user_role').value = user.role;
|
|
document.getElementById('modalTitle').innerText = 'Edit User: ' + user.name;
|
|
|
|
// Reset checkboxes
|
|
document.querySelectorAll('.proc-checkbox').forEach(cb => cb.checked = false);
|
|
|
|
if (user.assigned_processes) {
|
|
const procs = JSON.parse(user.assigned_processes);
|
|
procs.forEach(p => {
|
|
const cb = document.getElementById('proc_' + p);
|
|
if (cb) cb.checked = true;
|
|
});
|
|
}
|
|
|
|
toggleRoleFields();
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|