first feasability of event management feature

This commit is contained in:
2025-06-19 07:34:56 +00:00
parent dc7fa6b2cb
commit 19f817b796
8 changed files with 120 additions and 19 deletions

1
.gitignore vendored
View File

@@ -72,3 +72,4 @@ dashboard/pages/test.py
.gitignore .gitignore
dashboard/sidebar_test.py dashboard/sidebar_test.py
dashboard/assets/responsive-sidebar.css dashboard/assets/responsive-sidebar.css
certs/

View File

@@ -36,6 +36,10 @@ app.layout = dmc.MantineProvider([
]) ])
if __name__ == "__main__": 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")
)

View File

@@ -201,11 +201,30 @@ body {
z-index: var(--mantine-z-index-modal, 3000) !important; z-index: var(--mantine-z-index-modal, 3000) !important;
} }
/* Modalbox */
.mantine-Modal-inner, .mantine-Modal-inner,
.mantine-Modal-content { .mantine-Modal-content {
z-index: 4000 !important; 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: Icon-Farbe normal */
.sidebar.collapsed .sidebar-item-collapsed svg { .sidebar.collapsed .sidebar-item-collapsed svg {
color: #7c5617; /* Icon-Linie/Text */ color: #7c5617; /* Icon-Linie/Text */

View File

@@ -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 import dash_mantine_components as dmc
from dash_iconify import DashIconify from dash_iconify import DashIconify
import dash_quill import dash_quill
@@ -313,7 +313,6 @@ def update_end_time_options(search_value):
prevent_initial_call=True prevent_initial_call=True
) )
def handle_end_time(start_time, reset_clicks, current_end_time): def handle_end_time(start_time, reset_clicks, current_end_time):
ctx = callback.ctx
if not ctx.triggered: if not ctx.triggered:
return no_update return no_update
trigger_id = ctx.triggered[0]['prop_id'].split('.')[0] 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( @callback(
[ [
Output('title-input', 'value'), Output('title-input', 'value'),
Output('start-date-input', 'value'), Output('start-date-input', 'value', allow_duplicate=True),
Output('time-start', 'value'), Output('time-start', 'value'),
Output('type-input', 'value'), Output('type-input', 'value'),
Output('description-input', 'value'), Output('description-input', 'value'),

View File

@@ -2,9 +2,13 @@
import requests import requests
import json import json
from flask import session 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 os
import sys import sys
from datetime import datetime, timedelta
# --- Modalbox öffnen: jetzt auch auf Kalenderklick reagieren ---
sys.path.append('/workspace') sys.path.append('/workspace')
print("appointments_callbacks.py geladen") print("appointments_callbacks.py geladen")
@@ -69,19 +73,86 @@ def load_events(view_dates):
@callback( @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("open-appointment-modal-btn", "n_clicks"),
Input("close-appointment-modal-btn", "n_clicks") Input("close-appointment-modal-btn", "n_clicks"),
], ],
State("appointment-modal", "opened"), State("appointment-modal", "opened"),
prevent_initial_call=True prevent_initial_call=True
) )
def toggle_appointment_modal(open_click, close_click, is_open): def open_modal(date_click, select, open_click, close_click, is_open):
from dash import ctx
trigger = ctx.triggered_id 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: 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: if trigger == "close-appointment-modal-btn" and close_click:
return False return False, no_update, no_update, no_update
return is_open
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

View File

@@ -62,11 +62,6 @@ def get_appointment_modal():
title="Neuen Termin anlegen", title="Neuen Termin anlegen",
centered=True, centered=True,
size="auto", # oder "80vw" size="auto", # oder "80vw"
# fullScreen=True,
# styles={
# "modal": {"zIndex": 2001, "position": "relative"},
# "overlay": {"zIndex": 2000}
# },
children=[ children=[
dmc.Container([ dmc.Container([
dmc.Grid([ dmc.Grid([
@@ -147,13 +142,14 @@ def get_appointment_modal():
{"value": "website", "label": "Website"}, {"value": "website", "label": "Website"},
{"value": "video", "label": "Video"}, {"value": "video", "label": "Video"},
{"value": "message", "label": "Nachricht"}, {"value": "message", "label": "Nachricht"},
{"value": "webuntis", "label": "WebUntis"},
{"value": "other", "label": "Sonstiges"} {"value": "other", "label": "Sonstiges"}
], ],
id="type-input", id="type-input",
required=True, required=True,
style={"flex": 1} 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"), html.Div(id="type-specific-fields"),
create_input_with_tooltip_full( create_input_with_tooltip_full(

View File

@@ -11,6 +11,7 @@ services:
- "443:443" - "443:443"
volumes: volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on: depends_on:
- server - server
- dashboard - dashboard

View File

@@ -7,6 +7,16 @@ http {
listen 80; listen 80;
server_name _; 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/ { location /api/ {
proxy_pass http://infoscreen-api:8000/api/; proxy_pass http://infoscreen-api:8000/api/;
} }