Skip to content

Send rejection emails when bulk rejecting partner applications - #3678

Merged
steven-tey merged 2 commits into
mainfrom
bulk-reject-send-emails
Apr 1, 2026
Merged

Send rejection emails when bulk rejecting partner applications#3678
steven-tey merged 2 commits into
mainfrom
bulk-reject-send-emails

Conversation

@devkiran

@devkiran devkiran commented Apr 1, 2026

Copy link
Copy Markdown
Collaborator

Added email notification functionality to bulk partner application rejection

Summary by CodeRabbit

  • New Features

    • Partners now receive batch email notifications when their applications are bulk-rejected; emails include program name and support contact.
  • Improvements

    • Emails are sent only after audit logging and fraud checks complete.
    • Emails are sent only to partners with valid emails; send failures are caught and logged without interrupting processing.

@vercel

vercel Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview Apr 1, 2026 2:25pm

Request Review

@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Fetches program details, preserves audit logging and fraud resolution in a post-transaction background task, then conditionally sends batch rejection emails to partners with emails using program.name and program.supportEmail; email errors are caught and logged without affecting earlier background work.

Changes

Cohort / File(s) Summary
Partner Application Rejection Enhancement
apps/web/lib/actions/partners/bulk-reject-partner-applications.ts
Refactors post-transaction background work from a direct waitUntil(Promise.allSettled(...)) to waitUntil(async IIFE). Keeps recordAuditLog and resolveFraudGroups in the IIFE, then fetches program (name, slug, supportEmail), filters enrollments to partners with emails, and calls sendBatchEmail rendering PartnerApplicationRejected for recipients; email send failures are caught and logged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Action as ServerAction
  participant DB
  participant Audit as Audit/Fraud
  participant Email as EmailService

  Client->>Action: invoke bulkRejectPartnerApplications
  Action->>DB: transactional updates (reject applications)
  Action->>Action: waitUntil(async IIFE)
  activate Action
  Action->>Audit: recordAuditLog(...)
  Action->>Audit: resolveFraudGroups(...)
  Audit-->>Action: settled
  Action->>DB: fetch program (name, slug, supportEmail)
  Action->>Email: sendBatchEmail(recipients, renderedTemplate)
  Email-->>Action: success/failure (errors caught & logged)
  Action-->>Client: respond (transaction completed)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • pepeladeira
  • steven-tey

Poem

🐰 I hopped to the DB, a quiet little chore,
I logged every step and checked every door,
Then stitched up the mails with the program's name,
Sent them in batches — no blame, just a frame,
Hop, send, and sleep till the next spring game.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main change: adding email notifications to the bulk partner application rejection workflow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bulk-reject-send-emails

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/web/lib/actions/partners/bulk-reject-partner-applications.ts (2)

160-165: Consider including partner count in error log for debugging.

Adding the count of partners attempted would help diagnose partial failures.

