Role management, login logic, and debug improvements. MariaDB login now uses correct syntax.
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
py_app/app/__pycache__/settings.cpython-312.pyc
Normal file
BIN
py_app/app/__pycache__/settings.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
45
py_app/app/db_create_scripts/create_roles_table.py
Normal file
45
py_app/app/db_create_scripts/create_roles_table.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
|
||||||
|
def create_roles_and_users_tables(db_path):
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
# Create users table if not exists
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT UNIQUE NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
role TEXT NOT NULL
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
# Insert superadmin user if not exists (default password: 'admin', change after first login)
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO users (username, password, role)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', ('superadmin', 'superadmin123', 'superadmin'))
|
||||||
|
# Create roles table if not exists
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS roles (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT UNIQUE NOT NULL,
|
||||||
|
access_level TEXT NOT NULL,
|
||||||
|
description TEXT
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
# Insert superadmin role if not exists
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO roles (name, access_level, description)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', ('superadmin', 'full', 'Full access to all app areas and functions'))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Default path to users.db in instance folder
|
||||||
|
instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance'))
|
||||||
|
if not os.path.exists(instance_folder):
|
||||||
|
os.makedirs(instance_folder)
|
||||||
|
db_path = os.path.join(instance_folder, 'users.db')
|
||||||
|
create_roles_and_users_tables(db_path)
|
||||||
|
print("Roles and users tables created. Superadmin user and role initialized.")
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import mariadb
|
||||||
|
import os
|
||||||
|
|
||||||
|
def get_external_db_connection():
|
||||||
|
settings_file = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance/external_server.conf'))
|
||||||
|
settings = {}
|
||||||
|
with open(settings_file, 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
key, value = line.strip().split('=', 1)
|
||||||
|
settings[key] = value
|
||||||
|
return mariadb.connect(
|
||||||
|
user=settings['username'],
|
||||||
|
password=settings['password'],
|
||||||
|
host=settings['server_domain'],
|
||||||
|
port=int(settings['port']),
|
||||||
|
database=settings['database_name']
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
conn = get_external_db_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("DROP TABLE IF EXISTS users")
|
||||||
|
cursor.execute("DROP TABLE IF EXISTS roles")
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print("Dropped users and roles tables from external database.")
|
||||||
30
py_app/app/db_create_scripts/print_internal_users.py
Normal file
30
py_app/app/db_create_scripts/print_internal_users.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
|
||||||
|
instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance'))
|
||||||
|
db_path = os.path.join(instance_folder, 'users.db')
|
||||||
|
|
||||||
|
if not os.path.exists(db_path):
|
||||||
|
print("users.db not found at", db_path)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Check if users table exists
|
||||||
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='users'")
|
||||||
|
if not cursor.fetchone():
|
||||||
|
print("No users table found in users.db.")
|
||||||
|
conn.close()
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# Print all users
|
||||||
|
cursor.execute("SELECT id, username, password, role FROM users")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
if not rows:
|
||||||
|
print("No users found in users.db.")
|
||||||
|
else:
|
||||||
|
print("Users in users.db:")
|
||||||
|
for row in rows:
|
||||||
|
print(f"id={row[0]}, username={row[1]}, password={row[2]}, role={row[3]}")
|
||||||
|
conn.close()
|
||||||
34
py_app/app/db_create_scripts/seed_internal_superadmin.py
Normal file
34
py_app/app/db_create_scripts/seed_internal_superadmin.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['SECRET_KEY'] = 'your_secret_key' # Use the same key as in __init__.py
|
||||||
|
|
||||||
|
instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance'))
|
||||||
|
if not os.path.exists(instance_folder):
|
||||||
|
os.makedirs(instance_folder)
|
||||||
|
db_path = os.path.join(instance_folder, 'users.db')
|
||||||
|
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Create users table if not exists
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT UNIQUE NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
role TEXT NOT NULL
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Insert superadmin user if not exists
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO users (username, password, role)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', ('superadmin', 'superadmin123', 'superadmin'))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print("Internal users.db seeded with superadmin user.")
|
||||||
@@ -9,9 +9,17 @@ from reportlab.pdfgen import canvas
|
|||||||
from flask import Blueprint, render_template, request, redirect, url_for, flash
|
from flask import Blueprint, render_template, request, redirect, url_for, flash
|
||||||
import csv
|
import csv
|
||||||
from .warehouse import add_location
|
from .warehouse import add_location
|
||||||
|
from .settings import settings_handler, edit_access_roles_handler
|
||||||
|
|
||||||
bp = Blueprint('main', __name__)
|
bp = Blueprint('main', __name__)
|
||||||
warehouse_bp = Blueprint('warehouse', __name__)
|
warehouse_bp = Blueprint('warehouse', __name__)
|
||||||
|
bp = Blueprint('main', __name__)
|
||||||
|
warehouse_bp = Blueprint('warehouse', __name__)
|
||||||
|
|
||||||
|
@bp.route('/update_role_access/<role>', methods=['POST'])
|
||||||
|
def update_role_access(role):
|
||||||
|
from .settings import update_role_access_handler
|
||||||
|
return update_role_access_handler(role)
|
||||||
|
|
||||||
@bp.route('/store_articles')
|
@bp.route('/store_articles')
|
||||||
def store_articles():
|
def store_articles():
|
||||||
@@ -48,40 +56,47 @@ def login():
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.form['username']
|
username = request.form['username']
|
||||||
password = request.form['password']
|
password = request.form['password']
|
||||||
user = User.query.filter_by(username=username, password=password).first()
|
user = None
|
||||||
|
print("Raw form input:", repr(username), repr(password))
|
||||||
|
# Only check external MariaDB for user authentication
|
||||||
|
try:
|
||||||
|
conn = get_db_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SHOW TABLES LIKE 'users'")
|
||||||
|
if cursor.fetchone():
|
||||||
|
cursor.execute("SELECT username, password, role FROM users WHERE username=%s AND password=%s", (username.strip(), password.strip()))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
print("External DB query result:", row)
|
||||||
|
if row:
|
||||||
|
user = {'username': row[0], 'password': row[1], 'role': row[2]}
|
||||||
|
conn.close()
|
||||||
|
except Exception as e:
|
||||||
|
print("External DB error:", e)
|
||||||
if user:
|
if user:
|
||||||
session['user'] = user.username
|
session['user'] = user['username']
|
||||||
session['role'] = user.role
|
session['role'] = user['role']
|
||||||
|
print("Logged in as:", session.get('user'), session.get('role'))
|
||||||
return redirect(url_for('main.dashboard'))
|
return redirect(url_for('main.dashboard'))
|
||||||
else:
|
else:
|
||||||
|
print("Login failed for:", username, password)
|
||||||
flash('Invalid credentials. Please try again.')
|
flash('Invalid credentials. Please try again.')
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
@bp.route('/dashboard')
|
@bp.route('/dashboard')
|
||||||
def dashboard():
|
def dashboard():
|
||||||
|
print("Session user:", session.get('user'), session.get('role'))
|
||||||
if 'user' not in session:
|
if 'user' not in session:
|
||||||
return redirect(url_for('main.login'))
|
return redirect(url_for('main.login'))
|
||||||
return render_template('dashboard.html')
|
return render_template('dashboard.html')
|
||||||
|
|
||||||
@bp.route('/settings')
|
@bp.route('/settings')
|
||||||
def settings():
|
def settings():
|
||||||
if 'role' not in session or session['role'] != 'superadmin':
|
return settings_handler()
|
||||||
flash('Access denied: Superadmin only.')
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
|
|
||||||
# Fetch all users from the database
|
|
||||||
users = User.query.all()
|
|
||||||
|
|
||||||
# Load external database settings from the instance folder
|
# Route for editing access roles (superadmin only)
|
||||||
external_settings = {}
|
@bp.route('/edit_access_roles')
|
||||||
settings_file = os.path.join(current_app.instance_path, 'external_server.conf')
|
def edit_access_roles():
|
||||||
if (os.path.exists(settings_file)):
|
return edit_access_roles_handler()
|
||||||
with open(settings_file, 'r') as f:
|
|
||||||
for line in f:
|
|
||||||
key, value = line.strip().split('=', 1)
|
|
||||||
external_settings[key] = value
|
|
||||||
|
|
||||||
return render_template('settings.html', users=users, external_settings=external_settings)
|
|
||||||
|
|
||||||
@bp.route('/quality')
|
@bp.route('/quality')
|
||||||
def quality():
|
def quality():
|
||||||
|
|||||||
76
py_app/app/settings.py
Normal file
76
py_app/app/settings.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
from flask import render_template, request, session, redirect, url_for, flash
|
||||||
|
from .models import User
|
||||||
|
from . import db
|
||||||
|
|
||||||
|
# Settings module logic
|
||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
def ensure_roles_table():
|
||||||
|
instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance'))
|
||||||
|
if not os.path.exists(instance_folder):
|
||||||
|
os.makedirs(instance_folder)
|
||||||
|
db_path = os.path.join(instance_folder, 'users.db')
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS roles (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT UNIQUE NOT NULL,
|
||||||
|
access_level TEXT NOT NULL,
|
||||||
|
description TEXT
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT OR IGNORE INTO roles (name, access_level, description)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
""", ('superadmin', 'full', 'Full access to all app areas and functions'))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
# List of roles (should match your app's roles)
|
||||||
|
ROLES = [
|
||||||
|
'superadmin', 'admin', 'manager', 'warehouse_manager', 'warehouse_worker', 'quality_manager', 'quality_worker'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Helper to check if current user is superadmin
|
||||||
|
def is_superadmin():
|
||||||
|
return session.get('role') == 'superadmin'
|
||||||
|
|
||||||
|
# Route handler for editing access roles
|
||||||
|
def edit_access_roles_handler():
|
||||||
|
if not is_superadmin():
|
||||||
|
flash('Access denied: Superadmin only.')
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
|
ensure_roles_table()
|
||||||
|
return render_template('edit_access_roles.html', roles=ROLES)
|
||||||
|
|
||||||
|
# Handler for updating role access (stub, to be implemented)
|
||||||
|
def update_role_access_handler(role):
|
||||||
|
if not is_superadmin():
|
||||||
|
flash('Access denied: Superadmin only.')
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
|
if role == 'superadmin':
|
||||||
|
flash('Superadmin access cannot be changed.')
|
||||||
|
return redirect(url_for('main.edit_access_roles'))
|
||||||
|
access_level = request.form.get('access_level')
|
||||||
|
# TODO: Save access_level for the role in the database or config
|
||||||
|
flash(f'Access for role {role} updated to {access_level}.')
|
||||||
|
return redirect(url_for('main.edit_access_roles'))
|
||||||
|
|
||||||
|
def settings_handler():
|
||||||
|
if 'role' not in session or session['role'] != 'superadmin':
|
||||||
|
flash('Access denied: Superadmin only.')
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
|
users = User.query.all()
|
||||||
|
# Load external database settings from the instance folder
|
||||||
|
external_settings = {}
|
||||||
|
import os
|
||||||
|
from flask import current_app
|
||||||
|
settings_file = os.path.join(current_app.instance_path, 'external_server.conf')
|
||||||
|
if os.path.exists(settings_file):
|
||||||
|
with open(settings_file, 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
key, value = line.strip().split('=', 1)
|
||||||
|
external_settings[key] = value
|
||||||
|
return render_template('settings.html', users=users, external_settings=external_settings)
|
||||||
|
|
||||||
|
# Add more settings-related functions here as needed
|
||||||
@@ -25,6 +25,22 @@
|
|||||||
<a href="{{ url_for('warehouse.import_locations_csv') }}" class="btn" style="padding: 4px 12px; font-size: 0.95em;">Go to Import Page</a>
|
<a href="{{ url_for('warehouse.import_locations_csv') }}" class="btn" style="padding: 4px 12px; font-size: 0.95em;">Go to Import Page</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Delete Location Area -->
|
||||||
|
{% if session['role'] in ['administrator', 'management'] %}
|
||||||
|
<div style="margin-top: 32px; padding: 12px; border-top: 1px solid #eee;">
|
||||||
|
<label style="font-weight:bold;">Delete location from table</label>
|
||||||
|
<div style="font-size:0.95em; margin-bottom:8px;">To delete a location, enter the ID of the location and press delete.<br>To delete 2 or multiple locations, enter the IDs separated by "," and then press delete.</div>
|
||||||
|
<form method="POST" style="display:flex; gap:8px; align-items:center;" onsubmit="return confirmDeleteLocations();">
|
||||||
|
<input type="text" name="delete_ids" placeholder="e.g. 5,7,12" style="width:160px;">
|
||||||
|
<button type="submit" name="delete_locations" value="1" class="btn" style="padding:4px 16px;">Delete Locations</button>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
function confirmDeleteLocations() {
|
||||||
|
return confirm('Do you really want to delete the selected locations?');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<!-- Locations Table Card -->
|
<!-- Locations Table Card -->
|
||||||
<div class="card scan-table-card">
|
<div class="card scan-table-card">
|
||||||
|
|||||||
43
py_app/app/templates/edit_access_roles.html
Normal file
43
py_app/app/templates/edit_access_roles.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Edit Access Roles{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="card" style="max-width: 700px; margin: 32px auto;">
|
||||||
|
<h3>Role Access Management</h3>
|
||||||
|
<p>Configure which roles can view or execute functions on each app page and feature.</p>
|
||||||
|
<table class="scan-table" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Access Level</th>
|
||||||
|
<th>Editable</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>superadmin</td>
|
||||||
|
<td>Full access to all pages and functions</td>
|
||||||
|
<td><span style="color:#888;">Not editable</span></td>
|
||||||
|
</tr>
|
||||||
|
{% for role in roles %}
|
||||||
|
{% if role != 'superadmin' %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ role }}</td>
|
||||||
|
<td>
|
||||||
|
<form method="POST" action="{{ url_for('main.update_role_access', role=role) }}">
|
||||||
|
<select name="access_level">
|
||||||
|
<option value="view">View Only</option>
|
||||||
|
<option value="execute">View & Execute</option>
|
||||||
|
<option value="none">No Access</option>
|
||||||
|
</select>
|
||||||
|
<button type="submit" class="btn">Save</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>Editable</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p style="margin-top:16px; color:#888;">Only superadmin users can view and manage role access.</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -37,9 +37,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<!-- Popup Modal -->
|
<!-- Popup Modal -->
|
||||||
<div id="popup-modal" style="display:none; position:fixed; top:0; left:0; width:100vw; height:100vh; background:rgba(0,0,0,0.3); z-index:9999; align-items:center; justify-content:center;">
|
<div id="popup-modal" 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 style="background:#fff; padding:32px; border-radius:8px; box-shadow:0 2px 8px #333; text-align: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>Performing the creation of the warehouse locations</h3>
|
<h3 style="color:var(--app-label-text);">Performing the creation of the warehouse locations</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
<h3>Manage Users</h3>
|
<h3>Manage Users</h3>
|
||||||
<ul class="user-list">
|
<ul class="user-list">
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<li data-user-id="{{ user.id }}">
|
<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-name">{{ user.username }}</span>
|
||||||
<span class="user-role">Role: {{ user.role }}</span>
|
<span class="user-role">Role: {{ user.role }}</span>
|
||||||
<button class="btn edit-btn">Edit Rights</button>
|
<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</button>
|
<button class="btn delete-btn">Delete User</button>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -35,51 +35,37 @@
|
|||||||
<button type="submit" class="btn">Save/Update External Database Info Settings</button>
|
<button type="submit" class="btn">Save/Update External Database Info Settings</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card" style="margin-top: 32px;">
|
||||||
|
<h3>Edit Access Roles</h3>
|
||||||
|
<p>Manage which roles can view or execute functions on each app page and feature.</p>
|
||||||
|
<a href="{{ url_for('main.edit_access_roles') }}" class="btn">Edit Access Roles</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Popup for creating a new user -->
|
<!-- Popup for creating/editing a user -->
|
||||||
<div id="create-user-popup" class="popup">
|
<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">
|
<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>Create User</h3>
|
<h3 id="user-popup-title">Create/Edit User</h3>
|
||||||
<form id="create-user-form" method="POST" action="{{ url_for('main.create_user') }}">
|
<form id="user-form" method="POST" action="{{ url_for('main.create_user') }}">
|
||||||
<label for="username">Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input type="text" id="username" name="username" required>
|
<input type="text" id="username" name="username" required>
|
||||||
|
<label for="email">Email Address:</label>
|
||||||
|
<input type="email" id="email" name="email" required>
|
||||||
<label for="password">Password:</label>
|
<label for="password">Password:</label>
|
||||||
<input type="password" id="password" name="password" required>
|
<input type="password" id="password" name="password" required>
|
||||||
<label for="role">Role:</label>
|
<label for="role">Role:</label>
|
||||||
<select id="role" name="role" required>
|
<select id="role" name="role" required>
|
||||||
<option value="superadmin">Superadmin</option>
|
<option value="superadmin">Superadmin</option>
|
||||||
<option value="administrator">Administrator</option>
|
<option value="admin">Admin</option>
|
||||||
<option value="quality">Quality</option>
|
<option value="manager">Manager</option>
|
||||||
<option value="warehouse">Warehouse</option>
|
<option value="warehouse_manager">Warehouse Manager</option>
|
||||||
<option value="scan">Scan</option>
|
<option value="warehouse_worker">Warehouse Worker</option>
|
||||||
|
<option value="quality_manager">Quality Manager</option>
|
||||||
|
<option value="quality_worker">Quality Worker</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="submit" class="btn">Create</button>
|
<button type="submit" class="btn">Save</button>
|
||||||
<button type="button" id="close-popup-btn" class="btn cancel-btn">Cancel</button>
|
<button type="button" id="close-user-popup-btn" class="btn cancel-btn">Cancel</button>
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Popup for editing a user -->
|
|
||||||
<div id="edit-user-popup" class="popup">
|
|
||||||
<div class="popup-content">
|
|
||||||
<h3>Edit User</h3>
|
|
||||||
<form id="edit-user-form" method="POST" action="{{ url_for('main.edit_user') }}">
|
|
||||||
<input type="hidden" id="edit-user-id" name="user_id">
|
|
||||||
<label for="edit-username">Username:</label>
|
|
||||||
<input type="text" id="edit-username" name="username" readonly>
|
|
||||||
<label for="edit-password">New Password:</label>
|
|
||||||
<input type="password" id="edit-password" name="password">
|
|
||||||
<label for="edit-role">Role:</label>
|
|
||||||
<select id="edit-role" name="role" required>
|
|
||||||
<option value="superadmin">Superadmin</option>
|
|
||||||
<option value="administrator">Administrator</option>
|
|
||||||
<option value="quality">Quality</option>
|
|
||||||
<option value="warehouse">Warehouse</option>
|
|
||||||
<option value="scan">Scan</option>
|
|
||||||
</select>
|
|
||||||
<button type="submit" class="btn">Update</button>
|
|
||||||
<button type="button" id="close-edit-popup-btn" class="btn cancel-btn">Cancel</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,4 +81,27 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</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('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('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('user-form').setAttribute('action', '/edit_user/' + btn.getAttribute('data-user-id'));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -76,13 +76,32 @@ def recreate_warehouse_locations_table():
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
def delete_locations_by_ids(ids_str):
|
||||||
|
ids = [id.strip() for id in ids_str.split(',') if id.strip().isdigit()]
|
||||||
|
if not ids:
|
||||||
|
return "No valid IDs provided."
|
||||||
|
conn = get_db_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
deleted = 0
|
||||||
|
for id in ids:
|
||||||
|
cursor.execute("DELETE FROM warehouse_locations WHERE id = ?", (id,))
|
||||||
|
if cursor.rowcount:
|
||||||
|
deleted += 1
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return f"Deleted {deleted} location(s)."
|
||||||
|
|
||||||
def create_locations_handler():
|
def create_locations_handler():
|
||||||
message = None
|
message = None
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
location_code = request.form.get("location_code")
|
if request.form.get("delete_locations"):
|
||||||
size = request.form.get("size")
|
ids_str = request.form.get("delete_ids", "")
|
||||||
description = request.form.get("description")
|
message = delete_locations_by_ids(ids_str)
|
||||||
message = add_location(location_code, size, description)
|
else:
|
||||||
|
location_code = request.form.get("location_code")
|
||||||
|
size = request.form.get("size")
|
||||||
|
description = request.form.get("description")
|
||||||
|
message = add_location(location_code, size, description)
|
||||||
locations = get_locations()
|
locations = get_locations()
|
||||||
return render_template("create_locations.html", locations=locations, message=message)
|
return render_template("create_locations.html", locations=locations, message=message)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -4,19 +4,9 @@ from app.models import User
|
|||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
# Add default users
|
# Add only the superadmin user
|
||||||
users = [
|
user = User(username='superadmin', password='superadmin123', role='superadmin')
|
||||||
User(username='superadmin', password='superadmin123', role='superadmin'),
|
if not User.query.filter_by(username=user.username).first():
|
||||||
User(username='admin', password='admin123', role='administrator'),
|
db.session.add(user)
|
||||||
User(username='quality_user', password='quality123', role='quality'),
|
|
||||||
User(username='warehouse_user', password='warehouse123', role='warehouse'),
|
|
||||||
User(username='scan_user', password='scan123', role='scan'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add users to the database
|
|
||||||
for user in users:
|
|
||||||
if not User.query.filter_by(username=user.username).first():
|
|
||||||
db.session.add(user)
|
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print("Database seeded with default users.")
|
print("Database seeded with only the superadmin user.")
|
||||||
61
py_app/static/style.css
Normal file
61
py_app/static/style.css
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/* Theme variables for popup and cards */
|
||||||
|
:root {
|
||||||
|
--app-overlay-bg: rgba(30,41,59,0.85);
|
||||||
|
--app-card-bg: #f8fafc;
|
||||||
|
--app-card-text: #1e293b;
|
||||||
|
--app-input-bg: #e2e8f0;
|
||||||
|
--app-input-text: #1e293b;
|
||||||
|
--app-label-text: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.light-mode {
|
||||||
|
background: #f8fafc;
|
||||||
|
color: #1e293b;
|
||||||
|
}
|
||||||
|
body.dark-mode {
|
||||||
|
background: #1e293b;
|
||||||
|
color: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup {
|
||||||
|
background: var(--app-overlay-bg) !important;
|
||||||
|
}
|
||||||
|
.popup-content {
|
||||||
|
background: var(--app-card-bg) !important;
|
||||||
|
color: var(--app-card-text) !important;
|
||||||
|
}
|
||||||
|
.popup-content label,
|
||||||
|
#user-popup-title {
|
||||||
|
color: var(--app-label-text) !important;
|
||||||
|
}
|
||||||
|
.popup-content input,
|
||||||
|
.popup-content select {
|
||||||
|
background: var(--app-input-bg) !important;
|
||||||
|
color: var(--app-input-text) !important;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
width: 90%;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--app-card-bg);
|
||||||
|
color: var(--app-card-text);
|
||||||
|
box-shadow: 0 2px 8px #333;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background: #1e293b;
|
||||||
|
color: #f8fafc;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.btn.cancel-btn {
|
||||||
|
background: #e11d48;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user