-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Email campaign updates #3062
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Email campaign updates #3062
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR adds a new optional Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Editor as Campaign Editor UI
participant API as PATCH /api/campaigns/[id]
participant DB as Database
participant PreviewSvc as Send Preview Action
participant Template as Campaign Email Template
User->>Editor: Edit subject and/or toggle/set preview
Editor->>API: PATCH with { subject?, preview? }
API->>DB: Update campaign (preview included when provided)
DB-->>API: Update result
User->>Editor: Click "Send preview"
Editor->>PreviewSvc: sendPreviewEmail({ subject, preview, body, from })
PreviewSvc->>Template: render with campaign.preview (if present)
Template-->>User: Preview email rendered/sent
Estimated code review effortπ― 3 (Moderate) | β±οΈ ~25 minutes Areas requiring extra attention:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touchesβ Passed checks (2 passed)
β¨ Finishing touches
π§ͺ 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 |
This reverts commit 086b3f5.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (11)
apps/web/app/(ee)/api/campaigns/[campaignId]/route.ts(2 hunks)apps/web/app/(ee)/api/cron/campaigns/broadcast/route.ts(1 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-editor.tsx(9 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/send-email-preview-modal.tsx(2 hunks)apps/web/lib/actions/campaigns/send-campaign-preview-email.ts(3 hunks)apps/web/lib/api/campaigns/validate-campaign.ts(1 hunks)apps/web/lib/api/workflows/execute-send-campaign-workflow.ts(1 hunks)apps/web/lib/api/workflows/render-campaign-email-html.ts(2 hunks)apps/web/lib/zod/schemas/campaigns.ts(2 hunks)packages/email/src/templates/campaign-email.tsx(3 hunks)packages/prisma/schema/campaign.prisma(1 hunks)
π§° Additional context used
π§ Learnings (7)
π Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
π Learning: 2025-09-24T16:10:37.349Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/ui/partners/partner-about.tsx:11-11
Timestamp: 2025-09-24T16:10:37.349Z
Learning: In the Dub codebase, the team prefers to import Icon as a runtime value from "dub/ui" and uses Icon as both a type and variable name in component props, even when this creates shadowing. This is their established pattern and should not be suggested for refactoring.
Applied to files:
packages/email/src/templates/campaign-email.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-editor.tsx
π Learning: 2025-10-15T01:05:43.266Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx:432-457
Timestamp: 2025-10-15T01:05:43.266Z
Learning: In apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx, defer refactoring the custom MenuItem component (lines 432-457) to use the shared dub/ui MenuItem component to a future PR, as requested by steven-tey.
Applied to files:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-editor.tsx
π Learning: 2025-09-24T15:47:40.293Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx:152-157
Timestamp: 2025-09-24T15:47:40.293Z
Learning: The Button component from dub/ui automatically adds type="button" when an onClick prop is passed, preventing accidental form submissions without requiring explicit type specification. The implementation uses: type={onClick ? "button" : type}
Applied to files:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-editor.tsx
π Learning: 2025-09-24T15:47:40.293Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx:152-157
Timestamp: 2025-09-24T15:47:40.293Z
Learning: The Button component from dub/ui automatically adds type="button" when an onClick prop is passed, preventing accidental form submissions without requiring explicit type specification.
Applied to files:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-editor.tsx
π Learning: 2025-09-24T15:47:40.293Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx:152-157
Timestamp: 2025-09-24T15:47:40.293Z
Learning: The Button component from dub/ui automatically sets type="button" when an onClick prop is passed and defaults to type="submit" otherwise, using the logic: type={props.onClick ? "button" : "submit"}. This prevents accidental form submissions when buttons are used for modal triggers or other non-form actions.
Applied to files:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-editor.tsx
π Learning: 2025-10-08T21:33:23.553Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2936
File: apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/analytics/add-hostname-modal.tsx:28-34
Timestamp: 2025-10-08T21:33:23.553Z
Learning: In the dub/ui Button component, when the `disabledTooltip` prop is set to a non-undefined value (e.g., a string), the button is automatically disabled. Therefore, it's not necessary to also add the same condition to the `disabled` propβsetting `disabledTooltip={permissionsError || undefined}` is sufficient to disable the button when there's a permissions error.
Applied to files:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-editor.tsx
𧬠Code graph analysis (1)
packages/email/src/templates/campaign-email.tsx (1)
packages/email/src/react-email.d.ts (3)
Html(4-4)Head(5-5)Preview(18-18)
πͺ Biome (2.1.2)
packages/email/src/templates/campaign-email.tsx
[error] 78-78: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
π Additional comments (12)
apps/web/lib/api/workflows/render-campaign-email-html.ts (2)
23-27: LGTM! Image styling enhancement.The inline styles ensure responsive image rendering in email clients, which is best practice since many email clients have limited CSS support.
91-91: LGTM! Sanitization updated correctly.The
styleattribute is now allowed forimgtags, which is necessary for the inline styles added in the Image extension configuration.apps/web/lib/actions/campaigns/send-campaign-preview-email.ts (2)
19-19: LGTM! Schema validation for preview field.The
nullish()validator correctly allows bothnullandundefined, matching the optional nature of the preview field in the Prisma schema.
36-37: LGTM! Preview field wired through correctly.The
previewvalue is extracted from the input and passed to theCampaignEmailtemplate undercampaign.preview, aligning with the data shape expected by the email template.Also applies to: 71-71
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/send-email-preview-modal.tsx (2)
30-33: LGTM! Form field watching updated correctly.The
useWatchhook now tracks thepreviewfield alongsidesubject,bodyJson, andfrom, ensuring the modal has access to the latest preview value from the form.
84-84: LGTM! Preview passed to action.The
previewvalue is correctly passed to thesendEmailPreviewaction, completing the data flow from form to email sending.apps/web/app/(ee)/api/cron/campaigns/broadcast/route.ts (1)
229-229: LGTM! Preview field added to email template data.The
previewfield is now passed to theCampaignEmailtemplate within thecampaignobject, enabling email clients to display preview text. Note that the email subject line (line 219) correctly continues to usecampaign.subject.apps/web/app/(ee)/api/campaigns/[campaignId]/route.ts (2)
67-67: LGTM! Preview extracted from validated input.The
previewfield is correctly extracted from the validated campaign input alongside other editable fields.
125-125: Review comment is based on a misunderstanding of the schema design.The update patterns are intentional and correct. The zod
updateCampaignSchemaexplicitly defines:
subjectasz.string()(not nullable) β cannot be set to nullpreviewasz.string().nullable()β can be set to nullscheduledAtasparseDateSchema.nullish()β can be set to nullThe update logic correctly reflects these constraints:
subject && { subject }only updates for truthy values (correct for non-nullable field)preview !== undefined && { preview }allows explicit null (correct for nullable field)scheduledAt !== undefined && { scheduledAt }allows explicit null (correct for nullish field)No changes needed.
Likely an incorrect or invalid review comment.
apps/web/lib/api/campaigns/validate-campaign.ts (1)
37-37: LGTM! Validation includes preview field.The
previewfield is correctly included in the editability check, ensuring it follows the same status-based editing rules as other campaign content fields likesubjectandbodyJson.apps/web/lib/api/workflows/execute-send-campaign-workflow.ts (1)
195-195: LGTM! Preview field added to workflow email template data.The
previewfield is now passed to theCampaignEmailtemplate within thecampaignobject. Note that both the message subject (line 163) and email subject (line 184) correctly continue to usecampaign.subject, maintaining consistency with the broadcast route.packages/prisma/schema/campaign.prisma (1)
30-30: Schema change verified; migration status unclearβrequires manual verification.The
preview String?field is correctly added to the Campaign model (line 26) and positioned logically aftersubject. However, the migrations directory does not exist at the standard Prisma location (packages/prisma/migrations/), making it impossible to verify whether a migration has been generated and applied. Confirm that Prisma migrations have been properly created for this schema change before deployment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
π§Ή Nitpick comments (1)
apps/web/scripts/perplexity/ban-partners.ts (1)
8-8: Maintain import consistency with absolute paths.The codebase uses absolute imports with the
@/alias prefix (lines 1, 3), but this change introduces a relative path. For consistency and maintainability, consider using the absolute import pattern instead.Apply this diff to align with the project's import convention:
-import { syncTotalCommissions } from "../../lib/api/partners/sync-total-commissions"; +import { syncTotalCommissions } from "@/lib/api/partners/sync-total-commissions";
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (4)
apps/web/lib/zod/schemas/campaigns.ts(2 hunks)apps/web/scripts/perplexity/ban-partners.ts(3 hunks)apps/web/tests/campaigns/index.test.ts(1 hunks)packages/prisma/schema/campaign.prisma(1 hunks)
π§ Files skipped from review as they are similar to previous changes (1)
- packages/prisma/schema/campaign.prisma
π§° Additional context used
π§ Learnings (3)
π Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
π Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.
Applied to files:
apps/web/scripts/perplexity/ban-partners.ts
π Learning: 2025-09-24T15:50:16.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
Applied to files:
apps/web/lib/zod/schemas/campaigns.ts
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
π Additional comments (7)
apps/web/lib/zod/schemas/campaigns.ts (2)
53-53: LGTM! Correct use of.default(null)in the main schema.Using
.default(null)here ensures thepreviewfield is always present when parsing campaign objects, which is appropriate for the mainCampaignSchema.
88-88: Previous concern addressed! Correct use of.nullish()for PATCH.The use of
.nullish()(equivalent to.nullable().optional()) correctly handles the update schema. This ensures that:
- Omitted
previewin PATCH requests staysundefined(preserves existing value)- Explicit
nullclears the field- String value updates the field
This addresses the data loss concern from the previous review.
apps/web/tests/campaigns/index.test.ts (1)
36-36: LGTM! Test correctly reflects the new schema.Adding
preview: nullto the expected campaign object correctly aligns with the updatedCampaignSchemathat includes the newpreviewfield with a default value ofnull.apps/web/scripts/perplexity/ban-partners.ts (4)
1-162: Verify that this file belongs in this PR.This file contains changes to a partner banning script, which appears unrelated to the PR's stated objectives of fixing email campaign image widths, hiding the Dub logo, and adding preview text customization. Please confirm whether these changes were intentionally included or if this file was modified by mistake.
41-43: Good defensive filter to prevent re-banning.The status filter ensures already-banned partners are excluded from processing, making the script safer to re-run and preventing duplicate ban operations.
133-134: LGTM!The debug logging is helpful for monitoring script execution and is consistent with the existing logging pattern throughout the file.
49-49: Add pagination or document the 200-partner limit.The concern is valid. This script processes only the first 200 partners from the CSV without pagination. If the CSV contains more matches, they won't be banned. While this is an admin one-off script, it will silently process a truncated set if the CSV grows beyond 200 entries.
Consider either:
- Adding a loop with skip/offset to process all partners in batches
- Adding a check after the query:
if (programEnrollments.length === 200) { console.warn("WARNING: 200 partners processed; may be more to ban"); }- Documenting that this script intentionally processes max 200 partners per run
Summary by CodeRabbit
New Features
Tests / Chores