8000 Dashboard item quantity changes by BilalG1 · Pull Request #936 · stack-auth/stack-auth · GitHub
[go: up one dir, main page]

Skip to content

Conversation

BilalG1
Copy link
Collaborator
@BilalG1 BilalG1 commented Oct 8, 2025

https://www.loom.com/share/cd979cfbd6e64dc9827bc50f1bb62085?sid=bf5bc9b1-5a7a-4c0f-864f-52ab8f3c648c

High-level PR Summary

This PR adds a new Items page to the dashboard that allows admins to inspect and adjust item quantities for different customer types (users, teams, or custom customers). The new page includes functionality to select customers, view their current item quantities, and create quantity adjustments with optional expiration dates and descriptions. The existing ItemDialog component was refactored and moved to a shared location, and the old payment item table was removed. Additionally, navigation for teams was improved with clickable rows, and a new team search table component was added to support customer selection.

⏱️ Estimated Review Time: 30-90 minutes

💡 Review Order Suggestion
Order File Path
1 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
2 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx
3 apps/dashboard/src/components/payments/item-dialog.tsx
4 apps/dashboard/src/components/data-table/team-search-table.tsx
5 apps/dashboard/src/components/data-table/team-table.tsx
6 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
7 packages/stack-ui/src/components/data-table/data-table.tsx
8 packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
9 apps/dashboard/src/components/data-table/payment-item-table.tsx
10 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx
11 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx
12 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx

Need help? Join our Discord

Analyze latest changes


Important

Adds a new Items page to the dashboard for managing item quantities, refactors components, and updates navigation and tests.

  • New Features:
    • Adds Items page in page-client.tsx for managing item quantities by customer type (User, Team, Custom).
    • Introduces CustomerSelector and ItemTable components in page-client.tsx.
    • Adds sidebar navigation entry for Items in sidebar-layout.tsx.
  • Refactor:
    • Moves and refactors ItemDialog to item-dialog.tsx with clearer validation and optional enforced customer type.
    • Makes team table rows clickable in team-table.tsx.
    • Enables row-click handling in data-table.tsx.
  • Tests:
    • Updates payments.test.ts to test new item quantity functionalities.
  • Misc:
    • Removes payment-item-table.tsx and item-dialog.tsx from payments/products directory.
    • Minor updates to team-search-table.tsx and team-table.tsx.

This description was created by Ellipsis for f5165f5. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • Added an Items page under Payments to inspect and manage item quantities by customer type (User, Team, Custom).
    • Sidebar now includes an Items navigation entry.
    • Customer selection via searchable user/team tables or custom ID entry.
    • Per-item live quantity display and dialogs to adjust quantities with contextual success/error toasts.
  • Refactor

    • Simplified item creation/edit dialog with clearer validation and optional enforced customer type.
    • Team table rows and data tables support row-click navigation.

Copy link
vercel bot commented Oct 8, 2025

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

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Oct 8, 2025 11:04pm
stack-dashboard Ready Ready Preview Comment Oct 8, 2025 11:04pm
stack-demo Ready Ready Preview Comment Oct 8, 2025 11:04pm
stack-docs Ready Ready Preview Comment Oct 8, 2025 11:04pm

@BilalG1 BilalG1 marked this pull request as ready for review October 8, 2025 17:05
Copy link
claude bot commented Oct 8, 2025

Claude finished @BilalG1's task —— View job


I'll analyze this and get back to you.

Copy link
Contributor
coderabbitai bot commented Oct 8, 2025

Note

Other AI code review bot(s) detected

CodeRabbit 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.

Walkthrough

Adds a new Items page and client UI for inspecting/managing per-customer-type item quantities; refactors and relocates ItemDialog, removes legacy payment-item-table, adds team search/table components, enables row-click on DataTable, updates sidebar navigation, and changes admin quantity-change payload.

Changes

