Skip to content

Restore Resend email sending with a full transactional template set - #96

Open
projectamazonph wants to merge 4 commits into
mainfrom
claude/email-templates-resend-testing-eluzhi
Open

Restore Resend email sending with a full transactional template set#96
projectamazonph wants to merge 4 commits into
mainfrom
claude/email-templates-resend-testing-eluzhi

Conversation

@projectamazonph

@projectamazonph projectamazonph commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Rebuilds src/lib/email.ts (previously a no-op stub from the PayMongo/Resend
strip) into a real Resend-backed sender, with templates as React Email
components under src/emails/ styled to the Field Manual design system.

Wired into live flows: account-claim invite (admin-enroll), welcome
(signup), live-class registration confirmation, and certificate-issued.
Payment receipt, refund status, payment-failed, and password-reset ship as
templates + send helpers, ready to wire in once those flows exist (no
PayMongo checkout or reset-token flow currently emits the data they need).

All sends stay best-effort — errors are logged, never thrown — matching
the existing no-op fallback when RESEND_API_KEY is unset.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01RKvzDvyd3X1jXTSyQ1ELVt

Summary by CodeRabbit

  • New Features
    • Added/expanded transactional emails (account invitations, welcome, live class confirmations, certificate issued, password resets, payment receipts, payment failures, and refund status) with best-effort, non-blocking delivery.
    • Enabled email delivery via Resend; when not configured, emails safely remain logs-only.
  • Documentation
    • Updated .env.example to reflect PayMongo removal while manual payments and enrollment continue, and added Resend configuration variables.
  • Tests
    • Expanded coverage for email sending success, skipping when unset, and error handling.

Rebuilds src/lib/email.ts (previously a no-op stub from the PayMongo/Resend
strip) into a real Resend-backed sender, with templates as React Email
components under src/emails/ styled to the Field Manual design system.

Wired into live flows: account-claim invite (admin-enroll), welcome
(signup), live-class registration confirmation, and certificate-issued.
Payment receipt, refund status, payment-failed, and password-reset ship as
templates + send helpers, ready to wire in once those flows exist (no
PayMongo checkout or reset-token flow currently emits the data they need).

All sends stay best-effort — errors are logged, never thrown — matching
the existing no-op fallback when RESEND_API_KEY is unset.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RKvzDvyd3X1jXTSyQ1ELVt
Copilot AI review requested due to automatic review settings July 30, 2026 04:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@projectamazonph, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cc139626-9136-41e9-8b79-486dceb8a5b1

📥 Commits

Reviewing files that changed from the base of the PR and between c7f1be6 and bc678e6.

📒 Files selected for processing (10)
  • src/emails/AccountInviteEmail.tsx
  • src/emails/CertificateIssuedEmail.tsx
  • src/emails/LiveClassConfirmationEmail.tsx
  • src/emails/PasswordResetEmail.tsx
  • src/emails/PaymentFailedEmail.tsx
  • src/emails/PaymentReceiptEmail.tsx
  • src/emails/RefundStatusEmail.tsx
  • src/emails/WelcomeEmail.tsx
  • src/lib/__tests__/email.test.ts
  • src/lib/email.ts
📝 Walkthrough

Walkthrough

Changes

The PR adds Resend-backed transactional email delivery, shared React Email components and templates, integrations for enrollment, signup, and certificate issuance, and tests for disabled, successful, and failing delivery paths.

Transactional email delivery

