#601 event creation prefill resources (#641)

Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
lethemanh
2026-03-18 17:31:29 +07:00
committed by GitHub
parent daf7257292
commit 3a84756872
9 changed files with 83 additions and 41 deletions
@@ -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 () => {
+21
View File
@@ -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" };
@@ -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);
@@ -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
+14 -16
View File
@@ -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<userAttendee[]>(
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
};
+4 -2
View File
@@ -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 ?? "",
+1 -3
View File
@@ -44,9 +44,7 @@ export async function getUserDetails(id: string): Promise<OpenPaasUserData> {
}
export async function getResourceDetails(id: string): Promise<ResourceData> {
const resource = await api
.get(`api/resources/${encodeURIComponent(id)}`)
.json();
const resource = await api.get(`api/resources/${id}`).json();
return resource as ResourceData;
}
+1 -1
View File
@@ -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",
+3 -1
View File
@@ -173,5 +173,7 @@ export function restoreFormDataFromTemp(
) {
setters.setHasEndDateChanged(tempData.hasEndDateChanged);
}
setters.setSelectedResources?.(tempData.resources ?? []);
if (tempData.resources !== undefined) {
setters.setSelectedResources?.(tempData.resources);
}
}