Restore Resend email sending with a full transactional template set - #96
Restore Resend email sending with a full transactional template set#96projectamazonph wants to merge 4 commits into
Conversation
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
|
Warning Review limit reached
Next review available in: 50 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (10)
📝 WalkthroughWalkthroughChangesThe 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
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
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
src/emails/welcome.tsx (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRename 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (17)
.env.exampleeslint-rules/no-tailwind.jspackage.jsonsrc/app/actions/admin-enroll.tssrc/app/actions/auth.tssrc/app/actions/certificates.tssrc/emails/account-invite.tsxsrc/emails/certificate-issued.tsxsrc/emails/live-class-confirmation.tsxsrc/emails/password-reset.tsxsrc/emails/payment-failed.tsxsrc/emails/payment-receipt.tsxsrc/emails/refund-status.tsxsrc/emails/shared.tsxsrc/emails/welcome.tsxsrc/lib/__tests__/email.test.tssrc/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>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/emails/account-invite.tsx (1)
25-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse PascalCase filenames for the React email components.
All six changed
.tsxfiles export React components but use kebab-case filenames. Rename them and update every import path.
src/emails/account-invite.tsx#L25-L26: rename tosrc/emails/AccountInviteEmail.tsx.src/emails/certificate-issued.tsx#L18-L18: rename tosrc/emails/CertificateIssuedEmail.tsx.src/emails/live-class-confirmation.tsx#L35-L35: rename tosrc/emails/LiveClassConfirmationEmail.tsx.src/emails/password-reset.tsx#L25-L26: rename tosrc/emails/PasswordResetEmail.tsx.src/emails/payment-failed.tsx#L21-L22: rename tosrc/emails/PaymentFailedEmail.tsx.src/emails/welcome.tsx#L11-L11: rename tosrc/emails/WelcomeEmail.tsx.As per coding guidelines, React component
.tsxfiles 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
📒 Files selected for processing (12)
.env.examplesrc/app/actions/admin-enroll.tssrc/emails/account-invite.tsxsrc/emails/certificate-issued.tsxsrc/emails/live-class-confirmation.tsxsrc/emails/password-reset.tsxsrc/emails/payment-failed.tsxsrc/emails/payment-receipt.tsxsrc/emails/refund-status.tsxsrc/emails/welcome.tsxsrc/lib/__tests__/email.test.tssrc/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
| if (!process.env.RESEND_API_KEY) { | ||
| logger.info({ to, subject }, '[email disabled] RESEND_API_KEY not set, skipped'); |
There was a problem hiding this comment.
🔒 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>
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
.env.exampleto reflect PayMongo removal while manual payments and enrollment continue, and added Resend configuration variables.