From b270d89791b59d3cd18126bc30e99f112aed828c Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Sun, 24 May 2026 18:01:10 -0400 Subject: [PATCH 1/5] the creator of a meeting is now shown as required --- src/backend/src/services/calendar.services.ts | 74 +++++++++++++------ 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/backend/src/services/calendar.services.ts b/src/backend/src/services/calendar.services.ts index 8e07d2f609..61d8ccd63b 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,14 @@ export default class CalendarService { // Check for conflicts using expanded slots const { hasConflict, conflictingEvent } = await checkEventConflicts(scheduleSlots, organization, location, undefined); + // Returns whether the event creator is part of the list of required members + const creatorInRequiredMembers = () => { + for (const member of requiredMemberIds) { + if (member === submitter.userId) return true; + } + return false; + }; + const newEvent = await prisma.event.create({ data: { userCreatedId: submitter.userId, @@ -429,7 +436,9 @@ export default class CalendarService { title, eventTypeId, requiredMembers: { - connect: requiredMemberIds.map((userId) => ({ userId })) + connect: requiredMemberIds + .concat(creatorInRequiredMembers() ? [] : [submitter.userId]) + .map((userId) => ({ userId })) }, optionalMembers: { connect: optionalMemberIds.map((userId) => ({ userId })) @@ -471,7 +480,11 @@ export default class CalendarService { let calendarEventIds: string[] = []; if (process.env.NODE_ENV === 'production') { try { - const allMemberIds = [...requiredMemberIds, ...optionalMemberIds]; + const allMemberIds = [ + ...requiredMemberIds, + ...(creatorInRequiredMembers() ? [] : [submitter.userId]), + ...optionalMemberIds + ]; const isInPerson = !!location; calendarEventIds = await createCalendarEvent( @@ -496,7 +509,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(requiredMemberIds).concat(creatorInRequiredMembers() ? [] : submitter.userId) + } + } }); // get the user settings for all the members invited, who are leaderingship @@ -633,17 +650,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 +756,19 @@ export default class CalendarService { } } + // Returns whether the event creator is part of the list of required members + const creatorInRequiredMembers = () => { + for (const member of requiredMemberIds) { + if (member === submitter.userId) return true; + } + return false; + }; + // 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(requiredMemberIds)), + ...(creatorInRequiredMembers() ? [] : [{ userId: submitter.userId }]) + ]; const updatedOptionalMembers = getPrismaQueryUserIds(await getUsers(optionalMemberIds)); // Update the event with new data (excluding schedule slots) From 6ffc70b7fe8f0702dc9fad27f0c7d1571a112f42 Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Sun, 24 May 2026 18:30:17 -0400 Subject: [PATCH 2/5] updated tests to reflect changed number of required members --- src/backend/tests/unit/calendar.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/tests/unit/calendar.test.ts b/src/backend/tests/unit/calendar.test.ts index 50a6b39623..f4df9bbafa 100644 --- a/src/backend/tests/unit/calendar.test.ts +++ b/src/backend/tests/unit/calendar.test.ts @@ -901,7 +901,7 @@ describe('Calendar Tests', () => { expect(result.title).toBe('Team Sync'); expect(result.eventTypeId).toBe(eventType.eventTypeId); - expect(result.requiredMembers).toHaveLength(1); + expect(result.requiredMembers).toHaveLength(2); expect(result.requiredMembers[0].userId).toBe(member.userId); expect(result.optionalMembers).toHaveLength(1); expect(result.optionalMembers[0].userId).toBe(adminUser.userId); @@ -1007,7 +1007,7 @@ describe('Calendar Tests', () => { expect(result.title).toBe('Minimal Event'); expect(result.eventTypeId).toBe(eventType.eventTypeId); - expect(result.requiredMembers).toHaveLength(1); + expect(result.requiredMembers).toHaveLength(2); expect(result.requiredMembers[0].userId).toBe(member.userId); expect(result.optionalMembers).toHaveLength(1); expect(result.optionalMembers[0].userId).toBe(adminUser.userId); @@ -1887,7 +1887,7 @@ describe('Calendar Tests', () => { expect(result.eventId).toBe(event.eventId); expect(result.title).toBe('Updated Event Title'); - expect(result.requiredMembers).toHaveLength(1); + expect(result.requiredMembers).toHaveLength(2); expect(result.requiredMembers[0].userId).toBe(newMember.userId); expect(result.optionalMembers).toHaveLength(1); expect(result.optionalMembers[0].userId).toBe(adminUser.userId); From 6f8b70e0312ac3360dba8b8e797e5196c5fbe666 Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Sun, 24 May 2026 18:39:52 -0400 Subject: [PATCH 3/5] updated tests to reflect changed number of required members again --- src/backend/tests/unit/calendar.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/tests/unit/calendar.test.ts b/src/backend/tests/unit/calendar.test.ts index f4df9bbafa..ddcbaa9b25 100644 --- a/src/backend/tests/unit/calendar.test.ts +++ b/src/backend/tests/unit/calendar.test.ts @@ -902,7 +902,7 @@ describe('Calendar Tests', () => { expect(result.title).toBe('Team Sync'); expect(result.eventTypeId).toBe(eventType.eventTypeId); expect(result.requiredMembers).toHaveLength(2); - expect(result.requiredMembers[0].userId).toBe(member.userId); + 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); @@ -1008,7 +1008,7 @@ describe('Calendar Tests', () => { expect(result.title).toBe('Minimal Event'); expect(result.eventTypeId).toBe(eventType.eventTypeId); expect(result.requiredMembers).toHaveLength(2); - expect(result.requiredMembers[0].userId).toBe(member.userId); + 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); @@ -1888,7 +1888,7 @@ describe('Calendar Tests', () => { expect(result.eventId).toBe(event.eventId); expect(result.title).toBe('Updated Event Title'); expect(result.requiredMembers).toHaveLength(2); - expect(result.requiredMembers[0].userId).toBe(newMember.userId); + 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([]); From e41409e41e1b72681d0271c5f3a1e36eec0fe3fb Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Mon, 25 May 2026 14:19:52 -0400 Subject: [PATCH 4/5] made a constant instead of a method for creator in required --- src/backend/src/services/calendar.services.ts | 39 ++++++------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/backend/src/services/calendar.services.ts b/src/backend/src/services/calendar.services.ts index 61d8ccd63b..aec453da3a 100644 --- a/src/backend/src/services/calendar.services.ts +++ b/src/backend/src/services/calendar.services.ts @@ -421,13 +421,10 @@ export default class CalendarService { // Check for conflicts using expanded slots const { hasConflict, conflictingEvent } = await checkEventConflicts(scheduleSlots, organization, location, undefined); - // Returns whether the event creator is part of the list of required members - const creatorInRequiredMembers = () => { - for (const member of requiredMemberIds) { - if (member === submitter.userId) return true; - } - return false; - }; + const allRequiredMembers = [ + ...requiredMemberIds, + ...(requiredMemberIds.includes(submitter.userId) ? [] : [submitter.userId]) + ]; const newEvent = await prisma.event.create({ data: { @@ -436,9 +433,7 @@ export default class CalendarService { title, eventTypeId, requiredMembers: { - connect: requiredMemberIds - .concat(creatorInRequiredMembers() ? [] : [submitter.userId]) - .map((userId) => ({ userId })) + connect: allRequiredMembers.map((userId) => ({ userId })) }, optionalMembers: { connect: optionalMemberIds.map((userId) => ({ userId })) @@ -480,11 +475,7 @@ export default class CalendarService { let calendarEventIds: string[] = []; if (process.env.NODE_ENV === 'production') { try { - const allMemberIds = [ - ...requiredMemberIds, - ...(creatorInRequiredMembers() ? [] : [submitter.userId]), - ...optionalMemberIds - ]; + const allMemberIds = [...allRequiredMembers, ...optionalMemberIds]; const isInPerson = !!location; calendarEventIds = await createCalendarEvent( @@ -511,7 +502,7 @@ export default class CalendarService { const members = await prisma.user.findMany({ where: { userId: { - in: optionalMemberIds.concat(requiredMemberIds).concat(creatorInRequiredMembers() ? [] : submitter.userId) + in: optionalMemberIds.concat(allRequiredMembers) } } }); @@ -756,19 +747,13 @@ export default class CalendarService { } } - // Returns whether the event creator is part of the list of required members - const creatorInRequiredMembers = () => { - for (const member of requiredMemberIds) { - if (member === submitter.userId) return true; - } - return false; - }; + 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)), - ...(creatorInRequiredMembers() ? [] : [{ userId: submitter.userId }]) - ]; + const updatedRequiredMembers = [...getPrismaQueryUserIds(await getUsers(allRequiredMembers))]; const updatedOptionalMembers = getPrismaQueryUserIds(await getUsers(optionalMemberIds)); // Update the event with new data (excluding schedule slots) From 2110e550c80471c78e4ca4ea233f6c213a5b8946 Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Mon, 25 May 2026 23:15:58 -0400 Subject: [PATCH 5/5] added an additional feature that underlines required members --- .../Components/EventAvailabilityPage.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) 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 + )}