Cohort / File(s) Summary
New Items page (UI & routing)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx, apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx
Adds a client Items page with customer-type filtering, customer selection (user/team/custom), item listing, Suspense-wrapped rows, adjust-quantity dialog, and page metadata/title.
Payments item dialog relocation & API change
apps/dashboard/src/components/payments/item-dialog.tsx, apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx
Removes old local ItemDialog implementation and adds a shared ItemDialog at apps/.../components/payments/item-dialog.tsx with new props (onSave, editingItem, existingItemIds, forceCustomerType) and simplified Dialog-based UI.
Consumers updated to new import
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx, apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx
Updated imports to use @/components/payments/item-dialog instead of the old relative path.
Removed legacy payment table
apps/dashboard/src/components/data-table/payment-item-table.tsx
Deleted the payment-item-table component and its actions/dialogs (CreateItemQuantityChangeDialog, edit/delete flows).
New team search table & table UX
apps/dashboard/src/components/data-table/team-search-table.tsx, apps/dashboard/src/components/data-table/team-table.tsx
Adds TeamSearchTable component (searchable teams table) and enables row-click navigation in TeamTable to route to team pages.
DataTable API extension
packages/stack-ui/src/components/data-table/data-table.tsx
Adds optional onRowClick?: (row: TData) => void prop and passes it through to DataTableBase to support clickable rows.
Sidebar navigation
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
Adds an “Items” navigation entry under Payments with the Package icon and corresponding path/regex.
Admin app behavior change
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
createItemQuantityChange payload now includes allow_negative: true for quantity updates.
E2E tests & server API surface
apps/e2e/tests/js/payments.test.ts
Tests updated to receive serverApp from createApp, use serverApp.getItem, and call new Item.tryDecreaseQuantity(amount) which returns boolean instead of throwing.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant ItemsPage as PageClient
  participant Selector as CustomerSelector
  participant ItemHook as useItemForCustomer
  participant Admin as adminApp
  participant UI as AdjustItemQuantityDialog
  participant Toast as Toasts

  User->>ItemsPage: Open Items page
  ItemsPage->>Selector: Render customer type & selector
  User->>Selector: Select type + customer (user/team/custom)
  Selector-->>ItemsPage: Selected customer context
  ItemsPage->>ItemHook: Fetch items for selected customer (type-aware)
  ItemHook-->>ItemsPage: Item data (Suspense)

  alt User adjusts quantity
    User->>UI: Open Adjust dialog
    UI->>Admin: createItemQuantityChange({..., allow_negative: true})
    Admin-->>UI: Success / KnownError
    opt Success
      UI->>ItemHook: Refresh item data
      UI->>Toast: Show success
    end
    opt KnownError
      UI->>Toast: Show specific error
    end
  end
Loading
sequenceDiagram
  autonumber
  participant TeamTable as TeamTable
  participant DataTable as DataTable
  participant Router as NextRouter

  TeamTable->>DataTable: render(onRowClick => navigate)
  User->>DataTable: click row
  DataTable-->>TeamTable: onRowClick(team)
  TeamTable->>Router: push(/projects/{projectId}/teams/{teamId})
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • N2D4

Poem

🐰 I hopped to the Items page at dawn,
New dialogs brighten fields of lawn.
Teams and users, custom too—
Rows that jump when clicked—how new!
Carrot-coded cheers, adjust and on!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly indicates that the dashboard is enhanced to handle item quantity changes, capturing a core aspect of the pull request. It refers directly to the new functionality for adjusting item quantities within the dashboard. While it does not explicitly mention the addition of a dedicated Items page or the customer type selection, it remains accurate and not misleading.
Description Check ✅ Passed The description includes the required CONTRIBUTING guidelines comment from the template and offers a comprehensive high-level summary, a detailed breakdown of features, refactors, removals, and a suggested review order. It also links to a Loom demo and clearly outlines test updates, satisfying completeness requirements. The PR description is well-structured, informative, and aligned with the repository’s template.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dashboard-item-quantity-changes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor
@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Summary

This PR implements a comprehensive dashboard feature for managing item quantities across different customer types (users, teams, and custom customers). The changes introduce a new dedicated Items page in the dashboard navigation, refactor the ItemDialog component for better reusability, and enhance data table functionality with row click navigation.

The core functionality centers around a new /items page that allows administrators to select customers (users, teams, or custom types) and manage their associated item quantities. The implementation includes customer search capabilities, item quantity adjustment dialogs, and comprehensive error handling. The changes follow established patterns in the codebase by using the page/page-client structure and integrating with existing UI components.

Key architectural changes include moving the ItemDialog component from local directories to a shared @/components/payments/ location for better reusability, adding row click functionality to data tables for improved navigation, and creating a new TeamSearchTable component for customer selection workflows. The backend integration includes enabling negative quantity changes through the allow_negative: true parameter, which supports business scenarios like refunds and inventory corrections.

