diff --git a/src/backend/src/services/calendar.services.ts b/src/backend/src/services/calendar.services.ts index 8e07d2f609..aec453da3a 100644 --- a/src/backend/src/services/calendar.services.ts +++ b/src/backend/src/services/calendar.services.ts @@ -311,17 +311,16 @@ export default class CalendarService { }); // Validate required memberIds - if (requiredMemberIds.length > 0) { - const foundMembers = await prisma.user.findMany({ - where: { - userId: { in: requiredMemberIds }, - organizations: { some: { organizationId: organization.organizationId } } - } - }); - if (foundMembers.length !== requiredMemberIds.length) { - const missingIds = requiredMemberIds.filter((id) => !foundMembers.some((user) => user.userId === id)); - throw new NotFoundException('User', missingIds.join(', ')); + + const foundMembers = await prisma.user.findMany({ + where: { + userId: { in: requiredMemberIds || submitter.userId }, + organizations: { some: { organizationId: organization.organizationId } } } + }); + if (foundMembers.length !== requiredMemberIds.length) { + const missingIds = requiredMemberIds.filter((id) => !foundMembers.some((user) => user.userId === id)); + throw new NotFoundException('User', missingIds.join(', ')); } // Validate optionals memberIds @@ -422,6 +421,11 @@ export default class CalendarService { // Check for conflicts using expanded slots const { hasConflict, conflictingEvent } = await checkEventConflicts(scheduleSlots, organization, location, undefined); + const allRequiredMembers = [ + ...requiredMemberIds, + ...(requiredMemberIds.includes(submitter.userId) ? [] : [submitter.userId]) + ]; + const newEvent = await prisma.event.create({ data: { userCreatedId: submitter.userId, @@ -429,7 +433,7 @@ export default class CalendarService { title, eventTypeId, requiredMembers: { - connect: requiredMemberIds.map((userId) => ({ userId })) + connect: allRequiredMembers.map((userId) => ({ userId })) }, optionalMembers: { connect: optionalMemberIds.map((userId) => ({ userId })) @@ -471,7 +475,7 @@ export default class CalendarService { let calendarEventIds: string[] = []; if (process.env.NODE_ENV === 'production') { try { - const allMemberIds = [...requiredMemberIds, ...optionalMemberIds]; + const allMemberIds = [...allRequiredMembers, ...optionalMemberIds]; const isInPerson = !!location; calendarEventIds = await createCalendarEvent( @@ -496,7 +500,11 @@ export default class CalendarService { if (foundEventType.sendSlackNotifications) { const members = await prisma.user.findMany({ - where: { userId: { in: optionalMemberIds.concat(requiredMemberIds) } } + where: { + userId: { + in: optionalMemberIds.concat(allRequiredMembers) + } + } }); // get the user settings for all the members invited, who are leaderingship @@ -633,17 +641,15 @@ export default class CalendarService { } // Validate required memberIds - if (requiredMemberIds.length > 0) { - const foundMembers = await prisma.user.findMany({ - where: { - userId: { in: requiredMemberIds }, - organizations: { some: { organizationId: organization.organizationId } } - } - }); - if (foundMembers.length !== requiredMemberIds.length) { - const missingIds = requiredMemberIds.filter((id) => !foundMembers.some((user) => user.userId === id)); - throw new NotFoundException('User', missingIds.join(', ')); + const foundMembers = await prisma.user.findMany({ + where: { + userId: { in: requiredMemberIds || submitter.userId }, + organizations: { some: { organizationId: organization.organizationId } } } + }); + if (foundMembers.length !== requiredMemberIds.length) { + const missingIds = requiredMemberIds.filter((id) => !foundMembers.some((user) => user.userId === id)); + throw new NotFoundException('User', missingIds.join(', ')); } // Validate optional memberIds @@ -741,8 +747,13 @@ export default class CalendarService { } } + const allRequiredMembers = [ + ...requiredMemberIds, + ...(requiredMemberIds.includes(submitter.userId) ? [] : [submitter.userId]) + ]; + // throw if a user isn't found, then build prisma queries for connecting userIds - const updatedRequiredMembers = getPrismaQueryUserIds(await getUsers(requiredMemberIds)); + const updatedRequiredMembers = [...getPrismaQueryUserIds(await getUsers(allRequiredMembers))]; const updatedOptionalMembers = getPrismaQueryUserIds(await getUsers(optionalMemberIds)); // Update the event with new data (excluding schedule slots) diff --git a/src/backend/tests/unit/calendar.test.ts b/src/backend/tests/unit/calendar.test.ts index 50a6b39623..ddcbaa9b25 100644 --- a/src/backend/tests/unit/calendar.test.ts +++ b/src/backend/tests/unit/calendar.test.ts @@ -901,8 +901,8 @@ describe('Calendar Tests', () => { expect(result.title).toBe('Team Sync'); expect(result.eventTypeId).toBe(eventType.eventTypeId); - expect(result.requiredMembers).toHaveLength(1); - expect(result.requiredMembers[0].userId).toBe(member.userId); + expect(result.requiredMembers).toHaveLength(2); + expect(result.requiredMembers[1].userId).toBe(member.userId); expect(result.optionalMembers).toHaveLength(1); expect(result.optionalMembers[0].userId).toBe(adminUser.userId); expect(result.shops).toHaveLength(1); @@ -1007,8 +1007,8 @@ describe('Calendar Tests', () => { expect(result.title).toBe('Minimal Event'); expect(result.eventTypeId).toBe(eventType.eventTypeId); - expect(result.requiredMembers).toHaveLength(1); - expect(result.requiredMembers[0].userId).toBe(member.userId); + expect(result.requiredMembers).toHaveLength(2); + expect(result.requiredMembers[1].userId).toBe(member.userId); expect(result.optionalMembers).toHaveLength(1); expect(result.optionalMembers[0].userId).toBe(adminUser.userId); expect(result.shops).toHaveLength(1); @@ -1887,8 +1887,8 @@ describe('Calendar Tests', () => { expect(result.eventId).toBe(event.eventId); expect(result.title).toBe('Updated Event Title'); - expect(result.requiredMembers).toHaveLength(1); - expect(result.requiredMembers[0].userId).toBe(newMember.userId); + expect(result.requiredMembers).toHaveLength(2); + expect(result.requiredMembers[1].userId).toBe(newMember.userId); expect(result.optionalMembers).toHaveLength(1); expect(result.optionalMembers[0].userId).toBe(adminUser.userId); expect(result.documents).toEqual([]); diff --git a/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx b/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx index c0e133b704..35daeb1ff7 100644 --- a/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx +++ b/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx @@ -334,12 +334,17 @@ export const EventAvailabilityPage: React.FC = () => { {currentAvailableUsers.length > 0 ? ( reorderByConfirmation(currentAvailableUsers).map((user) => { const isConfirmed = event.confirmedMembers.some((cm) => cm.userId === user.userId); + const isRequired = event.requiredMembers.some((cm) => cm.userId === user.userId); const displayName = fullNamePipe(user); return ( {displayName} @@ -361,12 +366,17 @@ export const EventAvailabilityPage: React.FC = () => { {currentUnavailableUsers.length > 0 ? ( reorderByConfirmation(currentUnavailableUsers).map((user) => { const isConfirmed = event.confirmedMembers.some((cm) => cm.userId === user.userId); + const isRequired = event.requiredMembers.some((cm) => cm.userId === user.userId); const displayName = fullNamePipe(user); return ( {displayName} @@ -383,7 +393,10 @@ export const EventAvailabilityPage: React.FC = () => { {(currentAvailableUsers.length > 0 || currentUnavailableUsers.length > 0) && ( - Red means has not confirmed availability + Red means that member has not confirmed availability + + Underline means that member is required for the meeting + )}