Skip to content

Payments app design fixes#1375

Open
Developing-Gamer wants to merge 50 commits into
devfrom
Payments-app-design-fixes
Open

Payments app design fixes#1375
Developing-Gamer wants to merge 50 commits into
devfrom
Payments-app-design-fixes

Conversation

@Developing-Gamer
Copy link
Copy Markdown
Contributor

@Developing-Gamer Developing-Gamer commented Apr 23, 2026

Summary by CodeRabbit

  • New Features

    • Introduced DesignDialog component for standardized modal/dialog interfaces across the dashboard.
    • Added "Roids" skill configuration documentation.
  • Documentation

    • Updated design guide with DesignDialog specifications and usage patterns.
    • Added dialog examples and props reference to design-language documentation.
  • Refactor

    • Redesigned auth methods, payments, and sign-up rules pages with design-system components.
    • Migrated dialog implementations across product, pricing, and item dialogs to use DesignDialog.
    • Simplified pricing logic by removing legacy "include-by-default" handling.
  • Style

    • Updated button, dropdown, and popover styling for consistency.
    • Enhanced layout and spacing across payment-related interfaces.

Review Change Stack

Introduce a canonical dialog surface with structured header/body/footer slots and styling controls so feature pages can reuse a consistent modal foundation.

Made-with: Cursor
Expose the new dialog component and related helper types from the package root so dashboard pages can import the shared modal API consistently.

Made-with: Cursor
Add guidance for when and how to use DesignDialog so modal redesign work follows a single documented pattern across dashboard routes.

Made-with: Cursor
Replace the hand-wired trigger history dialog chrome with the shared DesignDialog wrapper while preserving the existing summary header and trigger list behavior.

Made-with: Cursor
Show confirmation, rich-header, tester, and parity examples so agents and developers can copy the shared modal patterns directly.

Made-with: Cursor
Introduce a dedicated dialog playground entry with shape presets and generated snippets to make modal experimentation and reuse easier.

Made-with: Cursor
Ensures popover content receives pointer events consistently in
stack-ui and the dashboard shadcn wrapper.

Made-with: Cursor
Split hover, focus, and data-[highlighted] so keyboard navigation
matches pointer hover for menu and select items.

Made-with: Cursor
Payments product flows use the updated repeating list behavior and styling.

Made-with: Cursor
Aligns the payments item dialog with the new form and popover patterns.

Made-with: Cursor
Applies the updated UI patterns to the product line creation flow.

Made-with: Cursor
Refactors included item editing to match the new dialog and input UX.

Made-with: Cursor
Aligns price editing with the shared dialog and form components.

Made-with: Cursor
Wires the new product flow to the refactored dialogs and pricing UI.

Made-with: Cursor
Replace generic Card layout with DesignCard, shared StatusRow, and
DesignBadge/DesignButton for consistent status styling across not
connected, incomplete setup, and connected states.

Made-with: Cursor
Use DesignCard with gradient by state and DesignBadge for the test-mode
feature list when active, matching the updated payments settings visuals.

Made-with: Cursor
Swap Card for DesignCard, move save/cancel into card actions, refine
rows and accordions, and add a Modified badge for pending toggles on
controllable and platform-managed sections.

Made-with: Cursor
Replace SettingSwitch with a DesignCard, optimistic Switch updates, and
tighter section spacing to match the rest of the payments settings page.

Made-with: Cursor
@Developing-Gamer Developing-Gamer self-assigned this Apr 23, 2026
Copilot AI review requested due to automatic review settings April 23, 2026 04:06
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-auth-mcp Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-auth-skills Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-backend Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-dashboard Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-demo Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-docs Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-preview-backend Ready Ready Preview, Comment May 19, 2026 5:17pm
stack-preview-dashboard Ready Ready Preview, Comment May 19, 2026 5:17pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces DesignDialog as the standardized modal component across the dashboard while simultaneously removing the legacy "include-by-default" pricing sentinel and standardizing to explicit prices objects. It includes widespread migration of dialog-based UIs to the new design system and refactoring of payment and sign-up rule pages with design components.

Changes

DesignDialog Component Foundation & Documentation

Layer / File(s) Summary
DesignDialog Component Implementation
packages/dashboard-ui-components/src/components/dialog.tsx
New DesignDialog client component wrapping Radix primitives with class maps for sizing/variants, conditional header/body/footer rendering, overlay control, and styled composition layers.
Dialog Exports & Barrel Files
packages/dashboard-ui-components/src/index.ts, apps/dashboard/src/components/design-components/index.ts
Exports DesignDialog, related types (DesignDialogSize, DesignDialogVariant, DesignDialogProps), and primitive aliases through barrel files.
Design Guide & Documentation
apps/dashboard/DESIGN-GUIDE.md
Documents when to use DesignDialog for focus-trapping modals, specifies props, lists re-exported aliases, and provides canonical patterns for confirmations and rich layouts.

Remove Legacy Include-by-Default Pricing Sentinel

