diff --git a/docs/user_guide/assets/licenses/frontend_licenses.txt b/docs/user_guide/assets/licenses/frontend_licenses.txt
index 88683fd764..6a4858e759 100644
--- a/docs/user_guide/assets/licenses/frontend_licenses.txt
+++ b/docs/user_guide/assets/licenses/frontend_licenses.txt
@@ -1388,6 +1388,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+@mui/x-date-pickers 6.18.7
+MIT
+MIT License
+
+Copyright (c) 2020 Material-UI SAS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
@popperjs/core 2.11.8
MIT
The MIT License (MIT)
diff --git a/package-lock.json b/package-lock.json
index e27cd0c091..98fa6e5948 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"@microsoft/signalr": "^8.0.0",
"@mui/icons-material": "^5.14.19",
"@mui/material": "^5.14.16",
+ "@mui/x-date-pickers": "^6.18.7",
"@redux-devtools/extension": "^3.2.5",
"@reduxjs/toolkit": "^1.9.5",
"@segment/analytics-next": "^1.55.0",
@@ -4301,6 +4302,72 @@
"react-dom": ">=18.0.0"
}
},
+ "node_modules/@material-table/core/node_modules/@mui/x-date-pickers": {
+ "version": "5.0.20",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.20.tgz",
+ "integrity": "sha512-ERukSeHIoNLbI1C2XRhF9wRhqfsr+Q4B1SAw2ZlU7CWgcG8UBOxgqRKDEOVAIoSWL+DWT6GRuQjOKvj6UXZceA==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.9",
+ "@date-io/core": "^2.15.0",
+ "@date-io/date-fns": "^2.15.0",
+ "@date-io/dayjs": "^2.15.0",
+ "@date-io/luxon": "^2.15.0",
+ "@date-io/moment": "^2.15.0",
+ "@mui/utils": "^5.10.3",
+ "@types/react-transition-group": "^4.4.5",
+ "clsx": "^1.2.1",
+ "prop-types": "^15.7.2",
+ "react-transition-group": "^4.4.5",
+ "rifm": "^0.12.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.4.1",
+ "@mui/system": "^5.4.1",
+ "date-fns": "^2.25.0",
+ "dayjs": "^1.10.7",
+ "luxon": "^1.28.0 || ^2.0.0 || ^3.0.0",
+ "moment": "^2.29.1",
+ "react": "^17.0.2 || ^18.0.0",
+ "react-dom": "^17.0.2 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "date-fns": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@material-table/core/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@matt-block/react-recaptcha-v2": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@matt-block/react-recaptcha-v2/-/react-recaptcha-v2-2.0.1.tgz",
@@ -4656,25 +4723,20 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/@mui/x-date-pickers": {
- "version": "5.0.20",
- "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.20.tgz",
- "integrity": "sha512-ERukSeHIoNLbI1C2XRhF9wRhqfsr+Q4B1SAw2ZlU7CWgcG8UBOxgqRKDEOVAIoSWL+DWT6GRuQjOKvj6UXZceA==",
+ "version": "6.18.7",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.18.7.tgz",
+ "integrity": "sha512-4NoapaCT3jvEk2cuAUjG0ReZvTEk1i4dGDz94Gt1Oc08GuC1AuzYRwCR1/1tdmbDynwkR8ilkKL6AyS3NL1H4A==",
"dependencies": {
- "@babel/runtime": "^7.18.9",
- "@date-io/core": "^2.15.0",
- "@date-io/date-fns": "^2.15.0",
- "@date-io/dayjs": "^2.15.0",
- "@date-io/luxon": "^2.15.0",
- "@date-io/moment": "^2.15.0",
- "@mui/utils": "^5.10.3",
- "@types/react-transition-group": "^4.4.5",
- "clsx": "^1.2.1",
- "prop-types": "^15.7.2",
- "react-transition-group": "^4.4.5",
- "rifm": "^0.12.1"
+ "@babel/runtime": "^7.23.2",
+ "@mui/base": "^5.0.0-beta.22",
+ "@mui/utils": "^5.14.16",
+ "@types/react-transition-group": "^4.4.8",
+ "clsx": "^2.0.0",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
},
"engines": {
- "node": ">=12.0.0"
+ "node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
@@ -4683,14 +4745,17 @@
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
- "@mui/material": "^5.4.1",
- "@mui/system": "^5.4.1",
+ "@mui/material": "^5.8.6",
+ "@mui/system": "^5.8.0",
"date-fns": "^2.25.0",
+ "date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.10.7",
- "luxon": "^1.28.0 || ^2.0.0 || ^3.0.0",
- "moment": "^2.29.1",
- "react": "^17.0.2 || ^18.0.0",
- "react-dom": "^17.0.2 || ^18.0.0"
+ "luxon": "^3.0.2",
+ "moment": "^2.29.4",
+ "moment-hijri": "^2.1.2",
+ "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
@@ -4702,6 +4767,9 @@
"date-fns": {
"optional": true
},
+ "date-fns-jalali": {
+ "optional": true
+ },
"dayjs": {
"optional": true
},
@@ -4710,17 +4778,15 @@
},
"moment": {
"optional": true
+ },
+ "moment-hijri": {
+ "optional": true
+ },
+ "moment-jalaali": {
+ "optional": true
}
}
},
- "node_modules/@mui/x-date-pickers/node_modules/clsx": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
- "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
diff --git a/package.json b/package.json
index 91ec72b69f..97af8c6156 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"@microsoft/signalr": "^8.0.0",
"@mui/icons-material": "^5.14.19",
"@mui/material": "^5.14.16",
+ "@mui/x-date-pickers": "^6.18.7",
"@redux-devtools/extension": "^3.2.5",
"@reduxjs/toolkit": "^1.9.5",
"@segment/analytics-next": "^1.55.0",
diff --git a/src/components/App/AppLoggedIn.tsx b/src/components/App/AppLoggedIn.tsx
index 713866723c..3090bb416f 100644
--- a/src/components/App/AppLoggedIn.tsx
+++ b/src/components/App/AppLoggedIn.tsx
@@ -4,6 +4,7 @@ import { Theme, ThemeProvider, createTheme } from "@mui/material/styles";
import { ReactElement, useEffect, useMemo, useState } from "react";
import { Route, Routes } from "react-router-dom";
+import DatePickersLocalizationProvider from "components/App/DatePickersLocalizationProvider";
import SignalRHub from "components/App/SignalRHub";
import AppBar from "components/AppBar/AppBarComponent";
import PageNotFound from "components/PageNotFound/component";
@@ -72,7 +73,7 @@ export default function AppWithBar(): ReactElement {
: theme;
return (
- <>
+
@@ -113,6 +114,6 @@ export default function AppWithBar(): ReactElement {
- >
+
);
}
diff --git a/src/components/App/DatePickersLocalizationProvider.tsx b/src/components/App/DatePickersLocalizationProvider.tsx
new file mode 100644
index 0000000000..582ca64c31
--- /dev/null
+++ b/src/components/App/DatePickersLocalizationProvider.tsx
@@ -0,0 +1,26 @@
+import { LocalizationProvider } from "@mui/x-date-pickers";
+import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
+// Import for each non-en uiWritingSystem lang in src/types/writingSystem.ts:
+import "dayjs/locale/ar";
+import "dayjs/locale/es";
+import "dayjs/locale/fr";
+import "dayjs/locale/pt";
+import "dayjs/locale/zh";
+import { ReactElement, ReactNode } from "react";
+
+import i18n from "i18n";
+
+export default function DatePickersLocalizationProvider(props: {
+ children: ReactNode;
+}): ReactElement {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/src/components/ProjectSettings/ProjectSchedule/CalendarView.tsx b/src/components/ProjectSettings/ProjectSchedule/CalendarView.tsx
index 67ac753ace..ed4aa71930 100644
--- a/src/components/ProjectSettings/ProjectSchedule/CalendarView.tsx
+++ b/src/components/ProjectSettings/ProjectSchedule/CalendarView.tsx
@@ -1,55 +1,35 @@
import { Icon } from "@mui/material";
-import {
- CalendarPicker,
- PickersDay,
- PickersDayProps,
-} from "@mui/x-date-pickers";
-import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
-import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
+import { DateCalendar } from "@mui/x-date-pickers";
import dayjs, { Dayjs } from "dayjs";
import { ReactElement } from "react";
+import ProjectPickersDay from "components/ProjectSettings/ProjectSchedule/ProjectPickersDay";
+
interface CalendarViewProps {
projectSchedule: Date[];
}
export default function CalendarView(props: CalendarViewProps): ReactElement {
- // Custom renderer for CalendarPicker
- function customDayRenderer(
- day: Dayjs,
- _selectedDays: Array,
- pickersDayProps: PickersDayProps
- ): ReactElement {
- const date = day.toDate();
- const selected =
- props.projectSchedule &&
- props.projectSchedule.findIndex(
- (d) =>
- d.getDate() === date.getDate() &&
- d.getMonth() === date.getMonth() &&
- d.getFullYear() === date.getFullYear()
- ) >= 0;
- return ;
- }
-
function handleCalendarView(monthToRender?: Dayjs[]): ReactElement[] {
if (!monthToRender) {
return [];
}
return monthToRender.map((tempDayjs) => (
- {}}
- date={null}
disableHighlightToday
- renderDay={customDayRenderer}
+ slots={{
+ day: ProjectPickersDay,
+ leftArrowIcon: Icon,
+ rightArrowIcon: Icon,
+ }}
+ slotProps={{ day: { days: props.projectSchedule } as any }}
/>
));
}
@@ -60,9 +40,5 @@ export default function CalendarView(props: CalendarViewProps): ReactElement {
return Array.from(new Set(months)).sort().map(dayjs);
}
- return (
-
- {handleCalendarView(getScheduledMonths(props.projectSchedule))}
-
- );
+ return <>{handleCalendarView(getScheduledMonths(props.projectSchedule))}>;
}
diff --git a/src/components/ProjectSettings/ProjectSchedule/DateScheduleEdit.tsx b/src/components/ProjectSettings/ProjectSchedule/DateScheduleEdit.tsx
index a4fb5b510f..5c4a39c394 100644
--- a/src/components/ProjectSettings/ProjectSchedule/DateScheduleEdit.tsx
+++ b/src/components/ProjectSettings/ProjectSchedule/DateScheduleEdit.tsx
@@ -1,17 +1,12 @@
import { Button, Grid } from "@mui/material";
-import {
- CalendarPicker,
- PickersDay,
- PickersDayProps,
-} from "@mui/x-date-pickers";
-import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
-import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
-import { Dayjs } from "dayjs";
+import { DateCalendar } from "@mui/x-date-pickers";
+import dayjs, { Dayjs } from "dayjs";
import { ReactElement, useState } from "react";
import { useTranslation } from "react-i18next";
import { Project } from "api/models";
import { LoadingButton } from "components/Buttons";
+import ProjectPickersDay from "components/ProjectSettings/ProjectSchedule/ProjectPickersDay";
interface DateScheduleEditProps {
close: () => void;
@@ -28,23 +23,6 @@ export default function DateScheduleEdit(
);
const { t } = useTranslation();
- // Custom renderer for CalendarPicker
- function customDayRenderer(
- day: Dayjs,
- _selectedDays: Array,
- pickersDayProps: PickersDayProps
- ): ReactElement {
- const date = day.toDate();
- const selected =
- projectSchedule.findIndex(
- (d) =>
- d.getDate() === date.getDate() &&
- d.getMonth() === date.getMonth() &&
- d.getFullYear() === date.getFullYear()
- ) >= 0;
- return ;
- }
-
async function handleSubmit(): Promise {
// update the schedule to the project setting
const updatedSchedule = projectSchedule.map((date) => date.toISOString());
@@ -79,15 +57,20 @@ export default function DateScheduleEdit(
}
return (
-
-
+
-
+
-
+
-
+ >
);
}
diff --git a/src/components/ProjectSettings/ProjectSchedule/DateSelector.tsx b/src/components/ProjectSettings/ProjectSchedule/DateSelector.tsx
index 18df63e7e7..7bcd1ec0e6 100644
--- a/src/components/ProjectSettings/ProjectSchedule/DateSelector.tsx
+++ b/src/components/ProjectSettings/ProjectSchedule/DateSelector.tsx
@@ -1,8 +1,5 @@
import { Button, Grid } from "@mui/material";
-import TextField from "@mui/material/TextField";
-import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
-import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { Dayjs } from "dayjs";
import { enqueueSnackbar } from "notistack";
import { ReactElement, useState } from "react";
@@ -63,22 +60,20 @@ export default function DateSelector(props: DateSelectorProps): ReactElement {
}
return (
-
+ <>
setStartDate(newValue)}
- renderInput={(params) => }
/>
setEndDate(newValue)}
- renderInput={(params) => }
/>
-
+
-
+
-
+ >
);
}
diff --git a/src/components/ProjectSettings/ProjectSchedule/ProjectPickersDay.tsx b/src/components/ProjectSettings/ProjectSchedule/ProjectPickersDay.tsx
new file mode 100644
index 0000000000..8681b71967
--- /dev/null
+++ b/src/components/ProjectSettings/ProjectSchedule/ProjectPickersDay.tsx
@@ -0,0 +1,29 @@
+import { PickersDay, PickersDayProps } from "@mui/x-date-pickers";
+import { Dayjs } from "dayjs";
+import { ReactElement } from "react";
+
+interface ProjectPickersDayProps extends PickersDayProps {
+ days?: Date[];
+}
+
+/** A customized (`@mui/x-date-pickers`) `PickersDay` component.
+ * To select multiple dates in the `DateCalendar` component,
+ * add `ProjectPickersDay` to the `DateCalendar` props as follows:
+ * `slots={{ day: ProjectPickersDay }}`
+ * `slotProps={{ day: { days: } as any }}` */
+export default function ProjectPickersDay(
+ props: ProjectPickersDayProps
+): ReactElement {
+ const { days, ...pickersDayProps } = props;
+ const date = pickersDayProps.day.toDate();
+ const selected = days
+ ? days.findIndex(
+ (d) =>
+ d.getDate() === date.getDate() &&
+ d.getMonth() === date.getMonth() &&
+ d.getFullYear() === date.getFullYear()
+ ) > -1
+ : false;
+
+ return ;
+}
diff --git a/src/components/ProjectSettings/ProjectSchedule/index.tsx b/src/components/ProjectSettings/ProjectSchedule/index.tsx
index 9db44c07c8..8b63e00511 100644
--- a/src/components/ProjectSettings/ProjectSchedule/index.tsx
+++ b/src/components/ProjectSettings/ProjectSchedule/index.tsx
@@ -31,6 +31,10 @@ export default function ProjectSchedule(
const { t } = useTranslation();
+ useEffect(() => {
+ Modal.setAppElement("body");
+ }, []);
+
/** Remove all elements from workshopSchedule in project settings */
async function handleRemoveAll(): Promise {
await props.updateProject({ ...props.project, workshopSchedule: [] });
@@ -135,7 +139,7 @@ export default function ProjectSchedule(
{t("projectSettings.schedule.removeAll")}
-
+
-
+