-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add customerDataSharingEnabledAt feature #2991
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
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughIntroduces customer data sharing control flag to partner program enrollments with conditional email masking in API responses. Refactors analytics export components from default to named exports and consolidates analytics/events export logic by removing redundant event-options component. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes The changes span database schema, API logic, UI components, and internal refactoring. While individual edits are straightforward (conditional email masking, component export changes, form state wiring), the mix of heterogeneous changes across multiple layers requires separate reasoning for each cohort. No dense logic or complex control flow, but moderate file spread and feature scope. Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🧰 Additional context used🧬 Code graph analysis (1)apps/web/ui/partners/partner-advanced-settings-modal.tsx (1)
⏰ 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)
🔇 Additional comments (8)
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.
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 (5)
apps/web/lib/zod/schemas/programs.ts (1)
126-131: Schema looks good; prefer nullable over nullish for stable API shape.Switch to
.nullable()to avoid “undefined” creeping into consumers; DB returns null anyway.- customerDataSharingEnabledAt: z - .date() - .nullish() + customerDataSharingEnabledAt: z + .date() + .nullable() .describe( "The date when customer data sharing was enabled for this partner.", ),apps/web/ui/partners/partner-advanced-settings-modal.tsx (1)
60-66: Avoid overwriting an existing enabledAt when toggling back on.If “enabledAt” represents the first enable time, preserve the prior timestamp instead of always writing a fresh Date.
- setValue("customerDataSharingEnabledAt", checked ? new Date() : null, { + setValue( + "customerDataSharingEnabledAt", + checked + ? (getValues?.("customerDataSharingEnabledAt") ?? new Date()) + : null, + { shouldDirty: true, shouldValidate: true, - }); + }, + );If “last enabled” is intended, ignore this.
apps/web/app/(ee)/api/partner-profile/programs/[programId]/customers/[customerId]/route.ts (1)
80-87: Deduplicate masking logic via a shared helper.Inline Zod with regex appears in multiple places; extract to a util to ensure one consistent policy (and simplify tests).
- PartnerProfileCustomerSchema.extend({ - email: customerDataSharingEnabledAt - ? z.string() - : z - .string() - .transform((email) => email.replace(/(?<=^.).+(?=.@)/, "****")), - }).parse({ + PartnerProfileCustomerSchema.parse({ + ...transformCustomer({ + ...customer, + email: + maskEmail( + customer.email || customer.name || generateRandomName(), + Boolean(customerDataSharingEnabledAt), + ), + }),Utility (new file, e.g., lib/privacy/email.ts):
export function maskEmail(email: string, share: boolean) { if (share) return email; return email.includes("@") ? email.replace(/(?<=^.).+(?=@)/, "****") : "****"; }Note: Regex adjusted to (?=@) to avoid leaking the last local-part char.
apps/web/lib/actions/partners/update-partner-enrollment.ts (1)
54-56: Optional: guard against future timestamps and improve audit trail.
- Reject future dates to prevent clock skews.
- Include old/new values in audit log for traceability.
- customerDataSharingEnabledAt, + customerDataSharingEnabledAt,Add validation to the schema:
- customerDataSharingEnabledAt: z.coerce.date().nullable(), + customerDataSharingEnabledAt: z.coerce + .date() + .nullable() + .refine((d) => !d || d.getTime() <= Date.now(), "enabledAt cannot be in the future"),Enhance audit log payload (after update):
- description: `Partner ${partnerId} enrollment updated`, + description: `Partner ${partnerId} enrollment updated`, + metadata: { + changes: { + tenantId, + customerDataSharingEnabledAt: programEnrollment.customerDataSharingEnabledAt, + }, + },apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts (1)
65-75: Use shared email-masking helper to keep policy consistent.Same masking logic as customers route; prefer a single helper (e.g., maskEmail) and avoid inline regex.
- customer: z - .object({ - id: z.string(), - email: customerDataSharingEnabledAt - ? z.string() - : z - .string() - .transform((email) => - email.replace(/(?<=^.).+(?=.@)/, "****"), - ), - }) - .parse(event.customer), + customer: z + .object({ + id: z.string(), + email: z.string().transform((e) => + maskEmail(e, Boolean(customerDataSharingEnabledAt)), + ), + }) + .parse(event.customer),Also consider updating the regex to
(?=@)to avoid leaking the last local-part character.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/customers/[customerId]/route.ts(2 hunks)apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts(2 hunks)apps/web/lib/actions/partners/update-partner-enrollment.ts(2 hunks)apps/web/lib/zod/schemas/partner-profile.ts(1 hunks)apps/web/lib/zod/schemas/programs.ts(1 hunks)apps/web/ui/partners/partner-advanced-settings-modal.tsx(7 hunks)packages/prisma/schema/program.prisma(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
PR: dubinc/dub#2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.
Applied to files:
apps/web/lib/actions/partners/update-partner-enrollment.ts
🧬 Code graph analysis (3)
apps/web/lib/actions/partners/update-partner-enrollment.ts (1)
apps/web/lib/actions/safe-action.ts (1)
authActionClient(33-82)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/customers/[customerId]/route.ts (3)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts (1)
GET(14-84)apps/web/lib/api/programs/get-program-enrollment-or-throw.ts (1)
getProgramEnrollmentOrThrow(6-67)apps/web/lib/zod/schemas/partner-profile.ts (1)
PartnerProfileCustomerSchema(97-104)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts (1)
apps/web/lib/api/programs/get-program-enrollment-or-throw.ts (1)
getProgramEnrollmentOrThrow(6-67)
⏰ 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 (3)
apps/web/ui/partners/partner-advanced-settings-modal.tsx (1)
161-161: Nice UX touch with Save disabled until dirty.Prevents no-op submissions. LGTM.
packages/prisma/schema/program.prisma (1)
98-104: Clarify semantics: field currently tracks "last enabled" not "first enabled", and consider adding an index.The current implementation (line 62 of
partner-advanced-settings-modal.tsx) overwrites the timestamp withnew Date()on each toggle, making this field "last enabled at" rather than "first enabled at" despite its name. If audit history is needed, you'll need a separate solution (audit table or history).For performance: the field is queried in multiple API routes to check if sharing is enabled (used for access control). Adding an index on this nullable column can improve query performance at scale.
apps/web/lib/zod/schemas/partner-profile.ts (1)
97-104: Email masking is already properly implemented in the sole route using PartnerProfileCustomerSchema.Verification confirms the [customerId] route applies conditional email masking based on
customerDataSharingEnabledAt: when false, emails are transformed to mask the middle characters (e.g., "u****@example.com"). Since this is the only route using the schema and masking is already in place at the edge, no unintended exposures exist. The suggested maskEmail helper extraction is not critical given the single usage.
…ton in partner analytics
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 (7)
apps/web/lib/actions/partners/update-partner-enrollment.ts(2 hunks)apps/web/ui/analytics/analytics-export-button.tsx(1 hunks)apps/web/ui/analytics/analytics-options.tsx(1 hunks)apps/web/ui/analytics/events/events-export-button.tsx(2 hunks)apps/web/ui/analytics/events/events-options.tsx(0 hunks)apps/web/ui/analytics/toggle.tsx(2 hunks)apps/web/ui/partners/partner-advanced-settings-modal.tsx(7 hunks)
💤 Files with no reviewable changes (1)
- apps/web/ui/analytics/events/events-options.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/ui/partners/partner-advanced-settings-modal.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-16T19:21:23.506Z
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
Applied to files:
apps/web/ui/analytics/toggle.tsx
🧬 Code graph analysis (3)
apps/web/ui/analytics/analytics-options.tsx (2)
apps/web/ui/analytics/analytics-export-button.tsx (1)
AnalyticsExportButton(7-59)apps/web/ui/analytics/events/events-export-button.tsx (1)
EventsExportButton(7-62)
apps/web/lib/actions/partners/update-partner-enrollment.ts (1)
apps/web/lib/actions/safe-action.ts (1)
authActionClient(33-82)
apps/web/ui/analytics/toggle.tsx (1)
apps/web/ui/analytics/analytics-options.tsx (1)
AnalyticsOptions(8-39)
⏰ 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 (8)
apps/web/ui/analytics/analytics-export-button.tsx (1)
7-59: LGTM - Clean refactor to named export.The conversion from default export to named export
AnalyticsExportButtonis clean and maintains the same props signature and functionality.apps/web/ui/analytics/analytics-options.tsx (2)
5-6: LGTM - Proper named imports.Both export button components are correctly imported as named exports.
8-21: LGTM - Clean conditional rendering pattern.The component correctly accepts a
pagediscriminator prop and conditionally renders the appropriate export button. Both buttons receive the samesetOpenPopoverprop for consistent popover control.apps/web/ui/analytics/toggle.tsx (2)
34-34: LGTM - Updated to named import.Correctly updated to import
AnalyticsOptionsas a named export, aligning with the refactored analytics-options.tsx.
273-273: LGTM - Correct prop passthrough.The
pageprop is correctly passed toAnalyticsOptions, allowing it to render the appropriate export button based on whether the current view is analytics or events.apps/web/ui/analytics/events/events-export-button.tsx (3)
3-3: LGTM - Required imports added.Correctly added
DispatchandSetStateActionimports to support the new prop signature.
7-11: LGTM - Standardized prop signature.The prop signature change from optional
onClickcallback to requiredsetOpenPopoveraligns withAnalyticsExportButtonand creates a consistent control pattern across both export buttons.
58-58: LGTM - Correct popover state management.The
setOpenPopover(false)call correctly closes the popover after triggering the export, maintaining consistency with the AnalyticsExportButton implementation.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
Summary by CodeRabbit
New Features
Refactor