updated settings page
This commit is contained in:
@@ -172,37 +172,41 @@ def settings_handler():
|
||||
flash('Access denied: Superadmin only.')
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
# Get users from external MariaDB database
|
||||
# Get users from external MariaDB database with modules (same logic as user_management_simple)
|
||||
users = []
|
||||
try:
|
||||
conn = get_external_db_connection()
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Create users table if it doesn't exist
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(255)
|
||||
)
|
||||
''')
|
||||
|
||||
# Get all users from external database
|
||||
cursor.execute("SELECT id, username, password, role, email FROM users")
|
||||
users_data = cursor.fetchall()
|
||||
|
||||
# Convert to list of dictionaries for template compatibility
|
||||
users = []
|
||||
for user_data in users_data:
|
||||
users.append({
|
||||
'id': user_data[0],
|
||||
'username': user_data[1],
|
||||
'password': user_data[2],
|
||||
'role': user_data[3],
|
||||
'email': user_data[4] if len(user_data) > 4 else None
|
||||
})
|
||||
# Check if users table exists and get users with modules
|
||||
cursor.execute("SHOW TABLES LIKE 'users'")
|
||||
if cursor.fetchone():
|
||||
cursor.execute("SELECT id, username, role, modules FROM users")
|
||||
for row in cursor.fetchall():
|
||||
user_data = {
|
||||
'id': row[0],
|
||||
'username': row[1],
|
||||
'role': row[2],
|
||||
'modules': row[3] if len(row) > 3 else None
|
||||
}
|
||||
# Create a mock user object with get_modules method
|
||||
class MockUser:
|
||||
def __init__(self, data):
|
||||
self.id = data['id']
|
||||
self.username = data['username']
|
||||
self.role = data['role']
|
||||
self.modules = data['modules']
|
||||
|
||||
def get_modules(self):
|
||||
if not self.modules:
|
||||
return []
|
||||
try:
|
||||
import json
|
||||
return json.loads(self.modules)
|
||||
except:
|
||||
return []
|
||||
|
||||
users.append(MockUser(user_data))
|
||||
|
||||
conn.close()
|
||||
|
||||
|
||||
@@ -3,138 +3,318 @@
|
||||
{% block title %}Settings{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card-container">
|
||||
<div class="card">
|
||||
<h3>Manage Users (Legacy)</h3>
|
||||
<ul class="user-list">
|
||||
<div class="settings-layout">
|
||||
<!-- First Column - Users Card -->
|
||||
<div class="settings-column-1">
|
||||
<div class="card">
|
||||
<h3>👥 Current Users</h3>
|
||||
<div class="users-grid">
|
||||
{% for user in users %}
|
||||
<li data-user-id="{{ user.id }}" data-username="{{ user.username }}" data-email="{{ user.email if user.email else '' }}" data-role="{{ user.role }}">
|
||||
<span class="user-name">{{ user.username }}</span>
|
||||
<span class="user-role">Role: {{ user.role }}</span>
|
||||
<button class="btn edit-user-btn" data-user-id="{{ user.id }}" data-username="{{ user.username }}" data-email="{{ user.email if user.email else '' }}" data-role="{{ user.role }}">Edit User</button>
|
||||
<button class="btn delete-btn delete-user-btn" data-user-id="{{ user.id }}" data-username="{{ user.username }}">Delete User</button>
|
||||
</li>
|
||||
<div class="user-card">
|
||||
<div class="user-header">
|
||||
<div class="user-avatar">
|
||||
{% if user.role == 'superadmin' %}
|
||||
👑
|
||||
{% elif user.role == 'admin' %}
|
||||
🛡️
|
||||
{% elif user.role == 'manager' %}
|
||||
🎯
|
||||
{% else %}
|
||||
👤
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">{{ user.username }}</div>
|
||||
<div class="user-role-badge role-{{ user.role }}">
|
||||
{{ user.role.title() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-modules">
|
||||
<div class="modules-section">
|
||||
<strong>Modules:</strong>
|
||||
{% if user.role in ['superadmin', 'admin'] %}
|
||||
<div class="module-tags">
|
||||
<span class="module-tag all-modules">All Modules</span>
|
||||
</div>
|
||||
{% elif user.modules %}
|
||||
<div class="module-tags">
|
||||
{% for module in user.get_modules() %}
|
||||
<span class="module-tag module-{{ module }}">{{ module.title() }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="module-tags">
|
||||
<span class="module-tag no-modules">No modules assigned</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="access-level-section">
|
||||
<strong>Access Level:</strong>
|
||||
<div class="access-description">
|
||||
{% 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 %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<button id="create-user-btn" class="btn create-btn">Create User</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>External Server Settings</h3>
|
||||
<form method="POST" action="{{ url_for('main.save_external_db') }}" class="form-centered">
|
||||
<label for="server_domain">Server Domain/IP Address:</label>
|
||||
<input type="text" id="server_domain" name="server_domain" value="{{ external_settings.get('server_domain', '') }}" required>
|
||||
<label for="port">Port:</label>
|
||||
<input type="number" id="port" name="port" value="{{ external_settings.get('port', '') }}" required>
|
||||
<label for="database_name">Database Name:</label>
|
||||
<input type="text" id="database_name" name="database_name" value="{{ external_settings.get('database_name', '') }}" required>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" value="{{ external_settings.get('username', '') }}" required>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" value="{{ external_settings.get('password', '') }}" required>
|
||||
<button type="submit" class="btn">Save/Update External Database Info Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top: 32px;">
|
||||
<h3>🎯 User & Permissions Management</h3>
|
||||
<p><strong>Simplified 4-Tier System:</strong> Superadmin → Admin → Manager → Worker</p>
|
||||
<p>Streamlined interface with module-based permissions (Quality, Warehouse, Labels)</p>
|
||||
<div style="margin-top: 15px;">
|
||||
<a href="{{ url_for('main.user_management_simple') }}" class="btn" style="background-color: #2196f3; color: white; margin-right: 10px;">
|
||||
🎯 Manage Users (Simplified)
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<small style="display: block; margin-top: 10px; color: #666;">
|
||||
Recommended: Use the simplified user management for easier administration
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.users-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
background: var(--card-bg, #fff);
|
||||
border: 1px solid var(--border-color, #ddd);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.user-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.user-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
font-size: 24px;
|
||||
margin-right: 12px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--avatar-bg, #f0f0f0);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: var(--text-color, #333);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-role-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.role-superadmin {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.role-admin {
|
||||
background-color: #fd7e14;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.role-manager {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.role-worker {
|
||||
background-color: #198754;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.user-modules {
|
||||
font-size: 14px;
|
||||
color: var(--text-color, #555);
|
||||
}
|
||||
|
||||
.modules-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.access-level-section {
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--border-color, #eee);
|
||||
}
|
||||
|
||||
.access-description {
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
color: var(--muted-text, #666);
|
||||
font-style: italic;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.module-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.module-tag {
|
||||
padding: 3px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.all-modules {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.module-quality {
|
||||
background-color: #0dcaf0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.module-warehouse {
|
||||
background-color: #ffc107;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.module-labels {
|
||||
background-color: #20c997;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.no-modules {
|
||||
background-color: #e9ecef;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
body.dark-mode .user-card {
|
||||
background: #2a2a2a;
|
||||
border-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
body.dark-mode .user-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
body.dark-mode .user-modules {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
body.dark-mode .access-level-section {
|
||||
border-top-color: #555;
|
||||
}
|
||||
|
||||
body.dark-mode .access-description {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
body.dark-mode .user-avatar {
|
||||
background: #404040;
|
||||
}
|
||||
|
||||
body.dark-mode .user-card:hover {
|
||||
box-shadow: 0 4px 8px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.users-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
/* Layout Styles for 2-column structure */
|
||||
.settings-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 30px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.settings-column-1 {
|
||||
min-height: fit-content;
|
||||
}
|
||||
|
||||
.settings-column-2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 1024px) {
|
||||
.settings-layout {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
|
||||
<!-- Second Column - Management and Settings Cards -->
|
||||
<div class="settings-column-2">
|
||||
<div class="card">
|
||||
<h3>🎯 User & Permissions Management</h3>
|
||||
<p><strong>Simplified 4-Tier System:</strong> Superadmin → Admin → Manager → Worker</p>
|
||||
<p>Streamlined interface with module-based permissions (Quality, Warehouse, Labels)</p>
|
||||
<div style="margin-top: 15px;">
|
||||
<a href="{{ url_for('main.user_management_simple') }}" class="btn" style="background-color: #2196f3; color: white; margin-right: 10px;">
|
||||
🎯 Manage Users (Simplified)
|
||||
</a>
|
||||
</div>
|
||||
<small style="display: block; margin-top: 10px; color: #666;">
|
||||
Recommended: Use the simplified user management for easier administration
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>External Server Settings</h3>
|
||||
<form method="POST" action="{{ url_for('main.save_external_db') }}" class="form-centered">
|
||||
<label for="server_domain">Server Domain/IP Address:</label>
|
||||
<input type="text" id="server_domain" name="server_domain" value="{{ external_settings.get('server_domain', '') }}" required>
|
||||
<label for="port">Port:</label>
|
||||
<input type="number" id="port" name="port" value="{{ external_settings.get('port', '') }}" required>
|
||||
<label for="database_name">Database Name:</label>
|
||||
<input type="text" id="database_name" name="database_name" value="{{ external_settings.get('database_name', '') }}" required>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" value="{{ external_settings.get('username', '') }}" required>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" value="{{ external_settings.get('password', '') }}" required>
|
||||
<button type="submit" class="btn">Save/Update External Database Info Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Popup for creating/editing a user -->
|
||||
<div id="user-popup" class="popup" style="display:none; position:fixed; top:0; left:0; width:100vw; height:100vh; background:var(--app-overlay-bg, rgba(30,41,59,0.85)); z-index:9999; align-items:center; justify-content:center;">
|
||||
<div class="popup-content" style="margin:auto; padding:32px; border-radius:8px; box-shadow:0 2px 8px #333; min-width:320px; max-width:400px; text-align:center;">
|
||||
<h3 id="user-popup-title">Create/Edit User</h3>
|
||||
<form id="user-form" method="POST" action="{{ url_for('main.create_user') }}">
|
||||
<input type="hidden" id="user-id" name="user_id">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
<label for="email">Email (Optional):</label>
|
||||
<input type="email" id="email" name="email">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
<label for="role">Role:</label>
|
||||
<select id="role" name="role" required>
|
||||
<option value="superadmin">Superadmin</option>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="manager">Manager</option>
|
||||
<option value="warehouse_manager">Warehouse Manager</option>
|
||||
<option value="warehouse_worker">Warehouse Worker</option>
|
||||
<option value="quality_manager">Quality Manager</option>
|
||||
<option value="quality_worker">Quality Worker</option>
|
||||
</select>
|
||||
<button type="submit" class="btn">Save</button>
|
||||
<button type="button" id="close-user-popup-btn" class="btn cancel-btn">Cancel</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Popup for confirming user deletion -->
|
||||
<div id="delete-user-popup" class="popup">
|
||||
<div class="popup-content">
|
||||
<h3>Do you really want to delete the user <span id="delete-username"></span>?</h3>
|
||||
<form id="delete-user-form" method="POST" action="{{ url_for('main.delete_user') }}">
|
||||
<input type="hidden" id="delete-user-id" name="user_id">
|
||||
<button type="submit" class="btn delete-confirm-btn">Yes</button>
|
||||
<button type="button" id="close-delete-popup-btn" class="btn cancel-btn">No</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('create-user-btn').onclick = function() {
|
||||
document.getElementById('user-popup').style.display = 'flex';
|
||||
document.getElementById('user-popup-title').innerText = 'Create User';
|
||||
document.getElementById('user-form').reset();
|
||||
document.getElementById('user-form').setAttribute('action', '{{ url_for("main.create_user") }}');
|
||||
document.getElementById('user-id').value = '';
|
||||
document.getElementById('password').required = true;
|
||||
document.getElementById('password').placeholder = '';
|
||||
document.getElementById('username').readOnly = false;
|
||||
};
|
||||
|
||||
document.getElementById('close-user-popup-btn').onclick = function() {
|
||||
document.getElementById('user-popup').style.display = 'none';
|
||||
};
|
||||
|
||||
// Edit User button logic
|
||||
Array.from(document.getElementsByClassName('edit-user-btn')).forEach(function(btn) {
|
||||
btn.onclick = function() {
|
||||
document.getElementById('user-popup').style.display = 'flex';
|
||||
document.getElementById('user-popup-title').innerText = 'Edit User';
|
||||
document.getElementById('user-id').value = btn.getAttribute('data-user-id');
|
||||
document.getElementById('username').value = btn.getAttribute('data-username');
|
||||
document.getElementById('email').value = btn.getAttribute('data-email') || '';
|
||||
document.getElementById('role').value = btn.getAttribute('data-role');
|
||||
document.getElementById('password').value = '';
|
||||
document.getElementById('password').required = false;
|
||||
document.getElementById('password').placeholder = 'Leave blank to keep current password';
|
||||
document.getElementById('username').readOnly = true;
|
||||
document.getElementById('user-form').setAttribute('action', '{{ url_for("main.edit_user") }}');
|
||||
};
|
||||
});
|
||||
|
||||
// Delete User button logic
|
||||
Array.from(document.getElementsByClassName('delete-user-btn')).forEach(function(btn) {
|
||||
btn.onclick = function() {
|
||||
document.getElementById('delete-user-popup').style.display = 'flex';
|
||||
document.getElementById('delete-username').innerText = btn.getAttribute('data-username');
|
||||
document.getElementById('delete-user-id').value = btn.getAttribute('data-user-id');
|
||||
};
|
||||
});
|
||||
|
||||
document.getElementById('close-delete-popup-btn').onclick = function() {
|
||||
document.getElementById('delete-user-popup').style.display = 'none';
|
||||
};
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -3,4 +3,5 @@ from app import create_app
|
||||
app = create_app()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, port=8781, host='0.0.0.0')
|
||||
app.run(debug=True, port=8782, host='0.0.0.0') # Run on port 8782 for local testing
|
||||
# In production, use Gunicorn with wsgi.py as the entry point with port 8781
|
||||
|
||||
Reference in New Issue
Block a user