#602 adminstrator can manage calendar resources (#703)

Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
lethemanh
2026-03-25 14:28:24 +07:00
committed by GitHub
parent fdc9d7651b
commit 43424607d4
21 changed files with 237 additions and 36 deletions
+22 -2
View File
@@ -1,11 +1,31 @@
import { Avatar } from "@linagora/twake-mui";
import { Avatar, Box } from "@linagora/twake-mui";
import LayersOutlinedIcon from "@mui/icons-material/LayersOutlined";
interface ResourceIconProps {
avatarUrl?: string;
colorIcon?: boolean;
color?: string;
}
export function ResourceIcon({ avatarUrl }: ResourceIconProps) {
export function ResourceIcon({
avatarUrl,
colorIcon,
color,
}: ResourceIconProps) {
if (colorIcon && avatarUrl) {
return (
<Box
sx={{
width: "24px",
height: "24px",
backgroundColor: color,
maskImage: `url(${avatarUrl})`,
maskSize: "cover",
}}
/>
);
}
return avatarUrl ? (
<Avatar
sx={{ backgroundColor: "transparent", width: "24px", height: "24px" }}
@@ -133,7 +133,13 @@ export function CalendarAccessRights({
access: invite.access,
};
})
?.filter((invite) => !!invite) || []) satisfies UserInCalendar[];
?.filter(
(invite) =>
!!invite &&
!calendar.owner.administrators?.some(
(admin) => admin.id === invite.id
)
) || []) as UserInCalendar[];
const loaded = await handleLoadUsers(usersInCal, cancelled);
@@ -154,7 +160,13 @@ export function CalendarAccessRights({
return () => {
cancelled = true;
};
}, [calendar.invite, handleLoadUsers, onChange, onInvitesLoaded]);
}, [
calendar.invite,
calendar.owner.administrators,
handleLoadUsers,
onChange,
onInvitesLoaded,
]);
useEffect(() => {
const isResource = calendar.owner.resource;
+18 -8
View File
@@ -7,6 +7,7 @@ import { Box, Typography } from "@linagora/twake-mui";
import SquareRoundedIcon from "@mui/icons-material/SquareRounded";
import { useI18n } from "twake-i18n";
import { OwnerCaption } from "./OwnerCaption";
import { ResourceIcon } from "../Attendees/ResourceIcon";
export function CalendarName({ calendar }: { calendar: Calendar }) {
const userData = useAppSelector((state) => state.user.userData);
@@ -15,8 +16,9 @@ export function CalendarName({ calendar }: { calendar: Calendar }) {
const ownerId = calendar.id.split("/")[0];
const ownerDisplayName = makeDisplayName(calendar) ?? "";
const isOwnCalendar = userData.openpaasId === ownerId;
const isResource = calendar.owner?.resource;
const showCaption =
calendar.name !== "#default" && !isOwnCalendar && !calendar.owner?.resource;
calendar.name !== "#default" && !isOwnCalendar && !isResource;
return (
<Box
@@ -27,13 +29,21 @@ export function CalendarName({ calendar }: { calendar: Calendar }) {
alignItems: "center",
}}
>
<SquareRoundedIcon
style={{
color: calendar.color?.light ?? defaultColors[0].light,
width: 24,
height: 24,
}}
/>
{isResource ? (
<ResourceIcon
colorIcon
avatarUrl={calendar.owner.resourceIcon}
color={calendar.color?.light ?? defaultColors[0].light}
/>
) : (
<SquareRoundedIcon
style={{
color: calendar.color?.light ?? defaultColors[0].light,
width: 24,
height: 24,
}}
/>
)}
<Box style={{ display: "flex", flexDirection: "column" }}>
<Typography variant="body2" sx={{ wordBreak: "break-word" }}>
{renameDefault(calendar.name, ownerDisplayName, t, isOwnCalendar)}
@@ -137,7 +137,10 @@ export default function CalendarSelection({
(id) => extractEventBaseUuid(id) === userId
);
const delegatedCalendars = Object.keys(calendars || {}).filter(
(id) => extractEventBaseUuid(id) !== userId && calendars[id]?.delegated
(id) =>
extractEventBaseUuid(id) !== userId &&
calendars[id]?.delegated &&
!calendars?.[id]?.owner?.resource
);
const sharedCalendars = Object.keys(calendars || {}).filter(
(id) =>
@@ -147,9 +150,7 @@ export default function CalendarSelection({
);
const resourceCalendars = Object.keys(calendars || {}).filter(
(id) =>
extractEventBaseUuid(id) !== userId &&
!calendars?.[id]?.delegated &&
calendars?.[id]?.owner?.resource
extractEventBaseUuid(id) !== userId && calendars?.[id]?.owner?.resource
);
const handleCalendarToggle = (name: string) => {
@@ -15,10 +15,22 @@ import { buildFamilyName } from "@/utils/buildFamilyName";
import { isEventOrganiser } from "@/utils/isEventOrganiser";
function updateEventAttendees(
calendar: Calendar,
event: CalendarEvent,
user: userData | undefined,
rsvp: PartStat
) {
if (calendar.owner?.resource) {
const updatedAttendees =
event.attendee?.map((attendeeData) =>
attendeeData.cutype === "RESOURCE" && attendeeData.cn === calendar.name
? { ...attendeeData, partstat: rsvp }
: attendeeData
) || [];
return { attendee: updatedAttendees };
}
if (!user) {
throw new Error("Cannot update attendees without user data");
}
@@ -100,7 +112,7 @@ export async function handleRSVP(
) {
const newEvent = {
...event,
...updateEventAttendees(event, user, rsvp),
...updateEventAttendees(calendar, event, user, rsvp),
};
if (typeOfAction === "solo") {