🔧 Enhanced error logging
           } catch (error) {
             console.error("Failed to send bulk rejection emails", {
               error,
               programId,
+              partnerCount: partnersWithEmail.length,
             });
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/lib/actions/partners/bulk-reject-partner-applications.ts` around
lines 160 - 165, The catch block in bulk-reject-partner-applications.ts logs
failure to send bulk rejection emails but omits how many partners were involved;
update the catch in the try/catch that currently calls console.error("Failed to
send bulk rejection emails", { error, programId }) to include the partner count
(e.g., compute partnerCount from the variable used to iterate/reject partners
such as partners.length or partnerIds.length, guarding for undefined) and
include partnerCount in the logged object so the log becomes something like
console.error(..., { error, programId, partnerCount }) to aid debugging of
partial failures.

132-159: Consider chunking for large batch email sends.

If many partners are rejected at once, the batch email call may exceed Resend's API limit of 100 emails per batch. Without chunking, a large rejection batch could fail or be partially processed.

♻️ Proposed chunking implementation
         const partnersWithEmail = programEnrollments
           .filter(({ partner }) => partner.email)
           .map(({ partner }) => partner);

         if (partnersWithEmail.length > 0) {
           try {
-            await sendBatchEmail(
-              partnersWithEmail.map((partner) => ({
+            const BATCH_SIZE = 100;
+            const emailPayloads = partnersWithEmail.map((partner) => ({
               to: partner.email!,
               subject: `Your application to ${program.name} was not approved`,
               variant: "notifications" as const,
               replyTo: program.supportEmail || "noreply",
               react: PartnerApplicationRejected({
                 partner: {
                   name: partner.name ?? "there",
                   email: partner.email!,
                 },
                 program: {
                   name: program.name,
                   slug: program.slug,
                   supportEmail: program.supportEmail ?? undefined,
                 },
                 rejectionReason: undefined,
                 additionalNotes: undefined,
                 canReapplyImmediately: false,
               }),
-              })),
-            );
+            }));
+
+            for (let i = 0; i < emailPayloads.length; i += BATCH_SIZE) {
+              await sendBatchEmail(emailPayloads.slice(i, i + BATCH_SIZE));
+            }
           } catch (error) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/lib/actions/partners/bulk-reject-partner-applications.ts` around
lines 132 - 159, The current sendBatchEmail call can exceed Resend's
100-recipient limit when partnersWithEmail (derived from programEnrollments) is
large; split partnersWithEmail into chunks of at most 100 and call
sendBatchEmail for each chunk sequentially (or in controlled parallelism),
mapping each chunk to the same payload shape used now; update the try/catch to
handle per-chunk failures (e.g., log which chunk failed or rethrow after
aggregating errors). Ensure you modify the area around partnersWithEmail and the
sendBatchEmail invocation so the batching logic slices partnersWithEmail into
arrays of size <=100 before mapping and sending.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/lib/actions/partners/bulk-reject-partner-applications.ts`:
- Around line 160-165: The catch block in bulk-reject-partner-applications.ts
logs failure to send bulk rejection emails but omits how many partners were
involved; update the catch in the try/catch that currently calls
console.error("Failed to send bulk rejection emails", { error, programId }) to
include the partner count (e.g., compute partnerCount from the variable used to
iterate/reject partners such as partners.length or partnerIds.length, guarding
for undefined) and include partnerCount in the logged object so the log becomes
something like console.error(..., { error, programId, partnerCount }) to aid
debugging of partial failures.
- Around line 132-159: The current sendBatchEmail call can exceed Resend's
100-recipient limit when partnersWithEmail (derived from programEnrollments) is
large; split partnersWithEmail into chunks of at most 100 and call
sendBatchEmail for each chunk sequentially (or in controlled parallelism),
mapping each chunk to the same payload shape used now; update the try/catch to
handle per-chunk failures (e.g., log which chunk failed or rethrow after
aggregating errors). Ensure you modify the area around partnersWithEmail and the
sendBatchEmail invocation so the batching logic slices partnersWithEmail into
arrays of size <=100 before mapping and sending.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bac0b71e-b795-49d6-92de-6db99e738fa3

📥 Commits

Reviewing files that changed from the base of the PR and between 06479e9 and ab0e197.

📒 Files selected for processing (1)
  • apps/web/lib/actions/partners/bulk-reject-partner-applications.ts

@devkiran

devkiran commented Apr 1, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review please

@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

@devkiran Sure! I'll kick off a full review of PR #3678 right away.


🧠 Learnings used
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Learnt from: devkiran
Repo: dubinc/dub PR: 3664
File: apps/web/ui/partners/program-card.tsx:114-117
Timestamp: 2026-03-30T06:47:27.235Z
Learning: In `apps/web/lib/actions/partners/reject-partner-application.ts`, when `allowImmediateReapply` is `true`, the action **deletes** the pending `programEnrollment` row (after updating the `programApplication` record). The partner has no enrollment row left for that program, so they can reapply immediately. When `allowImmediateReapply` is `false`, the enrollment is updated to `status: "rejected"`. These two paths are mutually exclusive, meaning `ProgramCard` with `status === "rejected"` is only ever rendered for the 30-day waiting path, making the hardcoded "You can re-apply in 30 days." message always correct in context.
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/web/lib/actions/partners/bulk-reject-partner-applications.ts (1)

136-159: Consider adding error handling for email sending failures.

The PR description mentions "error handling and logging for email sending failures," but the sendBatchEmail call is not wrapped in a try-catch. If the email sending fails, the error will propagate silently in the waitUntil context without any explicit logging.

♻️ Suggested improvement with error handling
         if (partnersWithEmail.length > 0) {
+          try {
             await sendBatchEmail(
               partnersWithEmail.map((partner) => ({
                 to: partner.email!,
                 subject: `Your application to ${program.name} was not approved`,
                 variant: "notifications",
                 replyTo: program.supportEmail || "noreply",
                 react: PartnerApplicationRejected({
                   partner: {
                     name: partner.name ?? "there",
                     email: partner.email!,
                   },
                   program: {
                     name: program.name,
                     slug: program.slug,
                     supportEmail: program.supportEmail ?? undefined,
                   },
                   rejectionReason: undefined,
                   additionalNotes: undefined,
                   canReapplyImmediately: false,
                 }),
               })),
             );
+          } catch (error) {
+            console.error(
+              "Failed to send bulk rejection emails:",
+              error,
+            );
+          }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/lib/actions/partners/bulk-reject-partner-applications.ts` around
lines 136 - 159, The sendBatchEmail invocation for partnersWithEmail should be
wrapped in a try-catch so email sending failures are caught and logged instead
of propagating from the waitUntil context; update the block around
sendBatchEmail (the call that maps partnersWithEmail to
PartnerApplicationRejected payloads) to catch errors, call your logger (or
processLogger) with context including program.slug and number of recipients, and
decide whether to rethrow or return a failure result — at minimum log the error
and continue so the surrounding bulk-reject flow can handle partial failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/lib/actions/partners/bulk-reject-partner-applications.ts`:
- Around line 136-159: The sendBatchEmail invocation for partnersWithEmail
should be wrapped in a try-catch so email sending failures are caught and logged
instead of propagating from the waitUntil context; update the block around
sendBatchEmail (the call that maps partnersWithEmail to
PartnerApplicationRejected payloads) to catch errors, call your logger (or
processLogger) with context including program.slug and number of recipients, and
decide whether to rethrow or return a failure result — at minimum log the error
and continue so the surrounding bulk-reject flow can handle partial failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71aa2568-7230-4799-9be0-1a49aba83494

📥 Commits

Reviewing files that changed from the base of the PR and between 06479e9 and 6d8ac00.

📒 Files selected for processing (1)
  • apps/web/lib/actions/partners/bulk-reject-partner-applications.ts

@steven-tey
steven-tey merged commit 04c459e into main Apr 1, 2026
9 of 10 checks passed
@steven-tey
steven-tey deleted the bulk-reject-send-emails branch April 1, 2026 19:43
@coderabbitai coderabbitai Bot mentioned this pull request May 13, 2026
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.

2 participants