Files
workavia-calendar-front/src/components/Calendar/AccessTab.tsx
T
Camille Moussu edf158f13f [#558] added owner to access details (#603)
Co-authored-by: Camille Moussu <cmoussu@linagora.com>
2026-03-03 21:12:34 +01:00

200 lines
5.8 KiB
TypeScript

import {
exportCalendar,
getSecretLink,
} from "@/features/Calendars/CalendarApi";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import {
Box,
Button,
CircularProgress,
IconButton,
InputAdornment,
TextField,
Typography,
} from "@linagora/twake-mui";
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
import { useEffect, useState } from "react";
import { useI18n } from "twake-i18n";
import { ErrorSnackbar } from "../Error/ErrorSnackbar";
import { FieldWithLabel } from "../Event/components/FieldWithLabel";
import { SnackbarAlert } from "../Loading/SnackBarAlert";
import { CalendarAccessRights, UserWithAccess } from "./CalendarAccessRights";
interface AccessTabProps {
calendar: Calendar;
usersWithAccess: UserWithAccess[];
onUsersWithAccessChange: (users: UserWithAccess[]) => void;
onInvitesLoaded: (users: UserWithAccess[]) => void;
}
export function AccessTab({
calendar,
usersWithAccess,
onUsersWithAccessChange,
onInvitesLoaded,
}: AccessTabProps) {
const { t } = useI18n();
const calDAVLink = `${window.DAV_BASE_URL}${calendar.link.replace(".json", "")}`;
const [secretLink, setSecretLink] = useState("");
const [open, setOpen] = useState(false);
useEffect(() => {
async function fetchSecret() {
const existing = await getSecretLink(
calendar.link.replace(".json", ""),
false
);
setSecretLink(existing.secretLink);
}
fetchSecret();
}, [calendar.link]);
const handleCopy = (content: string) => {
navigator.clipboard.writeText(content);
setOpen(true);
};
const handleResetSecretLink = async () => {
const newSecret = await getSecretLink(
calendar.link.replace(".json", ""),
true
);
setSecretLink(newSecret.secretLink);
};
const [exportLoading, setExportLoading] = useState(false);
const [exportError, setExportError] = useState("");
const handleExport = async () => {
try {
setExportLoading(true);
const exportedData = await exportCalendar(
calendar.link.replace(".json", "")
);
const blob = new Blob([exportedData], { type: "text/calendar" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `${calendar.id.split("/")[1]}.ics`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} catch (e) {
setExportError((e as Error).message);
} finally {
setExportLoading(false);
}
};
return (
<>
<CalendarAccessRights
calendar={calendar}
value={usersWithAccess}
onChange={onUsersWithAccessChange}
onInvitesLoaded={onInvitesLoaded}
/>
{!!window.DAV_BASE_URL && (
<FieldWithLabel label={t("calendar.caldav_access")} isExpanded={false}>
<Box mt={2}>
<TextField
disabled
fullWidth
label=""
inputProps={{ "aria-label": t("calendar.caldav_access") }}
value={calDAVLink}
size="small"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={() => handleCopy(calDAVLink)}
edge="end"
>
<ContentCopyIcon fontSize="small" />
</IconButton>
</InputAdornment>
),
}}
/>
</Box>
</FieldWithLabel>
)}
<FieldWithLabel label={t("calendar.secretUrl")} isExpanded={false}>
<Box mt={3} display="flex" alignItems="center" gap={1}>
<TextField
disabled
fullWidth
label=""
inputProps={{ "aria-label": t("calendar.secretUrl") }}
value={secretLink}
size="small"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => handleCopy(secretLink)} edge="end">
<ContentCopyIcon fontSize="small" />
</IconButton>
</InputAdornment>
),
}}
/>
<Button
variant="contained"
color="secondary"
onClick={handleResetSecretLink}
sx={{ borderRadius: "4px" }}
>
{t("actions.reset")}
</Button>
</Box>
<Typography
variant="body2"
sx={{ color: "text.secondary", mt: 1, lineHeight: 1.5 }}
>
{t("calendar.secretUrlDesc")}
</Typography>
</FieldWithLabel>
<FieldWithLabel label={t("calendar.exportCalendar")} isExpanded={false}>
<Typography
variant="body2"
sx={{ color: "text.secondary", my: 1, lineHeight: 1.5 }}
>
{t("calendar.exportDesc")}
</Typography>
<Button
variant="contained"
color="secondary"
onClick={handleExport}
startIcon={!exportLoading && <FileDownloadOutlinedIcon />}
disabled={exportLoading}
sx={{ borderRadius: "4px" }}
>
{exportLoading ? (
<Box display="flex" alignItems="center" gap={1}>
<CircularProgress size={18} />
{t("actions.exporting")}
</Box>
) : (
t("actions.export")
)}
</Button>
</FieldWithLabel>
<SnackbarAlert
setOpen={setOpen}
open={open}
message={t("common.link_copied")}
/>
<ErrorSnackbar error={exportError} type="calendar" />
</>
);
}