#598 resource field in event (#634)

Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
lethemanh
2026-03-17 20:11:12 +07:00
committed by GitHub
parent e4fb209147
commit 387e64d58c
21 changed files with 503 additions and 91 deletions
+52 -5
View File
@@ -43,6 +43,7 @@ import { userAttendee } from "../User/models/attendee";
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
import { useEventOrganizer } from "./useEventOrganizer";
import { buildDelegatedEventURL } from "./utils/buildDelegatedEventURL";
import { Resource } from "@/components/Attendees/ResourceSearch";
function EventPopover({
open,
@@ -86,6 +87,16 @@ function EventPopover({
return resolveTimezone(tz);
}, [calendarTimezone]);
const resources: Resource[] = useMemo(() => {
const resourcesInEvent =
event?.attendee?.filter((attendee) => attendee.cutype === "RESOURCE") ??
[];
return resourcesInEvent.map((resource) => ({
email: resource.cal_address,
displayName: resource.cn,
}));
}, [event?.attendee]);
const [showMore, setShowMore] = useState(false);
const [showDescription, setShowDescription] = useState(
event?.description ? true : false
@@ -127,6 +138,7 @@ function EventPopover({
const [repetition, setRepetition] = useState<RepetitionObject>(
event?.repetition ?? ({} as RepetitionObject)
);
const [selectedResources, setSelectedResources] = useState(resources ?? []);
// Derive the effective organizer based on the selected calendar.
// When a delegated calendar is selected, the organizer must be the
@@ -139,11 +151,16 @@ function EventPopover({
const [attendees, setAttendees] = useState<userAttendee[]>(
event?.attendee
? event.attendee.filter((a) => a.cal_address !== organizer?.cal_address)
? event.attendee.filter(
(a) =>
a.cal_address !== organizer?.cal_address && a.cutype !== "RESOURCE"
)
: []
);
const [alarm, setAlarm] = useState(event?.alarm?.trigger ?? "");
const [eventClass, setEventClass] = useState(event?.class ?? "PUBLIC");
const [eventClass, setEventClass] = useState<string>(
event?.class ?? "PUBLIC"
);
const [busy, setBusy] = useState(event?.transp ?? "OPAQUE");
const [timezone, setTimezone] = useState(
event?.timezone ? resolveTimezone(event.timezone) : resolvedCalendarTimezone
@@ -200,6 +217,7 @@ function EventPopover({
setHasVideoConference(false);
setMeetingLink(null);
setHasEndDateChanged(false);
setSelectedResources([]);
}, [resolvedCalendarTimezone, defaultCalendarId]);
// Track if we should sync from selectedRange (only on initial selection, not on toggle)
@@ -466,7 +484,9 @@ function EventPopover({
setAttendees(
event.attendee
? event.attendee.filter(
(a) => a.cal_address !== organizer?.cal_address
(a) =>
a.cal_address !== organizer?.cal_address &&
a.cutype !== "RESOURCE"
)
: []
);
@@ -491,17 +511,23 @@ function EventPopover({
setDescription(event.description);
}
}
setSelectedResources(resources ?? []);
} else if (event && event.attendee && event.attendee.length > 0) {
// Handle tempEvent case (no uid but has attendees from temp calendar search)
setAttendees(
event.attendee.filter((a) => a.cal_address !== organizer?.cal_address)
event.attendee.filter(
(a) =>
a.cal_address !== organizer?.cal_address && a.cutype !== "RESOURCE"
)
);
setSelectedResources(resources ?? []);
}
}, [
event,
organizer?.cal_address,
resolvedCalendarTimezone,
defaultCalendarId,
resources,
]);
// Reset state when creating new event (event is empty object or undefined)
@@ -535,6 +561,7 @@ function EventPopover({
setHasVideoConference(false);
setMeetingLink(null);
setHasEndDateChanged(false);
setSelectedResources([]);
}
if (!isCreatingNew) {
@@ -640,6 +667,7 @@ function EventPopover({
resetAllStateToDefault();
setStart("");
setEnd("");
setSelectedResources([]);
shouldSyncFromRangeRef.current = true; // Reset for next time
isCalendarIdUserSelectedRef.current = false; // Reset so next open gets fresh default
};
@@ -666,6 +694,7 @@ function EventPopover({
showDescription,
showRepeat,
hasEndDateChanged,
resources: selectedResources,
};
return buildEventFormTempData(formState);
}, [
@@ -688,6 +717,7 @@ function EventPopover({
showDescription,
showRepeat,
hasEndDateChanged,
selectedResources,
]);
// Check for temp data when modal opens
@@ -722,6 +752,7 @@ function EventPopover({
setShowDescription,
setShowRepeat,
setHasEndDateChanged,
setSelectedResources,
});
// Clear the error flag but keep data until successful save
const updatedTempData = { ...tempData, fromError: false };
@@ -766,7 +797,7 @@ function EventPopover({
uid: newEventUID,
description,
location,
class: eventClass,
class: eventClass as "PUBLIC" | "PRIVATE" | "CONFIDENTIAL",
repetition,
organizer,
timezone,
@@ -787,6 +818,20 @@ function EventPopover({
x_openpass_videoconference: meetingLink || undefined,
};
// Map data of resources to attendee before creating event
if (selectedResources?.length) {
selectedResources.forEach((resource: Resource) => {
newEvent.attendee.push({
cn: resource?.displayName ?? "",
cal_address: resource?.email ?? "",
partstat: "NEEDS-ACTION",
rsvp: "TRUE",
role: "REQ-PARTICIPANT",
cutype: "RESOURCE",
});
});
}
if (allday) {
const startDateOnly = (start || "").split("T")[0];
const endDateOnlyUI = (end || start || "").split("T")[0];
@@ -957,6 +1002,8 @@ function EventPopover({
onValidationChange={setIsFormValid}
showValidationErrors={showValidationErrors}
onHasEndDateChangedChange={setHasEndDateChanged}
setSelectedResources={setSelectedResources}
selectedResources={selectedResources}
/>
</ResponsiveDialog>
);