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

@@ -4,11 +4,16 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Flask App{% endblock %}</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<!-- Base CSS for common styles -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/base.css') }}">
<!-- Legacy CSS for backward compatibility (temporarily) -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<!-- Page-specific CSS -->
{% block extra_css %}{% endblock %}
{% block head %}{% endblock %}
</head>
<body class="light-mode">
@@ -56,5 +61,8 @@
{% if request.endpoint != 'main.fg_quality' %}
<script src="{{ url_for('static', filename='script.js') }}"></script>
{% endif %}
<!-- Bootstrap JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,440 @@
{% extends "base.html" %}
{% block title %}FG Quality Module{% endblock %}
{% block content %}
<div class="scan-container">
<!-- Reports Card -->
<div class="card report-form-card">
<h3>FG Quality Reports</h3>
<!-- Reports List with Label-Button Layout -->
<div class="reports-grid">
<div class="form-centered">
<label class="report-description">Daily report will export all FG orders scanned at quality scanning points</label>
<button class="btn report-btn" data-report="1">Daily Complete FG Orders Report</button>
</div>
<div class="form-centered">
<label class="report-description">Select day for daily FG report will export all orders scanned at quality scanning points</label>
<button class="btn report-btn" id="select-day-report">Select Day Daily FG Report</button>
</div>
<div class="form-centered">
<label class="report-description">Select date range for custom FG report - from start date 00:00 to end date 23:59</label>
<button class="btn report-btn" id="date-range-report">Date Range FG Report</button>
</div>
<div class="form-centered">
<label class="report-description">5-day report will export all FG orders scanned at quality scanning points</label>
<button class="btn report-btn" data-report="2">5-Day Complete FG Orders Report</button>
</div>
<div class="form-centered">
<label class="report-description">Report on FG items with quality issues for the current day</label>
<button class="btn report-btn" data-report="3">Report on FG Items with Defects for the Current Day</button>
</div>
<div class="form-centered">
<label class="report-description">Select specific day for FG quality defects report - items with quality issues</label>
<button class="btn report-btn" id="select-day-defects-report">Select Day FG Quality Defects Report</button>
</div>
<div class="form-centered">
<label class="report-description">Select date range for FG quality defects report - items with quality issues between two dates</label>
<button class="btn report-btn" id="date-range-defects-report">Date Range FG Quality Defects Report</button>
</div>
<div class="form-centered">
<label class="report-description">Report on FG items with quality issues for the last 5 days</label>
<button class="btn report-btn" data-report="4">Report on FG Items with Defects for the Last 5 Days</button>
</div>
<div class="form-centered">
<label class="report-description">Report all FG entries from the database</label>
<button class="btn report-btn" data-report="5">Report FG Database</button>
</div>
</div>
<!-- Separator -->
<div class="report-separator"></div>
<!-- Export Section -->
<div class="export-section">
<div class="form-centered last-buttons">
<label class="export-description">Export current report as:</label>
<div class="button-row">
<button class="btn export-btn" id="export-csv">Export CSV</button>
<!-- <button class="btn export-btn" id="export-excel">Export excell</button> -->
{% if session.get('role') == 'superadmin' %}
<button class="btn export-btn test-db-btn" id="test-database">Test FG Database</button>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Data Display Card -->
<div class="card report-table-card">
<h3 id="report-title">No data to display, please select a report.</h3>
<div class="report-table-container">
<table class="scan-table" id="report-table">
<thead>
<tr>
<!-- Table headers will be dynamically populated -->
</tr>
</thead>
<tbody>
<!-- Table data will be dynamically populated -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Calendar Popup Modal -->
<div id="calendar-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h4>Select Date for Daily FG Report</h4>
<span class="close-modal">&times;</span>
</div>
<div class="modal-body">
<div class="calendar-container">
<div class="calendar-header">
<button id="prev-month" class="calendar-nav">&lt;</button>
<h3 id="calendar-month-year"></h3>
<button id="next-month" class="calendar-nav">&gt;</button>
</div>
<div class="calendar-grid">
<div class="calendar-weekdays">
<div>Sun</div>
<div>Mon</div>
<div>Tue</div>
<div>Wed</div>
<div>Thu</div>
<div>Fri</div>
<div>Sat</div>
</div>
<div class="calendar-days" id="calendar-days">
<!-- Days will be populated by JavaScript -->
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" id="cancel-date">Cancel</button>
<button class="btn btn-primary" id="confirm-date" disabled>Generate Report</button>
</div>
</div>
</div>
<!-- Date Range Popup Modal -->
<div id="date-range-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h4>Select Date Range for FG Report</h4>
<span class="close-modal" id="close-date-range">&times;</span>
</div>
<div class="modal-body">
<div class="date-range-container">
<div class="date-input-group">
<label for="start-date">Start Date:</label>
<input type="date" id="start-date" class="date-input" />
<small class="date-help">Report will include data from 00:00:00</small>
</div>
<div class="date-input-group">
<label for="end-date">End Date:</label>
<input type="date" id="end-date" class="date-input" />
<small class="date-help">Report will include data until 23:59:59</small>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" id="cancel-date-range">Cancel</button>
<button class="btn btn-primary" id="confirm-date-range" disabled>Generate Report</button>
</div>
</div>
</div>
{% endblock %}
{% block head %}
<script>
// Debug check - this will run immediately when the page loads
console.log('FG Quality page loaded - checking script conflicts');
window.addEventListener('DOMContentLoaded', function() {
console.log('FG Quality DOM loaded');
// Add a visible indicator that our script loaded
setTimeout(() => {
const titleElement = document.getElementById('report-title');
if (titleElement && titleElement.textContent === 'No data to display, please select a report.') {
titleElement.textContent = '🟢 FG Quality Module Ready - Select a report';
}
}, 100);
});
</script>
<script src="{{ url_for('static', filename='fg_quality.js') }}"></script>
<script>
// Theme functionality for FG Quality page
document.addEventListener('DOMContentLoaded', function() {
const body = document.body;
const themeToggleButton = document.getElementById('theme-toggle');
// Helper function to update the theme toggle button text
function updateThemeToggleButtonText() {
if (body.classList.contains('dark-mode')) {
themeToggleButton.textContent = 'Change to Light Mode';
} else {
themeToggleButton.textContent = 'Change to Dark Mode';
}
}
// Check and apply the saved theme from localStorage
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
body.classList.toggle('dark-mode', savedTheme === 'dark');
}
// Update the button text based on the current theme
if (themeToggleButton) {
updateThemeToggleButtonText();
// Toggle the theme on button click
themeToggleButton.addEventListener('click', () => {
const isDarkMode = body.classList.toggle('dark-mode');
localStorage.setItem('theme', isDarkMode ? 'dark' : 'light');
updateThemeToggleButtonText(); // Update the button text after toggling
});
}
});
// Override script - runs after both script.js and fg_quality.js
window.addEventListener('DOMContentLoaded', function() {
// Wait a bit to ensure all scripts have loaded
setTimeout(() => {
console.log('🔧 FG Quality Override Script - Fixing event handlers');
// Override the problematic calendar functionality
const selectDayReport = document.getElementById('select-day-report');
const selectDayDefectsReport = document.getElementById('select-day-defects-report');
const dateRangeReport = document.getElementById('date-range-report');
const dateRangeDefectsReport = document.getElementById('date-range-defects-report');
let currentFGReportType = null;
let selectedFGDate = null;
// Clear and re-add event listeners with FG-specific functionality
if (selectDayReport) {
// Remove all existing listeners by cloning
const newSelectDayReport = selectDayReport.cloneNode(true);
selectDayReport.parentNode.replaceChild(newSelectDayReport, selectDayReport);
newSelectDayReport.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('🎯 FG Select Day Report clicked');
currentFGReportType = '6';
showFGCalendarModal();
});
}
if (dateRangeReport) {
const newDateRangeReport = dateRangeReport.cloneNode(true);
dateRangeReport.parentNode.replaceChild(newDateRangeReport, dateRangeReport);
newDateRangeReport.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('🎯 FG Date Range Report clicked');
currentFGReportType = '7';
showFGDateRangeModal();
});
}
// FG-specific calendar modal functionality
function showFGCalendarModal() {
const calendarModal = document.getElementById('calendar-modal');
if (calendarModal) {
calendarModal.style.display = 'block';
generateFGCalendar();
}
}
function showFGDateRangeModal() {
const dateRangeModal = document.getElementById('date-range-modal');
if (dateRangeModal) {
dateRangeModal.style.display = 'block';
const today = new Date().toISOString().split('T')[0];
document.getElementById('start-date').value = today;
document.getElementById('end-date').value = today;
}
}
function generateFGCalendar() {
const calendarDays = document.getElementById('calendar-days');
const monthYear = document.getElementById('calendar-month-year');
if (!calendarDays || !monthYear) return;
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
monthYear.textContent = `${currentDate.toLocaleString('default', { month: 'long' })} ${year}`;
// Clear previous days
calendarDays.innerHTML = '';
// Get first day of month and number of days
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
// Add empty cells for previous month
for (let i = 0; i < firstDay; i++) {
const emptyDay = document.createElement('div');
emptyDay.className = 'calendar-day empty';
calendarDays.appendChild(emptyDay);
}
// Add days of current month
for (let day = 1; day <= daysInMonth; day++) {
const dayElement = document.createElement('div');
dayElement.className = 'calendar-day';
dayElement.textContent = day;
// Highlight October 15 as it has FG data
if (month === 9 && day === 15) { // October is month 9
dayElement.style.backgroundColor = '#e3f2fd';
dayElement.style.border = '2px solid #2196f3';
dayElement.title = 'FG data available';
}
dayElement.addEventListener('click', () => {
// Remove previous selection
document.querySelectorAll('.calendar-day.selected').forEach(el => {
el.classList.remove('selected');
});
// Add selection to clicked day
dayElement.classList.add('selected');
// Set selected date
selectedFGDate = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
console.log('🗓️ FG Calendar date selected:', selectedFGDate);
// Enable confirm button
const confirmButton = document.getElementById('confirm-date');
if (confirmButton) {
confirmButton.disabled = false;
}
});
calendarDays.appendChild(dayElement);
}
}
// Override confirm button
const confirmDateBtn = document.getElementById('confirm-date');
if (confirmDateBtn) {
const newConfirmBtn = confirmDateBtn.cloneNode(true);
confirmDateBtn.parentNode.replaceChild(newConfirmBtn, confirmDateBtn);
newConfirmBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('✅ FG Calendar confirm clicked:', selectedFGDate, 'Report type:', currentFGReportType);
if (selectedFGDate && currentFGReportType) {
const url = `/generate_fg_report?report=${currentFGReportType}&date=${selectedFGDate}`;
console.log('🚀 Calling FG endpoint:', url);
document.getElementById('report-title').textContent = 'Loading FG data...';
fetch(url)
.then(response => response.json())
.then(data => {
console.log('📊 FG Data received:', data);
const table = document.getElementById('report-table');
const thead = table.querySelector('thead tr');
const tbody = table.querySelector('tbody');
// Clear existing content
thead.innerHTML = '';
tbody.innerHTML = '';
// Add headers
if (data.headers) {
data.headers.forEach(header => {
const th = document.createElement('th');
th.textContent = header;
thead.appendChild(th);
});
}
// Add rows
if (data.rows && data.rows.length > 0) {
data.rows.forEach(row => {
const tr = document.createElement('tr');
row.forEach(cell => {
const td = document.createElement('td');
td.textContent = cell || '';
tr.appendChild(td);
});
tbody.appendChild(tr);
});
document.getElementById('report-title').textContent = `FG Daily Report for ${selectedFGDate} (${data.rows.length} records)`;
} else {
const tr = document.createElement('tr');
const td = document.createElement('td');
td.colSpan = data.headers ? data.headers.length : 1;
td.textContent = data.message || 'No FG data found';
td.style.textAlign = 'center';
tr.appendChild(td);
tbody.appendChild(tr);
document.getElementById('report-title').textContent = `FG Daily Report for ${selectedFGDate} - No data`;
}
})
.catch(error => {
console.error('❌ Error fetching FG data:', error);
document.getElementById('report-title').textContent = 'Error loading FG data';
});
// Hide modal
document.getElementById('calendar-modal').style.display = 'none';
}
});
}
// Override date range confirm button
const confirmDateRangeBtn = document.getElementById('confirm-date-range');
if (confirmDateRangeBtn) {
const newConfirmRangeBtn = confirmDateRangeBtn.cloneNode(true);
confirmDateRangeBtn.parentNode.replaceChild(newConfirmRangeBtn, confirmDateRangeBtn);
newConfirmRangeBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const startDate = document.getElementById('start-date').value;
const endDate = document.getElementById('end-date').value;
console.log('📅 FG Date range confirm:', startDate, 'to', endDate);
if (startDate && endDate && currentFGReportType) {
const url = `/generate_fg_report?report=${currentFGReportType}&start_date=${startDate}&end_date=${endDate}`;
console.log('🚀 Calling FG range endpoint:', url);
fetch(url)
.then(response => response.json())
.then(data => {
console.log('📊 FG Range data received:', data);
// Handle response similar to above
document.getElementById('report-title').textContent = `FG Date Range Report (${startDate} to ${endDate})`;
})
.catch(error => {
console.error('❌ Error fetching FG range data:', error);
});
// Hide modal
document.getElementById('date-range-modal').style.display = 'none';
}
});
}
console.log('✅ FG Quality Override Script - Event handlers fixed');
}, 500); // Wait 500ms to ensure all other scripts are loaded
});
</script>
{% endblock %}