Important Files Changed

Changed Files
Filename Score Overview
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx 4/5 New comprehensive dashboard page for managing item quantities with customer selection and quantity adjustment functionality
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx 5/5 Standard Next.js page wrapper for the new Item Quantities feature
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx 5/5 Adds Items navigation menu item to the sidebar under Payments section
apps/dashboard/src/components/payments/item-dialog.tsx 4/5 Complete rewrite from FormDialog to custom Dialog with manual state management and callback-based API
apps/dashboard/src/components/data-table/team-search-table.tsx 4/5 New simplified team search table component for customer selection workflows
apps/dashboard/src/components/data-table/team-table.tsx 5/5 Adds row click navigation functionality to team table for better UX
packages/stack-ui/src/components/data-table/data-table.tsx 4/5 Adds optional onRowClick callback prop with portal prevention logic
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx 5/5 Updates import path for ItemDialog to shared components directory
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx 5/5 Updates import path for ItemDialog to shared components directory
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts 4/5 Enables negative quantity changes by adding allow_negative parameter
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx 4/5 Removed file - functionality consolidated into shared components
apps/dashboard/src/components/data-table/payment-item-table.tsx 4/5 Removed file - replaced with comprehensive page-based approach

Confidence score: 4/5

  • This PR introduces significant new functionality with proper error handling and follows established architectural patterns
  • Score reflects the complexity of the new item management system and some concerns about the ItemDialog rewrite removing form validation frameworks
  • Pay close attention to the ItemDialog component rewrite and the new items page-client.tsx for comprehensive testing

Sequence Diagram

sequenceDiagram
    participant User
    participant PageClient as "PageClient Component"
    participant CustomerSelector as "Customer Selector"
    participant ItemTable as "Item Table"
    participant AdjustDialog as "Adjust Quantity Dialog"
    participant AdminApp as "Admin App"
    participant API as "Backend API"

    User->>PageClient: "Load items page"
    PageClient->>AdminApp: "useProject() & useConfig()"
    AdminApp-->>PageClient: "Return project config and payment items"
    
    User->>PageClient: "Select customer type (user/team/custom)"
    PageClient->>PageClient: "setCustomerType()"
    PageClient->>PageClient: "Filter items by customer type"
    
    User->>CustomerSelector: "Click 'Select customer' button"
    CustomerSelector->>CustomerSelector: "Open ActionDialog"
    alt Customer type is user
        CustomerSelector->>AdminApp: "Load TeamMemberSearchTable"
        User->>CustomerSelector: "Select user"
    else Customer type is team
        CustomerSelector->>AdminApp: "Load TeamSearchTable"
        User->>CustomerSelector: "Select team"
    else Customer type is custom
        User->>CustomerSelector: "Enter custom customer ID"
    end
    CustomerSelector->>PageClient: "setSelectedCustomer()"
    
    PageClient->>ItemTable: "Render table with customer selection"
    ItemTable->>AdminApp: "useItem() for each item"
    AdminApp-->>ItemTable: "Return item quantities"
    
    User->>ItemTable: "Click 'Adjust' button for item"
    ItemTable->>AdjustDialog: "Open AdjustItemQuantityDialog"
    AdjustDialog->>AdjustDialog: "Display quantity adjustment form"
    
    User->>AdjustDialog: "Enter quantity change and description"
    User->>AdjustDialog: "Click 'Apply change'"
    AdjustDialog->>AdminApp: "createItemQuantityChange()"
    AdminApp->>API: "POST item quantity update"
    API-->>AdminApp: "Return success/error"
    
    alt Success
        AdminApp->>AdminApp: "refreshItem()"
        AdminApp-->>AdjustDialog: "Success response"
        AdjustDialog->>AdjustDialog: "Show success toast"
        AdjustDialog->>AdjustDialog: "Close dialog"
        ItemTable->>ItemTable: "Refresh item quantities"
    else Error
        AdminApp-->>AdjustDialog: "Error response"
        AdjustDialog->>AdjustDialog: "Show error toast"
        AdjustDialog->>AdjustDialog: "Keep dialog open"
    end
Loading

12 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +55 to +61
await onSave({
id: itemId.trim(),
displayName: displayName.trim(),
customerType
});

