Merge pull request #54 from linagora/4-story-manage-attendance
4 story manage attendance
This commit is contained in:
@@ -2,11 +2,10 @@ import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { Calendars } from "./CalendarTypes";
|
||||
import { CalendarEvent } from "../Events/EventsTypes";
|
||||
import { getCalendar, getCalendars } from "./CalendarApi";
|
||||
import getOpenPaasUser from "../User/userAPI";
|
||||
import { getOpenPaasUser, getUserDetails } from "../User/userAPI";
|
||||
import { parseCalendarEvent } from "../Events/eventUtils";
|
||||
import { deleteEvent, putEvent } from "../Events/EventApi";
|
||||
import { formatDateToYYYYMMDDTHHMMSS } from "../../utils/dateUtils";
|
||||
import { responsiveFontSizes } from "@mui/material";
|
||||
|
||||
export const getCalendarsListAsync = createAsyncThunk<
|
||||
Record<string, Calendars> // Return type
|
||||
@@ -19,14 +18,28 @@ export const getCalendarsListAsync = createAsyncThunk<
|
||||
for (const cal of rawCalendars) {
|
||||
const name = cal["dav:name"];
|
||||
const description = cal["dav:description"];
|
||||
const id = cal["calendarserver:source"]
|
||||
let delegated = false;
|
||||
let source = cal["calendarserver:source"]
|
||||
? cal["calendarserver:source"]._links.self.href
|
||||
.replace("/calendars/", "")
|
||||
.replace(".json", "")
|
||||
: cal._links.self.href.replace("/calendars/", "").replace(".json", "");
|
||||
: cal._links.self.href;
|
||||
if (cal["calendarserver:delegatedsource"]) {
|
||||
source = cal["calendarserver:delegatedsource"];
|
||||
delegated = true;
|
||||
}
|
||||
console.log(source);
|
||||
const id = source.replace("/calendars/", "").replace(".json", "");
|
||||
|
||||
const ownerData: any = await getUserDetails(id.split("/")[0]);
|
||||
const color = cal["apple:color"];
|
||||
importedCalendars[id] = { id, name, description, color, events: {} };
|
||||
importedCalendars[id] = {
|
||||
id,
|
||||
name,
|
||||
ownerEmails: ownerData.emails,
|
||||
description,
|
||||
delegated,
|
||||
color,
|
||||
events: {},
|
||||
};
|
||||
}
|
||||
|
||||
return importedCalendars;
|
||||
@@ -41,8 +54,9 @@ export const getCalendarDetailAsync = createAsyncThunk<
|
||||
const events: CalendarEvent[] = calendar._embedded["dav:item"].flatMap(
|
||||
(eventdata: any) => {
|
||||
const vevents = eventdata.data[2] as any[][]; // array of ['vevent', RawEntry[], []]
|
||||
const eventURL = eventdata._links.self.href;
|
||||
return vevents.map((vevent: any[]) => {
|
||||
return parseCalendarEvent(vevent[1], color, calId);
|
||||
return parseCalendarEvent(vevent[1], color, calId, eventURL);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -54,18 +68,19 @@ export const putEventAsync = createAsyncThunk<
|
||||
{ calId: string; events: CalendarEvent[] }, // Return type
|
||||
{ cal: Calendars; newEvent: CalendarEvent } // Arg type
|
||||
>("calendars/putEvent", async ({ cal, newEvent }) => {
|
||||
const response = await putEvent(cal, newEvent);
|
||||
const response = await putEvent(newEvent);
|
||||
const calEvents = (await getCalendar(cal.id, {
|
||||
start: formatDateToYYYYMMDDTHHMMSS(newEvent.start),
|
||||
start: formatDateToYYYYMMDDTHHMMSS(new Date(newEvent.start)),
|
||||
end: formatDateToYYYYMMDDTHHMMSS(
|
||||
new Date(newEvent.start.getTime() + 86400000)
|
||||
new Date(new Date(newEvent.start).getTime() + 86400000)
|
||||
),
|
||||
})) as Record<string, any>;
|
||||
const events: CalendarEvent[] = calEvents._embedded["dav:item"].flatMap(
|
||||
(eventdata: any) => {
|
||||
const vevents = eventdata.data[2] as any[][];
|
||||
const eventURL = eventdata._links.self.href;
|
||||
return vevents.map((vevent: any[]) => {
|
||||
return parseCalendarEvent(vevent[1], cal.color ?? "", cal.id);
|
||||
return parseCalendarEvent(vevent[1], cal.color ?? "", cal.id, eventURL);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -78,9 +93,9 @@ export const putEventAsync = createAsyncThunk<
|
||||
|
||||
export const deleteEventAsync = createAsyncThunk<
|
||||
{ calId: string; eventId: string }, // Return type
|
||||
{ calId: string; eventId: string } // Arg type
|
||||
>("calendars/delEvent", async ({ calId, eventId }) => {
|
||||
const response = await deleteEvent(calId, eventId);
|
||||
{ calId: string; eventId: string; eventURL: string } // Arg type
|
||||
>("calendars/delEvent", async ({ calId, eventId, eventURL }) => {
|
||||
const response = await deleteEvent(eventURL);
|
||||
return { calId, eventId };
|
||||
});
|
||||
|
||||
@@ -105,6 +120,11 @@ const CalendarSlice = createSlice({
|
||||
}
|
||||
state.list[action.payload.calendarUid].events[action.payload.event.uid] =
|
||||
action.payload.event;
|
||||
state.list[action.payload.calendarUid].events[
|
||||
action.payload.event.uid
|
||||
].URL = `dav/calendars/${action.payload.calendarUid}/${
|
||||
action.payload.event.uid.split("/")[0]
|
||||
}.isc`;
|
||||
},
|
||||
removeEvent: (
|
||||
state,
|
||||
|
||||
@@ -3,8 +3,10 @@ import { CalendarEvent } from "../Events/EventsTypes";
|
||||
export interface Calendars {
|
||||
id: string;
|
||||
name: string;
|
||||
delegated?: boolean;
|
||||
prodid?: string;
|
||||
color?: string;
|
||||
ownerEmails?: string[];
|
||||
description?: string;
|
||||
calscale?: string;
|
||||
version?: string;
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
import { api } from "../../utils/apiUtils";
|
||||
import { Calendars } from "../Calendars/CalendarTypes";
|
||||
import { CalendarEvent } from "./EventsTypes";
|
||||
import { calendarEventToJCal } from "./eventUtils";
|
||||
|
||||
export async function putEvent(cal: Calendars, event: CalendarEvent) {
|
||||
const response = await api(
|
||||
`dav/calendars/${cal.id}/${event.uid.split("/")[0]}.isc`,
|
||||
{
|
||||
method: "PUT",
|
||||
body: JSON.stringify(calendarEventToJCal(event)),
|
||||
headers: {
|
||||
"content-type": "text/calendar; charset=utf-8",
|
||||
},
|
||||
}
|
||||
).json();
|
||||
return response;
|
||||
export async function putEvent(event: CalendarEvent) {
|
||||
const response = await api(`dav${event.URL}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(calendarEventToJCal(event)),
|
||||
headers: {
|
||||
"content-type": "text/calendar; charset=utf-8",
|
||||
},
|
||||
});
|
||||
if (response.status === 201) {
|
||||
console.log("PUT (201) :", response.url);
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function deleteEvent(calId: string, eventId: string) {
|
||||
const response = await api(
|
||||
`dav/calendars/${calId}/${eventId.split("/")[0]}.isc`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
).json();
|
||||
export async function deleteEvent(eventURL: string) {
|
||||
const response = await api(eventURL, {
|
||||
method: "DELETE",
|
||||
}).json();
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { deleteEventAsync } from "../Calendars/CalendarSlice";
|
||||
import { deleteEventAsync, putEventAsync } from "../Calendars/CalendarSlice";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import {
|
||||
Popover,
|
||||
@@ -25,6 +25,7 @@ import CircleIcon from "@mui/icons-material/Circle";
|
||||
import CancelIcon from "@mui/icons-material/Cancel";
|
||||
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
||||
import { userAttendee } from "../User/userDataTypes";
|
||||
import { putEvent } from "./EventApi";
|
||||
|
||||
function EventDisplayModal({
|
||||
eventId,
|
||||
@@ -74,6 +75,18 @@ function EventDisplayModal({
|
||||
(a) => a.cal_address === event.organizer?.cal_address
|
||||
);
|
||||
|
||||
function handleRSVP(rsvp: string) {
|
||||
const newEvent = {
|
||||
...event,
|
||||
attendee: event.attendee?.map((a) =>
|
||||
a.cal_address === user.userData.email ? { ...a, partstat: rsvp } : a
|
||||
),
|
||||
};
|
||||
|
||||
dispatch(putEventAsync({ cal: calendar, newEvent }));
|
||||
onClose({}, "backdropClick");
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover open={open} anchorEl={anchorEl} onClose={onClose}>
|
||||
<Card sx={{ width: 300, p: 2, position: "relative" }}>
|
||||
@@ -195,9 +208,36 @@ function EventDisplayModal({
|
||||
Will you attend?
|
||||
</Typography>
|
||||
<ButtonGroup size="small" fullWidth>
|
||||
<Button>Accept</Button>
|
||||
<Button>Maybe</Button>
|
||||
<Button>Decline</Button>
|
||||
<Button
|
||||
color={
|
||||
currentUserAttendee.partstat === "ACCEPTED"
|
||||
? "success"
|
||||
: "primary"
|
||||
}
|
||||
onClick={() => handleRSVP("ACCEPTED")}
|
||||
>
|
||||
Accept
|
||||
</Button>
|
||||
<Button
|
||||
color={
|
||||
currentUserAttendee.partstat === "TENTATIVE"
|
||||
? "warning"
|
||||
: "primary"
|
||||
}
|
||||
onClick={() => handleRSVP("TENTATIVE")}
|
||||
>
|
||||
Maybe
|
||||
</Button>
|
||||
<Button
|
||||
color={
|
||||
currentUserAttendee.partstat === "DECLINED"
|
||||
? "error"
|
||||
: "primary"
|
||||
}
|
||||
onClick={() => handleRSVP("DECLINED")}
|
||||
>
|
||||
Decline
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { userAttendee, userOrganiser } from "../User/userDataTypes";
|
||||
|
||||
export interface CalendarEvent {
|
||||
URL: string;
|
||||
calId: string;
|
||||
uid: string;
|
||||
transp?: string;
|
||||
|
||||
@@ -8,7 +8,8 @@ type RawEntry = [string, Record<string, string>, string, any];
|
||||
export function parseCalendarEvent(
|
||||
data: RawEntry[],
|
||||
color: string,
|
||||
calendarid: string
|
||||
calendarid: string,
|
||||
eventURL: string
|
||||
): CalendarEvent {
|
||||
const event: Partial<CalendarEvent> = { color, attendee: [] };
|
||||
let recurrenceId;
|
||||
@@ -75,6 +76,7 @@ export function parseCalendarEvent(
|
||||
event.uid = `${event.uid}/${recurrenceId}`;
|
||||
}
|
||||
|
||||
event.URL = eventURL;
|
||||
if (!event.uid || !event.start) {
|
||||
console.error(
|
||||
`missing crucial event param in calendar ${calendarid} `,
|
||||
@@ -98,7 +100,7 @@ export function calendarEventToJCal(event: CalendarEvent): any[] {
|
||||
"dtstart",
|
||||
{ tzid },
|
||||
event.allday ? "date" : "date-time",
|
||||
formatDateToICal(event.start, event.allday ?? false),
|
||||
formatDateToICal(new Date(event.start), event.allday ?? false),
|
||||
],
|
||||
["class", {}, "text", event.class ?? "PUBLIC"],
|
||||
[
|
||||
@@ -117,7 +119,7 @@ export function calendarEventToJCal(event: CalendarEvent): any[] {
|
||||
"dtend",
|
||||
{ tzid },
|
||||
event.allday ? "date" : "date-time",
|
||||
formatDateToICal(event.end, event.allday ?? false),
|
||||
formatDateToICal(new Date(event.end), event.allday ?? false),
|
||||
]);
|
||||
}
|
||||
if (event.organizer) {
|
||||
@@ -139,15 +141,18 @@ export function calendarEventToJCal(event: CalendarEvent): any[] {
|
||||
}
|
||||
|
||||
event.attendee.forEach((att) => {
|
||||
const attendee: Record<string, string> = {
|
||||
partstat: att.partstat,
|
||||
rsvp: att.rsvp,
|
||||
role: att.role,
|
||||
cutype: att.cutype,
|
||||
};
|
||||
if (att.cn) {
|
||||
attendee.cn = att.cn;
|
||||
}
|
||||
vevent[1].push([
|
||||
"attendee",
|
||||
{
|
||||
cn: att.cn,
|
||||
partstat: att.partstat,
|
||||
rsvp: att.rsvp,
|
||||
role: att.role,
|
||||
cutype: att.cutype,
|
||||
},
|
||||
attendee,
|
||||
"cal-address",
|
||||
`mailto:${att.cal_address}`,
|
||||
]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { api } from "../../utils/apiUtils";
|
||||
|
||||
export default async function getOpenPaasUser() {
|
||||
export async function getOpenPaasUser() {
|
||||
const user = await api.get(`api/user`).json();
|
||||
return user;
|
||||
}
|
||||
@@ -25,3 +25,8 @@ export async function searchUsers(query: string) {
|
||||
avatarUrl: user.photos?.[0]?.url || "",
|
||||
}));
|
||||
}
|
||||
|
||||
export async function getUserDetails(id: string) {
|
||||
const user = await api.get(`api/users/${id}`).json();
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface userOrganiser {
|
||||
}
|
||||
|
||||
export interface userAttendee {
|
||||
cn: string;
|
||||
cn?: string;
|
||||
cal_address: string;
|
||||
partstat: string;
|
||||
rsvp: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { userData, userOrganiser } from "./userDataTypes";
|
||||
import getOpenPaasUser from "./userAPI";
|
||||
import { getOpenPaasUser } from "./userAPI";
|
||||
|
||||
export const getOpenPaasUserDataAsync = createAsyncThunk<any>(
|
||||
"user/getOpenPaasUserData",
|
||||
|
||||
Reference in New Issue
Block a user