-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Manual commissions #2517
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
Manual commissions #2517
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis 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
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
Suggested reviewers
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
🤖 Bug0 QA Agent Here are the results of the automated tests for PR #2517:
To re-run the tests, please comment |
|
🤖 Bug0 QA Agent Here are the results of the automated tests for PR #2517:
To re-run the tests, please comment |
|
🤖 Bug0 QA Agent Here are the results of the automated tests for PR #2517:
To re-run the tests, please 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: 3
🔭 Outside diff range comments (1)
apps/web/lib/partners/create-partner-commission.ts (1)
158-174:⚠️ Potential issueEnsure
currencyis always populated
currencyis passed to Prisma regardless of event type.
When called from the custom-commission path it isundefined, leading to a DB error if the column isNOT 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 plainconsole.errorWhile 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: falseis the default – can be omittedUnless you intend to emphasise the explicit choice, dropping
nested: falsereduces 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 importsAll 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 commissionsThe partner/customer fetch happens even when
linkIdis absent and you immediately return in the next block.
Moving the custom-commission guard before thisPromise.allwill 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 * 100anddata.saleAmount * 100can introduce precision issues (19.99 * 100→1998.999…).
UseMath.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
📒 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 acknowledgedTrailing-space removal and ensuring a newline at EOF look good – no functional impact.
packages/prisma/schema/commission.prisma (1)
27-27: Consider whetherdescriptionneeds length / index constraints
description String?is fine for optional free-text, but if you expect this field to be:
- Queried frequently (e.g., full-text search or filtering).
- Potentially very long (user-supplied notes).
then clarifying its DB representation (
@db.Textvs 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 onlyDestructuring with inline type keeps the signature concise; no functional change.
.../web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-button.tsx
Show resolved
Hide resolved
| if (!linkId) { | ||
| await createPartnerCommission({ | ||
| event: "custom", | ||
| partnerId, | ||
| programId, | ||
| amount: amount ?? 0, | ||
| quantity: 1, | ||
| createdAt: date ?? new Date(), | ||
| }); | ||
|
|
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.
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.
| 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.
| 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(), | ||
| }); |
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.
🛠️ 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
amountfor one-time commissions (!linkId). linkId(andsaleAmountwhensaleEventDateis supplied) for sale commissions.linkIdfor 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 Here are the results of the automated tests for PR #2517:
To re-run the tests, please 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: 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 = …insideonSubmitshadows the earlierdateobtained fromwatch.
While harmless, using distinct identifiers (e.g.isoDate) avoids cognitive overhead.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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
| <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" | ||
| /> |
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.
🛠️ Refactor suggestion
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.
| <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> | ||
| )} |
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.
🛠️ Refactor suggestion
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.
| 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]); |
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.
💡 Verification agent
🧩 Analysis chain
shouldDisableSubmit omits critical validations
- A lead commission can be submitted without selecting a customer (
customerId) even though the UI makes the customer section mandatory. - A sale commission can be submitted without a
saleEventDate, which later hits the backend asnulleven 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.
| 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.
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
🔭 Outside diff range comments (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-commission-sheet.tsx (1)
125-146:⚠️ Potential issueMonetary conversion can lose cents & hides a shadowed variable
data.amount * 100(and same forsaleAmount) producesfloating-pointrounding errors (0.1 * 100 → 10.0000000001).- The local constant
dateshadows the watcheddate, 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 issueSubmit guard still omits
saleEventDate&customerId
Issue was raised previously and remains unfixed.The button allows:
• a sale without asaleEventDate
• a lead without acustomerIdPlease incorporate the missing checks as already suggested.
394-435:⚠️ Potential issueSwitch for “custom lead event date” is inside a zero-height container
When
hasCustomLeadEventDateisfalsethe container height is0 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 issueSame 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 issueInvoice-ID toggle unreachable
<AnimatedSizeContainer>is hidden (display:none) untilhasInvoiceIdis 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-appearanceis deprecated and ignored in modern Firefox.
Add the standards-trackappearance: 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
setOpenAccordionsruns on everycommissionTypechange 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 initialuseEffect).- 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
saleAmountinput lacksonKeyDown={handleMoneyKeyDown}while theamountfield 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
📒 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 Here are the results of the automated tests for PR #2517:
To re-run the tests, please comment |
|
@CodeRabbit final review? |
|
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 Key points to double-check before merge
Minor polish No blockers detected—once the migration is in place and the validation semantics are confirmed, this looks good to ship. 🎉 |
|
🤖 Bug0 QA Agent Here are the results of the automated tests for PR #2517:
To re-run the tests, please comment |
Summary by CodeRabbit
New Features
Improvements
UI Updates
Other