Skip to content

Commit 031b387

Browse files
authored
IDOL: Update coffee chat bingo board for SP25 (#835)
1 parent 3dcc3a1 commit 031b387

File tree

8 files changed

+46
-223
lines changed

8 files changed

+46
-223
lines changed

backend/src/API/coffeeChatAPI.ts

+13-127
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import { Request } from 'express';
22
import CoffeeChatDao from '../dao/CoffeeChatDao';
33
import PermissionsManager from '../utils/permissionsManager';
44
import { BadRequestError, PermissionError } from '../utils/errors';
5-
import { getMember, allMembers } from './memberAPI';
6-
import { LEAD_ROLES } from '../consts';
7-
import { getGeneralRoleFromLeadType } from '../utils/memberUtil';
5+
import { getMember } from './memberAPI';
6+
import { ADVISOR_ROLES } from '../consts';
87
import { sendCoffeeChatReminder } from './mailAPI';
98

109
const coffeeChatDao = new CoffeeChatDao();
@@ -66,12 +65,15 @@ export const createCoffeeChat = async (
6665
'approved',
6766
coffeeChat.otherMember
6867
);
69-
const prevChats = [...pendingChats, ...approvedChats];
68+
const prevChats = [
69+
...pendingChats.filter((chat) => !chat.isArchived),
70+
...approvedChats.filter((chat) => !chat.isArchived)
71+
];
7072
const chatExists = prevChats.length > 0;
7173

7274
if (chatExists) {
7375
throw new Error(
74-
'Cannot create coffee chat with member. Previous coffee chats from previous semesters exist.'
76+
'Cannot create coffee chat with member. Coffee chats from current or previous semesters exist.'
7577
);
7678
}
7779

@@ -203,138 +205,22 @@ export const checkMemberMeetsCategory = async (
203205
submitterEmail: string,
204206
category: string
205207
): Promise<{ status: MemberMeetsCategoryStatus; message: string }> => {
206-
const otherMemberProperties = await CoffeeChatDao.getMemberProperties(otherMemberEmail);
207-
const submitterProperties = await CoffeeChatDao.getMemberProperties(submitterEmail);
208208
const otherMember = await getMember(otherMemberEmail);
209209
const submitter = await getMember(submitterEmail);
210-
const haveNoCommonSubteams = (member1: IdolMember, member2: IdolMember): boolean =>
211-
member2.subteams.every((team) => !member1.subteams.includes(team)) &&
212-
member1.subteams.every((team) => !member2.subteams.includes(team));
213210
let status: MemberMeetsCategoryStatus = 'no data';
214211
let message: string = '';
215212

216-
// If otherMember doesn't exist in the DB, assume they are an alumni
217-
if (!otherMember && category === 'an alumni') {
218-
return { status: 'pass', message };
219-
}
220-
221213
// If otherMember and submitter don't exist, status should stay undefined
222214
if (otherMember && submitter) {
223-
if (category === 'an alumni') {
224-
status = (await allMembers()).every((member) => member.email !== otherMember.email)
225-
? 'pass'
226-
: 'fail';
227-
if (status === 'fail') {
228-
message = `${otherMember.firstName} ${otherMember.lastName} is not an alumni`;
229-
}
230-
} else if (category === 'courseplan member') {
231-
status = otherMember.subteams.includes('courseplan') ? 'pass' : 'fail';
232-
if (status === 'fail') {
233-
message = `${otherMember.firstName} ${otherMember?.lastName} is not a CoursePlan member`;
234-
}
235-
} else if (category === 'business member') {
236-
status = otherMember.role === 'business' ? 'pass' : 'fail';
237-
if (status === 'fail') {
238-
message = `${otherMember.firstName} ${otherMember.lastName} is not a business member`;
239-
}
240-
} else if (category === 'idol member') {
241-
status = otherMember.subteams.includes('idol') ? 'pass' : 'fail';
242-
if (status === 'fail') {
243-
message = `${otherMember.firstName} ${otherMember.lastName} is not an IDOL member`;
244-
}
245-
} else if (category === 'curaise member') {
246-
status = otherMember.subteams.includes('curaise') ? 'pass' : 'fail';
247-
if (status === 'fail') {
248-
message = `${otherMember.firstName} ${otherMember.lastName} is not a CURaise member`;
249-
}
250-
} else if (category === 'cornellgo member') {
251-
status = otherMember.subteams.includes('cornellgo') ? 'pass' : 'fail';
252-
if (status === 'fail') {
253-
message = `${otherMember.firstName} ${otherMember.lastName} is not a CornellGo member`;
254-
}
255-
} else if (category === 'carriage member') {
256-
status = otherMember.subteams.includes('carriage') ? 'pass' : 'fail';
215+
if (category === 'a newbie') {
216+
status = otherMember.semesterJoined === 'Spring 2025' ? 'pass' : 'fail';
257217
if (status === 'fail') {
258-
message = `${otherMember.firstName} ${otherMember.lastName} is not a Carriage member`;
218+
message = `${otherMember.firstName} ${otherMember.lastName} is not a newbie`;
259219
}
260-
} else if (category === 'qmi member') {
261-
status = otherMember.subteams.includes('queuemein') ? 'pass' : 'fail';
220+
} else if (category === 'is an advisor') {
221+
status = ADVISOR_ROLES.includes(otherMember.role) ? 'pass' : 'fail';
262222
if (status === 'fail') {
263-
message = `${otherMember.firstName} ${otherMember.lastName} is not a QMI member`;
264-
}
265-
} else if (category === 'cuapts member') {
266-
status = otherMember.subteams.includes('cuapts') ? 'pass' : 'fail';
267-
if (status === 'fail') {
268-
message = `${otherMember.firstName} ${otherMember.lastName} is not a CUApts member`;
269-
}
270-
} else if (category === 'a pm (not your team)') {
271-
const isPm = otherMember.role === 'pm';
272-
const notSameTeam = haveNoCommonSubteams(submitter, otherMember);
273-
status = isPm && notSameTeam ? 'pass' : 'fail';
274-
if (status === 'fail') {
275-
if (!isPm) {
276-
message = `${otherMember.firstName} ${otherMember.lastName} is not a PM`;
277-
} else {
278-
message = `${otherMember.firstName} ${otherMember.lastName} is a PM, but is on the same team as ${submitter?.firstName} ${submitter?.lastName}`;
279-
}
280-
}
281-
} else if (category === 'a tpm (not your team)') {
282-
const isTpm = otherMember.role === 'tpm';
283-
const notSameTeam = haveNoCommonSubteams(submitter, otherMember);
284-
status = isTpm && notSameTeam ? 'pass' : 'fail';
285-
if (status === 'fail') {
286-
if (!isTpm) {
287-
message = `${otherMember.firstName} ${otherMember.lastName} is not a TPM`;
288-
} else {
289-
message = `${otherMember.firstName} ${otherMember.lastName} is a TPM, but is on the same team as ${submitter?.firstName} ${submitter?.lastName}`;
290-
}
291-
}
292-
}
293-
294-
// If otherMemberProperties doesn't exist, status should stay undefined
295-
if (otherMemberProperties) {
296-
if (category === 'a newbie') {
297-
status = otherMemberProperties.newbie ? 'pass' : 'fail';
298-
if (status === 'fail') {
299-
message = `${otherMember.firstName} ${otherMember.lastName} is not a newbie`;
300-
}
301-
} else if (category === 'is/was a TA') {
302-
status = otherMemberProperties.ta ? 'pass' : 'fail';
303-
if (status === 'fail') {
304-
message = `${otherMember.firstName} ${otherMember.lastName} was never a TA`;
305-
}
306-
} else if (category === 'major/minor that is not cs/infosci') {
307-
status = otherMemberProperties.notCsOrInfosci ? 'pass' : 'fail';
308-
if (status === 'fail') {
309-
message = `${otherMember.firstName} ${otherMember.lastName} is a CS or Infosci major`;
310-
}
311-
}
312-
313-
// If submitterProperties doesn't exist, status should stay undefined
314-
if (submitterProperties) {
315-
if (category === 'from a different college') {
316-
status = otherMemberProperties.college !== submitterProperties.college ? 'pass' : 'fail';
317-
if (status === 'fail') {
318-
message = `${otherMember.firstName} ${otherMember.lastName} is from the same college as ${submitter.firstName} ${submitter.lastName} (${otherMemberProperties.college})`;
319-
}
320-
}
321-
}
322-
}
323-
324-
if (category === 'a lead (not your role)') {
325-
const isLead = LEAD_ROLES.includes(otherMember.role);
326-
if (!isLead) {
327-
status = 'fail';
328-
message = `${otherMember.firstName} ${otherMember.lastName} is not a lead`;
329-
} else {
330-
const diffRole = !LEAD_ROLES.includes(submitter.role)
331-
? getGeneralRoleFromLeadType(otherMember.role) !== submitter.role
332-
: getGeneralRoleFromLeadType(otherMember.role) !==
333-
getGeneralRoleFromLeadType(submitter.role);
334-
status = diffRole ? 'pass' : 'fail';
335-
if (!diffRole) {
336-
message = `${otherMember.firstName} ${otherMember.lastName} is a lead, but from the same role (${submitter.role}) as ${submitter.firstName} ${submitter.lastName}`;
337-
}
223+
message = `${otherMember.firstName} ${otherMember.lastName} is not an advisor`;
338224
}
339225
}
340226
}

backend/src/consts.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const COFFEE_CHAT_BINGO_BOARD = [
2-
['an alumni', 'courseplan member', 'a pm (not your team)', 'business member'],
3-
['is/was a TA', 'major/minor that is not cs/infosci', 'idol member', 'a newbie'],
4-
['from a different college', 'curaise member', 'cornellgo member', 'a tpm (not your team)'],
5-
['carriage member', 'qmi member', 'a lead (not your role)', 'cuapts member']
2+
['has studied abroad', 'a newbie', 'plays DTI on roblox', 'sleeps before 12am'],
3+
['double major', 'uses windows', 'is an advisor', 'loves to cook'],
4+
['attended a hackathon', 'collects blindboxes', 'lives in ctown', 'plays a sport'],
5+
['lives on west', 'is left handed', 'has a digital camera', 'loves pineapple on pizza']
66
];
77

88
export default COFFEE_CHAT_BINGO_BOARD;

backend/src/types/DataTypes.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export type DBCoffeeChat = {
115115
category: string;
116116
status: Status;
117117
date: number;
118+
isArchived: boolean;
118119
memberMeetsCategory: MemberMeetsCategoryStatus;
119120
reason?: string;
120121
errorMessage?: string;

backend/tests/CoffeeChatAPI.test.ts

+17-86
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
} from '../src/API/coffeeChatAPI';
1313
import { setMember, deleteMember } from '../src/API/memberAPI';
1414
import { PermissionError } from '../src/utils/errors';
15-
import { getGeneralRoleFromLeadType } from '../src/utils/memberUtil';
1615

1716
const user = fakeIdolMember();
1817
const user2 = fakeIdolMember();
@@ -50,7 +49,7 @@ describe('User is not lead or admin', () => {
5049
test('createCoffeeChat should throw error if previous chats exist', async () => {
5150
await expect(createCoffeeChat(coffeeChat, user)).rejects.toThrow(
5251
new Error(
53-
'Cannot create coffee chat with member. Previous coffee chats from previous semesters exist.'
52+
'Cannot create coffee chat with member. Coffee chats from current or previous semesters exist.'
5453
)
5554
);
5655
});
@@ -191,110 +190,42 @@ describe('User is lead or admin', () => {
191190

192191
describe('More complicated member meets category checks', () => {
193192
const admin = { ...fakeIdolLead() };
194-
const user1 = { ...fakeIdolMember(), subteams: ['team1'], role: 'developer' as Role };
195-
const user2 = { ...fakeIdolMember(), role: 'pm' as Role, subteams: ['team2'] };
196-
const user3 = { ...fakeIdolMember(), role: 'pm' as Role, subteams: ['team1'] };
197-
const user4 = { ...fakeIdolMember(), role: 'business' as Role };
198-
const user5 = { ...fakeIdolMember(), role: 'tpm' as Role, subteams: ['team3'] };
199-
const user6 = { ...fakeIdolMember(), role: 'tpm' as Role, subteams: ['team1'] };
200-
const user7 = { ...fakeIdolMember(), role: 'product-lead' as Role };
201-
const user8 = { ...fakeIdolMember(), role: 'dev-lead' as Role };
202-
const user9 = { ...fakeIdolMember(), role: 'ops-lead' as Role };
203-
const user10 = { ...fakeIdolMember(), role: 'ops-lead' as Role };
193+
const user1 = fakeIdolMember();
194+
const user2 = { ...fakeIdolMember(), role: 'dev-advisor' as Role };
195+
const user3 = { ...fakeIdolMember(), role: 'tpm' as Role };
196+
const user4 = { ...fakeIdolMember(), semesterJoined: 'Spring 2025' };
204197

205198
beforeAll(async () => {
206-
const users = [user1, user2, user3, user4, user5, user6, user7, user8, user9, user10];
199+
const users = [user1, user2, user3, user4];
207200
await Promise.all(users.map((user) => setMember(user, admin)));
208201
});
209202

210203
afterAll(async () => {
211-
const users = [user1, user2, user3, user4, user5, user6, user7, user8, user9, user10];
204+
const users = [user1, user2, user3, user4];
212205
await Promise.all(users.map((user) => deleteMember(user.email, admin)));
213-
await CoffeeChatDao.deleteMemberProperties(user7.email);
214-
await CoffeeChatDao.deleteMemberProperties(user8.email);
215206
});
216207

217-
test('pm that is not on same team', async () => {
218-
const result = await checkMemberMeetsCategory(user2.email, user1.email, 'a pm (not your team)');
208+
test('is an advisor', async () => {
209+
const result = await checkMemberMeetsCategory(user2.email, user1.email, 'is an advisor');
219210
expect(result.status).toBe('pass');
220211
expect(result.message).toBe('');
221212
});
222213

223-
test('pm that is on same team', async () => {
224-
const result = await checkMemberMeetsCategory(user3.email, user1.email, 'a pm (not your team)');
214+
test('is not an advisor', async () => {
215+
const result = await checkMemberMeetsCategory(user3.email, user1.email, 'is an advisor');
225216
expect(result.status).toBe('fail');
226-
expect(result.message).toBe(
227-
`${user3.firstName} ${user3.lastName} is a PM, but is on the same team as ${user1.firstName} ${user1.lastName}`
228-
);
229-
});
230-
231-
test('not a pm', async () => {
232-
const result = await checkMemberMeetsCategory(user4.email, user1.email, 'a pm (not your team)');
233-
expect(result.status).toBe('fail');
234-
expect(result.message).toBe(`${user4.firstName} ${user4.lastName} is not a PM`);
217+
expect(result.message).toBe(`${user3.firstName} ${user3.lastName} is not an advisor`);
235218
});
236219

237-
test('tpm that is not on same team', async () => {
238-
const result = await checkMemberMeetsCategory(
239-
user5.email,
240-
user1.email,
241-
'a tpm (not your team)'
242-
);
220+
test('is newbie', async () => {
221+
const result = await checkMemberMeetsCategory(user4.email, user1.email, 'a newbie');
243222
expect(result.status).toBe('pass');
244223
expect(result.message).toBe('');
245224
});
246225

247-
test('tpm that is on same team', async () => {
248-
const result = await checkMemberMeetsCategory(
249-
user6.email,
250-
user1.email,
251-
'a tpm (not your team)'
252-
);
253-
expect(result.status).toBe('fail');
254-
expect(result.message).toBe(
255-
`${user6.firstName} ${user6.lastName} is a TPM, but is on the same team as ${user1.firstName} ${user1.lastName}`
256-
);
257-
});
258-
259-
test('not a tpm', async () => {
260-
const result = await checkMemberMeetsCategory(
261-
user4.email,
262-
user1.email,
263-
'a tpm (not your team)'
264-
);
265-
expect(result.status).toBe('fail');
266-
expect(result.message).toBe(`${user4.firstName} ${user4.lastName} is not a TPM`);
267-
});
268-
269-
test('a lead that is not same role', async () => {
270-
const result = await checkMemberMeetsCategory(
271-
user7.email,
272-
user1.email,
273-
'a lead (not your role)'
274-
);
275-
expect(result.status).toBe('pass');
276-
expect(result.message).toBe('');
277-
});
278-
279-
test('a lead that is the same role', async () => {
280-
const result = await checkMemberMeetsCategory(
281-
user8.email,
282-
user1.email,
283-
'a lead (not your role)'
284-
);
285-
expect(result.status).toBe('fail');
286-
expect(result.message).toBe(
287-
`${user8.firstName} ${user8.lastName} is a lead, but from the same role (${getGeneralRoleFromLeadType(user8.role)}) as ${user1.firstName} ${user1.lastName}`
288-
);
289-
});
290-
291-
test('not a lead', async () => {
292-
const result = await checkMemberMeetsCategory(
293-
user4.email,
294-
user1.email,
295-
'a lead (not your role)'
296-
);
226+
test('is newbie', async () => {
227+
const result = await checkMemberMeetsCategory(user2.email, user1.email, 'a newbie');
297228
expect(result.status).toBe('fail');
298-
expect(result.message).toBe(`${user4.firstName} ${user4.lastName} is not a lead`);
229+
expect(result.message).toBe(`${user2.firstName} ${user2.lastName} is not a newbie`);
299230
});
300231
});

backend/tests/data/createData.ts

+1
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ export const fakeCoffeeChat = (): CoffeeChat => {
223223
category: 'test',
224224
status: 'pending' as Status,
225225
date: Date.now(),
226+
isArchived: false,
226227
memberMeetsCategory: 'no data' as MemberMeetsCategoryStatus
227228
};
228229
return CC;

common-types/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ interface CoffeeChat {
245245
readonly category: string;
246246
readonly status: Status;
247247
readonly date: number;
248+
readonly isArchived: boolean;
248249
readonly memberMeetsCategory: MemberMeetsCategoryStatus;
249250
readonly reason?: string;
250251
readonly errorMessage?: string;

frontend/src/components/Admin/CoffeeChats/CoffeeChats.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ const CoffeeChats: React.FC = () => {
4646
setSelectedMember(member);
4747

4848
CoffeeChatAPI.getCoffeeChatsByUser(member).then((coffeeChats) => {
49-
setSpecificApprovedChats(coffeeChats.filter((chat) => chat.status === 'approved'));
50-
setSpecificPendingChats(coffeeChats.filter((chat) => chat.status === 'pending'));
51-
setSpecificRejectedChats(coffeeChats.filter((chat) => chat.status === 'rejected'));
49+
const filteredChats = coffeeChats.filter((chat) => !chat.isArchived);
50+
setSpecificApprovedChats(filteredChats.filter((chat) => chat.status === 'approved'));
51+
setSpecificPendingChats(filteredChats.filter((chat) => chat.status === 'pending'));
52+
setSpecificRejectedChats(filteredChats.filter((chat) => chat.status === 'rejected'));
5253
setIsChatLoading(false);
5354
});
5455

frontend/src/components/Forms/CoffeeChatsForm/CoffeeChatsForm.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ const CoffeeChatsForm: React.FC = () => {
2727
useEffect(() => {
2828
MembersAPI.getAllMembers().then((members) => setMembersList(members));
2929
CoffeeChatAPI.getCoffeeChatsByUser(userInfo).then((coffeeChats) => {
30-
setApprovedChats(coffeeChats.filter((chat) => chat.status === 'approved'));
31-
setPendingChats(coffeeChats.filter((chat) => chat.status === 'pending'));
32-
setRejectedChats(coffeeChats.filter((chat) => chat.status === 'rejected'));
30+
const filteredChats = coffeeChats.filter((chat) => !chat.isArchived);
31+
setApprovedChats(filteredChats.filter((chat) => chat.status === 'approved'));
32+
setPendingChats(filteredChats.filter((chat) => chat.status === 'pending'));
33+
setRejectedChats(filteredChats.filter((chat) => chat.status === 'rejected'));
3334
setIsChatLoading(false);
3435
});
3536
CoffeeChatAPI.getCoffeeChatBingoBoard().then((board) => setBingoBoard(board));
@@ -118,6 +119,7 @@ const CoffeeChatsForm: React.FC = () => {
118119
category,
119120
status: 'pending' as Status,
120121
date: new Date().getTime(),
122+
isArchived: false,
121123
memberMeetsCategory,
122124
errorMessage
123125
};

0 commit comments

Comments
 (0)