diff --git a/__test__/features/Calendars/api/addCalendarResourceAsync.test.tsx b/__test__/features/Calendars/api/addCalendarResourceAsync.test.tsx index 2599a00..5ec1800 100644 --- a/__test__/features/Calendars/api/addCalendarResourceAsync.test.tsx +++ b/__test__/features/Calendars/api/addCalendarResourceAsync.test.tsx @@ -111,8 +111,20 @@ describe("addCalendarResourceAsync thunk", () => { expect(mockedToRejectedError).toHaveBeenCalledWith(errorDetails); - expect(result.type).toBe("calendars/addCalendarResource/rejected"); - expect(result.payload).toEqual(mockRejectedErrorResult); + expect(result.type).toBe("calendars/addCalendarResource/fulfilled"); + expect(result.payload).toEqual({ + calId: "res-456/cal-123", + color: { background: "#000000", foreground: "#FFFFFF" }, + desc: "A meeting room", + link: "/calendars/user-123/cal-123.json", + name: "Resource Room A", + owner: { + firstname: "", + lastname: "Resource Room A", + emails: [], + resource: true, + }, + }); }); it("should handle error if addSharedCalendar fails", async () => { diff --git a/__test__/features/user/userAPI.test.tsx b/__test__/features/user/userAPI.test.tsx index 6a233fe..6dcfe9a 100644 --- a/__test__/features/user/userAPI.test.tsx +++ b/__test__/features/user/userAPI.test.tsx @@ -3,6 +3,7 @@ import { getOpenPaasUser, updateUserConfigurations, getResourceDetails, + getUserDetails, } from "@/features/User/userAPI"; import { api } from "@/utils/apiUtils"; @@ -25,6 +26,26 @@ describe("getOpenPaasUser", () => { }); }); +describe("getUserDetails", () => { + it("should fetch and return user details", async () => { + const mockUser = { + firstname: "John", + lastname: "Doe", + emails: ["john@test.com"], + }; + const userId = "123"; + + (api.get as jest.Mock).mockReturnValue({ + json: jest.fn().mockResolvedValue(mockUser), + }); + + const result = await getUserDetails(userId); + + expect(api.get).toHaveBeenCalledWith(`api/users/${userId}`); + expect(result).toEqual(mockUser); + }); +}); + describe("getResourceDetails", () => { it("should fetch and return resource details", async () => { const mockResource = { _id: "res-123", name: "Meeting Room A" }; diff --git a/src/components/Calendar/handlers/eventHandlers.ts b/src/components/Calendar/handlers/eventHandlers.ts index 867137c..e9de525 100644 --- a/src/components/Calendar/handlers/eventHandlers.ts +++ b/src/components/Calendar/handlers/eventHandlers.ts @@ -11,7 +11,10 @@ import { import { getEvent } from "@/features/Events/EventApi"; import { CalendarEvent } from "@/features/Events/EventsTypes"; import { updateAttendeesAfterTimeChange } from "@/features/Events/updateEventHelpers/updateAttendeesAfterTimeChange"; -import { createAttendee } from "@/features/User/models/attendee.mapper"; +import { + AttendeeOptions, + createAttendee, +} from "@/features/User/models/attendee.mapper"; import { getDeltaInMilliseconds } from "@/utils/dateUtils"; import { CalendarApi, @@ -70,13 +73,18 @@ export const createEventHandlers = (props: EventHandlersProps) => { end: selectInfo?.end ? formatLocalDateTime(selectInfo?.end, timezone) : "", - attendee: tempUsers.map((user) => - createAttendee({ + attendee: tempUsers.map((user) => { + const attendeeOption: AttendeeOptions = { cal_address: user.email, cn: user.displayName, rsvp: "TRUE", - }) - ), + }; + + if (user.objectType === "resource") { + attendeeOption.cutype = "RESOURCE"; + } + return createAttendee(attendeeOption); + }), } as CalendarEvent; setTempEvent(newEvent); diff --git a/src/features/Calendars/api/addCalendarResourceAsync.ts b/src/features/Calendars/api/addCalendarResourceAsync.ts index 6a48500..60ee6aa 100644 --- a/src/features/Calendars/api/addCalendarResourceAsync.ts +++ b/src/features/Calendars/api/addCalendarResourceAsync.ts @@ -32,12 +32,6 @@ export const addCalendarResourceAsync = createAsyncThunk< ?.replace(".json", "") ?.split("/")[0]; - if (!resourceId) { - return rejectWithValue({ - message: "Unable to extract resource ID from calendar link", - } as RejectedError); - } - let owner: OpenPaasUserData = { firstname: "", lastname: cal.cal["dav:name"] ?? "", @@ -46,11 +40,18 @@ export const addCalendarResourceAsync = createAsyncThunk< }; try { await addSharedCalendar(userId, calId, cal); - const resource = await getResourceDetails(resourceId!); - owner = { - ...(await getUserDetails(resource.creator)), - resource: true, - }; + + if (resourceId) { + try { + const resource = await getResourceDetails(resourceId!); + owner = { + ...(await getUserDetails(resource.creator)), + resource: true, + }; + } catch (e) { + toRejectedError(e); + } + } return { calId: cal.cal._links.self?.href diff --git a/src/features/Events/EventModal.tsx b/src/features/Events/EventModal.tsx index 6a493da..1401540 100644 --- a/src/features/Events/EventModal.tsx +++ b/src/features/Events/EventModal.tsx @@ -96,6 +96,12 @@ function EventPopover({ displayName: resource.cn, })); }, [event?.attendee]); + const eventAttendees = useMemo( + () => + event?.attendee?.filter((attendee) => attendee.cutype !== "RESOURCE") ?? + [], + [event?.attendee] + ); const [showMore, setShowMore] = useState(false); const [showDescription, setShowDescription] = useState( @@ -150,11 +156,8 @@ function EventPopover({ }); const [attendees, setAttendees] = useState( - event?.attendee - ? event.attendee.filter( - (a) => - a.cal_address !== organizer?.cal_address && a.cutype !== "RESOURCE" - ) + eventAttendees + ? eventAttendees.filter((a) => a.cal_address !== organizer?.cal_address) : [] ); const [alarm, setAlarm] = useState(event?.alarm?.trigger ?? ""); @@ -482,11 +485,9 @@ function EventPopover({ setRepetition(event.repetition ?? ({} as RepetitionObject)); setShowRepeat(event.repetition?.freq ? true : false); setAttendees( - event.attendee - ? event.attendee.filter( - (a) => - a.cal_address !== organizer?.cal_address && - a.cutype !== "RESOURCE" + eventAttendees + ? eventAttendees.filter( + (a) => a.cal_address !== organizer?.cal_address ) : [] ); @@ -512,13 +513,10 @@ function EventPopover({ } } setSelectedResources(resources ?? []); - } else if (event && event.attendee && event.attendee.length > 0) { + } 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 && a.cutype !== "RESOURCE" - ) + eventAttendees.filter((a) => a.cal_address !== organizer?.cal_address) ); setSelectedResources(resources ?? []); } @@ -528,6 +526,7 @@ function EventPopover({ resolvedCalendarTimezone, defaultCalendarId, resources, + eventAttendees, ]); // Reset state when creating new event (event is empty object or undefined) @@ -667,7 +666,6 @@ function EventPopover({ resetAllStateToDefault(); setStart(""); setEnd(""); - setSelectedResources([]); shouldSyncFromRangeRef.current = true; // Reset for next time isCalendarIdUserSelectedRef.current = false; // Reset so next open gets fresh default }; diff --git a/src/features/User/models/attendee.mapper.ts b/src/features/User/models/attendee.mapper.ts index befceb1..6bba9cb 100644 --- a/src/features/User/models/attendee.mapper.ts +++ b/src/features/User/models/attendee.mapper.ts @@ -1,13 +1,15 @@ import { userAttendee } from "./attendee"; -export function createAttendee(options?: { +export interface AttendeeOptions { cal_address?: string; cn?: string; role?: userAttendee["role"]; partstat?: userAttendee["partstat"]; rsvp?: userAttendee["rsvp"]; cutype?: userAttendee["cutype"]; -}): userAttendee { +} + +export function createAttendee(options?: AttendeeOptions): userAttendee { return { cal_address: options?.cal_address ?? "", cn: options?.cn ?? "", diff --git a/src/features/User/userAPI.ts b/src/features/User/userAPI.ts index 19ffea0..9992e1f 100644 --- a/src/features/User/userAPI.ts +++ b/src/features/User/userAPI.ts @@ -44,9 +44,7 @@ export async function getUserDetails(id: string): Promise { } export async function getResourceDetails(id: string): Promise { - const resource = await api - .get(`api/resources/${encodeURIComponent(id)}`) - .json(); + const resource = await api.get(`api/resources/${id}`).json(); return resource as ResourceData; } diff --git a/src/locales/fr.json b/src/locales/fr.json index 1938a78..b9c0234 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -56,7 +56,7 @@ "select_timezone": "Sélectionner le fuseau horaire", "moreOptions": "Plus d'options", "search": "Rechercher", - "resource": "Ressources" + "resource": "Ressource" }, "search": { "searchIn": "Rechercher dans", diff --git a/src/utils/eventFormTempStorage.ts b/src/utils/eventFormTempStorage.ts index 561e954..0ccd8b4 100644 --- a/src/utils/eventFormTempStorage.ts +++ b/src/utils/eventFormTempStorage.ts @@ -173,5 +173,7 @@ export function restoreFormDataFromTemp( ) { setters.setHasEndDateChanged(tempData.hasEndDateChanged); } - setters.setSelectedResources?.(tempData.resources ?? []); + if (tempData.resources !== undefined) { + setters.setSelectedResources?.(tempData.resources); + } }