[#64] added creation modal to list and api call
This commit is contained in:
committed by
Benoit TELLIER
parent
d1140a47b2
commit
9d43c15ee0
@@ -14,6 +14,7 @@ import { CalendarEvent } from "../../features/Events/EventsTypes";
|
|||||||
import CalendarSelection from "./CalendarSelection";
|
import CalendarSelection from "./CalendarSelection";
|
||||||
import {
|
import {
|
||||||
getCalendarDetailAsync,
|
getCalendarDetailAsync,
|
||||||
|
getCalendarsListAsync,
|
||||||
getEventAsync,
|
getEventAsync,
|
||||||
putEventAsync,
|
putEventAsync,
|
||||||
updateEventLocal,
|
updateEventLocal,
|
||||||
@@ -92,13 +93,15 @@ export default function CalendarApp() {
|
|||||||
|
|
||||||
let filteredEvents: CalendarEvent[] = [];
|
let filteredEvents: CalendarEvent[] = [];
|
||||||
selectedCalendars.forEach((id) => {
|
selectedCalendars.forEach((id) => {
|
||||||
filteredEvents = filteredEvents
|
if (calendars[id].events) {
|
||||||
.concat(
|
filteredEvents = filteredEvents
|
||||||
Object.keys(calendars[id].events).map(
|
.concat(
|
||||||
(eventid) => calendars[id].events[eventid]
|
Object.keys(calendars[id].events).map(
|
||||||
|
(eventid) => calendars[id].events[eventid]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
.filter((event) => !(event.status === "CANCELLED"));
|
||||||
.filter((event) => !(event.status === "CANCELLED"));
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -125,7 +128,6 @@ export default function CalendarApp() {
|
|||||||
const [openEventDisplay, setOpenEventDisplay] = useState(false);
|
const [openEventDisplay, setOpenEventDisplay] = useState(false);
|
||||||
const [eventDisplayedId, setEventDisplayedId] = useState("");
|
const [eventDisplayedId, setEventDisplayedId] = useState("");
|
||||||
const [eventDisplayedCalId, setEventDisplayedCalId] = useState("");
|
const [eventDisplayedCalId, setEventDisplayedCalId] = useState("");
|
||||||
const [anchorElCal, setAnchorElCal] = useState<HTMLElement | null>(null);
|
|
||||||
const [selectedRange, setSelectedRange] = useState<DateSelectArg | null>(
|
const [selectedRange, setSelectedRange] = useState<DateSelectArg | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
@@ -248,7 +250,6 @@ export default function CalendarApp() {
|
|||||||
selectedCalendars={selectedCalendars}
|
selectedCalendars={selectedCalendars}
|
||||||
setSelectedCalendars={setSelectedCalendars}
|
setSelectedCalendars={setSelectedCalendars}
|
||||||
/>
|
/>
|
||||||
<button onClick={() => setAnchorElCal(document.body)}>+</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="calendar">
|
<div className="calendar">
|
||||||
<ImportAlert />
|
<ImportAlert />
|
||||||
@@ -270,7 +271,8 @@ export default function CalendarApp() {
|
|||||||
customButtons={{
|
customButtons={{
|
||||||
refresh: {
|
refresh: {
|
||||||
text: "↻",
|
text: "↻",
|
||||||
click: () => {
|
click: async () => {
|
||||||
|
await dispatch(getCalendarsListAsync());
|
||||||
selectedCalendars.forEach((id) => {
|
selectedCalendars.forEach((id) => {
|
||||||
if (!pending && rangeKey) {
|
if (!pending && rangeKey) {
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -521,11 +523,6 @@ export default function CalendarApp() {
|
|||||||
setSelectedRange={setSelectedRange}
|
setSelectedRange={setSelectedRange}
|
||||||
calendarRef={calendarRef}
|
calendarRef={calendarRef}
|
||||||
/>
|
/>
|
||||||
<CalendarPopover
|
|
||||||
anchorEl={anchorElCal}
|
|
||||||
open={Boolean(anchorElCal)}
|
|
||||||
onClose={() => setAnchorElCal(null)}
|
|
||||||
/>
|
|
||||||
{openEventDisplay && eventDisplayedId && eventDisplayedCalId && (
|
{openEventDisplay && eventDisplayedId && eventDisplayedCalId && (
|
||||||
<EventPreviewModal
|
<EventPreviewModal
|
||||||
eventId={eventDisplayedId}
|
eventId={eventDisplayedId}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
import { Button } from "@mui/material";
|
||||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import CalendarPopover from "../../features/Calendars/CalendarModal";
|
||||||
|
|
||||||
export default function CalendarSelection({
|
export default function CalendarSelection({
|
||||||
selectedCalendars,
|
selectedCalendars,
|
||||||
@@ -24,32 +28,20 @@ export default function CalendarSelection({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {}, [calendars]);
|
||||||
|
const [anchorElCal, setAnchorElCal] = useState<HTMLElement | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<span className="calendarListHeader">
|
<div>
|
||||||
<h3>Personnal Calendars</h3>
|
<div className="calendarListHeader">
|
||||||
</span>
|
<h3>Personnal Calendars</h3>
|
||||||
{personnalCalendars.map((id) => {
|
<Button onClick={() => setAnchorElCal(document.body)}>
|
||||||
return (
|
<AddIcon />
|
||||||
<div key={id}>
|
</Button>
|
||||||
<label>
|
</div>
|
||||||
<input
|
{personnalCalendars.map((id) => {
|
||||||
type="checkbox"
|
return (
|
||||||
style={{ backgroundColor: calendars[id].color }}
|
|
||||||
checked={selectedCalendars.includes(id)}
|
|
||||||
onChange={() => handleCalendarToggle(id)}
|
|
||||||
/>
|
|
||||||
{calendars[id].name}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{delegatedCalendars.length > 0 && (
|
|
||||||
<>
|
|
||||||
<span className="calendarListHeader">
|
|
||||||
<h3>Delegated Calendars</h3>
|
|
||||||
</span>
|
|
||||||
{delegatedCalendars.map((id) => (
|
|
||||||
<div key={id}>
|
<div key={id}>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
@@ -61,29 +53,54 @@ export default function CalendarSelection({
|
|||||||
{calendars[id].name}
|
{calendars[id].name}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
);
|
||||||
</>
|
})}
|
||||||
)}
|
{delegatedCalendars.length > 0 && (
|
||||||
{sharedCalendars.length > 0 && (
|
<>
|
||||||
<>
|
<span className="calendarListHeader">
|
||||||
<span className="calendarListHeader">
|
<h3>Delegated Calendars</h3>
|
||||||
<h3>Shared Calendars</h3>
|
</span>
|
||||||
</span>
|
{delegatedCalendars.map((id) => (
|
||||||
{sharedCalendars.map((id) => (
|
<div key={id}>
|
||||||
<div key={id}>
|
<label>
|
||||||
<label>
|
<input
|
||||||
<input
|
type="checkbox"
|
||||||
type="checkbox"
|
style={{ backgroundColor: calendars[id].color }}
|
||||||
style={{ backgroundColor: calendars[id].color }}
|
checked={selectedCalendars.includes(id)}
|
||||||
checked={selectedCalendars.includes(id)}
|
onChange={() => handleCalendarToggle(id)}
|
||||||
onChange={() => handleCalendarToggle(id)}
|
/>
|
||||||
/>
|
{calendars[id].name}
|
||||||
{calendars[id].name}
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
{sharedCalendars.length > 0 && (
|
||||||
</div>
|
<>
|
||||||
|
<span className="calendarListHeader">
|
||||||
|
<h3>Shared Calendars</h3>
|
||||||
|
</span>
|
||||||
|
{sharedCalendars.map((id) => (
|
||||||
|
<div key={id}>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
style={{ backgroundColor: calendars[id].color }}
|
||||||
|
checked={selectedCalendars.includes(id)}
|
||||||
|
onChange={() => handleCalendarToggle(id)}
|
||||||
|
/>
|
||||||
|
{calendars[id].name}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<CalendarPopover
|
||||||
|
anchorEl={anchorElCal}
|
||||||
|
open={Boolean(anchorElCal)}
|
||||||
|
onClose={() => setAnchorElCal(null)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,3 +30,24 @@ export async function getCalendar(
|
|||||||
const calendar = await response.json();
|
const calendar = await response.json();
|
||||||
return calendar;
|
return calendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function postCalendar(
|
||||||
|
userId: string,
|
||||||
|
calId: string,
|
||||||
|
color: string,
|
||||||
|
name: string,
|
||||||
|
desc: string
|
||||||
|
) {
|
||||||
|
const response = await api.post(`dav/calendars/${userId}.json`, {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json, text/plain, */*",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: calId,
|
||||||
|
"dav:name": name,
|
||||||
|
"apple:color": color,
|
||||||
|
"caldav:description": desc,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { createCalendar } from "./CalendarSlice";
|
import { createCalendar, createCalendarAsync } from "./CalendarSlice";
|
||||||
import { useAppDispatch } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
TextField,
|
TextField,
|
||||||
@@ -21,14 +21,18 @@ function CalendarPopover({
|
|||||||
onClose: (Calendar: {}, reason: "backdropClick" | "escapeKeyDown") => void;
|
onClose: (Calendar: {}, reason: "backdropClick" | "escapeKeyDown") => void;
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const userId =
|
||||||
|
useAppSelector((state) => state.user.userData.openpaasId) ?? "";
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [color, setColor] = useState("");
|
const [color, setColor] = useState("");
|
||||||
const [timeZone, setTimeZone] = useState("");
|
const [timeZone, setTimeZone] = useState("");
|
||||||
const timezones = Intl.supportedValuesOf?.("timeZone") ?? [];
|
const timezones = Intl.supportedValuesOf?.("timeZone") ?? [];
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
dispatch(createCalendar({ name, description, color }));
|
const calId = crypto.randomUUID();
|
||||||
|
dispatch(
|
||||||
|
createCalendarAsync({ name, desc: description, color, userId, calId })
|
||||||
|
);
|
||||||
onClose({}, "backdropClick");
|
onClose({}, "backdropClick");
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
@@ -68,7 +72,7 @@ function CalendarPopover({
|
|||||||
gutterBottom
|
gutterBottom
|
||||||
style={{ backgroundColor: color }}
|
style={{ backgroundColor: color }}
|
||||||
>
|
>
|
||||||
Create a Calendar
|
Calendar configuration
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { Calendars } from "./CalendarTypes";
|
import { Calendars } from "./CalendarTypes";
|
||||||
import { CalendarEvent } from "../Events/EventsTypes";
|
import { CalendarEvent } from "../Events/EventsTypes";
|
||||||
import { getCalendar, getCalendars } from "./CalendarApi";
|
import { getCalendar, getCalendars, postCalendar } from "./CalendarApi";
|
||||||
import { getOpenPaasUser, getUserDetails } from "../User/userAPI";
|
import { getOpenPaasUser, getUserDetails } from "../User/userAPI";
|
||||||
import { parseCalendarEvent } from "../Events/eventUtils";
|
import { parseCalendarEvent } from "../Events/eventUtils";
|
||||||
import { deleteEvent, getEvent, moveEvent, putEvent } from "../Events/EventApi";
|
import { deleteEvent, getEvent, moveEvent, putEvent } from "../Events/EventApi";
|
||||||
@@ -157,6 +157,14 @@ export const deleteEventAsync = createAsyncThunk<
|
|||||||
return { calId, eventId };
|
return { calId, eventId };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const createCalendarAsync = createAsyncThunk<
|
||||||
|
{ userId: string; calId: string; color: string; name: string; desc: string }, // Return type
|
||||||
|
{ userId: string; calId: string; color: string; name: string; desc: string } // Arg type
|
||||||
|
>("calendars/createCalendar", async ({ userId, calId, color, name, desc }) => {
|
||||||
|
const response = await postCalendar(userId, calId, color, name, desc);
|
||||||
|
return { userId, calId, color, name, desc };
|
||||||
|
});
|
||||||
|
|
||||||
const CalendarSlice = createSlice({
|
const CalendarSlice = createSlice({
|
||||||
name: "calendars",
|
name: "calendars",
|
||||||
initialState: { list: {} as Record<string, Calendars>, pending: false },
|
initialState: { list: {} as Record<string, Calendars>, pending: false },
|
||||||
@@ -206,9 +214,7 @@ const CalendarSlice = createSlice({
|
|||||||
getCalendarsListAsync.fulfilled,
|
getCalendarsListAsync.fulfilled,
|
||||||
(state, action: PayloadAction<Record<string, Calendars>>) => {
|
(state, action: PayloadAction<Record<string, Calendars>>) => {
|
||||||
state.pending = false;
|
state.pending = false;
|
||||||
Object.keys(action.payload).forEach((id) => {
|
state.list = action.payload;
|
||||||
state.list[id] = action.payload[id];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.addCase(
|
.addCase(
|
||||||
@@ -324,6 +330,15 @@ const CalendarSlice = createSlice({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.addCase(createCalendarAsync.fulfilled, (state, action) => {
|
||||||
|
state.pending = false;
|
||||||
|
state.list[`${action.payload.userId}/${action.payload.calId}`] = {
|
||||||
|
color: action.payload.color,
|
||||||
|
id: `${action.payload.userId}/${action.payload.calId}`,
|
||||||
|
description: action.payload.desc,
|
||||||
|
name: action.payload.name,
|
||||||
|
} as unknown as Calendars;
|
||||||
|
})
|
||||||
.addCase(getCalendarDetailAsync.pending, (state) => {
|
.addCase(getCalendarDetailAsync.pending, (state) => {
|
||||||
state.pending = true;
|
state.pending = true;
|
||||||
})
|
})
|
||||||
@@ -341,6 +356,9 @@ const CalendarSlice = createSlice({
|
|||||||
})
|
})
|
||||||
.addCase(deleteEventAsync.pending, (state) => {
|
.addCase(deleteEventAsync.pending, (state) => {
|
||||||
state.pending = true;
|
state.pending = true;
|
||||||
|
})
|
||||||
|
.addCase(createCalendarAsync.pending, (state) => {
|
||||||
|
state.pending = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,28 +18,30 @@ export default function ImportAlert() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.keys(calendars).map((calendarId) =>
|
{Object.keys(calendars).map((calendarId) =>
|
||||||
Object.keys(calendars[calendarId].events)
|
calendars[calendarId]?.events
|
||||||
.filter((id) => calendars[calendarId].events[id].error)
|
? Object.keys(calendars[calendarId]?.events)
|
||||||
.map((id) => {
|
.filter((id) => calendars[calendarId]?.events[id].error)
|
||||||
const isVisible =
|
.map((id) => {
|
||||||
visibleAlerts[calendars[calendarId].events[id].uid] ?? true; // default to visible
|
const isVisible =
|
||||||
|
visibleAlerts[calendars[calendarId].events[id].uid] ?? true; // default to visible
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapse
|
<Collapse
|
||||||
in={isVisible}
|
in={isVisible}
|
||||||
key={calendars[calendarId].events[id].uid}
|
key={calendars[calendarId].events[id].uid}
|
||||||
>
|
>
|
||||||
<Alert
|
<Alert
|
||||||
severity="error"
|
severity="error"
|
||||||
onClose={() =>
|
onClose={() =>
|
||||||
toggleEventAlert(calendars[calendarId].events[id].uid)
|
toggleEventAlert(calendars[calendarId].events[id].uid)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{calendars[calendarId].events[id].error}
|
{calendars[calendarId].events[id].error}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
: []
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user