Files
infoscreen/server/routes/event_exceptions.py
RobbStarkAustria a7df3c2708 feat(dashboard): header user dropdown (Syncfusion) + proper logout; docs: clarify architecture; build: add splitbuttons; bump alpha.10
Dashboard

Add top-right user dropdown using Syncfusion DropDownButton: shows username + role; menu entries “Profil” and “Abmelden”.
Replace custom dropdown logic with Syncfusion component; position at header’s right edge.
Update /logout page to call backend logout and redirect to /login (reliable user switching).
Build/Config

Add @syncfusion/ej2-react-splitbuttons and @syncfusion/ej2-splitbuttons dependencies.
Update Vite optimizeDeps.include to pre-bundle splitbuttons and avoid import-analysis errors.
Docs

README: Rework Architecture Overview with clearer data flow:
Listener consumes MQTT (discovery/heartbeats) and updates API.
Scheduler reads from API and publishes events via MQTT to clients.
Clients send via MQTT and receive via MQTT.
Worker receives commands directly from API and reports results back (no MQTT).
Explicit note: MariaDB is accessed exclusively by the API Server; Dashboard never talks to DB directly.
README: Add SplitButtons to “Syncfusion Components Used”; add troubleshooting steps for @syncfusion/ej2-react-splitbuttons import issues (optimizeDeps + volume reset).
Copilot instructions: Document header user menu and splitbuttons technical notes (deps, optimizeDeps, dev-container node_modules volume).
Program info

Bump to 2025.1.0-alpha.10 with changelog:
UI: Header user menu (DropDownButton with username/role; Profil/Abmelden).
Frontend: Syncfusion SplitButtons integration + Vite pre-bundling config.
Fix: Added README guidance for splitbuttons import errors.
No breaking changes.
2025-10-15 16:33:35 +00:00

118 lines
4.8 KiB
Python

from flask import Blueprint, request, jsonify
from server.permissions import editor_or_higher
from server.database import Session
from models.models import EventException, Event
from datetime import datetime, date
event_exceptions_bp = Blueprint("event_exceptions", __name__, url_prefix="/api/event_exceptions")
@event_exceptions_bp.route("", methods=["POST"])
@editor_or_higher
def create_exception():
data = request.json
session = Session()
# required: event_id, exception_date
required = ["event_id", "exception_date"]
for f in required:
if f not in data:
return jsonify({"error": f"Missing field: {f}"}), 400
# Validate event exists
event = session.query(Event).filter_by(id=data["event_id"]).first()
if not event:
session.close()
return jsonify({"error": "Event not found"}), 404
exc_date = datetime.fromisoformat(data["exception_date"]).date() if isinstance(data["exception_date"], str) else data["exception_date"]
# Check if an exception for this event and date already exists
existing_exc = session.query(EventException).filter_by(event_id=event.id, exception_date=exc_date).first()
if existing_exc:
# Optionally, update the existing exception fields if needed
existing_exc.is_skipped = bool(data.get("is_skipped", existing_exc.is_skipped))
existing_exc.override_title = data.get("override_title", existing_exc.override_title)
existing_exc.override_description = data.get("override_description", existing_exc.override_description)
existing_exc.override_start = datetime.fromisoformat(data["override_start"]) if data.get("override_start") else existing_exc.override_start
existing_exc.override_end = datetime.fromisoformat(data["override_end"]) if data.get("override_end") else existing_exc.override_end
session.commit()
return jsonify({"success": True, "id": existing_exc.id, "updated": True})
# Otherwise, create a new exception
exc = EventException(
event_id=event.id,
exception_date=exc_date,
is_skipped=bool(data.get("is_skipped", False)),
override_title=data.get("override_title"),
override_description=data.get("override_description"),
override_start=(datetime.fromisoformat(data["override_start"]) if data.get("override_start") else None),
override_end=(datetime.fromisoformat(data["override_end"]) if data.get("override_end") else None),
)
session.add(exc)
session.commit()
return jsonify({"success": True, "id": exc.id})
@event_exceptions_bp.route("/<exc_id>", methods=["PUT"])
@editor_or_higher
def update_exception(exc_id):
data = request.json
session = Session()
exc = session.query(EventException).filter_by(id=exc_id).first()
if not exc:
session.close()
return jsonify({"error": "Exception not found"}), 404
if "exception_date" in data:
exc.exception_date = datetime.fromisoformat(data["exception_date"]).date()
if "is_skipped" in data:
exc.is_skipped = bool(data["is_skipped"])
if "override_title" in data:
exc.override_title = data.get("override_title")
if "override_description" in data:
exc.override_description = data.get("override_description")
if "override_start" in data:
exc.override_start = datetime.fromisoformat(data["override_start"]) if data.get("override_start") else None
if "override_end" in data:
exc.override_end = datetime.fromisoformat(data["override_end"]) if data.get("override_end") else None
session.commit()
session.close()
return jsonify({"success": True})
@event_exceptions_bp.route("/<exc_id>", methods=["DELETE"])
@editor_or_higher
def delete_exception(exc_id):
session = Session()
exc = session.query(EventException).filter_by(id=exc_id).first()
if not exc:
session.close()
return jsonify({"error": "Exception not found"}), 404
session.delete(exc)
session.commit()
session.close()
return jsonify({"success": True})
@event_exceptions_bp.route("", methods=["GET"])
def list_exceptions():
session = Session()
event_id = request.args.get("event_id")
q = session.query(EventException)
if event_id:
q = q.filter(EventException.event_id == int(event_id))
rows = q.all()
out = []
for r in rows:
out.append({
"id": r.id,
"event_id": r.event_id,
"exception_date": r.exception_date.isoformat(),
"is_skipped": r.is_skipped,
"override_title": r.override_title,
"override_description": r.override_description,
"override_start": r.override_start.isoformat() if r.override_start else None,
"override_end": r.override_end.isoformat() if r.override_end else None,
})
session.close()
return jsonify(out)