From 26e55b9a3e6f9a1bc1873c76be2374fada18cfce Mon Sep 17 00:00:00 2001 From: Yaro Kasear Date: Mon, 7 Jul 2025 15:36:15 -0500 Subject: [PATCH] Add user creation and update functionality; refactor user template and helpers --- models/users.py | 13 +++++++- routes/helpers.py | 4 +-- routes/settings.py | 8 ----- routes/user.py | 61 ++++++++++++++++++++++++++++++++-- templates/inventory.html | 2 +- templates/user.html | 70 ++++++++++++++++++++++++++++++++++------ 6 files changed, 134 insertions(+), 24 deletions(-) diff --git a/models/users.py b/models/users.py index 5d95df0..d1424f5 100644 --- a/models/users.py +++ b/models/users.py @@ -1,4 +1,4 @@ -from typing import List, Optional, TYPE_CHECKING +from typing import Any, List, Optional, TYPE_CHECKING if TYPE_CHECKING: from .inventory import Inventory from .rooms import Room @@ -55,3 +55,14 @@ class User(db.Model): 'staff': self.staff, 'active': self.active } + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> "User": + return cls( + staff=bool(data.get("staff", False)), + active=bool(data.get("active", False)), + last_name=data.get("last_name"), + first_name=data.get("first_name"), + location_id=data.get("location_id"), + supervisor_id=data.get("supervisor_id") + ) \ No newline at end of file diff --git a/routes/helpers.py b/routes/helpers.py index c49d311..5139f20 100644 --- a/routes/helpers.py +++ b/routes/helpers.py @@ -19,9 +19,7 @@ inventory_headers = { } checked_box = ''' - - - + ''' unchecked_box = '' diff --git a/routes/settings.py b/routes/settings.py index f650227..94dc93a 100644 --- a/routes/settings.py +++ b/routes/settings.py @@ -11,15 +11,10 @@ from ..utils.load import eager_load_room_relationships @main.route('/settings', methods=['GET', 'POST']) def settings(): if request.method == 'POST': - print("⚠️⚠️⚠️ POST /settings reached! ⚠️⚠️⚠️") form = request.form - print("📝 Raw form payload:", form) try: state = json.loads(form['formState']) - import pprint - print("🧠 Parsed state:") - pprint.pprint(state, indent=2, width=120) except Exception: flash("Invalid form state submitted. JSON decode failed.", "danger") traceback.print_exc() @@ -58,12 +53,9 @@ def settings(): function_map=function_map ) - print("✅ COMMIT executed.") flash("Changes saved.", "success") except Exception as e: - print("❌ COMMIT FAILED ❌") - traceback.print_exc() flash(f"Error saving changes: {e}", "danger") return redirect(url_for('main.settings')) diff --git a/routes/user.py b/routes/user.py index 094f082..badf7ab 100644 --- a/routes/user.py +++ b/routes/user.py @@ -1,4 +1,4 @@ -from flask import render_template +from flask import render_template, request, jsonify from . import main from .helpers import ACTIVE_STATUSES, user_headers, inventory_headers, worklog_headers @@ -63,4 +63,61 @@ def user(id): worklog=worklog, worklog_headers=filtered_worklog_headers, worklog_rows=[{"id": log.id, "cells": [fn(log) for fn in filtered_worklog_headers.values()]} for log in worklog] - ) \ No newline at end of file + ) + +@main.route("/user/new", methods=["GET"]) +def new_user(): + rooms = eager_load_room_relationships(db.session.query(Room)).all() + users = eager_load_user_relationships(db.session.query(User)).all() + + user = User( + active=True + ) + + return render_template( + "user.html", + title="New User", + user=user, + users=users, + rooms=rooms + ) + +@main.route("/api/user", methods=["POST"]) +def create_user(): + try: + data = request.get_json(force=True) + + new_user = User.from_dict(data) + + db.session.add(new_user) + db.session.commit() + + return jsonify({"success": True, "id": new_user.id}), 201 + + except Exception as e: + db.session.rollback() + return jsonify({"success": False, "error": str(e)}), 400 + +@main.route("/api/user/", methods=["PUT"]) +def update_user(id): + try: + data = request.get_json(force=True) + user = db.session.query(User).get(id) + + if not user: + return jsonify({"success": False, "error": f"User with ID {id} not found."}), 404 + + user.staff = bool(data.get("staff", user.staff)) + user.active = bool(data.get("active", user.active)) + user.last_name = data.get("last_name", user.last_name) + user.first_name = data.get("first_name", user.first_name) + user.location_id = data.get("location_id", user.location_id) + user.supervisor_id = data.get("supervisor_id", user.supervisor_id) + + db.session.commit() + + return jsonify({"success": True, "id": user.id}), 200 + + except Exception as e: + db.session.rollback() + return jsonify({"success": False, "error": str(e)}), 400 \ No newline at end of file diff --git a/templates/inventory.html b/templates/inventory.html index fbcc601..e09b90e 100644 --- a/templates/inventory.html +++ b/templates/inventory.html @@ -157,7 +157,7 @@ const id = document.querySelector("#inventoryId").value; const isEdit = id && id !== "None"; - const endpoint = isEdit ? `/api/inventory/${id}` : "/api/inventory" + const endpoint = isEdit ? `/api/inventory/${id}` : "/api/inventory"; const method = isEdit ? "PUT" : "POST"; const response = await fetch(endpoint, { diff --git a/templates/user.html b/templates/user.html index cbdba37..5c87e25 100644 --- a/templates/user.html +++ b/templates/user.html @@ -7,26 +7,28 @@ {{ breadcrumbs.breadcrumb_header( -title=title, -breadcrumbs=[ -{'label': 'Users', 'url': url_for('main.list_users')} -] + title=title, + breadcrumbs=[ + {'label': 'Users', 'url': url_for('main.list_users')} + ], + save_button = True ) }} {% if not user.active %}
This user is inactive. You will not be able to make any changes to this record.
{% endif %} +
- +
- +
@@ -61,11 +63,11 @@ breadcrumbs=[
- +
- +
@@ -87,4 +89,54 @@ breadcrumbs=[ {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} + +{% block script %} + const saveButton = document.getElementById("saveButton"); + const deleteButton = document.getElementById("deleteButton"); + + if (saveButton) { + saveButton.addEventListener("click", async (e) => { + e.preventDefault(); + + const payload = { + staff: document.querySelector("input[name='staffCheck']").checked, + active: document.querySelector("input[name='activeCheck']").checked, + last_name: document.querySelector("input[name='lastName']").value, + first_name: document.querySelector("input[name='firstName']").value, + supervisor_id: parseInt(document.querySelector("select[name='supervisor']").value) || null, + location_id: parseInt(document.querySelector("select[name='location']").value) || null + }; + + try { + const id = document.querySelector("#userId").value; + const isEdit = id && id !== "None"; + + const endpoint = isEdit ? `/api/user/${id}` : "/api/user"; + const method = isEdit ? "PUT" : "POST"; + + const response = await fetch(endpoint, { + method, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(payload) + }); + + const result = await response.json(); + if (result.success) { + localStorage.setItem("toastMessage", JSON.stringify({ + message: isEdit ? "User updated!" : "User created!", + type: "success" + })); + + window.location.href = `/user/${result.id}`; + } else { + renderToast({ message: `Error: ${result.error}`, type: "danger" }); + } + } catch (err) { + console.error(err); + } + }); + } +{% endblock %}