diff --git a/py_app/app/__init__.py b/py_app/app/__init__.py index b828291..f9429e8 100644 --- a/py_app/app/__init__.py +++ b/py_app/app/__init__.py @@ -1,10 +1,20 @@ from flask import Flask +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() def create_app(): app = Flask(__name__) - app.config['SECRET_KEY'] = 'Recticel a plecat Aquinos a falimentat Innofa a venit' + app.config['SECRET_KEY'] = 'your_secret_key' + app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + + db.init_app(app) from .routes import bp app.register_blueprint(bp) + with app.app_context(): + db.create_all() # Create database tables if they don't exist + return app \ No newline at end of file diff --git a/py_app/app/__pycache__/__init__.cpython-311.pyc b/py_app/app/__pycache__/__init__.cpython-311.pyc index deb4586..cb93869 100644 Binary files a/py_app/app/__pycache__/__init__.cpython-311.pyc and b/py_app/app/__pycache__/__init__.cpython-311.pyc differ diff --git a/py_app/app/__pycache__/models.cpython-311.pyc b/py_app/app/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000..9ed0bde Binary files /dev/null and b/py_app/app/__pycache__/models.cpython-311.pyc differ diff --git a/py_app/app/__pycache__/routes.cpython-311.pyc b/py_app/app/__pycache__/routes.cpython-311.pyc index 309cc95..0a92761 100644 Binary files a/py_app/app/__pycache__/routes.cpython-311.pyc and b/py_app/app/__pycache__/routes.cpython-311.pyc differ diff --git a/py_app/app/models.py b/py_app/app/models.py new file mode 100644 index 0000000..73b4e22 --- /dev/null +++ b/py_app/app/models.py @@ -0,0 +1,10 @@ +from . import db + +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + password = db.Column(db.String(120), nullable=False) + role = db.Column(db.String(20), nullable=False) # Role: superadmin, administrator, quality, warehouse, scan + + def __repr__(self): + return f'' \ No newline at end of file diff --git a/py_app/app/routes.py b/py_app/app/routes.py index 0612728..8980201 100644 --- a/py_app/app/routes.py +++ b/py_app/app/routes.py @@ -1,17 +1,18 @@ from flask import Blueprint, render_template, redirect, url_for, request, flash, session +from .models import User +from . import db bp = Blueprint('main', __name__) -# Dummy user data -users = {"admin@home.com": "1234"} - @bp.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - email = request.form['email'] + username = request.form['username'] password = request.form['password'] - if email in users and users[email] == password: - session['user'] = email + user = User.query.filter_by(username=username, password=password).first() + if user: + session['user'] = user.username + session['role'] = user.role return redirect(url_for('main.dashboard')) else: flash('Invalid credentials. Please try again.') @@ -23,11 +24,108 @@ def dashboard(): return redirect(url_for('main.login')) return render_template('dashboard.html') +@bp.route('/settings') +def settings(): + if 'role' not in session or session['role'] != 'superadmin': + flash('Access denied: Superadmin only.') + return redirect(url_for('main.dashboard')) + + # Fetch all users from the database + users = User.query.all() + return render_template('settings.html', users=users) + +@bp.route('/quality') +def quality(): + if 'role' not in session or session['role'] not in ['superadmin', 'quality']: + flash('Access denied: Quality users only.') + return redirect(url_for('main.dashboard')) + return render_template('quality.html') + +@bp.route('/warehouse') +def warehouse(): + if 'role' not in session or session['role'] not in ['superadmin', 'warehouse']: + flash('Access denied: Warehouse users only.') + return redirect(url_for('main.dashboard')) + return render_template('warehouse.html') + +@bp.route('/scan') +def scan(): + if 'role' not in session or session['role'] not in ['superadmin', 'scan']: + flash('Access denied: Scan users only.') + return redirect(url_for('main.dashboard')) + return render_template('scan.html') + @bp.route('/logout') def logout(): session.pop('user', None) + session.pop('role', None) return redirect(url_for('main.login')) -@bp.route('/settings') -def settings(): - return render_template('settings.html') \ No newline at end of file +@bp.route('/create_user', methods=['POST']) +def create_user(): + if 'role' not in session or session['role'] != 'superadmin': + flash('Access denied: Superadmin only.') + return redirect(url_for('main.settings')) + + username = request.form['username'] + password = request.form['password'] + role = request.form['role'] + + # Check if the username already exists + if User.query.filter_by(username=username).first(): + flash('User already exists.') + return redirect(url_for('main.settings')) + + # Create a new user + new_user = User(username=username, password=password, role=role) + db.session.add(new_user) + db.session.commit() + + flash('User created successfully.') + return redirect(url_for('main.settings')) + +@bp.route('/edit_user', methods=['POST']) +def edit_user(): + if 'role' not in session or session['role'] != 'superadmin': + flash('Access denied: Superadmin only.') + return redirect(url_for('main.settings')) + + user_id = request.form['user_id'] + password = request.form['password'] + role = request.form['role'] + + # Fetch the user from the database + user = User.query.get(user_id) + if not user: + flash('User not found.') + return redirect(url_for('main.settings')) + + # Update the user's details + if password: + user.password = password + user.role = role + db.session.commit() + + flash('User updated successfully.') + return redirect(url_for('main.settings')) + +@bp.route('/delete_user', methods=['POST']) +def delete_user(): + if 'role' not in session or session['role'] != 'superadmin': + flash('Access denied: Superadmin only.') + return redirect(url_for('main.settings')) + + user_id = request.form['user_id'] + + # Fetch the user from the database + user = User.query.get(user_id) + if not user: + flash('User not found.') + return redirect(url_for('main.settings')) + + # Delete the user + db.session.delete(user) + db.session.commit() + + flash('User deleted successfully.') + return redirect(url_for('main.settings')) \ No newline at end of file diff --git a/py_app/app/static/script.js b/py_app/app/static/script.js index 0ec2000..d3ad383 100644 --- a/py_app/app/static/script.js +++ b/py_app/app/static/script.js @@ -31,4 +31,93 @@ document.addEventListener('DOMContentLoaded', () => { themeToggle.textContent = 'Change to light theme'; } } + + const createUserBtn = document.getElementById('create-user-btn'); + const createUserPopup = document.getElementById('create-user-popup'); + const closePopupBtn = document.getElementById('close-popup-btn'); + + // Open the popup + createUserBtn.addEventListener('click', () => { + createUserPopup.style.display = 'flex'; + }); + + // Close the popup + closePopupBtn.addEventListener('click', () => { + createUserPopup.style.display = 'none'; + }); + + // Close the popup when clicking outside the popup content + createUserPopup.addEventListener('click', (e) => { + if (e.target === createUserPopup) { + createUserPopup.style.display = 'none'; + } + }); + + const editButtons = document.querySelectorAll('.edit-btn'); + const editUserPopup = document.getElementById('edit-user-popup'); + const closeEditPopupBtn = document.getElementById('close-edit-popup-btn'); + + // Open the edit user popup + editButtons.forEach((button) => { + button.addEventListener('click', (e) => { + const userElement = e.target.closest('li'); + const username = userElement.querySelector('.user-name').textContent; + const role = userElement.querySelector('.user-role').textContent.split(': ')[1]; + const userId = userElement.dataset.userId; + + // Populate the form fields + document.getElementById('edit-user-id').value = userId; + document.getElementById('edit-username').value = username; + document.getElementById('edit-role').value = role; + + // Show the popup + editUserPopup.style.display = 'flex'; + }); + }); + + // Close the edit user popup + closeEditPopupBtn.addEventListener('click', () => { + editUserPopup.style.display = 'none'; + }); + + // Close the popup when clicking outside the popup content + editUserPopup.addEventListener('click', (e) => { + if (e.target === editUserPopup) { + editUserPopup.style.display = 'none'; + } + }); + + const deleteButtons = document.querySelectorAll('.delete-btn'); + const deleteUserPopup = document.getElementById('delete-user-popup'); + const closeDeletePopupBtn = document.getElementById('close-delete-popup-btn'); + const deleteUsernameSpan = document.getElementById('delete-username'); + const deleteUserIdInput = document.getElementById('delete-user-id'); + + // Open the delete user popup + deleteButtons.forEach((button) => { + button.addEventListener('click', (e) => { + const userElement = e.target.closest('li'); + const username = userElement.querySelector('.user-name').textContent; + const userId = userElement.dataset.userId; + + // Populate the popup with user details + deleteUsernameSpan.textContent = username; + deleteUserIdInput.value = userId; + + // Show the popup + deleteUserPopup.style.display = 'flex'; + }); + }); + + // Close the delete user popup + closeDeletePopupBtn.addEventListener('click', () => { + deleteUserPopup.style.display = 'none'; + }); + + // Close the popup when clicking outside the popup content + deleteUserPopup.addEventListener('click', (e) => { + if (e.target === deleteUserPopup) { + deleteUserPopup.style.display = 'none'; + } + }); }); \ No newline at end of file diff --git a/py_app/app/static/style.css b/py_app/app/static/style.css index cbfe929..a734b6d 100644 --- a/py_app/app/static/style.css +++ b/py_app/app/static/style.css @@ -6,7 +6,7 @@ body { } .container { - width: 98%; + width: 100%; margin: auto; overflow: hidden; padding: 0; /* Remove padding */ @@ -17,10 +17,11 @@ body { .login-page { display: flex; - align-items: center; - justify-content: center; - height: 100vh; + align-items: center; /* Vertically center the content */ + justify-content: space-between; /* Space between the logo and the form container */ + height: 100vh; /* Full height of the viewport */ background-color: #f4f4f9; + padding: 0 20px; /* Add padding to the sides */ } header { @@ -92,12 +93,13 @@ header { } .form-container { - width: 600px; /* Set a fixed width for the login container */ + width: 600px; /* Fixed width for the login container */ background: #fff; - padding: 15px 30px 15px 15px; /* Add 30px padding to the right */ + padding: 15px 30px; /* Add padding inside the container */ border-radius: 5px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - margin: 0 0 0 20px; /* Move the container closer to the picture */ + margin: 0; /* Remove any extra margin */ + align-self: center; /* Vertically center the form container */ } .form-container h2 { @@ -228,18 +230,17 @@ body.dark-mode .card { /* Common card styles */ .card { - width: 400px; /* Fixed width */ - height: 150px; /* Fixed height */ + width: 600px; + background: #fff; border-radius: 5px; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - margin: 20px auto; /* Center the card horizontally */ + margin: 20px auto; text-align: center; - transition: background-color 0.3s ease, color 0.3s ease, border 0.3s ease; } .card h3 { - margin-bottom: 10px; + margin-bottom: 20px; font-size: 1.5em; } @@ -261,4 +262,134 @@ body.dark-mode .card { .card .btn:hover { background-color: #0056b3; +} + +.user-list { + list-style: none; + padding: 0; + margin: 0 0 20px 0; +} + +.user-list li { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0; + border-bottom: 1px solid #ddd; +} + +.user-name { + font-size: 1em; + font-weight: bold; +} + +.user-role { + font-size: 0.9em; + color: #555; + margin-left: 10px; +} + +.btn { + padding: 5px 10px; + font-size: 1em; + color: #fff; + background-color: #007bff; + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.btn:hover { + background-color: #0056b3; +} + +.create-btn { + margin-top: 20px; + background-color: #28a745; +} + +.create-btn:hover { + background-color: #218838; +} + +.popup { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + justify-content: center; + align-items: center; +} + +/* Light mode styles for pop-ups */ +body.light-mode .popup-content { + background: #fff; + color: #000; + border: 1px solid #ddd; +} + +/* Dark mode styles for pop-ups */ +body.dark-mode .popup-content { + background: #1e1e1e; + color: #fff; + border: 1px solid #444; +} + +/* Common styles for pop-ups */ +.popup-content { + padding: 20px; + border-radius: 5px; + width: 400px; + text-align: center; + transition: background-color 0.3s ease, color 0.3s ease, border 0.3s ease; +} + +.popup-content h3 { + margin-bottom: 20px; + font-size: 1.2em; +} + +.popup-content form { + display: flex; + flex-direction: column; +} + +.popup-content label { + margin-bottom: 5px; + font-weight: bold; +} + +.popup-content input, +.popup-content select { + margin-bottom: 15px; + padding: 10px; + font-size: 1em; + border: 1px solid #ccc; + border-radius: 5px; + width: 100%; +} + +.popup-content input[readonly] { + background-color: #e9ecef; + cursor: not-allowed; +} + +.cancel-btn { + background-color: #dc3545; +} + +.cancel-btn:hover { + background-color: #c82333; +} + +.delete-confirm-btn { + background-color: #dc3545; +} + +.delete-confirm-btn:hover { + background-color: #c82333; } \ No newline at end of file diff --git a/py_app/app/templates/login.html b/py_app/app/templates/login.html index a1f1100..d09fab4 100644 --- a/py_app/app/templates/login.html +++ b/py_app/app/templates/login.html @@ -10,10 +10,10 @@

