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
106 changes: 82 additions & 24 deletions src/services/checkin/checkin-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ const REGULAR_EVENT_FOR_CHECKIN = {
attendanceCount: 0,
};

const SPECIAL_EVENT_FOR_CHECKIN = {
eventId: uuidv4(),
name: "Second Regular Event",
startTime: new Date((NOW_SECONDS - 600) * 1000).toISOString(),
endTime: new Date((NOW_SECONDS + ONE_HOUR_SECONDS) * 1000).toISOString(),
points: 50,
description: "A second regular event.",
isVirtual: false,
imageUrl: null,
location: "Siebel 2405",
eventType: EventType.enum.SPECIAL,
isVisible: true,
attendanceCount: 0,
};

const MEALS_EVENT = {
eventId: uuidv4(),
name: "Lunch Time",
Expand Down Expand Up @@ -338,19 +353,8 @@ describe("POST /checkin/scan/staff", () => {
expect(attendeeError).toBeNull();
expect(updatedAttendee).toMatchObject({
points: TEST_ATTENDEE_1.points + REGULAR_EVENT_FOR_CHECKIN.points,
[`hasPriority${currentDay}`]: true,
[`hasPriority${currentDay}`]: false, // No priority on first check-in
});

const { data: subscription } = await SupabaseDB.SUBSCRIPTIONS.select(
"*"
)
.eq("userId", TEST_ATTENDEE_1.userId)
.eq("mailingList", payload.eventId)
.single()
.throwOnError();
expect(subscription).not.toBeNull();
expect(subscription!.userId).toBe(TEST_ATTENDEE_1.userId);
expect(subscription!.mailingList).toBe(payload.eventId);
}, 100000);

it("should successfully check-in user to a CHECKIN type event and update records", async () => {
Expand Down Expand Up @@ -584,19 +588,8 @@ describe("POST /checkin/event", () => {
.single();
expect(updatedAttendee).toMatchObject({
points: TEST_ATTENDEE_1.points + REGULAR_EVENT_FOR_CHECKIN.points,
[`hasPriority${currentDay}`]: true,
[`hasPriority${currentDay}`]: false, // No priority on first check-in
});

const { data: subscription } = await SupabaseDB.SUBSCRIPTIONS.select(
"*"
)
.eq("userId", TEST_ATTENDEE_1.userId)
.eq("mailingList", payload.eventId)
.single()
.throwOnError();
expect(subscription).not.toBeNull();
expect(subscription.userId!).toBe(TEST_ATTENDEE_1.userId);
expect(subscription.mailingList!).toBe(payload.eventId);
});

it("should successfully check-in to a check in event and update records", async () => {
Expand Down Expand Up @@ -766,6 +759,71 @@ describe("POST /checkin/event", () => {
expect(attendeeAfter).toEqual(attendeeBefore);
expect(attendanceCountAfter).toBe(attendanceCountBefore);
});

it("should give priority after second check-in to regular event", async () => {
// First check-in - should not get priority
payload.eventId = REGULAR_EVENT_FOR_CHECKIN.eventId;
payload.userId = TEST_ATTENDEE_1.userId;

await postAsAdmin("/checkin/event")
.send(payload)
.expect(StatusCodes.OK);

// Verify no priority after first check-in
const { data: attendeeAfterFirst } = await SupabaseDB.ATTENDEES.select()
.eq("userId", payload.userId)
.single();
expect(attendeeAfterFirst).toMatchObject({
[`hasPriority${currentDay}`]: false,
});

// Verify first event is in attendance record
const { data: attendeeAttendanceAfterFirst } =
await SupabaseDB.ATTENDEE_ATTENDANCES.select("eventsAttended")
.eq("userId", payload.userId)
.single();
expect(attendeeAttendanceAfterFirst?.eventsAttended).toContain(
REGULAR_EVENT_FOR_CHECKIN.eventId
);
expect(attendeeAttendanceAfterFirst?.eventsAttended).toHaveLength(1);

await SupabaseDB.EVENTS.insert([SPECIAL_EVENT_FOR_CHECKIN]);

// Second check-in - should get priority
payload.eventId = SPECIAL_EVENT_FOR_CHECKIN.eventId;

await postAsAdmin("/checkin/event")
.send(payload)
.expect(StatusCodes.OK);

// Verify priority is given after second check-in
const { data: attendeeAfterSecond } =
await SupabaseDB.ATTENDEES.select()
.eq("userId", payload.userId)
.single();
expect(attendeeAfterSecond).toMatchObject({
[`hasPriority${currentDay}`]: true,
});

// Verify both events are in the eventsAttended array
const { data: attendeeAttendance } =
await SupabaseDB.ATTENDEE_ATTENDANCES.select("eventsAttended")
.eq("userId", payload.userId)
.single();
expect(attendeeAttendance?.eventsAttended).toContain(
SPECIAL_EVENT_FOR_CHECKIN.eventId
);
expect(attendeeAttendance?.eventsAttended).toContain(
REGULAR_EVENT_FOR_CHECKIN.eventId
);
expect(attendeeAttendance?.eventsAttended).toHaveLength(2);

// Clean up
await SupabaseDB.EVENTS.delete().eq(
"eventId",
SPECIAL_EVENT_FOR_CHECKIN.eventId
);
});
});

describe("POST /checkin/scan/merch", () => {
Expand Down
52 changes: 44 additions & 8 deletions src/services/checkin/checkin-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,55 @@ export async function checkInUserToEvent(eventId: string, userId: string) {
.single()
.throwOnError();

// Update attendance records first
await updateAttendanceRecords(eventId, userId);

// Check if user should get priority (only for non-meal/checkin events and if they have attended >1 event)
if (
event.eventType !== EventType.Enum.MEALS &&
event.eventType !== EventType.Enum.CHECKIN
) {
await updateAttendeePriority(userId);
// Check how many events the user has attended (including the current one)
const { data: attendeeAttendance } =
await SupabaseDB.ATTENDEE_ATTENDANCES.select("eventsAttended")
.eq("userId", userId)
.maybeSingle()
.throwOnError();

const eventsAttended = attendeeAttendance?.eventsAttended || [];

if (eventsAttended.length > 0) {
// Get details of all attended events to filter by type and day
const { data: attendedEvents } = await SupabaseDB.EVENTS.select(
"eventId, eventType, startTime"
)
.in("eventId", eventsAttended)
.throwOnError();

const currentDay = getCurrentDay();

// Filter events: exclude MEALS and CHECKIN, and only count events from current day
const filteredEvents =
attendedEvents?.filter((eventData) => {
const eventDate = new Date(eventData.startTime);
const eventDay = new Intl.DateTimeFormat("en-US", {
timeZone: "America/Chicago",
weekday: "short",
}).format(eventDate) as DayKey;

return (
eventData.eventType !== EventType.Enum.MEALS &&
eventData.eventType !== EventType.Enum.CHECKIN &&
eventDay === currentDay
);
}) || [];

// Only give priority if they have attended 2 or more qualifying events today
if (filteredEvents.length >= 2) {
await updateAttendeePriority(userId);
}
}
}

await SupabaseDB.SUBSCRIPTIONS.insert({
userId: userId,
mailingList: eventId,
}).throwOnError();

await updateAttendanceRecords(eventId, userId);
await assignPixelsToUser(userId, event.points);
}

Expand Down