View File

@@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block title %}Reports Module{% endblock %}
{% block content %}
<div class="reports-container">
<h1>Reports Module</h1>
<p>Access different quality and production reporting modules for data analysis and verification.</p>
<!-- Row of evenly distributed cards -->
<div class="dashboard-container">
<!-- Card 1: Quality Reports -->
<div class="dashboard-card">
<h3>Quality Reports</h3>
<p>Access quality scanning reports and analysis for production process verification.</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="{{ url_for('main.quality') }}" class="btn">Launch Quality Reports</a>
</div>
</div>
<!-- Card 2: FG Quality Reports -->
<div class="dashboard-card">
<h3>FG Quality Reports</h3>
<p>Finished Goods quality reports and analysis for final product verification.</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="{{ url_for('main.fg_quality') }}" class="btn">Launch FG Quality Reports</a>
</div>
</div>
<!-- Card 3: Additional Reports (Placeholder for future expansion) -->
<div class="dashboard-card">
<h3>Additional Reports</h3>
<p>Access additional reporting modules and data analysis tools.</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button class="btn" disabled style="opacity: 0.5;">Coming Soon</button>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,454 +0,0 @@
{% extends "base.html" %}
{% block title %}Role Permissions Management{% endblock %}
{% block head %}
<style>
.permissions-container {
max-width: 1600px;
margin: 0 auto;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.permissions-table-container {
background: white;
border-radius: 15px;
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
overflow: hidden;
margin: 0 auto 30px auto;
border: 2px solid #dee2e6;
max-width: 100%;
}
.permissions-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
margin: 0;
}
.permissions-table thead {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
}
.permissions-table th {
padding: 15px 12px;
text-align: left;
font-weight: 600;
border-bottom: 2px solid rgba(255,255,255,0.2);
}
.permissions-table th:nth-child(1) { width: 15%; }
.permissions-table th:nth-child(2) { width: 20%; }
.permissions-table th:nth-child(3) { width: 25%; }
.permissions-table th:nth-child(4) { width: 40%; }
.permission-row {
border-bottom: 2px solid #dee2e6 !important;
transition: all 0.3s ease;
}
.permission-row:hover {
background: linear-gradient(135deg, #e3f2fd, #f0f8ff) !important;
transform: translateY(-1px) !important;
box-shadow: 0 4px 12px rgba(0,123,255,0.15) !important;
}
.role-cell, .module-cell, .page-cell, .functions-cell {
padding: 15px 12px !important;
vertical-align: top !important;
border-right: 1px solid #f1f3f4 !important;
}
.role-cell {
border-left: 4px solid #007bff !important;
}
.module-cell {
border-left: 2px solid #28a745 !important;
}
.page-cell {
border-left: 2px solid #ffc107 !important;
}
.functions-cell {
border-left: 2px solid #dc3545 !important;
}
.role-badge {
display: flex;
align-items: center;
gap: 8px;
background: #e3f2fd;
padding: 8px 12px;
border-radius: 20px;
}
.functions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
}
.function-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: #f8f9fa;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.function-toggle {
display: flex;
align-items: center;
cursor: pointer;
}
.toggle-slider {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
background: #ccc;
border-radius: 20px;
transition: all 0.3s ease;
}
.toggle-slider::before {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
background: white;
border-radius: 50%;
transition: all 0.3s ease;
}
input[type="checkbox"]:checked + .toggle-slider {
background: #007bff;
}
input[type="checkbox"]:checked + .toggle-slider::before {
transform: translateX(20px);
}
input[type="checkbox"] {
display: none;
}
.function-text {
font-size: 12px;
font-weight: 500;
}
.role-separator, .module-separator {
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
.separator-line {
padding: 12px 20px;
font-weight: 600;
color: #495057;
background: linear-gradient(135deg, #e9ecef, #f8f9fa);
}
.module-badge {
padding: 8px 15px;
background: linear-gradient(135deg, #28a745, #20c997);
color: white;
border-radius: 15px;
font-weight: 500;
}
.action-buttons-container {
text-align: center;
margin: 30px 0;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: #007bff;
color: white;
}
.btn-primary:hover {
background: #0056b3;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,123,255,0.3);
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #545b62;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(108,117,125,0.3);
}
</style>
{% endblock %}
{% block content %}
<div class="permissions-container">
<div style="text-align: center; margin-bottom: 40px;">
<h1 style="color: #2c3e50; margin-bottom: 15px; font-weight: 700; font-size: 32px;">
🔐 Role Permissions Management
</h1>
<p style="color: #6c757d; font-size: 16px;">
Configure granular access permissions for each role in the system
</p>
</div>
<!-- 4-Column Permissions Table -->
<div class="permissions-table-container">
<table class="permissions-table" id="permissionsTable">
<thead>
<tr>
<th>👤 Role Name</th>
<th>🏢 Module Name</th>
<th>📄 Page Name</th>
<th>⚙️ Functions & Permissions</th>
</tr>
</thead>
<tbody>
{% set current_role = '' %}
{% set current_module = '' %}
{% for role_name, role_data in roles.items() %}
{% for page_key, page_data in pages.items() %}
{% for section_key, section_data in page_data.sections.items() %}
<!-- Role separator row -->
{% if current_role != role_name %}
{% set current_role = role_name %}
<tr class="role-separator">
<td colspan="4">
<div class="separator-line">
<span>{{ role_data.display_name }} (Level {{ role_data.level }})</span>
</div>
</td>
</tr>
{% endif %}
<!-- Module separator -->
{% if current_module != page_key %}
{% set current_module = page_key %}
<tr class="module-separator">
<td></td>
<td colspan="3">
<div style="padding: 8px 15px;">
<span class="module-badge">{{ page_data.name }}</span>
</div>
</td>
</tr>
{% endif %}
<tr class="permission-row" data-role="{{ role_name }}" data-module="{{ page_key }}">
<td class="role-cell">
<div class="role-badge">
<span>👤</span>
<span>{{ role_data.display_name }}</span>
</div>
</td>
<td class="module-cell">
<span>{{ page_data.name }}</span>
</td>
<td class="page-cell">
<div style="display: flex; align-items: center; gap: 8px;">
<span>📋</span>
<span>{{ section_data.name }}</span>
</div>
</td>
<td class="functions-cell">
<div class="functions-grid">
{% for action in section_data.actions %}
{% set permission_key = page_key + '.' + section_key + '.' + action %}
<div class="function-item" data-permission="{{ permission_key }}" data-role="{{ role_name }}">
<label class="function-toggle">
<input type="checkbox"
data-role="{{ role_name }}"
data-page="{{ page_key }}"
data-section="{{ section_key }}"
data-action="{{ action }}"
onchange="togglePermission('{{ role_name }}', '{{ page_key }}', '{{ section_key }}', '{{ action }}', this)">
<span class="toggle-slider"></span>
</label>
<span class="function-text">{{ action_names[action] }}</span>
</div>
{% endfor %}
</div>
</td>
</tr>
{% endfor %}
{% set current_module = '' %}
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
<!-- Action Buttons -->
<div class="action-buttons-container">
<div class="action-buttons">
<button class="btn btn-secondary" onclick="resetAllToDefaults()">
<span>🔄</span>
Reset All to Defaults
</button>
<button class="btn btn-primary" onclick="saveAllPermissions()">
<span>💾</span>
Save All Changes
</button>
</div>
</div>
</div>
<script>
// Initialize data from backend
let permissions = {{ permissions_json|safe }};
let rolePermissions = {{ role_permissions_json|safe }};
// Toggle permission function
function togglePermission(roleName, pageKey, sectionKey, action, checkbox) {
const isChecked = checkbox.checked;
const permissionKey = `${pageKey}.${sectionKey}.${action}`;
// Update visual state of the function item
const functionItem = checkbox.closest('.function-item');
if (isChecked) {
functionItem.classList.remove('disabled');
} else {
functionItem.classList.add('disabled');
}
// Update data structure (flat array format)
if (!rolePermissions[roleName]) {
rolePermissions[roleName] = [];
}
if (isChecked && !rolePermissions[roleName].includes(permissionKey)) {
rolePermissions[roleName].push(permissionKey);
} else if (!isChecked) {
const index = rolePermissions[roleName].indexOf(permissionKey);
if (index > -1) {
rolePermissions[roleName].splice(index, 1);
}
}
}
// Save all permissions
function saveAllPermissions() {
// Convert flat permission arrays to nested structure for backend
const structuredPermissions = {};
for (const [roleName, permissions] of Object.entries(rolePermissions)) {
structuredPermissions[roleName] = {};
permissions.forEach(permissionKey => {
const [pageKey, sectionKey, action] = permissionKey.split('.');
if (!structuredPermissions[roleName][pageKey]) {
structuredPermissions[roleName][pageKey] = {};
}
if (!structuredPermissions[roleName][pageKey][sectionKey]) {
structuredPermissions[roleName][pageKey][sectionKey] = [];
}
structuredPermissions[roleName][pageKey][sectionKey].push(action);
});
}
fetch('/settings/save_all_role_permissions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
permissions: structuredPermissions
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('All permissions saved successfully!');
} else {
alert('Error saving permissions: ' + data.error);
}
})
.catch(error => {
alert('Error saving permissions: ' + error);
});
}
// Reset all permissions to defaults
function resetAllToDefaults() {
if (confirm('Are you sure you want to reset ALL role permissions to defaults? This will overwrite all current settings.')) {
fetch('/settings/reset_all_role_permissions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error resetting permissions: ' + data.error);
}
})
.catch(error => {
alert('Error resetting permissions: ' + error);
});
}
}
// Initialize checkbox states when page loads
document.addEventListener('DOMContentLoaded', function() {
// Set initial states based on data
document.querySelectorAll('.function-item').forEach(item => {
const roleName = item.dataset.role;
const permissionKey = item.dataset.permission;
const checkbox = item.querySelector('input[type="checkbox"]');
// Check if this role has this permission
const hasPermission = rolePermissions[roleName] && rolePermissions[roleName].includes(permissionKey);
if (hasPermission) {
checkbox.checked = true;
item.classList.remove('disabled');
} else {
checkbox.checked = false;
item.classList.add('disabled');
}
});
});
</script>
{% endblock %}

View File

@@ -44,9 +44,7 @@
<a href="{{ url_for('main.user_management_simple') }}" class="btn" style="background-color: #2196f3; color: white; margin-right: 10px;">
🎯 Manage Users (Simplified)
</a>
<a href="{{ url_for('main.role_permissions') }}" class="btn" style="background-color: #6c757d; color: white;">
⚙️ Advanced Role Permissions
</a>
</div>
<small style="display: block; margin-top: 10px; color: #666;">
Recommended: Use the simplified user management for easier administration

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 %}