From 0c529c3cb8208e1ca1a8eb2714de72c72c01bda4 Mon Sep 17 00:00:00 2001 From: Semir Date: Fri, 6 Jun 2025 10:43:25 +0200 Subject: [PATCH] feat: Calendar Layout and Function Update --- package.json | 1 + src/app/home/page.tsx | 39 +- src/components/calendar.tsx | 38 ++ src/components/custom-toolbar.css | 70 +- src/components/custom-toolbar.tsx | 249 ++++--- src/components/react-big-calendar.css | 904 ++++++++++++++++++++++++++ yarn.lock | 38 +- 7 files changed, 1172 insertions(+), 167 deletions(-) create mode 100644 src/components/calendar.tsx create mode 100644 src/components/react-big-calendar.css diff --git a/package.json b/package.json index b4c9bfb..1dbd9d4 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "next-themes": "^0.4.6", "react": "^19.1.0", "react-big-calendar": "^1.18.0", + "react-datepicker": "^8.4.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.2.0" }, diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 6149d47..0b7515a 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,38 +1,3 @@ -"use client"; +import Calendar from '@/components/calendar'; -import { Calendar, momentLocalizer } from 'react-big-calendar'; -import moment from 'moment'; -import 'react-big-calendar/lib/css/react-big-calendar.css'; -import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; -import CustomToolbar from '@/components/custom-toolbar'; - -moment.updateLocale('en', { - week: { - dow: 1, - doy: 4, - }, -}); - -const localizer = momentLocalizer(moment) - -const MyCalendar = (props) => ( -
- -
-) - -export default MyCalendar; +export default function home () {return } \ No newline at end of file diff --git a/src/components/calendar.tsx b/src/components/calendar.tsx new file mode 100644 index 0000000..defedab --- /dev/null +++ b/src/components/calendar.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { Calendar, momentLocalizer } from 'react-big-calendar'; +import moment from 'moment'; +import '@/components/react-big-calendar.css'; +import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; +import CustomToolbar from '@/components/custom-toolbar'; + +moment.updateLocale('en', { + week: { + dow: 1, + doy: 4, + }, +}); + +const localizer = momentLocalizer(moment) + +const MyCalendar = (props) => ( +
+ +
+) + +export default MyCalendar; diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css index 16e86ed..e16a8e4 100644 --- a/src/components/custom-toolbar.css +++ b/src/components/custom-toolbar.css @@ -13,8 +13,7 @@ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } -/* Style für den Bereich, in dem die Ansichten (Month, Week, etc.) gewechselt werden */ -.custom-toolbar .view-change .view-switcher { +/*.custom-toolbar .view-change .view-switcher { display: flex; gap: 8px; justify-content: center; @@ -24,7 +23,7 @@ padding: 8px 16px; background-color: #c1830d; /*border: 1px solid #ccc;*/ - border-radius: 11px; +/* border-radius: 11px; font-size: 12px; cursor: pointer; transition: background-color 0.2s, border-color 0.2s; @@ -42,7 +41,7 @@ background-color: #d0d0d0; border-color: #aaa; cursor: default; -} +}*/ /* Anzeige des aktuellen Datums (Monat und Jahr) */ .custom-toolbar .current-date { @@ -50,7 +49,7 @@ font-size: 12px; text-align: center; color: #ffffff; - margin: 4px 0; + /*margin: 4px 0;*/ background-color: #717171; width: 178px; height: 37px; @@ -110,30 +109,53 @@ } .right-section { - background-color: #717171; - width: 393px; - height: 37px; - border-radius: 11px; + background-color: #717171; + width: 393px; + height: 48px; + border-radius: 11px; + justify-items: center; + align-items: center; } .custom-toolbar .navigation-controls .handleWeek button { - background-color: #717171; - height: 30px; - width: 30px; - margin-bottom: 3.5px; + background-color: #717171; + height: 30px; + width: 30px; + margin-bottom: 3.5px; } -.custom-toolbar .navigation-controls .today button { - background-color: #c6c6c6; - height: 30px; - width: 100px; - color: #000000; - margin-top: 3.5px; -} +/*.custom-toolbar .navigation-controls .today button { + background-color: #c6c6c6; + height: 30px; + width: 100px; + color: #000000; + margin-top: 3.5px; +}*/ .view-change { - background-color: #717171; - height: 37px; - width: 290px; - border-radius: 11px; + background-color: #717171; + height: 48px; + width: 323px; + border-radius: 11px; + justify-items: center; +} + +.right-section .datepicker-box { + color: #000000; + background-color: #c6c6c6; + height: 36px; + width: 85px; + border-radius: 11px; + font-size: 12px; + align-self: center; +} + +.datepicker { + text-align: center; + width: 85px; + height: 30px; +} + +.datepicker-box { + z-index: 9999; } diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx index 10feafa..5f9a2cf 100644 --- a/src/components/custom-toolbar.tsx +++ b/src/components/custom-toolbar.tsx @@ -1,214 +1,253 @@ import React, { useState, useEffect } from 'react'; import { format } from 'date-fns'; import './custom-toolbar.css'; +import { Button } from '@/components/custom-ui/button'; +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; interface CustomToolbarProps { - // Das aktuell angezeigte Datum (wird z. B. von der Calendar-Komponente geliefert) + //Aktuell angezeigtes Datum date: Date; - // Aktuelle Ansicht: "month", "week", "day" oder "agenda" + //Aktuelle Ansicht view: 'month' | 'week' | 'day' | 'agenda'; - /** - * onNavigate ermöglicht das Wechseln des angezeigten Datums. - * Action kann bspw. 'TODAY' oder 'SET_DATE' sein; newDate wird übergeben, wenn benötigt. - */ + onNavigate: (action: string, newDate?: Date) => void; - // onView wechselt die Ansicht + //Ansichtwechsel onView: (newView: 'month' | 'week' | 'day' | 'agenda') => void; } const CustomToolbar: React.FC = ({ date, view, onNavigate, onView }) => { - // Hilfsfunktion, um die ISO-Wochennummer eines Datums zu ermitteln + //ISO-Wochennummer eines Datums ermitteln const getISOWeek = (date: Date): number => { const tmp = new Date(date.getTime()); - // Verschiebe das Datum so, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) + //Datum so verschieben, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); const yearStart = new Date(tmp.getFullYear(), 0, 1); const weekNo = Math.ceil((((tmp.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); return weekNo; }; - // Neue Funktion: Ermittelt das ISO-Wochenjahr eines Datums. - // Das ISO-Wochenjahr entspricht dem Jahr des Donnerstags in dieser Woche. + //ISO-Wochenjahr eines Datums ermitteln const getISOWeekYear = (date: Date): number => { const tmp = new Date(date.getTime()); tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); return tmp.getFullYear(); }; - // Ermittelt die Anzahl der ISO-Wochen im Jahr + //Ermittlung der Anzahl der Wochen im Jahr const getISOWeeksInYear = (year: number): number => { const d = new Date(year, 11, 31); const week = getISOWeek(d); return week === 1 ? getISOWeek(new Date(year, 11, 24)) : week; }; - /* - Berechnet den Montag der gewünschten ISO-Woche eines Jahres. - Wir ermitteln zunächst den ersten Montag der ersten ISO-Woche und addieren dann (week - 1) * 7 Tage. - */ const getDateOfISOWeek = (week: number, year: number): Date => { const jan1 = new Date(year, 0, 1); const dayOfWeek = jan1.getDay(); const isoDayOfWeek = dayOfWeek === 0 ? 7 : dayOfWeek; let firstMonday: Date; if (isoDayOfWeek <= 4) { - // Jan 1 gehört zur ersten ISO-Woche – bestimme den Montag dieser Woche + //1. Januar gehört zur ersten ISO-Woche (Montag dieser Woche bestimmen) firstMonday = new Date(year, 0, 1 - isoDayOfWeek + 1); } else { - // Andernfalls liegt der erste Montag in der darauffolgenden Woche + //Ansonsten liegt der erste Montag in der darauffolgenden Woche firstMonday = new Date(year, 0, 1 + (8 - isoDayOfWeek)); } firstMonday.setDate(firstMonday.getDate() + (week - 1) * 7); return firstMonday; }; - // Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) + //Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) const [selectedWeek, setSelectedWeek] = useState(getISOWeek(date)); const [selectedYear, setSelectedYear] = useState(getISOWeekYear(date)); - // Aktualisiere die Auswahl, wenn sich die Prop "date" ändert + //Auswahl aktualisieren, wenn sich die Prop "date" ändert useEffect(() => { setSelectedWeek(getISOWeek(date)); setSelectedYear(getISOWeekYear(date)); }, [date]); - // Für die Dropdown-Liste der Wochen: Liste von 1 bis totalWeeks + //Dropdown-Liste der Wochen const totalWeeks = getISOWeeksInYear(selectedYear); const weekOptions = Array.from({ length: totalWeeks }, (_, i) => i + 1); - // Beispielhafte Jahresliste: aktuelles ISO-Wochenjahr ± 10 + //Jahresliste const yearOptions = Array.from({ length: 21 }, (_, i) => selectedYear - 10 + i); - // Berechne den Start (Montag) und das Ende (Sonntag) der aktuell angezeigten Woche + //Start (Montag) und Ende (Sonntag) der aktuell angezeigten Woche berechnen const weekStartDate = getDateOfISOWeek(selectedWeek, selectedYear); const weekEndDate = new Date(weekStartDate); weekEndDate.setDate(weekStartDate.getDate() + 6); - // Ermittele Monat und Jahr von Start- und Enddatum (normales Kalenderjahr) + //Monat und Jahr von Start- und Enddatum ermitteln const monthStart = format(weekStartDate, 'MMMM'); const monthEnd = format(weekEndDate, 'MMMM'); const yearAtStart = format(weekStartDate, 'yyyy'); const yearAtEnd = format(weekEndDate, 'yyyy'); - // Erstelle das Label: - // 1. Falls der Wochenanfang und das Wochenende in unterschiedlichen Jahren liegen, - // wird z. B. "Dezember 2025 - Januar 2026" angezeigt. - // 2. Liegen beide im gleichen Jahr, wird unterschieden zwischen gleichem Monat und unterschiedlichem Monat. - let dateLabel: string; - if (yearAtStart !== yearAtEnd) { - dateLabel = `${monthStart} ${yearAtStart} - ${monthEnd} ${yearAtEnd}`; - } else if (monthStart !== monthEnd) { - dateLabel = `${monthStart} - ${monthEnd} ${yearAtStart}`; - } else { - dateLabel = `${monthStart} ${yearAtStart}`; - } - - // Handler zum Wechseln der Ansicht + //Ansichtwechsel const handleViewChange = (newView: 'month' | 'week' | 'day' | 'agenda') => { onView(newView); }; - // "Today"-Button: setzt das Datum auf das heutige Datum (unter Verwendung des ISO-Wochenjahrs) + //Today-Button aktualisiert das Datum im DatePicker auf das heutige const handleToday = () => { const today = new Date(); + setSelectedDate(today); setSelectedWeek(getISOWeek(today)); setSelectedYear(getISOWeekYear(today)); onNavigate('TODAY', today); }; - // Wechselt zur vorherigen Woche. Bei Woche < 1, wird ins Vorjahr gewechselt. - const handlePrevWeek = () => { - let newWeek = selectedWeek - 1; - let newYear = selectedYear; - if (newWeek < 1) { - newYear = selectedYear - 1; - newWeek = getISOWeeksInYear(newYear); + //Pfeiltaste nach Vorne + const handleNext = () => { + let newDate: Date; + if (view === 'day' || view === 'agenda') { + newDate = new Date(date); + newDate.setDate(newDate.getDate() + 1); + } else if (view === 'week') { + let newWeek = selectedWeek + 1; + let newYear = selectedYear; + if (newWeek > getISOWeeksInYear(selectedYear)) { + newYear = selectedYear + 1; + newWeek = 1; + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + newDate = getDateOfISOWeek(newWeek, newYear); + } else if (view === 'month') { + newDate = new Date(date.getFullYear(), date.getMonth() + 1, 1); + } else { + newDate = new Date(date); } - setSelectedWeek(newWeek); - setSelectedYear(newYear); - const newDate = getDateOfISOWeek(newWeek, newYear); + //Datum im DatePicker aktualisieren + setSelectedDate(newDate); onNavigate('SET_DATE', newDate); }; - // Wechselt zur nächsten Woche. Überschreitet die Woche die maximale Zahl, wechselt ins nächste Jahr. - const handleNextWeek = () => { - let newWeek = selectedWeek + 1; - let newYear = selectedYear; - if (newWeek > getISOWeeksInYear(selectedYear)) { - newYear = selectedYear + 1; - newWeek = 1; + //Pfeiltaste nach Hinten + const handlePrev = () => { + let newDate: Date; + if (view === 'day' || view === 'agenda') { + newDate = new Date(date); + newDate.setDate(newDate.getDate() - 1); + } else if (view === 'week') { + let newWeek = selectedWeek - 1; + let newYear = selectedYear; + if (newWeek < 1) { + newYear = selectedYear - 1; + newWeek = getISOWeeksInYear(newYear); + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + newDate = getDateOfISOWeek(newWeek, newYear); + } else if (view === 'month') { + newDate = new Date(date.getFullYear(), date.getMonth() - 1, 1); + } else { + newDate = new Date(date); } - setSelectedWeek(newWeek); - setSelectedYear(newYear); - const newDate = getDateOfISOWeek(newWeek, newYear); + //Datum im DatePicker aktualisieren + setSelectedDate(newDate); onNavigate('SET_DATE', newDate); }; - // Handler, wenn der Nutzer über das Dropdown eine Woche auswählt - const handleWeekChange = (event: React.ChangeEvent) => { - const newWeek = parseInt(event.target.value, 10); - setSelectedWeek(newWeek); - const newDate = getDateOfISOWeek(newWeek, selectedYear); - onNavigate('SET_DATE', newDate); + const [selectedDate, setSelectedDate] = useState(new Date()); + + const handleDateChange = (date: Date | null) => { + setSelectedDate(date); + if (date) { + if (view === 'week') { + const newWeek = getISOWeek(date); + const newYear = getISOWeekYear(date); + setSelectedWeek(newWeek); + setSelectedYear(newYear); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + } else if (view === 'day') { + onNavigate('SET_DATE', date); + } else if (view === 'month') { + const newDate = new Date(date.getFullYear(), date.getMonth(), 1); + onNavigate('SET_DATE', newDate); + } else if (view === 'agenda') { + onNavigate('SET_DATE', date); + } + } }; - // Handler, wenn der Nutzer über das Dropdown ein Jahr auswählt - const handleYearChange = (event: React.ChangeEvent) => { - const newYear = parseInt(event.target.value, 10); - setSelectedYear(newYear); - const totalWeeksInNewYear = getISOWeeksInYear(newYear); - const newWeek = Math.min(selectedWeek, totalWeeksInNewYear); - setSelectedWeek(newWeek); - const newDate = getDateOfISOWeek(newWeek, newYear); - onNavigate('SET_DATE', newDate); - }; return (
- {/* Anzeige des Datums-Labels */} -
- {dateLabel} -
- - {/* Ansicht wechseln */}
- - - - + + + +
- {/* Navigationsbuttons */}
- - + +
- +
- {/* DropDowns für Woche und Jahr */} -
- - +
+
diff --git a/src/components/react-big-calendar.css b/src/components/react-big-calendar.css new file mode 100644 index 0000000..305dce0 --- /dev/null +++ b/src/components/react-big-calendar.css @@ -0,0 +1,904 @@ +@charset "UTF-8"; +.rbc-btn { + color: inherit; + font: inherit; + margin: 0; +} + +button.rbc-btn { + overflow: visible; + text-transform: none; + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + cursor: pointer; +} + +button[disabled].rbc-btn { + cursor: not-allowed; +} + +button.rbc-input::-moz-focus-inner { + border: 0; + padding: 0; +} + +.rbc-calendar { + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 100%; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.rbc-m-b-negative-3 { + margin-bottom: -3px; +} + +.rbc-h-full { + height: 100%; +} + +.rbc-calendar *, +.rbc-calendar *:before, +.rbc-calendar *:after { + -webkit-box-sizing: inherit; + box-sizing: inherit; +} + +.rbc-abs-full, .rbc-row-bg { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.rbc-ellipsis, .rbc-show-more, .rbc-row-segment .rbc-event-content, .rbc-event-label { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.rbc-rtl { + direction: rtl; +} + +.rbc-off-range { + color: #999999; +} + +.rbc-off-range-bg { + background: #e6e6e6; +} + +.rbc-header { + overflow: hidden; + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0 3px; + text-align: center; + vertical-align: middle; + font-weight: bold; + font-size: 90%; + min-height: 0; + border-bottom: 1px solid #ddd; +} +.rbc-header + .rbc-header { + border-left: 1px solid #c6c6c6; /*#ddd*/ +} +.rbc-rtl .rbc-header + .rbc-header { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-header > a, .rbc-header > a:active, .rbc-header > a:visited { + color: inherit; + text-decoration: none; +} + +.rbc-button-link { + color: inherit; + background: none; + margin: 0; + padding: 0; + border: none; + cursor: pointer; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.rbc-row-content { + position: relative; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; + z-index: 4; +} + +.rbc-row-content-scrollable { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + height: 100%; +} +.rbc-row-content-scrollable .rbc-row-content-scroll-container { + height: 100%; + overflow-y: scroll; + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + /* Hide scrollbar for Chrome, Safari and Opera */ +} +.rbc-row-content-scrollable .rbc-row-content-scroll-container::-webkit-scrollbar { + display: none; +} + +.rbc-today { + background-color: #5770ff; /*#eaf6ff*/ +} +/*Own changes 10*/ +.rbc-allday-cell .rbc-row-bg .rbc-day-bg.rbc-today { + background-color: transparent !important; + /*border: none !important;*/ +} +/*Own changes 10*/ + +/*Own changes 11*/ +.rbc-time-header-cell .rbc-header:first-child.rbc-today { + border-top-left-radius: 11px !important; +} + +.rbc-time-header-cell .rbc-header:last-child.rbc-today { + border-top-right-radius: 11px !important; +} +/*Own changes 11*/ + +.rbc-toolbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + margin-bottom: 10px; + font-size: 16px; +} +.rbc-toolbar .rbc-toolbar-label { + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 0 10px; + text-align: center; + + /*Own changes 01*/ + background-color: #717171; + color: #ffffff; + /*Own changes 01*/ +} +.rbc-toolbar button { + color: #373a3c; + display: inline-block; + margin: 0; + text-align: center; + vertical-align: middle; + background: none; + background-image: none; + border: 1px solid #ccc; + padding: 0.375rem 1rem; + border-radius: 4px; + line-height: normal; + white-space: nowrap; +} +.rbc-toolbar button:active, .rbc-toolbar button.rbc-active { + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + background-color: #e6e6e6; + border-color: #adadad; +} +.rbc-toolbar button:active:hover, .rbc-toolbar button:active:focus, .rbc-toolbar button.rbc-active:hover, .rbc-toolbar button.rbc-active:focus { + color: #373a3c; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.rbc-toolbar button:focus { + color: #373a3c; + background-color: #e6e6e6; + border-color: #adadad; +} +.rbc-toolbar button:hover { + color: #373a3c; + cursor: pointer; + background-color: #e6e6e6; + border-color: #adadad; +} + +.rbc-btn-group { + display: inline-block; + white-space: nowrap; +} +.rbc-btn-group > button:first-child:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + + /*Own changes 02*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 02*/ +} +.rbc-btn-group > button:last-child:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + + /*Own changes 03*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 03*/ +} +.rbc-rtl .rbc-btn-group > button:first-child:not(:last-child) { + border-radius: 4px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.rbc-rtl .rbc-btn-group > button:last-child:not(:first-child) { + border-radius: 4px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-btn-group > button:not(:first-child):not(:last-child) { + border-radius: 0; + + /*Own changes 04*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 04*/ +} +.rbc-btn-group button + button { + margin-left: -1px; +} +.rbc-rtl .rbc-btn-group button + button { + margin-left: 0; + margin-right: -1px; +} +.rbc-btn-group + .rbc-btn-group, .rbc-btn-group + button { + margin-left: 10px; +} + +@media (max-width: 767px) { + .rbc-toolbar { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + } +} +.rbc-event, .rbc-day-slot .rbc-background-event { + border: none; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: none; + box-shadow: none; + margin: 0; + padding: 2px 5px; + background-color: #3174ad; + border-radius: 5px; + color: #fff; + cursor: pointer; + width: 100%; + text-align: left; +} +.rbc-slot-selecting .rbc-event, .rbc-slot-selecting .rbc-day-slot .rbc-background-event, .rbc-day-slot .rbc-slot-selecting .rbc-background-event { + cursor: inherit; + pointer-events: none; +} +.rbc-event.rbc-selected, .rbc-day-slot .rbc-selected.rbc-background-event { + background-color: #265985; +} +.rbc-event:focus, .rbc-day-slot .rbc-background-event:focus { + outline: 5px auto #3b99fc; +} + +.rbc-event-label { + font-size: 80%; +} + +.rbc-event-overlaps { + -webkit-box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5); + box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5); +} + +.rbc-event-continues-prior { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.rbc-event-continues-after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.rbc-event-continues-earlier { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.rbc-event-continues-later { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.rbc-row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} + +.rbc-row-segment { + padding: 0 1px 1px 1px; +} +.rbc-selected-cell { + background-color: rgba(0, 0, 0, 0.1); +} + +.rbc-show-more { + background-color: rgba(255, 255, 255, 0.3); + z-index: 4; + font-weight: bold; + font-size: 85%; + height: auto; + line-height: normal; + color: #3174ad; +} +.rbc-show-more:hover, .rbc-show-more:focus { + color: #265985; +} + +.rbc-month-view { + position: relative; + border: 1px solid #ddd; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + width: 100%; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; + height: 100%; +} + +.rbc-month-header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} + +.rbc-month-row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + position: relative; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + -ms-flex-preferred-size: 0px; + flex-basis: 0px; + overflow: hidden; + height: 100%; +} +.rbc-month-row + .rbc-month-row { + border-top: 1px solid #ddd; +} + +.rbc-date-cell { + -webkit-box-flex: 1; + -ms-flex: 1 1 0px; + flex: 1 1 0; + min-width: 0; + padding-right: 5px; + text-align: right; +} +.rbc-date-cell.rbc-now { + font-weight: bold; +} +.rbc-date-cell > a, .rbc-date-cell > a:active, .rbc-date-cell > a:visited { + color: inherit; + text-decoration: none; +} + +.rbc-row-bg { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: hidden; + right: 1px; +} + +.rbc-day-bg { + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; +} +.rbc-day-bg + .rbc-day-bg { + border-left: 1px solid #ddd; +} +.rbc-rtl .rbc-day-bg + .rbc-day-bg { + border-left-width: 0; + border-right: 1px solid #ddd; +} + +.rbc-overlay { + position: absolute; + z-index: 5; + border: 1px solid #e5e5e5; + background-color: #fff; + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.25); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.25); + padding: 10px; +} +.rbc-overlay > * + * { + margin-top: 1px; +} + +.rbc-overlay-header { + border-bottom: 1px solid #e5e5e5; + margin: -10px -10px 5px -10px; + padding: 2px 10px; +} + +.rbc-agenda-view { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: auto; +} +.rbc-agenda-view table.rbc-agenda-table { + width: 100%; + border: 1px solid #ddd; + border-spacing: 0; + border-collapse: collapse; +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr > td { + padding: 5px 10px; + vertical-align: top; +} +.rbc-agenda-view table.rbc-agenda-table .rbc-agenda-time-cell { + padding-left: 15px; + padding-right: 15px; + text-transform: lowercase; +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td { + border-left: 1px solid #ddd; +} +.rbc-rtl .rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr + tr { + border-top: 1px solid #ddd; +} +.rbc-agenda-view table.rbc-agenda-table thead > tr > th { + padding: 3px 5px; + text-align: left; + border-bottom: 1px solid #ddd; +} +.rbc-rtl .rbc-agenda-view table.rbc-agenda-table thead > tr > th { + text-align: right; +} + +.rbc-agenda-time-cell { + text-transform: lowercase; +} +.rbc-agenda-time-cell .rbc-continues-after:after { + content: " »"; +} +.rbc-agenda-time-cell .rbc-continues-prior:before { + content: "« "; +} + +.rbc-agenda-date-cell, +.rbc-agenda-time-cell { + white-space: nowrap; +} + +.rbc-agenda-event-cell { + width: 100%; +} + +.rbc-time-column { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + min-height: 100%; + + /*Own changes 06*/ + background-color: #383838; + /*Own changes 06*/ +} +.rbc-time-column .rbc-timeslot-group { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.rbc-timeslot-group { + border-bottom: 1px solid #8d8d8d; /*#ddd*/ + min-height: 40px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; +} + +.rbc-time-gutter, +.rbc-header-gutter { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + + /*Own changes 07*/ + background-color: #8d8d8d; + /*Own changes 07*/ +} + +.rbc-label { + padding: 0 5px; +} + +.rbc-day-slot { + position: relative; +} +.rbc-day-slot .rbc-events-container { + bottom: 0; + left: 0; + position: absolute; + right: 0; + margin-right: 10px; + top: 0; +} +.rbc-day-slot .rbc-events-container.rbc-rtl { + left: 10px; + right: 0; +} +.rbc-day-slot .rbc-event, .rbc-day-slot .rbc-background-event { + border: 1px solid #265985; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + max-height: 100%; + min-height: 20px; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-flow: column wrap; + flex-flow: column wrap; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + overflow: hidden; + position: absolute; +} +.rbc-day-slot .rbc-background-event { + opacity: 0.75; +} +.rbc-day-slot .rbc-event-label { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + padding-right: 5px; + width: auto; +} +.rbc-day-slot .rbc-event-content { + width: 100%; + -webkit-box-flex: 1; + -ms-flex: 1 1 0px; + flex: 1 1 0; + word-wrap: break-word; + line-height: 1; + height: 100%; + min-height: 1em; +} +.rbc-day-slot .rbc-time-slot { + border-top: 1px solid #383838; /*#f7f7f7*/ +} + +.rbc-time-view-resources .rbc-time-gutter, +.rbc-time-view-resources .rbc-time-header-gutter { + position: sticky; + left: 0; + background-color: white; + border-right: 1px solid #ddd; + z-index: 10; + margin-right: -1px; +} +.rbc-time-view-resources .rbc-time-header { + overflow: hidden; +} +.rbc-time-view-resources .rbc-time-header-content { + min-width: auto; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + -ms-flex-preferred-size: 0px; + flex-basis: 0px; +} +.rbc-time-view-resources .rbc-time-header-cell-single-day { + display: none; +} +.rbc-time-view-resources .rbc-day-slot { + min-width: 140px; +} +.rbc-time-view-resources .rbc-header, +.rbc-time-view-resources .rbc-day-bg { + width: 140px; + -webkit-box-flex: 1; + -ms-flex: 1 1 0px; + flex: 1 1 0; + -ms-flex-preferred-size: 0 px; + flex-basis: 0 px; +} + +.rbc-time-header-content + .rbc-time-header-content { + margin-left: -1px; +} + +.rbc-time-slot { + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; +} +.rbc-time-slot.rbc-now { + font-weight: bold; +} + +.rbc-day-header { + text-align: center; +} + +.rbc-slot-selection { + z-index: 10; + position: absolute; + background-color: rgba(0, 0, 0, 0.5); + color: white; + font-size: 75%; + width: 100%; + padding: 3px; +} + +.rbc-slot-selecting { + cursor: move; +} + +.rbc-time-view { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + width: 100%; + border: 1px solid #ddd; + min-height: 0; +} +.rbc-time-view .rbc-time-gutter { + white-space: nowrap; + text-align: right; +} +.rbc-time-view .rbc-allday-cell { + -webkit-box-sizing: content-box; + box-sizing: content-box; + width: 100%; + height: 100%; + position: relative; + + /*Own changes 05*/ + background-color: #555555; + /*Own changes 05*/ +} +.rbc-time-view .rbc-allday-cell + .rbc-allday-cell { + border-left: 1px solid #ddd; +} +.rbc-time-view .rbc-allday-events { + position: relative; + z-index: 4; +} +.rbc-time-view .rbc-row { + -webkit-box-sizing: border-box; + box-sizing: border-box; + min-height: 20px; +} + +.rbc-time-header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} +.rbc-time-header.rbc-overflowing { + border-right: 1px solid #ddd; +} +.rbc-rtl .rbc-time-header.rbc-overflowing { + border-right-width: 0; + border-left: 1px solid #ddd; +} +.rbc-time-header > .rbc-row:first-child { + border-bottom: 1px solid #ddd; +} +.rbc-time-header > .rbc-row.rbc-row-resource { + border-bottom: 1px solid #ddd; +} + +.rbc-time-header-cell-single-day { + display: none; +} + +.rbc-time-header-content { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + min-width: 0; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + border-left: 1px solid #ddd; + + /*Own changes 08*/ + background-color: #c6c6c6; + color: #000000; + border-top-left-radius: 11px; + border-top-right-radius: 11px; + /*Own changes 08*/ +} +.rbc-rtl .rbc-time-header-content { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-time-header-content > .rbc-row.rbc-row-resource { + border-bottom: 1px solid #ddd; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.rbc-time-content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + width: 100%; + border-top: 2px solid #717171; /*#ddd*/ + overflow-y: auto; + position: relative; +} +.rbc-time-content > .rbc-time-gutter { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + + /*Own changes 09*/ + border-top-left-radius: 11px; + border-bottom-left-radius: 11px; + /*Own changes 09*/ +} +.rbc-time-content > * + * > * { + border-left: 1px solid #c6c6c6; /*#ddd*/ +} +.rbc-rtl .rbc-time-content > * + * > * { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-time-content > .rbc-day-slot { + width: 100%; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; +} + +.rbc-current-time-indicator { + position: absolute; + z-index: 3; + left: 0; + right: 0; + height: 1px; + background-color: #74ad31; + pointer-events: none; +} + +.rbc-resource-grouping.rbc-time-header-content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} +.rbc-resource-grouping .rbc-row .rbc-header { + width: 141px; +} + +/*# sourceMappingURL=react-big-calendar.css.map */ \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 61defe3..08d5235 100644 --- a/yarn.lock +++ b/yarn.lock @@ -206,7 +206,7 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react-dom@npm:^2.0.0": +"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.1.3": version: 2.1.3 resolution: "@floating-ui/react-dom@npm:2.1.3" dependencies: @@ -218,6 +218,20 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react@npm:^0.27.3": + version: 0.27.12 + resolution: "@floating-ui/react@npm:0.27.12" + dependencies: + "@floating-ui/react-dom": "npm:^2.1.3" + "@floating-ui/utils": "npm:^0.2.9" + tabbable: "npm:^6.0.0" + peerDependencies: + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 10c0/da453965074bd4ded8e3de97ceb2c0833df8df2ecd9eff5ae4d336413443ea5abde5c9e37b092956901b97e7b47f9138d51d4896fa82da68e77eb0090289bf64 + languageName: node + linkType: hard + "@floating-ui/utils@npm:^0.2.9": version: 0.2.9 resolution: "@floating-ui/utils@npm:0.2.9" @@ -4044,6 +4058,7 @@ __metadata: prisma: "npm:6.9.0" react: "npm:^19.1.0" react-big-calendar: "npm:^1.18.0" + react-datepicker: "npm:^8.4.0" react-dom: "npm:^19.0.0" tailwind-merge: "npm:^3.2.0" tailwindcss: "npm:4.1.10" @@ -4580,6 +4595,20 @@ __metadata: languageName: node linkType: hard +"react-datepicker@npm:^8.4.0": + version: 8.4.0 + resolution: "react-datepicker@npm:8.4.0" + dependencies: + "@floating-ui/react": "npm:^0.27.3" + clsx: "npm:^2.1.1" + date-fns: "npm:^4.1.0" + peerDependencies: + react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + checksum: 10c0/e96ba4f2b54476f66bfa33aa0c21729095c83d293671b07a9ddd96ab48dad85b6530acc5ca016e83ef8907feeebfefb7133bf7a02dc550175c48c5d9f66d70ac + languageName: node + linkType: hard + "react-dom@npm:^19.0.0": version: 19.1.0 resolution: "react-dom@npm:19.1.0" @@ -5195,6 +5224,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^6.0.0": + version: 6.2.0 + resolution: "tabbable@npm:6.2.0" + checksum: 10c0/ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898 + languageName: node + linkType: hard + "tailwind-merge@npm:^3.2.0": version: 3.3.1 resolution: "tailwind-merge@npm:3.3.1"