Layer / File(s) Summary
Backend Pricing Logic & Validation
apps/backend/prisma/seed.ts, apps/backend/src/app/api/latest/payments/products/.../switch/route.ts
Removes "include-by-default" sentinel handling; derives firstPriceId from explicit prices, and updates switch endpoint to determine free-plan status via currency-aware logic instead of sentinel.
Create Product Page Pricing Refactor
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/products/new/page-client.tsx
Removes include-by-default from form initialization, validation, and serialization; standardizes to explicit prices object and migrates header/field UI to design components.
Edit Product Page Pricing Refactor
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/products/[productId]/edit/page-client.tsx
Eliminates include-by-default parsing and state; shows destructive alert for products with empty prices from legacy data; updates pricing section controls.
Product List & Pricing Display Updates
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/products/page-client-list-view.tsx
Adds ProductsWithoutPricesAlert for legacy data visibility; removes legacy price format handling from display and sorting logic.
Test Updates for Pricing Sentinel Removal
apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts
Updates test to use explicit pricing instead of sentinel; replaces legacy test with admin config-override test asserting sentinel rejection.

Dialog Migrations in Payments UI

Layer / File(s) Summary
Create Product Line Dialog Migration
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/products/create-product-line-dialog.tsx
Migrates from shadcn Dialog primitives to DesignDialog, updates close behavior, switches inputs to DesignInput, changes async helper to runAsynchronouslyWithAlert.
Included Item Dialog Migration
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/products/included-item-dialog.tsx
Migrates to DesignDialog, replaces Shadcn selects with DesignSelectorDropdown using memoized options, updates footer to use DesignDialogClose.
Price Edit Dialog Migration
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/products/price-edit-dialog.tsx
Migrates to DesignDialog with footer prop handling; refactors Free Trial editor from EditableGrid to Popover with DesignInput; updates Server Only to DesignSelectorDropdown.
Pricing Section Component Updates
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/products/pricing-section.tsx
Replaces Button/Checkbox with DesignButton; removes free-by-default props; simplifies free product UI to single destructive clear-prices button.

Dialog Migrations Across Dashboard Pages

Layer / File(s) Summary
Playground Dialog Demo Component
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx
Adds DesignDialog as selectable playground component with comprehensive shape/size/variant/styling controls, preview rendering, and code generation.
Design Language Page Dialog Examples
apps/dashboard/src/app/(main)/(protected)/projects/.../design-language/page-client.tsx
Adds comprehensive DesignDialog examples demonstrating confirmation, rich header, wide tester, and region-specific styling patterns with props reference.
Sign-up Rules Page Refactor with Design Components
apps/dashboard/src/app/(main)/(protected)/projects/.../sign-up-rules/page-client.tsx
Large refactor reorganizing page into helper components, migrating all UI to design components, extracting layout logic into PageBody, adding trigger history, rule editor state hook, and menu-based actions.

Payment Pages Design System Updates

Layer / File(s) Summary
Payment Methods Settings UI Migration
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/settings/payment-methods.tsx
Migrates from Card/Button/Typography to DesignCard/DesignButton/DesignBadge with updated accordion styling and redesigned action controls.
Stripe Connection Status Component
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/settings/stripe-connection-check.tsx
Refactors with StatusRow layout component, DesignCard/DesignButton UI, and badge-based capability display.
Checkout Controls (Block New Purchases) Toggle
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/settings/page-client.tsx
Replaces toggle with DesignCard using optimistic updates with request-id guarding to prevent stale responses.
Test Mode Toggle Redesign
apps/dashboard/src/app/(main)/(protected)/projects/.../payments/settings/test-mode-toggle.tsx
Refactors from Card primitives to DesignCard with dynamic gradient/icons and replaces styled badges with DesignBadge.

Base UI Primitives & Minor Updates

Layer / File(s) Summary
Payment Item Dialog Migration
apps/dashboard/src/components/payments/item-dialog.tsx
Migrates to DesignDialog; centralizes CUSTOMER_TYPE_OPTIONS; updates form fields to design components.
Shadcn Primitive Style Updates
apps/dashboard/src/components/ui/action-dialog.tsx, apps/dashboard/src/components/ui/dropdown-menu.tsx, apps/dashboard/src/components/ui/select.tsx, packages/stack-ui/src/components/ui/popover.tsx, apps/dashboard/src/components/ui/popover.tsx
ActionDialog gains keepOpenOnOutsideInteraction and contentClassName props; DropdownMenuItem and SelectItem get explicit hover/highlighted state styling; popovers gain pointer-events-auto.
Minor Styling & Layout Adjustments
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx, apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/layout.tsx, apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsx
Button border-radius addition, payments layout container flexbox update, product-lines slideshow centering adjustment.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • hexclave/stack-auth#1350: Removes legacy "include-by-default" pricing model, refactors switch endpoint and product validation logic similar to this PR's backend pricing changes.

Suggested Reviewers

  • BilalG1
  • N2D4

