Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -360,7 +360,7 @@ describe("calendar Availability search", () => {
|
||||
|
||||
calendarApi.changeView("dayGridMonth");
|
||||
await waitFor(() => {
|
||||
expect(screen.queryAllByRole("columnheader").length).toBe(7);
|
||||
expect(screen.queryAllByRole("columnheader").length).toBe(14);
|
||||
});
|
||||
|
||||
const dayNumbers = screen.getAllByRole("columnheader");
|
||||
@@ -394,8 +394,8 @@ describe("calendar Availability search", () => {
|
||||
calendarApi.changeView("dayGridMonth");
|
||||
fireEvent.click(screen.getByTestId("ChevronRightIcon"));
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(2);
|
||||
const callArgs = spy.mock.calls[1][0];
|
||||
expect(spy).toHaveBeenCalledTimes(4);
|
||||
const callArgs = spy.mock.calls[3][0];
|
||||
expect(callArgs.calId).toBe("user1/cal1");
|
||||
|
||||
const startDate = new Date(
|
||||
|
||||
@@ -57,6 +57,6 @@ describe("MiniCalendar", () => {
|
||||
const dateTestId = `date-${today.getFullYear()}-${today.getMonth()}-${today.getDate()}`;
|
||||
|
||||
const todayTile = screen.getByTestId(dateTestId);
|
||||
expect(todayTile?.parentElement).toHaveClass("today");
|
||||
expect(todayTile).toHaveClass("today");
|
||||
});
|
||||
});
|
||||
|
||||
Generated
+116
-80
@@ -19,6 +19,7 @@
|
||||
"@fullcalendar/timegrid": "^6.1.17",
|
||||
"@mui/icons-material": "^7.1.2",
|
||||
"@mui/material": "^7.1.2",
|
||||
"@mui/x-date-pickers": "^8.14.0",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@types/node": "^16.18.126",
|
||||
"@types/react": "^19.1.8",
|
||||
@@ -30,7 +31,6 @@
|
||||
"moment-timezone": "^0.5.48",
|
||||
"openid-client": "^6.5.3",
|
||||
"react": "^19.1.0",
|
||||
"react-calendar": "^6.0.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^13.5.0",
|
||||
"react-redux": "^9.2.0",
|
||||
@@ -1908,7 +1908,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.27.6",
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
||||
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -3261,10 +3263,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/types": {
|
||||
"version": "7.4.4",
|
||||
"version": "7.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.7.tgz",
|
||||
"integrity": "sha512-8vVje9rdEr1rY8oIkYgP+Su5Kwl6ik7O3jQ0wl78JGSmiZhRHV+vkjooGdKD8pbtZbutXFVTWQYshu2b3sG9zw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6"
|
||||
"@babel/runtime": "^7.28.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -3276,15 +3280,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "7.2.0",
|
||||
"version": "7.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.3.tgz",
|
||||
"integrity": "sha512-kwNAUh7bLZ7mRz9JZ+6qfRnnxbE4Zuc+RzXnhSpRSxjTlSTj7b4JxRLXpG+MVtPVtqks5k/XC8No1Vs3x4Z2gg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"@mui/types": "^7.4.4",
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/types": "^7.4.7",
|
||||
"@types/prop-types": "^15.7.15",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^19.1.0"
|
||||
"react-is": "^19.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -3303,6 +3309,94 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-date-pickers": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.14.0.tgz",
|
||||
"integrity": "sha512-fz8z1hoi1tbG1QUcZAkQdiO3GsCOpUeRfyANXDDzDN88L4VqwNEyrv0wmzmCfIX2sgur4gWwWJzMuCvauMgXRw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/utils": "^7.3.3",
|
||||
"@mui/x-internals": "8.14.0",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
|
||||
"@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
|
||||
"date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0",
|
||||
"date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0",
|
||||
"dayjs": "^1.10.7",
|
||||
"luxon": "^3.0.2",
|
||||
"moment": "^2.29.4",
|
||||
"moment-hijri": "^2.1.2 || ^3.0.0",
|
||||
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns-jalali": {
|
||||
"optional": true
|
||||
},
|
||||
"dayjs": {
|
||||
"optional": true
|
||||
},
|
||||
"luxon": {
|
||||
"optional": true
|
||||
},
|
||||
"moment": {
|
||||
"optional": true
|
||||
},
|
||||
"moment-hijri": {
|
||||
"optional": true
|
||||
},
|
||||
"moment-jalaali": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-internals": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.14.0.tgz",
|
||||
"integrity": "sha512-esYyl61nuuFXiN631TWuPh2tqdoyTdBI/4UXgwH3rytF8jiWvy6prPBPRHEH1nvW3fgw9FoBI48FlOO+yEI8xg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/utils": "^7.3.3",
|
||||
"reselect": "^5.1.1",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.3.tgz",
|
||||
@@ -5115,13 +5209,6 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@wojtekmaj/date-utils": {
|
||||
"version": "2.0.2",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/wojtekmaj/date-utils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"license": "MIT",
|
||||
@@ -6408,6 +6495,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.18",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz",
|
||||
"integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"license": "MIT",
|
||||
@@ -8203,16 +8298,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-user-locale": {
|
||||
"version": "3.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"memoize": "^10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/wojtekmaj/get-user-locale?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"dev": true,
|
||||
@@ -10752,19 +10837,6 @@
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/memoize": {
|
||||
"version": "10.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-function": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/memoize?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/merge": {
|
||||
"version": "2.1.1",
|
||||
"dev": true,
|
||||
@@ -10803,16 +10875,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-function": {
|
||||
"version": "5.0.1",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/min-indent": {
|
||||
"version": "1.0.1",
|
||||
"dev": true,
|
||||
@@ -11733,29 +11795,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-calendar": {
|
||||
"version": "6.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@wojtekmaj/date-utils": "^2.0.2",
|
||||
"clsx": "^2.0.0",
|
||||
"get-user-locale": "^3.0.0",
|
||||
"warning": "^4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/wojtekmaj/react-calendar?sponsor=1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.1.0",
|
||||
"license": "MIT",
|
||||
@@ -11787,7 +11826,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "19.1.0",
|
||||
"version": "19.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz",
|
||||
"integrity": "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
@@ -13485,7 +13526,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -13547,13 +13590,6 @@
|
||||
"makeerror": "1.0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/warning": {
|
||||
"version": "4.0.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/web-vitals": {
|
||||
"version": "2.1.4",
|
||||
"license": "Apache-2.0"
|
||||
|
||||
+1
-2
@@ -14,6 +14,7 @@
|
||||
"@fullcalendar/timegrid": "^6.1.17",
|
||||
"@mui/icons-material": "^7.1.2",
|
||||
"@mui/material": "^7.1.2",
|
||||
"@mui/x-date-pickers": "^8.14.0",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@types/node": "^16.18.126",
|
||||
"@types/react": "^19.1.8",
|
||||
@@ -23,10 +24,8 @@
|
||||
"ky": "^1.8.1",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.48",
|
||||
"moment": "^2.30.1",
|
||||
"openid-client": "^6.5.3",
|
||||
"react": "^19.1.0",
|
||||
"react-calendar": "^6.0.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^13.5.0",
|
||||
"react-redux": "^9.2.0",
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
|
||||
.sidebar
|
||||
border-right 2px solid #f3f4f6
|
||||
width 330px
|
||||
padding 16px 25px
|
||||
width 350px
|
||||
padding-top 16px
|
||||
padding-left 5px
|
||||
height 90vh
|
||||
flex-direction column
|
||||
overflow auto
|
||||
@@ -71,32 +72,8 @@
|
||||
font-family "Segoe UI", Tahoma, Geneva, Verdana, sans-serif
|
||||
|
||||
/* mini calendar style */
|
||||
.react-calendar
|
||||
border none
|
||||
font-size 14px
|
||||
width 100%
|
||||
user-select none
|
||||
|
||||
/* selected day hover */
|
||||
.react-calendar__tile
|
||||
transition background-color 0.3s ease, color 0.3s ease
|
||||
border none
|
||||
background-color white
|
||||
width 12px
|
||||
height 37.52px
|
||||
|
||||
.react-calendar__tile--active
|
||||
background-color rgb(255, 255, 255)
|
||||
|
||||
.today
|
||||
background orange !important
|
||||
color white
|
||||
|
||||
.selectedWeek
|
||||
background-color lightgrey
|
||||
|
||||
.react-calendar__month-view__days__day--neighboringMonth
|
||||
color gray
|
||||
.MuiDayCalendar-weekContainer:has(.selectedWeek)
|
||||
background-color lightgray !important
|
||||
|
||||
/* sidebar header */
|
||||
.sidebar-calendar h2
|
||||
|
||||
@@ -41,6 +41,13 @@ import { EventErrorHandler } from "../Error/EventErrorHandler";
|
||||
import { EventErrorSnackbar } from "../Error/ErrorSnackbar";
|
||||
import momentTimezonePlugin from "@fullcalendar/moment-timezone";
|
||||
import { TimezoneSelector } from "./TimezoneSelector";
|
||||
import { DateCalendar } from "@mui/x-date-pickers/DateCalendar";
|
||||
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
||||
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
|
||||
import moment from "moment";
|
||||
import { DayCalendarSkeleton } from "@mui/x-date-pickers/DayCalendarSkeleton";
|
||||
import { PickersDay } from "@mui/x-date-pickers/PickersDay";
|
||||
import { MiniCalendar } from "./MiniCalendar";
|
||||
|
||||
interface CalendarAppProps {
|
||||
calendarRef: React.RefObject<CalendarApi | null>;
|
||||
@@ -242,14 +249,6 @@ export default function CalendarApp({
|
||||
errorHandler: errorHandler.current,
|
||||
});
|
||||
|
||||
const handleMonthUp = () => {
|
||||
eventHandlers.handleMonthUp(selectedMiniDate, setSelectedMiniDate);
|
||||
};
|
||||
|
||||
const handleMonthDown = () => {
|
||||
eventHandlers.handleMonthDown(selectedMiniDate, setSelectedMiniDate);
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
(window as any).__calendarRef = calendarRef;
|
||||
}
|
||||
@@ -265,87 +264,13 @@ export default function CalendarApp({
|
||||
>
|
||||
<AddIcon /> <p>Create Event</p>
|
||||
</Button>
|
||||
<div className="calendar-label">
|
||||
<div className="calendar-label">
|
||||
<span title="mini calendar month">
|
||||
{selectedMiniDate.toLocaleDateString("en-us", {
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<button onClick={handleMonthUp}><</button>
|
||||
<button onClick={handleMonthDown}>></button>
|
||||
</div>
|
||||
<ReactCalendar
|
||||
key={selectedMiniDate.toDateString()}
|
||||
calendarType="iso8601"
|
||||
formatShortWeekday={(locale, date) =>
|
||||
date.toLocaleDateString(locale, { weekday: "narrow" })
|
||||
}
|
||||
value={selectedMiniDate}
|
||||
onClickDay={(date) => {
|
||||
setSelectedDate(date);
|
||||
setSelectedMiniDate(date);
|
||||
calendarRef.current?.gotoDate(date);
|
||||
}}
|
||||
prevLabel={null}
|
||||
showNeighboringMonth={true}
|
||||
nextLabel={null}
|
||||
showNavigation={false}
|
||||
tileClassName={({ date }) => {
|
||||
const classNames: string[] = [];
|
||||
|
||||
const today = new Date().setHours(0, 0, 0, 0);
|
||||
if (date.getTime() === today) {
|
||||
classNames.push("today");
|
||||
}
|
||||
const selected = new Date(selectedDate);
|
||||
selected.setHours(0, 0, 0, 0);
|
||||
if (
|
||||
calendarRef.current?.view.type === "timeGridWeek" ||
|
||||
calendarRef.current?.view.type === undefined
|
||||
) {
|
||||
const startOfWeek = computeStartOfTheWeek(selected);
|
||||
|
||||
const endOfWeek = new Date(startOfWeek);
|
||||
endOfWeek.setDate(startOfWeek.getDate() + 6); // Sunday
|
||||
endOfWeek.setHours(23, 59, 59, 999);
|
||||
|
||||
if (date <= endOfWeek && date >= startOfWeek) {
|
||||
classNames.push("selectedWeek");
|
||||
}
|
||||
}
|
||||
if (
|
||||
calendarRef.current?.view.type === "timeGridDay" &&
|
||||
date.getTime() === selected.getTime()
|
||||
) {
|
||||
classNames.push("selectedWeek");
|
||||
}
|
||||
return classNames;
|
||||
}}
|
||||
tileContent={({ date }) => {
|
||||
const classNames: string[] = [];
|
||||
const hasEvents = dottedEvents.some((event) => {
|
||||
const eventDate = new Date(event.start);
|
||||
return (
|
||||
eventDate.getFullYear() === date.getFullYear() &&
|
||||
eventDate.getMonth() === date.getMonth() &&
|
||||
eventDate.getDate() === date.getDate() &&
|
||||
event.status !== "CANCELLED"
|
||||
);
|
||||
});
|
||||
if (hasEvents) {
|
||||
classNames.push("event-dot");
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames.join(" ")}
|
||||
data-testid={`date-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`}
|
||||
></div>
|
||||
);
|
||||
}}
|
||||
<MiniCalendar
|
||||
calendarRef={calendarRef}
|
||||
selectedDate={selectedMiniDate}
|
||||
setSelectedDate={setSelectedDate}
|
||||
setSelectedMiniDate={setSelectedMiniDate}
|
||||
dottedEvents={dottedEvents}
|
||||
/>
|
||||
<TempCalendarsInput
|
||||
setAnchorEl={setAnchorEl}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
||||
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
|
||||
import { DateCalendar, PickersDay } from "@mui/x-date-pickers";
|
||||
import { DayCalendarSkeleton } from "@mui/x-date-pickers/DayCalendarSkeleton";
|
||||
import moment from "moment";
|
||||
import {
|
||||
computeStartOfTheWeek,
|
||||
formatDateToYYYYMMDDTHHMMSS,
|
||||
getCalendarRange,
|
||||
} from "../../utils/dateUtils";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import { refreshCalendars } from "../Event/utils/eventUtils";
|
||||
import { getCalendarDetailAsync } from "../../features/Calendars/CalendarSlice";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function MiniCalendar({
|
||||
calendarRef,
|
||||
selectedDate,
|
||||
setSelectedDate,
|
||||
setSelectedMiniDate,
|
||||
dottedEvents,
|
||||
}: {
|
||||
calendarRef: any;
|
||||
selectedDate: Date;
|
||||
setSelectedDate: (d: Date) => void;
|
||||
setSelectedMiniDate: (d: Date) => void;
|
||||
dottedEvents: any[];
|
||||
}) {
|
||||
const dispatch = useAppDispatch();
|
||||
const calendars = useAppSelector((state) => state.calendars);
|
||||
const [visibleDate, setVisibleDate] = useState(selectedDate);
|
||||
|
||||
useEffect(() => setVisibleDate(selectedDate), [selectedDate]);
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="en-gb">
|
||||
<DateCalendar
|
||||
value={moment(visibleDate)}
|
||||
onChange={(dateMoment) => {
|
||||
if (!dateMoment) return;
|
||||
const date = dateMoment.toDate();
|
||||
setSelectedDate(date);
|
||||
setSelectedMiniDate(date);
|
||||
calendarRef.current?.gotoDate(date);
|
||||
}}
|
||||
showDaysOutsideCurrentMonth
|
||||
onMonthChange={(month) => {
|
||||
const calendarRange = getCalendarRange(month.toDate());
|
||||
setVisibleDate(month.toDate());
|
||||
Object.values(calendars.list).forEach((cal) =>
|
||||
dispatch(
|
||||
getCalendarDetailAsync({
|
||||
calId: cal.id,
|
||||
match: {
|
||||
start: formatDateToYYYYMMDDTHHMMSS(calendarRange.start),
|
||||
end: formatDateToYYYYMMDDTHHMMSS(calendarRange.end),
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
}}
|
||||
views={["month", "day"]}
|
||||
slotProps={{
|
||||
day: (ownerState) => {
|
||||
const date = ownerState.day.toDate();
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const selected = new Date(selectedDate);
|
||||
selected.setHours(0, 0, 0, 0);
|
||||
|
||||
const isToday = date.getTime() === today.getTime();
|
||||
const isSelectedDay =
|
||||
calendarRef.current?.view.type === "timeGridDay" &&
|
||||
date.getTime() === selected.getTime();
|
||||
|
||||
const isInSelectedWeek =
|
||||
calendarRef.current?.view.type === "timeGridWeek" ||
|
||||
calendarRef.current?.view.type === undefined
|
||||
? (() => {
|
||||
const startOfWeek = computeStartOfTheWeek(selected);
|
||||
const endOfWeek = new Date(startOfWeek);
|
||||
endOfWeek.setDate(startOfWeek.getDate() + 6);
|
||||
endOfWeek.setHours(23, 59, 59, 999);
|
||||
return date >= startOfWeek && date <= endOfWeek;
|
||||
})()
|
||||
: false;
|
||||
|
||||
const hasEvents = dottedEvents.some((event) => {
|
||||
const eventDate = new Date(event.start);
|
||||
return (
|
||||
eventDate.getFullYear() === date.getFullYear() &&
|
||||
eventDate.getMonth() === date.getMonth() &&
|
||||
eventDate.getDate() === date.getDate() &&
|
||||
event.status !== "CANCELLED"
|
||||
);
|
||||
});
|
||||
|
||||
const classNames = [
|
||||
isToday ? "today" : "",
|
||||
isSelectedDay ? "selectedDay" : "",
|
||||
isInSelectedWeek ? "selectedWeek" : "",
|
||||
hasEvents ? "event-dot" : "",
|
||||
].join(" ");
|
||||
|
||||
return {
|
||||
className: classNames,
|
||||
selected: classNames.includes("selectedWeek"),
|
||||
outsideCurrentMonth: ownerState.isDayOutsideMonth,
|
||||
disableMargin: false,
|
||||
onClick: () => {
|
||||
setSelectedDate(date);
|
||||
setSelectedMiniDate(date);
|
||||
calendarRef.current?.gotoDate(date);
|
||||
},
|
||||
style: {
|
||||
backgroundColor: "transparent",
|
||||
position: "relative",
|
||||
flexDirection: "column",
|
||||
border: 0,
|
||||
},
|
||||
sx: {
|
||||
"&.Mui-selected": {
|
||||
color: "inherit !important",
|
||||
fontWeight: "inherit !important",
|
||||
},
|
||||
"&.selectedDay": {
|
||||
backgroundColor: "lightgray !important",
|
||||
},
|
||||
"&.today": {
|
||||
background: "orange !important",
|
||||
color: "white !important",
|
||||
},
|
||||
},
|
||||
"data-testid": `date-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
|
||||
children: (
|
||||
<>
|
||||
{ownerState.day.date()}
|
||||
{hasEvents && <div className="event-dot" />}
|
||||
</>
|
||||
),
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
}
|
||||
@@ -89,24 +89,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
||||
setOpenEventDisplay(false);
|
||||
};
|
||||
|
||||
const handleMonthUp = (
|
||||
selectedMiniDate: Date,
|
||||
setSelectedMiniDate: (date: Date) => void
|
||||
) => {
|
||||
setSelectedMiniDate(
|
||||
new Date(selectedMiniDate.getFullYear(), selectedMiniDate.getMonth() - 1)
|
||||
);
|
||||
};
|
||||
|
||||
const handleMonthDown = (
|
||||
selectedMiniDate: Date,
|
||||
setSelectedMiniDate: (date: Date) => void
|
||||
) => {
|
||||
setSelectedMiniDate(
|
||||
new Date(selectedMiniDate.getFullYear(), selectedMiniDate.getMonth() + 1)
|
||||
);
|
||||
};
|
||||
|
||||
const handleEventClick = (info: any) => {
|
||||
info.jsEvent.preventDefault();
|
||||
|
||||
@@ -255,8 +237,6 @@ export const createEventHandlers = (props: EventHandlersProps) => {
|
||||
handleDateSelect,
|
||||
handleClosePopover,
|
||||
handleCloseEventDisplay,
|
||||
handleMonthUp,
|
||||
handleMonthDown,
|
||||
handleEventClick,
|
||||
handleEventAllow,
|
||||
handleEventDrop,
|
||||
|
||||
@@ -25,8 +25,6 @@ export const useCalendarEventHandlers = (props: EventHandlersProps) => {
|
||||
eventHandlers.handleCloseEventDisplay,
|
||||
[props.setOpenEventDisplay]
|
||||
),
|
||||
handleMonthUp: useCallback(eventHandlers.handleMonthUp, []),
|
||||
handleMonthDown: useCallback(eventHandlers.handleMonthDown, []),
|
||||
handleEventClick: useCallback(eventHandlers.handleEventClick, [
|
||||
props.setOpenEventDisplay,
|
||||
props.setEventDisplayedId,
|
||||
|
||||
Reference in New Issue
Block a user