Skip to content

Hacker admissions emails and Tito duplicate check fix#516

Open
ReehalS wants to merge 13 commits intomainfrom
515-hacker-admissions-emails
Open

Hacker admissions emails and Tito duplicate check fix#516
ReehalS wants to merge 13 commits intomainfrom
515-hacker-admissions-emails

Conversation

@ReehalS
Copy link
Copy Markdown
Member

@ReehalS ReehalS commented May 1, 2026

Closes #515, #502

ReehalS added 5 commits May 1, 2026 03:08
When a pre-fetched existingInvitationsMap is provided (email→url):
- If the email is already in the map, return the cached URL immediately without hitting the Tito API at all.
- Otherwise, create normally.
@ReehalS ReehalS changed the title 515 hacker admissions emails Hacker admissions emails and Tito duplicate check fix May 1, 2026
@ReehalS ReehalS linked an issue May 1, 2026 that may be closed by this pull request
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds hacker admissions emailing (single + bulk) into the admin invites workflow while also reducing Tito duplicate-check latency during bulk sends.

Changes:

  • Introduces hacker admission invite types (accept/waitlist_accept/waitlist/reject) and corresponding single/bulk invite responses.
  • Consolidates the admin invites UI into a unified InvitePanel (single + bulk) shared across hackers/judges/mentors/volunteers.
  • Optimizes Tito duplicate handling by prefetching existing RSVP invitations into an email→URL map and reusing URLs when possible.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
app/_types/emails.ts Adds hacker admission types, labels, and response/result types used across UI + server actions.
app/(pages)/admin/invites/page.tsx Updates invites page to include Hackers tab and use the consolidated InvitePanel.
app/(pages)/admin/_components/InvitePanel/InvitePanel.tsx New shared panel for single/bulk invite flows across roles (with Tito config fetching).
app/(pages)/admin/_components/InvitePanel/SingleInviteForm.tsx New unified single-invite form including hacker admission decision handling.
app/(pages)/admin/_components/InvitePanel/BulkInviteForm.tsx New unified bulk-invite form including hacker CSV Type column + result downloads.
app/(pages)/admin/_utils/generateInviteResultsCSV.ts Adds optional admission Type column support for results export.
app/(pages)/admin/_utils/generateInviteFailuresCSV.ts Adds optional Type column in failures export to enable re-upload for retries.
app/(api)/_actions/emails/sendSingleHackerInvite.ts Implements server action for single hacker admissions emails (email-only vs Tito+Hub).
app/(api)/_actions/emails/sendBulkHackerInvites.ts Implements server action for bulk hacker admissions emails using custom CSV parser.
app/(api)/_actions/emails/parseHackerAdmissionsCSV.ts Adds CSV parsing/validation for hacker admissions (Type column).
app/(api)/_actions/emails/processBulkInvites.ts Allows swapping the default CSV parser with a custom parser per bulk flow.
app/(api)/api/admissions/hackers/route.ts Adds an authenticated admissions API endpoint for batch sending hacker emails.
app/(api)/_actions/tito/getAllRsvpInvitations.ts New helper to prefetch all RSVP invitations for duplicate short-circuiting.
app/(api)/_actions/tito/getOrCreateTitoInvitation.ts Extends Tito invite creation to accept a prefetched invitation map for faster duplicate handling.
app/(api)/_actions/emails/sendBulkMentorOrVolunteerInvites.ts Uses prefetched Tito invitations map to reduce duplicate-check latency.
app/(api)/_actions/emails/transporter.ts Makes Nodemailer transporter lazily initialized and exports DEFAULT_SENDER for guards.
app/(api)/_actions/emails/emailTemplates/2026HackerInviteTemplate.ts Adds 2026 hacker acceptance email template.
app/(api)/_actions/emails/emailTemplates/2026HackerWaitlistAcceptTemplate.ts Adds 2026 hacker waitlist-accept template.
app/(api)/_actions/emails/emailTemplates/2026HackerWaitlistTemplate.ts Adds 2026 hacker waitlist template.
app/(api)/_actions/emails/emailTemplates/2026HackerRejectionTemplate.ts Adds 2026 hacker rejection template.
Deleted legacy judge/mentor/volunteer invite form components Removes now-redundant per-role invite forms replaced by InvitePanel.
Comments suppressed due to low confidence (2)

app/(pages)/admin/_components/InvitePanel/InvitePanel.tsx:59

  • When role changes to a Tito-required role, the effect starts fetching RSVP lists/releases but loading isn't set back to true. This can render SingleInviteForm/BulkInviteForm with empty rsvpLists/releases briefly and allow interaction before data is loaded. Consider setting setLoading(true) at the start of the effect (when needsTito(role)), and optionally clearing loadError/data when switching roles.
    app/(api)/_actions/tito/getOrCreateTitoInvitation.ts:56
  • The comment says duplicate recovery is only for the single-invite path (no pre-fetched map), but the recovery logic runs even when existingInvitationsMap is provided (it triggers on any duplicate error). Either update the comment to match the behavior, or gate the recovery behind !existingInvitationsMap if the intent is to avoid per-email lookups in bulk sends.
  // Duplicate recovery for single-invite path (no pre-fetched map)
  if (!titoResponse.ok && isDuplicateTicketError(titoResponse.error)) {
    console.warn(`[Tito] Duplicate detected for ${email}, attempting recovery`);

    const existingRes = await getRsvpInvitationByEmail(rsvpListSlug, email);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +49 to +51
// Pre-fetch all existing Tito invitations for rows that need them
const existingInvitationsMap = await getAllRsvpInvitations(rsvpListSlug);

Comment on lines +11 to +24
export default async function getAllRsvpInvitations(
rsvpListSlug: string
): Promise<Map<string, string>> {
const map = new Map<string, string>();
const pageSize = 1000;
let page = 1;

let hasMore = true;
while (hasMore) {
try {
const data = await TitoRequest<{
release_invitations: ReleaseInvitation[];
}>(
`/rsvp_lists/${rsvpListSlug}/release_invitations?page[size]=${pageSize}&page[number]=${page}`

const TAB_DESCRIPTIONS: Record<Tab, string> = {
hackers:
'Send a Tito e-ticket and HackDavis Hub registration invite to hackers.',
Comment on lines +18 to +20
judges:
'Send HackDavis Hub invites to judges. Navigate to emergency-invites for one-time links.',
mentors: 'Send Tito e-ticket invites to mentors.',
Comment on lines 18 to 22
/**
* Generates a CSV string from bulk invite results.
* @param rows Merged invite result rows (one per person).
* @param includeHub Set true for hacker invites that include a Hub URL column.
* @param rows Merged invite result rows (one per person).
* @param includeHub Set true for hacker invites that include a Hub URL column.
*/
email: string;
admissionType?: HackerAdmissionType;
titoUrl?: string;
hubUrl?: string; // populated for hacker invites; omitted for mentor-only
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hacker Admissions Emails Fix tito duplicate check latency

3 participants