updated control access

This commit is contained in:
Quality System Admin
2025-10-16 02:36:32 +03:00
parent 50c791e242
commit c96039542d
266 changed files with 32656 additions and 9 deletions

View File

@@ -0,0 +1,890 @@
{% extends "base.html" %}
{% block title %}User Management - Simplified{% endblock %}
{% block extra_css %}
<style>
/* Theme-aware card styles */
.user-management-page .card {
text-align: left !important;
flex: none !important;
max-width: 100% !important;
padding: 0 !important;
border-radius: 5px !important;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1) !important;
}
/* Light mode card styles */
body.light-mode .user-management-page .card {
background: #fff !important;
color: #000 !important;
border: 1px solid #ddd !important;
}
body.light-mode .user-management-page .card-body {
background: #fff !important;
color: #000 !important;
padding: 1.25rem !important;
}
body.light-mode .user-management-page .card-header {
background-color: #f8f9fa !important;
color: #333 !important;
border-bottom: 1px solid #ddd !important;
padding: 0.75rem 1.25rem !important;
border-top-left-radius: 4px !important;
border-top-right-radius: 4px !important;
}
/* Dark mode card styles */
body.dark-mode .user-management-page .card {
background: #1e1e1e !important;
color: #fff !important;
border: 1px solid #444 !important;
}
body.dark-mode .user-management-page .card-body {
background: #1e1e1e !important;
color: #fff !important;
padding: 1.25rem !important;
}
body.dark-mode .user-management-page .card-header {
background-color: #2a2a2a !important;
color: #ccc !important;
border-bottom: 1px solid #444 !important;
padding: 0.75rem 1.25rem !important;
border-top-left-radius: 4px !important;
border-top-right-radius: 4px !important;
}
/* Theme-aware header text */
.user-management-page .card-header h5 {
margin: 0 !important;
font-weight: 600 !important;
}
body.light-mode .user-management-page .card-header h5 {
color: #333 !important;
}
body.dark-mode .user-management-page .card-header h5 {
color: #ccc !important;
}
/* Theme-aware role badges */
.user-role-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 0.8em;
font-weight: bold;
}
.role-superadmin { background-color: #dc3545; color: white; }
.role-admin { background-color: #fd7e14; color: white; }
.role-manager { background-color: #007bff; color: white; }
.role-worker { background-color: #28a745; color: white; }
/* Dark mode badge adjustments */
body.dark-mode .role-manager { background-color: #0056b3; }
body.dark-mode .role-worker { background-color: #1e7e34; }
/* Theme-aware module badges */
.module-badges .badge {
margin-right: 5px;
margin-bottom: 3px;
}
body.light-mode .module-badges .badge {
background-color: #007bff;
color: white;
}
body.dark-mode .module-badges .badge {
background-color: #0056b3;
color: white;
}
.form-group {
margin-bottom: 15px;
}
.module-checkboxes {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-top: 5px;
}
.module-checkbox {
display: flex;
align-items: center;
gap: 5px;
}
/* Theme-aware info panels */
.access-level-info {
border-left: 4px solid #007bff;
padding: 10px;
margin-top: 10px;
font-size: 0.9em;
}
body.light-mode .access-level-info {
background-color: #f8f9fa;
color: #333;
}
body.dark-mode .access-level-info {
background-color: #2a2a2a;
color: #ccc;
border-left-color: #0056b3;
}
.table-warning {
background-color: #fff3cd !important;
}
body.dark-mode .table-warning {
background-color: #664d03 !important;
color: #fff !important;
}
/* Colored card headers - respect theme but use accent colors */
.card-header.bg-primary {
background-color: #007bff !important;
color: white !important;
border-color: #007bff !important;
}
.card-header.bg-primary h5 {
color: white !important;
margin: 0 !important;
}
.card-header.bg-success {
background-color: #28a745 !important;
color: white !important;
border-color: #28a745 !important;
}
.card-header.bg-success h5 {
color: white !important;
margin: 0 !important;
}
/* Dark mode adjustments for colored headers */
body.dark-mode .card-header.bg-primary {
background-color: #0056b3 !important;
border-color: #0056b3 !important;
}
body.dark-mode .card-header.bg-success {
background-color: #1e7e34 !important;
border-color: #1e7e34 !important;
}
.btn-block {
width: 100%;
}
#quickModuleEdit {
border: 2px solid #198754;
border-radius: 8px;
padding: 15px;
background-color: #f8fff8;
}
/* Theme-aware table styling */
.thead-dark {
background-color: #343a40 !important;
color: white !important;
}
body.dark-mode .thead-dark {
background-color: #1a1a1a !important;
}
.user-management-page .table {
background-color: transparent !important;
}
body.light-mode .user-management-page .table {
color: #000 !important;
}
body.dark-mode .user-management-page .table {
color: #fff !important;
}
.user-management-page .table th,
.user-management-page .table td {
border-top: 1px solid #dee2e6 !important;
padding: 0.75rem !important;
vertical-align: top !important;
}
body.dark-mode .user-management-page .table th,
body.dark-mode .user-management-page .table td {
border-top: 1px solid #444 !important;
}
.user-management-page .table-striped tbody tr:nth-of-type(odd) {
background-color: rgba(0, 0, 0, 0.05) !important;
}
body.dark-mode .user-management-page .table-striped tbody tr:nth-of-type(odd) {
background-color: rgba(255, 255, 255, 0.05) !important;
}
.user-management-page .table-hover tbody tr:hover {
background-color: rgba(0, 0, 0, 0.075) !important;
cursor: pointer;
}
body.dark-mode .user-management-page .table-hover tbody tr:hover {
background-color: rgba(255, 255, 255, 0.075) !important;
}
/* Theme-aware form elements */
.user-management-page .form-control {
border: 1px solid #ced4da !important;
}
body.light-mode .user-management-page .form-control {
background-color: #fff !important;
color: #000 !important;
border-color: #ced4da !important;
}
body.dark-mode .user-management-page .form-control {
background-color: #2a2a2a !important;
color: #fff !important;
border-color: #444 !important;
}
.user-management-page .form-control:focus {
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
}
body.light-mode .user-management-page .form-control:focus {
border-color: #80bdff !important;
}
body.dark-mode .user-management-page .form-control:focus {
border-color: #0056b3 !important;
}
.user-management-page label {
font-weight: 500 !important;
margin-bottom: 0.5rem !important;
}
body.light-mode .user-management-page label {
color: #333 !important;
}
body.dark-mode .user-management-page label {
color: #ccc !important;
}
/* Theme-aware buttons */
.user-management-page .btn-primary {
background-color: #007bff !important;
border-color: #007bff !important;
}
.user-management-page .btn-primary:hover {
background-color: #0056b3 !important;
border-color: #0056b3 !important;
}
body.dark-mode .user-management-page .btn-primary {
background-color: #0056b3 !important;
border-color: #0056b3 !important;
}
body.dark-mode .user-management-page .btn-primary:hover {
background-color: #004085 !important;
border-color: #004085 !important;
}
/* Additional theme-aware elements */
#quickModuleEdit {
border-radius: 8px;
padding: 15px;
border: 2px solid #28a745;
}
body.light-mode #quickModuleEdit {
background-color: #f8fff8;
color: #333;
}
body.dark-mode #quickModuleEdit {
background-color: #1a2f1a;
color: #ccc;
border-color: #1e7e34;
}
/* Theme-aware text colors */
body.light-mode .user-management-page h2,
body.light-mode .user-management-page p {
color: #000 !important;
}
body.dark-mode .user-management-page h2,
body.dark-mode .user-management-page p {
color: #fff !important;
}
body.light-mode .user-management-page .text-muted {
color: #6c757d !important;
}
body.dark-mode .user-management-page .text-muted {
color: #adb5bd !important;
}
/* Theme-aware select dropdown */
body.light-mode .user-management-page select.form-control {
background-color: #fff !important;
color: #000 !important;
}
body.dark-mode .user-management-page select.form-control {
background-color: #2a2a2a !important;
color: #fff !important;
}
</style>
{% endblock %}
{% block content %}
<div class="container mt-4 user-management-page">
<h2>Simplified User Management</h2>
<p class="text-muted">Manage users with the new 4-tier permission system: Superadmin → Admin → Manager → Worker</p>
<div class="row">
<!-- Create User Card -->
<div class="col-md-6">
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">👤 Create New User</h5>
</div>
<div class="card-body">
<form id="addUserForm" method="POST" action="/create_user_simple">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username" class="form-control" required>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label for="role">Role:</label>
<select id="role" name="role" class="form-control" required onchange="updateModuleSelection()">
<option value="">Select a role...</option>
<option value="superadmin">Superadmin - Full system access</option>
<option value="admin">Admin - Full app access (except role permissions)</option>
<option value="manager">Manager - Module-based access (can have multiple modules)</option>
<option value="worker">Worker - Limited module access (can have multiple modules)</option>
</select>
</div>
<div class="form-group" id="moduleSelection" style="display: none;">
<label>Modules:</label>
<div class="module-checkboxes">
<div class="module-checkbox">
<input type="checkbox" id="module_quality" name="modules" value="quality">
<label for="module_quality">Quality Control</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="module_warehouse" name="modules" value="warehouse">
<label for="module_warehouse">Warehouse Management</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="module_labels" name="modules" value="labels">
<label for="module_labels">Label Management</label>
</div>
</div>
<div id="accessLevelInfo" class="access-level-info" style="display: none;"></div>
</div>
<button type="submit" class="btn btn-primary btn-block">Create User</button>
</form>
</div>
</div>
</div>
<!-- Edit User Rights Card -->
<div class="col-md-6">
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h5 class="mb-0">⚙️ Edit User Rights</h5>
</div>
<div class="card-body">
<div id="userRightsPanel">
<div class="text-center text-muted py-4">
<i class="fas fa-user-edit fa-3x mb-3"></i>
<p>Select a user from the table below to edit their rights and module access.</p>
</div>
<!-- Quick Module Management for Selected User -->
<div id="quickModuleEdit" style="display: none;">
<h6>Quick Module Management</h6>
<div class="mb-3">
<strong>User:</strong> <span id="selectedUserName"></span>
<br><small class="text-muted">Role: <span id="selectedUserRole"></span></small>
</div>
<div class="form-group">
<label>Current Modules:</label>
<div id="currentModules" class="module-checkboxes">
<div class="module-checkbox">
<input type="checkbox" id="quick_module_quality" name="quick_modules" value="quality">
<label for="quick_module_quality">Quality Control</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="quick_module_warehouse" name="quick_modules" value="warehouse">
<label for="quick_module_warehouse">Warehouse Management</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="quick_module_labels" name="quick_modules" value="labels">
<label for="quick_module_labels">Label Management</label>
</div>
</div>
</div>
<div class="btn-group btn-group-sm w-100">
<button type="button" class="btn btn-success" onclick="updateUserModules()">
💾 Save Module Changes
</button>
<button type="button" class="btn btn-info" onclick="showFullEditModal()">
✏️ Full Edit
</button>
<button type="button" class="btn btn-danger" onclick="deleteSelectedUser()">
🗑️ Delete User
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Current Users Table -->
<div class="card">
<div class="card-header">
<h5>👥 Current Users</h5>
</div>
<div class="card-body">
{% if users %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="thead-dark">
<tr>
<th>Username</th>
<th>Role</th>
<th>Modules</th>
<th>Access Level</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="user-row" data-user-id="{{ user.id }}" data-username="{{ user.username }}" data-role="{{ user.role }}" data-modules="{{ (user.get_modules() or []) | tojson }}">
<td>
<strong>{{ user.username }}</strong>
</td>
<td>
<span class="user-role-badge role-{{ user.role }}">
{{ user.role.title() }}
</span>
</td>
<td>
<div class="module-badges">
{% if user.role in ['superadmin', 'admin'] %}
<span class="badge bg-secondary">All Modules</span>
{% elif user.modules %}
{% for module in user.get_modules() %}
<span class="badge bg-info">{{ module.title() }}</span>
{% endfor %}
{% else %}
<span class="text-muted">No modules assigned</span>
{% endif %}
</div>
</td>
<td>
<small class="text-muted">
{% if user.role == 'superadmin' %}
Full system access
{% elif user.role == 'admin' %}
Full app access
{% elif user.role == 'manager' %}
Full module access + reports
{% elif user.role == 'worker' %}
Basic operations only (no reports) - Can have multiple modules
{% endif %}
</small>
</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary select-user-btn"
data-user-id="{{ user.id }}"
data-username="{{ user.username }}"
data-role="{{ user.role }}"
data-modules="{{ (user.get_modules() or []) | tojson }}"
title="Select for quick edit">
📝 Select
</button>
<button class="btn btn-outline-info edit-user-btn"
data-user-id="{{ user.id }}"
data-username="{{ user.username }}"
data-role="{{ user.role }}"
data-modules="{{ (user.get_modules() or []) | tojson }}"
title="Full edit">
⚙️ Edit
</button>
{% if user.username != session.get('user') %}
<button class="btn btn-outline-danger delete-user-btn"
data-user-id="{{ user.id }}"
data-username="{{ user.username }}"
title="Delete user">
🗑️
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center text-muted py-4">
<i class="fas fa-users fa-3x mb-3"></i>
<p>No users found. Create your first user above!</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Edit User Modal -->
<div class="modal fade" id="editUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="editUserForm" method="POST" action="/edit_user_simple">
<div class="modal-body">
<input type="hidden" id="edit_user_id" name="user_id">
<div class="form-group">
<label for="edit_username">Username:</label>
<input type="text" id="edit_username" name="username" class="form-control" required>
</div>
<div class="form-group">
<label for="edit_password">Password (leave blank to keep current):</label>
<input type="password" id="edit_password" name="password" class="form-control">
</div>
<div class="form-group">
<label for="edit_role">Role:</label>
<select id="edit_role" name="role" class="form-control" required onchange="updateEditModuleSelection()">
<option value="">Select a role...</option>
<option value="superadmin">Superadmin - Full system access</option>
<option value="admin">Admin - Full app access (except role permissions)</option>
<option value="manager">Manager - Module-based access (can have multiple modules)</option>
<option value="worker">Worker - Limited module access (can have multiple modules)</option>
</select>
</div>
<div class="form-group" id="editModuleSelection" style="display: none;">
<label>Modules:</label>
<div class="module-checkboxes">
<div class="module-checkbox">
<input type="checkbox" id="edit_module_quality" name="modules" value="quality">
<label for="edit_module_quality">Quality Control</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="edit_module_warehouse" name="modules" value="warehouse">
<label for="edit_module_warehouse">Warehouse Management</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="edit_module_labels" name="modules" value="labels">
<label for="edit_module_labels">Label Management</label>
</div>
</div>
<div id="editAccessLevelInfo" class="access-level-info" style="display: none;"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
<script>
function updateModuleSelection() {
const role = document.getElementById('role').value;
const moduleSelection = document.getElementById('moduleSelection');
const accessLevelInfo = document.getElementById('accessLevelInfo');
const checkboxes = document.querySelectorAll('#moduleSelection input[type="checkbox"]');
if (role === 'superadmin' || role === 'admin') {
moduleSelection.style.display = 'none';
checkboxes.forEach(cb => cb.checked = false);
accessLevelInfo.style.display = 'none';
} else if (role === 'manager' || role === 'worker') {
moduleSelection.style.display = 'block';
checkboxes.forEach(cb => cb.checked = false);
if (role === 'manager') {
accessLevelInfo.style.display = 'block';
accessLevelInfo.innerHTML = '<strong>Manager Access:</strong> Can have multiple modules. Full access to assigned modules including reports and management functions.';
} else if (role === 'worker') {
accessLevelInfo.style.display = 'block';
accessLevelInfo.innerHTML = '<strong>Worker Access:</strong> Can have multiple modules. Limited to basic operations only (no reports or management functions). <br><small>Examples: Quality worker can scan, Warehouse worker can move orders, etc.</small>';
}
} else {
moduleSelection.style.display = 'none';
accessLevelInfo.style.display = 'none';
}
}
function updateEditModuleSelection() {
const role = document.getElementById('edit_role').value;
const moduleSelection = document.getElementById('editModuleSelection');
const accessLevelInfo = document.getElementById('editAccessLevelInfo');
const checkboxes = document.querySelectorAll('#editModuleSelection input[type="checkbox"]');
if (role === 'superadmin' || role === 'admin') {
moduleSelection.style.display = 'none';
checkboxes.forEach(cb => cb.checked = false);
accessLevelInfo.style.display = 'none';
} else if (role === 'manager' || role === 'worker') {
moduleSelection.style.display = 'block';
if (role === 'manager') {
accessLevelInfo.style.display = 'block';
accessLevelInfo.innerHTML = '<strong>Manager Access:</strong> Can have multiple modules. Full access to assigned modules including reports and management functions.';
} else if (role === 'worker') {
accessLevelInfo.style.display = 'block';
accessLevelInfo.innerHTML = '<strong>Worker Access:</strong> Can have multiple modules. Limited to basic operations only (no reports or management functions). <br><small>Examples: Quality worker can scan, Warehouse worker can move orders, etc.</small>';
}
} else {
moduleSelection.style.display = 'none';
accessLevelInfo.style.display = 'none';
}
}
// Global variable to store selected user
let selectedUser = null;
function selectUserForQuickEdit(userId, username, role, modules) {
selectedUser = {id: userId, username: username, role: role, modules: modules};
// Update the quick edit panel
document.getElementById('selectedUserName').textContent = username;
document.getElementById('selectedUserRole').textContent = role;
// Clear all module checkboxes first
const checkboxes = document.querySelectorAll('#currentModules input[type="checkbox"]');
checkboxes.forEach(cb => cb.checked = false);
// Check the appropriate modules
if (modules && Array.isArray(modules)) {
modules.forEach(module => {
const checkbox = document.getElementById('quick_module_' + module);
if (checkbox) checkbox.checked = true;
});
}
// Show the quick edit panel
document.getElementById('quickModuleEdit').style.display = 'block';
document.querySelector('#userRightsPanel .text-center').style.display = 'none';
// Highlight selected row
document.querySelectorAll('.user-row').forEach(row => {
row.classList.remove('table-warning');
});
document.querySelector(`[data-user-id="${userId}"]`).classList.add('table-warning');
}
function updateUserModules() {
if (!selectedUser) {
alert('No user selected');
return;
}
// Get selected modules
const checkboxes = document.querySelectorAll('#currentModules input[type="checkbox"]:checked');
const modules = Array.from(checkboxes).map(cb => cb.value);
// Validate modules for the role
if ((selectedUser.role === 'manager' || selectedUser.role === 'worker') && modules.length === 0) {
alert(`${selectedUser.role}s must have at least one module assigned.`);
return;
}
// Create and submit form
const form = document.createElement('form');
form.method = 'POST';
form.action = '/quick_update_modules';
// Add user ID
const userIdInput = document.createElement('input');
userIdInput.type = 'hidden';
userIdInput.name = 'user_id';
userIdInput.value = selectedUser.id;
form.appendChild(userIdInput);
// Add modules
modules.forEach(module => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'modules';
input.value = module;
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
}
function showFullEditModal() {
if (!selectedUser) {
alert('No user selected');
return;
}
editUser(selectedUser.id, selectedUser.username, selectedUser.role, selectedUser.modules);
}
function deleteSelectedUser() {
if (!selectedUser) {
alert('No user selected');
return;
}
deleteUser(selectedUser.id, selectedUser.username);
}
function editUser(userId, username, role, modules) {
document.getElementById('edit_user_id').value = userId;
document.getElementById('edit_username').value = username;
document.getElementById('edit_role').value = role;
document.getElementById('edit_password').value = '';
// Clear all module checkboxes first
const checkboxes = document.querySelectorAll('#editModuleSelection input[type="checkbox"]');
checkboxes.forEach(cb => cb.checked = false);
// Check the appropriate modules
if (modules && Array.isArray(modules)) {
modules.forEach(module => {
const checkbox = document.getElementById('edit_module_' + module);
if (checkbox) checkbox.checked = true;
});
}
updateEditModuleSelection();
const modal = new bootstrap.Modal(document.getElementById('editUserModal'));
modal.show();
}
function deleteUser(userId, username) {
if (confirm(`Are you sure you want to delete user "${username}"?`)) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '/delete_user_simple';
const userIdInput = document.createElement('input');
userIdInput.type = 'hidden';
userIdInput.name = 'user_id';
userIdInput.value = userId;
form.appendChild(userIdInput);
document.body.appendChild(form);
form.submit();
}
}
function deleteUser(userId, username) {
if (confirm(`Are you sure you want to delete user "${username}"?`)) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '/delete_user_simple';
const userIdInput = document.createElement('input');
userIdInput.type = 'hidden';
userIdInput.name = 'user_id';
userIdInput.value = userId;
form.appendChild(userIdInput);
document.body.appendChild(form);
form.submit();
}
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
updateModuleSelection();
// Add event listeners for select user buttons
document.querySelectorAll('.select-user-btn').forEach(button => {
button.addEventListener('click', function() {
const userId = this.dataset.userId;
const username = this.dataset.username;
const role = this.dataset.role;
let modules = [];
try {
modules = JSON.parse(this.dataset.modules) || [];
} catch (e) {
console.log('Error parsing modules for user:', username, e);
modules = [];
}
selectUserForQuickEdit(userId, username, role, modules);
});
});
// Add event listeners for edit buttons
document.querySelectorAll('.edit-user-btn').forEach(button => {
button.addEventListener('click', function() {
const userId = this.dataset.userId;
const username = this.dataset.username;
const role = this.dataset.role;
let modules = [];
try {
modules = JSON.parse(this.dataset.modules) || [];
} catch (e) {
console.log('Error parsing modules for user:', username, e);
modules = [];
}
editUser(userId, username, role, modules);
});
});
// Add event listeners for delete buttons
document.querySelectorAll('.delete-user-btn').forEach(button => {
button.addEventListener('click', function() {
const userId = this.dataset.userId;
const username = this.dataset.username;
deleteUser(userId, username);
});
});
});
</script>
{% endblock %}