Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Jun 12, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced commission creation form with support for multiple commission types (custom, sale, lead) and dynamic accordion UI.
    • Added support for creating custom commissions without requiring a referral link.
    • Commission records now support an optional description field.
  • Improvements

    • Commission form includes type-aware validation and conditional fields.
    • Partner commission logic updated for improved type handling and flexibility.
    • Commission data schema reorganized for clearer categorization.
  • UI Updates

    • Commission sheet replaces payout sheet in partner details for streamlined workflow.
  • Other

    • Minor logging and style refinements.
    • Number input fields no longer display default spinner controls in browsers.

@vercel
Copy link
Contributor

vercel bot commented Jun 12, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
dub ✅ Ready (Inspect) Visit Preview Jun 12, 2025 9:52pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 12, 2025

Walkthrough

This update introduces a refactored commission creation workflow, supporting multiple commission types with dynamic form logic, validation, and UI. Schema and backend logic are updated to accommodate new commission types, optional fields, and improved type safety. Related UI components and hooks are synchronized with these changes, and a new description field is added to the Commission model.

Changes

File(s) Change Summary
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-button.tsx
apps/web/ui/partners/partner-details-sheet.tsx
Refactored commission sheet to support multiple commission types with dynamic UI, validation, and state. Updated related button and partner detail sheet to use new commission sheet logic and hooks.
apps/web/lib/actions/partners/create-commission.ts
apps/web/lib/partners/create-partner-commission.ts
Backend logic updated to allow custom commissions without linkId, support optional fields, and improve type safety and event handling for commission creation.
apps/web/lib/zod/schemas/commissions.ts Commission schema reorganized and extended: customerId is now optional, new fields for custom, lead, and sale commissions added, and fields grouped by type.
packages/prisma/schema/commission.prisma Added optional description field to the Commission model.
apps/web/app/(ee)/api/paypal/webhook/verify-signature.ts Reformatted a logging statement for improved readability; no logic changes.
apps/web/lib/stripe/index.ts Simplified function parameter destructuring and type annotation for Stripe client export.
packages/ui/src/icons/nucleo/plus2.tsx Removed trailing space and added newline; no logic changes.
apps/web/styles/globals.css Added CSS rules to hide default spinner controls on number input fields across browsers.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CreateCommissionButton
    participant CreateCommissionSheet
    participant Backend (API/Actions)
    participant DB (Prisma)

    User->>CreateCommissionButton: Click "Create Commission"
    CreateCommissionButton->>CreateCommissionSheet: Open with options (type, partnerId, etc.)
    CreateCommissionSheet->>User: Display dynamic form (type selection, fields)
    User->>CreateCommissionSheet: Fill form and submit
    CreateCommissionSheet->>Backend (API/Actions): Submit commission data
    Backend (API/Actions)->>DB (Prisma): Create commission (custom/lead/sale)
    DB (Prisma)-->>Backend (API/Actions): Save and return result
    Backend (API/Actions)-->>CreateCommissionSheet: Success/failure response
    CreateCommissionSheet-->>User: Show result/close sheet
Loading

Suggested reviewers

  • devkiran

Poem

In fields of forms where commissions grow,
Rabbits hop and toggles show—
One-time, sale, or lead anew,
Accordions open, options in view.
Backend burrows, schema expands,
Description fields in gentle hands.
With every change, the garden’s neat—
More carrots for all, a bunny treat! 🥕

✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 12, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2517:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 12, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2517:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 12, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2517:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@devkiran devkiran marked this pull request as ready for review June 12, 2025 18:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
apps/web/lib/partners/create-partner-commission.ts (1)

158-174: ⚠️ Potential issue

Ensure currency is always populated

currency is passed to Prisma regardless of event type.
When called from the custom-commission path it is undefined, leading to a DB error if the column is NOT NULL.

-        currency,
+        currency: currency ?? "usd",

Consider making the parameter required or defaulting earlier to avoid silent failures.

🧹 Nitpick comments (5)
apps/web/app/(ee)/api/paypal/webhook/verify-signature.ts (1)

37-39: Use structured logging rather than plain console.error

While re-formatting is harmless, relying on console.* makes log aggregation harder in prod. Prefer your central logger (e.g., logger.error({ msg: "...", headers })) so JSON output is preserved.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-button.tsx (1)

9-9: nested: false is the default – can be omitted

Unless you intend to emphasise the explicit choice, dropping nested: false reduces noise.

