From 19f817b7966e904a460e11f2cafaf9adaf3628b2 Mon Sep 17 00:00:00 2001 From: olaf Date: Thu, 19 Jun 2025 07:34:56 +0000 Subject: [PATCH] first feasability of event management feature --- .gitignore | 1 + dashboard/app.py | 8 +- dashboard/assets/custom.css | 19 ++++ .../callbacks/appointment_modal_callbacks.py | 5 +- dashboard/callbacks/appointments_callbacks.py | 87 +++++++++++++++++-- dashboard/components/appointment_modal.py | 8 +- docker-compose.yml | 1 + nginx.conf | 10 +++ 8 files changed, 120 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index ab2a88a..9bfad9c 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,4 @@ dashboard/pages/test.py .gitignore dashboard/sidebar_test.py dashboard/assets/responsive-sidebar.css +certs/ diff --git a/dashboard/app.py b/dashboard/app.py index f4c6f79..fa08372 100644 --- a/dashboard/app.py +++ b/dashboard/app.py @@ -36,6 +36,10 @@ app.layout = dmc.MantineProvider([ ]) - if __name__ == "__main__": - app.run(host="0.0.0.0", port=8050, debug=(ENV=="development")) + app.run( + host="0.0.0.0", + port=8050, + debug=(ENV=="development"), + ssl_context=("/workspace/certs/dev.crt", "/workspace/certs/dev.key") + ) diff --git a/dashboard/assets/custom.css b/dashboard/assets/custom.css index 80c5981..c2bda9b 100644 --- a/dashboard/assets/custom.css +++ b/dashboard/assets/custom.css @@ -201,11 +201,30 @@ body { z-index: var(--mantine-z-index-modal, 3000) !important; } +/* Modalbox */ .mantine-Modal-inner, .mantine-Modal-content { z-index: 4000 !important; } +/* Popups (Dropdowns, Datepicker, Autocomplete, Menüs) innerhalb der Modalbox */ +.mantine-Popover-dropdown, +.mantine-Select-dropdown, +.mantine-DatePicker-dropdown, +.mantine-Autocomplete-dropdown, +.mantine-Menu-dropdown { + z-index: 4100 !important; +} + +/* Optional: Overlay für Popups noch höher, falls benötigt */ +.mantine-Popover-root, +.mantine-Select-root, +.mantine-DatePicker-root, +.mantine-Autocomplete-root, +.mantine-Menu-root { + z-index: 4101 !important; +} + /* Sidebar collapsed: Icon-Farbe normal */ .sidebar.collapsed .sidebar-item-collapsed svg { color: #7c5617; /* Icon-Linie/Text */ diff --git a/dashboard/callbacks/appointment_modal_callbacks.py b/dashboard/callbacks/appointment_modal_callbacks.py index 2c4f09e..60398d4 100644 --- a/dashboard/callbacks/appointment_modal_callbacks.py +++ b/dashboard/callbacks/appointment_modal_callbacks.py @@ -1,4 +1,4 @@ -from dash import Input, Output, State, callback, html, dcc, no_update +from dash import Input, Output, State, callback, html, dcc, no_update, ctx import dash_mantine_components as dmc from dash_iconify import DashIconify import dash_quill @@ -313,7 +313,6 @@ def update_end_time_options(search_value): prevent_initial_call=True ) def handle_end_time(start_time, reset_clicks, current_end_time): - ctx = callback.ctx if not ctx.triggered: return no_update trigger_id = ctx.triggered[0]['prop_id'].split('.')[0] @@ -338,7 +337,7 @@ def handle_end_time(start_time, reset_clicks, current_end_time): @callback( [ Output('title-input', 'value'), - Output('start-date-input', 'value'), + Output('start-date-input', 'value', allow_duplicate=True), Output('time-start', 'value'), Output('type-input', 'value'), Output('description-input', 'value'), diff --git a/dashboard/callbacks/appointments_callbacks.py b/dashboard/callbacks/appointments_callbacks.py index f82510e..2948660 100644 --- a/dashboard/callbacks/appointments_callbacks.py +++ b/dashboard/callbacks/appointments_callbacks.py @@ -2,9 +2,13 @@ import requests import json from flask import session -from dash import Input, Output, State, callback, ctx, dash +from dash import Input, Output, State, callback, ctx, dash, no_update import os import sys +from datetime import datetime, timedelta + +# --- Modalbox öffnen: jetzt auch auf Kalenderklick reagieren --- + sys.path.append('/workspace') print("appointments_callbacks.py geladen") @@ -69,19 +73,86 @@ def load_events(view_dates): @callback( - Output("appointment-modal", "opened"), [ + Output("appointment-modal", "opened"), + Output("start-date-input", "value", allow_duplicate=True), + Output("time-start", "value", allow_duplicate=True), + Output("time-end", "value", allow_duplicate=True), + ], + [ + Input("calendar", "lastDateClick"), + Input("calendar", "lastSelect"), Input("open-appointment-modal-btn", "n_clicks"), - Input("close-appointment-modal-btn", "n_clicks") + Input("close-appointment-modal-btn", "n_clicks"), ], State("appointment-modal", "opened"), prevent_initial_call=True ) -def toggle_appointment_modal(open_click, close_click, is_open): - from dash import ctx +def open_modal(date_click, select, open_click, close_click, is_open): trigger = ctx.triggered_id + + # Bereichsauswahl (lastSelect) + if trigger == "calendar" and select: + try: + start_dt = datetime.fromisoformat(select["start"]) + end_dt = datetime.fromisoformat(select["end"]) + + return ( + True, + start_dt.date().isoformat(), + start_dt.strftime("%H:%M"), + end_dt.strftime("%H:%M"), + ) + except Exception as e: + print("Fehler beim Parsen von select:", e) + return no_update, no_update, no_update, no_update + + # Einzelklick (lastDateClick) + if trigger == "calendar" and date_click: + try: + dt = datetime.fromisoformat(date_click) + # Versuche, die Slotlänge aus dem Kalender zu übernehmen (optional) + # Hier als Beispiel 30 Minuten aufaddieren, falls keine Endzeit vorhanden + end_dt = dt + timedelta(minutes=30) + return ( + True, + dt.date().isoformat(), + dt.strftime("%H:%M"), + end_dt.strftime("%H:%M"), + ) + except Exception as e: + print("Fehler beim Parsen von date_click:", e) + return no_update, no_update, no_update, no_update + + # Modal öffnen per Button if trigger == "open-appointment-modal-btn" and open_click: - return True + now = datetime.now() + end_dt = now + timedelta(minutes=30) + return True, now.date().isoformat(), now.strftime("%H:%M"), end_dt.strftime("%H:%M") + + # Modal schließen if trigger == "close-appointment-modal-btn" and close_click: - return False - return is_open + return False, no_update, no_update, no_update + + return is_open, no_update, no_update, no_update + + +# @callback( +# Output("time-end", "value", allow_duplicate=True), +# Input("time-start", "value"), +# prevent_initial_call=True +# ) +# def handle_end_time(start_time, duration="00:30"): +# trigger = ctx.triggered_id +# if trigger == "time-start" and start_time and duration: +# try: +# # Beispiel für start_time: "09:00" +# start_dt = datetime.strptime(start_time, "%H:%M") +# # Dauer in Stunden und Minuten, z.B. "01:30" +# hours, minutes = map(int, duration.split(":")) +# # Endzeit berechnen: Dauer addieren! +# end_dt = start_dt + timedelta(hours=hours, minutes=minutes) +# return end_dt.strftime("%H:%M") +# except Exception as e: +# print("Fehler bei der Berechnung der Endzeit:", e) +# return no_update diff --git a/dashboard/components/appointment_modal.py b/dashboard/components/appointment_modal.py index fedcb84..3afd7d7 100644 --- a/dashboard/components/appointment_modal.py +++ b/dashboard/components/appointment_modal.py @@ -62,11 +62,6 @@ def get_appointment_modal(): title="Neuen Termin anlegen", centered=True, size="auto", # oder "80vw" - # fullScreen=True, - # styles={ - # "modal": {"zIndex": 2001, "position": "relative"}, - # "overlay": {"zIndex": 2000} - # }, children=[ dmc.Container([ dmc.Grid([ @@ -147,13 +142,14 @@ def get_appointment_modal(): {"value": "website", "label": "Website"}, {"value": "video", "label": "Video"}, {"value": "message", "label": "Nachricht"}, + {"value": "webuntis", "label": "WebUntis"}, {"value": "other", "label": "Sonstiges"} ], id="type-input", required=True, style={"flex": 1} ), - "Wählen Sie den Typ des Termins für bessere Kategorisierung" + "Wählen Sie die Art der Präsentation aus." ), html.Div(id="type-specific-fields"), create_input_with_tooltip_full( diff --git a/docker-compose.yml b/docker-compose.yml index 100a9c6..50c29f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./certs:/etc/nginx/certs:ro depends_on: - server - dashboard diff --git a/nginx.conf b/nginx.conf index 457383e..0995143 100644 --- a/nginx.conf +++ b/nginx.conf @@ -7,6 +7,16 @@ http { listen 80; server_name _; + # Optional: HTTP auf HTTPS weiterleiten + return 301 https://$host$request_uri; + } + server { + listen 443 ssl; + server_name _; + + ssl_certificate /etc/nginx/certs/dev.crt; + ssl_certificate_key /etc/nginx/certs/dev.key; + location /api/ { proxy_pass http://infoscreen-api:8000/api/; }