Add customer signup date and subscription date as reward condition#3514
Add customer signup date and subscription date as reward condition#3514steven-tey merged 12 commits intomainfrom
Conversation
…d schemas - Added `signupDate` to customer context in `checkout-session-completed`, `invoice-paid`, `track-sale`, and `create-shopify-sale` files. - Updated `create-partner-commission` to include `signupDate` and `subscriptionStartDate`. - Enhanced reward schemas to support `signupDate` and `subscriptionStartDate` as date types. - Implemented date formatting in the UI for reward modifiers and conditions.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThreads Changes
Sequence Diagram(s)sequenceDiagram
participant Webhook as Webhook (Stripe)
participant TrackSale as TrackSale Flow
participant CommissionSvc as CommissionService
participant Rewards as Rewards Schema
participant UI as Rewards UI
Webhook->>CommissionSvc: createPartnerCommission(context with customer.signupDate)
TrackSale->>CommissionSvc: createPartnerCommission(context with customer.signupDate)
CommissionSvc->>CommissionSvc: derive subscriptionStartDate from firstCommission?.createdAt
CommissionSvc->>Rewards: include signupDate & subscriptionStartDate in reward context
Rewards->>UI: expose date attributes & DATE_CONDITION_OPERATORS
UI->>User: render DatePicker and formatted date values
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/web/lib/zod/schemas/rewards.ts (1)
231-233: Enforce date operator/value constraints at schema level (not only in UI).
DATE_CONDITION_OPERATORSis defined, but invalid operator/value combinations can still passrewardConditionSchemafor date attributes.🧩 Suggested hardening
export const rewardConditionSchema = z.object({ entity: z.enum( REWARD_CONDITION_ENTITIES.map(({ id }) => id) as [string, ...string[]], ), attribute: z.enum( REWARD_CONDITION_ATTRIBUTES.map(({ id }) => id) as [string, ...string[]], ), operator: z.enum(CONDITION_OPERATORS), value: z.union([ z.string(), z.number(), z.array(z.string()), z.array(z.number()), ]), label: z .string() .nullish() .describe("Product name used for display purposes in the UI."), -}); +}).superRefine((data, ctx) => { + const attr = REWARD_CONDITION_ATTRIBUTES.find((a) => a.id === data.attribute); + if (attr?.type === "date") { + if (!DATE_CONDITION_OPERATORS.includes(data.operator)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["operator"], + message: "Invalid operator for date condition.", + }); + } + if (typeof data.value !== "number") { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["value"], + message: "Date condition value must be a timestamp (number).", + }); + } + } +});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/zod/schemas/rewards.ts` around lines 231 - 233, The schema currently allows invalid operator/value combinations for date attributes; update rewardConditionSchema to validate date-specific rules: add a zod refine/superRefine to check when attributeType === "date" then operator must be one of DATE_CONDITION_OPERATORS and the provided value must be a valid date/ISO date (e.g., Date.parse(value) is not NaN) and when attributeType !== "date" disallow the DATE_CONDITION_OPERATORS; reference the existing constants DATE_CONDITION_OPERATORS and the rewardConditionSchema and implement the checks via refine/superRefine on rewardConditionSchema so validation is enforced at the schema level.apps/web/ui/partners/rewards/rewards-logic.tsx (1)
261-268: Minor edge case: timestamp value of 0.The check
!valueon line 263 would treat a timestamp of0(representing Jan 1, 1970 00:00:00 UTC) as invalid. This is unlikely to be a real-world scenario for reward conditions, but worth noting.💡 Optional: More explicit check
// For date values, format timestamp as readable date + time if (type === "date") { - if (!value || isNaN(Number(value))) { + if (value === undefined || value === null || isNaN(Number(value))) { return "Value"; } return formatDateTime(new Date(Number(value))); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/ui/partners/rewards/rewards-logic.tsx` around lines 261 - 268, The current date branch treats falsy 0 as invalid; in the block handling type === "date" (around the formatDateTime(new Date(Number(value))) usage) change the existence check from a truthy check (!value) to an explicit null/undefined check (e.g., value == null) and still validate numeric conversion with isNaN(Number(value)) so a timestamp of 0 is accepted and only null/undefined/non-numeric values return "Value".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/lib/partners/create-partner-commission.ts`:
- Around line 120-127: firstCommission can be null for a first-sale, so
subscriptionStartDate is undefined and later subscriptionDurationMonths logic
fails; set subscriptionStartDate to fall back to the current commission's
createdAt (createdAt ?? new Date()) when firstCommission is null, then compute
subscriptionDurationMonths with differenceInMonths(createdAt ?? new Date(),
subscriptionStartDate) so first-sale modifiers evaluate correctly (also apply
the same fallback where subscriptionStartDate is used again around the other
occurrence at line ~133).
In `@apps/web/tests/utils/resource.ts`:
- Line 130: The UUID assigned to the id property in tests/utils/resource.ts
contains an invalid 'g' character; replace the string value for the id field
(id: "f4b8c2d3-ed55-4f9a-c342-20b9531g2d16") with a valid hex-only UUID
(characters 0-9 and a-f) preserving the UUID format (8-4-4-4-12) so tests use a
proper UUID.
---
Nitpick comments:
In `@apps/web/lib/zod/schemas/rewards.ts`:
- Around line 231-233: The schema currently allows invalid operator/value
combinations for date attributes; update rewardConditionSchema to validate
date-specific rules: add a zod refine/superRefine to check when attributeType
=== "date" then operator must be one of DATE_CONDITION_OPERATORS and the
provided value must be a valid date/ISO date (e.g., Date.parse(value) is not
NaN) and when attributeType !== "date" disallow the DATE_CONDITION_OPERATORS;
reference the existing constants DATE_CONDITION_OPERATORS and the
rewardConditionSchema and implement the checks via refine/superRefine on
rewardConditionSchema so validation is enforced at the schema level.
In `@apps/web/ui/partners/rewards/rewards-logic.tsx`:
- Around line 261-268: The current date branch treats falsy 0 as invalid; in the
block handling type === "date" (around the formatDateTime(new
Date(Number(value))) usage) change the existence check from a truthy check
(!value) to an explicit null/undefined check (e.g., value == null) and still
validate numeric conversion with isNaN(Number(value)) so a timestamp of 0 is
accepted and only null/undefined/non-numeric values return "Value".
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.tsapps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.tsapps/web/lib/api/conversions/track-sale.tsapps/web/lib/integrations/shopify/create-sale.tsapps/web/lib/partners/create-partner-commission.tsapps/web/lib/zod/schemas/rewards.tsapps/web/tests/rewards/reward-conditions.test.tsapps/web/tests/rewards/sale-reward.test.tsapps/web/tests/utils/resource.tsapps/web/ui/partners/program-reward-modifiers-tooltip.tsxapps/web/ui/partners/rewards/rewards-logic.tsx
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/web/lib/zod/schemas/rewards.ts (1)
23-23: Consider adding explicit date validation torewardConditionSchemafor clarity, though current implementation works correctly.The value schema currently allows
z.string()andz.number()for all condition types, including dates. While theDateConditionPickerin the UI enforces numeric timestamps and the two-stage validation pattern (form-level permissive schema for UX, submission-level strict schema) prevents invalid dates from being submitted, the schema itself doesn't explicitly codify that date conditions require numeric values. Adding a.refine()or.superRefine()torewardConditionSchemato validate that date-type conditions have numeric timestamps would improve schema clarity and guard against edge cases where the form schema might be bypassed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/lib/zod/schemas/rewards.ts` at line 23, rewardConditionSchema should explicitly enforce that when condition.type === "date" the value is a numeric timestamp; add a .refine() or .superRefine() on rewardConditionSchema to check (e.g., if data.type === "date") that typeof data.value === "number" (or Number.isFinite(Number(data.value)) if strings may be passed) and throw a clear error path when it isn't—refer to rewardConditionSchema and DateConditionPicker to ensure the refinement matches the UI's timestamp representation and error message wording.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/lib/zod/schemas/rewards.ts`:
- Line 23: rewardConditionSchema should explicitly enforce that when
condition.type === "date" the value is a numeric timestamp; add a .refine() or
.superRefine() on rewardConditionSchema to check (e.g., if data.type === "date")
that typeof data.value === "number" (or Number.isFinite(Number(data.value)) if
strings may be passed) and throw a clear error path when it isn't—refer to
rewardConditionSchema and DateConditionPicker to ensure the refinement matches
the UI's timestamp representation and error message wording.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7e33f81a-8573-46d7-8e63-5868832b5c4b
📒 Files selected for processing (2)
apps/web/lib/api/conversions/track-sale.tsapps/web/lib/zod/schemas/rewards.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/lib/api/conversions/track-sale.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/ui/src/date-picker/date-picker.tsx (1)
91-104: Theinvalidprop is not connected toaria-invalidfor the default Trigger.The
invalidprop is passed to custom triggers (line 89) but not used for the defaultTrigger'saria-invalidattribute. This inconsistency could cause accessibility issues when using the default trigger.Proposed fix
<Trigger placeholder={placeholder} disabled={disabled} className={className} hasError={hasError} aria-required={props.required || props["aria-required"]} - aria-invalid={props["aria-invalid"]} + aria-invalid={invalid || props["aria-invalid"]} aria-label={props["aria-label"]} aria-labelledby={props["aria-labelledby"]} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/date-picker/date-picker.tsx` around lines 91 - 104, The default Trigger JSX is not receiving the component's invalid prop for its aria-invalid attribute; update the Trigger element in date-picker.tsx (the default branch rendering <Trigger ...>{displayValue}</Trigger>) to pass aria-invalid using the invalid prop when props["aria-invalid"] is not explicitly provided (e.g., set aria-invalid to props["aria-invalid"] || invalid) so the component's invalid state is reflected for accessibility; ensure you reference the existing Trigger component and the invalid variable used for custom triggers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/ui/partners/rewards/rewards-logic.tsx`:
- Around line 485-490: The DatePicker onChange handler calls
setValue(conditionKey, { ...condition, value: date ? date.getTime() : undefined
}) but omits the shouldDirty option so the form may not mark itself dirty;
update the call to pass the options object with shouldDirty: true (i.e., call
setValue with the same value plus { shouldDirty: true }) in the onChange handler
where conditionKey and condition are used.
---
Nitpick comments:
In `@packages/ui/src/date-picker/date-picker.tsx`:
- Around line 91-104: The default Trigger JSX is not receiving the component's
invalid prop for its aria-invalid attribute; update the Trigger element in
date-picker.tsx (the default branch rendering <Trigger
...>{displayValue}</Trigger>) to pass aria-invalid using the invalid prop when
props["aria-invalid"] is not explicitly provided (e.g., set aria-invalid to
props["aria-invalid"] || invalid) so the component's invalid state is reflected
for accessibility; ensure you reference the existing Trigger component and the
invalid variable used for custom triggers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2e06c239-5600-4af4-8e9c-d8ecdcf87f0e
📒 Files selected for processing (3)
apps/web/ui/partners/rewards/rewards-logic.tsxpackages/ui/src/date-picker/date-picker.tsxpackages/ui/src/date-picker/index.ts
|
@coderabbitai final review, any feedback? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 177345 🏁 Script executed: Length of output: 6905
🐛 Bug:
|
Summary by CodeRabbit
New Features
UI
Tests