Files
ScanLook/templates/manage_users.html
Javier 21671d6bee v0.12.0 - Add modular system architecture with user-based module access
- Add Modules and UserModules database tables
- Create home page with module selection grid
- Implement per-user module assignment in user management
- Add route guards for module access control
- Refactor navigation: login -> home -> modules, admin console via button
- Add Font Awesome icons
2026-01-26 11:35:29 -06:00

324 lines
12 KiB
HTML

{% extends "base.html" %}
{% block title %}Manage Users - ScanLook{% endblock %}
{% block content %}
<div class="dashboard-container">
<div class="dashboard-header">
<div class="header-left">
<h1 class="page-title">Manage Users</h1>
<label class="filter-toggle">
<input type="checkbox" id="showInactive" onchange="toggleInactiveUsers()">
<span class="filter-label">Show Inactive Users</span>
</label>
</div>
<button class="btn btn-primary" onclick="openAddUser()">
<span class="btn-icon">+</span> Add User
</button>
</div>
{% if users %}
<div class="users-table-container">
<table class="users-table">
<thead>
<tr>
<th>Username</th>
<th>Full Name</th>
<th>Email</th>
<th>Role</th>
<th>Location</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="{{ 'inactive-user' if not user.is_active else '' }}">
<td><strong>{{ user.username }}</strong></td>
<td>{{ user.full_name }}</td>
<td>{{ user.email or '-' }}</td>
<td><span class="role-pill role-{{ user.role }}">{{ user.role }}</span></td>
<td>{{ user.branch }}</td>
<td>
{% if user.is_active %}
<span class="status-badge status-success">Active</span>
{% else %}
<span class="status-badge status-neutral">Inactive</span>
{% endif %}
</td>
<td>
<div class="action-buttons">
<button class="btn-action btn-edit" onclick="openEditUser({{ user.user_id }})" title="Edit">✏️</button>
{% if user.user_id != session.user_id %}
<button class="btn-action btn-delete" onclick="deleteUser({{ user.user_id }}, '{{ user.username }}')" title="Delete">🗑️</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty-state">
<div class="empty-icon">👥</div>
<h2 class="empty-title">No Users Found</h2>
<p class="empty-text">Add your first user to get started</p>
</div>
{% endif %}
</div>
<!-- Add/Edit User Modal -->
<div id="userModal" class="modal">
<div class="modal-content modal-large">
<div class="modal-header-bar">
<h3 class="modal-title" id="modalTitle">Add User</h3>
<button type="button" class="btn-close-modal" onclick="closeUserModal()"></button>
</div>
<form id="userForm" class="user-form">
<input type="hidden" id="userId" value="">
<div class="form-row">
<div class="form-group">
<label class="form-label">Username *</label>
<input type="text" id="username" class="form-input" required autocomplete="off">
</div>
<div class="form-group">
<label class="form-label">Password <span id="passwordOptional" style="display:none;">(leave blank to keep current)</span></label>
<input type="password" id="password" class="form-input" autocomplete="new-password">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">First Name *</label>
<input type="text" id="firstName" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label">Last Name *</label>
<input type="text" id="lastName" class="form-input" required>
</div>
</div>
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" id="email" class="form-input">
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Role *</label>
<select id="role" class="form-select" required>
{% if session.role == 'owner' %}
<option value="owner">Owner</option>
<option value="admin">Admin</option>
{% endif %}
<option value="staff">Staff</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Location</label>
<input type="text" id="branch" class="form-input" value="Main">
</div>
</div>
<div class="form-group" id="activeToggleGroup" style="display: none;">
<label class="form-label">
<input type="checkbox" id="isActive" checked> Active
</label>
</div>
<div class="form-group">
<label class="form-label">Module Access</label>
<div class="module-checkboxes" id="moduleCheckboxes">
{% for module in modules %}
<label class="checkbox-label">
<input type="checkbox" name="modules" value="{{ module.module_id }}" class="module-checkbox">
{{ module.module_name }}
</label>
{% endfor %}
</div>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeUserModal()">Cancel</button>
<button type="submit" class="btn btn-primary">Save User</button>
</div>
</form>
</div>
</div>
<script>
let editingUserId = null;
// Hide inactive users by default on page load
document.addEventListener('DOMContentLoaded', function() {
toggleInactiveUsers();
});
function toggleInactiveUsers() {
const showInactive = document.getElementById('showInactive').checked;
const inactiveRows = document.querySelectorAll('tr.inactive-user');
inactiveRows.forEach(row => {
row.style.display = showInactive ? '' : 'none';
});
}
function openAddUser() {
editingUserId = null;
document.getElementById('modalTitle').textContent = 'Add User';
document.getElementById('userForm').reset();
document.getElementById('userId').value = '';
document.getElementById('passwordOptional').style.display = 'none';
document.getElementById('password').required = true;
document.getElementById('activeToggleGroup').style.display = 'none';
// Uncheck all modules for new user
document.querySelectorAll('.module-checkbox').forEach(cb => cb.checked = false);
document.getElementById('userModal').style.display = 'flex';
}
function openEditUser(userId) {
editingUserId = userId;
document.getElementById('modalTitle').textContent = 'Edit User';
document.getElementById('passwordOptional').style.display = 'inline';
document.getElementById('password').required = false;
document.getElementById('activeToggleGroup').style.display = 'block';
// Fetch user data
fetch('/settings/users/' + userId)
.then(response => response.json())
.then(data => {
if (data.success) {
const user = data.user;
document.getElementById('userId').value = user.user_id;
document.getElementById('username').value = user.username;
document.getElementById('firstName').value = user.first_name;
document.getElementById('lastName').value = user.last_name;
document.getElementById('email').value = user.email || '';
document.getElementById('role').value = user.role;
document.getElementById('branch').value = user.branch;
document.getElementById('isActive').checked = user.is_active == 1;
// If editing yourself, disable role change
const isEditingSelf = user.user_id === {{ session.user_id }};
document.getElementById('role').disabled = isEditingSelf;
// Load user's modules
fetch('/settings/users/' + userId + '/modules')
.then(resp => resp.json())
.then(moduleData => {
// Uncheck all first
document.querySelectorAll('.module-checkbox').forEach(cb => cb.checked = false);
// Check assigned modules
if (moduleData.success) {
moduleData.module_ids.forEach(id => {
const cb = document.querySelector(`.module-checkbox[value="${id}"]`);
if (cb) cb.checked = true;
});
}
// Show modal after modules are loaded
document.getElementById('userModal').style.display = 'flex';
});
} else {
alert(data.message);
}
})
.catch(error => {
alert('Error loading user data');
console.error(error);
});
}
function closeUserModal() {
document.getElementById('userModal').style.display = 'none';
document.getElementById('userForm').reset();
}
document.getElementById('userForm').addEventListener('submit', function(e) {
e.preventDefault();
const userId = document.getElementById('userId').value;
const userData = {
username: document.getElementById('username').value,
password: document.getElementById('password').value,
first_name: document.getElementById('firstName').value,
last_name: document.getElementById('lastName').value,
email: document.getElementById('email').value,
role: document.getElementById('role').value,
branch: document.getElementById('branch').value,
is_active: document.getElementById('isActive').checked ? 1 : 0
};
const url = userId ? `/settings/users/${userId}/update` : '/settings/users/add';
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Save modules if editing existing user
if (userId) {
const moduleIds = Array.from(document.querySelectorAll('.module-checkbox:checked'))
.map(cb => parseInt(cb.value));
fetch(`/settings/users/${userId}/modules`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ module_ids: moduleIds })
}).then(() => {
closeUserModal();
location.reload();
});
} else {
closeUserModal();
location.reload();
}
} else {
alert(data.message);
}
})
.catch(error => {
alert('Error saving user');
console.error(error);
});
});
function deleteUser(userId, username) {
if (!confirm(`Delete user "${username}"? This will deactivate their account.`)) {
return;
}
fetch(`/settings/users/${userId}/delete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert(data.message);
}
})
.catch(error => {
alert('Error deleting user');
console.error(error);
});
}
// Close modal on escape
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeUserModal();
}
});
</script>
{% endblock %}