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
This commit is contained in:
Javier
2026-01-26 11:35:29 -06:00
parent cbd7e535e6
commit 21671d6bee
17 changed files with 365 additions and 47 deletions

View File

@@ -129,6 +129,18 @@
</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>
@@ -162,9 +174,10 @@ function openAddUser() {
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';
@@ -191,7 +204,22 @@ function openEditUser(userId) {
const isEditingSelf = user.user_id === {{ session.user_id }};
document.getElementById('role').disabled = isEditingSelf;
document.getElementById('userModal').style.display = 'flex';
// 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);
}
@@ -201,7 +229,6 @@ function openEditUser(userId) {
console.error(error);
});
}
function closeUserModal() {
document.getElementById('userModal').style.display = 'none';
document.getElementById('userForm').reset();
@@ -234,8 +261,23 @@ document.getElementById('userForm').addEventListener('submit', function(e) {
.then(response => response.json())
.then(data => {
if (data.success) {
closeUserModal();
location.reload();
// 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);
}