handleClose();
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Missing error handling around the onSave call - if it throws, the dialog won't close and user gets no feedback

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/src/components/payments/item-dialog.tsx
Line: 55:61

Comment:
**logic:** Missing error handling around the onSave call - if it throws, the dialog won't close and user gets no feedback

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

Copy link
@recurseml recurseml bot left a comment

Choose a reason for hiding this comment

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

Review by RecurseML

🔍 Review performed on 017b43f..aeb2508

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (12)

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
apps/dashboard/src/components/data-table/payment-item-table.tsx
apps/dashboard/src/components/data-table/team-search-table.tsx
apps/dashboard/src/components/data-table/team-table.tsx
apps/dashboard/src/components/payments/item-dialog.tsx
packages/stack-ui/src/components/data-table/data-table.tsx
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts

displayName: yup.string().optional().label("Display Name"),
customerType: yup.string().oneOf(["user", "team", "custom"]).defined().label("Customer Type"),
});
await onSave({
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider wrapping the onSave call in a try/catch block in validateAndSave so that if saving fails, the dialog can display an error instead of closing unexpectedly.

defaultColumnFilters={[]}
defaultSorting={[{ id: 'createdAt', desc: true }]}
onRowClick={(row) => {
router.push(`/projects/${encodeURIComponent(stackAdminApp.projectId)}/teams/${encodeURIComponent(row.id)}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

When building navigation URLs, consider using a tagged template literal (e.g., urlString``) as per best practices for URL fragments instead of manually concatenating with encodeURIComponent.

Suggested change
router.push(`/projects/${encodeURIComponent(stackAdminApp.projectId)}/teams/${encodeURIComponent(row.id)}`);
router.push(urlString`/projects/${stackAdminApp.projectId}/teams/${row.id}`);

This comment was generated because it violated a code review rule: mrule_pmzJAgHDlFZgwIwD.

Comment on lines -1 to -2
'use client';
import { useAdminApp } from "@/app/(main)/(protected)/projects/[projectId]/use-admin-app";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

unused

Comment on lines -1 to -3
"use client";

import { cn } from "@/lib/utils";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to components/payments/item-dialog

delta: options.quantity,
expires_at: options.expiresAt,
description: options.description,
allow_negative: true,
Copy link

Choose a reason for hiding this comment

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

The admin app now always passes allow_negative: true for item quantity changes, but the items page still includes error handling for ItemQuantityInsufficientAmount errors, which will never occur when negative quantities are allowed.

View Details
📝 Patch Details
diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
index d700f66e..9bf6c4dd 100644
--- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
+++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
@@ -504,14 +504,7 @@ function handleItemQuantityError(error: unknown) {
     });
     return;
   }
-  if (error instanceof KnownErrors.ItemQuantityInsufficientAmount) {
-    toast({
-      title: "Quantity too low",
-      description: "This change would reduce the quantity below zero.",
-      variant: "destructive",
-    });
-    return;
-  }
+
   toast({ title: "Unable to update quantity", variant: "destructive" });
 }
 

Analysis

Dead error handling for ItemQuantityInsufficientAmount in admin items page

What fails: The handleItemQuantityError() function in apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx contains error handling for KnownErrors.ItemQuantityInsufficientAmount that can never be triggered.

How to reproduce: The admin app's createItemQuantityChange() method (line 581 in packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts) always passes allow_negative: true to the API. When this parameter is true, the backend API never throws ItemQuantityInsufficientAmount errors, making the error handling code unreachable.

Result: Dead code in error handler (lines 507-514) that displays "This change would reduce the quantity below zero" message.

Expected: Remove the unreachable error handling code since the admin app explicitly allows negative quantities by design.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stack-ui/src/components/data-table/data-table.tsx (1)

74-83: Guard row clicks from bubbling controls.
Any button/link inside the row (e.g., action menus) now fires onRowClick, so trying to open those controls immediately navigates away. Add a guard to ignore events originating from interactive descendants before calling the handler.

Apply this diff:

-                  onClick={(ev) => {
-                    // only trigger onRowClick if the element is a direct descendant; don't trigger for portals
-                    if (ev.target instanceof Node && ev.currentTarget.contains(ev.target)) {
-                      props.onRowClick?.(row.original);
-                    }
-                  }}
+                  onClick={(ev) => {
+                    if (!(ev.target instanceof HTMLElement)) {
+                      return;
+                    }
+                    if (!ev.currentTarget.contains(ev.target)) {
+                      return;
+                    }
+                    if (ev.target.closest('button, a, input, textarea, select, [data-no-row-click]')) {
+                      return;
+                    }
+                    props.onRowClick?.(row.original);
+                  }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 017b43f and aeb2508.

📒 Files selected for processing (12)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx (1 hunks)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx (1 hunks)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx (0 hunks)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx (1 hunks)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx (1 hunks)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (2 hunks)
  • apps/dashboard/src/components/data-table/payment-item-table.tsx (0 hunks)
  • apps/dashboard/src/components/data-table/team-search-table.tsx (1 hunks)
  • apps/dashboard/src/components/data-table/team-table.tsx (1 hunks)
  • apps/dashboard/src/components/payments/item-dialog.tsx (1 hunks)
  • packages/stack-ui/src/components/data-table/data-table.tsx (2 hunks)
  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx
  • apps/dashboard/src/components/data-table/payment-item-table.tsx
🧰 Additional context used
📓 Path-based instructions (4)
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

For blocking alerts and errors in UI, do not use toast notifications; use alerts instead

Files:

  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
  • apps/dashboard/src/components/data-table/team-table.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx
  • apps/dashboard/src/components/payments/item-dialog.tsx
  • packages/stack-ui/src/components/data-table/data-table.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/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-catalogs-view.tsx
  • apps/dashboard/src/components/data-table/team-search-table.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
  • apps/dashboard/src/components/data-table/team-table.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx
  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
  • apps/dashboard/src/components/payments/item-dialog.tsx
  • packages/stack-ui/src/components/data-table/data-table.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/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-catalogs-view.tsx
  • apps/dashboard/src/components/data-table/team-search-table.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Keep hover/click animations snappy; avoid pre-transition delays on hover and apply transitions after the action (e.g., fade-out on hover end)

Files:

  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
  • apps/dashboard/src/components/data-table/team-table.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx
  • apps/dashboard/src/components/payments/item-dialog.tsx
  • packages/stack-ui/src/components/data-table/data-table.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/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-catalogs-view.tsx
  • apps/dashboard/src/components/data-table/team-search-table.tsx
packages/template/**

📄 CodeRabbit inference engine (AGENTS.md)

When modifying the SDK copies, make changes in packages/template (source of truth)

Files:

  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
🧬 Code graph analysis (5)
apps/dashboard/src/components/data-table/team-table.tsx (3)
apps/dashboard/src/components/router.tsx (1)
  • useRouter (15-33)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
  • useAdminApp (27-34)
packages/stack-ui/src/components/data-table/data-table.tsx (1)
  • DataTable (124-162)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx (1)
  • PageClient (46-142)
apps/dashboard/src/components/payments/item-dialog.tsx (3)
packages/stack-ui/src/components/simple-tooltip.tsx (1)
  • SimpleTooltip (5-46)
packages/stack-ui/src/components/ui/input.tsx (1)
  • Input (10-41)
apps/dashboard/src/lib/utils.tsx (1)
  • cn (7-9)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx (5)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
  • useAdminApp (27-34)
apps/dashboard/src/components/payments/item-dialog.tsx (1)
  • ItemDialog (20-188)
apps/dashboard/src/components/data-table/team-member-search-table.tsx (1)
  • TeamMemberSearchTable (9-68)
apps/dashboard/src/components/data-table/team-search-table.tsx (1)
  • TeamSearchTable (25-71)
apps/dashboard/src/components/form-dialog.tsx (1)
  • SmartFormDialog (11-51)
apps/dashboard/src/components/data-table/team-search-table.tsx (5)
packages/stack-ui/src/components/data-table/toolbar-items.tsx (1)
  • SearchToolbarItem (4-13)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
  • useAdminApp (27-34)
packages/stack-ui/src/components/data-table/column-header.tsx (1)
  • DataTableColumnHeader (22-51)
packages/stack-ui/src/components/data-table/cells.tsx (1)
  • TextCell (7-43)
packages/stack-ui/src/components/data-table/data-table.tsx (1)
  • DataTable (124-162)
⏰ 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). (11)
  • GitHub Check: sync
  • GitHub Check: Vercel Agent Review
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: restart-dev-and-test
  • GitHub Check: all-good
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: setup-tests
  • GitHub Check: Security Check
🔇 Additional comments (2)
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts (1)

570-582: Negative adjustments enabled appropriately.

Setting allow_negative: true ensures admin quantity decrements work with the new flows. Looks good.

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (1)

38-263: Navigation entry looks good.

The new Items route hooks cleanly into the payments section and icon import; no issues spotted.

Comment on lines +28 to +78
const [itemId, setItemId] = useState(editingItem?.id || "");
const [displayName, setDisplayName] = useState(editingItem?.displayName || "");
const [customerType, setCustomerType] = useState<'user' | 'team' | 'custom'>(forceCustomerType || editingItem?.customerType || 'user');
const [errors, setErrors] = useState<Record<string, string>>({});

const validateAndSave = async () => {
const newErrors: Record<string, string> = {};

// Validate item ID
if (!itemId.trim()) {
newErrors.itemId = "Item ID is required";
} else if (!/^[a-z0-9-]+$/.test(itemId)) {
newErrors.itemId = "Item ID must contain only lowercase letters, numbers, and hyphens";
} else if (!editingItem && existingItemIds.includes(itemId)) {
newErrors.itemId = "This item ID already exists";
}

// Validate display name
if (!displayName.trim()) {
newErrors.displayName = "Display name is required";
}

if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
)

export function ItemDialog({ open, onOpenChange, project, mode, initial }: Props) {
const itemSchema = yup.object({
itemId: userSpecifiedIdSchema("itemId").defined().label("Item ID"),
displayName: yup.string().optional().label("Display Name"),
customerType: yup.string().oneOf(["user", "team", "custom"]).defined().label("Customer Type"),
});
await onSave({
id: itemId.trim(),
displayName: displayName.trim(),
customerType
});

handleClose();
};

useEffect(() => {
if (forceCustomerType || editingItem?.customerType) {
setCustomerType(forceCustomerType || editingItem?.customerType || 'user');
}
}, [forceCustomerType, editingItem]);

const handleClose = () => {
if (!editingItem) {
setItemId("");
setDisplayName("");
setCustomerType('user');
}
setErrors({});
onOpenChange(false);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Sync dialog state with editingItem.

Because the local state is initialised only once, switching into edit mode leaves the inputs blank (or stuck with whatever was typed during the previous create flow). Likewise, after editing an item and closing, reopening in “create” mode still shows the last edited values. The dialog never reacts to editingItem/forceCustomerType changes, so the form can’t reliably load the record you’re trying to edit. Please wire the state to those props before shipping.

Suggested fix:

@@
-  const [itemId, setItemId] = useState(editingItem?.id || "");
-  const [displayName, setDisplayName] = useState(editingItem?.displayName || "");
-  const [customerType, setCustomerType] = useState<'user' | 'team' | 'custom'>(forceCustomerType || editingItem?.customerType || 'user');
-  const [errors, setErrors] = useState<Record<string, string>>({});
+  const [itemId, setItemId] = useState(editingItem?.id || "");
+  const [displayName, setDisplayName] = useState(editingItem?.displayName || "");
+  const [customerType, setCustomerType] = useState<'user' | 'team' | 'custom'>(forceCustomerType || editingItem?.customerType || 'user');
+  const [errors, setErrors] = useState<Record<string, string>>({});
+
+  useEffect(() => {
+    if (editingItem) {
+      setItemId(editingItem.id);
+      setDisplayName(editingItem.displayName);
+    } else {
+      setItemId("");
+      setDisplayName("");
+    }
+    setErrors({});
+  }, [editingItem]);
+
+  useEffect(() => {
+    setCustomerType(forceCustomerType ?? editingItem?.customerType ?? 'user');
+  }, [editingItem, forceCustomerType]);
@@
-  useEffect(() => {
-    if (forceCustomerType || editingItem?.customerType) {
-      setCustomerType(forceCustomerType || editingItem?.customerType || 'user');
-    }
-  }, [forceCustomerType, editingItem]);
+ 
📝 Committable suggestion

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

Suggested change
const [itemId, setItemId] = useState(editingItem?.id || "");
const [displayName, setDisplayName] = useState(editingItem?.displayName || "");
const [customerType, setCustomerType] = useState<'user' | 'team' | 'custom'>(forceCustomerType || editingItem?.customerType || 'user');
const [errors, setErrors] = useState<Record<string, string>>({});
const validateAndSave = async () => {
const newErrors: Record<string, string> = {};
// Validate item ID
if (!itemId.trim()) {
newErrors.itemId = "Item ID is required";
} else if (!/^[a-z0-9-]+$/.test(itemId)) {
newErrors.itemId = "Item ID must contain only lowercase letters, numbers, and hyphens";
} else if (!editingItem && existingItemIds.includes(itemId)) {
newErrors.itemId = "This item ID already exists";
}
// Validate display name
if (!displayName.trim()) {
newErrors.displayName = "Display name is required";
}
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
)
export function ItemDialog({ open, onOpenChange, project, mode, initial }: Props) {
const itemSchema = yup.object({
itemId: userSpecifiedIdSchema("itemId").defined().label("Item ID"),
displayName: yup.string().optional().label("Display Name"),
customerType: yup.string().oneOf(["user", "team", "custom"]).defined().label("Customer Type"),
});
await onSave({
id: itemId.trim(),
displayName: displayName.trim(),
customerType
});
handleClose();
};
useEffect(() => {
if (forceCustomerType || editingItem?.customerType) {
setCustomerType(forceCustomerType || editingItem?.customerType || 'user');
}
}, [forceCustomerType, editingItem]);
const handleClose = () => {
if (!editingItem) {
setItemId("");
setDisplayName("");
setCustomerType('user');
}
setErrors({});
onOpenChange(false);
};
const [itemId, setItemId] = useState(editingItem?.id || "");
const [displayName, setDisplayName] = useState(editingItem?.displayName || "");
const [customerType, setCustomerType] = useState<'user' | 'team' | 'custom'>(forceCustomerType || editingItem?.customerType || 'user');
const [errors, setErrors] = useState<Record<string, string>>({});
// Sync itemId, displayName, and errors when editingItem changes
useEffect(() => {
if (editingItem) {
setItemId(editingItem.id);
setDisplayName(editingItem.displayName);
} else {
setItemId("");
setDisplayName("");
}
setErrors({});
}, [editingItem]);
// Sync customerType when editingItem or forceCustomerType changes
useEffect(() => {
setCustomerType(forceCustomerType ?? editingItem?.customerType ?? 'user');
}, [editingItem, forceCustomerType]);
const validateAndSave = async () => {
const newErrors: Record<string, string> = {};
// Validate item ID
if (!itemId.trim()) {
newErrors.itemId = "Item ID is required";
} else if (!/^[a-z0-9-]+$/.test(itemId)) {
newErrors.itemId = "Item ID must contain only lowercase letters, numbers, and hyphens";
} else if (!editingItem && existingItemIds.includes(itemId)) {
newErrors.itemId = "This item ID already exists";
}
// Validate display name
if (!displayName.trim()) {
newErrors.displayName = "Display name is required";
}
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
await onSave({
id: itemId.trim(),
displayName: displayName.trim(),
customerType
});
handleClose();
};
const handleClose = () => {
if (!editingItem) {
setItemId("");
setDisplayName("");
setCustomerType('user');
}
setErrors({});
onOpenChange(false);
};
🤖 Prompt for AI Agents
In apps/dashboard/src/components/payments/item-dialog.tsx around lines 28 to 78,
the local form state is only set on initial render so the dialog doesn’t update
when editingItem or forceCustomerType change; add a useEffect that watches
[editingItem, forceCustomerType] and sets itemId to editingItem?.id || "" (or ""
for create), displayName to editingItem?.displayName || "", and customerType to
forceCustomerType || editingItem?.customerType || 'user' so the form loads the
correct values when switching between create/edit and resets to defaults when
closing; keep existing handleClose behavior to clear state on create close.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aeb2508 and f5165f5.

📒 Files selected for processing (1)
  • apps/e2e/tests/js/payments.test.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

In tests, prefer .toMatchInlineSnapshot where possible; refer to snapshot-serializer.ts for snapshot formatting and handling of non-deterministic values

Files:

  • apps/e2e/tests/js/payments.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • apps/e2e/tests/js/payments.test.ts
🧬 Code graph analysis (1)
apps/e2e/tests/js/payments.test.ts (1)
apps/e2e/tests/js/js-helpers.ts (1)
  • createApp (41-86)
⏰ 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). (10)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: all-good
  • GitHub Check: setup-tests
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: Security Check
🔇 Additional comments (1)
apps/e2e/tests/js/payments.test.ts (1)

168-168: LGTM!

The addition of serverApp to the destructuring aligns with the updated createApp helper function signature and is necessary for the server-side API usage below.

Comment on lines 199 to +202
// Try to decrease by 1 (should fail with KnownErrors.ItemQuantityInsufficientAmount)
await expect(adminApp.createItemQuantityChange({ teamId: team.id, itemI 6D40 d, quantity: -1 }))
.rejects.toThrow();
const item = await serverApp.getItem({ teamId: team.id, itemId });
const success = await item.tryDecreaseQuantity(1);
expect(success).toBe(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update the outdated comment.

The comment on line 199 mentions that the operation "should fail with KnownErrors.ItemQuantityInsufficientAmount", but the new implementation uses tryDecreaseQuantity() which returns a boolean (false on failure) instead of throwing an exception. The test logic correctly expects false, but the comment no longer reflects the actual behavior.

Apply this diff to update the comment:

-  // Try to decrease by 1 (should fail with KnownErrors.ItemQuantityInsufficientAmount)
+  // Try to decrease by 1 (should return false as quantity cannot go below zero)

Additionally, the implementation change from exception-based to boolean-based error handling is good—it provides a cleaner API for attempting operations that may fail due to business constraints.

📝 Committable suggestion

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

Suggested change
// Try to decrease by 1 (should fail with KnownErrors.ItemQuantityInsufficientAmount)
await expect(adminApp.createItemQuantityChange({ teamId: team.id, itemId, quantity: -1 }))
.rejects.toThrow();
const item = await serverApp.getItem({ teamId: team.id, itemId });
const success = await item.tryDecreaseQuantity(1);
expect(success).toBe(false);
// Try to decrease by 1 (should return false as quantity cannot go below zero)
const item = await serverApp.getItem({ teamId: team.id, itemId });
const success = await item.tryDecreaseQuantity(1);
expect(success).toBe(false);
🤖 Prompt for AI Agents
In apps/e2e/tests/js/payments.test.ts around lines 199-202, the inline comment
is outdated — it still says the decrease "should fail with
KnownErrors.ItemQuantityInsufficientAmount" even though tryDecreaseQuantity()
now returns a boolean; update the comment to state that tryDecreaseQuantity(1)
should return false when quantity is insufficient (i.e., the method returns a
boolean indicating success/failure rather than throwing an exception), leaving
the test assertion expect(success).toBe(false) unchanged.

Comment on lines +77 to +80
const handleSaveItem = async (item: { id: string, displayName: string, customerType: 'user' | 'team' | 'custom' }) => {
await project.updateConfig({ [`payments.items.${item.id}`]: { displayName: item.displayName, customerType: item.customerType } });
setShowItemDialog(false);
};
Copy link

Choose a reason for hiding this comment

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

The handleSaveItem function doesn't provide user feedback via toast notifications, unlike similar functions in other parts of the codebase, leading to inconsistent user experience.

View Details
📝 Patch Details
diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
index d700f66e..f88be3da 100644
--- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
+++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx
@@ -77,6 +77,7 @@ export default function PageClient() {
   const handleSaveItem = async (item: { id: string, displayName: string, customerType: 'user' | 'team' | 'custom' }) => {
     await project.updateConfig({ [`payments.items.${item.id}`]: { displayName: item.displayName, customerType: item.customerType } });
     setShowItemDialog(false);
+    toast({ title: "Item created" });
   };
 
   return (

Analysis

Missing toast notification in handleSaveItem function creates inconsistent UX

What fails: The handleSaveItem function in apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/items/page-client.tsx successfully creates/updates items but provides no user feedback, unlike identical functions in other files.

How to reproduce:

  1. Navigate to a project's Items page
  2. Click "Create User Item" (or Team/Custom Item)
  3. Fill in item details and save
  4. Observe no success feedback is shown to user

Result: Users get no confirmation that their item was created successfully, creating poor UX

Expected: Should show toast notification like similar functions in page-client-catalogs-view.tsx line 1544 and page-client-list-view.tsx line 753: toast({ title: "Item created" })

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.

1 participant
0