update cal details (#113)
* [#65] added button to access cal details * [#65] added proppatch call * [#65] added tests * fixup! [#65] added proppatch call * [#65] added trimming to name and desc * fixup! [#65] added proppatch call * fixup! [#65] added proppatch call * fixup! [#65] added proppatch call --------- Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -51,3 +51,27 @@ export async function postCalendar(
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function proppatchCalendar(
|
||||
calLink: string,
|
||||
patch: { name: string; desc: string; color: string }
|
||||
) {
|
||||
const body: Record<string, string> = {};
|
||||
if (patch.name) {
|
||||
body["dav:name"] = patch.name;
|
||||
}
|
||||
if (patch.desc) {
|
||||
body["caldav:description"] = patch.desc;
|
||||
}
|
||||
if (patch.color) {
|
||||
body["apple:color"] = patch.color;
|
||||
}
|
||||
const response = await api(`dav${calLink}`, {
|
||||
method: "PROPPATCH",
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState } from "react";
|
||||
import { createCalendar, createCalendarAsync } from "./CalendarSlice";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
createCalendarAsync /*, updateCalendarAsync */,
|
||||
patchCalendarAsync,
|
||||
} from "./CalendarSlice";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import {
|
||||
Popover,
|
||||
@@ -7,38 +10,76 @@ import {
|
||||
Button,
|
||||
Box,
|
||||
Typography,
|
||||
Select,
|
||||
ButtonGroup,
|
||||
} from "@mui/material";
|
||||
import { Calendars } from "./CalendarTypes";
|
||||
|
||||
function CalendarPopover({
|
||||
anchorEl,
|
||||
open,
|
||||
onClose,
|
||||
calendar,
|
||||
}: {
|
||||
anchorEl: HTMLElement | null;
|
||||
open: boolean;
|
||||
onClose: (Calendar: {}, reason: "backdropClick" | "escapeKeyDown") => void;
|
||||
onClose: (
|
||||
event: object | null,
|
||||
reason: "backdropClick" | "escapeKeyDown"
|
||||
) => void;
|
||||
calendar?: Calendars;
|
||||
}) {
|
||||
const dispatch = useAppDispatch();
|
||||
const userId =
|
||||
useAppSelector((state) => state.user.userData.openpaasId) ?? "";
|
||||
const [name, setName] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [color, setColor] = useState("");
|
||||
const handleSave = () => {
|
||||
const calId = crypto.randomUUID();
|
||||
if (name) {
|
||||
dispatch(
|
||||
createCalendarAsync({ name, desc: description, color, userId, calId })
|
||||
);
|
||||
onClose({}, "backdropClick");
|
||||
const [name, setName] = useState(calendar?.name ?? "");
|
||||
const [description, setDescription] = useState(calendar?.description ?? "");
|
||||
const [color, setColor] = useState(calendar?.color ?? "");
|
||||
|
||||
// Reset
|
||||
setName("");
|
||||
setDescription("");
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
if (calendar) {
|
||||
setName(calendar.name);
|
||||
setDescription(calendar.description ?? "");
|
||||
setColor(calendar.color ?? "");
|
||||
} else {
|
||||
setName("");
|
||||
setDescription("");
|
||||
setColor("");
|
||||
}
|
||||
}
|
||||
}, [calendar, open]);
|
||||
|
||||
const handleSave = () => {
|
||||
const trimmedName = name.trim();
|
||||
const trimmedDesc = description.trim();
|
||||
|
||||
if (trimmedName) {
|
||||
const calId = calendar ? calendar.id : crypto.randomUUID();
|
||||
|
||||
if (calendar?.id) {
|
||||
dispatch(
|
||||
patchCalendarAsync({
|
||||
calId: calendar.id,
|
||||
calLink: calendar.link,
|
||||
patch: { name: trimmedName, desc: trimmedDesc, color },
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(
|
||||
createCalendarAsync({
|
||||
name: trimmedName,
|
||||
desc: trimmedDesc,
|
||||
color,
|
||||
userId,
|
||||
calId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
onClose({}, "backdropClick");
|
||||
}
|
||||
};
|
||||
|
||||
const palette = [
|
||||
"#D50000",
|
||||
"#E67C73",
|
||||
@@ -52,19 +93,14 @@ function CalendarPopover({
|
||||
"#8E24AA",
|
||||
"#616161",
|
||||
];
|
||||
|
||||
return (
|
||||
<Popover
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={onClose}
|
||||
anchorOrigin={{
|
||||
vertical: "center",
|
||||
horizontal: "center",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "center",
|
||||
horizontal: "center",
|
||||
}}
|
||||
onClose={(e, reason) => onClose(e, reason)}
|
||||
anchorOrigin={{ vertical: "center", horizontal: "center" }}
|
||||
transformOrigin={{ vertical: "center", horizontal: "center" }}
|
||||
>
|
||||
<Box p={2}>
|
||||
<Typography
|
||||
@@ -93,22 +129,23 @@ function CalendarPopover({
|
||||
rows={2}
|
||||
/>
|
||||
<ButtonGroup>
|
||||
{palette.map((color) => (
|
||||
{palette.map((c) => (
|
||||
<Button
|
||||
style={{ backgroundColor: color }}
|
||||
onClick={() => setColor(color)}
|
||||
key={c}
|
||||
style={{ backgroundColor: c }}
|
||||
onClick={() => setColor(c)}
|
||||
/>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
<Box mt={2} display="flex" justifyContent="flex-end" gap={1}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => onClose({}, "backdropClick")}
|
||||
onClick={(e) => onClose({}, "backdropClick")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={name && name !== "" ? false : true}
|
||||
disabled={!name.trim()}
|
||||
variant="contained"
|
||||
onClick={handleSave}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { Calendars } from "./CalendarTypes";
|
||||
import { CalendarEvent } from "../Events/EventsTypes";
|
||||
import { getCalendar, getCalendars, postCalendar } from "./CalendarApi";
|
||||
import {
|
||||
getCalendar,
|
||||
getCalendars,
|
||||
postCalendar,
|
||||
proppatchCalendar,
|
||||
} from "./CalendarApi";
|
||||
import { getOpenPaasUser, getUserDetails } from "../User/userAPI";
|
||||
import { parseCalendarEvent } from "../Events/eventUtils";
|
||||
import { deleteEvent, getEvent, moveEvent, putEvent } from "../Events/EventApi";
|
||||
@@ -17,11 +22,12 @@ export const getCalendarsListAsync = createAsyncThunk<
|
||||
|
||||
for (const cal of rawCalendars) {
|
||||
const name = cal["dav:name"];
|
||||
const description = cal["dav:description"];
|
||||
const description = cal["caldav:description"];
|
||||
let delegated = false;
|
||||
let source = cal["calendarserver:source"]
|
||||
? cal["calendarserver:source"]._links.self.href
|
||||
: cal._links.self.href;
|
||||
const link = cal._links.self.href;
|
||||
if (cal["calendarserver:delegatedsource"]) {
|
||||
source = cal["calendarserver:delegatedsource"];
|
||||
delegated = true;
|
||||
@@ -33,6 +39,7 @@ export const getCalendarsListAsync = createAsyncThunk<
|
||||
importedCalendars[id] = {
|
||||
id,
|
||||
name,
|
||||
link,
|
||||
owner: `${ownerData.firstname ? `${ownerData.firstname} ` : ""}${
|
||||
ownerData.lastname
|
||||
}`,
|
||||
@@ -121,6 +128,25 @@ export const getEventAsync = createAsyncThunk<
|
||||
event: response,
|
||||
};
|
||||
});
|
||||
export const patchCalendarAsync = createAsyncThunk<
|
||||
{
|
||||
calId: string;
|
||||
calLink: string;
|
||||
patch: { name: string; desc: string; color: string };
|
||||
}, // Return type
|
||||
{
|
||||
calId: string;
|
||||
calLink: string;
|
||||
patch: { name: string; desc: string; color: string };
|
||||
} // Arg type
|
||||
>("calendars/patchCalendar", async ({ calId, calLink, patch }) => {
|
||||
const response = await proppatchCalendar(calLink, patch);
|
||||
return {
|
||||
calId,
|
||||
calLink,
|
||||
patch,
|
||||
};
|
||||
});
|
||||
|
||||
export const moveEventAsync = createAsyncThunk<
|
||||
{ calId: string; events: CalendarEvent[] }, // Return type
|
||||
@@ -339,6 +365,32 @@ const CalendarSlice = createSlice({
|
||||
name: action.payload.name,
|
||||
} as unknown as Calendars;
|
||||
})
|
||||
.addCase(patchCalendarAsync.fulfilled, (state, action) => {
|
||||
state.pending = false;
|
||||
if (action.payload.patch.color) {
|
||||
state.list[action.payload.calId] = {
|
||||
...state.list[action.payload.calId],
|
||||
color: action.payload.patch.color,
|
||||
};
|
||||
Object.keys(state.list[action.payload.calId].events).forEach(
|
||||
(evId) =>
|
||||
(state.list[action.payload.calId].events[evId].color =
|
||||
action.payload.patch.color)
|
||||
);
|
||||
}
|
||||
if (action.payload.patch.desc) {
|
||||
state.list[action.payload.calId] = {
|
||||
...state.list[action.payload.calId],
|
||||
description: action.payload.patch.desc,
|
||||
};
|
||||
}
|
||||
if (action.payload.patch.name) {
|
||||
state.list[action.payload.calId] = {
|
||||
...state.list[action.payload.calId],
|
||||
name: action.payload.patch.name,
|
||||
};
|
||||
}
|
||||
})
|
||||
.addCase(getCalendarDetailAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
})
|
||||
@@ -357,6 +409,9 @@ const CalendarSlice = createSlice({
|
||||
.addCase(deleteEventAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
})
|
||||
.addCase(patchCalendarAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
})
|
||||
.addCase(createCalendarAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CalendarEvent } from "../Events/EventsTypes";
|
||||
|
||||
export interface Calendars {
|
||||
id: string;
|
||||
link: string;
|
||||
name: string;
|
||||
delegated?: boolean;
|
||||
prodid?: string;
|
||||
|
||||
Reference in New Issue
Block a user