#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(mockedToRejectedError).toHaveBeenCalledWith(errorDetails);
expect(result.type).toBe("calendars/addCalendarResource/rejected"); expect(result.type).toBe("calendars/addCalendarResource/fulfilled");
expect(result.payload).toEqual(mockRejectedErrorResult); 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 () => { it("should handle error if addSharedCalendar fails", async () => {
+21
View File
@@ -3,6 +3,7 @@ import {
getOpenPaasUser, getOpenPaasUser,
updateUserConfigurations, updateUserConfigurations,
getResourceDetails, getResourceDetails,
getUserDetails,
} from "@/features/User/userAPI"; } from "@/features/User/userAPI";
import { api } from "@/utils/apiUtils"; 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", () => { describe("getResourceDetails", () => {
it("should fetch and return resource details", async () => { it("should fetch and return resource details", async () => {
const mockResource = { _id: "res-123", name: "Meeting Room A" }; const mockResource = { _id: "res-123", name: "Meeting Room A" };
@@ -11,7 +11,10 @@ import {
import { getEvent } from "@/features/Events/EventApi"; import { getEvent } from "@/features/Events/EventApi";
import { CalendarEvent } from "@/features/Events/EventsTypes"; import { CalendarEvent } from "@/features/Events/EventsTypes";
import { updateAttendeesAfterTimeChange } from "@/features/Events/updateEventHelpers/updateAttendeesAfterTimeChange"; 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 { getDeltaInMilliseconds } from "@/utils/dateUtils";
import { import {
CalendarApi, CalendarApi,
@@ -70,13 +73,18 @@ export const createEventHandlers = (props: EventHandlersProps) => {
end: selectInfo?.end end: selectInfo?.end
? formatLocalDateTime(selectInfo?.end, timezone) ? formatLocalDateTime(selectInfo?.end, timezone)
: "", : "",
attendee: tempUsers.map((user) => attendee: tempUsers.map((user) => {
createAttendee({ const attendeeOption: AttendeeOptions = {
cal_address: user.email, cal_address: user.email,
cn: user.displayName, cn: user.displayName,
rsvp: "TRUE", rsvp: "TRUE",
}) };
),
if (user.objectType === "resource") {
attendeeOption.cutype = "RESOURCE";
}
return createAttendee(attendeeOption);
}),
} as CalendarEvent; } as CalendarEvent;
setTempEvent(newEvent); setTempEvent(newEvent);
@@ -32,12 +32,6 @@ export const addCalendarResourceAsync = createAsyncThunk<
?.replace(".json", "") ?.replace(".json", "")
?.split("/")[0]; ?.split("/")[0];
if (!resourceId) {
return rejectWithValue({
message: "Unable to extract resource ID from calendar link",
} as RejectedError);
}
let owner: OpenPaasUserData = { let owner: OpenPaasUserData = {
firstname: "", firstname: "",
lastname: cal.cal["dav:name"] ?? "", lastname: cal.cal["dav:name"] ?? "",
@@ -46,11 +40,18 @@ export const addCalendarResourceAsync = createAsyncThunk<
}; };
try { try {
await addSharedCalendar(userId, calId, cal); await addSharedCalendar(userId, calId, cal);
const resource = await getResourceDetails(resourceId!);
owner = { if (resourceId) {
...(await getUserDetails(resource.creator)), try {
resource: true, const resource = await getResourceDetails(resourceId!);
}; owner = {
...(await getUserDetails(resource.creator)),
resource: true,
};
} catch (e) {
toRejectedError(e);
}
}
return { return {
calId: cal.cal._links.self?.href calId: cal.cal._links.self?.href
+14 -16
View File
@@ -96,6 +96,12 @@ function EventPopover({
displayName: resource.cn, displayName: resource.cn,
})); }));
}, [event?.attendee]); }, [event?.attendee]);
const eventAttendees = useMemo(
() =>
event?.attendee?.filter((attendee) => attendee.cutype !== "RESOURCE") ??
[],
[event?.attendee]
);
const [showMore, setShowMore] = useState(false); const [showMore, setShowMore] = useState(false);
const [showDescription, setShowDescription] = useState( const [showDescription, setShowDescription] = useState(
@@ -150,11 +156,8 @@ function EventPopover({
}); });
const [attendees, setAttendees] = useState<userAttendee[]>( const [attendees, setAttendees] = useState<userAttendee[]>(
event?.attendee eventAttendees
? event.attendee.filter( ? eventAttendees.filter((a) => a.cal_address !== organizer?.cal_address)
(a) =>
a.cal_address !== organizer?.cal_address && a.cutype !== "RESOURCE"
)
: [] : []
); );
const [alarm, setAlarm] = useState(event?.alarm?.trigger ?? ""); const [alarm, setAlarm] = useState(event?.alarm?.trigger ?? "");
@@ -482,11 +485,9 @@ function EventPopover({
setRepetition(event.repetition ?? ({} as RepetitionObject)); setRepetition(event.repetition ?? ({} as RepetitionObject));
setShowRepeat(event.repetition?.freq ? true : false); setShowRepeat(event.repetition?.freq ? true : false);
setAttendees( setAttendees(
event.attendee eventAttendees
? event.attendee.filter( ? eventAttendees.filter(
(a) => (a) => a.cal_address !== organizer?.cal_address
a.cal_address !== organizer?.cal_address &&
a.cutype !== "RESOURCE"
) )
: [] : []
); );
@@ -512,13 +513,10 @@ function EventPopover({
} }
} }
setSelectedResources(resources ?? []); 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) // Handle tempEvent case (no uid but has attendees from temp calendar search)
setAttendees( setAttendees(
event.attendee.filter( eventAttendees.filter((a) => a.cal_address !== organizer?.cal_address)
(a) =>
a.cal_address !== organizer?.cal_address && a.cutype !== "RESOURCE"
)
); );
setSelectedResources(resources ?? []); setSelectedResources(resources ?? []);
} }
@@ -528,6 +526,7 @@ function EventPopover({
resolvedCalendarTimezone, resolvedCalendarTimezone,
defaultCalendarId, defaultCalendarId,
resources, resources,
eventAttendees,
]); ]);
// Reset state when creating new event (event is empty object or undefined) // Reset state when creating new event (event is empty object or undefined)
@@ -667,7 +666,6 @@ function EventPopover({
resetAllStateToDefault(); resetAllStateToDefault();
setStart(""); setStart("");
setEnd(""); setEnd("");
setSelectedResources([]);
shouldSyncFromRangeRef.current = true; // Reset for next time shouldSyncFromRangeRef.current = true; // Reset for next time
isCalendarIdUserSelectedRef.current = false; // Reset so next open gets fresh default isCalendarIdUserSelectedRef.current = false; // Reset so next open gets fresh default
}; };
+4 -2
View File
@@ -1,13 +1,15 @@
import { userAttendee } from "./attendee"; import { userAttendee } from "./attendee";
export function createAttendee(options?: { export interface AttendeeOptions {
cal_address?: string; cal_address?: string;
cn?: string; cn?: string;
role?: userAttendee["role"]; role?: userAttendee["role"];
partstat?: userAttendee["partstat"]; partstat?: userAttendee["partstat"];
rsvp?: userAttendee["rsvp"]; rsvp?: userAttendee["rsvp"];
cutype?: userAttendee["cutype"]; cutype?: userAttendee["cutype"];
}): userAttendee { }
export function createAttendee(options?: AttendeeOptions): userAttendee {
return { return {
cal_address: options?.cal_address ?? "", cal_address: options?.cal_address ?? "",
cn: options?.cn ?? "", 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> { export async function getResourceDetails(id: string): Promise<ResourceData> {
const resource = await api const resource = await api.get(`api/resources/${id}`).json();
.get(`api/resources/${encodeURIComponent(id)}`)
.json();
return resource as ResourceData; return resource as ResourceData;
} }
+1 -1
View File
@@ -56,7 +56,7 @@
"select_timezone": "Sélectionner le fuseau horaire", "select_timezone": "Sélectionner le fuseau horaire",
"moreOptions": "Plus d'options", "moreOptions": "Plus d'options",
"search": "Rechercher", "search": "Rechercher",
"resource": "Ressources" "resource": "Ressource"
}, },
"search": { "search": {
"searchIn": "Rechercher dans", "searchIn": "Rechercher dans",
+3 -1
View File
@@ -173,5 +173,7 @@ export function restoreFormDataFromTemp(
) { ) {
setters.setHasEndDateChanged(tempData.hasEndDateChanged); setters.setHasEndDateChanged(tempData.hasEndDateChanged);
} }
setters.setSelectedResources?.(tempData.resources ?? []); if (tempData.resources !== undefined) {
setters.setSelectedResources?.(tempData.resources);
}
} }