@backstage/plugin-microsoft-calendar
Version:
685 lines (670 loc) • 23.5 kB
JavaScript
import React, { useState, useCallback } from 'react';
import { useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { sortBy } from 'lodash';
import { DateTime } from 'luxon';
import { Link, InfoCard, Progress } from '@backstage/core-components';
import Box from '@material-ui/core/Box';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import PrevIcon from '@material-ui/icons/NavigateBefore';
import NextIcon from '@material-ui/icons/NavigateNext';
import { useApi, errorApiRef, microsoftAuthApiRef } from '@backstage/core-plugin-api';
import { microsoftCalendarApiRef } from '../index.esm.js';
import calendarIcon from '../icons/calendar.svg';
import classnames from 'classnames';
import { usePopupState, bindTrigger, bindPopover } from 'material-ui-popup-state/hooks';
import Paper from '@material-ui/core/Paper';
import Popover from '@material-ui/core/Popover';
import Tooltip from '@material-ui/core/Tooltip';
import { makeStyles, styled } from '@material-ui/core/styles';
import webcamIcon from '../icons/webcam.svg';
import DOMPurify from 'dompurify';
import Divider from '@material-ui/core/Divider';
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
import Badge from '@material-ui/core/Badge';
import Chip from '@material-ui/core/Chip';
import CancelIcon from '@material-ui/icons/Cancel';
import CheckIcon from '@material-ui/icons/CheckCircle';
import Checkbox from '@material-ui/core/Checkbox';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import ListItemText from '@material-ui/core/ListItemText';
import MenuItem from '@material-ui/core/MenuItem';
import Select from '@material-ui/core/Select';
import Button from '@material-ui/core/Button';
import useAsync from 'react-use/esm/useAsync';
import '@backstage/errors';
const ResponseStatusMap = {
needsAction: "none",
accepted: "accepted",
declined: "declined",
maybe: "tentativelyAccepted",
notResponded: "notResponded"
};
const useCalendarsQuery = ({ enabled }) => {
const calendarApi = useApi(microsoftCalendarApiRef);
const errorApi = useApi(errorApiRef);
return useQuery(
["calendars-list"],
async () => {
const calendars = [];
const value = await calendarApi.getCalendars();
calendars.push(...value);
return calendars;
},
{
enabled,
// old data will be returned if last request was made within 3 mins.
staleTime: 18e4,
refetchInterval: 36e5,
onError: () => {
errorApi.post({
name: "API error",
message: "Failed to fetch calendars."
});
}
}
);
};
const useEventsQuery = ({
calendarId,
enabled,
timeMin,
timeMax,
timeZone
}) => {
const calendarApi = useApi(microsoftCalendarApiRef);
return useQuery(
["calendarEvents", calendarId, timeMin, timeMax, timeZone],
async () => {
const data = await calendarApi.getEvents(
calendarId,
{
startDateTime: timeMin,
endDateTime: timeMax
},
{
Prefer: `outlook.timezone="${timeZone}"`
}
);
return data;
},
{
cacheTime: 3e5,
enabled,
refetchInterval: 6e4,
refetchIntervalInBackground: true
}
);
};
const useSignIn = () => {
const [isSignedIn, setSignedIn] = useState(false);
const [isInitialized, setInitialized] = useState(false);
const authApi = useApi(microsoftAuthApiRef);
const signIn = useCallback(
async (optional = false) => {
const token = await authApi.getAccessToken("Calendars.Read", {
optional,
instantPopup: !optional
});
setSignedIn(!!token);
setInitialized(true);
},
[authApi, setSignedIn]
);
return { isSignedIn, isInitialized, signIn };
};
const useStyles$3 = makeStyles((theme) => {
const getIconColor = (responseStatus) => {
if (!responseStatus)
return theme.palette.primary.light;
return {
[ResponseStatusMap.accepted]: theme.palette.status.ok,
[ResponseStatusMap.declined]: theme.palette.status.error
}[responseStatus];
};
return {
responseStatus: {
color: ({ responseStatus }) => getIconColor(responseStatus)
},
badge: {
right: 10,
top: 5,
"& svg": {
height: 16,
width: 16,
background: theme.palette.common.white
}
}
};
});
const ResponseIcon = ({ responseStatus }) => {
if (responseStatus === ResponseStatusMap.accepted) {
return /* @__PURE__ */ React.createElement(CheckIcon, { "data-testid": "accepted-icon" });
}
if (responseStatus === ResponseStatusMap.declined) {
return /* @__PURE__ */ React.createElement(CancelIcon, { "data-testid": "declined-icon" });
}
return null;
};
const AttendeeChip = ({ user }) => {
var _a, _b, _c;
const classes = useStyles$3({ responseStatus: ((_a = user.status) == null ? void 0 : _a.response) || "" });
return /* @__PURE__ */ React.createElement(
Badge,
{
classes: {
root: classes.responseStatus,
badge: classes.badge
},
badgeContent: /* @__PURE__ */ React.createElement(ResponseIcon, { responseStatus: ((_b = user.status) == null ? void 0 : _b.response) || "" })
},
/* @__PURE__ */ React.createElement(
Chip,
{
size: "small",
variant: "outlined",
label: (_c = user.emailAddress) == null ? void 0 : _c.address,
color: "primary"
}
)
);
};
function getOnlineMeetingLink(event) {
var _a;
const onlineEntrypoint = ((_a = event.onlineMeeting) == null ? void 0 : _a.joinUrl) || event.onlineMeetingUrl;
if (onlineEntrypoint) {
return onlineEntrypoint;
}
return "";
}
function getTimePeriod(event) {
var _a, _b;
if (isAllDay(event)) {
return getAllDayTimePeriod(event);
}
const format = {
hour: "2-digit",
minute: "2-digit"
};
const startTime = DateTime.fromISO(((_a = event.start) == null ? void 0 : _a.dateTime) || "");
const endTime = DateTime.fromISO(((_b = event.end) == null ? void 0 : _b.dateTime) || "");
return `${startTime.toLocaleString(format)} - ${endTime.toLocaleString(
format
)}`;
}
function getAllDayTimePeriod(event) {
var _a, _b;
const format = { month: "long", day: "numeric" };
const startTime = DateTime.fromISO(((_a = event.start) == null ? void 0 : _a.dateTime) || "");
const endTime = DateTime.fromISO(((_b = event.end) == null ? void 0 : _b.dateTime) || "").minus({ day: 1 });
if (startTime.toISO() === endTime.toISO()) {
return startTime.toLocaleString(format);
}
return `${startTime.toLocaleString(format)} - ${endTime.toLocaleString(
format
)}`;
}
function isPassed(event) {
var _a, _b;
if (!((_a = event.end) == null ? void 0 : _a.dateTime))
return false;
const eventDate = DateTime.fromISO((_b = event.end) == null ? void 0 : _b.dateTime);
return DateTime.now() >= eventDate;
}
function isAllDay(event) {
var _a, _b;
const startTime = DateTime.fromISO(((_a = event.start) == null ? void 0 : _a.dateTime) || "");
const endTime = DateTime.fromISO(((_b = event.end) == null ? void 0 : _b.dateTime) || "");
return endTime.diff(startTime, "day").days >= 1;
}
function getStartDate(event) {
var _a;
return DateTime.fromISO(((_a = event.start) == null ? void 0 : _a.dateTime) || "");
}
const useStyles$2 = makeStyles(
(theme) => ({
description: {
wordBreak: "break-word",
"& a": {
color: theme.palette.primary.main,
fontWeight: 500
}
},
divider: {
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2)
}
}),
{
name: "MicrosoftCalendarEventPopoverContent"
}
);
const CalendarEventPopoverContent = ({
event
}) => {
const classes = useStyles$2();
const onlineMeetingLink = getOnlineMeetingLink(event);
return /* @__PURE__ */ React.createElement(Box, { display: "flex", flexDirection: "column", width: 400, p: 2 }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(Box, { flex: 1 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, event.subject), /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2" }, getTimePeriod(event))), event.webLink && /* @__PURE__ */ React.createElement(Tooltip, { title: "Open in Calendar" }, /* @__PURE__ */ React.createElement(
Link,
{
"data-testid": "open-calendar-link",
to: event.webLink,
onClick: (_e) => {
},
noTrack: true
},
/* @__PURE__ */ React.createElement(IconButton, null, /* @__PURE__ */ React.createElement(ArrowForwardIcon, null))
))), onlineMeetingLink && /* @__PURE__ */ React.createElement(Link, { to: onlineMeetingLink, onClick: (_e) => {
}, noTrack: true }, "Join Online Meeting"), event.bodyPreview && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Divider, { className: classes.divider, variant: "fullWidth" }), /* @__PURE__ */ React.createElement(
Box,
{
className: classes.description,
dangerouslySetInnerHTML: {
__html: DOMPurify.sanitize(
event.body && event.body.content || "",
{
USE_PROFILES: { html: true }
}
)
}
}
)), event.attendees && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Divider, { className: classes.divider, variant: "fullWidth" }), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2" }, "Attendees"), /* @__PURE__ */ React.createElement(Box, { mb: 1 }), sortBy(event.attendees || [], "emailAddress").map((user) => {
var _a;
return /* @__PURE__ */ React.createElement(
AttendeeChip,
{
key: ((_a = user.emailAddress) == null ? void 0 : _a.address) || "",
user
}
);
}))));
};
const useStyles$1 = makeStyles(
(theme) => ({
event: {
display: "flex",
alignItems: "center",
marginBottom: theme.spacing(1),
cursor: "pointer",
paddingRight: 12
},
declined: {
textDecoration: "line-through"
},
passed: {
opacity: 0.6,
transition: "opacity 0.15s ease-in-out",
"&:hover": {
opacity: 1
}
},
link: {
width: 48,
height: 48,
display: "inline-block",
padding: 8,
borderRadius: "50%",
"&:hover": {
backgroundColor: theme.palette.grey[100]
}
},
calendarColor: {
width: 8,
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4
}
}),
{
name: "MicrosoftCalendarEvent"
}
);
const CalendarEvent = ({ event }) => {
const classes = useStyles$1();
const popoverState = usePopupState({
variant: "popover",
popupId: event.id,
disableAutoFocus: true
});
const [hovered, setHovered] = useState(false);
const onlineMeetingLink = getOnlineMeetingLink(event);
const { onClick, ...restBindProps } = bindTrigger(popoverState);
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
Paper,
{
onClick: (e) => {
onClick(e);
},
...restBindProps,
onMouseEnter: () => setHovered(true),
onMouseLeave: () => setHovered(false),
elevation: hovered ? 4 : 1,
className: classnames(classes.event, {
[classes.passed]: isPassed(event)
}),
"data-testid": "microsoft-calendar-event"
},
/* @__PURE__ */ React.createElement(Box, { className: classes.calendarColor, mr: 1, alignSelf: "stretch" }),
/* @__PURE__ */ React.createElement(Box, { flex: 1, pt: 1, pb: 1 }, /* @__PURE__ */ React.createElement(
Typography,
{
variant: "subtitle2",
className: classnames({ [classes.declined]: event.isCancelled })
},
event.subject
), !isAllDay(event) && /* @__PURE__ */ React.createElement(Typography, { variant: "body2", "data-testid": "calendar-event-time" }, getTimePeriod(event))),
event.isOnlineMeeting && /* @__PURE__ */ React.createElement(Tooltip, { title: "Join Online Meeting" }, /* @__PURE__ */ React.createElement(
Link,
{
"data-testid": "calendar-event-online-meeting-link",
className: classes.link,
to: onlineMeetingLink,
onClick: (e) => {
e.stopPropagation();
},
noTrack: true
},
/* @__PURE__ */ React.createElement(
"img",
{
height: 32,
width: 32,
src: webcamIcon,
alt: "Online Meeting link"
}
)
))
), /* @__PURE__ */ React.createElement(
Popover,
{
...bindPopover(popoverState),
anchorOrigin: {
vertical: "top",
horizontal: "left"
},
transformOrigin: {
vertical: "top",
horizontal: "center"
},
"data-testid": "calendar-event-popover"
},
/* @__PURE__ */ React.createElement(CalendarEventPopoverContent, { event })
));
};
const useStyles = makeStyles(
{
formControl: {
width: 120
},
selectedCalendars: {
textOverflow: "ellipsis",
overflow: "hidden"
}
},
{
name: "MicrosoftCalendarSelect"
}
);
const CalendarSelect = ({
disabled,
selectedCalendarId,
setSelectedCalendarId,
calendars
}) => {
const classes = useStyles();
return /* @__PURE__ */ React.createElement(FormControl, { className: classes.formControl }, /* @__PURE__ */ React.createElement(
Select,
{
labelId: "calendars-label",
disabled: disabled || calendars.length === 0,
value: selectedCalendarId || "",
onChange: async (e) => setSelectedCalendarId(e.target.value),
input: /* @__PURE__ */ React.createElement(Input, null),
renderValue: (selected) => {
var _a;
return /* @__PURE__ */ React.createElement(Typography, { className: classes.selectedCalendars, variant: "body2" }, (_a = calendars.find((c) => c.id === selected)) == null ? void 0 : _a.name);
},
MenuProps: {
PaperProps: {
style: {
width: 350
}
}
}
},
sortBy(calendars, "name").map((c) => /* @__PURE__ */ React.createElement(MenuItem, { key: c.id, value: c.id }, /* @__PURE__ */ React.createElement(Checkbox, { checked: c.id === selectedCalendarId }), /* @__PURE__ */ React.createElement(ListItemText, { primary: c.name })))
));
};
var mockEvents = [
{
id: "AAMkADQyMDdlODU1LTk4Y2ItNGFmYi04YzYyLWQ4NmEzMmRhZTY5MAFRAAgI2vdUnGxAAEYAAAAABZfL1rFDfUO0G4cMgXCC1gcApFI7gxQldkOLWHDOlgt60AAAAAABDQAApFI7gxQldkOLWHDOlgt60AAAYl_I0AAAEA==",
createdDateTime: "2022-12-22T08:28:36.6131501Z",
lastModifiedDateTime: "2023-01-20T05:24:31.9666518Z",
changeKey: "pFI7gxQldkOLWHDOlgt60AAAd5KhaA==",
categories: [
],
transactionId: null,
originalStartTimeZone: "India Standard Time",
originalEndTimeZone: "India Standard Time",
iCalUId: "040000008200E00074C5B7101A82E00807E7011006D3A5A92DC7D80100000000000000001000000002BB709705E28C4493E46124FF07DD89",
reminderMinutesBeforeStart: 15,
isReminderOn: true,
hasAttachments: false,
subject: "Abhay Soni",
bodyPreview: "",
importance: "normal",
sensitivity: "normal",
isAllDay: false,
isCancelled: false,
isOrganizer: false,
responseRequested: true,
seriesMasterId: "AAMkADQyMDdlODU1LTk4Y2ItNGFmYi04YzYyLWQ4NmEzMmRhZTY5MABGAAAAAAAFl8vWsUN9Q7QbhwyBcILWBwCkUjuDFCV2Q4tYcM6WC3rQAAAAAAENAACkUjuDFCV2Q4tYcM6WC3rQAABiX4jQAAA=",
showAs: "tentative",
type: "occurrence",
webLink: "",
onlineMeetingUrl: "",
isOnlineMeeting: true,
onlineMeetingProvider: "teamsForBusiness",
allowNewTimeProposals: true,
occurrenceId: "OID.AAMkADQyMDdlODU1LTk4Y2ItNGFmYi04YzYyLWQ4NmEzMmRhZTY5MABGAAAAAAAFl8vWsUN9Q7QbhwyBcILWBwCkUjuDFCV2Q4tYcM6WC3rQAAAAAAENAACkUjuDFCV2Q4tYcM6WC3rQAABiX4jQAAA=.2023-01-16",
isDraft: false,
hideAttendees: false,
responseStatus: {
response: "notResponded",
time: "0001-01-01T00:00:00Z"
},
body: {
contentType: "html",
content: ""
},
start: {
dateTime: "2023-01-16T11:00:00.0000000",
timeZone: "Asia/Calcutta"
},
end: {
dateTime: "2023-01-16T11:30:00.0000000",
timeZone: "Asia/Calcutta"
},
location: {
displayName: "",
locationType: "default",
uniqueIdType: "unknown",
address: {
},
coordinates: {
}
},
locations: [
],
recurrence: null,
attendees: [
],
organizer: {
emailAddress: {
name: "Abhay Soni",
address: "abhaysoni.developer@gmail.com"
}
},
onlineMeeting: {
joinUrl: "https://abhay-soni-developer.github.io/MyReusme/"
}
},
{
id: "AAMkADQyMDdlODU1LTk4Y2ItNGFmYi04YzYyLWQ4NmEzMmRhZTY5MAFRAAgI2vdUnGxAAEYAAAAABZfL1rFDfUO0G4cMgXCC1gcApFI7gxQldkOLWHDOlgt60AAAAAABDQAApFI7gxQldkOLWHDOlgt60AAAYl_I0AAAEA==",
createdDateTime: "2022-12-22T08:28:36.6131501Z",
lastModifiedDateTime: "2023-01-20T05:24:31.9666518Z",
changeKey: "pFI7gxQldkOLWHDOlgt60AAAd5KhaA==",
categories: [
],
transactionId: null,
originalStartTimeZone: "India Standard Time",
originalEndTimeZone: "India Standard Time",
iCalUId: "040000008200E00074C5B7101A82E00807E7011006D3A5A92DC7D80100000000000000001000000002BB709705E28C4493E46124FF07DD89",
reminderMinutesBeforeStart: 15,
isReminderOn: true,
hasAttachments: false,
subject: "https://abhay-soni-developer.github.io/MyReusme/",
bodyPreview: "",
importance: "normal",
sensitivity: "normal",
isAllDay: false,
isCancelled: false,
isOrganizer: false,
responseRequested: true,
seriesMasterId: "AAMkADQyMDdlODU1LTk4Y2ItNGFmYi04YzYyLWQ4NmEzMmRhZTY5MABGAAAAAAAFl8vWsUN9Q7QbhwyBcILWBwCkUjuDFCV2Q4tYcM6WC3rQAAAAAAENAACkUjuDFCV2Q4tYcM6WC3rQAABiX4jQAAA=",
showAs: "tentative",
type: "occurrence",
webLink: "",
onlineMeetingUrl: "",
isOnlineMeeting: true,
onlineMeetingProvider: "teamsForBusiness",
allowNewTimeProposals: true,
occurrenceId: "OID.AAMkADQyMDdlODU1LTk4Y2ItNGFmYi04YzYyLWQ4NmEzMmRhZTY5MABGAAAAAAAFl8vWsUN9Q7QbhwyBcILWBwCkUjuDFCV2Q4tYcM6WC3rQAAAAAAENAACkUjuDFCV2Q4tYcM6WC3rQAABiX4jQAAA=.2023-01-16",
isDraft: false,
hideAttendees: false,
responseStatus: {
response: "notResponded",
time: "0001-01-01T00:00:00Z"
},
body: {
contentType: "html",
content: ""
},
start: {
dateTime: "2023-01-16T11:00:00.0000000",
timeZone: "Asia/Calcutta"
},
end: {
dateTime: "2023-01-16T11:30:00.0000000",
timeZone: "Asia/Calcutta"
},
location: {
displayName: "",
locationType: "default",
uniqueIdType: "unknown",
address: {
},
coordinates: {
}
},
locations: [
],
recurrence: null,
attendees: [
],
organizer: {
emailAddress: {
name: "Abhay Soni",
address: "abhaysoni.developer@gmail.com"
}
},
onlineMeeting: {
joinUrl: "https://abhay-soni-developer.github.io/MyReusme/"
}
}
];
const TransparentBox = styled(Box)({
opacity: 0.3,
filter: "blur(1.5px)"
});
const SignInContent = ({ handleAuthClick }) => {
return /* @__PURE__ */ React.createElement(Box, { position: "relative", height: "100%", width: "100%" }, /* @__PURE__ */ React.createElement(TransparentBox, { p: 1 }, mockEvents.map((event) => /* @__PURE__ */ React.createElement(CalendarEvent, { key: event.id, event }))), /* @__PURE__ */ React.createElement(
Box,
{
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "absolute",
left: 0,
top: 0
},
/* @__PURE__ */ React.createElement(
Button,
{
variant: "contained",
color: "primary",
onClick: handleAuthClick,
size: "large"
},
"Sign in"
)
));
};
const CalendarCard = () => {
var _a, _b;
const [date, setDate] = useState(DateTime.now());
const [selectedCalendarId, setSelectedCalendarId] = useState("");
const changeDay = (offset = 1) => {
setDate((prev) => prev.plus({ day: offset }));
};
const { isSignedIn, isInitialized, signIn } = useSignIn();
useAsync(async () => signIn(true), [signIn]);
const {
isLoading: isCalendarLoading,
isFetching: isCalendarFetching,
data: calendars = []
} = useCalendarsQuery({
enabled: isSignedIn
});
const defaultCalendarId = (_a = calendars.find((c) => c.isDefaultCalendar)) == null ? void 0 : _a.id;
const { data: events, isLoading: isEventLoading } = useEventsQuery({
calendarId: selectedCalendarId || defaultCalendarId || "",
enabled: isSignedIn && calendars.length > 0,
timeMin: date.startOf("day").toISO(),
timeMax: date.endOf("day").toISO(),
timeZone: (_b = date.zoneName) != null ? _b : void 0
});
const showLoader = isCalendarLoading && isCalendarFetching || isEventLoading || !isInitialized;
return /* @__PURE__ */ React.createElement(
InfoCard,
{
noPadding: true,
title: /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(Box, { height: 32, width: 32, mr: 1 }, /* @__PURE__ */ React.createElement("img", { src: calendarIcon, alt: "Microsoft Calendar" })), isSignedIn ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(IconButton, { onClick: () => changeDay(-1), size: "small" }, /* @__PURE__ */ React.createElement(PrevIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: () => changeDay(1), size: "small" }, /* @__PURE__ */ React.createElement(NextIcon, null)), /* @__PURE__ */ React.createElement(Box, { mr: 0.5 }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, date.toLocaleString({
weekday: "short",
month: "short",
day: "numeric"
})), /* @__PURE__ */ React.createElement(Box, { flex: 1 }), /* @__PURE__ */ React.createElement(
CalendarSelect,
{
calendars,
selectedCalendarId: selectedCalendarId || defaultCalendarId,
setSelectedCalendarId,
disabled: isCalendarFetching && isCalendarLoading || !isSignedIn
}
)) : /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, "Agenda")),
deepLink: {
link: "https://outlook.office.com/calendar/",
title: "Go to Calendar"
}
},
/* @__PURE__ */ React.createElement(Box, null, showLoader && /* @__PURE__ */ React.createElement(Box, { py: 2 }, /* @__PURE__ */ React.createElement(Progress, { variant: "query" })), !isSignedIn && isInitialized && /* @__PURE__ */ React.createElement(SignInContent, { handleAuthClick: () => signIn(false) }), !isEventLoading && !isCalendarLoading && isSignedIn && /* @__PURE__ */ React.createElement(Box, { p: 1, pb: 0, maxHeight: 602, overflow: "auto" }, (events == null ? void 0 : events.length) === 0 && /* @__PURE__ */ React.createElement(Box, { pt: 2, pb: 2 }, /* @__PURE__ */ React.createElement(Typography, { align: "center", variant: "h6" }, "No events")), sortBy(events, [getStartDate]).map((event) => /* @__PURE__ */ React.createElement(CalendarEvent, { key: `${event.id}`, event }))))
);
};
const queryClient = new QueryClient();
const MicrosoftCalendar = () => {
return /* @__PURE__ */ React.createElement(QueryClientProvider, { client: queryClient }, /* @__PURE__ */ React.createElement(CalendarCard, null));
};
export { MicrosoftCalendar };
//# sourceMappingURL=index-DGcR6krL.esm.js.map