🐰 Dialog dreams refined, prices now defined,
No more defaults left behind,
Components cascade through dashboards wide,
Design system's pride!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Payments-app-design-fixes

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR migrates dashboard payment and auth pages to a new design-component system (DesignButton, DesignInput, DesignDialog, etc.) and introduces a full Conversations/Support feature including a DB schema, backend API routes, and a dashboard UI. It also refactors the payments plan-switch logic to correctly handle free non-recurring plans.

  • Design system migration: Payment settings, product creation/editing dialogs, sign-up rules, and auth-methods pages are updated to use glassmorphic design components (DesignDialog, DesignButton, DesignInput, DesignSelectorDropdown), alongside new ActionDialog props (keepOpenOnOutsideInteraction, contentClassName).
  • Conversations feature: Adds Conversation, ConversationEntryPoint, and ConversationMessage database tables with a new backend library, internal dashboard routes, and user-facing dogfood routes, all using createSmartRouteHandler per the existing convention.
  • Payments switch logic fix: isPriceFree/fromIsFreePlan is now computed before the "one-time purchase" guard so $0 non-recurring plans are correctly exempted from the "cannot switch" error.

Confidence Score: 5/5

Safe to merge; the logic changes are well-scoped and the new conversations feature follows established patterns.

The payments switch refactor correctly moves the free-plan check earlier without altering downstream authorization. All new conversation routes use createSmartRouteHandler and properly scope reads through viewerProjectUserId. The design migration is additive. The only finding is a redundant direct-source-path re-export that is currently harmless but could diverge in a production build.

apps/dashboard/src/components/design-components/index.ts — redundant cross-package source import that duplicates what the package index already exports.

Important Files Changed

Filename Overview
apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts isPriceFree/fromIsFreePlan moved earlier so free $0 non-recurring plans correctly bypass the one-time-purchase guard; both authorization checks are preserved unchanged.
apps/backend/prisma/migrations/20260420000000_add_conversations/migration.sql Adds Conversation, ConversationEntryPoint, and ConversationMessage tables with CHECK constraints, cascade FK relationships, and appropriate composite indexes.
apps/backend/src/lib/conversations.tsx New 1016-line conversations library; viewerProjectUserId correctly gates conversation reads. UPDATE statements after getConversationState rely on the preceding ownership check without re-applying the userId filter in the write — acceptable since conversation ownership is immutable.
apps/dashboard/src/components/design-components/index.ts Re-exports DesignDialog both via export * from "@stackframe/dashboard-ui-components" (which now includes it) and via an explicit cross-package relative path import — the named export shadows the wildcard, but the redundant direct source path is fragile.
packages/dashboard-ui-components/src/components/dialog.tsx New DesignDialog component with glassmorphic/plain variants, size map, and composable header/body/footer regions; conditional rendering guards prevent empty DialogBody from rendering padding.
apps/backend/src/lib/conversations-api.ts Shared auth schemas, pagination helpers, and public (snake_case) response schemas for conversation API routes; toPublicConversationDetail correctly maps camelCase internals to the public API shape.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/price-edit-dialog.tsx Migrated to DesignDialog/DesignButton; save button correctly wraps onSave in runAsynchronouslyWithAlert.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/page-client.tsx Large redesign of auth-methods page; async toggle handlers use runAsynchronouslyWithAlert correctly; InlineSaveDiscard wraps save callbacks.

Sequence Diagram

sequenceDiagram
    participant U as End User
    participant DU as Dashboard UI (dogfood routes)
    participant DI as Dashboard Internal (admin routes)
    participant CL as conversations.tsx lib
    participant DB as PostgreSQL

    Note over DU,DB: User-facing conversation flow
    U->>DU: POST /dogfood/support/conversations (subject, message)
    DU->>CL: createConversation(tenancyId, userId, ...)
    CL->>DB: INSERT Conversation + ConversationEntryPoint + ConversationMessage
    DU-->>U: "{ conversation_id }"

    U->>DU: PATCH /dogfood/support/conversations/:id (message)
    DU->>CL: "appendConversationMessage(viewerProjectUserId=userId, ...)"
    CL->>DB: "SELECT Conversation WHERE projectUserId=viewerProjectUserId (auth gate)"
    CL->>DB: INSERT ConversationMessage + UPDATE Conversation timestamps
    DU-->>U: updated conversation detail

    Note over DI,DB: Admin / support-agent flow
    DI->>CL: listConversationSummaries(tenancyId, status?, userId?)
    CL->>DB: SELECT with LEFT JOIN ProjectUser for display name/email
    DI-->>DI: agent replies / status changes / metadata updates
    DI->>CL: updateConversationStatus / appendConversationMessage (agent sender)
    CL->>DB: INSERT message + UPDATE Conversation (SLA timestamps, status)
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/dashboard/src/components/design-components/index.ts:1-14
`DesignDialog` and its siblings are now exported from `packages/dashboard-ui-components/src/index.ts` (added in this PR), so `export * from "@stackframe/dashboard-ui-components"` already surfaces them. The explicit re-export via a 5-level relative path to the package source is redundant: if the package ever resolves to its compiled `dist/` output in a production build, this direct source import would produce a second, distinct module instance — breaking React component identity comparisons (`===`) for anything wrapping or memoising these components.

