-
Notifications
You must be signed in to change notification settings - Fork 498
Rename catalog to product line #1107
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.
|
|
Caution Review failedThe pull request is closed. Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughRenames payment grouping from Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as "Payments API"
participant Service as "Payments Service"
participant Config as "Config / Migration"
participant DB
Client->>API: request purchase / validate-code / switch
API->>Service: validate request (expects productLineId)
Service->>Config: load payments config
alt legacy config present
Config->>Config: migrateCatalogsToProductLines()\n(convert catalogs→productLines, catalogId→productLineId)
Config-->>Service: productLines + inferred customerType
else already migrated
Config-->>Service: productLines
end
Service->>DB: read/write subscriptions & products (productLineId keys)
DB-->>Service: subscription/product data
Service-->>API: response (errors/messages reference "product line")
API-->>Client: return result / redirect URL
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
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
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.
Pull request overview
This PR renames the "catalog" terminology to "product line" throughout the codebase to better reflect the business concept. The changes include schema updates, backend logic, API endpoints, frontend UI, and comprehensive test coverage.
Changes:
- Renamed
catalogs/catalogIdtoproductLines/productLineIdacross schema, backend, and frontend - Added
customerTypefield to product lines with validation ensuring products match their product line's customer type - Included backward compatibility migration logic for old config properties
- Updated all UI components, error messages, and documentation to use "product line" terminology
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/stack-shared/src/schema-fields.ts | Updated product schema to use productLineId instead of catalogId |
| packages/stack-shared/src/config/schema.ts | Renamed config schema from catalogs to productLines, added customerType field and validation, added migration functions |
| packages/stack-shared/src/utils/jwt.tsx | Added debug console.log statement (unrelated to PR) |
| packages/stack-shared/src/config/schema-fuzzer.test.ts | Updated test fuzzer config for new schema |
| apps/e2e/tests/backend/endpoints/api/v1/payments/*.test.ts | Updated E2E tests to use new terminology |
| apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/*.test.ts | Added backward compatibility tests |
| apps/backend/src/lib/payments.tsx | Updated all payment logic to use productLineId instead of catalogId |
| apps/backend/src/lib/payments.test.tsx | Updated unit tests |
| apps/backend/src/app/api/latest/payments/**/*.ts | Updated API endpoints to use new terminology |
| apps/backend/prisma/seed.ts | Updated seed data |
| apps/dashboard/src/lib/apps-frontend.tsx | Updated navigation to "Product Lines" |
| apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/**/*.tsx | Renamed all UI components and dialogs from catalog to product line |
| apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/catalogs/layout.tsx | Deleted old catalogs layout file |
Comments suppressed due to low confidence (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:2448
- The
onCreateProductLinefunction only setsdisplayNamebut the schema now requirescustomerTypefor product lines. This will cause validation errors. The function should passcustomerTypefrom thenewProductLineCustomerTypestate variable. Update line 2446 to:{ displayName, customerType: newProductLineCustomerType }
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:2161 - Button text should have proper spacing: 'Create Product Line' instead of 'Create ProductLine'.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:1504 - The function signature for
onCreateProductLineis missing thecustomerTypeparameter, which is now required by the schema. Update the signature to:onCreateProductLine: (productLineId: string, displayName: string, customerType: 'user' | 'team' | 'custom') => Promise<void>
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:2139 - The call to
onCreateProductLineis missing the requiredcustomerTypeargument. Update to:await onCreateProductLine(id, displayName, newProductLineCustomerType);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Outdated
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
Greptile SummaryThis PR renames "catalog" to "product line" throughout the codebase, introducing a clearer naming convention for the mutual exclusivity grouping concept in the payments system. The refactoring includes schema updates, API logic changes, UI component updates, and comprehensive backward compatibility migrations. Key Changes:
Critical Issues Found:
Test Coverage: Comprehensive - old config formats are tested to verify they work after migration, though the migration bug may cause those tests to fail in practice. Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Customer
participant App as App Client
participant API as Backend API
participant Config as Config System
participant Stripe
Customer->>App: Select product
App->>API: POST /create-purchase-url
API->>Config: Load payments config
Config->>Config: Migrate old catalogs→productLines
Config->>Config: Validate productLineId.customerType matches product.customerType
API->>API: getSubscriptions()<br/>Check for product line conflicts
API->>API: validatePurchaseSession()<br/>Ensure mutual exclusivity
API->>Stripe: Create/update subscription
Stripe-->>API: subscription_id
API-->>App: Purchase code
App-->>Customer: Payment link
Note over Config: ISSUE: Migration doesn't<br/>infer customerType,<br/>old configs will fail validation
|
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.
30 files reviewed, 2 comments
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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/backend/src/lib/payments.test.tsx (2)
1206-1206: Update error message assertion to match production code.The test expects
"Multiple include-by-default products configured in the same catalog"but the production code throws"Multiple include-by-default products configured in the same product line". Changecatalogtoproduct linein the assertion on line 1206.
879-879: Update error message assertions to match production code.Two test assertions use incorrect error message strings:
- Line 879: expects
"...in this product catalog"but production code throws"...in this product line"- Line 1206: expects
"...in the same catalog"but production code throws"...in the same product line"Change both occurrences from "product catalog" to "product line" to match the actual error messages in
payments.tsx.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
329-358: Replace try/catch + destructive toast withrunAsynchronouslyWithAlert(and avoid toast for blocking errors).
Thecatchblock uses a destructive toast (Line 354-355). This is a blocking failure (product wasn’t created) and should be an alert /runAsynchronouslyWithAlertpath per guidelines.Based on learnings and as per coding guidelines, prefer `runAsynchronouslyWithAlert` for async submit handlers and avoid error toasts.Proposed refactor (keep your loading state; let the alert wrapper surface errors)
- const handleSave = async () => { + const handleSave = () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } - setIsSaving(true); - try { + runAsynchronouslyWithAlert(async () => { + setIsSaving(true); + try { const product: Product = { displayName, customerType, productLineId: productLineId || undefined, isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, stackable, prices: freeByDefault ? 'include-by-default' : prices, includedItems, serverOnly, freeTrial, }; await project.updateConfig({ [`payments.products.${productId}`]: product }); toast({ title: "Product created" }); router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { - setIsSaving(false); - } + } finally { + setIsSaving(false); + } + }); };Also applies to: 341-357
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
2044-2166: Replace blocking validation error toasts with alerts (and consider loading state).
Creating a product line can fail validation (invalid ID / duplicate ID), and those failures block the action. Usingtoast(..., variant: "destructive")here is easy to miss.As per coding guidelines, blocking alerts/errors shouldn’t use toast. Also, this is an async handler without a loading/disabled state; consider disabling the button while the request is in-flight.
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx:
- Around line 564-565: Replace the internal camelCase “productLine” with
user-facing “product line” in the UI copy: update the DialogDescription text
(and the other corresponding user-facing strings near the same block) so phrases
like “in the same productLine” and “same customer type and productLine” read “in
the same product line” and “same customer type and product line”; locate these
strings in the page-client.tsx DialogDescription/related JSX and change only the
displayed text.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx:
- Around line 762-767: existingProductsList currently maps over
paymentsConfig.products assuming each product exists, which can crash if some
entries are null/undefined; update the creation of existingProductsList to
mirror the falsy-product guard used for groupedProducts by filtering
Object.entries(paymentsConfig.products) to only include entries where product is
truthy (or by conditionally mapping and skipping nulls) before constructing
objects with id, displayName, productLineId, and customerType so no access on
null/undefined occurs.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:
- Around line 2437-2443: The onSaveProductWithGroup handler is overwriting the
whole payments.productLines.{productLineId} with an empty object which wipes
properties like displayName created by onCreateProductLine; update
onSaveProductWithGroup to stop setting
[`payments.productLines.${productLineId}`] to {} (remove that key from the
update payload) or instead set it to preserve the existing displayName (e.g.,
[`payments.productLines.${productLineId}`]: { displayName: existingDisplayName
}) while still writing [`payments.products.${productId}`]: product and calling
toast.
In `@packages/stack-shared/src/utils/jwt.tsx`:
- Line 66: Remove the leftover debug log "console.log({ options })" from
packages/stack-shared/src/utils/jwt.tsx so the raw JWT/token in the options
object is not emitted to logs; search the jwt utility for any other ad-hoc
console.log/debug prints and delete them or replace with safe, non-sensitive
logging via the existing logger if needed, then run tests/lint to ensure no
unrelated changes were introduced.
🧹 Nitpick comments (9)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsx (1)
97-97: Align hover transition with coding guidelines and file consistency.The className uses
transition-colorswithouthover:transition-none, which creates both hover-enter and hover-exit transitions. Per coding guidelines, hover transitions should only occur on hover-exit. The form variant in this same file (lines 163, 239) correctly implements this pattern withtransition-colors duration-150 hover:transition-none.♻️ Proposed fix to align with hover transition pattern
- className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors" + className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors duration-150 hover:transition-none"As per coding guidelines: "When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions".
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
8-14: Consider usingsanitizeUserSpecifiedIdfor consistency.The
toIdFormathelper lowercases the input and strips non-alphanumeric characters, butsanitizeUserSpecifiedId(used on line 116 for manual edits) preserves case. This creates inconsistent behavior:
- Auto-generated ID:
"Pricing Tiers"→"pricing-tiers"(lowercase)- Manual edit:
"Pricing-Tiers"→"Pricing-Tiers"(case preserved)Consider applying
.toLowerCase()to the manual edit path as well, or documenting this as intentional behavior.♻️ Optional: Apply consistent lowercase transformation
onChange={(e) => { - const value = sanitizeUserSpecifiedId(e.target.value); + const value = sanitizeUserSpecifiedId(e.target.value).toLowerCase(); setProductLineId(value); setHasManuallyEditedId(true); setErrors(prev => ({ ...prev, id: undefined })); }}apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts (1)
1260-1260: Test title still references "catalog" instead of "product line".The test title says "same catalog" but should say "same product line" to be consistent with the rename throughout the codebase.
Suggested fix
-it("should immediately cancel existing subscriptions when granting a product of same catalog", async ({ expect }) => { +it("should immediately cancel existing subscriptions when granting a product of same product line", async ({ expect }) => {apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (1)
670-670: Minor: transition class may not work as expected.The
transition-colorsclass is applied but there's no hover state on this div. The opacity transition on the buttons inside (line 678) usesgroup-hover, but the parent div doesn't have thegroupclass.Suggested fix
<div key={itemId} - className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors" + className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors group" >apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx (1)
633-645: Minor: consider normalizing/typingproductLineIdand avoiding!where easy.
Ifproduct.productLineIdcan ever benull/""(orproductcan be falsy), normalizing toundefined(and/or tightening types forgroups/paymentsGroups) will reduce edge-case drift. As per coding guidelines, prefer defensive patterns over non-null assertions when reasonable.Also applies to: 680-685, 708-710
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
314-320: Product line + add-on selection should be constrained bycustomerType(and ideally auto-filtered).
Right now, product lines are reset on customer type change (Line 280-282), but the Select still lists all product lines (Line 915-919). Also, the add-on parent picker isn’t filtered by customer type, only validated by productLine (Line 314-319). This can let users pick invalid combinations that later UIs prevent.As per coding guidelines, prefer preventing invalid user choices over surfacing toasts/errors later.
Also applies to: 895-925
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (3)
60-66:toIdFormathelper is fine, but it’s duplicated across multiple files.
Consider centralizing to avoid subtle divergence over time (you now have at least 3 copies).
2167-2204: Async edit/delete product line flows: consider error surfacing + loading.
These handlers call async callbacks and then toast success, but failures will currently surface poorly (or via a global boundary).As per coding guidelines, prefer
runAsynchronouslyWithAlert(or equivalent) for async button actions that can fail.Also applies to: 2206-2225
2455-2478: Deletion-by-move looks correct; confirmundefinedis the right “unset” value for persisted config.
You setproductLineId: undefinedwhen moving products to “No product line”. If the config layer expectsnullto clear fields (as in other parts of the codebase), you may want to normalize tonullor omit the key entirely. The inline comment explaining theas anyis good.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (30)
apps/backend/prisma/seed.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/catalogs/layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/config/schema.tspackages/stack-shared/src/schema-fields.tspackages/stack-shared/src/utils/jwt.tsxpackages/template/src/components-page/account-settings/payments/payments-panel.tsx
💤 Files with no reviewable changes (1)
- apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/catalogs/layout.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxpackages/stack-shared/src/config/schema.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tspackages/template/src/components-page/account-settings/payments/payments-panel.tsxpackages/stack-shared/src/schema-fields.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tspackages/stack-shared/src/utils/jwt.tsxapps/backend/prisma/seed.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxpackages/stack-shared/src/config/schema.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tspackages/template/src/components-page/account-settings/payments/payments-panel.tsxpackages/stack-shared/src/schema-fields.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tspackages/stack-shared/src/utils/jwt.tsxapps/backend/prisma/seed.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxpackages/stack-shared/src/config/schema.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tspackages/template/src/components-page/account-settings/payments/payments-panel.tsxpackages/stack-shared/src/schema-fields.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tspackages/stack-shared/src/utils/jwt.tsxapps/backend/prisma/seed.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
{**/apps-frontend.tsx,**/apps-config.ts}
📄 CodeRabbit inference engine (AGENTS.md)
To update the list of available apps, edit
apps-frontend.tsxandapps-config.ts. Check existing apps for inspiration when implementing new apps or pages
Files:
apps/dashboard/src/lib/apps-frontend.tsx
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Use.toMatchInlineSnapshotover other selectors in tests when possible, and check/modify snapshot-serializer.ts to understand how snapshots are formatted
Files:
apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/lib/payments.test.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
packages/stack-shared/src/config/schema.ts
📄 CodeRabbit inference engine (AGENTS.md)
Whenever making backwards-incompatible changes to the config schema, update the migration functions in
packages/stack-shared/src/config/schema.ts
Files:
packages/stack-shared/src/config/schema.ts
🧠 Learnings (5)
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to {**/apps-frontend.tsx,**/apps-config.ts} : To update the list of available apps, edit `apps-frontend.tsx` and `apps-config.ts`. Check existing apps for inspiration when implementing new apps or pages
Applied to files:
apps/dashboard/src/lib/apps-frontend.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Applied to files:
apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tsapps/backend/src/lib/payments.test.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to packages/stack-shared/src/config/schema.ts : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`
Applied to files:
packages/stack-shared/src/config/schema.tspackages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/schema-fields.tsapps/backend/prisma/seed.ts
📚 Learning: 2026-01-11T06:44:57.542Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 1069
File: packages/template/src/components-page/onboarding.tsx:59-61
Timestamp: 2026-01-11T06:44:57.542Z
Learning: In the stack-auth codebase, Button components from stackframe/stack-ui (including those used via MessageCard props like primaryAction and secondaryAction) can accept async functions directly and automatically handle loading states with indicators. No need to wrap these with runAsynchronouslyWithAlert.
Applied to files:
packages/template/src/components-page/account-settings/payments/payments-panel.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
packages/template/src/components-page/account-settings/payments/payments-panel.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
🧬 Code graph analysis (10)
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.ts (1)
apps/backend/src/lib/payments.tsx (1)
isActiveSubscription(308-310)
apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx (1)
apps/backend/src/lib/payments.tsx (1)
validatePurchaseSession(598-682)
apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts (1)
packages/stack-shared/src/utils/errors.tsx (1)
StatusError(152-261)
apps/backend/src/lib/payments.tsx (2)
packages/stack-shared/src/utils/objects.tsx (2)
typedEntries(263-265)typedKeys(304-306)packages/stack-shared/src/schema-fields.ts (1)
productSchema(624-647)
apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts (1)
apps/e2e/tests/helpers.ts (1)
it(12-12)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (2)
packages/stack-shared/src/schema-fields.ts (3)
isValidUserSpecifiedId(453-455)getUserSpecifiedIdErrorMessage(460-462)sanitizeUserSpecifiedId(469-477)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronously(343-366)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (8)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useProjectId(46-53)packages/stack-shared/src/utils/strings.tsx (1)
stringCompare(61-65)packages/stack-shared/src/schema-fields.ts (2)
isValidUserSpecifiedId(453-455)getUserSpecifiedIdErrorMessage(460-462)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsx (1)
PageClient(68-108)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (1)
PageClient(72-95)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx (1)
PageClient(598-944)packages/stack-shared/src/utils/objects.tsx (2)
typedEntries(263-265)typedFromEntries(281-283)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (15)
- GitHub Check: CodeQL analysis (javascript-typescript)
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: all-good
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: docker
- GitHub Check: lint_and_build (latest)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
🔇 Additional comments (68)
packages/template/src/components-page/account-settings/payments/payments-panel.tsx (2)
5-8: LGTM!The
Resultimport is properly added and correctly used withResult.fromThrowingAsync()on line 389 for error handling in the plan-switching flow. Import ordering is clean.
380-380: LGTM!The terminology update from "catalog" to "product line" aligns with the PR objective. The text is properly internationalized via
t().packages/stack-shared/src/schema-fields.ts (1)
624-647: Schema field rename looks correct and is properly supported by migrations.The
productLineIdfield definition follows the established pattern with proper validation viauserSpecifiedIdSchemaand includes a clear OpenAPI description explaining the mutual exclusivity semantics.The migration function at lines 380-382 of
packages/stack-shared/src/config/schema.tscorrectly handles the backwards-incompatiblecatalogId→productLineIdrename for existing data, using therenamePropertyutility to update the config path.apps/dashboard/src/lib/apps-frontend.tsx (1)
150-167: LGTM! Navigation and description updates are consistent with the route structure.The changes correctly update both the navigation item (displayName to "Product Lines" and href to "./product-lines") and the store description text. The corresponding product-lines directory exists in the route structure, and no remaining "catalogs" references were found in the payments section.
packages/stack-shared/src/config/schema-fuzzer.test.ts (2)
67-82: LGTM! Good backward compatibility testing for migration paths.The fuzzer config correctly includes both the new
productLinesstructure withcustomerTypeand the legacycatalogs/groupsproperties to ensure migration functions work correctly. This is well-aligned with the schema migration strategy.
96-98: Migration coverage is appropriate.Including
catalogIdandgroupIdalongsideproductLineIdin the fuzzer ensures backward compatibility during the migration period.apps/backend/src/app/api/latest/payments/purchases/validate-code/route.ts (1)
73-91: LGTM! Product line conflict detection logic is correct.The refactored code properly:
- Looks up product lines from the tenancy config
- Filters conflicts by matching
productLineId- Preserves the API response contract with
conflicting_productsThe guard at line 76 (
if (productLineId)) correctly handles the case when a product has no product line assigned.apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.ts (2)
1-63: Excellent backward compatibility test coverage.This test correctly validates that the migration layer properly handles legacy
catalogsandcatalogIdconfig properties. The test structure is clear with good comments explaining the purpose.Based on learnings, adding E2E tests for API changes is critical in this industry, and this test appropriately covers the backward compatibility path.
65-167: Comprehensive conflict detection test with legacy config.The test properly validates the complete flow:
- Setup with legacy
catalogs/catalogIdproperties- Purchase one product
- Validate that the second product correctly reports conflicts
This ensures the migration layer maintains functional correctness for conflict detection.
apps/backend/src/lib/payments.test.tsx (3)
61-62: LGTM! Manual change tests updated correctly.The test fixtures properly use
productLines: {}for scenarios without product line grouping.Also applies to: 104-106
146-159: LGTM! Subscription tests properly updated.The test fixtures correctly use
productLinesin the tenancy config andproductLineIdin product definitions.
913-914: LGTM! Response property assertions updated correctly.The tests properly verify the renamed response properties:
res.productLineId(wasres.catalogId)res.conflictingProductLineSubscriptions(wasres.conflictingCatalogSubscriptions)Also applies to: 951-952, 1064-1065
apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts (2)
64-66: LGTM! Product line validation correctly updated.The validation properly checks
productLineIdand the error message is updated to use "product line" terminology consistently.
93-99: LGTM! One-time purchase check updated consistently.The variable rename to
hasOneTimeInProductLineand the updated error message maintain consistency with the broader nomenclature change.apps/backend/prisma/seed.ts (3)
115-119: Consistent rename fromcatalogstoproductLinesin internal project config.The payments configuration correctly migrates from
catalogstoproductLinesterminology, with all product definitions updated to useproductLineIdinstead ofcatalogId. The structure and semantics remain unchanged.Also applies to: 122-122, 143-143, 164-164, 179-179
817-817: Dummy payments setup correctly updated for productLine terminology.The
buildDummyPaymentsSetupfunction properly renames:
- Product definitions now use
productLineId(e.g.,'workspace','add_ons')- Top-level config key renamed from
catalogstoproductLinesConsistent with the broader refactor.
Also applies to: 845-845, 881-881, 908-915
1234-1239: Inline product seed data updated to useproductLineId.The inline product definitions in
seedDummyTransactions(legacy enterprise pilot and design audit pass) correctly useproductLineIdinstead ofcatalogId.Also applies to: 1409-1418
apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.ts (2)
49-70: Switch options grouping correctly migrated toproductLineId.The Map-based grouping logic properly renamed from
switchOptionsByCatalogIdtoswitchOptionsByProductLineId. The guard on line 55 and subsequent Map operations consistently useproduct.productLineId. Logic remains functionally identical.
76-80: Product mapping correctly referencesproductLineIdfor switch options.The final product mapping extracts
productLineIdand uses it to look up switch options from the grouped Map. Consistent with the upstream changes.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (2)
22-56: Component and state management correctly updated for Product Line.The component properly:
- Renames from
CreateCatalogDialogtoCreateProductLineDialog- Updates
onCreatecallback signature to useproductLineobject- Adds
hasManuallyEditedIdstate for tracking user intent- Validates using
productLineIdwith appropriate error messagesForm reset logic correctly clears all new state fields.
66-142: UI terminology and auto-ID generation properly implemented.The dialog correctly:
- Updates all labels and descriptions to "Product Line" terminology
- Auto-generates ID from display name when user hasn't manually edited
- Uses
runAsynchronouslyfor asynconCreatehandler per coding guidelinesapps/backend/src/lib/payments.tsx (5)
86-88: Inline product correctly returnsproductLineId: undefined.When creating an inline product (not from config),
productLineIdis correctly set toundefinedsince inline products don't belong to any product line.
318-375: Subscription retrieval logic correctly migrated toproductLines.The
getSubscriptionsfunction properly:
- Reads from
tenancy.config.payments.productLines- Tracks which product lines have DB subscriptions via
productLinesWithDbSubscriptionsSet- Iterates product lines to find and add default "include-by-default" products
- Maintains correct error message for multiple defaults in the same product line (line 356)
Logic remains functionally identical to the previous catalog-based implementation.
377-393: Ungrouped default detection correctly checksproductLineId === undefined.Products without a
productLineIdthat are "include-by-default" are correctly identified and added as default subscriptions.
609-681:validatePurchaseSessionreturn type and conflict detection updated.The function correctly:
- Returns
productLineIdandconflictingProductLineSubscriptionsinstead of catalog equivalents- Looks up product line from
tenancy.config.payments.productLines- Blocks purchases when one-time purchase exists in same product line
- Filters conflicting subscriptions based on
product.productLineIdAll terminology and logic consistent with the rename.
721-755:grantProductToCustomercorrectly uses renamed conflict subscriptions.The destructuring and conflict handling logic properly uses
conflictingProductLineSubscriptionsto cancel conflicting subscriptions in the same product line.apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx (1)
61-73: Purchase session handler correctly uses renamed conflict subscriptions.The destructuring and conflict handling logic properly uses
conflictingProductLineSubscriptionsfrom the updatedvalidatePurchaseSessionreturn type. Logic remains unchanged.apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts (1)
1270-1316: LGTM!The product configuration correctly uses the new
productLinesandproductLineIdproperties. The test validates the expected behavior of products within the same product line.packages/stack-shared/src/config/schema.ts (4)
151-157: LGTM!The schema correctly renames
catalogstoproductLinesandcatalogIdtoproductLineId. The addition ofcustomerTypeto product lines with corresponding cross-field validation is a good enhancement.
169-191: LGTM!The cross-field validation ensures data integrity by:
- Verifying that referenced product lines exist
- Enforcing that product
customerTypematches its product line'scustomerTypeThe error messages are descriptive and helpful for debugging.
374-384: LGTM!The migration functions correctly handle the backward-incompatible schema changes:
- Renames
payments.catalogstopayments.productLines- Renames
payments.products.*.catalogIdtopayments.products.*.productLineIdThis follows the established migration pattern and ensures existing configurations continue to work. Based on learnings, this is the required approach for backwards-incompatible changes to the config schema.
585-591: LGTM!The default values are correctly updated to use the new
productLinesandproductLineIdnaming.apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.ts (3)
1-60: LGTM! Excellent backward compatibility coverage.This test correctly validates that the old
catalogsandcatalogIdconfiguration properties continue to work after the rename. The migration functions inschema.tsshould automatically convert these toproductLinesandproductLineId.
62-130: LGTM!This test validates that:
- Old
catalogs/catalogIdconfig is properly migrated- Product line grouping logic works correctly after migration
- Error messages use the new "product line" terminology
132-240: LGTM!The subscription switching test validates that the group-based switching logic continues to work correctly with the old configuration format after migration.
apps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.ts (1)
880-880: LGTM!The updated assertion correctly expects the new "product line" terminology in the error message, even when the test uses the old configuration format. This validates that the migration layer works correctly end-to-end.
apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts (3)
4-13: LGTM!The
setupProductsfunction signature and implementation are correctly updated to useproductLinesinstead ofcatalogs.
16-63: LGTM!The test is correctly updated:
- Title reflects new "product line" terminology
- Configuration uses
productLineIdinstead ofcatalogId- Expected error message uses new terminology
65-106: LGTM!The test correctly uses
productLineIdfor both products, maintaining consistency with the rename.apps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.ts (1)
157-176: LGTM! Consistent rename from catalog to product line terminology.The test configuration correctly updates
catalogs→productLinesandcatalogId→productLineIdfor bothproductAandproductB. Test logic and assertions remain unchanged.apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (5)
169-189: LGTM! Product line terminology correctly applied to group subscription test.The test configuration properly uses
productLinesandproductLineIdfor testing one-time purchases with conflicting group subscriptions.
641-675: LGTM! Consistent rename in subscription switching test.The product configuration correctly uses
productLinesandproductLineIdfor bothproductAandproductBwhen testing subscription updates within a group.
748-781: LGTM! DB-only subscription cancellation test updated correctly.Configuration and product definitions properly reflect the product line terminology.
911-932: LGTM! One-time purchase blocking test updated.The test correctly uses
productLinesandproductLineIdfor testing purchase blocking within the same group.
969-969: LGTM! Error message assertion updated to reflect product line terminology.The assertion correctly checks for the updated error message containing "one-time purchase in this product line".
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (7)
9-9: LGTM! Import updated to match renamed dialog component.
25-26: LGTM! Props interface updated with product line terminology.Both
existingProductsitem type andexistingProductLinesprop correctly reflect the rename.
65-65: LGTM! State variables renamed consistently.
productLineIdandshowProductLineDialogstates properly renamed from their catalog equivalents.Also applies to: 76-76
135-140: LGTM! Validation logic correctly updated for product line consistency.The add-on validation properly checks that all selected products share the same
productLineIdand shows an appropriate error message.
466-499: LGTM! UI labels and selection logic updated for product lines.The select component correctly uses "Product Line" terminology in labels, placeholders, and options.
568-572: LGTM! Add-on product display shows product line name.The display logic correctly looks up product line names from
existingProductLines.
770-778: LGTM! CreateProductLineDialog integration updated correctly.The dialog integration properly uses the renamed component and callback.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsx (1)
3-5: LGTM! Page metadata title updated to reflect product line terminology.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsx (3)
9-9: LGTM! Import updated to renamed view component.
17-17: LGTM! WelcomeScreen title and description updated with product line terminology.The descriptions clearly explain the product line concept and its relationship to products.
Also applies to: 51-56
100-105: LGTM! PageLayout and view component properly renamed.The title, description, and component reference consistently use product line terminology.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx (2)
357-370: Product grouping/rendering rename looks consistent (productLineId end-to-end).
The filter + render paths now consistently key byproductLineIdand fall back to “Other” for ungrouped.Also applies to: 388-436
818-829: WiringpaymentsConfig.productLines+existingProductLinesinto child components looks correct.
Prop renames are consistent and the page now passes product-line groups to both desktop and mobile layouts.Also applies to: 880-890, 913-927
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (2)
193-285: ResettingproductLineIdon customer type change is a good guardrail.
This keeps productLine selection consistent with the “product lines are customer-type-specific” constraint.
287-292: No changes required—the code pattern is correct. TheCreateProductLineDialog'sonCreatecontract explicitly accepts(productLine: {...}) => void | Promise<void>, andhandleCreateProductLinecorrectly returnsvoid. The dialog wraps the result withrunAsynchronously(), which handles bothvoidandPromise<void>return types uniformly via its signaturepromiseOrFunc: void | Promise<unknown> | (...) | undefined. This pattern is intentional and working as designed.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (3)
105-107:productLineNameresolution + header display look good.
The fallback to ID when displayName is missing is consistent and keeps the header stable.Also applies to: 216-221
259-283: Product line selection/create flow is coherent (incl.__none__sentinel).
Nice touch using__none__to avoid empty string Select values; handlers correctly map it tonullin config updates.Also applies to: 338-357, 417-429, 627-632
202-204: No action needed: route is already correct.The actual code at line 202 uses
/payments/product-lines#product-${productId}(with hyphens), which matches the existing directory structure. The review comment's snippet is inaccurate; the navigation route is not broken.Likely an incorrect or invalid review comment.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (5)
571-573: ProductLine metadata propagation (prompt + add-on candidate filtering) looks correct.
This keeps generated docs and add-on constraints aligned with the productLine model.Also applies to: 850-852
1512-1689: Regrouping by(productLineId, customerType)is a solid model for “type-specific” product lines.
The keying and sorting logic is clear and should keep the UI deterministic across customer types.
1716-1753: Edit/delete affordances for product lines are implemented cleanly (hover-revealed actions).
The empty product line rendering + controls is also a nice completeness touch.Also applies to: 1990-2016
2386-2418: Double-check semantics: deleting the last product also deletes its product line.
Given this file now explicitly supports empty product lines (including an empty-product-line section + edit/delete), auto-deleting the product line on last product deletion may be surprising. If intentional, it might be worth a comment in code / UI copy explaining it.
2502-2505:existingProductLinesmapping intoProductDialoglooks consistent with the new naming.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Outdated
Show resolved
Hide resolved
.../src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
331-360: Replace manual try-catch withrunAsynchronouslyWithAlertfor consistent error handling.The
handleSavefunction uses a try-catch block withtoastfor error notification. As per coding guidelines, you should never try-catch-all and never usetoastfor blocking alerts and errors. Instead, userunAsynchronouslyWithAlertwhich provides consistent error handling.♻️ Suggested refactor
const handleSave = async () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setIsSaving(true); - try { + await runAsynchronouslyWithAlert(async () => { const product: Product = { displayName, customerType, productLineId: productLineId || undefined, isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, stackable, prices: freeByDefault ? 'include-by-default' : prices, includedItems, serverOnly, freeTrial, }; await project.updateConfig({ [`payments.products.${productId}`]: product }); toast({ title: "Product created" }); router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { - setIsSaving(false); - } + }).finally(() => { + setIsSaving(false); + }); };
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx:
- Around line 202-203: Change the menu item label text to use a human-readable
form: update the DropdownMenuItem that calls
router.push(`/projects/${projectId}/payments/product-lines#product-${productId}`)
so its display string is "View in Product Lines" (two words) instead of "View in
ProductLines"; keep the router.push call and URL unchanged and only modify the
visible text inside the DropdownMenuItem component.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:
- Line 2162: Replace the inconsistent label "Create ProductLine" with "Create
Product Line" in the component that renders the create button in
page-client-product-lines-view (search for the literal "Create ProductLine" in
page-client-product-lines-view.tsx and update the JSX/label or translation key
used by the button to the spaced form); if the label comes from a translation
key, update the corresponding copy/value instead of the inline string so it
remains consistent across the UI.
♻️ Duplicate comments (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (2)
2019-2021: Query parameterproductLineIdmay not be read by the target page.This link passes
productLineIdas a query parameter, but based on prior review comments, the receiving page (/payments/products/new/page-client.tsx) may not be reading this parameter. Please verify the target page usesuseSearchParamsto read and apply this value for pre-selecting the product line.#!/bin/bash # Check if the new product page reads the productLineId query parameter rg -n "productLineId|useSearchParams" "apps/dashboard/src/app/(main)/(protected)/projects/\[projectId\]/payments/products/new/" -A 3 -B 1
2438-2443: Product line displayName will be wiped when saving product with group.As noted in a prior review, the
overridefunction replaces the entire object at a given key path rather than merging. Settingpayments.productLines.${productLineId}to{}will wipe thedisplayNameproperty that was set during product line creation.Either remove this line entirely if the product line already exists, or preserve the displayName:
Proposed fix
onSaveProductWithGroup={async (productLineId, productId, product) => { + // Get existing productLine data to preserve displayName + const existingProductLine = paymentsConfig.productLines[productLineId]; await project.updateConfig({ - [`payments.productLines.${productLineId}`]: {}, + [`payments.productLines.${productLineId}`]: existingProductLine ?? {}, [`payments.products.${productId}`]: product, }); toast({ title: "Product created" }); }}Or if the product line should already exist before this is called, simply remove the productLines update:
onSaveProductWithGroup={async (productLineId, productId, product) => { await project.updateConfig({ - [`payments.productLines.${productLineId}`]: {}, [`payments.products.${productId}`]: product, }); toast({ title: "Product created" }); }}apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (2)
564-565: Minor UI copy: Use "product line" (two words) in dialog description.The user-facing text should read "in the same product line" instead of "in the same productLine".
589-589: Minor UI copy: Use "product line" (two words) in empty state message.Should read "same customer type and product line" instead of "same customer type and productLine".
🧹 Nitpick comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
382-384: Navigation change from router towindow.history.back()is intentional but loses type safety.Using
window.history.back()means the user could navigate to an external page if they entered this page directly. Consider using the router with a fallback:💡 Optional: Add fallback for direct page access
const handleCancel = () => { - window.history.back(); + if (window.history.length > 1) { + window.history.back(); + } else { + router.push(`/projects/${projectId}/payments/products`); + } };
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
AGENTS.mdapps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Use.toMatchInlineSnapshotover other selectors in tests when possible, and check/modify snapshot-serializer.ts to understand how snapshots are formatted
Files:
apps/backend/src/lib/payments.test.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
🧠 Learnings (7)
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Applied to files:
apps/backend/src/lib/payments.test.tsx
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to {**/apps-frontend.tsx,**/apps-config.ts} : To update the list of available apps, edit `apps-frontend.tsx` and `apps-config.ts`. Check existing apps for inspiration when implementing new apps or pages
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Code defensively using `?? throwErr(...)` instead of non-null assertions, with good error messages explicitly stating violated assumptions
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use ES6 maps instead of records wherever possible
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. Use `runAsynchronously` or `runAsynchronouslyWithAlert` instead of general try-catch error handling
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't use `Date.now()` for measuring elapsed (real) time; instead use `performance.now()`
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Try to avoid the `any` type. When using `any`, leave a comment explaining why and how the type system fails or how errors would still be caught
Applied to files:
AGENTS.md
🧬 Code graph analysis (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (6)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useProjectId(46-53)packages/stack-shared/src/utils/strings.tsx (1)
stringCompare(61-65)packages/stack-shared/src/utils/urls.tsx (1)
urlString(314-316)packages/stack-shared/src/schema-fields.ts (2)
isValidUserSpecifiedId(453-455)getUserSpecifiedIdErrorMessage(460-462)packages/stack-shared/src/utils/objects.tsx (2)
typedEntries(263-265)typedFromEntries(281-283)
⏰ 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). (12)
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: setup-tests
- GitHub Check: lint_and_build (latest)
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
🔇 Additional comments (28)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (4)
289-294: LGTM!The
handleCreateProductLinefunction correctly usesrunAsynchronouslyWithAlertfor async error handling, and the rename fromhandleCreateCatalogtohandleCreateProductLinewith the updated config pathpayments.productLines.{id}is consistent with the PR objectives.
315-321: LGTM!The validation logic correctly checks that all selected add-on products belong to the same product line, with the error message properly updated to reference "product line" instead of "catalog".
897-927: LGTM!The product line dropdown is correctly implemented with:
- Proper state binding to
productLineId- Sentinel value
'no-product-line'for the empty state- Iteration over
paymentsConfig.productLines- Create new option triggering the dialog
The rename from catalog terminology is complete and consistent.
991-995: LGTM!The
CreateProductLineDialogcomponent is correctly wired up with the renamed state and handler props, consistent with the external snippet showing the dialog's expected interface.apps/backend/src/lib/payments.test.tsx (6)
61-62: Consistent terminology update across test fixtures.The rename from
catalogs→productLinesandcatalogId→productLineIdis applied consistently across the manual changes and subscription test cases. The mock tenancy configurations correctly use the new property names.Also applies to: 105-106, 150-153, 188-191, 227-230
913-914: Property accessors correctly updated.The assertions accessing
res.productLineIdandres.conflictingProductLineSubscriptionsreflect the renamed return type properties fromvalidatePurchaseSession.Also applies to: 951-952, 1064-1065
817-846: Test correctly validates duplicate one-time purchase blocking.The mock data at line 820 correctly uses
productLineId: undefinedfor the existing purchase, and thecodeData.productat line 834 also usesproductLineId: undefined. The test logic is sound for validating that duplicate purchases for the sameproductIdare blocked.
849-880: Test validates one-time purchase blocking within the same product line.The mock at line 853 correctly sets
productLineId: 'g1'for an existing purchase, and the test product at line 867 also usesproductLineId: 'g1'. This correctly tests the scenario where a purchase should be blocked when another exists in the same product line.
879-879: Error message correctly matches implementation.The test assertion expects
'Customer already has a one-time purchase in this product line', which matches the implementation inpayments.tsxat line 665. The fix is correct.
1167-1167: Test description and error message match implementation.The test name at line 1167 correctly describes the scenario as "same line", and the error message at line 1206 expects
'Multiple include-by-default products configured in the same product line', which matches the error thrown in the implementation at line 356 of payments.tsx.AGENTS.md (1)
89-89: Good addition for URL safety best practice.The
urlStringtagged template literal utility is a sensible addition to the coding guidelines, as it automatically encodes interpolated values usingencodeURIComponent(), preventing URL encoding issues and potential injection vulnerabilities. Recommending its use (or plainencodeURIComponent()as a fallback) for URL construction promotes both safety and consistency across the codebase.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (8)
61-67: LGTM - Helper function for ID generationThe
toIdFormatfunction correctly handles basic slug generation. It converts to lowercase, replaces non-alphanumeric sequences with hyphens, and trims leading/trailing hyphens.
572-574: LGTM - Consistent rename in prompt generationThe prompt generation correctly uses
productLineIdinstead of the oldcatalogId.
851-851: LGTM - Filter updated to use productLineIdThe add-on filter logic correctly uses
productLineIdfor determining which products could be add-ons.
1496-1521: LGTM - Type definitions updated consistentlyThe type renames and new callback props for CRUD operations on product lines are well-structured.
1527-1536: LGTM - State variables for product line managementThe new state variables for managing product line editing and deletion are well-organized.
2168-2226: LGTM - Product line edit and delete dialogsThe dialogs for editing and deleting product lines are well-implemented with proper state management and clear user messaging about the consequences of deletion.
2455-2479: LGTM - Product line deletion with product migrationThe deletion logic correctly moves products to "No product line" before removing the product line. The
as anycast is justified by the comment explaining the dynamic nature of the config update.
2231-2237: LGTM - PageClient component setupThe component correctly uses the renamed types and props for the product line view.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (9)
56-56: LGTM!Import correctly updated to use the renamed
CreateProductLineDialogcomponent.
105-106: LGTM!The product line derivation correctly handles the lookup with proper null checks and fallback behavior.
135-141: LGTM!Props and function signature correctly updated to use
productLineNamewith proper nullable typing.
216-219: LGTM!Product line display correctly uses human-readable "Product Line:" label and properly handles the null case.
259-282: LGTM!The
productLineOptionsderivation correctly:
- Determines customer type compatibility from existing products in each product line
- Disables incompatible options with clear explanatory text
- Uses
__none__sentinel value for "No product line" option- Has complete dependency array
304-316: LGTM!The filter correctly uses
productLineIdfor matching products in the same product line, with proper dependencies.
338-357: LGTM!Both handlers correctly implement the product line operations:
handleProductLineUpdateproperly converts the__none__sentinel tonullhandleCreateProductLinesequentially creates the product line and assigns the product
417-429: LGTM!Grid item correctly configured with proper user-facing labels, tooltip, and the "Create new product line" action.
627-632: LGTM!
CreateProductLineDialogcorrectly integrated with proper state management and callbacks.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Outdated
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
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
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx:
- Around line 387-389: The handleCancel function uses window.history.back()
which fails when the page is opened directly; update handleCancel to check
history length and fall back to a deterministic navigation to the products list
(e.g., if (window.history.length > 1) window.history.back(); else
router.push(`/projects/${projectId}/payments/products`)). Use the existing
projectId value and your router/navigation helper (e.g., next/navigation's
useRouter or a navigate function) to implement the fallback so cancel always
returns the user to the products list.
In `@packages/stack-shared/src/config/schema-fuzzer.test.ts`:
- Around line 97-99: productLineId (and legacy catalogId/groupId) values include
"some-other-product-line-id" but the productLines fuzzer config only defines
"some-product-line-id", causing potential invalid references during cross-field
validation; update the productLines fuzzer configuration to also include
"some-other-product-line-id" (or alternatively change the
productLineId/catalogId/groupId arrays to only reference existing IDs) so
generated products always reference defined product lines; locate symbols
productLineId, catalogId, groupId and the productLines fuzzer entries in
schema-fuzzer.test.ts to apply the fix.
In `@packages/stack-shared/src/config/schema.ts`:
- Around line 388-411: The current inference loop treats res as flat
dot-notation keys and thus never finds keys like
"payments.products.{id}.customerType"; replace this logic with the same nested
traversal utility used elsewhere (e.g., use mapProperty/renameProperty) to
iterate payments.products and collect productLineId -> customerType, or
explicitly traverse res.payments.products and res.payments.productLines objects
to build productLineCustomerTypes and then set missing
payments.productLines.{id}.customerType; target symbols: res,
productLineCustomerTypes, mapProperty/renameProperty, payments.products,
payments.productLines.
♻️ Duplicate comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
2162-2162: Inconsistent button text casing.The button text "Create ProductLine" should be "Create Product Line" (with a space) for consistency with the rest of the UI copy.
Proposed fix
- Create ProductLine + Create Product Linepackages/stack-shared/src/config/schema.ts (1)
152-158: Fix backwards compatibility for empty productLines in migration.The
customerTypefield is required in the schema but will remainundefinedafter migration for product lines without associated products. The migration at lines 388-410 only inferscustomerTypefrom products that reference the product line, leaving empty productLines unable to satisfy schema validation.Consider either:
- Detecting old configs with empty catalogs and assigning them a default
customerType, or- Making
customerTypeoptional in the schema to gracefully handle this edge case.
🧹 Nitpick comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
2130-2132: Consider user-friendly error message.The
getUserSpecifiedIdErrorMessage("productLineId")may produce a technical error message. Consider whether this message will be clear to dashboard users.#!/bin/bash # Check what error message is generated for productLineId rg -n "getUserSpecifiedIdErrorMessage" --type ts -A 3 | head -40apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
336-365: Replace try-catch-all withrunAsynchronouslyWithAlertfor proper error handling.Per coding guidelines, this code has two issues:
- Uses try-catch-all pattern which should be avoided
- Uses
toastfor errors (line 361) when alerts should be used for blocking errors♻️ Suggested refactor using runAsynchronouslyWithAlert
const handleSave = async () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setIsSaving(true); - try { + runAsynchronouslyWithAlert(async () => { const product: Product = { displayName, customerType, productLineId: productLineId || undefined, isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, stackable, prices: freeByDefault ? 'include-by-default' : prices, includedItems, serverOnly, freeTrial, }; await project.updateConfig({ [`payments.products.${productId}`]: product }); toast({ title: "Product created" }); router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { + }).finally(() => { setIsSaving(false); - } + }); };
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
AGENTS.mdREADME.mdapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/config/schema.ts
✅ Files skipped from review due to trivial changes (1)
- README.md
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts
- apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
- apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Use.toMatchInlineSnapshotover other selectors in tests when possible, and check/modify snapshot-serializer.ts to understand how snapshots are formatted
Files:
packages/stack-shared/src/config/schema-fuzzer.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
packages/stack-shared/src/config/schema-fuzzer.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
packages/stack-shared/src/config/schema-fuzzer.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
packages/stack-shared/src/config/schema-fuzzer.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
packages/stack-shared/src/config/schema.ts
📄 CodeRabbit inference engine (AGENTS.md)
Whenever making backwards-incompatible changes to the config schema, update the migration functions in
packages/stack-shared/src/config/schema.ts
Files:
packages/stack-shared/src/config/schema.ts
🧠 Learnings (9)
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to packages/stack-shared/src/config/schema.ts : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`
Applied to files:
packages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/config/schema.ts
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Code defensively using `?? throwErr(...)` instead of non-null assertions, with good error messages explicitly stating violated assumptions
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use ES6 maps instead of records wherever possible
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. Use `runAsynchronously` or `runAsynchronouslyWithAlert` instead of general try-catch error handling
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Try to avoid the `any` type. When using `any`, leave a comment explaining why and how the type system fails or how errors would still be caught
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't use `Date.now()` for measuring elapsed (real) time; instead use `performance.now()`
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., use `usePathname` instead of `await params`)
Applied to files:
AGENTS.md
📚 Learning: 2025-12-03T07:19:44.433Z
Learnt from: madster456
Repo: stack-auth/stack-auth PR: 1040
File: packages/stack-shared/src/interface/crud/oauth-providers.ts:62-87
Timestamp: 2025-12-03T07:19:44.433Z
Learning: In packages/stack-shared/src/interface/crud/oauth-providers.ts and similar CRUD files, the tag "Oauth" (not "OAuth") is the correct capitalization format as it's used by the documentation generation system and follows OpenAPI conventions.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useProjectId(46-53)packages/stack-shared/src/utils/urls.tsx (1)
urlString(314-316)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
PageClient(178-1031)
packages/stack-shared/src/config/schema.ts (2)
packages/stack-shared/src/schema-fields.ts (2)
yupRecord(283-322)yupObject(247-251)packages/stack-shared/src/utils/objects.tsx (1)
getOrUndefined(545-548)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (13)
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: Cursor Bugbot
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: setup-tests
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
🔇 Additional comments (21)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (9)
51-67: LGTM!The
urlStringimport aligns with the coding guideline to preferurlString/encodeURIComponentfor URLs. ThetoIdFormathelper correctly converts display names to valid ID format.
1496-1521: LGTM!The
ProductLineViewPropstype correctly includescustomerTypeinonCreateProductLine, and theProductLineTypeKeytype provides proper grouping semantics.
2019-2021: LGTM!The
productLineIdquery parameter is correctly passed usingurlStringfor proper URL encoding. Per the relevant code snippets, the receiving page (new/page-client.tsx) reads this parameter viasearchParams.get("productLineId").
2438-2448: LGTM!The
onSaveProductWithGrouphandler correctly updates only the product without overwriting the product line. TheonCreateProductLinehandler properly includescustomerTypein the saved data.
2454-2478: LGTM!The
onDeleteProductLinehandler correctly moves products to "No product line" before deletion. Theas anycast includes appropriate justification per coding guidelines.
1716-1755: LGTM!The product line header with hover-to-reveal edit/delete buttons follows good UX patterns. The transition classes correctly use
hover:transition-noneper coding guidelines for snappy hover interactions.
2168-2227: LGTM!The edit and delete dialogs are well-implemented with proper state management and appropriate use of toast for success feedback.
2386-2423: LGTM!The
handleDeleteProductfunction correctly handles cascading deletion of empty product lines and uses proper immutable update patterns withtypedFromEntries/typedEntries.
1653-1666: LGTM!The
emptyProductLinescomputation correctly identifies product lines without any products for rendering empty state UI.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (7)
33-36: LGTM! Import changes align with product line terminology.The
useSearchParamsusage is appropriate here since this is a client component, which is the preferred approach per coding guidelines for keeping pages static.
240-246: LGTM! Property mapping correctly updated.
289-299: LGTM! Handler correctly usesrunAsynchronouslyWithAlert.The config path update to
payments.productLines.${productLine.id}aligns with the backend schema changes.
320-327: LGTM! Validation logic correctly enforces product line consistency.The check ensures all selected add-on products belong to the same product line, which maintains the mutual exclusivity constraint.
428-439: LGTM! Code generation correctly reflects product line terminology.
902-932: LGTM! Product line selector UI correctly implemented.The select component properly handles the three cases: no product line, existing product lines, and creating a new one. Terminology is consistent throughout.
996-1000: LGTM! Dialog correctly integrated with product line creation flow.AGENTS.md (1)
89-91: LGTM!These new guidelines are clear, actionable, and align well with defensive coding practices. The path notation guidance (line 90) is particularly relevant for the config migration changes in this PR.
packages/stack-shared/src/config/schema.ts (3)
15-15: LGTM!The
getOrUndefinedimport is correctly added to support the new cross-field validation logic.
170-193: LGTM!The cross-field validation is well-implemented:
- Safely handles missing
productLineIdby continuing- Provides clear error messages with specific IDs for debugging
- Uses
getOrUndefineddefensively to check productLine existence before accessing properties
613-619: LGTM!The defaults are correctly updated to reflect the rename. The
customerType: undefineddefault is appropriate since this field must be explicitly set when creating new product lines.packages/stack-shared/src/config/schema-fuzzer.test.ts (1)
68-83: LGTM!Excellent test coverage for the migration paths. The fuzzer config correctly includes:
- New
productLinesstructure withcustomerType(lines 68-73)- Legacy
catalogsandgroupsstructures (lines 74-83) to ensure migrations workThis ensures the fuzzer exercises both fresh configs and migrated legacy configs.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...hboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
Show resolved
Hide resolved
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Outdated
Show resolved
Hide resolved
...hboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
Show resolved
Hide resolved
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
336-365: Replace try-catch withrunAsynchronouslyWithAlertfor error handling.The current implementation uses try-catch with
toastfor error display, which violates the coding guidelines: "NEVER try-catch-all" and "For blocking alerts and errors, never use toast, as they are easily missed by the user."🛠️ Suggested refactor
- const handleSave = async () => { + const handleSave = () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setIsSaving(true); - try { + runAsynchronouslyWithAlert(async () => { const product: Product = { displayName, customerType, productLineId: productLineId || undefined, isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, stackable, prices: freeByDefault ? 'include-by-default' : prices, includedItems, serverOnly, freeTrial, }; await project.updateConfig({ [`payments.products.${productId}`]: product }); toast({ title: "Product created" }); router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { + }, { + finally: () => setIsSaving(false), + }); - setIsSaving(false); - } };Based on coding guidelines, use
runAsynchronouslyWithAlertinstead of general try-catch error handling.
♻️ Duplicate comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
269-272: Customer type selection should resetproductLineIdinitialized from URL.This remains an issue from a previous review: when a user arrives via URL with
?productLineId=...and then selects a different customer type, theproductLineIdstate is not reset, unlike when changing via the dropdown (handleCustomerTypeChange). This can cause schema validation failures at save time.🛠️ Suggested fix
const handleSelectCustomerType = (type: 'user' | 'team' | 'custom') => { setCustomerType(type); setHasSelectedCustomerType(true); + // Reset productLineId if it was pre-set from URL for a different customer type + if (productLineId) { + const existingProductLine = paymentsConfig.productLines[productLineId]; + if (existingProductLine && existingProductLine.customerType !== type) { + setProductLineId(""); + } + } };
🧹 Nitpick comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
906-936: Consider filtering product lines by customer type in the dropdown.The dropdown displays all product lines from
paymentsConfig.productLineswithout filtering bycustomerType. Users might inadvertently select a product line created for a different customer type (e.g., selecting a "team" product line when creating a "user" product).💡 Optional enhancement
- {Object.entries(paymentsConfig.productLines).map(([id, productLine]) => ( + {Object.entries(paymentsConfig.productLines) + .filter(([, productLine]) => productLine.customerType === customerType) + .map(([id, productLine]) => ( <SelectItem key={id} value={id} className="rounded-lg"> {productLine.displayName || id} </SelectItem> ))}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/components/data-table/payment-product-table.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/components/data-table/payment-product-table.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/dashboard/src/components/data-table/payment-product-table.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/dashboard/src/components/data-table/payment-product-table.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (2)
packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronouslyWithAlert(312-328)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (14)
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: setup-tests
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: check_prisma_migrations (22.x)
🔇 Additional comments (8)
apps/dashboard/src/components/data-table/payment-product-table.tsx (3)
11-15: Clean type alias extraction.The
BranchPaymentstype alias centralizes the schema inference and improves readability. UsingNonNullableis appropriate here to ensure the type is non-nullable when accessing nested properties likeproducts.
54-54: Prop typing aligned with the new type alias.The updated prop type is consistent with the
BranchPaymentstype alias, maintaining type safety while improving code readability.
106-116: Consider the toast usage for delete confirmation.The
toastis used here for success feedback after deletion, which is generally acceptable. However, since deletion is a destructive action, verify this aligns with your UX patterns — the guideline mentions avoidingtoastfor blocking alerts and errors, but a success notification after an irreversible action might benefit from being more prominent if you want users to clearly confirm the action completed.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (5)
289-298: LGTM!The handler correctly uses
runAsynchronouslyWithAlertfor async operations with proper error handling, and the rename from catalog to product line is consistent.
387-393: LGTM!The cancel handler now properly checks
window.history.lengthbefore callingback(), with a fallback to navigate to the products list. This addresses the concern from the previous review about direct URL navigation.
320-327: LGTM!The validation logic correctly renames catalog references to product line while maintaining the same behavior for checking add-on product consistency.
1000-1004: LGTM!The dialog component is correctly renamed and wired with appropriate props matching the
CreateProductLineDialoginterface.
436-436: LGTM!The inline product code generation and prompt generation correctly use
productLineIdterminology, ensuring copied code snippets are consistent with the new naming.Also applies to: 473-473
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
...hboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
Outdated
Show resolved
Hide resolved
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
351-371: Refactor to userunAsynchronouslyWithAlertinstead of try-catch-all with toast.Per coding guidelines, avoid try-catch-all patterns and never use
toastfor error notifications (they're easily missed). Consider refactoring to userunAsynchronouslyWithAlert:Suggested refactor
const handleSave = async () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setIsSaving(true); - try { - const product: Product = { - displayName, - customerType, - productLineId: productLineId || undefined, - isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, - stackable, - prices: freeByDefault ? 'include-by-default' : prices, - includedItems, - serverOnly, - freeTrial, - }; - - await project.updateConfig({ [`payments.products.${productId}`]: product }); - toast({ title: "Product created" }); - router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { - setIsSaving(false); - } + runAsynchronouslyWithAlert(async () => { + const product: Product = { + displayName, + customerType, + productLineId: productLineId || undefined, + isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, + stackable, + prices: freeByDefault ? 'include-by-default' : prices, + includedItems, + serverOnly, + freeTrial, + }; + + await project.updateConfig({ [`payments.products.${productId}`]: product }); + toast({ title: "Product created" }); + router.push(`/projects/${projectId}/payments/products`); + }).finally(() => setIsSaving(false)); };Based on coding guidelines: "NEVER try-catch-all" and "For blocking alerts and errors, never use
toast".
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx:
- Around line 933-937: The product-line dropdown currently maps all entries from
paymentsConfig.productLines, causing mismatches with the selected customer type;
update the rendering to first filter Object.entries(paymentsConfig.productLines)
to only include lines whose productLine.customerType matches the form's
customerType (or includes it if productLine.customerType is an array) before
mapping to SelectItem; locate the dropdown mapping near the SelectItem render
and use the same customerType state/prop used by handleCreateProductLine to
ensure only customer-type-compatible product lines are shown.
♻️ Duplicate comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
195-202: Product line URL parameter is validated for existence but not customer type compatibility.The validation correctly checks if the
productLineIdexists in config, which addresses the previous crash concern. However, whenhandleSelectCustomerTypeis called (line 276), the pre-selectedproductLineIdfrom URL isn't validated against the newly selected customer type. A user could navigate with?productLineId=team-line, select "user" customer type, and end up with a mismatched product line.Consider resetting
productLineIdinhandleSelectCustomerTypeif it doesn't match the selected customer type:Suggested fix
const handleSelectCustomerType = (type: 'user' | 'team' | 'custom') => { setCustomerType(type); setHasSelectedCustomerType(true); + // Reset productLineId if it doesn't match the selected customer type + if (productLineId) { + const productLine = paymentsConfig.productLines[productLineId]; + if (productLine?.customerType !== type) { + setProductLineId(""); + } + } };
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxpackages/stack-shared/src/config/migrate-catalogs-to-product-lines.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/stack-shared/src/config/migrate-catalogs-to-product-lines.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (14)
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: lint_and_build (latest)
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: Cursor Bugbot
- GitHub Check: build (22.x)
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: restart-dev-and-test
- GitHub Check: build (22.x)
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: docker
- GitHub Check: setup-tests
🔇 Additional comments (5)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (5)
296-306: LGTM!The
handleCreateProductLinefunction correctly usesrunAsynchronouslyWithAlertfor async error handling and properly updates the config with the new product line.
394-400: LGTM!The cancel handler now properly checks history length before navigating back, with a fallback to the products list page. This addresses the previous concern about direct URL access.
328-333: LGTM!The add-on validation correctly ensures all selected products belong to the same product line, with updated terminology.
439-450: LGTM!The inline code generation and prompt functions correctly include
productLineIdwhen set, with proper conditional formatting.Also applies to: 476-481
1007-1011: LGTM!The
CreateProductLineDialogis correctly wired up with the renamed state and handler.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...hboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
Outdated
Show resolved
Hide resolved
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.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
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
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx:
- Around line 298-310: The filter in availableProducts fails when productLineId
uses inconsistent null/undefined so normalize both sides before comparing:
inside the useMemo filter for config.payments.products (the availableProducts
computation) coerce p.productLineId and product.productLineId to a consistent
value (e.g., use the nullish coalescing result like (p.productLineId ?? null)
and (product.productLineId ?? null) or stringify both) and compare those
normalized values along with id !== productId and p.customerType ===
product.customerType so add-on candidates aren't incorrectly excluded.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:
- Around line 2131-2136: Replace the non-blocking toast calls used for
validation with a blocking alert mechanism: where the code currently calls
toast({ title: getUserSpecifiedIdErrorMessage("productLineId"), variant:
"destructive" }) and toast({ title: "Product line ID already exists", variant:
"destructive" }) (in the product line creation/validation branch that checks
groups and productLineId), render or invoke a blocking Alert (or window.alert
for a quick fix) that presents the same message and prevents continuation; if
your app uses a UI Alert component, set it to an error/destructive state and
surface it synchronously instead of calling toast so the user must acknowledge
the validation failure before proceeding.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx:
- Around line 574-577: The span rendering uses
existingProductLines[product.productLineId].displayName without guarding for a
missing entry, which throws if that key is undefined; update the expression in
product-dialog.tsx (the block that references product.productLineId and
existingProductLines) to safely access the metadata (use optional chaining) and
fall back to product.productLineId or a safe placeholder when
existingProductLines[product.productLineId] is undefined so the UI doesn't crash
for deleted/unknown product lines.
♻️ Duplicate comments (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
276-279: Reset mismatched productLineId on initial customer type selection.If the URL preselects a product line with a different customer type, the first-step selection keeps the invalid
productLineId, and the mismatch isn’t corrected until save. Consider clearing it up-front.🛠️ Proposed fix
const handleSelectCustomerType = (type: 'user' | 'team' | 'custom') => { setCustomerType(type); setHasSelectedCustomerType(true); + if (productLineId) { + const line = paymentsConfig.productLines[productLineId]; + if (!line || line.customerType !== type) { + setProductLineId(""); + } + } };apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
1644-1646: Guard against deleted product lines when sorting/rendering.
groups[productLineId]can beundefinedwhen drafts reference deleted product lines, which will crash during sorting/rendering.🛠️ Proposed fix
-const nameA = a.productLineId ? (groups[a.productLineId].displayName || a.productLineId) : ''; -const nameB = b.productLineId ? (groups[b.productLineId].displayName || b.productLineId) : ''; +const nameA = a.productLineId ? (groups[a.productLineId]?.displayName || a.productLineId) : ''; +const nameB = b.productLineId ? (groups[b.productLineId]?.displayName || b.productLineId) : ''; -const groupName = productLineId ? (groups[productLineId].displayName || productLineId) : 'No product line'; +const groupName = productLineId ? (groups[productLineId]?.displayName || productLineId) : 'No product line';Also applies to: 1700-1701
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (1)
559-586: User-facing copy: “product line” should be two words.✏️ Suggested copy tweak
- <p className="text-sm text-muted-foreground">No other products with the same customer type and productLine</p> + <p className="text-sm text-muted-foreground">No other products with the same customer type and product line</p>
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧠 Learnings (2)
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to {**/apps-frontend.tsx,**/apps-config.ts} : To update the list of available apps, edit `apps-frontend.tsx` and `apps-config.ts`. Check existing apps for inspiration when implementing new apps or pages
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
📚 Learning: 2025-12-03T07:19:44.433Z
Learnt from: madster456
Repo: stack-auth/stack-auth PR: 1040
File: packages/stack-shared/src/interface/crud/oauth-providers.ts:62-87
Timestamp: 2025-12-03T07:19:44.433Z
Learning: In packages/stack-shared/src/interface/crud/oauth-providers.ts and similar CRUD files, the tag "Oauth" (not "OAuth") is the correct capitalization format as it's used by the documentation generation system and follows OpenAPI conventions.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (14)
- GitHub Check: setup-tests
- GitHub Check: restart-dev-and-test
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: lint_and_build (latest)
- GitHub Check: build (22.x)
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: docker
- GitHub Check: build (22.x)
🔇 Additional comments (17)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (7)
181-202: URL productLineId preselection is safely validated.Good guard against invalid query params before initializing state.
285-305: Customer-type change now clears incompatible product-line state.Nice reset of product line and add-on selections when customer type changes.
327-333: Add‑on validation is correctly constrained to a single product line.
392-397: Cancel/back fallback looks solid.
413-478: Inline code + prompt now include productLineId when set.
911-937: Product line dropdown is correctly filtered by customer type.
1007-1011: CreateProductLineDialog wiring looks correct.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (4)
134-140: Add‑on validation now enforces single product line.
444-451: Customer type change correctly clears productLineId.
470-504: Product line selector and filtering are aligned with customer type.
776-783: CreateProductLineDialog integration looks good.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (3)
61-67: ID normalization helper is clean and reusable.
1879-1889: “Add product” now preserves productLineId in the URL.
2444-2447: Product line creation now persists customerType.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (3)
105-106: Product line header wiring looks consistent.
Clean fallback to displayName/id and conditional rendering.Also applies to: 123-123, 138-141, 216-220
202-204: Navigation label/route for Product Lines looks correct.
56-56: Product line selection + create dialog integration looks solid.
Filtering by customer type and the create flow wiring are coherent.Also applies to: 257-276, 332-354, 417-425, 624-629
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
...shboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx
Show resolved
Hide resolved
...shboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx
Show resolved
Hide resolved
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.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
...hboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
Outdated
Show resolved
Hide resolved
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: 2
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:
- Around line 2359-2383: The onDeleteProductLine handler currently moves
persisted products to no product line but leaves in-memory drafts orphaned;
before calling project.updateConfig you should migrate or clear drafts that
reference the deleted productLineId by updating their product.productLineId to
undefined (or relocating them) and then call the drafts setter/callback (e.g.,
setDrafts or migrateDrafts) with the updated drafts; to do this, accept a drafts
updater into the handler (or lift the handler so it has access to setDrafts),
iterate the existing drafts to modify any draft whose product.productLineId ===
productLineId, apply the updated drafts state, and only then proceed to build
updateConfig and call project.updateConfig.
In `@packages/stack-shared/src/config/migrate-catalogs-to-product-lines.ts`:
- Around line 81-105: The migration currently uses result.set(catalogId,
customerType) (and similarly result.set(value, obj[customerTypeKey])) which
silently overwrites existing entries; update both places to check if
result.has(catalogId) (or result.has(value)) and if so verify the existing value
equals the new customerType, otherwise throw an Error that includes the
conflicting catalogId and both customerType values to force manual resolution;
apply this logic in the nested-branch (where catalogId is derived via
findPropertyValue) and in the root flat-format branch (where customerTypeKey is
used), leaving typedEntries, isObjectLike, and findPropertyValue unchanged.
♻️ Duplicate comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
2050-2055: Use alerts instead of toasts for blocking validation errors.These validation errors prevent the action from completing. Per coding guidelines, blocking errors should use alerts rather than toasts since toasts can be easily missed.
🔧 Suggested fix
Replace the toast calls with alert dialogs or the standard
alert()function:if (!isValidUserSpecifiedId(id)) { - toast({ title: getUserSpecifiedIdErrorMessage("productLineId"), variant: "destructive" }); + alert(getUserSpecifiedIdErrorMessage("productLineId")); return; } if (Object.prototype.hasOwnProperty.call(groups, id)) { - toast({ title: "Product line ID already exists", variant: "destructive" }); + alert("Product line ID already exists"); return; }Based on coding guidelines requiring alerts for blocking errors.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
289-295: Reset productLineId when customer type is chosen via the selection screen.The dropdown path clears productLineId, but the initial selection flow doesn’t. If a user goes “Back” and picks a different customer type, the previous productLineId can linger and cause a mismatch on save.
🛠️ Suggested fix
const handleSelectCustomerType = (type: 'user' | 'team' | 'custom') => { setCustomerType(type); + setProductLineId(""); + setIsAddOnTo([]); setHasSelectedCustomerType(true); };
🧹 Nitpick comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
1610-1631: Consider usingconstfor the non-reassigned variable.
customerTypeat line 1612 is never reassigned, soconstwould be more appropriate.✨ Suggested fix
const productLinesToRender = useMemo(() => { const productLines = Object.entries(groups).map(([id, productLine]) => { - let customerType = productLine.customerType; + const customerType = productLine.customerType; return { id, displayName: productLine.displayName || id, customerType, }; });
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/backend/prisma/seed.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/migrate-catalogs-to-product-lines.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/backend/prisma/seed.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxpackages/stack-shared/src/config/migrate-catalogs-to-product-lines.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxpackages/stack-shared/src/config/migrate-catalogs-to-product-lines.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxpackages/stack-shared/src/config/migrate-catalogs-to-product-lines.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧠 Learnings (6)
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to {**/apps-frontend.tsx,**/apps-config.ts} : To update the list of available apps, edit `apps-frontend.tsx` and `apps-config.ts`. Check existing apps for inspiration when implementing new apps or pages
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to packages/stack-shared/src/config/schema.ts : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`
Applied to files:
packages/stack-shared/src/config/migrate-catalogs-to-product-lines.ts
📚 Learning: 2025-12-03T07:19:44.433Z
Learnt from: madster456
Repo: stack-auth/stack-auth PR: 1040
File: packages/stack-shared/src/interface/crud/oauth-providers.ts:62-87
Timestamp: 2025-12-03T07:19:44.433Z
Learning: In packages/stack-shared/src/interface/crud/oauth-providers.ts and similar CRUD files, the tag "Oauth" (not "OAuth") is the correct capitalization format as it's used by the documentation generation system and follows OpenAPI conventions.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : For blocking alerts and errors, never use `toast`, as they are easily missed by the user. Instead, use alerts
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. Use `runAsynchronously` or `runAsynchronouslyWithAlert` instead of general try-catch error handling
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧬 Code graph analysis (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (2)
packages/stack-shared/src/config/schema.ts (1)
CompleteConfig(1118-1118)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
packages/stack-shared/src/config/migrate-catalogs-to-product-lines.ts (2)
packages/stack-shared/src/utils/objects.tsx (2)
typedEntries(263-265)isObjectLike(579-581)packages/stack-shared/src/utils/errors.tsx (1)
StackAssertionError(69-85)
⏰ 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). (14)
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: setup-tests
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: all-good
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: lint_and_build (latest)
🔇 Additional comments (22)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (4)
61-67: LGTM!The
toIdFormathelper is well-implemented with proper lowercase conversion, non-alphanumeric replacement, and hyphen trimming at boundaries.
851-852: LGTM!The add-on filtering logic correctly matches products within the same product line.
1496-1521: LGTM!The updated types properly define the product line CRUD callbacks with appropriate parameters including
customerType.
2343-2358: LGTM!The
onSaveProductWithGrouphandler now correctly saves only the product without overwriting the product line's properties. TheonCreateProductLinecallback properly includescustomerType.packages/stack-shared/src/config/migrate-catalogs-to-product-lines.ts (7)
8-26: LGTM for the path-aware rename helper.Recursion + segment mapping looks solid, and the dot-guard on
newNameis a good safety check.
116-128: LGTM for dot-notation fallback lookup.
134-151: LGTM for catalog ID enumeration across formats.
158-207: Removal logic covers nested + flat shapes nicely.
212-283: CustomerType injection handles mixed formats cleanly.
286-475: Nice breadth of migration tests across formats and edge cases.
41-69: Migration is properly wired. The function is imported at line 19 and invoked in the migration pipeline at line 379 within the correct version gate (isBranchOrHigher), ensuring existing configs are upgraded automatically.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (11)
33-36: LGTM for adding search params + product line dialog imports.
187-199: Good validation of URL prefill inputs.
205-218: State init aligns with prefill + new product line dialog.
251-272: Product line ID now flows into preview and existing product list cleanly.
300-309: LGTM for creating product lines with the active customerType.
332-337: Addon validation updated for product lines looks good.
354-366: LGTM for persisting productLineId on product creation.
396-401: Back/cancel fallback looks solid.
441-482: Inline code + prompt reflect product line selection correctly.
915-946: Filtering product lines by customer type is a good guardrail.
1011-1014: Dialog wiring looks correct.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Show resolved
Hide resolved
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
1855-1865: KeepproductLineIdin the Add‑product link even whencustomerTypeis missing.
If a migrated config lackscustomerType, the current conditional dropsproductLineId, so the new product can’t be pre‑scoped to its line. Consider always passingproductLineIdand only appendingcustomerTypewhen present.🔧 Suggested adjustment
- <Link href={productLineId && customerType ? urlString`/projects/${projectId}/payments/products/new?productLineId=${productLineId}&customerType=${customerType}` : urlString`/projects/${projectId}/payments/products/new`}> + <Link + href={ + productLineId + ? (customerType + ? urlString`/projects/${projectId}/payments/products/new?productLineId=${productLineId}&customerType=${customerType}` + : urlString`/projects/${projectId}/payments/products/new?productLineId=${productLineId}`) + : urlString`/projects/${projectId}/payments/products/new` + } + >
♻️ Duplicate comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (1)
777-785: Product line creation doesn't persist to config.The
onCreatehandler only updates local state but doesn't save the new product line to the project config. When the user subsequently saves the product with this newly created product line ID, schema validation will fail because the product line doesn't exist inpayments.productLines.The comment on line 781 acknowledges this gap. Consider adding an
onCreateProductLinecallback prop (as suggested in past reviews) to persist the product line before setting it locally.
🧹 Nitpick comments (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (1)
677-677: Consider aligning hover transition pattern with the rest of the file.The coding guidelines recommend using
hover:transition-noneto avoid hover-enter transitions. This line hastransition-colorsbut lacks thehover:transition-nonemodifier that other similar elements in this file use.🔧 Suggested fix
- className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors" + className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors hover:transition-none"apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (2)
2041-2065: Wrap product‑line async actions withrunAsynchronouslyWithAlert.
New create/edit/delete handlers are async; using the standard helper keeps error handling consistent and surfaces failures to users. Apply the same pattern to the edit and delete actions as well.♻️ Example update (apply similarly to edit/delete)
- onClick={async () => { + onClick={() => runAsynchronouslyWithAlert(async () => { const id = newProductLineId.trim(); const displayName = newProductLineDisplayName.trim(); if (!id || !displayName) return; if (!isValidUserSpecifiedId(id)) { alert(getUserSpecifiedIdErrorMessage("productLineId")); return; } if (Object.prototype.hasOwnProperty.call(groups, id)) { alert("Product line ID already exists"); return; } await onCreateProductLine(id, displayName, newProductLineCustomerType); setNewProductLineDisplayName(""); setNewProductLineId(""); setHasManuallyEditedProductLineId(false); toast({ title: "Product line created" }); - }} + })}Based on learnings, ...
Also applies to: 2097-2103, 2119-2126
2166-2171: Avoid non‑null assertion when pushing to the group map.
Prefer an explicit guard or?? throwErr(...)to make the assumption explicit and safer.🔧 Safer variant
- groups.get(productLineId)!.push({ id, product }); + const group = groups.get(productLineId) ?? throwErr(`Missing group for productLineId: ${String(productLineId)}`); + group.push({ id, product });As per coding guidelines, ...
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧠 Learnings (5)
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to **/*.{ts,tsx} : Code defensively using `?? throwErr(...)` instead of non-null assertions, with good error messages explicitly stating violated assumptions
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx
📚 Learning: 2025-12-03T07:19:44.433Z
Learnt from: madster456
Repo: stack-auth/stack-auth PR: 1040
File: packages/stack-shared/src/interface/crud/oauth-providers.ts:62-87
Timestamp: 2025-12-03T07:19:44.433Z
Learning: In packages/stack-shared/src/interface/crud/oauth-providers.ts and similar CRUD files, the tag "Oauth" (not "OAuth") is the correct capitalization format as it's used by the documentation generation system and follows OpenAPI conventions.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : For blocking alerts and errors, never use `toast`, as they are easily missed by the user. Instead, use alerts
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
📚 Learning: 2026-01-13T18:14:29.993Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.993Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. Use `runAsynchronously` or `runAsynchronouslyWithAlert` instead of general try-catch error handling
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (2)
packages/stack-shared/src/utils/objects.tsx (1)
getOrUndefined(545-548)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (14)
- GitHub Check: build (22.x)
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: lint_and_build (latest)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: setup-tests
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: docker
- GitHub Check: check_prisma_migrations (22.x)
🔇 Additional comments (6)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (5)
8-10: LGTM!Import changes correctly support the catalog → product line rename. The
getOrUndefinedutility enables defensive property access.
136-141: LGTM!Validation logic correctly enforces that add-ons target products within the same product line. The Set-based approach handles both specified and unspecified (
undefined) product lines properly.
448-452: LGTM!Correctly resets the product line selection when customer type changes, since product lines are customer-type-specific and the previous selection may no longer be valid.
471-506: LGTM!The product line selector correctly filters by customer type and provides appropriate options for no selection and creating new product lines.
575-579: Previous concern addressed.The use of
getOrUndefinedwith optional chaining correctly guards against missing product line metadata, falling back to the rawproductLineIdwhen the product line doesn't exist. Based on learnings, this is good defensive coding.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
61-67: Clean auto‑ID normalization helper.
Nice, consistent slug formatting for display names.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
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.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
...hboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
Show resolved
Hide resolved
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.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| // If not, treat it as "no product line" - this handles cases where URL params have mismatched types | ||
| const effectiveProductLineId = productLineId && paymentsConfig.productLines[productLineId].customerType === customerType | ||
| ? productLineId | ||
| : ""; |
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.
Missing null safety when accessing product line customerType
Medium Severity
The expression paymentsConfig.productLines[productLineId].customerType accesses .customerType without first checking if the product line still exists. If the product line is deleted (e.g., by another browser tab or concurrent user) while this page is open, the component would crash with a TypeError when it re-renders with the updated config. Optional chaining (paymentsConfig.productLines[productLineId]?.customerType) would handle this edge case gracefully.
| }, | ||
| add_ons: { | ||
| displayName: 'Add-ons', | ||
| customerType: 'team', |
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.
Seed data has mismatched customerType between products and productLines
Medium Severity
The productLines in buildDummyPaymentsSetup() are defined with customerType: 'team', but all products referencing them (starter, growth, regression-addon) have customerType: 'user'. Similarly, the legacy subscription uses productLineId: 'workspace' with customerType: 'user', and the one-time purchase uses productLineId: 'add_ons' with customerType: 'custom'. The UI enforces that products can only belong to product lines with matching customerType, so this creates an invalid state that wouldn't occur through normal operations and could cause products to not appear under their product lines.
Additional Locations (2)
|
📝 Documentation updates detected! Updated existing suggestion: Document subscription plan switching feature |
Note
Refactors payments to replace "catalogs" with "product lines" end-to-end, standardizing on
productLinesandproductLineId.catalogs→productLinesandcatalogId→productLineIdacross seeds, APIs (/products, switch, purchase-session, validate-code), core logic (getSubscriptions,validatePurchaseSession, grants), and error messages; update tests accordinglycatalogsview; addproduct-linespages/components and dialogs; update products UI, creation flows, filtering/grouping, and deep links to use product linescustomerType; UI enforces matching types; update defensive coding guidance and URL handling (urlString)pnpm lint --fixin stop hook; minor README tip adjustmentsWritten by Cursor Bugbot for commit 5406496. This will update automatically on new commits. Configure here.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.