-    useCreateCommissionSheet({
-      nested: false,
-      partnerId: "",
-    });
+    useCreateCommissionSheet({ partnerId });
apps/web/ui/partners/partner-details-sheet.tsx (1)

21-21: Use the existing alias for app-layer imports

All other internal paths use the @/ alias while this line relies on "app/app.dub.co/…".
Prefer a single convention (@/...) or add a proper TS path alias; otherwise the build may break outside the Next-js app dir.

apps/web/lib/actions/partners/create-commission.ts (1)

39-52: Skip unnecessary DB queries for custom commissions

The partner/customer fetch happens even when linkId is absent and you immediately return in the next block.
Moving the custom-commission guard before this Promise.all will avoid an extra network/database round-trip.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (1)

145-147: Avoid floating-point rounding errors when scaling currency

data.amount * 100 and data.saleAmount * 100 can introduce precision issues (19.99 * 1001998.999…).
Use Math.round(Number(value) * 100) or a decimal library.

- amount: data.amount ? data.amount * 100 : null,
+ amount: data.amount ? Math.round(data.amount * 100) : null,
- saleAmount: data.saleAmount ? data.saleAmount * 100 : null,
+ saleAmount: data.saleAmount ? Math.round(data.saleAmount * 100) : null,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa43e14 and 0140aa8.