```suggestion
export * from "@stackframe/dashboard-ui-components";
```

Reviews (2): Last reviewed commit: "Enhance PageClient with customizable dia..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

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/components/payments/product-dialog.tsx (1)

70-75: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Show duplicate-ID failures inline instead of via toast.

This blocks submit, but the only feedback is a destructive toast that's easy to miss and isn't attached to productId. Prefer a field error or inline Alert in the dialog so the failure is self-explanatory. As per coding guidelines "Never use toast for blocking alerts and errors; use alerts instead as toasts are easily missed by users".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard/src/components/payments/product-dialog.tsx` around lines 70 -
75, The submit handler inside onSubmit (the branch where mode === "create")
currently uses toast to surface a duplicate productId; replace that with an
inline error tied to the productId field or render an inline Alert in the
ProductDialog so the failure is clearly attached to the input. Specifically,
after awaiting project.getConfig() and checking has(config.payments.products,
values.productId), set a field error on productId (or toggle a local state flag
used to render an Alert component inside the dialog) instead of calling toast({
... }); keep returning "prevent-close-and-prevent-reset" so the dialog stays
open and focus can remain on the productId field. Ensure the change updates
wherever form errors are consumed (the form state or Alert render) so the
message is shown adjacent to values.productId.
🧹 Nitpick comments (2)
examples/demo/src/app/payments-demo/api/create-checkout-url/route.ts (1)

27-32: ⚡ Quick win

Consider validating JSON parsing with explicit error handling.

If the request body contains malformed JSON, request.json() will throw and Next.js will return a generic 500 error. For consistency with the explicit error handling for authentication (401) and authorization (403), consider also catching JSON parse errors and returning a 400.

📋 Proposed defensive error handling
   const user = await stackServerApp.getUser();
   if (user == null) {
     return NextResponse.json({ error: "Sign in before creating a checkout URL." }, { status: 401 });
   }
-  const body = readBody(await request.json());
+  let rawBody: unknown;
+  try {
+    rawBody = await request.json();
+  } catch {
+    return NextResponse.json({ error: "Invalid JSON in request body." }, { status: 400 });
+  }
+  const body = readBody(rawBody);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/demo/src/app/payments-demo/api/create-checkout-url/route.ts` around
lines 27 - 32, The POST handler should guard against malformed JSON from
request.json(); wrap the call in a try/catch inside the POST function (where
readBody(await request.json()) is called), catch parsing errors (e.g.,
SyntaxError) and return a 400 NextResponse.json with a clear message (e.g.,
"Invalid JSON in request body") instead of letting it bubble to a 500; ensure
this error path is placed before any further processing that uses the parsed
body and keep existing authentication checks (stackServerApp.getUser())
unchanged.
examples/demo/src/app/payments-demo/api/send-test-email/route.ts (1)

23-26: ⚡ Quick win

Consider validating JSON parsing with explicit error handling.

Currently, if the request body contains malformed JSON, request.json() will throw and Next.js will return a generic 500 error. For better API consumer experience, consider catching JSON parse errors and returning a 400 with a clear message.

