Delete Calendars (#148)
* [#66] added delete popup and logic * [#66] Homogenize Dialogs components between modify and delete to keep them coherent+ fixed tests * [#66] added tests * [#66] text adjusment * fixup! [#66] added delete popup and logic --------- Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
import { screen, fireEvent, waitFor } from "@testing-library/react";
|
import { screen, fireEvent, waitFor, cleanup } from "@testing-library/react";
|
||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
import CalendarSelection from "../../src/components/Calendar/CalendarSelection";
|
import CalendarSelection from "../../src/components/Calendar/CalendarSelection";
|
||||||
import { renderWithProviders } from "../utils/Renderwithproviders";
|
import { renderWithProviders } from "../utils/Renderwithproviders";
|
||||||
|
import * as calendarThunks from "../../src/features/Calendars/CalendarSlice";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
describe("CalendarSelection", () => {
|
describe("CalendarSelection", () => {
|
||||||
const baseUser = {
|
const baseUser = {
|
||||||
@@ -35,7 +37,10 @@ describe("CalendarSelection", () => {
|
|||||||
ownerEmails: ["charlie@example.com"],
|
ownerEmails: ["charlie@example.com"],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
beforeAll(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
it("renders personal, delegated and other calendars", () => {
|
it("renders personal, delegated and other calendars", () => {
|
||||||
renderWithProviders(
|
renderWithProviders(
|
||||||
<CalendarSelection
|
<CalendarSelection
|
||||||
@@ -101,7 +106,7 @@ describe("CalendarSelection", () => {
|
|||||||
expect(updater(["user1/cal1"])).toEqual([]);
|
expect(updater(["user1/cal1"])).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opens CalendarPopover modal when personal Add button is clicked", () => {
|
it("opens CalendarPopover modal when personal Add button is clicked", async () => {
|
||||||
renderWithProviders(
|
renderWithProviders(
|
||||||
<CalendarSelection
|
<CalendarSelection
|
||||||
selectedCalendars={[]}
|
selectedCalendars={[]}
|
||||||
@@ -114,9 +119,71 @@ describe("CalendarSelection", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const addButtons = screen.getAllByRole("button");
|
const addButtons = screen.getAllByRole("button");
|
||||||
|
fireEvent.click(addButtons[1]);
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByText("Calendar configuration")).toBeInTheDocument()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Navigates to deletion dialog and deletes personnal cal", async () => {
|
||||||
|
const spy = jest
|
||||||
|
.spyOn(calendarThunks, "removeCalendarAsync")
|
||||||
|
.mockImplementation((payload) => {
|
||||||
|
return () => Promise.resolve(payload) as any;
|
||||||
|
});
|
||||||
|
renderWithProviders(
|
||||||
|
<CalendarSelection
|
||||||
|
selectedCalendars={[]}
|
||||||
|
setSelectedCalendars={jest.fn()}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
user: baseUser,
|
||||||
|
calendars: { list: calendarsMock, pending: false },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const addButtons = screen.getAllByTestId("MoreVertIcon");
|
||||||
fireEvent.click(addButtons[0]);
|
fireEvent.click(addButtons[0]);
|
||||||
|
|
||||||
waitFor(() => expect(screen.getByRole("presentation")).toBeInTheDocument());
|
userEvent.click(screen.getByText(/delete/i));
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByText("Remove Calendar personal?")).toBeInTheDocument()
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: /delete/i }));
|
||||||
|
|
||||||
|
await waitFor(() => expect(spy).toHaveBeenCalled());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Navigates to deletion dialog and deletes other cal", async () => {
|
||||||
|
const spy = jest
|
||||||
|
.spyOn(calendarThunks, "removeCalendarAsync")
|
||||||
|
.mockImplementation((payload) => {
|
||||||
|
return () => Promise.resolve(payload) as any;
|
||||||
|
});
|
||||||
|
renderWithProviders(
|
||||||
|
<CalendarSelection
|
||||||
|
selectedCalendars={[]}
|
||||||
|
setSelectedCalendars={jest.fn()}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
user: baseUser,
|
||||||
|
calendars: { list: calendarsMock, pending: false },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const addButtons = screen.getAllByTestId("MoreVertIcon");
|
||||||
|
fireEvent.click(addButtons[1]);
|
||||||
|
|
||||||
|
userEvent.click(screen.getByText(/remove/i));
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByText("Remove Calendar delegated?")).toBeInTheDocument()
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: /remove/i }));
|
||||||
|
|
||||||
|
await waitFor(() => expect(spy).toHaveBeenCalled());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opens CalendarSearch modal when Other Add button is clicked", () => {
|
it("opens CalendarSearch modal when Other Add button is clicked", () => {
|
||||||
@@ -131,10 +198,10 @@ describe("CalendarSelection", () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const addButtons = screen.getAllByRole("button");
|
const addButtons = screen.getAllByTestId("AddIcon");
|
||||||
fireEvent.click(addButtons[1]); // seccond Add button (other)
|
fireEvent.click(addButtons[1]); // seccond Add button (other)
|
||||||
|
|
||||||
expect(screen.getByRole("presentation")).toBeInTheDocument();
|
expect(screen.getByText("Browse other calendars")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders only personal calendars if no delegated/other exist", () => {
|
it("renders only personal calendars if no delegated/other exist", () => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
getCalendars,
|
getCalendars,
|
||||||
postCalendar,
|
postCalendar,
|
||||||
proppatchCalendar,
|
proppatchCalendar,
|
||||||
|
removeCalendar,
|
||||||
} from "../../../src/features/Calendars/CalendarApi";
|
} from "../../../src/features/Calendars/CalendarApi";
|
||||||
import { clientConfig } from "../../../src/features/User/oidcAuth";
|
import { clientConfig } from "../../../src/features/User/oidcAuth";
|
||||||
import { api } from "../../../src/utils/apiUtils";
|
import { api } from "../../../src/utils/apiUtils";
|
||||||
@@ -99,4 +100,16 @@ describe("Calendar API", () => {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("remove Calendar", async () => {
|
||||||
|
const calLink = "/calendars/calId.json";
|
||||||
|
const result = await removeCalendar(calLink);
|
||||||
|
|
||||||
|
expect(api).toHaveBeenCalledWith(`dav${calLink}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json, text/plain, */*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { screen, fireEvent, waitFor } from "@testing-library/react";
|
import { screen, fireEvent, waitFor } from "@testing-library/react";
|
||||||
import CalendarPopover from "../../../src/features/Calendars/CalendarModal";
|
import CalendarPopover from "../../../src/components/Calendar/CalendarModal";
|
||||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||||
import * as eventThunks from "../../../src/features/Calendars/CalendarSlice";
|
import * as eventThunks from "../../../src/features/Calendars/CalendarSlice";
|
||||||
|
|
||||||
@@ -23,11 +23,7 @@ describe("CalendarPopover", () => {
|
|||||||
calendars: { list: {}, pending: true },
|
calendars: { list: {}, pending: true },
|
||||||
};
|
};
|
||||||
renderWithProviders(
|
renderWithProviders(
|
||||||
<CalendarPopover
|
<CalendarPopover open={open} onClose={mockOnClose} />,
|
||||||
anchorEl={document.body}
|
|
||||||
open={open}
|
|
||||||
onClose={mockOnClose}
|
|
||||||
/>,
|
|
||||||
preloadedState
|
preloadedState
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -133,7 +129,6 @@ describe("CalendarPopover (editing mode)", () => {
|
|||||||
it("prefills fields when calendar prop is given", () => {
|
it("prefills fields when calendar prop is given", () => {
|
||||||
renderWithProviders(
|
renderWithProviders(
|
||||||
<CalendarPopover
|
<CalendarPopover
|
||||||
anchorEl={document.body}
|
|
||||||
open={true}
|
open={true}
|
||||||
onClose={mockOnClose}
|
onClose={mockOnClose}
|
||||||
calendar={existingCalendar}
|
calendar={existingCalendar}
|
||||||
@@ -149,14 +144,9 @@ describe("CalendarPopover (editing mode)", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Save button is disabled when name is empty or whitespace only", () => {
|
test("Save button is disabled when name is empty or whitespace only", () => {
|
||||||
renderWithProviders(
|
renderWithProviders(<CalendarPopover open={true} onClose={jest.fn()} />, {
|
||||||
<CalendarPopover
|
user: baseUser,
|
||||||
anchorEl={document.createElement("div")}
|
});
|
||||||
open={true}
|
|
||||||
onClose={jest.fn()}
|
|
||||||
/>,
|
|
||||||
{ user: baseUser }
|
|
||||||
);
|
|
||||||
|
|
||||||
const saveButton = screen.getByRole("button", { name: /save/i });
|
const saveButton = screen.getByRole("button", { name: /save/i });
|
||||||
expect(saveButton).toBeDisabled();
|
expect(saveButton).toBeDisabled();
|
||||||
@@ -180,7 +170,6 @@ describe("CalendarPopover (editing mode)", () => {
|
|||||||
|
|
||||||
renderWithProviders(
|
renderWithProviders(
|
||||||
<CalendarPopover
|
<CalendarPopover
|
||||||
anchorEl={document.body}
|
|
||||||
open={true}
|
open={true}
|
||||||
onClose={mockOnClose}
|
onClose={mockOnClose}
|
||||||
calendar={existingCalendar}
|
calendar={existingCalendar}
|
||||||
|
|||||||
@@ -153,17 +153,24 @@ export default function CalendarApp({
|
|||||||
calendarRange.end,
|
calendarRange.end,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const [prevTempCalendars, setPrevTempCalendars] = useState<string[]>([]);
|
||||||
|
const [prevRangeKey, setPrevRangeKey] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateCalsDetails(
|
updateCalsDetails(
|
||||||
Object.keys(tempcalendars),
|
Object.keys(tempcalendars),
|
||||||
|
prevTempCalendars,
|
||||||
pending,
|
pending,
|
||||||
tempcalendars,
|
|
||||||
rangeKey,
|
rangeKey,
|
||||||
|
prevRangeKey,
|
||||||
dispatch,
|
dispatch,
|
||||||
calendarRange,
|
calendarRange,
|
||||||
"temp"
|
"temp"
|
||||||
);
|
);
|
||||||
}, [rangeKey, Object.keys(tempcalendars).join(",")]);
|
|
||||||
|
setPrevTempCalendars(Object.keys(tempcalendars));
|
||||||
|
setPrevRangeKey(rangeKey);
|
||||||
|
}, [rangeKey, Object.keys(tempcalendars).join(","), pending]);
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
||||||
const [anchorPosition, setAnchorPosition] = useState<{
|
const [anchorPosition, setAnchorPosition] = useState<{
|
||||||
|
|||||||
+29
-41
@@ -2,26 +2,24 @@ import { useEffect, useState } from "react";
|
|||||||
import {
|
import {
|
||||||
createCalendarAsync /*, updateCalendarAsync */,
|
createCalendarAsync /*, updateCalendarAsync */,
|
||||||
patchCalendarAsync,
|
patchCalendarAsync,
|
||||||
} from "./CalendarSlice";
|
} from "../../features/Calendars/CalendarSlice";
|
||||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import {
|
import {
|
||||||
Popover,
|
|
||||||
TextField,
|
TextField,
|
||||||
Button,
|
Button,
|
||||||
Box,
|
Dialog,
|
||||||
Typography,
|
DialogTitle,
|
||||||
ButtonGroup,
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { ColorPicker } from "../../components/Calendar/CalendarColorPicker";
|
import { ColorPicker } from "./CalendarColorPicker";
|
||||||
import { Calendars } from "./CalendarTypes";
|
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
||||||
|
|
||||||
function CalendarPopover({
|
function CalendarPopover({
|
||||||
anchorEl,
|
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
calendar,
|
calendar,
|
||||||
}: {
|
}: {
|
||||||
anchorEl: HTMLElement | null;
|
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: (
|
onClose: (
|
||||||
event: object | null,
|
event: object | null,
|
||||||
@@ -96,21 +94,11 @@ function CalendarPopover({
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Dialog open={open} onClose={(e, reason) => onClose(e, reason)}>
|
||||||
open={open}
|
<DialogTitle style={{ backgroundColor: color }}>
|
||||||
anchorEl={anchorEl}
|
Calendar configuration
|
||||||
onClose={(e, reason) => onClose(e, reason)}
|
</DialogTitle>
|
||||||
anchorOrigin={{ vertical: "center", horizontal: "center" }}
|
<DialogContent>
|
||||||
transformOrigin={{ vertical: "center", horizontal: "center" }}
|
|
||||||
>
|
|
||||||
<Box p={2}>
|
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
gutterBottom
|
|
||||||
style={{ backgroundColor: color }}
|
|
||||||
>
|
|
||||||
Calendar configuration
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
label="Name"
|
label="Name"
|
||||||
@@ -133,24 +121,24 @@ function CalendarPopover({
|
|||||||
onChange={(color) => setColor(color)}
|
onChange={(color) => setColor(color)}
|
||||||
selectedColor={color}
|
selectedColor={color}
|
||||||
/>
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
<Box mt={2} display="flex" justifyContent="flex-end" gap={1}>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={(e) => onClose({}, "backdropClick")}
|
onClick={(e) => onClose({}, "backdropClick")}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={!name.trim()}
|
disabled={!name.trim()}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</DialogActions>
|
||||||
</Box>
|
</Dialog>
|
||||||
</Popover>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,15 +2,19 @@ import Accordion from "@mui/material/Accordion";
|
|||||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import { useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import CalendarPopover from "../../features/Calendars/CalendarModal";
|
import CalendarPopover from "./CalendarModal";
|
||||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import Checkbox from "@mui/material/Checkbox";
|
import Checkbox from "@mui/material/Checkbox";
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
|
import CalendarSearch from "./CalendarSearch";
|
||||||
|
import { Divider, Menu, MenuItem } from "@mui/material";
|
||||||
|
import { removeCalendarAsync } from "../../features/Calendars/CalendarSlice";
|
||||||
|
import { DeleteCalendarDialog } from "./DeleteCalendarDialog";
|
||||||
|
|
||||||
function CalendarAccordion({
|
function CalendarAccordion({
|
||||||
title,
|
title,
|
||||||
@@ -59,21 +63,21 @@ function CalendarAccordion({
|
|||||||
)}
|
)}
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
{calendars.map((id) =>
|
{calendars.map((id) => (
|
||||||
CalendarSelector(
|
<CalendarSelector
|
||||||
allCalendars,
|
key={id}
|
||||||
id,
|
calendars={allCalendars}
|
||||||
selectedCalendars,
|
id={id}
|
||||||
handleToggle,
|
isPersonnal={defaultExpanded}
|
||||||
() => setOpen(id)
|
selectedCalendars={selectedCalendars}
|
||||||
)
|
handleCalendarToggle={handleToggle}
|
||||||
)}
|
setOpen={() => setOpen(id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
|
|
||||||
import CalendarSearch from "./CalendarSearch";
|
|
||||||
|
|
||||||
export default function CalendarSelection({
|
export default function CalendarSelection({
|
||||||
selectedCalendars,
|
selectedCalendars,
|
||||||
@@ -151,7 +155,6 @@ export default function CalendarSelection({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<CalendarPopover
|
<CalendarPopover
|
||||||
anchorEl={anchorElCal}
|
|
||||||
open={Boolean(anchorElCal)}
|
open={Boolean(anchorElCal)}
|
||||||
calendar={calendars[selectedCalId] ?? undefined}
|
calendar={calendars[selectedCalId] ?? undefined}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@@ -173,30 +176,79 @@ export default function CalendarSelection({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CalendarSelector(
|
function CalendarSelector({
|
||||||
calendars: Record<string, Calendars>,
|
calendars,
|
||||||
id: string,
|
id,
|
||||||
selectedCalendars: string[],
|
isPersonnal,
|
||||||
handleCalendarToggle: (name: string) => void,
|
selectedCalendars,
|
||||||
setOpen: Function
|
handleCalendarToggle,
|
||||||
) {
|
setOpen,
|
||||||
|
}: {
|
||||||
|
calendars: Record<string, Calendars>;
|
||||||
|
id: string;
|
||||||
|
isPersonnal: boolean;
|
||||||
|
selectedCalendars: string[];
|
||||||
|
handleCalendarToggle: (name: string) => void;
|
||||||
|
setOpen: Function;
|
||||||
|
}) {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const calLink =
|
||||||
|
useAppSelector((state) => state.calendars.list[id].link) ?? "";
|
||||||
|
|
||||||
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
const [userId, calId] = id.split("/");
|
||||||
|
const isDefault = isPersonnal && userId === calId;
|
||||||
|
|
||||||
|
const [deletePopupOpen, setDeletePopupOpen] = useState(false);
|
||||||
|
const handleDeleteConfirm = () => {
|
||||||
|
dispatch(removeCalendarAsync({ calId: id, calLink }));
|
||||||
|
setDeletePopupOpen(false);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div key={id}>
|
<>
|
||||||
<label>
|
<div>
|
||||||
<Checkbox
|
<label>
|
||||||
sx={{
|
<Checkbox
|
||||||
color: calendars[id].color,
|
sx={{
|
||||||
"&.Mui-checked": { color: calendars[id].color },
|
color: calendars[id].color,
|
||||||
}}
|
"&.Mui-checked": { color: calendars[id].color },
|
||||||
size="small"
|
}}
|
||||||
checked={selectedCalendars.includes(id)}
|
size="small"
|
||||||
onChange={() => handleCalendarToggle(id)}
|
checked={selectedCalendars.includes(id)}
|
||||||
/>
|
onChange={() => handleCalendarToggle(id)}
|
||||||
{calendars[id].name}
|
/>
|
||||||
</label>
|
{calendars[id].name}
|
||||||
<IconButton onClick={() => setOpen()}>
|
</label>
|
||||||
<MoreVertIcon />
|
<IconButton onClick={handleClick}>
|
||||||
</IconButton>
|
<MoreVertIcon />
|
||||||
</div>
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
<Menu id={id} anchorEl={anchorEl} open={open} onClose={handleClose}>
|
||||||
|
<MenuItem onClick={() => setOpen()}>Modify</MenuItem>
|
||||||
|
{!isDefault && <Divider />}
|
||||||
|
{!isDefault && (
|
||||||
|
<MenuItem onClick={() => setDeletePopupOpen(!deletePopupOpen)}>
|
||||||
|
{isPersonnal ? "Delete" : "Remove"}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
<DeleteCalendarDialog
|
||||||
|
deletePopupOpen={deletePopupOpen}
|
||||||
|
setDeletePopupOpen={setDeletePopupOpen}
|
||||||
|
calendars={calendars}
|
||||||
|
id={id}
|
||||||
|
isPersonnal={isPersonnal}
|
||||||
|
handleDeleteConfirm={handleDeleteConfirm}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import Dialog from "@mui/material/Dialog";
|
||||||
|
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
||||||
|
import DialogTitle from "@mui/material/DialogTitle";
|
||||||
|
import DialogContent from "@mui/material/DialogContent";
|
||||||
|
import DialogContentText from "@mui/material/DialogContentText";
|
||||||
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
export function DeleteCalendarDialog({
|
||||||
|
deletePopupOpen,
|
||||||
|
setDeletePopupOpen,
|
||||||
|
calendars,
|
||||||
|
id,
|
||||||
|
isPersonnal,
|
||||||
|
handleDeleteConfirm,
|
||||||
|
}: {
|
||||||
|
deletePopupOpen: boolean;
|
||||||
|
setDeletePopupOpen: (e: boolean) => void;
|
||||||
|
calendars: Record<string, Calendars>;
|
||||||
|
id: string;
|
||||||
|
isPersonnal: boolean;
|
||||||
|
handleDeleteConfirm: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog open={deletePopupOpen} onClose={() => setDeletePopupOpen(false)}>
|
||||||
|
<DialogTitle>Remove {calendars[id].name}?</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
Are you sure you want to remove this calendar?{" "}
|
||||||
|
{isPersonnal
|
||||||
|
? "You will loose all events in this calendar."
|
||||||
|
: "You will loose access to its events. You will still be able to add it back later."}
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => setDeletePopupOpen(false)}>Cancel</Button>
|
||||||
|
<Button onClick={handleDeleteConfirm} variant="contained">
|
||||||
|
{isPersonnal ? "Delete" : "Remove"}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -66,18 +66,35 @@ export const extractEvents = (
|
|||||||
|
|
||||||
export const updateCalsDetails = (
|
export const updateCalsDetails = (
|
||||||
selectedCalendars: string[],
|
selectedCalendars: string[],
|
||||||
|
previousSelectedCalendars: string[],
|
||||||
pending: boolean,
|
pending: boolean,
|
||||||
calendars: Record<string, Calendars>,
|
|
||||||
rangeKey: string,
|
rangeKey: string,
|
||||||
|
previousRangeKey: string,
|
||||||
dispatch: Function,
|
dispatch: Function,
|
||||||
calendarRange: { start: Date; end: Date },
|
calendarRange: { start: Date; end: Date },
|
||||||
calType?: "temp"
|
calType?: "temp"
|
||||||
) => {
|
) => {
|
||||||
selectedCalendars.forEach((id) => {
|
if (pending || !rangeKey) return;
|
||||||
if (Object.keys(calendars[id].events).length > 0) {
|
|
||||||
return;
|
const newCalendars = selectedCalendars.filter(
|
||||||
}
|
(id) => !previousSelectedCalendars.includes(id)
|
||||||
if (!pending && rangeKey) {
|
);
|
||||||
|
|
||||||
|
newCalendars.forEach((id) => {
|
||||||
|
dispatch(
|
||||||
|
getCalendarDetailAsync({
|
||||||
|
calId: id,
|
||||||
|
match: {
|
||||||
|
start: formatDateToYYYYMMDDTHHMMSS(calendarRange.start),
|
||||||
|
end: formatDateToYYYYMMDDTHHMMSS(calendarRange.end),
|
||||||
|
},
|
||||||
|
calType,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (rangeKey !== previousRangeKey) {
|
||||||
|
selectedCalendars.forEach((id) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
getCalendarDetailAsync({
|
getCalendarDetailAsync({
|
||||||
calId: id,
|
calId: id,
|
||||||
@@ -88,6 +105,6 @@ export const updateCalsDetails = (
|
|||||||
calType,
|
calType,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -102,3 +102,13 @@ export async function proppatchCalendar(
|
|||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeCalendar(calLink: string) {
|
||||||
|
const response = await api(`dav${calLink}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json, text/plain, */*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
getCalendars,
|
getCalendars,
|
||||||
postCalendar,
|
postCalendar,
|
||||||
proppatchCalendar,
|
proppatchCalendar,
|
||||||
|
removeCalendar,
|
||||||
} from "./CalendarApi";
|
} from "./CalendarApi";
|
||||||
import { getOpenPaasUser, getUserDetails } from "../User/userAPI";
|
import { getOpenPaasUser, getUserDetails } from "../User/userAPI";
|
||||||
import { parseCalendarEvent } from "../Events/eventUtils";
|
import { parseCalendarEvent } from "../Events/eventUtils";
|
||||||
@@ -191,6 +192,22 @@ export const patchCalendarAsync = createAsyncThunk<
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const removeCalendarAsync = createAsyncThunk<
|
||||||
|
{
|
||||||
|
calId: string;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
calId: string;
|
||||||
|
calLink: string;
|
||||||
|
}
|
||||||
|
>("calendars/removeCalendar", async ({ calId, calLink }) => {
|
||||||
|
const response = await removeCalendar(calLink);
|
||||||
|
return {
|
||||||
|
calId,
|
||||||
|
calLink,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
export const moveEventAsync = createAsyncThunk<
|
export const moveEventAsync = createAsyncThunk<
|
||||||
{ calId: string; events: CalendarEvent[] }, // Return type
|
{ calId: string; events: CalendarEvent[] }, // Return type
|
||||||
{ cal: Calendars; newEvent: CalendarEvent; newURL: string } // Arg type
|
{ cal: Calendars; newEvent: CalendarEvent; newURL: string } // Arg type
|
||||||
@@ -258,6 +275,7 @@ export const addSharedCalendarAsync = createAsyncThunk<
|
|||||||
{
|
{
|
||||||
calId: string;
|
calId: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
link: string;
|
||||||
name: string;
|
name: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
@@ -278,6 +296,7 @@ export const addSharedCalendarAsync = createAsyncThunk<
|
|||||||
.replace("/calendars/", "")
|
.replace("/calendars/", "")
|
||||||
.replace(".json", ""),
|
.replace(".json", ""),
|
||||||
color: cal.cal["apple:color"],
|
color: cal.cal["apple:color"],
|
||||||
|
link: `/calendars/${userId}/${calId}.json`,
|
||||||
desc: cal.cal["caldav:description"],
|
desc: cal.cal["caldav:description"],
|
||||||
name: cal.cal["dav:name"],
|
name: cal.cal["dav:name"],
|
||||||
owner: `${ownerData.firstname ? `${ownerData.firstname} ` : ""}${
|
owner: `${ownerData.firstname ? `${ownerData.firstname} ` : ""}${
|
||||||
@@ -527,6 +546,7 @@ const CalendarSlice = createSlice({
|
|||||||
state.list[action.payload.calId] = {
|
state.list[action.payload.calId] = {
|
||||||
color: action.payload.color,
|
color: action.payload.color,
|
||||||
id: action.payload.calId,
|
id: action.payload.calId,
|
||||||
|
link: action.payload.link,
|
||||||
description: action.payload.desc,
|
description: action.payload.desc,
|
||||||
name: action.payload.name,
|
name: action.payload.name,
|
||||||
events: {},
|
events: {},
|
||||||
@@ -534,6 +554,10 @@ const CalendarSlice = createSlice({
|
|||||||
ownerEmails: action.payload.ownerEmails,
|
ownerEmails: action.payload.ownerEmails,
|
||||||
} as Calendars;
|
} as Calendars;
|
||||||
})
|
})
|
||||||
|
.addCase(removeCalendarAsync.fulfilled, (state, action) => {
|
||||||
|
state.pending = false;
|
||||||
|
delete state.list[action.payload.calId];
|
||||||
|
})
|
||||||
.addCase(getCalendarDetailAsync.pending, (state) => {
|
.addCase(getCalendarDetailAsync.pending, (state) => {
|
||||||
state.pending = true;
|
state.pending = true;
|
||||||
})
|
})
|
||||||
@@ -563,6 +587,9 @@ const CalendarSlice = createSlice({
|
|||||||
})
|
})
|
||||||
.addCase(addSharedCalendarAsync.pending, (state) => {
|
.addCase(addSharedCalendarAsync.pending, (state) => {
|
||||||
state.pending = true;
|
state.pending = true;
|
||||||
|
})
|
||||||
|
.addCase(removeCalendarAsync.pending, (state) => {
|
||||||
|
state.pending = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user