initial feature/react-migration commit

This commit is contained in:
2025-06-22 20:57:21 +00:00
parent 6653f3cf72
commit 76f6baf533
66 changed files with 12038 additions and 91 deletions

View File

@@ -0,0 +1,258 @@
from dash import html, dcc
import dash_mantine_components as dmc
from dash_iconify import DashIconify
import dash_quill
def create_input_with_tooltip_full(component, tooltip_text):
return dmc.Stack([
dmc.Group([
component,
dmc.Tooltip(
children=[
dmc.ActionIcon(
DashIconify(icon="mdi:help-circle-outline", width=16),
variant="subtle",
color="gray",
size="sm"
)
],
label=tooltip_text,
position="top",
multiline=True,
w=300
)
], gap="xs", align="flex-end")
], gap=0)
def create_input_with_tooltip_time(component, tooltip_text):
return dmc.Group([
component,
dmc.Tooltip(
children=[
dmc.ActionIcon(
DashIconify(icon="mdi:help-circle-outline", width=16),
variant="subtle",
color="gray",
size="sm"
)
],
label=tooltip_text,
position="top",
multiline=True,
w=300
)
], gap="xs", align="flex-end")
def get_appointment_modal():
weekday_options = [
{"value": "0", "label": "Montag"},
{"value": "1", "label": "Dienstag"},
{"value": "2", "label": "Mittwoch"},
{"value": "3", "label": "Donnerstag"},
{"value": "4", "label": "Freitag"},
{"value": "5", "label": "Samstag"},
{"value": "6", "label": "Sonntag"}
]
time_options = [
{"value": f"{h:02d}:{m:02d}", "label": f"{h:02d}:{m:02d}"}
for h in range(6, 24) for m in [0, 30]
]
return dmc.Modal(
id="appointment-modal",
title="Neuen Termin anlegen",
centered=True,
size="auto", # oder "80vw"
children=[
dmc.Container([
dmc.Grid([
dmc.GridCol([
dmc.Paper([
dmc.Title("Termindetails", order=3, className="mb-3"),
dmc.Stack([
create_input_with_tooltip_full(
dmc.TextInput(
label="Titel",
placeholder="Terminbezeichnung eingeben",
leftSection=DashIconify(icon="mdi:calendar-text"),
id="title-input",
required=True,
style={"flex": 1}
),
"Geben Sie eine aussagekräftige Bezeichnung für den Termin ein"
),
create_input_with_tooltip_full(
dmc.DatePickerInput(
label="Startdatum",
id="start-date-input",
firstDayOfWeek=1,
weekendDays=[0, 6],
valueFormat="DD.MM.YYYY",
placeholder="Datum auswählen",
leftSection=DashIconify(icon="mdi:calendar"),
clearable=False,
style={"flex": 1}
),
"Wählen Sie das Datum für den Termin aus dem Kalender"
),
dmc.Grid([
dmc.GridCol([
dmc.Stack([
create_input_with_tooltip_time(
dmc.Select(
label="Startzeit",
placeholder="Zeit auswählen",
data=time_options,
searchable=True,
clearable=True,
id="time-start",
value="09:00",
required=True,
style={"flex": 1}
),
"Eingabe: 10:25, 10.25, 1025 oder 10 (wird automatisch formatiert)"
),
html.Div(id="start-time-feedback")
], gap="xs")
], span=6),
dmc.GridCol([
dmc.Stack([
create_input_with_tooltip_time(
dmc.Select(
label="Endzeit",
placeholder="Zeit auswählen",
data=time_options,
searchable=True,
clearable=True,
id="time-end",
required=True,
style={"flex": 1}
),
"Endzeit muss nach der Startzeit am selben Tag liegen. Termine können nicht über Mitternacht hinausgehen."
),
html.Div(id="end-time-feedback")
], gap="xs")
], span=6)
]),
create_input_with_tooltip_full(
dmc.Select(
label="Termintyp",
placeholder="Typ auswählen",
data=[
{"value": "presentation", "label": "Präsentation"},
{"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 die Art der Präsentation aus."
),
html.Div(id="type-specific-fields"),
create_input_with_tooltip_full(
dmc.Textarea(
label="Beschreibung",
placeholder="Zusätzliche Informationen...",
minRows=3,
autosize=True,
id="description-input",
style={"flex": 1}
),
"Optionale Beschreibung mit weiteren Details zum Termin"
)
], gap="md")
], p="md", shadow="sm")
], span=6),
dmc.GridCol([
dmc.Paper([
dmc.Title("Wiederholung", order=3, className="mb-3"),
dmc.Stack([
create_input_with_tooltip_full(
dmc.Checkbox(
label="Wiederholender Termin",
id="repeat-checkbox",
description="Aktivieren für wöchentliche Wiederholung"
),
"Aktivieren Sie diese Option, um den Termin an mehreren Wochentagen zu wiederholen"
),
html.Div(id="repeat-options", children=[
create_input_with_tooltip_full(
dmc.MultiSelect(
label="Wochentage",
placeholder="Wochentage auswählen",
data=weekday_options,
id="weekdays-select",
description="An welchen Wochentagen soll der Termin stattfinden?",
disabled=True,
style={"flex": 1}
),
"Wählen Sie mindestens einen Wochentag für die Wiederholung aus"
),
dmc.Space(h="md"),
create_input_with_tooltip_full(
dmc.DatePickerInput(
label="Wiederholung bis",
id="repeat-until-date",
firstDayOfWeek=1,
weekendDays=[0, 6],
valueFormat="DD.MM.YYYY",
placeholder="Enddatum auswählen",
leftSection=DashIconify(icon="mdi:calendar-end"),
description="Letzter Tag der Wiederholung",
disabled=True,
clearable=True,
style={"flex": 1}
),
"Datum bis wann die Wiederholung stattfinden soll. Muss nach dem Startdatum liegen."
),
dmc.Space(h="lg"),
create_input_with_tooltip_full(
dmc.Checkbox(
label="Ferientage berücksichtigen",
id="skip-holidays-checkbox",
description="Termine an Feiertagen und in Schulferien auslassen",
disabled=True
),
"Aktivieren Sie diese Option, um Termine an deutschen Feiertagen und in Schulferien automatisch zu überspringen"
)
])
], gap="md")
], p="md", shadow="sm"),
dmc.Paper([
dmc.Title("Aktionen", order=3, className="mb-3"),
dmc.Stack([
dmc.Button(
"Termin(e) speichern",
color="green",
leftSection=DashIconify(icon="mdi:content-save"),
id="btn-save",
size="lg",
fullWidth=True
),
dmc.Button(
"Zurücksetzen",
color="gray",
variant="outline",
leftSection=DashIconify(icon="mdi:refresh"),
id="btn-reset",
fullWidth=True
),
dmc.Button(
"Schließen",
id="close-appointment-modal-btn",
color="red", # oder "danger"
leftSection=DashIconify(icon="mdi:close"),
variant="filled",
style={"marginBottom": 10}
),
html.Div(id="save-feedback", className="mt-3")
], gap="md")
], p="md", shadow="sm", className="mt-3")
], span=6)
])
], size="lg")
]
)

View File

@@ -0,0 +1,13 @@
# dashboard/components/header.py
from dash import html
def Header():
return html.Div(
className="app-header",
children=[
html.Img(src="/assets/logo.png", className="logo"),
html.Span("Infoscreen-Manager", className="app-title"),
html.Span(" Organisationsname", className="org-name"),
html.Div(id="header-right", className="header-right") # Platzhalter für Login/ProfilButton
]
)

View File

@@ -0,0 +1,72 @@
# dashboard/components/sidebar.py
from dash import html
import dash_bootstrap_components as dbc
from dash_iconify import DashIconify
def Sidebar(collapsed: bool = False):
"""
Gibt nur den Inhalt der Sidebar zurück (ohne das äußere div mit id="sidebar").
Das äußere div wird bereits in app.py definiert.
"""
nav_items = [
{"label": "Übersicht", "href": "/overview", "icon": "mdi:view-dashboard"},
{"label": "Termine", "href": "/appointments","icon": "mdi:calendar"},
{"label": "Bildschirme", "href": "/clients", "icon": "mdi:monitor"},
{"label": "Einstellungen","href": "/settings", "icon": "mdi:cog"},
{"label": "Benutzer", "href": "/users", "icon": "mdi:account"},
]
if collapsed:
nav_links = [
dbc.NavLink(
DashIconify(icon=item["icon"], width=24),
href=item["href"],
active="exact",
className="sidebar-item-collapsed",
id={"type": "nav-item", "index": item["label"]},
)
for item in nav_items
]
else:
nav_links = [
dbc.NavLink(
[
DashIconify(icon=item["icon"], width=24),
html.Span(item["label"], className="ms-2 sidebar-label"),
],
href=item["href"],
active="exact",
className="sidebar-item",
id={"type": "nav-item", "index": item["label"]},
)
for item in nav_items
]
return [
html.Div(
className="sidebar-toggle",
children=html.Button(
DashIconify(icon="mdi:menu", width=28),
id="btn-toggle-sidebar",
className="toggle-button",
)
),
dbc.Collapse(
dbc.Nav(
nav_links,
vertical=True,
pills=True,
className="sidebar-nav",
),
is_open=not collapsed,
className="sidebar-nav",
) if not collapsed else
dbc.Nav(
nav_links,
vertical=True,
pills=True,
className="sidebar-nav-collapsed",
),
]