Layer / File(s) Summary
Email templates and configuration
.env.example, package.json, eslint-rules/no-tailwind.js, src/emails/*
Adds Resend configuration, dependencies, shared email styling, and templates for account invites, welcome messages, classes, certificates, payments, refunds, and password resets.
Resend transport and email senders
src/lib/email.ts
Adds lazy Resend initialization, logs-only behavior without an API key, non-throwing error handling, and sender functions for the transactional templates.
Application action integrations
src/app/actions/admin-enroll.ts, src/app/actions/auth.ts, src/app/actions/certificates.ts
Triggers invite, welcome, and certificate-issued emails from enrollment, signup, and newly created certificate flows.
Email sender test coverage
src/lib/__tests__/email.test.ts
Tests environment-disabled sends, payload construction, default values and formatting, and Resend error handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdminOrStudent
  participant ServerAction
  participant EmailModule
  participant Resend
  AdminOrStudent->>ServerAction: enroll, sign up, or issue certificate
  ServerAction->>EmailModule: invoke email sender
  EmailModule->>Resend: submit rendered template
  Resend-->>EmailModule: return result or error
  EmailModule-->>ServerAction: complete without throwing
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: restoring Resend email sending and adding a full set of transactional templates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/email-templates-resend-testing-eluzhi

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/emails/welcome.tsx (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename React component files to PascalCase.

  • src/emails/welcome.tsx#L1-L1: rename to a PascalCase component filename and update imports.
  • src/emails/refund-status.tsx#L1-L1: rename to a PascalCase component filename and update imports.

As per coding guidelines, “Use PascalCase filenames for React component files.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/emails/welcome.tsx` at line 1, Rename the React component files
src/emails/welcome.tsx (lines 1-1) and src/emails/refund-status.tsx (lines 1-1)
to PascalCase filenames, such as Welcome.tsx and RefundStatus.tsx, and update
every import or reference to use the new paths.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/emails/live-class-confirmation.tsx`:
- Line 35: Remove every em dash from the transactional email templates: update
previewText in src/emails/live-class-confirmation.tsx (35-35), the invitation
footnote in src/emails/account-invite.tsx (25-26), previewText in
src/emails/certificate-issued.tsx (18-18), the reset-email footnote in
src/emails/password-reset.tsx (25-26), the documentation comment in
src/emails/payment-receipt.tsx (22-23), and previewText in
src/emails/payment-receipt.tsx (35-35). Replace each with a comma, period, or
parentheses while preserving the existing message content.

In `@src/emails/welcome.tsx`:
- Line 11: Remove all newly introduced em dashes: use a permitted separator in
the preview text in src/emails/welcome.tsx lines 11-11, replace the em dashes in
src/emails/refund-status.tsx lines 39-39 and throughout that template, replace
the em dashes in src/lib/email.ts lines 53-53 and throughout that module, and
rephrase the comment in src/app/actions/admin-enroll.ts lines 73-74 without an
em dash.

In `@src/lib/__tests__/email.test.ts`:
- Line 1: Move the email test file from the __tests__ directory to sit beside
email.ts as email.test.ts, preserving its existing test contents and behavior.
- Around line 62-205: Fix the seven `mockSend.mock.calls[0][0]` usages in the
email tests by introducing a small typed helper that safely returns the first
mock call argument, then use it instead of direct unchecked indexing. Cast each
`ReactElement` props value to the appropriate shape before accessing fields such
as `verifyUrl`, `amount`, or `retryUrl`, while preserving the existing
assertions.

In `@src/lib/email.ts`:
- Around line 131-141: Add timeZone: 'Asia/Manila' to both Intl.DateTimeFormat
configurations used for date and time formatting around scheduledAt, while
preserving the existing en-PH locale and formatting options.

---

Nitpick comments:
In `@src/emails/welcome.tsx`:
- Line 1: Rename the React component files src/emails/welcome.tsx (lines 1-1)
and src/emails/refund-status.tsx (lines 1-1) to PascalCase filenames, such as
Welcome.tsx and RefundStatus.tsx, and update every import or reference to use
the new paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ca193acc-e17c-439b-97f5-7403ea40e88c

📥 Commits

Reviewing files that changed from the base of the PR and between dabee94 and 47e90af.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • .env.example
  • eslint-rules/no-tailwind.js
  • package.json
  • src/app/actions/admin-enroll.ts
  • src/app/actions/auth.ts
  • src/app/actions/certificates.ts
  • src/emails/account-invite.tsx
  • src/emails/certificate-issued.tsx
  • src/emails/live-class-confirmation.tsx
  • src/emails/password-reset.tsx
  • src/emails/payment-failed.tsx
  • src/emails/payment-receipt.tsx
  • src/emails/refund-status.tsx
  • src/emails/shared.tsx
  • src/emails/welcome.tsx
  • src/lib/__tests__/email.test.ts
  • src/lib/email.ts

Comment thread src/emails/live-class-confirmation.tsx Outdated
Comment thread src/emails/welcome.tsx Outdated
Comment thread src/lib/__tests__/email.test.ts
Comment thread src/lib/__tests__/email.test.ts
Comment thread src/lib/email.ts
- Fix TS2532 (noUncheckedIndexedAccess): mockSend.mock.calls[0][0] was
  possibly undefined, and React 19's ReactElement.props is typed unknown.
  Added a typed firstSendArg() helper and a props() cast helper instead of
  indexing/casting inline at each call site.
- Set an explicit Asia/Manila timeZone on both Intl.DateTimeFormat calls in
  the live-class reminder email — without it, the displayed class date/time
  follows the server's runtime timezone (UTC on Vercel), not the Philippines.
- Remove em dashes from code comments and email copy per AGENTS.md ("Don't
  use em-dashes. Use periods, commas, parentheses"), across all files
  touched in this PR.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/emails/account-invite.tsx (1)

25-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use PascalCase filenames for the React email components.

All six changed .tsx files export React components but use kebab-case filenames. Rename them and update every import path.

  • src/emails/account-invite.tsx#L25-L26: rename to src/emails/AccountInviteEmail.tsx.
  • src/emails/certificate-issued.tsx#L18-L18: rename to src/emails/CertificateIssuedEmail.tsx.
  • src/emails/live-class-confirmation.tsx#L35-L35: rename to src/emails/LiveClassConfirmationEmail.tsx.
  • src/emails/password-reset.tsx#L25-L26: rename to src/emails/PasswordResetEmail.tsx.
  • src/emails/payment-failed.tsx#L21-L22: rename to src/emails/PaymentFailedEmail.tsx.
  • src/emails/welcome.tsx#L11-L11: rename to src/emails/WelcomeEmail.tsx.

As per coding guidelines, React component .tsx files must use PascalCase filenames.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/emails/account-invite.tsx` around lines 25 - 26, Rename the React email
component files to PascalCase and update every import path:
src/emails/account-invite.tsx to AccountInviteEmail.tsx,
src/emails/certificate-issued.tsx to CertificateIssuedEmail.tsx,
src/emails/live-class-confirmation.tsx to LiveClassConfirmationEmail.tsx,
src/emails/password-reset.tsx to PasswordResetEmail.tsx,
src/emails/payment-failed.tsx to PaymentFailedEmail.tsx, and
src/emails/welcome.tsx to WelcomeEmail.tsx.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/email.ts`:
- Around line 52-53: Remove raw to and subject values from the disabled and
failure logging in the email send flow, using only a safe internal message
identifier or delivery event; update the corresponding assertions in the email
tests to verify the redacted log payload and messages.

---

Nitpick comments:
In `@src/emails/account-invite.tsx`:
- Around line 25-26: Rename the React email component files to PascalCase and
update every import path: src/emails/account-invite.tsx to
AccountInviteEmail.tsx, src/emails/certificate-issued.tsx to
CertificateIssuedEmail.tsx, src/emails/live-class-confirmation.tsx to
LiveClassConfirmationEmail.tsx, src/emails/password-reset.tsx to
PasswordResetEmail.tsx, src/emails/payment-failed.tsx to PaymentFailedEmail.tsx,
and src/emails/welcome.tsx to WelcomeEmail.tsx.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e2c7e867-69db-4081-a0e5-ac1ccc07361f

📥 Commits

Reviewing files that changed from the base of the PR and between 47e90af and c7f1be6.

📒 Files selected for processing (12)
  • .env.example
  • src/app/actions/admin-enroll.ts
  • src/emails/account-invite.tsx
  • src/emails/certificate-issued.tsx
  • src/emails/live-class-confirmation.tsx
  • src/emails/password-reset.tsx
  • src/emails/payment-failed.tsx
  • src/emails/payment-receipt.tsx
  • src/emails/refund-status.tsx
  • src/emails/welcome.tsx
  • src/lib/__tests__/email.test.ts
  • src/lib/email.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/emails/payment-receipt.tsx
  • src/emails/refund-status.tsx
  • .env.example
  • src/app/actions/admin-enroll.ts

Comment thread src/lib/email.ts Outdated
Comment on lines +52 to +53
if (!process.env.RESEND_API_KEY) {
logger.info({ to, subject }, '[email disabled] RESEND_API_KEY not set, skipped');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log raw email recipients and subjects.

The disabled path writes the recipient address and subject to application logs. The same fields are also logged on failures at Lines 59-62. Redact them or log only an internal message identifier and delivery event. Update the corresponding assertion in src/lib/__tests__/email.test.ts.

The raw logger payload makes this PII retention risk explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/email.ts` around lines 52 - 53, Remove raw to and subject values from
the disabled and failure logging in the email send flow, using only a safe
internal message identifier or delivery event; update the corresponding
assertions in the email tests to verify the redacted log payload and messages.

- Rename src/emails/*.tsx from kebab-case to PascalCase, matching this
  repo's actual component-file convention (every other .tsx component
  under src/components/ is PascalCase already).
- Stop logging raw recipient email addresses in src/lib/email.ts. Nothing
  else in the codebase logs a raw `email` field (userId is used instead),
  and the pino redact config exists for exactly this reason. Added a
  maskEmail() helper (e.g. "st***@example.com") used across the disabled,
  send-failed, and unexpected-error log lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

3 participants