Revamp calendar dialog (#158)
Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
import { Button, DialogActions, Tab, Tabs } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
createCalendarAsync /*, updateCalendarAsync */,
|
||||
patchCalendarAsync,
|
||||
} from "../../features/Calendars/CalendarSlice";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import {
|
||||
TextField,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
} from "@mui/material";
|
||||
import { ColorPicker } from "./CalendarColorPicker";
|
||||
createCalendarAsync,
|
||||
importEventFromFileAsync,
|
||||
patchACLCalendarAsync,
|
||||
patchCalendarAsync,
|
||||
} from "../../features/Calendars/CalendarSlice";
|
||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
||||
import { ResponsiveDialog } from "../Dialog";
|
||||
import { AccessTab } from "./AccessTab";
|
||||
import { ImportTab } from "./ImportTab";
|
||||
import { SettingsTab } from "./SettingsTab";
|
||||
|
||||
function CalendarPopover({
|
||||
open,
|
||||
@@ -30,101 +28,215 @@ function CalendarPopover({
|
||||
const dispatch = useAppDispatch();
|
||||
const userId =
|
||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||
const [name, setName] = useState(calendar?.name ?? "");
|
||||
const [description, setDescription] = useState(calendar?.description ?? "");
|
||||
const [color, setColor] = useState(calendar?.color ?? "");
|
||||
const calendars = useAppSelector((state) => state.calendars.list);
|
||||
const isOwn = calendar ? calendar?.id.split("/")[0] === userId : true;
|
||||
|
||||
// existing calendar params
|
||||
const [name, setName] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [color, setColor] = useState("");
|
||||
const [visibility, setVisibility] = useState<"private" | "public">("public");
|
||||
|
||||
// import tab state
|
||||
const [tab, setTab] = useState<"settings" | "access" | "import">("settings");
|
||||
const [importedContent, setImportedContent] = useState<File | null>(null);
|
||||
const [importTarget, setImportTarget] = useState("new");
|
||||
|
||||
// new calendar params (for import new)
|
||||
const [newCalName, setNewCalName] = useState("");
|
||||
const [newCalDescription, setNewCalDescription] = useState("");
|
||||
const [newCalColor, setNewCalColor] = useState("");
|
||||
const [newCalVisibility, setNewCalVisibility] = useState<
|
||||
"public" | "private"
|
||||
>("public");
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
if (calendar) {
|
||||
setName(calendar.name);
|
||||
setDescription(calendar.description ?? "");
|
||||
setColor(calendar.color ?? "");
|
||||
} else {
|
||||
setName("");
|
||||
setDescription("");
|
||||
setColor("");
|
||||
}
|
||||
if (!open) return;
|
||||
if (calendar) {
|
||||
setName(calendar.name);
|
||||
setDescription(calendar.description ?? "");
|
||||
setColor(calendar.color ?? "");
|
||||
setVisibility(calendar.visibility ?? "public");
|
||||
setImportTarget(calendar.id ?? "new");
|
||||
} else {
|
||||
setName("");
|
||||
setDescription("");
|
||||
setColor("");
|
||||
setVisibility("public");
|
||||
setImportTarget("new");
|
||||
}
|
||||
}, [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 updateCalendar = (calId: string, calLink: string) => {
|
||||
dispatch(
|
||||
patchCalendarAsync({
|
||||
calId,
|
||||
calLink,
|
||||
patch: { name: name.trim(), desc: description.trim(), color },
|
||||
})
|
||||
);
|
||||
if (visibility !== calendar?.visibility) {
|
||||
dispatch(
|
||||
patchACLCalendarAsync({
|
||||
calId,
|
||||
calLink,
|
||||
request: visibility === "public" ? "{DAV:}read" : "",
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={(e, reason) => onClose(e, reason)}>
|
||||
<DialogTitle style={{ backgroundColor: color }}>
|
||||
Calendar configuration
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
size="small"
|
||||
margin="dense"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
size="small"
|
||||
margin="dense"
|
||||
multiline
|
||||
rows={2}
|
||||
/>
|
||||
<ColorPicker
|
||||
onChange={(color) => setColor(color)}
|
||||
selectedColor={color}
|
||||
/>
|
||||
</DialogContent>
|
||||
const createCalendar = async (
|
||||
calId: string,
|
||||
name: string,
|
||||
desc: string,
|
||||
color: string,
|
||||
visibility: string
|
||||
) => {
|
||||
await dispatch(
|
||||
createCalendarAsync({
|
||||
name: name.trim(),
|
||||
desc: desc.trim(),
|
||||
color: color,
|
||||
userId,
|
||||
calId,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
patchACLCalendarAsync({
|
||||
calId: `${userId}/${calId}`,
|
||||
calLink: `/calendars/${userId}/${calId}.json`,
|
||||
request: visibility === "public" ? "{DAV:}read" : "",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={(e) => onClose({}, "backdropClick")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!name.trim()}
|
||||
variant="contained"
|
||||
onClick={handleSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
const handleSave = () => {
|
||||
if (!name.trim()) return;
|
||||
|
||||
if (calendar) {
|
||||
updateCalendar(calendar.id, calendar.link);
|
||||
} else {
|
||||
createCalendar(crypto.randomUUID(), name, description, color, visibility);
|
||||
}
|
||||
handleClose({}, "backdropClick");
|
||||
};
|
||||
|
||||
const handleImport = async () => {
|
||||
if (importTarget === "new") {
|
||||
const calId = crypto.randomUUID();
|
||||
if (newCalName.trim()) {
|
||||
await createCalendar(
|
||||
calId,
|
||||
newCalName,
|
||||
newCalDescription,
|
||||
newCalColor,
|
||||
newCalVisibility
|
||||
);
|
||||
importedContent &&
|
||||
dispatch(
|
||||
importEventFromFileAsync({
|
||||
calLink: `/calendar/${userId}/${calId}.json`,
|
||||
file: importedContent,
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
importedContent &&
|
||||
dispatch(
|
||||
importEventFromFileAsync({
|
||||
calLink: calendars[importTarget].link,
|
||||
file: importedContent,
|
||||
})
|
||||
);
|
||||
}
|
||||
handleClose({}, "backdropClick");
|
||||
};
|
||||
|
||||
const handleClose = (
|
||||
e: {},
|
||||
reason: "backdropClick" | "escapeKeyDown"
|
||||
): void => {
|
||||
onClose(e, reason);
|
||||
setName("");
|
||||
setDescription("");
|
||||
setColor("");
|
||||
setTab("settings");
|
||||
setVisibility("public");
|
||||
setImportTarget("new");
|
||||
setImportedContent(null);
|
||||
|
||||
setNewCalName("");
|
||||
setNewCalDescription("");
|
||||
setNewCalColor("");
|
||||
setNewCalVisibility("public");
|
||||
};
|
||||
|
||||
return (
|
||||
<ResponsiveDialog
|
||||
open={open}
|
||||
onClose={() => handleClose({}, "backdropClick")}
|
||||
title={
|
||||
<Tabs value={tab} onChange={(e, v) => setTab(v)}>
|
||||
<Tab
|
||||
value="settings"
|
||||
label={calendar ? "Settings" : "Add new calendar"}
|
||||
/>
|
||||
{calendar && <Tab value="access" label="Access" />}
|
||||
{isOwn && <Tab value="import" label="Import" />}
|
||||
</Tabs>
|
||||
}
|
||||
actions={
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={(e) => handleClose({}, "backdropClick")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={tab === "import" ? !importedContent : !name.trim()}
|
||||
variant="contained"
|
||||
onClick={tab === "import" ? handleImport : handleSave}
|
||||
>
|
||||
{tab === "import" ? "Import" : calendar ? "Save" : "Create"}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
}
|
||||
>
|
||||
{tab === "import" && (
|
||||
<ImportTab
|
||||
importTarget={importTarget}
|
||||
setImportTarget={setImportTarget}
|
||||
setImportedContent={setImportedContent}
|
||||
userId={userId}
|
||||
newCalParams={{
|
||||
name: newCalName,
|
||||
setName: setNewCalName,
|
||||
description: newCalDescription,
|
||||
setDescription: setNewCalDescription,
|
||||
color: newCalColor,
|
||||
setColor: setNewCalColor,
|
||||
visibility: newCalVisibility,
|
||||
setVisibility: setNewCalVisibility,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{tab === "settings" && (
|
||||
<SettingsTab
|
||||
name={name}
|
||||
setName={setName}
|
||||
description={description}
|
||||
setDescription={setDescription}
|
||||
color={color}
|
||||
setColor={setColor}
|
||||
visibility={visibility}
|
||||
setVisibility={setVisibility}
|
||||
calendar={calendar}
|
||||
/>
|
||||
)}
|
||||
{tab === "access" && calendar && <AccessTab calendar={calendar} />}
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user