Neueste Version

This commit is contained in:
Admin committed 2026-08-17 13:03:04 +00:00
1 parent 01d8e6d9c2
commit 4e852a81d7
1 file changed
+4 -4
@@ -54,17 +54,17 @@
"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}",
"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/* Auswahlfarbe von FullCalendar */\n#calendar .fc .fc-highlight {\n background-color: rgba(11, 116, 222, 0.12) !important;\n}\n\n/* Aktueller Tag */\n#calendar .fc-daygrid-day.fc-day-today {\n box-shadow: inset 0 0 0 1px #dc2626 !important;\n}\n\n#calendar .fc-daygrid-day.fc-day-today .fc-daygrid-day-frame {\n box-shadow: inset 0 0 0 1px #dc2626 !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 Line truncated
"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;\nconst SLOT_MINUTES = 30;\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 element = document.getElementById(\"calendarSeatTitle\");\n if (element) {\n element.textContent = `Kalender für ${getSeatName()}`;\n }\n}\nfunction updateSelectionText(text) {\n const element = document.getElementById(\"calendarSelection\");\n if (element) {\n element.textContent = text;\n }\n}\nfunction updateCalendarViewSettings(viewType) {\n if (!calendar) {\n return;\n }\n const isDayView = viewType === \"timeGridDay\";\n calendar.setOption(\"height\", isDayView ? DAY_HEIGHT : MONTH_HEIGHT);\n calendar.setOption(\"contentHeight\", isDayView ? DAY_CONTENT_HEIGHT : MONTH_CONTENT_HEIGHT);\n calendar.setOption(\"expandRows\", !isDayView);\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 calendar.updateSize();\n }\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 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 && endMinutes > startMinutes;\n}\nfunction isBusinessDayRange(start, end) {\n if (!start || !end) {\n return false;\n }\n const lastSelectedDay = new Date(end.getTime() - 1);\n const 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 isDayNumberClick(info) {\n const target = info.jsEvent && info.jsEvent.target;\n if (!target || typeof target.closest !== \"function\") {\n return false;\n }\n return Boolean(target.closest(\".fc-daygrid-day-number\"));\n}\nfunction getLocalDateKey(date) {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\nfunction clearDaySelectionHighlight() {\n if (!calendar || !calendar.el) {\n return;\n }\n const cells = calendar.el.querySelectorAll(\".fc-daygrid-day.fc-custom-selected-day\");\n cells.forEach(cell => {\n cell.classList.remove(\"fc-custom-selected-day\");\n });\n}\nfunction highlightSingleDay(date) {\n if (!calendar || !calendar.el || !date) {\n return;\n }\n clearDaySelectionHighlight();\n const dateKey = getLocalDateKey(date);\n const selector = `.fc-daygrid-day[data-date=\"${dateKey}\"]`;\n const cell = calendar.el.querySelector(selector);\n if (cell) {\n cell.classList.add(\"fc-custom-selected-day\");\n }\n}\nfunction roundToHalfHour(date) {\n const result = new Date(date);\n const minutes = result.getMinutes();\n result.setSeconds(0, 0);\n result.setMinutes(minutes < 30 ? 0 : 30);\n return result;\n}\nfunction saveCalendarSelection(type, start, end) {\n const selection = {\n type,\n start: start.toISOString(),\n end: end.toISOString()\n };\n appsmith.updateModel({\n calendarSelection: selection\n });\n if (type === \"day\") {\n updateSelectionText(`Ausgewählter Tag: ${formatDateTime(start)}`);\n } else if (type === \"days\") {\n uLine truncated
},
"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",
"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\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/* Auswahlfarbe von FullCalendar */\n#calendar .fc .fc-highlight {\n background-color: rgba(11, 116, 222, 0.12) !important;\n}\n\n/* Aktueller Tag */\n#calendar .fc-daygrid-day.fc-day-today {\n box-shadow: inset 0 0 0 1px #dc2626 !important;\n}\n\n#calendar .fc-daygrid-day.fc-day-today .fc-daygrid-day-frame {\n box-shadow: inset 0 0 0 1px #dc2626 !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 Line truncated
"js": "let calendar = null;\nlet resizeTimer = null;\n\nconst MONTH_HEIGHT = 620;\nconst MONTH_CONTENT_HEIGHT = 550;\nconst DAY_HEIGHT = 600;\nconst DAY_CONTENT_HEIGHT = 480;\n\nconst BUSINESS_START_MINUTES = 7 * 60;\nconst BUSINESS_END_MINUTES = 18 * 60;\nconst SLOT_MINUTES = 30;\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 return Array.isArray(model.events) ? model.events : [];\n}\n\nfunction getSeatName() {\n const model = getModel();\n return model.seatName || model.seatId || \"Kein Platz ausgewählt\";\n}\n\nfunction updateSeatTitle() {\n const element = document.getElementById(\"calendarSeatTitle\");\n if (element) {\n element.textContent = `Kalender für ${getSeatName()}`;\n }\n}\n\nfunction updateSelectionText(text) {\n const element = document.getElementById(\"calendarSelection\");\n if (element) {\n element.textContent = text;\n }\n}\n\nfunction updateCalendarViewSettings(viewType) {\n if (!calendar) {\n return;\n }\n\n const isDayView = viewType === \"timeGridDay\";\n\n calendar.setOption(\"height\", isDayView ? DAY_HEIGHT : MONTH_HEIGHT);\n calendar.setOption(\n \"contentHeight\",\n isDayView ? DAY_CONTENT_HEIGHT : MONTH_CONTENT_HEIGHT\n );\n calendar.setOption(\"expandRows\", !isDayView);\n\n if (typeof calendar.updateSize === \"function\") {\n calendar.updateSize();\n }\n}\n\nfunction scheduleResize() {\n window.clearTimeout(resizeTimer);\n\n resizeTimer = window.setTimeout(() => {\n if (calendar) {\n calendar.updateSize();\n }\n }, 250);\n}\n\nfunction isWeekend(date) {\n const day = date.getDay();\n return day === 0 || day === 6;\n}\n\nfunction isBusinessDay(date) {\n const day = date.getDay();\n return day >= 1 && day <= 5;\n}\n\nfunction getMinutesOfDay(date) {\n return date.getHours() * 60 + date.getMinutes();\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 if (start.toDateString() !== end.toDateString()) {\n return false;\n }\n\n const startMinutes = getMinutesOfDay(start);\n const endMinutes = getMinutesOfDay(end);\n\n return (\n startMinutes >= BUSINESS_START_MINUTES &&\n endMinutes <= BUSINESS_END_MINUTES &&\n endMinutes > startMinutes\n );\n}\n\nfunction isBusinessDayRange(start, end) {\n if (!start || !end) {\n return false;\n }\n\n const lastSelectedDay = new Date(end.getTime() - 1);\n const current = new Date(start);\n\n current.setHours(12, 0, 0, 0);\n lastSelectedDay.setHours(12, 0, 0, 0);\n\n while (current <= lastSelectedDay) {\n if (!isBusinessDay(current)) {\n return false;\n }\n\n current.setDate(current.getDate() + 1);\n }\n\n return true;\n}\n\nfunction showWeekendMessage() {\n updateSelectionText(\"Samstag und Sonntag sind nicht buchbar.\");\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(\"timeGridDay\", date);\n}\n\nfunction isDayNumberClick(info) {\n const target = info.jsEvent && info.jsEvent.target;\n\n if (!target || typeof target.closest !== \"function\") {\n return false;\n }\n\n return Boolean(target.closest(\".fc-daygrid-day-number\"));\n}\n\nfunction getLocalDateKey(date) {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n\n return `${year}-${month}-${day}`;\n}\n\nfunction clearDaySelectionHighlight() {\n if (!calendar || !calendar.el) {\n return;\n }\n\n const cells = calendar.el.querySelectorAll(\n \".fc-daygrid-day.fc-custom-selected-day\"\n );\n\n cells.forEach((cell) => {\n cell.classList.remove(\"fc-custom-selected-day\");\n });\n}\n\nfunction highlightSingleDay(date) {\n if (!calendar || !calendar.el || !date) {\n return;\n }\n\n clearDaySelectionHighlight();\n\n const dateKey = getLocalDateKey(date);\n const selector = `.fc-daygrid-day[data-date=\"${dateKey}\"]`;\n const cell = calendar.el.querySelector(selector);\n\n if (cell) {\n cell.classList.add(\"fc-custom-selected-day\");\n }\n}\n\nfunction roundToHalfHour(date) {\n const result = new Date(date);\n const minutes = result.getMinutes();\n\n result.setSeconds(0, 0);\n result.setMinutes(minutes < 30 ? 0 : 30);\n\n return result;\n}\n\nfunction saveCalendarSelection(type, start, end) {\n const selection = {\n type,\n start: start.toISOString(),\n end: end.toISOString()\n };\n\n appsmith.updateModel({\n calendarSelectioLine truncated
},
"version": 1,
"widgetId": "ad7fob2vs9",