EN · DE · RU · FR · ES

#2720: FullCalendarPanel.jsx

projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx React-Komponente, projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx 521 Zeilen · 450 Code · 38 Kommentare · 33 leer
Zweck: React-Webapp: FullCalendarPanel.jsx. FullCalendarPanel.jsx ist Teil der Open-Source-Projektmanagement-Anwendung ProjectForge.

Quelltext (erste 100 Zeilen)

import React, { useEffect, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import FullCalendar from '@fullcalendar/react';
import '@fortawesome/fontawesome-free/css/all.css';
import deLocale from '@fullcalendar/core/locales/de';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import listPlugin from '@fullcalendar/list';
import bootstrapPlugin from '@fullcalendar/bootstrap';
import { useSelector } from 'react-redux'; // ein Plugin!
import { createPopper } from '@popperjs/core';
import { Outlet, useNavigate } from 'react-router';
import LoadingContainer from '../../../components/design/loading-container';
import { fetchJsonGet, fetchJsonPost } from '../../../utilities/rest';
import CalendarEventTooltip from './CalendarEventTooltip';
import './FullCalendarPanel.scss';

/*
TODO:
 - Behandlung von wiederkehrenden Ereignissen.
*/

function FullCalendarPanel({
    activeCalendars,
    timesheetUserId = null,
    showBreaks = false,
    defaultDate = null,
    defaultView = 'timeGridWeek',
    translations,
    gridSize = 30,
    firstHour = 8,
    vacationGroups = [],
    vacationUsers = [],
    topHeight = '0px',
    alternateHoursBackground = true,
}) {
    const locale = useSelector((state) => state.authentication.user.locale) || 'de';
    const firstDayOfWeek = useSelector((state) => state.authentication.user.firstDayOfWeekSunday0) || 0;
    const timeNotation = useSelector((state) => state.authentication.user.timeNotation) || 'H24';
    const [queryString] = useState(window.location.search);
    const [currentHoverEvent, setCurrentHoverEvent] = useState(null);
    const [loading, setLoading] = useState(false);
    let initialDate = defaultDate;
    if (defaultDate && defaultView === 'dayGridMonth' && !initialDate.endsWith('01')) {
        // Fullcalendar benötigt den Tag des Monats als Startdatum, nicht den Start des
        // ersten Monats mit dem Tag des Vormonats:
        let date = new Date(initialDate);
        date = new Date(date.getFullYear(), date.getMonth() + 1, 1);
        initialDate = Date.toIsoDateString(date);
    }
    const [currentState, setCurrentState] = useState({
        date: initialDate,
        view: defaultView,
    });
    const activeCalendarsRef = useRef(activeCalendars);
    const timesheetUserIdRef = useRef(timesheetUserId);
    const showBreaksRef = useRef(showBreaks);

    const tooltipRef = useRef(undefined);
    const popperRef = useRef(undefined);

    const calendarRef = useRef();

    const navigate = useNavigate();

    const refetch = (currentApi) => {
        if (!currentApi) {
            // console.log('currentApi noch nicht verfügbar');
            return;
        }
        activeCalendarsRef.current = activeCalendars;
        timesheetUserIdRef.current = timesheetUserId;
        showBreaksRef.current = showBreaks;
        currentApi.refetchEvents();
    };

    const firstUpdate = useRef(true);

    useEffect(() => {
        if (firstUpdate.current) {
            firstUpdate.current = false;
            return;
        }
        const currentApi = calendarRef?.current?.getApi();
        // eslint-disable-next-line max-len
        // console.log('refetch', activeCalendars, timesheetUserId, vacationGroups, vacationUsers);
        refetch(currentApi);
    }, [activeCalendars, timesheetUserId, showBreaks, vacationGroups, vacationUsers]);

    useEffect(() => {
        const currentApi = calendarRef?.current?.getApi();
        if (!currentApi) {
            // console.log('currentApi noch nicht verfügbar');
            return;
        }
        if (queryString !== window.location.search) {
            const newQueryParams = new URLSearchParams(window.location.search);
            const queryParams = new URLSearchParams(queryString);
            const dateChanged = newQueryParams.get('gotoDate') !== undefined && queryParams.get('gotoDate') !== newQueryParams.get('gotoDate');

Git-Verlauf

bf988bc6d 43 npm-Sicherheitslücken beseitigt: react-scripts→Vite, ESLint 9, Abhängigkeitsbereinigung, Fehlerbehebungen
b2137d403 Kalendernavigation korrigiert: >= für exklusive activeEnd-Grenze verwendet
e81e7e839 Modaler Dialog auf Kalenderseite (Zeiterfassung, Urlaub, Ereignisse usw.) korrigiert: unterstützt jetzt Schließen mit ESC und Klick außerhalb des Modals.
3490f27f1 Kalender- und Zeiterfassungsseite korrigiert
f867698d3 wip: Paketversionen angehoben

bf988bc6d

43 npm-Sicherheitslücken beseitigt: react-scripts→Vite, ESLint 9, Abhängigkeitsbereinigung, Fehlerbehebungen
bf988bc6d14252863c2a7a54c409c947fb9feb7b
diff --git a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
index a66f36ea3..1f615af46 100644
--- a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
+++ b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
@@ -8,7 +8,7 @@ import timeGridPlugin from '@fullcalendar/timegrid';
 import interactionPlugin from '@fullcalendar/interaction';
 import listPlugin from '@fullcalendar/list';
 import bootstrapPlugin from '@fullcalendar/bootstrap';
-import { connect } from 'react-redux'; // ein Plugin!
+import { useSelector } from 'react-redux'; // ein Plugin!
 import { createPopper } from '@popperjs/core';
 import { Outlet, useNavigate } from 'react-router';
 import LoadingContainer from '../../../components/design/loading-container';
@@ -25,19 +25,19 @@ function FullCalendarPanel({
     activeCalendars,
     timesheetUserId = null,
     showBreaks = false,
-    locale = 'en',
-    firstDayOfWeek = 0,
     defaultDate = null,
     defaultView = 'timeGridWeek',
     translations,
     gridSize = 30,
     firstHour = 8,
-    timeNotation = 'H24',
     vacationGroups = [],
     vacationUsers = [],
     topHeight = '0px',
     alternateHoursBackground = true,
 }) {
+    const locale = useSelector((state) => state.authentication.user.locale) || 'de';
+    const firstDayOfWeek = useSelector((state) => state.authentication.user.firstDayOfWeekSunday0) || 0;
+    const timeNotation = useSelector((state) => state.authentication.user.timeNotation) || 'H24';
     const [queryString] = useState(window.location.search);
     const [currentHoverEvent, setCurrentHoverEvent] = useState(null);
     const [loading, setLoading] = useState(false);
@@ -518,10 +518,4 @@ FullCalendarPanel.propTypes = {
     alternateHoursBackground: PropTypes.bool,
 };
 
-const mapStateToProps = ({ authentication }) => ({
-    firstDayOfWeek: authentication.user.firstDayOfWeekSunday0,
-    timeNotation: authentication.user.timeNotation,
-    locale: authentication.user.locale,
-});
-
-export default connect(mapStateToProps)(FullCalendarPanel);
+export default FullCalendarPanel;

b2137d403

Kalendernavigation korrigiert: >= für exklusive activeEnd-Grenze verwendet
b2137d403ef3ff78452116247009096ad860896b
diff --git a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
index 55fc240e3..a66f36ea3 100644
--- a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
+++ b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
@@ -110,7 +110,7 @@ function FullCalendarPanel({
                 const viewStart = Date.toIsoDateString(currentApi.view.activeStart);
                 const viewEnd = Date.toIsoDateString(currentApi.view.activeEnd);
                 // console.log(date, viewStart, viewEnd, date < viewStart);
-                if (date < viewStart || date > viewEnd) {
+                if (date < viewStart || date >= viewEnd) {
                     // console.log('gotoDate', date);
                     currentApi.gotoDate(date);
                     refetchTriggerd = true;

e81e7e839

Modaler Dialog auf Kalenderseite (Zeiterfassung, Urlaub, Ereignisse usw.) korrigiert: unterstützt jetzt Schließen mit ESC und Klick außerhalb des Modals.
e81e7e8391fc2dccafd0e6f3fa954c9629423656
diff --git a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
index 3fef4e376..55fc240e3 100644
--- a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
+++ b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
@@ -9,7 +9,6 @@ import interactionPlugin from '@fullcalendar/interaction';
 import listPlugin from '@fullcalendar/list';
 import bootstrapPlugin from '@fullcalendar/bootstrap';
 import { connect } from 'react-redux'; // ein Plugin!
-import classNames from 'classnames';
 import { createPopper } from '@popperjs/core';
 import { Outlet, useNavigate } from 'react-router';
 import LoadingContainer from '../../../components/design/loading-container';
@@ -243,7 +242,7 @@ function FullCalendarPanel({
             navigate(`/react/calendar/timesheet/edit?startDate=${event.start.getTime() / 1000}&endDate=${event.end.getTime() / 1000}`);
         } else if (category === 'timesheet-stats') {
             // Nichts tun
-        } else if (category === 'vaction') {
+        } else if (category === 'vacation') {
             navigate(`/react/calendar/vacation/edit/${id}?returnToCaller=%2Freact%2Fcalendar`);
         } else if (category === 'address') {
             // Startdatum wird an den Server gesendet und wird für Serienereignisse benötigt, um die

3490f27f1

Kalender- und Zeiterfassungsseite korrigiert
3490f27f105c220263fa22ff716d907beb75c5a5
diff --git a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
index b86001836..3fef4e376 100644
--- a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
+++ b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
@@ -1,4 +1,5 @@
 import React, { useEffect, useMemo, useRef, useState } from 'react';
+import PropTypes from 'prop-types';
 import FullCalendar from '@fullcalendar/react';
 import '@fortawesome/fontawesome-free/css/all.css';
 import deLocale from '@fullcalendar/core/locales/de';
@@ -8,25 +9,36 @@ import interactionPlugin from '@fullcalendar/interaction';
 import listPlugin from '@fullcalendar/list';
 import bootstrapPlugin from '@fullcalendar/bootstrap';
 import { connect } from 'react-redux'; // ein Plugin!
+import classNames from 'classnames';
 import { createPopper } from '@popperjs/core';
-import { Route } from 'react-router';
+import { Outlet, useNavigate } from 'react-router';
 import LoadingContainer from '../../../components/design/loading-container';
 import { fetchJsonGet, fetchJsonPost } from '../../../utilities/rest';
 import CalendarEventTooltip from './CalendarEventTooltip';
-import history from '../../../utilities/history';
-import FormModal from '../../page/form/FormModal';
+import './FullCalendarPanel.scss';
 
 /*
 TODO:
  - Behandlung von wiederkehrenden Ereignissen.
 */
 
-function FullCalendarPanel(options) {
-    const {
-        activeCalendars, timesheetUserId, showBreaks, locale, firstDayOfWeek,
-        defaultDate, defaultView, match, translations, gridSize, firstHour,
-        timeNotation, vacationGroups, vacationUsers, topHeight, alternateHoursBackground,
-    } = options;
+function FullCalendarPanel({
+    activeCalendars,
+    timesheetUserId = null,
+    showBreaks = false,
+    locale = 'de',
+    firstDayOfWeek = 0,
+    defaultDate = null,
+    defaultView = 'timeGridWeek',
+    translations,
+    gridSize = 30,
+    firstHour = 8,
+    timeNotation = 'H24',
+    vacationGroups = [],
+    vacationUsers = [],
+    topHeight = '0px',
+    alternateHoursBackground = true,
+}) {
     const [queryString] = useState(window.location.search);
     const [currentHoverEvent, setCurrentHoverEvent] = useState(null);
     const [loading, setLoading] = useState(false);
@@ -51,9 +63,7 @@ function FullCalendarPanel(options) {
 
     const calendarRef = useRef();

f867698d3

wip: Paketversionen angehoben
f867698d30e539955cc1dd9aacc4986234ff36f2
diff --git a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
index 68a0688d1..b86001836 100644
--- a/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
+++ b/projectforge-webapp/src/containers/panel/calendar/FullCalendarPanel.jsx
@@ -9,7 +9,7 @@ import listPlugin from '@fullcalendar/list';
 import bootstrapPlugin from '@fullcalendar/bootstrap';
 import { connect } from 'react-redux'; // ein Plugin!
 import { createPopper } from '@popperjs/core';
-import { Route } from 'react-router-dom';
+import { Route } from 'react-router';
 import LoadingContainer from '../../../components/design/loading-container';
 import { fetchJsonGet, fetchJsonPost } from '../../../utilities/rest';
 import CalendarEventTooltip from './CalendarEventTooltip';
@@ -476,7 +476,7 @@ function FullCalendarPanel(options) {
                 eventMouseLeave={closePopOver}
             />
             <Route
-                path={`${match.url}/:category/:type/:id?`}
+                path={`${match.url}/:category/:type/:id?/:tab?`}
                 render={(props) => <FormModal baseUrl={match.url} {...props} />}
             />
         </LoadingContainer>