fix: resolve email broadcast Save Draft silent failure#567
Conversation
Remove replyTo from createEmailBroadcastWithTemplateSchema — empty string default was failing z.email() validation silently. The backend already pulls replyTo from the EmailAddress record when sending. Also fix layout children type (ReactElement → ReactNode) and scope fan unique index to workspaceId. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 14 Skipped Deployments
|
Greptile SummaryThis PR fixes a silent "Save Draft" failure in the email broadcast flow by removing the Key changes:
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as Browser (Modal)
participant tRPC as emailBroadcast.createWithTemplate
participant DB as PostgreSQL
Note over UI,DB: Before fix — replyTo: '' fails z.email() silently
UI->>tRPC: createWithTemplate({ ..., replyTo: '' })
tRPC-->>UI: Zod validation error (swallowed)
Note over UI,DB: After fix — replyTo removed from schema
UI->>tRPC: createWithTemplate({ name, fromId, subject, body, ... })
tRPC->>DB: INSERT INTO EmailTemplates (no replyTo)
DB-->>tRPC: emailTemplate
tRPC->>DB: INSERT INTO EmailBroadcasts
DB-->>tRPC: emailBroadcast
tRPC-->>UI: { emailBroadcast, emailTemplate }
Note over UI,DB: Fan upsert — requires composite index in DB
UI->>tRPC: bioRender / vipSwapRender (fan sign-up)
tRPC->>DB: INSERT INTO Fans ON CONFLICT (email, workspaceId) DO UPDATE
alt composite index exists (after db:push)
DB-->>tRPC: fan record (upserted)
else old single-column index still in DB
DB-->>tRPC: ERROR: no unique constraint matching (email, workspaceId)
end
Last reviewed commit: "fix: align fan upser..." |
| }, | ||
| f => ({ | ||
| unique: uniqueIndex('unique_email').on(f.email), | ||
| unique: uniqueIndex('unique_email').on(f.email, f.workspaceId), |
There was a problem hiding this comment.
onConflictDoUpdate calls will break at runtime
The unique index is changed from (email) to (email, workspaceId), but two fan-creation routes still use target: [Fans.email] in their onConflictDoUpdate calls, which must exactly match an existing unique constraint or index. Since the unique_email index no longer covers just email, PostgreSQL will throw an error at runtime.
Affected locations:
packages/lib/src/trpc/routes/bio-render.route.ts:238-247—.onConflictDoUpdate({ target: [Fans.email], set: { ... } })packages/lib/src/trpc/routes/vip-swap-render.route.ts:170-176—.onConflictDoUpdate({ target: [Fans.email], ... })(even has a now-incorrect comment:// Only email is unique, not email+workspace)
These two paths handle every new fan sign-up via bio pages and VIP swaps. Both will fail whenever a repeat visitor returns to sign up, causing fan creation to error out entirely.
Both call sites need to be updated to match the new composite constraint:
.onConflictDoUpdate({
target: [Fans.email, Fans.workspaceId],
set: { ... },
})
apps
db
|
The Fans table unique index changed from (email) to (email, workspaceId), but two routes still used the old target, causing runtime errors on repeat fan sign-ups. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@greptileai pls update the code review based on new changes. |
apps
db
|
Summary
replyTofromcreateEmailBroadcastWithTemplateSchema— empty string default was failingz.email()validation silently, causing "Save Draft" to do nothingreplyTofrom theEmailAddressrecord when sending (email-broadcast.trigger.ts:268), so the field was redundantchildrentype (ReactElement→ReactNode) to resolve typecheck errorsworkspaceIdto allow same email across workspacesTest plan
🤖 Generated with Claude Code