Skip to content

Dashboard item quantity changes#936

Closed
BilalG1 wants to merge 5 commits intodevfrom
dashboard-item-quantity-changes
Closed

Dashboard item quantity changes#936
BilalG1 wants to merge 5 commits intodevfrom
dashboard-item-quantity-changes

Conversation

@BilalG1
Copy link
Contributor

@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 create/edit dialog with clearer validation and optional enforced customer type.
    • Team table rows and data tables support row-click navigation.
    • Searchable team table component added.
  • Tests

    • E2E tests updated to exercise server-side item quantity behavior.

@vercel
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 10, 2025 8:21pm
stack-dashboard Ready Ready Preview Comment Oct 10, 2025 8:21pm
stack-demo Ready Ready Preview Comment Oct 10, 2025 8:21pm
stack-docs Ready Ready Preview Comment Oct 10, 2025 8:21pm

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

claude bot commented Oct 8, 2025

Claude finished @BilalG1's task —— View job


I'll analyze this and get back to you.

@coderabbitai
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 per-customer-type item inspection/management; relocates and simplifies 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 client Items page, customer-type filtering and selector (user/team/custom), per-item rows (Suspense), adjust-quantity dialog, and page metadata.
ItemDialog relocated & API changed
apps/dashboard/src/components/payments/item-dialog.tsx, apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx
Removes old payments/products/item-dialog.tsx and introduces shared apps/.../components/payments/item-dialog.tsx with new props (onSave, editingItem, existingItemIds, forceCustomerType) and simplified Dialog UI; consumer imports updated.
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 @/components/payments/item-dialog (no logic changes).
Removed legacy payment table
apps/dashboard/src/components/data-table/payment-item-table.tsx
Deleted legacy payment-item-table and its action dialogs (create quantity change, edit, delete) and related exports.
Team tables & row navigation
apps/dashboard/src/components/data-table/team-search-table.tsx, apps/dashboard/src/components/data-table/team-table.tsx
Adds TeamSearchTable (searchable teams table) and enables on-row-click navigation in TeamTable to team pages.
DataTable API extension
packages/stack-ui/src/components/data-table/data-table.tsx
Adds optional onRowClick?: (row: TData) => void to DataTableProps and threads it to DataTableBase.
Sidebar navigation
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
Adds “Items” navigation entry under Payments with Package icon and path/regex.
Admin app behavior change
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
createItemQuantityChange now sends allow_negative: true in its updateItemQuantity payload.
E2E tests & server API surface
apps/e2e/tests/js/payments.test.ts
createApp now returns serverApp; tests use serverApp.getItem and new Item.tryDecreaseQuantity(amount): Promise<boolean> behavior (returns boolean on failure 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 type & selector UI
  User->>Selector: Select type + customer
  Selector-->>ItemsPage: Selected customer context
  ItemsPage->>ItemHook: Fetch items for selected customer
  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(with onRowClick)
  User->>DataTable: click row
  DataTable-->>TeamTable: onRowClick(team)
  TeamTable->>Router: navigate to /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 leap when clicked anew.
Carrot-coded cheers — adjust and on!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title mentions “Dashboard item quantity changes,” which relates to a real aspect of the PR but is too generic and does not clearly convey that a new Items page and supporting UI for managing item quantities by customer type have been added. As a result, a teammate reviewing history may not understand the primary purpose of these changes. Consider renaming the pull request to something more descriptive, such as “Add Items page to dashboard for managing item quantities by customer type,” to clearly reflect the main user-facing change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed The pull request description includes the required CONTRIBUTING.md reminder comment, provides a Loom demo link, a clear high-level summary, detailed feature and refactor bullet points, a suggested review order, and test updates, covering the relevant information for reviewers.
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

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

📒 Files selected for processing (2)
  • 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]/sidebar-layout.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
🧰 Additional context used
📓 Path-based instructions (3)
{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]/payments/products/page-client-catalogs-view.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]/payments/products/page-client-catalogs-view.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]/payments/products/page-client-catalogs-view.tsx
⏰ 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). (9)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: all-good
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: setup-tests
  • GitHub Check: docker
  • GitHub Check: Security Check
🔇 Additional comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx (2)

42-42: LGTM! Import path updated to use shared component location.

The import path change from a relative path to an absolute alias correctly reflects the relocation of ItemDialog to the shared components directory.


42-43: Relative import for ProductDialog is correct
ProductDialog is defined in the same directory as page-client-catalogs-view.tsx, so a relative import is appropriate.


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
Contributor 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
Contributor 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, itemId, 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" })

@BilalG1 BilalG1 requested a review from N2D4 October 9, 2025 16:10
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Oct 9, 2025
@github-actions github-actions bot assigned BilalG1 and unassigned N2D4 Oct 10, 2025
@BilalG1 BilalG1 closed this Oct 10, 2025
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.

2 participants