📋 Proposed defensive error handling
 export async function POST(request: Request) {
   const user = await stackServerApp.getUser({ or: "throw" });
-  const body: unknown = await request.json();
+  let body: unknown;
+  try {
+    body = await request.json();
+  } catch {
+    return NextResponse.json({ error: "Invalid JSON in request body." }, { status: 400 });
+  }
   const count = readCount(body);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/demo/src/app/payments-demo/api/send-test-email/route.ts` around
lines 23 - 26, The POST handler currently calls await request.json() without
handling parse failures; wrap the JSON parse in a try/catch inside the exported
async POST function (where you call request.json() and then readCount(body)) and
on parse errors return a 400 response with a clear message (e.g., "Invalid JSON
body") instead of letting the error bubble to a 500; ensure you still call
readCount(body) only after successful parsing and include stackServerApp.getUser
usage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/backend/prisma/seed.ts`:
- Line 329: The line that sets firstPriceId using "const firstPriceId =
Object.keys(growthProduct.prices)[0] ?? null;" can silently produce a null
priceId and seed invalid billing state; change this to fail fast by throwing a
clear invariant error when no price exists (e.g., replace the null-coalescing
with an explicit throw such as using a helper like throwErr(...) or "throw new
Error(...)" ), referencing growthProduct and firstPriceId so the seed will stop
with a descriptive message if growthProduct.prices has no entries.

In
`@apps/backend/src/app/api/latest/payments/products/`[customer_type]/[customer_id]/switch/route.ts:
- Around line 80-87: The current guard rejects any product with only
non-interval prices as one-time purchases even if those prices are all $0;
compute or reuse the free-plan check before throwing and only reject when the
product is non-recurring AND not a free plan. Concretely, determine
fromIsFreePlan (or evaluate whether all prices are zero) prior to this guard and
change the condition from "if (fromPriceEntries.length > 0 &&
!fromHasIntervalPrice) throw ..." to only throw when the product is
non-recurring and not free (e.g., if (fromPriceEntries.length > 0 &&
!fromHasIntervalPrice && !fromIsFreePlan) throw ...). Apply the same fix to the
analogous block that was flagged (the 141-149 equivalent).

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/[productId]/edit/page-client.tsx:
- Around line 383-385: When handling the "Make free" action in onMakeFree (which
currently calls setPrices(createFreePrice())), also clear any stale validation
on prices: after setPrices(...) call setErrors(prev => ({ ...prev, prices:
undefined })) or remove the prices key so the old errors.prices message is
cleared; update the onMakeFree handler to call setErrors (or the form error
setter you use) immediately after setPrices to ensure the UI no longer shows the
stale error.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx:
- Around line 638-639: Replace the interpolated href string with urlString so
the link uses the repository's URL-construction utility: locate the JSX element
that currently sets href={`/projects/${projectId}/payments/products/${id}/edit`}
(in the page-client-list-view component) and change it to use urlString with the
same path and variables (projectId and id) so the href is constructed via
urlString(...) or the tagged template form, preserving the existing className
and behavior.

In `@apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts`:
- Around line 79-88: The product-line fixture is invalid because the product
"legacyDefault" references productLineId "catalog" as a user product but the
"catalog" product line lacks customerType, which can cause validation to fail
before the test exercises the deprecated-price sentinel; update the productLines
entry named "catalog" to include customerType: "user" so the fixture is
consistent (keep targeting the symbols productLines -> catalog and products ->
legacyDefault to locate the change).

In `@examples/demo/src/app/payments-demo/page.tsx`:
- Around line 147-159: The onClick handler calls createCheckoutUrl but lacks
error handling; wrap the async work with runAsynchronouslyWithAlert so
exceptions show an alert instead of causing unhandled rejections. Replace the
inline async handler with runAsynchronouslyWithAlert(async () => {
setLoading(true); try { const checkoutUrl = await createCheckoutUrl({ teamId:
props.team.id, productId: props.productId, returnUrl: window.location.href });
window.location.assign(checkoutUrl); } finally { setLoading(false); } });
ensuring you reference createCheckoutUrl, setLoading, and window.location.assign
inside the wrapped function (or remove manual loading if the Button already
manages it).

---

Outside diff comments:
In `@apps/dashboard/src/components/payments/product-dialog.tsx`:
- Around line 70-75: The submit handler inside onSubmit (the branch where mode
=== "create") currently uses toast to surface a duplicate productId; replace
that with an inline error tied to the productId field or render an inline Alert
in the ProductDialog so the failure is clearly attached to the input.
Specifically, after awaiting project.getConfig() and checking
has(config.payments.products, values.productId), set a field error on productId
(or toggle a local state flag used to render an Alert component inside the
dialog) instead of calling toast({ ... }); keep returning
"prevent-close-and-prevent-reset" so the dialog stays open and focus can remain
on the productId field. Ensure the change updates wherever form errors are
consumed (the form state or Alert render) so the message is shown adjacent to
values.productId.

---

Nitpick comments:
In `@examples/demo/src/app/payments-demo/api/create-checkout-url/route.ts`:
- Around line 27-32: The POST handler should guard against malformed JSON from
request.json(); wrap the call in a try/catch inside the POST function (where
readBody(await request.json()) is called), catch parsing errors (e.g.,
SyntaxError) and return a 400 NextResponse.json with a clear message (e.g.,
"Invalid JSON in request body") instead of letting it bubble to a 500; ensure
this error path is placed before any further processing that uses the parsed
body and keep existing authentication checks (stackServerApp.getUser())
unchanged.

In `@examples/demo/src/app/payments-demo/api/send-test-email/route.ts`:
- Around line 23-26: The POST handler currently calls await request.json()
without handling parse failures; wrap the JSON parse in a try/catch inside the
exported async POST function (where you call request.json() and then
readCount(body)) and on parse errors return a 400 response with a clear message
(e.g., "Invalid JSON body") instead of letting the error bubble to a 500; ensure
you still call readCount(body) only after successful parsing and include
stackServerApp.getUser usage unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be443cda-44a0-4e5f-99e5-faa7ad40a501

📥 Commits

Reviewing files that changed from the base of the PR and between f4de807 and 7759dc3.

📒 Files selected for processing (39)
  • apps/backend/prisma/migrations/20260421000000_drop_include_by_default_snapshots/migration.sql
  • apps/backend/prisma/migrations/20260421000000_drop_include_by_default_snapshots/tests/rewrite-snapshots.ts
  • apps/backend/prisma/seed.ts
  • apps/backend/scripts/verify-data-integrity/payments-verifier.ts
  • apps/backend/src/app/api/latest/internal/config/override/[level]/route.tsx
  • apps/backend/src/app/api/latest/internal/payments/transactions/route.tsx
  • apps/backend/src/app/api/latest/internal/payments/transactions/transaction-builder.ts
  • apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.ts
  • apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts
  • apps/backend/src/app/api/latest/payments/purchases/validate-code/route.ts
  • apps/backend/src/lib/payments.tsx
  • apps/backend/src/lib/payments/ensure-free-plan.ts
  • apps/backend/src/lib/payments/schema/__tests__/dual-write.test.ts
  • apps/backend/src/lib/payments/schema/types.ts
  • apps/backend/src/lib/seed-dummy-data.ts
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/edit/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-card-preview.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-price-row.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts
  • apps/dashboard/src/components/data-table/transaction-table.tsx
  • apps/dashboard/src/components/payments/product-dialog.tsx
  • apps/e2e/tests/backend/endpoints/api/v1/payments/block-new-purchases.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts
  • docs-mintlify/guides/apps/payments/overview.mdx
  • docs-mintlify/snippets/payments-concepts.jsx
  • examples/demo/src/app/payments-demo/api/config-check/route.ts
  • examples/demo/src/app/payments-demo/api/create-checkout-url/route.ts
  • examples/demo/src/app/payments-demo/api/send-test-email/route.ts
  • examples/demo/src/app/payments-demo/page.tsx
  • examples/demo/src/components/header.tsx
  • packages/stack-shared/src/config/schema-fuzzer.test.ts
  • packages/stack-shared/src/config/schema.ts
  • packages/stack-shared/src/schema-fields.ts
💤 Files with no reviewable changes (4)
  • docs-mintlify/guides/apps/payments/overview.mdx
  • apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.ts
  • apps/backend/src/lib/seed-dummy-data.ts
  • apps/backend/scripts/verify-data-integrity/payments-verifier.ts
✅ Files skipped from review due to trivial changes (3)
  • examples/demo/src/app/payments-demo/api/config-check/route.ts
  • examples/demo/src/components/header.tsx
  • apps/backend/src/app/api/latest/internal/payments/transactions/transaction-builder.ts

Comment thread apps/backend/prisma/seed.ts Outdated
Comment thread examples/demo/src/app/payments-demo/page.tsx Outdated
- Added error handling in the seed function to ensure at least one price is configured for the Growth product before seeding.
- Updated the product switching logic to correctly identify free plans based on all supported currencies, preventing unintended subscription switches.
- Refactored related code for clarity and maintainability, including the removal of redundant checks and improved error messages.
- Enhanced UI components with better accessibility features and loading states for user actions.

This commit improves the robustness of the seeding process and ensures proper handling of product pricing in the subscription management system.
@mantrakp04
Copy link
Copy Markdown
Collaborator

@greptile-ai review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/price-edit-dialog.tsx (1)

207-207: ⚡ Quick win

Specify radix parameter in parseInt for defensive coding.

Using parseInt without an explicit radix can lead to unexpected behavior and is flagged by most linters. Always specify base 10 for decimal number parsing.

🛡️ Proposed fix
-                          onChange={(e) => setPriceFreeTrialCount(Math.max(1, parseInt(e.target.value) || 1))}
+                          onChange={(e) => setPriceFreeTrialCount(Math.max(1, parseInt(e.target.value, 10) || 1))}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/price-edit-dialog.tsx
at line 207, The onChange handler uses parseInt without a radix which can
produce incorrect parsing; update the parseInt call in the onChange for
setPriceFreeTrialCount to include a radix of 10 (e.g., parseInt(e.target.value,
10)) and keep the existing Math.max(1, ... || 1) logic unchanged so invalid or
empty input still defaults to 1.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx (1)

676-686: ⚡ Quick win

Replace as casts in onValueChange with type guards.

Both ActionDropdown (Line 680, v as ActionType) and DefaultActionRow (Line 943, v as 'allow' | 'reject') bypass the type system by asserting a string is a narrower literal union. If the upstream DesignSelectorDropdown/DesignMenu option lists ever drift, these casts silently propagate invalid values into state.

The same file already uses the recommended pattern for the test-rules dropdowns (e.g., Line 1127-1131 for auth_method, Line 1187-1191 for turnstileResultOverride) — please mirror that approach here for consistency.

As per coding guidelines: "Do NOT use as/any/type casts or anything else like that to bypass the type system unless you specifically asked the user about it."

♻️ Proposed fix
 function ActionDropdown({ state, size = "sm", className }: { state: RuleEditorState, size?: "sm" | "md" | "lg", className?: string }) {
   return (
     <DesignSelectorDropdown
       value={state.actionType}
-      onValueChange={(v) => state.setActionType(v as ActionType)}
+      onValueChange={(v) => {
+        if (v === 'allow' || v === 'reject' || v === 'restrict' || v === 'log') {
+          state.setActionType(v);
+        }
+      }}
       size={size}
       className={className ?? "w-40"}
       options={ACTION_DROPDOWN_OPTIONS}
     />
   );
 }
       <DesignMenu
         variant="selector"
         trigger="button"
         triggerLabel={value === 'allow' ? 'Allow' : 'Reject'}
         value={value}
-        onValueChange={(v) => onChange(v as 'allow' | 'reject')}
+        onValueChange={(v) => {
+          if (v === 'allow' || v === 'reject') onChange(v);
+        }}
         options={[
           { id: "allow", label: "Allow" },
           { id: "reject", label: "Reject" },
         ]}
       />

Also applies to: 938-948

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx
around lines 676 - 686, Replace the unsafe "as" casts in ActionDropdown and
DefaultActionRow by validating the incoming value with a type guard before
calling state setters: in ActionDropdown (function ActionDropdown) check that
the clicked value exists in the ActionType set/allowed strings (derived from
ACTION_DROPDOWN_OPTIONS or ActionType enum) and only then call
state.setActionType(value); similarly in DefaultActionRow validate the value is
either "allow" or "reject" before calling the setter. Mirror the pattern used by
the test-rules dropdowns (the explicit string-check guard used for auth_method
and turnstileResultOverride) so invalid upstream option strings are ignored or
handled instead of being forced via "as".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/price-edit-dialog.tsx:
- Line 207: The onChange handler uses parseInt without a radix which can produce
incorrect parsing; update the parseInt call in the onChange for
setPriceFreeTrialCount to include a radix of 10 (e.g., parseInt(e.target.value,
10)) and keep the existing Math.max(1, ... || 1) logic unchanged so invalid or
empty input still defaults to 1.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx:
- Around line 676-686: Replace the unsafe "as" casts in ActionDropdown and
DefaultActionRow by validating the incoming value with a type guard before
calling state setters: in ActionDropdown (function ActionDropdown) check that
the clicked value exists in the ActionType set/allowed strings (derived from
ACTION_DROPDOWN_OPTIONS or ActionType enum) and only then call
state.setActionType(value); similarly in DefaultActionRow validate the value is
either "allow" or "reject" before calling the setter. Mirror the pattern used by
the test-rules dropdowns (the explicit string-check guard used for auth_method
and turnstileResultOverride) so invalid upstream option strings are ignored or
handled instead of being forced via "as".

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07cadb97-4abd-4a16-9896-1793d9f33574

📥 Commits

Reviewing files that changed from the base of the PR and between 7759dc3 and cdc26f8.

📒 Files selected for processing (17)
  • apps/backend/prisma/seed.ts
  • apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts
  • apps/dashboard/DESIGN-GUIDE.md
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/design-language/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/edit/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/included-item-dialog.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/price-edit-dialog.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/settings/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/settings/stripe-connection-check.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx
  • apps/dashboard/src/components/payments/item-dialog.tsx
  • apps/dashboard/src/components/ui/action-dialog.tsx
  • apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts
  • examples/demo/src/app/payments-demo/page.tsx
  • packages/dashboard-ui-components/src/components/dialog.tsx
🚧 Files skipped from review as they are similar to previous changes (13)
  • apps/backend/prisma/seed.ts
  • examples/demo/src/app/payments-demo/page.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/settings/page-client.tsx
  • apps/dashboard/src/components/payments/item-dialog.tsx
  • apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/settings/stripe-connection-check.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/design-language/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsx
  • packages/dashboard-ui-components/src/components/dialog.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/included-item-dialog.tsx
  • apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/edit/page-client.tsx

Comment on lines +152 to +154
{ value: 'day', label: 'day(s)' },
{ value: 'week', label: 'week(s)' },
{ value: 'month', label: 'month(s)' },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Async save handler used directly in onClick

validateAndSave is an async function assigned straight to onClick on the DesignButton. If onSave throws (e.g., a network error or the parent intentionally rejects to keep the dialog open), the rejection is silently dropped as an unhandled promise. Per team convention, async button handlers must be wrapped with runAsynchronouslyWithAlert so errors are surfaced to users. runAsynchronouslyWithAlert is already imported in this file.

Rule Used: Use runAsynchronouslyWithAlert from `@stackframe... (source)

Learned From
stack-auth/stack-auth#943

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/included-item-dialog.tsx
Line: 152-154

Comment:
**Async save handler used directly in `onClick`**

`validateAndSave` is an `async` function assigned straight to `onClick` on the DesignButton. If `onSave` throws (e.g., a network error or the parent intentionally rejects to keep the dialog open), the rejection is silently dropped as an unhandled promise. Per team convention, async button handlers must be wrapped with `runAsynchronouslyWithAlert` so errors are surfaced to users. `runAsynchronouslyWithAlert` is already imported in this file.

**Rule Used:** Use `runAsynchronouslyWithAlert` from `@stackframe... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))

