From ca9d29384ea240c2073f92a24a134fddc37a1e35 Mon Sep 17 00:00:00 2001 From: HanCotterell Date: Mon, 13 Oct 2025 14:29:42 -0700 Subject: [PATCH 1/2] Adds error handling and filtering of valid student timezones. --- src/email/templates/matchTeamIntro.ts | 37 +++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/email/templates/matchTeamIntro.ts b/src/email/templates/matchTeamIntro.ts index 0da858f..a9c7994 100644 --- a/src/email/templates/matchTeamIntro.ts +++ b/src/email/templates/matchTeamIntro.ts @@ -27,6 +27,11 @@ export async function getList(prisma: PrismaClient, event: PartialEvent): Promis type Interval = { start: number; end: number }; type Person = { timezone: string; timeManagementPlan?: Record }; + function isValidTimezone(tz: string){ + const dt = DateTime.now().setZone(tz); + return dt.isValid; +} + function localIntervalToUtc(day: string, interval: Interval, tz: string) { const weekdays: Record = { monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 7 }; const baseDay = weekdays[day.toLowerCase()] ?? 1; @@ -74,18 +79,30 @@ export async function getList(prisma: PrismaClient, event: PartialEvent): Promis function findCommonTimeslots(students: Person[]): Record> { const days = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]; const result: Record> = {}; - for (const day of days) { - const intervalsUTCPerStudent = students.map(s => - (s.timeManagementPlan?.[day] ?? []).map(iv => localIntervalToUtc(day, iv, s.timezone)) - ); - const overlapUTC = intersectIntervals(intervalsUTCPerStudent); - if (overlapUTC.length > 0) { - result[day] = {}; - students.forEach(s => { - result[day][s.timezone] = overlapUTC.map(iv => formatInterval(iv, s.timezone)); - }); + try { + const validStudents = students.filter(student => isValidTimezone(student.timezone)); + + if (validStudents.length === 0) { + return result; + } + + for (const day of days) { + const intervalsUTCPerStudent = validStudents.map(s => + (s.timeManagementPlan?.[day] ?? []).map(iv => localIntervalToUtc(day, iv, s.timezone)) + ); + const overlapUTC = intersectIntervals(intervalsUTCPerStudent); + if (overlapUTC.length > 0) { + result[day] = {}; + validStudents.forEach(s => { + result[day][s.timezone] = overlapUTC.map(iv => formatInterval(iv, s.timezone)); + }); + } } + } catch (error) { + console.warn('Error occurred while finding common timeslots:', error); + return {}; } + return result; } From 2a1f7af8048ff79dd75c4cd48515ecbe4d431626 Mon Sep 17 00:00:00 2001 From: HanCotterell Date: Sun, 19 Oct 2025 21:19:29 -0700 Subject: [PATCH 2/2] Revert "Revert 23b33d1599f77b9ab5b546e06380b53774d494db" This reverts commit 770e0b0dc8e56e02f947a784d67c7e4a345146e1. Also adds error fix so that if code fails for any reason an empty list of common timeslots are passed to email template. --- src/email/templates/matchTeamIntro.md | 8 ++++++++ src/email/templates/matchTeamIntro.ts | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/email/templates/matchTeamIntro.md b/src/email/templates/matchTeamIntro.md index 5df9abc..3971ca6 100644 --- a/src/email/templates/matchTeamIntro.md +++ b/src/email/templates/matchTeamIntro.md @@ -17,7 +17,15 @@ subject: "[Action Required] {{ event.name }} Team Intro: {{{ join (mapToKey proj **ACTION REQUIRED -- NEXT STEPS:** +{{#if commonTimeslots}} +**Suggested Meeting Times (all students available):** +{{#each commonTimeslots}} + - {{@key}}: {{#each this}}{{#each this}}{{this}} ({{@../key}}){{#unless @last}} || {{/unless}}{{/each}}{{/each}} +{{/each}} +{{else}} - **Mentors:** Send a [When2meet](https://www.when2meet.com/) for recurring meeting availability +{{/if}} + - **Students:**{{# if project.issueUrl }} 1. Exactly one member of your team should post "I'm working on this" in [the issue]({{project.issueUrl}}) AS SOON AS POSSIBLE to claim it. (If someone else from your team has already done this, you don't need to do it.){{/if}} 1. reply to this email and introduce yourself (e.g. where you go to school, career goals, or anything else you want to share with your mentor) diff --git a/src/email/templates/matchTeamIntro.ts b/src/email/templates/matchTeamIntro.ts index 3fc2f1d..a9c7994 100644 --- a/src/email/templates/matchTeamIntro.ts +++ b/src/email/templates/matchTeamIntro.ts @@ -2,6 +2,7 @@ import { MentorStatus, StudentStatus, PrismaClient } from '@prisma/client'; import { ProjectStatus } from '../../enums'; import { EmailContext } from '../spec'; import { PartialEvent } from '../loader'; +import { DateTime } from 'luxon'; export async function getId(): Promise { return `matchTeamIntro`; @@ -18,7 +19,7 @@ export async function getList(prisma: PrismaClient, event: PartialEvent): Promis }, include: { mentors: { where: { status: MentorStatus.ACCEPTED } }, - students: { where: { status: StudentStatus.ACCEPTED } }, + students: { where: { status: StudentStatus.ACCEPTED }, select: { timezone: true, timeManagementPlan: true } }, }, });