402 lines
16 KiB
Python
402 lines
16 KiB
Python
import dash
|
|
import full_calendar_component as fcc
|
|
from dash import *
|
|
import dash_mantine_components as dmc
|
|
from dash.exceptions import PreventUpdate
|
|
from datetime import datetime, date, timedelta
|
|
import dash_quill
|
|
|
|
# dash._dash_renderer._set_react_version('18.2.0')
|
|
|
|
app = Dash(__name__, prevent_initial_callbacks=True)
|
|
|
|
quill_mods = [
|
|
[{"header": "1"}, {"header": "2"}, {"font": []}],
|
|
[{"size": []}],
|
|
["bold", "italic", "underline", "strike", "blockquote"],
|
|
[{"list": "ordered"}, {"list": "bullet"}, {"indent": "-1"}, {"indent": "+1"}],
|
|
["link", "image"],
|
|
]
|
|
|
|
# Get today's date
|
|
today = datetime.now()
|
|
|
|
# Format the date
|
|
formatted_date = today.strftime("%Y-%m-%d")
|
|
|
|
app.layout = html.Div(
|
|
[
|
|
fcc.FullCalendarComponent(
|
|
id="calendar", # Unique ID for the component
|
|
initialView="listWeek", # dayGridMonth, timeGridWeek, timeGridDay, listWeek,
|
|
# dayGridWeek, dayGridYear, multiMonthYear, resourceTimeline, resourceTimeGridDay, resourceTimeLineWeek
|
|
headerToolbar={
|
|
"left": "prev,next today",
|
|
"center": "",
|
|
"right": "listWeek,timeGridDay,timeGridWeek,dayGridMonth",
|
|
}, # Calendar header
|
|
initialDate=f"{formatted_date}", # Start date for calendar
|
|
editable=True, # Allow events to be edited
|
|
selectable=True, # Allow dates to be selected
|
|
events=[],
|
|
nowIndicator=True, # Show current time indicator
|
|
navLinks=True, # Allow navigation to other dates
|
|
),
|
|
dmc.MantineProvider(
|
|
theme={"colorScheme": "dark"},
|
|
children=[
|
|
dmc.Modal(
|
|
id="modal",
|
|
size="xl",
|
|
title="Event Details",
|
|
zIndex=10000,
|
|
children=[
|
|
html.Div(id="modal_event_display_context"),
|
|
dmc.Space(h=20),
|
|
dmc.Group(
|
|
[
|
|
dmc.Button(
|
|
"Close",
|
|
color="red",
|
|
variant="outline",
|
|
id="modal-close-button",
|
|
),
|
|
],
|
|
pos="right",
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
dmc.MantineProvider(
|
|
theme={"colorScheme": "dark"},
|
|
children=[
|
|
dmc.Modal(
|
|
id="add_modal",
|
|
title="New Event",
|
|
size="xl",
|
|
children=[
|
|
dmc.Grid(
|
|
children=[
|
|
dmc.GridCol(
|
|
html.Div(
|
|
dmc.DatePickerInput(
|
|
id="start_date",
|
|
label="Start Date",
|
|
value=datetime.now().date(),
|
|
styles={"width": "100%"},
|
|
disabled=True,
|
|
),
|
|
style={"width": "100%"},
|
|
),
|
|
span=6,
|
|
),
|
|
dmc.GridCol(
|
|
html.Div(
|
|
dmc.TimeInput(
|
|
label="Start Time",
|
|
withSeconds=True,
|
|
value=datetime.now(),
|
|
# format="12",
|
|
id="start_time",
|
|
),
|
|
style={"width": "100%"},
|
|
),
|
|
span=6,
|
|
),
|
|
],
|
|
gutter="xl",
|
|
),
|
|
dmc.Grid(
|
|
children=[
|
|
dmc.GridCol(
|
|
html.Div(
|
|
dmc.DatePickerInput(
|
|
id="end_date",
|
|
label="End Date",
|
|
value=datetime.now().date(),
|
|
styles={"width": "100%"},
|
|
),
|
|
style={"width": "100%"},
|
|
),
|
|
span=6,
|
|
),
|
|
dmc.GridCol(
|
|
html.Div(
|
|
dmc.TimeInput(
|
|
label="End Time",
|
|
withSeconds=True,
|
|
value=datetime.now(),
|
|
# format="12",
|
|
id="end_time",
|
|
),
|
|
style={"width": "100%"},
|
|
),
|
|
span=6,
|
|
),
|
|
],
|
|
gutter="xl",
|
|
),
|
|
dmc.Grid(
|
|
children=[
|
|
dmc.GridCol(
|
|
span=6,
|
|
children=[
|
|
dmc.TextInput(
|
|
label="Event Title:",
|
|
style={"width": "100%"},
|
|
id="event_name_input",
|
|
required=True,
|
|
)
|
|
],
|
|
),
|
|
dmc.GridCol(
|
|
span=6,
|
|
children=[
|
|
dmc.Select(
|
|
label="Select event color",
|
|
placeholder="Select one",
|
|
id="event_color_select",
|
|
value="ng",
|
|
data=[
|
|
{
|
|
"value": "bg-gradient-primary",
|
|
"label": "bg-gradient-primary",
|
|
},
|
|
{
|
|
"value": "bg-gradient-secondary",
|
|
"label": "bg-gradient-secondary",
|
|
},
|
|
{
|
|
"value": "bg-gradient-success",
|
|
"label": "bg-gradient-success",
|
|
},
|
|
{
|
|
"value": "bg-gradient-info",
|
|
"label": "bg-gradient-info",
|
|
},
|
|
{
|
|
"value": "bg-gradient-warning",
|
|
"label": "bg-gradient-warning",
|
|
},
|
|
{
|
|
"value": "bg-gradient-danger",
|
|
"label": "bg-gradient-danger",
|
|
},
|
|
{
|
|
"value": "bg-gradient-light",
|
|
"label": "bg-gradient-light",
|
|
},
|
|
{
|
|
"value": "bg-gradient-dark",
|
|
"label": "bg-gradient-dark",
|
|
},
|
|
{
|
|
"value": "bg-gradient-white",
|
|
"label": "bg-gradient-white",
|
|
},
|
|
],
|
|
style={"width": "100%", "marginBottom": 10},
|
|
required=True,
|
|
)
|
|
],
|
|
),
|
|
]
|
|
),
|
|
dash_quill.Quill(
|
|
id="rich_text_input",
|
|
modules={
|
|
"toolbar": quill_mods,
|
|
"clipboard": {
|
|
"matchVisual": False,
|
|
},
|
|
},
|
|
),
|
|
dmc.Accordion(
|
|
children=[
|
|
dmc.AccordionItem(
|
|
[
|
|
dmc.AccordionControl("Raw HTML"),
|
|
dmc.AccordionPanel(
|
|
html.Div(
|
|
id="rich_text_output",
|
|
style={
|
|
"height": "300px",
|
|
"overflowY": "scroll",
|
|
},
|
|
)
|
|
),
|
|
],
|
|
value="raw_html",
|
|
),
|
|
],
|
|
),
|
|
dmc.Space(h=20),
|
|
dmc.Group(
|
|
[
|
|
dmc.Button(
|
|
"Submit",
|
|
id="modal_submit_new_event_button",
|
|
color="green",
|
|
),
|
|
dmc.Button(
|
|
"Close",
|
|
color="red",
|
|
variant="outline",
|
|
id="modal_close_new_event_button",
|
|
),
|
|
],
|
|
pos="right",
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
@app.callback(
|
|
Output("modal", "opened"),
|
|
Output("modal", "title"),
|
|
Output("modal_event_display_context", "children"),
|
|
Input("modal-close-button", "n_clicks"),
|
|
Input("calendar", "clickedEvent"),
|
|
State("modal", "opened"),
|
|
)
|
|
def open_event_modal(n, clickedEvent, opened):
|
|
ctx = callback_context
|
|
|
|
if not ctx.triggered:
|
|
raise PreventUpdate
|
|
else:
|
|
button_id = ctx.triggered[0]["prop_id"].split(".")[0]
|
|
|
|
if button_id == "calendar" and clickedEvent is not None:
|
|
event_title = clickedEvent["title"]
|
|
event_context = clickedEvent["extendedProps"]["context"]
|
|
return (
|
|
True,
|
|
event_title,
|
|
html.Div(
|
|
dash_quill.Quill(
|
|
id="input3",
|
|
value=f"{event_context}",
|
|
modules={
|
|
"toolbar": False,
|
|
"clipboard": {
|
|
"matchVisual": False,
|
|
},
|
|
},
|
|
),
|
|
style={"width": "100%", "overflowY": "auto"},
|
|
),
|
|
)
|
|
elif button_id == "modal-close-button" and n is not None:
|
|
return False, dash.no_update, dash.no_update
|
|
|
|
return opened, dash.no_update
|
|
|
|
|
|
@app.callback(
|
|
Output("add_modal", "opened"),
|
|
Output("start_date", "value"),
|
|
Output("end_date", "value"),
|
|
Output("start_time", "value"),
|
|
Output("end_time", "value"),
|
|
Input("calendar", "dateClicked"),
|
|
Input("modal_close_new_event_button", "n_clicks"),
|
|
State("add_modal", "opened"),
|
|
)
|
|
def open_add_modal(dateClicked, close_clicks, opened):
|
|
|
|
ctx = callback_context
|
|
|
|
if not ctx.triggered:
|
|
raise PreventUpdate
|
|
else:
|
|
button_id = ctx.triggered[0]["prop_id"].split(".")[0]
|
|
|
|
if button_id == "calendar" and dateClicked is not None:
|
|
try:
|
|
start_time = datetime.strptime(dateClicked, "%Y-%m-%dT%H:%M:%S%z").time()
|
|
start_date_obj = datetime.strptime(dateClicked, "%Y-%m-%dT%H:%M:%S%z")
|
|
start_date = start_date_obj.strftime("%Y-%m-%d")
|
|
end_date = start_date_obj.strftime("%Y-%m-%d")
|
|
except ValueError:
|
|
start_time = datetime.now().time()
|
|
start_date_obj = datetime.strptime(dateClicked, "%Y-%m-%d")
|
|
start_date = start_date_obj.strftime("%Y-%m-%d")
|
|
end_date = start_date_obj.strftime("%Y-%m-%d")
|
|
end_time = datetime.combine(date.today(), start_time) + timedelta(hours=1)
|
|
start_time_str = start_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
end_time_str = end_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
return True, start_date, end_date, start_time_str, end_time_str
|
|
|
|
elif button_id == "modal_close_new_event_button" and close_clicks is not None:
|
|
return False, dash.no_update, dash.no_update, dash.no_update, dash.no_update
|
|
|
|
return opened, dash.no_update, dash.no_update, dash.no_update, dash.no_update
|
|
|
|
|
|
@app.callback(
|
|
Output("calendar", "events"),
|
|
Output("add_modal", "opened", allow_duplicate=True),
|
|
Output("event_name_input", "value"),
|
|
Output("event_color_select", "value"),
|
|
Output("rich_text_input", "value"),
|
|
Input("modal_submit_new_event_button", "n_clicks"),
|
|
State("start_date", "value"),
|
|
State("start_time", "value"),
|
|
State("end_date", "value"),
|
|
State("end_time", "value"),
|
|
State("event_name_input", "value"),
|
|
State("event_color_select", "value"),
|
|
State("rich_text_output", "children"),
|
|
State("calendar", "events"),
|
|
)
|
|
def add_new_event(
|
|
n,
|
|
start_date,
|
|
start_time,
|
|
end_date,
|
|
end_time,
|
|
event_name,
|
|
event_color,
|
|
event_context,
|
|
current_events,
|
|
):
|
|
if n is None:
|
|
raise PreventUpdate
|
|
|
|
start_time_obj = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
|
|
end_time_obj = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
|
|
|
|
start_time_str = start_time_obj.strftime("%H:%M:%S")
|
|
end_time_str = end_time_obj.strftime("%H:%M:%S")
|
|
|
|
start_date = f"{start_date}T{start_time_str}"
|
|
end_date = f"{end_date}T{end_time_str}"
|
|
|
|
new_event = {
|
|
"title": event_name,
|
|
"start": start_date,
|
|
"end": end_date,
|
|
"className": event_color,
|
|
"context": event_context,
|
|
}
|
|
|
|
return current_events + [new_event], False, "", "bg-gradient-primary", ""
|
|
|
|
|
|
@app.callback(
|
|
Output("rich_text_output", "children"),
|
|
[Input("rich_text_input", "value")],
|
|
[State("rich_text_input", "charCount")],
|
|
)
|
|
def display_output(value, charCount):
|
|
return value
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host='0.0.0.0', debug=True, port=8050) |