Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 35 additions & 24 deletions src/backend/src/services/calendar.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -422,14 +421,19 @@ 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,
dateCreated: new Date(),
title,
eventTypeId,
requiredMembers: {
connect: requiredMemberIds.map((userId) => ({ userId }))
connect: allRequiredMembers.map((userId) => ({ userId }))
},
optionalMembers: {
connect: optionalMemberIds.map((userId) => ({ userId }))
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
12 changes: 6 additions & 6 deletions src/backend/tests/unit/calendar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Typography
key={user.userId}
variant="body2"
sx={{ py: 0.25, color: isConfirmed ? 'inherit' : '#ef4345' }}
sx={{
py: 0.25,
color: isConfirmed ? 'inherit' : '#ef4345',
textDecoration: isRequired ? 'underline' : 'none'
}}
>
{displayName}
</Typography>
Expand All @@ -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 (
<Typography
key={user.userId}
variant="body2"
sx={{ py: 0.25, color: isConfirmed ? 'inherit' : '#ef4345' }}
sx={{
py: 0.25,
color: isConfirmed ? 'inherit' : '#ef4345',
textDecoration: isRequired ? 'underline' : 'none'
}}
>
{displayName}
</Typography>
Expand All @@ -383,7 +393,10 @@ export const EventAvailabilityPage: React.FC = () => {

{(currentAvailableUsers.length > 0 || currentUnavailableUsers.length > 0) && (
<Typography variant="caption" color="text.secondary" sx={{ mt: 2, display: 'block' }}>
<span style={{ color: '#ef4345' }}>Red</span> means has not confirmed availability
<span style={{ color: '#ef4345' }}>Red</span> means that member has not confirmed availability
<Typography variant="caption" color="text.secondary" sx={{ mt: 2, display: 'block' }}>
<span style={{ textDecoration: 'underline' }}>Underline</span> means that member is required for the meeting
</Typography>
</Typography>
)}

Expand Down
Loading