📒 Files selected for processing (10)
  • apps/web/app/(ee)/api/paypal/webhook/verify-signature.ts (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-button.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (5 hunks)
  • apps/web/lib/actions/partners/create-commission.ts (2 hunks)
  • apps/web/lib/partners/create-partner-commission.ts (5 hunks)
  • apps/web/lib/stripe/index.ts (1 hunks)
  • apps/web/lib/zod/schemas/commissions.ts (1 hunks)
  • apps/web/ui/partners/partner-details-sheet.tsx (3 hunks)
  • packages/prisma/schema/commission.prisma (1 hunks)
  • packages/ui/src/icons/nucleo/plus2.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-button.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (1)
  • useCreateCommissionSheet (637-648)
apps/web/ui/partners/partner-details-sheet.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (1)
  • useCreateCommissionSheet (637-648)
apps/web/lib/actions/partners/create-commission.ts (1)
apps/web/lib/partners/create-partner-commission.ts (1)
  • createPartnerCommission (16-196)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (3)
packages/ui/src/icons/nucleo/plus2.tsx (1)

20-20: Stylistic change acknowledged

Trailing-space removal and ensuring a newline at EOF look good – no functional impact.

packages/prisma/schema/commission.prisma (1)

27-27: Consider whether description needs length / index constraints

description String? is fine for optional free-text, but if you expect this field to be:

  1. Queried frequently (e.g., full-text search or filtering).
  2. Potentially very long (user-supplied notes).

then clarifying its DB representation (@db.Text vs default varchar) or adding a dedicated index / @@index([description]) might be worthwhile.

Confirm the usage pattern before shipping a migration that could later require a costly datatype change.

apps/web/lib/stripe/index.ts (1)

12-12: LGTM – parameter inlining only

Destructuring with inline type keeps the signature concise; no functional change.

Comment on lines +55 to +64
if (!linkId) {
await createPartnerCommission({
event: "custom",
partnerId,
programId,
amount: amount ?? 0,
quantity: 1,
createdAt: date ?? new Date(),
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

currency is omitted → commission creation may fail

createPartnerCommission ultimately persists currency (non-null in the Prisma model) but this call doesn’t pass one for the "custom" event.
If the column is NOT NULL, Prisma will throw.

 await createPartnerCommission({
   event: "custom",
   partnerId,
   programId,
   amount: amount ?? 0,
   quantity: 1,
+  currency: "usd",          // or derive from workspace settings
   createdAt: date ?? new Date(),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!linkId) {
await createPartnerCommission({
event: "custom",
partnerId,
programId,
amount: amount ?? 0,
quantity: 1,
createdAt: date ?? new Date(),
});
if (!linkId) {
await createPartnerCommission({
event: "custom",
partnerId,
programId,
amount: amount ?? 0,
quantity: 1,
currency: "usd", // or derive from workspace settings
createdAt: date ?? new Date(),
});
🤖 Prompt for AI Agents
In apps/web/lib/actions/partners/create-commission.ts around lines 55 to 64, the
call to createPartnerCommission for the "custom" event is missing the required
non-null currency field, which will cause Prisma to throw an error. Fix this by
passing a valid currency value to the createPartnerCommission call, ensuring it
matches the expected non-null currency field in the Prisma model.

Comment on lines 88 to 106
export const createCommissionSchema = z.object({
workspaceId: z.string(),
partnerId: z.string(),

// Custom
date: parseDateSchema.nullish(),
amount: z.number().min(0).nullish(),

// Lead
customerId: z.string().nullish(),
linkId: z.string().nullish(),
customerId: z.string(),
leadEventDate: parseDateSchema.nullish(),
leadEventName: z.string().nullish(),

// Sale
saleEventDate: parseDateSchema.nullish(),
saleAmount: z.number().min(0).nullish(),
invoiceId: z.string().nullish(),
leadEventDate: parseDateSchema.nullish(),
leadEventName: z.string().nullish(),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add cross-field refinements to prevent invalid payloads

createCommissionSchema allows impossible combos (e.g. saleAmount without linkId, or nothing but empty object).
Introduce superRefine rules to enforce:

  • At least amount for one-time commissions (!linkId).
  • linkId (and saleAmount when saleEventDate is supplied) for sale commissions.
  • linkId for lead commissions.

This keeps server logic simpler and reduces brittle runtime checks.

🤖 Prompt for AI Agents
In apps/web/lib/zod/schemas/commissions.ts around lines 88 to 106, the
createCommissionSchema currently allows invalid combinations of fields, such as
saleAmount without linkId or empty objects. To fix this, add a superRefine
method to the schema that enforces these rules: if linkId is not present, amount
must be provided for one-time commissions; if saleEventDate is present, both
linkId and saleAmount must be present for sale commissions; and for lead
commissions, linkId must be present. This will ensure only valid payloads pass
validation and simplify server-side logic.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 12, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2517:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (1)

131-146: Minor: variable shadowing reduces clarity

const date = … inside onSubmit shadows the earlier date obtained from watch.
While harmless, using distinct identifiers (e.g. isoDate) avoids cognitive overhead.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0140aa8 and f2e1265.

📒 Files selected for processing (2)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (5 hunks)
  • apps/web/lib/zod/schemas/commissions.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/zod/schemas/commissions.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (7)
apps/web/lib/zod/schemas/commissions.ts (1)
  • createCommissionSchema (88-107)
apps/web/lib/swr/use-rewards.ts (1)
  • useRewards (6-25)
packages/prisma/client.ts (1)
  • CommissionType (5-5)
apps/web/lib/actions/partners/create-commission.ts (1)
  • createCommissionAction (19-267)
apps/web/ui/partners/program-sheet-accordion.tsx (4)
  • ProgramSheetAccordion (87-87)
  • ProgramSheetAccordionItem (89-89)
  • ProgramSheetAccordionTrigger (90-90)
  • ProgramSheetAccordionContent (88-88)
packages/ui/src/toggle-group.tsx (1)
  • ToggleGroup (15-83)
packages/ui/src/sheet.tsx (1)
  • Sheet (74-78)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

Comment on lines +557 to +570
<AnimatedSizeContainer
height
transition={{ ease: "easeInOut", duration: 0.2 }}
className={!hasInvoiceId ? "hidden" : ""}
style={{ display: !hasInvoiceId ? "none" : "block" }}
>
<div className="flex items-center gap-4">
<Switch
fn={setHasInvoiceId}
checked={hasInvoiceId}
trackDimensions="w-8 h-4"
thumbDimensions="w-3 h-3"
thumbTranslate="translate-x-4"
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Invoice-ID toggle suffers from the same invisibility defect

hasInvoiceId defaults to false, yet the Switch controlling it is rendered inside an AnimatedSizeContainer that is hidden (display:none) when the flag is false, making the toggle unreachable.

Move the Switch (and its label) outside the collapsible container and keep only the invoice-ID input inside.

🤖 Prompt for AI Agents
In
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx
around lines 557 to 570, the Switch controlling hasInvoiceId is inside an
AnimatedSizeContainer that is hidden when hasInvoiceId is false, making the
toggle unreachable. To fix this, move the Switch component and its label outside
the AnimatedSizeContainer so it is always visible, and keep only the invoice-ID
input inside the collapsible container that toggles visibility based on
hasInvoiceId.

Comment on lines +396 to +436
<AnimatedSizeContainer
height
transition={{ ease: "easeInOut", duration: 0.2 }}
style={{
height: hasCustomLeadEventDate ? "auto" : "0px",
overflow: "hidden",
}}
>
<div className="flex flex-col gap-6">
<div className="flex items-center gap-4">
<Switch
fn={setHasCustomLeadEventDate}
checked={hasCustomLeadEventDate}
trackDimensions="w-8 h-4"
thumbDimensions="w-3 h-3"
thumbTranslate="translate-x-4"
/>
<div className="flex flex-col gap-1">
<h3 className="text-sm font-medium text-neutral-700">
Set a custom lead event date
</h3>
</div>
</div>

{hasCustomLeadEventDate && (
<div className="p-px">
<SmartDateTimePicker
value={leadEventDate}
onChange={(date) => {
setValue("leadEventDate", date, {
shouldDirty: true,
});
}}
label="Lead event date"
placeholder='E.g. "2024-03-01", "Last Thursday", "2 hours ago"'
/>
</div>
)}
</div>
</AnimatedSizeContainer>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Switches are rendered inside zero-height containers → user can never toggle them

hasCustomLeadEventDate and hasCustomLeadEventName are both initialised to false, yet the only Switch controls that can mutate them live inside an AnimatedSizeContainer whose height is 0px when the flag is false.
Result: the switches are invisible → the user cannot expand the section → the flag can never become true.

Same pattern repeats for both “custom lead event date” and “custom lead event name”.

- <AnimatedSizeContainer ... style={{ height: hasCustomLeadEventDate ? "auto" : "0px" }}>
-   <div className="flex items-center gap-4">
-     <Switch fn={setHasCustomLeadEventDate} checked={hasCustomLeadEventDate} … />
-
-   </div>
-   {hasCustomLeadEventDate && <SmartDateTimePicker … />}
- </AnimatedSizeContainer>
+ {/* 1️⃣ Always show the switch */}
+ <div className="flex items-center gap-4">
+   <Switch fn={setHasCustomLeadEventDate} checked={hasCustomLeadEventDate} … />
+
+ </div>
+ {/* 2️⃣ Conditionally reveal the extra field(s) */}
+ <AnimatedSizeContainer … style={{ height: hasCustomLeadEventDate ? "auto" : "0px", overflow: "hidden" }}>
+   {hasCustomLeadEventDate && <SmartDateTimePicker … />}
+ </AnimatedSizeContainer>

Apply the same restructuring for the lead event name block.

Also applies to: 438-482

🤖 Prompt for AI Agents
In
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx
around lines 396 to 436 and 438 to 482, the Switch components controlling
hasCustomLeadEventDate and hasCustomLeadEventName are placed inside
AnimatedSizeContainer elements that have height set to "0px" when the flags are
false, making the switches invisible and unclickable. To fix this, move the
Switch components outside of the AnimatedSizeContainer so they are always
visible and interactive, and keep only the expandable content inside the
AnimatedSizeContainer that toggles height based on the flags. Apply this
restructuring consistently for both the lead event date and lead event name
sections.

Comment on lines +156 to +174
const shouldDisableSubmit = useMemo(() => {
if (!partnerId) {
return true;
}

if (commissionType === "custom") {
return !amount;
}

if (commissionType === "sale") {
return !linkId || !saleAmount;
}

if (commissionType === "lead") {
return !linkId;
}

return false;
}, [commissionType, partnerId, linkId, saleAmount, amount]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

shouldDisableSubmit omits critical validations

  1. A lead commission can be submitted without selecting a customer (customerId) even though the UI makes the customer section mandatory.
  2. A sale commission can be submitted without a saleEventDate, which later hits the backend as null even though sales are time-bound.

Consider tightening the predicate:

-    if (commissionType === "sale") {
-      return !linkId || !saleAmount;
+    if (commissionType === "sale") {
+      return !linkId || !saleAmount || !saleEventDate;
     }
-    if (commissionType === "lead") {
-      return !linkId;
+    if (commissionType === "lead") {
+      return !linkId || !customerId;
     }

Ensure the Zod schema and backend expectations stay in sync with these rules.


🏁 Script executed:

#!/bin/bash
# Locate the create-commission-sheet file and inspect relevant lines and variables
file=$(fd create-commission-sheet.tsx)
echo "Found file: $file"
echo "Lines 140–200 of the file:"
sed -n '140,200p' "$file"
echo ""
echo "Occurrences of saleEventDate in file:"
rg -n "saleEventDate" "$file"
echo ""
echo "Occurrences of customerId in file:"
rg -n "customerId" "$file"

Length of output: 3129


🏁 Script executed:

#!/bin/bash
# Show top of file where form is initialized to inspect schema and validation
file='apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx'
echo "Lines 1–80 of the file:"
sed -n '1,80p' "$file"
echo ""
echo "Lines around form initialization:"
rg -n -C3 "useForm" "$file"

Length of output: 3399


Tighten submit‐button logic for sale and lead commissions

The current shouldDisableSubmit only guards on linkId/saleAmount for sales and just linkId for leads, allowing a sale to go through without a date and a lead without a customer—even though both fields are presented as required in the form.

• File:
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx
Lines: 156–174

• Update the predicate to include saleEventDate and customerId:

   if (commissionType === "sale") {
-    return !linkId || !saleAmount;
+    return !linkId || !saleAmount || !saleEventDate;
   }

   if (commissionType === "lead") {
-    return !linkId;
+    return !linkId || !customerId;
   }

– After adjusting the UI, ensure your Zod schema (createCommissionSchema) and any backend validations are updated to enforce the same rules.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const shouldDisableSubmit = useMemo(() => {
if (!partnerId) {
return true;
}
if (commissionType === "custom") {
return !amount;
}
if (commissionType === "sale") {
return !linkId || !saleAmount;
}
if (commissionType === "lead") {
return !linkId;
}
return false;
}, [commissionType, partnerId, linkId, saleAmount, amount]);
const shouldDisableSubmit = useMemo(() => {
if (!partnerId) {
return true;
}
if (commissionType === "custom") {
return !amount;
}
if (commissionType === "sale") {
return !linkId || !saleAmount || !saleEventDate;
}
if (commissionType === "lead") {
return !linkId || !customerId;
}
return false;
}, [commissionType, partnerId, linkId, saleAmount, amount]);
🤖 Prompt for AI Agents
In
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx
between lines 156 and 174, the shouldDisableSubmit logic is incomplete as it
does not check for saleEventDate on sale commissions and customerId on lead
commissions, which are required fields in the form. Update the predicate to also
return true (disable submit) if saleEventDate is missing for sales and if
customerId is missing for leads. After updating this UI logic, ensure the
createCommissionSchema and backend validations are aligned to enforce these
required fields consistently.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🔭 Outside diff range comments (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (1)

125-146: ⚠️ Potential issue

Monetary conversion can lose cents & hides a shadowed variable

  1. data.amount * 100 (and same for saleAmount) produces floating-point rounding errors (0.1 * 100 → 10.0000000001).
  2. The local constant date shadows the watched date, which is confusing.
-const date = data.date ? new Date(data.date).toISOString() : null;
+const isoDate = data.date ? new Date(data.date).toISOString() : null;

-amount: data.amount ? data.amount * 100 : null,
-saleAmount: data.saleAmount ? data.saleAmount * 100 : null,
+amount: data.amount != null ? Math.round(data.amount * 100) : null,
+saleAmount: data.saleAmount != null ? Math.round(data.saleAmount * 100) : null,
♻️ Duplicate comments (4)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (4)

156-174: ⚠️ Potential issue

Submit guard still omits saleEventDate & customerId
Issue was raised previously and remains unfixed.

The button allows:
• a sale without a saleEventDate
• a lead without a customerId

Please incorporate the missing checks as already suggested.


394-435: ⚠️ Potential issue

Switch for “custom lead event date” is inside a zero-height container

When hasCustomLeadEventDate is false the container height is 0 px, so the switch is invisible and cannot be toggled—user is locked out of the feature.

Move the <Switch> outside the collapsing <AnimatedSizeContainer> and keep only the picker inside.


438-480: ⚠️ Potential issue

Same invisibility problem for “custom lead event name”

The pattern repeats: switch is hidden together with the inputs it controls.

Restructure as above.


556-610: ⚠️ Potential issue

Invoice-ID toggle unreachable

<AnimatedSizeContainer> is hidden (display:none) until hasInvoiceId is true, yet the <Switch> that could flip that flag sits inside the same container. Users can never activate it.

Expose the switch outside the collapsible block.

🧹 Nitpick comments (3)
apps/web/styles/globals.css (1)

25-32: Add Firefox-friendly rule to fully suppress number spinners

-moz-appearance is deprecated and ignored in modern Firefox.
Add the standards-track appearance: textfield; for Gecko to guarantee the spinner never flashes on focus.

 input[type="number"]::-webkit-inner-spin-button,
 input[type="number"]::-webkit-outer-spin-button {
   -webkit-appearance: none;
-  -moz-appearance: none;
-  appearance: none;
+  appearance: none; /* Chromium / WebKit */
+}
+
+input[type="number"] {
+  appearance: textfield; /* Firefox */
   margin: 0;
 }
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (2)

101-111: Effect overwrites user accordion choices every render

setOpenAccordions runs on every commissionType change and hard-codes the open panels, so a user who manually collapses a section will have it re-opened on the next keystroke.
Persist user state or update once (e.g. inside initial useEffect).

-  useEffect(() => {
-    const baseValues = ["partner-and-type"];
-    if (commissionType === "custom") {
-      setOpenAccordions([...baseValues, "commission"]);
-    } else if (commissionType === "sale") {
-      setOpenAccordions([...baseValues, "customer", "sale"]);
-    } else if (commissionType === "lead") {
-      setOpenAccordions([...baseValues, "customer"]);
-    }
-  }, [commissionType]);
+  // Initialise once; afterwards let the accordion manage itself
+  useEffect(() => {
+    setOpenAccordions((prev) =>
+      prev.length ? prev : ["partner-and-type", commissionType === "custom" ? "commission" : "customer"],
+    );
+  }, []); /* ← empty dep array */

527-547: Consistent money input UX

saleAmount input lacks onKeyDown={handleMoneyKeyDown} while the amount field has it. Add it for uniform validation and to prevent “e+” scientific notation entry.

-  <input
+  <input
     {...field}
     type="number"
+    onKeyDown={handleMoneyKeyDown}
     …
   />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2e1265 and dfb55a1.

📒 Files selected for processing (2)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (5 hunks)
  • apps/web/styles/globals.css (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 12, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2517:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@steven-tey
Copy link
Collaborator

@CodeRabbit final review?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 12, 2025

Tip

For best results, initiate chat on the files or code changes.

@steven-tey – here’s a consolidated “final review” of the Manual Commissions PR (#2517).

High-level
• End-to-end flow for creating custom/lead/sale commissions works coherently: schema → action → partner-reward utility → UI sheet.
• API changes are strictly additive (new optional fields, broader enum), so risk of breaking existing integrations is very low.
• All automated tests pass repeatedly (Bug0), and no type errors are reported.

Key points to double-check before merge

  1. Database migration
    description String? added to Commission. Confirm that a corresponding Prisma migration file is in the repo / has been applied in staging.
  2. Validation edge cases
    createCommissionSchema allows amount = 0 for custom commissions. If zero-dollar payouts should be disallowed, change to z.number().gt(0).
    • Optional date, saleEventDate, leadEventDate default to new Date() in the action. Make sure this is the intended UX (implicit “now”).
  3. Business rules in createPartnerCommission
    • When event === "custom" we now skip determinePartnerReward. That’s fine, but be aware we also bypass maxDuration / maxAmount caps—flag if those should still gate manual entries.
  4. UI/UX
    • Number-input spinners removed globally; for accessibility we should provide another affordance (e.g., keyboard arrows still work, but visually impaired users lose a cue).
    • Accordion panels: initial focus is on the whole sheet; consider shifting focus to the first open panel for screen-reader friendliness.
    partnerId defaulted to "" in CreateCommissionButton; inside the sheet we coerce falsy to undefined, but a stray empty string could leak—worth a quick test.
  5. Logging / monitoring
    • Error paths now console.error(err) in the sheet. If we have a central logger (Sentry etc.) we might forward there as well to capture UI-side failures.
  6. Tests
    • Unit tests cover legacy lead/sale paths; add one for a zero-link “custom” commission to guarantee the early-return branch.
    • E2E: happy-path custom commission plus a negative case (missing amount) would cement confidence.

Minor polish
• Docstrings: several newly exported functions/hooks lack JSDoc (e.g., useCreateCommissionSheet). Running @coderabbitai generate docstrings would tick the finishing-touch checkbox.
• Stripe util one-liner change is fine; maybe add a quick comment on why livemode defaults to env.
• CSS rule could be scoped (e.g., .no-spinner input[type=number]) if we later need native spinners elsewhere.

No blockers detected—once the migration is in place and the validation semantics are confirmed, this looks good to ship. 🎉

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 12, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2517:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants