Files
Sitzplatzbuchungstool/pages/Sitzplätze/widgets/SeatCalendar/Custom5.json
T
2026-08-17 09:58:57 +00:00

72 lines
28 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"backgroundColor": "#FFFFFF",
"borderColor": "#E0DEDE",
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
"borderWidth": "1",
"bottomRow": 69,
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
"defaultModel": "{{\n {\n events: GetBookings.data || [],\n seatId: appsmith.store.selectedSeatId,\n seatName: appsmith.store.selectedSeatName\n }\n}}",
"dynamicBindingPathList": [
{
"key": "theme"
},
{
"key": "borderRadius"
},
{
"key": "boxShadow"
},
{
"key": "defaultModel"
}
],
"dynamicHeight": "FIXED",
"dynamicPropertyPathList": [
{
"key": "seatSelected"
}
],
"dynamicTriggerPathList": [
{
"key": "seatSelected"
}
],
"events": [
"seatSelected"
],
"isCanvas": false,
"isLoading": false,
"isSearchWildcard": true,
"isVisible": true,
"key": "3r58ovsolf",
"leftColumn": 0,
"maxDynamicHeight": 9000,
"minDynamicHeight": 4,
"mobileBottomRow": 40,
"mobileLeftColumn": 3,
"mobileRightColumn": 26,
"mobileTopRow": 10,
"needsErrorInfo": false,
"parentColumnSpace": 7.40625,
"parentId": "ekq4ry46pv",
"parentRowSpace": 10,
"renderMode": "CANVAS",
"rightColumn": 63,
"seatSelected": "{{\n storeValue(\n \"selectedSeatId\",\n Custom4.model.selectedSeat.id\n )\n .then(() =>\n storeValue(\n \"selectedSeatName\",\n Custom4.model.selectedSeat.name\n )\n )\n .then(() => GetBookings.run())\n .then(() => showModal(SeatCalendar.name))\n}}",
"srcDoc": {
"css": "@charset \"UTF-8\";\nhtml,\nbody {\n margin: 0;\n padding: 0;\n width: 100%;\n min-height: 100%;\n font-family: Arial, sans-serif;\n overflow: hidden;\n}\n\n/* Äußerer Custom-Widget-Bereich */\n#calendarWrapper {\n width: 100%;\n min-height: 0;\n box-sizing: border-box;\n padding: 10px;\n background: #ffffff;\n overflow: visible;\n}\n\n/* Kopfbereich */\n#calendarHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 10px;\n padding: 8px 10px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n background: #f9fafb;\n}\n\n#calendarSeatTitle {\n font-size: 15px;\n font-weight: 600;\n color: #374151;\n}\n\n/* Kalender */\n#calendar {\n width: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n/* Auswahltext */\n#calendarSelection {\n margin-top: 10px;\n padding: 8px 10px;\n border: 1px solid #d1d5db;\n border-radius: 5px;\n background: #f9fafb;\n color: #374151;\n font-size: 13px;\n}\n\n/* FullCalendar-Grundformatierung */\n.fc {\n width: 100%;\n font-size: 13px;\n}\n\n.fc .fc-button {\n background: #374151;\n border-color: #374151;\n}\n\n.fc .fc-button:hover,\n.fc .fc-button-active {\n background: #111827;\n border-color: #111827;\n}\n\n.fc .fc-daygrid-day.fc-day-today,\n.fc .fc-timegrid-col.fc-day-today {\n background: #eff6ff;\n}\n\n.fc .fc-event {\n cursor: pointer;\n}\n\n/* Wochenenden grau */\n#calendar td.fc-day-sat,\n#calendar td.fc-day-sun,\n#calendar .fc-timegrid-col.fc-day-sat,\n#calendar .fc-timegrid-col.fc-day-sun {\n background-color: #eeeeee !important;\n}\n\n/* Wochenend-Kopfzellen */\n#calendar .fc-col-header-cell.fc-day-sat,\n#calendar .fc-col-header-cell.fc-day-sun {\n background-color: #d6d6d6 !important;\n}\n\n/*\n* Monatsansicht:\n* kein Scrollen innerhalb des Monats.\n*/\n#calendar .fc-daygrid .fc-scroller {\n height: auto !important;\n max-height: none !important;\n overflow-y: visible !important;\n overflow-x: visible !important;\n}\n\n/*\n* Tagesansicht:\n* nur der Zeitbereich scrollt.\n*/\n#calendar .fc-timegrid .fc-scroller {\n overflow-y: auto !important;\n overflow-x: hidden !important;\n overscroll-behavior: contain;\n}\n\n/*\n/*\n * TimeGrid-Grundstruktur\n */\n/*\n * TimeGrid-Grundstruktur\n */\n#calendar .fc-timegrid {\n min-height: 0 !important;\n}\n\n#calendar .fc-timegrid .fc-view-harness {\n min-height: 0 !important;\n}\n\n/*\n * Tabellenbreite\n */\n#calendar .fc-scrollgrid {\n width: 100%;\n max-width: 100%;\n}\n\n/*\n * Größere 30-Minuten-Zeilen\n */\n#calendar .fc-timegrid-slot {\n height: 30px !important;\n min-height: 30px !important;\n}\n\n#calendar .fc-timegrid-slot-label {\n height: 30px !important;\n min-height: 30px !important;\n}\n\n/*\n * Tagesansicht:\n * Keine feste CSS-Höhe setzen.\n * FullCalendar übernimmt 1080 px aus contentHeight.\n */\n#calendar .fc-timegrid .fc-scroller {\n overflow-y: auto !important;\n overflow-x: hidden !important;\n overscroll-behavior: contain;\n}",
"html": "<div id=\"calendarWrapper\">\n <div id=\"calendarHeader\">\n <div id=\"calendarSeatTitle\">\n Kalender\n </div>\n </div>\n\n <div id=\"calendar\"></div>\n\n <div id=\"calendarSelection\">\n Keine Auswahl\n </div>\n</div>\n\n<link\n rel=\"stylesheet\"\n href=\"https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.css\"\n>\n\n<script\n src=\"https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.js\">\n</script>",
"js": "let calendar = null;\nlet resizeTimer = null;\nconst MONTH_HEIGHT = 620;\nconst MONTH_CONTENT_HEIGHT = 550;\nconst DAY_HEIGHT = 600;\nconst DAY_CONTENT_HEIGHT = 480;\nconst BUSINESS_START_MINUTES = 7 * 60;\nconst BUSINESS_END_MINUTES = 18 * 60;\nfunction getModel() {\n const model = appsmith.model || {};\n if (typeof model === \"string\") {\n try {\n return JSON.parse(model);\n } catch (error) {\n return {};\n }\n }\n return model;\n}\nfunction getEvents() {\n const model = getModel();\n return Array.isArray(model.events) ? model.events : [];\n}\nfunction getSeatName() {\n const model = getModel();\n return model.seatName || model.seatId || \"kein Platz ausgewählt\";\n}\nfunction updateSeatTitle() {\n const titleElement = document.getElementById(\"calendarSeatTitle\");\n if (!titleElement) {\n return;\n }\n titleElement.textContent = `Kalender für ${getSeatName()}`;\n}\nfunction updateSelectionText(text) {\n const selectionElement = document.getElementById(\"calendarSelection\");\n if (!selectionElement) {\n return;\n }\n selectionElement.textContent = text;\n}\nfunction updateCalendarViewSettings(viewType) {\n if (!calendar) {\n return;\n }\n if (viewType === \"timeGridDay\") {\n calendar.setOption(\"height\", DAY_HEIGHT);\n calendar.setOption(\"contentHeight\", DAY_CONTENT_HEIGHT);\n calendar.setOption(\"expandRows\", false);\n } else {\n calendar.setOption(\"height\", MONTH_HEIGHT);\n calendar.setOption(\"contentHeight\", MONTH_CONTENT_HEIGHT);\n calendar.setOption(\"expandRows\", true);\n }\n if (typeof calendar.updateSize === \"function\") {\n calendar.updateSize();\n }\n}\nfunction scheduleResize() {\n window.clearTimeout(resizeTimer);\n resizeTimer = window.setTimeout(() => {\n if (!calendar) {\n return;\n }\n calendar.updateSize();\n }, 250);\n}\nfunction isWeekend(date) {\n const day = date.getDay();\n return day === 0 || day === 6;\n}\nfunction isBusinessDay(date) {\n const day = date.getDay();\n return day >= 1 && day <= 5;\n}\nfunction getMinutesOfDay(date) {\n return date.getHours() * 60 + date.getMinutes();\n}\nfunction isBusinessTimeRange(start, end) {\n if (!start || !end) {\n return false;\n }\n if (!isBusinessDay(start)) {\n return false;\n }\n\n /*\n * Bei einer Tagesansicht müssen Start und Ende\n * am gleichen Werktag liegen.\n */\n if (start.toDateString() !== end.toDateString()) {\n return false;\n }\n const startMinutes = getMinutesOfDay(start);\n const endMinutes = getMinutesOfDay(end);\n return startMinutes >= BUSINESS_START_MINUTES && endMinutes <= BUSINESS_END_MINUTES;\n}\nfunction isBusinessDayRange(start, end) {\n if (!start || !end) {\n return false;\n }\n\n /*\n * FullCalendar liefert ein exklusives Ende.\n * Deshalb prüfen wir bis zum letzten\n * tatsächlich ausgewählten Tag.\n */\n const lastSelectedDay = new Date(end.getTime() - 1);\n let current = new Date(start);\n current.setHours(12, 0, 0, 0);\n lastSelectedDay.setHours(12, 0, 0, 0);\n while (current <= lastSelectedDay) {\n if (!isBusinessDay(current)) {\n return false;\n }\n current.setDate(current.getDate() + 1);\n }\n return true;\n}\nfunction showWeekendMessage() {\n updateSelectionText(\"Samstag und Sonntag sind nicht buchbar.\");\n}\nfunction showBusinessHoursMessage() {\n updateSelectionText(\"Reservierungen sind Montag bis Freitag von 07:00 bis 18:00 Uhr möglich.\");\n}\nfunction openDayView(date) {\n if (!calendar) {\n return;\n }\n if (isWeekend(date)) {\n showWeekendMessage();\n return;\n }\n calendar.changeView(\"timeGridDay\", date);\n}\nfunction createCalendar() {\n const calendarElement = document.getElementById(\"calendar\");\n if (!calendarElement) {\n return;\n }\n if (typeof FullCalendar === \"undefined\") {\n calendarElement.textContent = \"FullCalendar konnte nicht geladen werden.\";\n return;\n }\n if (calendar) {\n calendar.destroy();\n calendar = null;\n }\n calendar = new FullCalendar.Calendar(calendarElement, {\n initialView: \"dayGridMonth\",\n locale: \"de\",\n firstDay: 1,\n businessHours: {\n daysOfWeek: [1, 2, 3, 4, 5],\n startTime: \"07:00\",\n endTime: \"18:00\"\n },\n navLinks: true,\n /*\n * Klick auf eine Nav-Link-Tageszahl.\n */\n navLinkDayClick(date) {\n openDayView(date);\n },\n /*\n * In der Monatsansicht keine\n * Reaktion auf die komplette Tageszelle.\n */\n dateClick(info) {\n const clickedDate = info.date;\n if (info.view.type === \"dayGridMonth\") {\n return;\n }\n if (info.view.type === \"timeGridDay\") {\n if (isWeekend(clickedDate)) {\n showWeekendMessage();\n return;\n }\n updateSelectionText(`Ausgewählter Zeitpunkt: ${formatDateTime(clickedDate)}`);\n }\n },\n /*\n * Mehrfachauswahl per Ziehen.\n */\n selectable: true,\n selectMirror: true,\n unselectAuto: false,\n selectMinDistance: 5,\n /*\n * Eigene Prüfung für Monats-\n * und Tagesauswahl.\n */\n selectAllow(info) {\n /*\n * Monatsauswahl:\n * allDay ist true, wenn ganze Tage markiert werden.\n */\n if (info.allDay) {\n return isBusinessDayRange(info.start, info.end);\n }\n\n /*\n * Tagesauswahl:\n * Auswahl nur innerhalb der Business Hours.\n */\n return isBusinessTimeRange(info.start, info.end);\n },\n editable: false,\n eventStartEditable: false,\n eventDurationEditable: false,\n /*\n * Wird nach einer gültigen Auswahl\n * ausgeführt.\n */\n select(info) {\n const start = info.start;\n const end = info.end;\n if (info.view.type === \"dayGridMonth\") {\n updateSelectionText(`Ausgewählte Tage: ${formatDateTime(start)} bis ${formatDateTime(new Date(end.getTime() - 1))}`);\n appsmith.updateModel({\n calendarSelection: {\n type: \"days\",\n start: start.toISOString(),\n end: end.toISOString()\n }\n });\n return;\n }\n if (info.view.type === \"timeGridDay\") {\n if (!isBusinessTimeRange(start, end)) {\n calendar.unselect();\n showBusinessHoursMessage();\n return;\n }\n updateSelectionText(`Ausgewählt: ${formatDateTime(start)} bis ${formatDateTime(end)}`);\n appsmith.updateModel({\n calendarSelection: {\n type: \"time\",\n start: start.toISOString(),\n end: end.toISOString()\n }\n });\n try {\n appsmith.triggerEvent(\"timeSelected\");\n } catch (error) {\n /*\n * Optionales Appsmith-Event\n * ist nicht konfiguriert.\n */\n }\n }\n },\n /*\n * Auswahl löschen.\n */\n unselect() {\n updateSelectionText(\"Keine Auswahl\");\n },\n height: MONTH_HEIGHT,\n contentHeight: MONTH_CONTENT_HEIGHT,\n expandRows: true,\n headerToolbar: {\n left: \"prev,next today\",\n center: \"title\",\n right: \"dayGridMonth,timeGridDay\"\n },\n buttonText: {\n today: \"Heute\",\n month: \"Monat\",\n day: \"Tag\"\n },\n slotDuration: \"00:30:00\",\n snapDuration: \"00:30:00\",\n slotMinTime: \"07:00:00\",\n slotMaxTime: \"22:00:00\",\n scrollTime: \"08:00:00\",\n scrollTimeReset: false,\n allDaySlot: false,\n nowIndicator: true,\n events: getEvents(),\n datesSet(info) {\n updateCalendarViewSettings(info.view.type);\n },\n eventClick(info) {\n const event = info.event;\n const start = event.start ? formatDateTime(event.start) : \"\";\n const end = event.end ? formatDateTime(event.end) : \"\";\n const rangeText = end ? `${start} bis ${end}` : start;\n updateSelectionText(`${event.title || \"Buchung\"} ${rangeText}`);\n }\n });\n calendar.render();\n updateCalendarViewSettings(calendar.view.type);\n}\nfunction formatDateTime(date) {\n if (!date) {\n return \"\";\n }\n return new Intl.DateTimeFormat(\"de-DE\", {\n dateStyle: \"short\",\n timeStyle: \"short\"\n }).format(date);\n}\nfunction refreshCalendarEvents() {\n if (!calendar) {\n createCalendar();\n return;\n }\n calendar.removeAllEvents();\n calendar.addEventSource(getEvents());\n calendar.updateSize();\n}\nfunction registerResizeHandling() {\n window.addEventListener(\"resize\", scheduleResize);\n}\nappsmith.onReady(() => {\n updateSeatTitle();\n createCalendar();\n registerResizeHandling();\n appsmith.onModelChange(() => {\n updateSeatTitle();\n refreshCalendarEvents();\n });\n});"
},
"theme": "{{appsmith.theme}}",
"topRow": 0,
"type": "CUSTOM_WIDGET",
"uncompiledSrcDoc": {
"css": "html,\nbody {\n\tmargin: 0;\n\tpadding: 0;\n\twidth: 100%;\n\tmin-height: 100%;\n\tfont-family: Arial, sans-serif;\n\toverflow: hidden;\n}\n\n/* Äußerer Custom-Widget-Bereich */\n\n#calendarWrapper {\n\twidth: 100%;\n\tmin-height: 0;\n\tbox-sizing: border-box;\n\tpadding: 10px;\n\tbackground: #ffffff;\n\toverflow: visible;\n}\n\n/* Kopfbereich */\n\n#calendarHeader {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-bottom: 10px;\n\tpadding: 8px 10px;\n\tborder: 1px solid #d1d5db;\n\tborder-radius: 6px;\n\tbackground: #f9fafb;\n}\n\n#calendarSeatTitle {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tcolor: #374151;\n}\n\n/* Kalender */\n\n#calendar {\n\twidth: 100%;\n\tmin-height: 0;\n\tbox-sizing: border-box;\n}\n\n/* Auswahltext */\n\n#calendarSelection {\n\tmargin-top: 10px;\n\tpadding: 8px 10px;\n\tborder: 1px solid #d1d5db;\n\tborder-radius: 5px;\n\tbackground: #f9fafb;\n\tcolor: #374151;\n\tfont-size: 13px;\n}\n\n/* FullCalendar-Grundformatierung */\n\n.fc {\n\twidth: 100%;\n\tfont-size: 13px;\n}\n\n.fc .fc-button {\n\tbackground: #374151;\n\tborder-color: #374151;\n}\n\n.fc .fc-button:hover,\n.fc .fc-button-active {\n\tbackground: #111827;\n\tborder-color: #111827;\n}\n\n.fc .fc-daygrid-day.fc-day-today,\n.fc .fc-timegrid-col.fc-day-today {\n\tbackground: #eff6ff;\n}\n\n.fc .fc-event {\n\tcursor: pointer;\n}\n\n/* Wochenenden grau */\n\n#calendar td.fc-day-sat,\n#calendar td.fc-day-sun,\n#calendar .fc-timegrid-col.fc-day-sat,\n#calendar .fc-timegrid-col.fc-day-sun {\n\tbackground-color: #eeeeee !important;\n}\n\n/* Wochenend-Kopfzellen */\n\n#calendar .fc-col-header-cell.fc-day-sat,\n#calendar .fc-col-header-cell.fc-day-sun {\n\tbackground-color: #d6d6d6 !important;\n}\n\n/*\n* Monatsansicht:\n* kein Scrollen innerhalb des Monats.\n*/\n#calendar .fc-daygrid .fc-scroller {\n\theight: auto !important;\n\tmax-height: none !important;\n\toverflow-y: visible !important;\n\toverflow-x: visible !important;\n}\n\n/*\n* Tagesansicht:\n* nur der Zeitbereich scrollt.\n*/\n#calendar .fc-timegrid .fc-scroller {\n\toverflow-y: auto !important;\n\toverflow-x: hidden !important;\n\toverscroll-behavior: contain;\n}\n\n/*\n/*\n * TimeGrid-Grundstruktur\n */\n/*\n * TimeGrid-Grundstruktur\n */\n#calendar .fc-timegrid {\n min-height: 0 !important;\n}\n\n#calendar .fc-timegrid .fc-view-harness {\n min-height: 0 !important;\n}\n\n/*\n * Tabellenbreite\n */\n#calendar .fc-scrollgrid {\n width: 100%;\n max-width: 100%;\n}\n\n/*\n * Größere 30-Minuten-Zeilen\n */\n#calendar .fc-timegrid-slot {\n height: 30px !important;\n min-height: 30px !important;\n}\n\n#calendar .fc-timegrid-slot-label {\n height: 30px !important;\n min-height: 30px !important;\n}\n\n/*\n * Tagesansicht:\n * Keine feste CSS-Höhe setzen.\n * FullCalendar übernimmt 1080 px aus contentHeight.\n */\n#calendar .fc-timegrid .fc-scroller {\n overflow-y: auto !important;\n overflow-x: hidden !important;\n overscroll-behavior: contain;\n}\n\n",
"html": "<div id=\"calendarWrapper\">\n <div id=\"calendarHeader\">\n <div id=\"calendarSeatTitle\">\n Kalender\n </div>\n </div>\n\n <div id=\"calendar\"></div>\n\n <div id=\"calendarSelection\">\n Keine Auswahl\n </div>\n</div>\n\n<link\n rel=\"stylesheet\"\n href=\"https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.css\"\n>\n\n<script\n src=\"https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.js\">\n</script>",
"js": "let calendar = null;\nlet resizeTimer = null;\n\nconst MONTH_HEIGHT = 620;\nconst MONTH_CONTENT_HEIGHT = 550;\n\nconst DAY_HEIGHT = 600;\nconst DAY_CONTENT_HEIGHT = 480;\n\nconst BUSINESS_START_MINUTES = 7 * 60;\nconst BUSINESS_END_MINUTES = 18 * 60;\n\nfunction getModel() {\n const model = appsmith.model || {};\n\n if (typeof model === \"string\") {\n try {\n return JSON.parse(model);\n } catch (error) {\n return {};\n }\n }\n\n return model;\n}\n\nfunction getEvents() {\n const model = getModel();\n\n return Array.isArray(model.events)\n ? model.events\n : [];\n}\n\nfunction getSeatName() {\n const model = getModel();\n\n return model.seatName ||\n model.seatId ||\n \"kein Platz ausgewählt\";\n}\n\nfunction updateSeatTitle() {\n const titleElement =\n document.getElementById(\"calendarSeatTitle\");\n\n if (!titleElement) {\n return;\n }\n\n titleElement.textContent =\n `Kalender für ${getSeatName()}`;\n}\n\nfunction updateSelectionText(text) {\n const selectionElement =\n document.getElementById(\"calendarSelection\");\n\n if (!selectionElement) {\n return;\n }\n\n selectionElement.textContent = text;\n}\n\nfunction updateCalendarViewSettings(viewType) {\n if (!calendar) {\n return;\n }\n\n if (viewType === \"timeGridDay\") {\n calendar.setOption(\n \"height\",\n DAY_HEIGHT\n );\n\n calendar.setOption(\n \"contentHeight\",\n DAY_CONTENT_HEIGHT\n );\n\n calendar.setOption(\n \"expandRows\",\n false\n );\n } else {\n calendar.setOption(\n \"height\",\n MONTH_HEIGHT\n );\n\n calendar.setOption(\n \"contentHeight\",\n MONTH_CONTENT_HEIGHT\n );\n\n calendar.setOption(\n \"expandRows\",\n true\n );\n }\n\n if (\n typeof calendar.updateSize === \"function\"\n ) {\n calendar.updateSize();\n }\n}\n\nfunction scheduleResize() {\n window.clearTimeout(\n resizeTimer\n );\n\n resizeTimer = window.setTimeout(() => {\n if (!calendar) {\n return;\n }\n\n calendar.updateSize();\n }, 250);\n}\n\nfunction isWeekend(date) {\n const day =\n date.getDay();\n\n return day === 0 || day === 6;\n}\n\nfunction isBusinessDay(date) {\n const day =\n date.getDay();\n\n return day >= 1 && day <= 5;\n}\n\nfunction getMinutesOfDay(date) {\n return (\n date.getHours() * 60 +\n date.getMinutes()\n );\n}\n\nfunction isBusinessTimeRange(start, end) {\n if (!start || !end) {\n return false;\n }\n\n if (!isBusinessDay(start)) {\n return false;\n }\n\n /*\n * Bei einer Tagesansicht müssen Start und Ende\n * am gleichen Werktag liegen.\n */\n if (\n start.toDateString() !==\n end.toDateString()\n ) {\n return false;\n }\n\n const startMinutes =\n getMinutesOfDay(start);\n\n const endMinutes =\n getMinutesOfDay(end);\n\n return (\n startMinutes >=\n BUSINESS_START_MINUTES &&\n endMinutes <=\n BUSINESS_END_MINUTES\n );\n}\n\nfunction isBusinessDayRange(start, end) {\n if (!start || !end) {\n return false;\n }\n\n /*\n * FullCalendar liefert ein exklusives Ende.\n * Deshalb prüfen wir bis zum letzten\n * tatsächlich ausgewählten Tag.\n */\n const lastSelectedDay =\n new Date(end.getTime() - 1);\n\n let current =\n new Date(start);\n\n current.setHours(\n 12,\n 0,\n 0,\n 0\n );\n\n lastSelectedDay.setHours(\n 12,\n 0,\n 0,\n 0\n );\n\n while (\n current <= lastSelectedDay\n ) {\n if (!isBusinessDay(current)) {\n return false;\n }\n\n current.setDate(\n current.getDate() + 1\n );\n }\n\n return true;\n}\n\nfunction showWeekendMessage() {\n updateSelectionText(\n \"Samstag und Sonntag sind nicht buchbar.\"\n );\n}\n\nfunction showBusinessHoursMessage() {\n updateSelectionText(\n \"Reservierungen sind Montag bis Freitag von 07:00 bis 18:00 Uhr möglich.\"\n );\n}\n\nfunction openDayView(date) {\n if (!calendar) {\n return;\n }\n\n if (isWeekend(date)) {\n showWeekendMessage();\n return;\n }\n\n calendar.changeView(\n \"timeGridDay\",\n date\n );\n}\n\nfunction createCalendar() {\n const calendarElement =\n document.getElementById(\"calendar\");\n\n if (!calendarElement) {\n return;\n }\n\n if (\n typeof FullCalendar ===\n \"undefined\"\n ) {\n calendarElement.textContent =\n \"FullCalendar konnte nicht geladen werden.\";\n return;\n }\n\n if (calendar) {\n calendar.destroy();\n calendar = null;\n }\n\n calendar =\n new FullCalendar.Calendar(\n calendarElement,\n {\n initialView:\n \"dayGridMonth\",\n\n locale: \"de\",\n firstDay: 1,\n\n businessHours: {\n daysOfWeek: [\n 1,\n 2,\n 3,\n 4,\n 5\n ],\n startTime: \"07:00\",\n endTime: \"18:00\"\n },\n\n navLinks: true,\n\n /*\n * Klick auf eine Nav-Link-Tageszahl.\n */\n navLinkDayClick(date) {\n openDayView(date);\n },\n\n /*\n * In der Monatsansicht keine\n * Reaktion auf die komplette Tageszelle.\n */\n dateClick(info) {\n const clickedDate =\n info.date;\n\n if (\n info.view.type ===\n \"dayGridMonth\"\n ) {\n return;\n }\n\n if (\n info.view.type ===\n \"timeGridDay\"\n ) {\n if (\n isWeekend(clickedDate)\n ) {\n showWeekendMessage();\n return;\n }\n\n updateSelectionText(\n `Ausgewählter Zeitpunkt: ${\n formatDateTime(\n clickedDate\n )\n }`\n );\n }\n },\n\n /*\n * Mehrfachauswahl per Ziehen.\n */\n selectable: true,\n selectMirror: true,\n unselectAuto: false,\n selectMinDistance: 5,\n\n /*\n * Eigene Prüfung für Monats-\n * und Tagesauswahl.\n */\n selectAllow(info) {\n /*\n * Monatsauswahl:\n * allDay ist true, wenn ganze Tage markiert werden.\n */\n if (info.allDay) {\n return isBusinessDayRange(\n info.start,\n info.end\n );\n }\n\n /*\n * Tagesauswahl:\n * Auswahl nur innerhalb der Business Hours.\n */\n return isBusinessTimeRange(\n info.start,\n info.end\n );\n},\n\n editable: false,\n eventStartEditable: false,\n eventDurationEditable: false,\n\n /*\n * Wird nach einer gültigen Auswahl\n * ausgeführt.\n */\n select(info) {\n const start =\n info.start;\n\n const end =\n info.end;\n\n if (\n info.view.type ===\n \"dayGridMonth\"\n ) {\n updateSelectionText(\n `Ausgewählte Tage: ${\n formatDateTime(start)\n } bis ${\n formatDateTime(\n new Date(\n end.getTime() - 1\n )\n )\n }`\n );\n\n appsmith.updateModel({\n calendarSelection: {\n type: \"days\",\n start: start.toISOString(),\n end: end.toISOString()\n }\n });\n\n return;\n }\n\n if (\n info.view.type ===\n \"timeGridDay\"\n ) {\n if (\n !isBusinessTimeRange(\n start,\n end\n )\n ) {\n calendar.unselect();\n showBusinessHoursMessage();\n return;\n }\n\n updateSelectionText(\n `Ausgewählt: ${\n formatDateTime(start)\n } bis ${\n formatDateTime(end)\n }`\n );\n\n appsmith.updateModel({\n calendarSelection: {\n type: \"time\",\n start: start.toISOString(),\n end: end.toISOString()\n }\n });\n\n try {\n appsmith.triggerEvent(\n \"timeSelected\"\n );\n } catch (error) {\n /*\n * Optionales Appsmith-Event\n * ist nicht konfiguriert.\n */\n }\n }\n },\n\n /*\n * Auswahl löschen.\n */\n unselect() {\n updateSelectionText(\n \"Keine Auswahl\"\n );\n },\n\n height:\n MONTH_HEIGHT,\n\n contentHeight:\n MONTH_CONTENT_HEIGHT,\n\n expandRows:\n true,\n\n headerToolbar: {\n left:\n \"prev,next today\",\n center:\n \"title\",\n right:\n \"dayGridMonth,timeGridDay\"\n },\n\n buttonText: {\n today: \"Heute\",\n month: \"Monat\",\n day: \"Tag\"\n },\n\n slotDuration:\n \"00:30:00\",\n\n snapDuration:\n \"00:30:00\",\n\n slotMinTime:\n \"07:00:00\",\n\n slotMaxTime:\n \"22:00:00\",\n\n scrollTime:\n \"08:00:00\",\n\n scrollTimeReset:\n false,\n\n allDaySlot:\n false,\n\n nowIndicator:\n true,\n\n events:\n getEvents(),\n\n datesSet(info) {\n updateCalendarViewSettings(\n info.view.type\n );\n },\n\n eventClick(info) {\n const event =\n info.event;\n\n const start =\n event.start\n ? formatDateTime(\n event.start\n )\n : \"\";\n\n const end =\n event.end\n ? formatDateTime(\n event.end\n )\n : \"\";\n\n const rangeText =\n end\n ? `${start} bis ${end}`\n : start;\n\n updateSelectionText(\n `${\n event.title ||\n \"Buchung\"\n } ${rangeText}`\n );\n }\n }\n );\n\n calendar.render();\n\n updateCalendarViewSettings(\n calendar.view.type\n );\n}\n\nfunction formatDateTime(date) {\n if (!date) {\n return \"\";\n }\n\n return new Intl.DateTimeFormat(\n \"de-DE\",\n {\n dateStyle: \"short\",\n timeStyle: \"short\"\n }\n ).format(date);\n}\n\nfunction refreshCalendarEvents() {\n if (!calendar) {\n createCalendar();\n return;\n }\n\n calendar.removeAllEvents();\n\n calendar.addEventSource(\n getEvents()\n );\n\n calendar.updateSize();\n}\n\nfunction registerResizeHandling() {\n window.addEventListener(\n \"resize\",\n scheduleResize\n );\n}\n\nappsmith.onReady(() => {\n updateSeatTitle();\n\n createCalendar();\n\n registerResizeHandling();\n\n appsmith.onModelChange(() => {\n updateSeatTitle();\n\n refreshCalendarEvents();\n });\n});"
},
"version": 1,
"widgetId": "ad7fob2vs9",
"widgetName": "Custom5"
}