Login

- - + + - +
diff --git a/py_app/app/templates/quality.html b/py_app/app/templates/quality.html new file mode 100644 index 0000000..a06480a --- /dev/null +++ b/py_app/app/templates/quality.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% block title %}Quality Module{% endblock %} +{% block content %} +

Quality Module

+

Welcome to the Quality Module.

+{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/scan.html b/py_app/app/templates/scan.html new file mode 100644 index 0000000..06a0a89 --- /dev/null +++ b/py_app/app/templates/scan.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% block title %}Scan Module{% endblock %} +{% block content %} +

Scan Module

+

Welcome to the Scan Module.

+{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/settings.html b/py_app/app/templates/settings.html index c95efc6..d0036fd 100644 --- a/py_app/app/templates/settings.html +++ b/py_app/app/templates/settings.html @@ -3,6 +3,77 @@ {% block title %}Settings{% endblock %} {% block content %} -

Settings Page

-

This is the settings page. Add your settings here.

+
+

Manage Users

+ + +
+ + + + + + + + + {% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/warehouse.html b/py_app/app/templates/warehouse.html new file mode 100644 index 0000000..99dd436 --- /dev/null +++ b/py_app/app/templates/warehouse.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% block title %}Warehouse Module{% endblock %} +{% block content %} +

Warehouse Module

+

Welcome to the Warehouse Module.

+{% endblock %} \ No newline at end of file diff --git a/py_app/instance/users.db b/py_app/instance/users.db new file mode 100644 index 0000000..4b807a3 Binary files /dev/null and b/py_app/instance/users.db differ diff --git a/py_app/requirements.txt b/py_app/requirements.txt index 65c249a..ad4356b 100644 --- a/py_app/requirements.txt +++ b/py_app/requirements.txt @@ -1,4 +1,5 @@ Flask==2.2.2 Flask-SSLify==0.1.5 Werkzeug==2.2.2 -gunicorn==20.1.0 \ No newline at end of file +gunicorn==20.1.0 +flask-sqlalchemy==2.5.1 diff --git a/py_app/seed.py b/py_app/seed.py new file mode 100644 index 0000000..6825eaa --- /dev/null +++ b/py_app/seed.py @@ -0,0 +1,22 @@ +from app import create_app, db +from app.models import User + +app = create_app() + +with app.app_context(): + # Add default users + users = [ + User(username='superadmin', password='superadmin123', role='superadmin'), + User(username='admin', password='admin123', role='administrator'), + 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() + print("Database seeded with default users.") \ No newline at end of file