**Learned From**
[stack-auth/stack-auth#943](https://github.com/stack-auth/stack-auth/pull/943)

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +115 to +117
className={errors.displayName ? "border-destructive focus-visible:ring-destructive/30" : ""}
/>
{errors.displayName && (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Async validateAndCreate not wrapped with runAsynchronouslyWithAlert

validateAndCreate is an async function passed directly to onClick. If it rejects, the error is silently swallowed as an unhandled promise rejection and the user gets no feedback. The file already imports runAsynchronously from @stackframe/stack-shared/dist/utils/promises; it should instead import and use runAsynchronouslyWithAlert so errors are shown to the user.

Rule Used: Use runAsynchronouslyWithAlert from `@stackframe... (source)

Learned From
stack-auth/stack-auth#943

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx
Line: 115-117

Comment:
**Async `validateAndCreate` not wrapped with `runAsynchronouslyWithAlert`**

`validateAndCreate` is an `async` function passed directly to `onClick`. If it rejects, the error is silently swallowed as an unhandled promise rejection and the user gets no feedback. The file already imports `runAsynchronously` from `@stackframe/stack-shared/dist/utils/promises`; it should instead import and use `runAsynchronouslyWithAlert` so errors are shown to the user.

**Rule Used:** Use `runAsynchronouslyWithAlert` from `@stackframe... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))

**Learned From**
[stack-auth/stack-auth#943](https://github.com/stack-auth/stack-auth/pull/943)

How can I resolve this? If you propose a fix, please make it concise.

@mantrakp04 mantrakp04 changed the base branch from dev to remove-default-prod-support May 14, 2026 00:19
- Added state management for custom header, body padding, and accent class names in the PageClient component.
- Implemented a custom header rendering option that replaces the default title/description region.
- Introduced toggle options for no body padding and accent class names to enhance UI flexibility.
- Updated the DesignDialog component to accept new props for custom header, body padding, and class names, improving overall dialog customization.

These changes enhance the user experience by providing more control over the dialog's appearance and layout.
Copy link
Copy Markdown

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

The APP_ICONS object is missing the 'session-replays' entry which is required by the AppId type, causing TypeScript compilation to fail

Fix on Vercel

- Replaced the existing icon imports in the app-card component to include PlayCircle for the "session-replays" app.
- This change enhances the visual representation of the app-card by providing a more relevant icon for session replay functionality.

These updates improve the clarity and usability of the app-card component in the documentation.
- Added a rounded-xl class to the Button in the PageClient component for improved styling.
- Updated the layout in PaymentsLayoutInner to enhance responsiveness and visual alignment.
- Modified the onboarding slideshow layout to ensure proper centering and flexibility in the display.

These changes improve the overall user experience by refining the visual presentation and responsiveness of key components.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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/create-product-line-dialog.tsx (1)

55-63: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Close/reset should happen only after successful create.

Line 55 starts async creation, but Lines 57-63 immediately clear state and close the dialog. If creation fails, the user loses input and must re-enter everything.

Proposed fix
-    runAsynchronouslyWithAlert(onCreate({ id: productLineId.trim(), displayName: displayName.trim() }));
-
-    // Reset form
-    setDisplayName("");
-    setProductLineId("");
-    setHasManuallyEditedId(false);
-    setErrors({});
-    onOpenChange(false);
+    runAsynchronouslyWithAlert(
+      Promise.resolve()
+        .then(() => onCreate({ id: productLineId.trim(), displayName: displayName.trim() }))
+        .then(() => {
+          setDisplayName("");
+          setProductLineId("");
+          setHasManuallyEditedId(false);
+          setErrors({});
+          onOpenChange(false);
+        }),
+    );

As per coding guidelines, “always carefully deal with loading and error states; be very explicit with these; ensure errors are NEVER just silently swallowed.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx
around lines 55 - 63, The code currently calls
runAsynchronouslyWithAlert(onCreate(...)) and immediately clears the form and
closes the dialog (setDisplayName, setProductLineId, setHasManuallyEditedId,
setErrors, onOpenChange), which loses user input if creation fails; change the
flow so you wait for the async create to succeed before resetting/closing:
invoke and await runAsynchronouslyWithAlert(onCreate({ id: productLineId.trim(),
displayName: displayName.trim() })) (or use its returned promise .then), and
only on successful resolution perform the resets (setDisplayName,
setProductLineId, setHasManuallyEditedId, setErrors) and call
onOpenChange(false); ensure errors from onCreate are propagated/handled by
runAsynchronouslyWithAlert so you do not clear state on failure and consider
setting/clearing an explicit loading state around the call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx:
- Around line 55-63: The code currently calls
runAsynchronouslyWithAlert(onCreate(...)) and immediately clears the form and
closes the dialog (setDisplayName, setProductLineId, setHasManuallyEditedId,
setErrors, onOpenChange), which loses user input if creation fails; change the
flow so you wait for the async create to succeed before resetting/closing:
invoke and await runAsynchronouslyWithAlert(onCreate({ id: productLineId.trim(),
displayName: displayName.trim() })) (or use its returned promise .then), and
only on successful resolution perform the resets (setDisplayName,
setProductLineId, setHasManuallyEditedId, setErrors) and call
onOpenChange(false); ensure errors from onCreate are propagated/handled by
runAsynchronouslyWithAlert so you do not clear state on failure and consider
setting/clearing an explicit loading state around the call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5aa16616-578d-442e-8b3a-379f9dc1c14b

📥 Commits

Reviewing files that changed from the base of the PR and between cdc26f8 and cc8e319.

📒 Files selected for processing (7)
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/layout.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx
  • docs/src/components/mdx/app-card.tsx
✅ Files skipped from review due to trivial changes (2)
  • docs/src/components/mdx/app-card.tsx
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx

@mantrakp04 mantrakp04 requested a review from nams1570 May 19, 2026 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants