Skip to content

feat: Catalog & pricing - product categories, products & filters - #3809

Open
ansmonjol wants to merge 4 commits into
mainfrom
feat/catalog-pricing-nav
Open

feat: Catalog & pricing - product categories, products & filters#3809
ansmonjol wants to merge 4 commits into
mainfrom
feat/catalog-pricing-nav

Conversation

@ansmonjol

@ansmonjol ansmonjol commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Adds the Catalog & pricing area: product categories, products, and product filters (list + drawer + details each), plus the nav section. The rate cards tab is a placeholder here (the rate cards feature lands in #4005).

Aligned to the new backend model naming (ProductCategory > Product > ProductFilter) and rebased onto main.

@ansmonjol ansmonjol self-assigned this Jul 2, 2026
@ansmonjol
ansmonjol force-pushed the feat/catalog-pricing-nav branch 2 times, most recently from f0fdc87 to 93780d4 Compare July 9, 2026 14:59
ansmonjol added a commit that referenced this pull request Jul 13, 2026
Fixes BIL-256

Third step of the Products & rate cards initiative, stacked on #3809
(`feat/catalog-pricing-nav`): the product create/edit drawer with its
real form, a reusable "Create more" drawer system, and the Product
details page. Also carries the catalog Create menu, Products list and
product item/filter drawer scaffolds from the previous step (they were
only in the working tree).

## What

### Product drawer (create + edit)

- Real TanStack form in `useProductDrawer`: name/code (required, zod),
description reveal pattern, invoice display name section.
`NameAndCodeGroup` auto-generates the code from the name in create mode
only (`disableAutoGenerateCode` in edit).
- Create: on success the drawer closes, navigates to the new product's
details and toasts.
- **Create more** toggle (footer left slot, create mode only): the
drawer stays open, the form resets with a fade-in + scroll-to-top + Name
refocus, and the success toast embeds a router link to the created
product (slug-prefixed via `prependOrgSlug`, quotes escaped).
- Edit (`openDrawer(product)`): prefilled values, "Save edits", code
locked when `attachedToPlanOrSubscription`, cleared optional fields sent
as `null` so they actually clear.
- Duplicate code (`ValueAlreadyExist`): inline error under the Code
input, drawer stays open, typing clears it.

### Product details page

- New route `/product-catalog/products/:productId/:tab` (Overview +
Product items / Plans / Activity logs placeholder tabs; activity logs
gated premium + `auditLogsView`).
- Breadcrumb with a link back to the catalog and a static grey "Product"
crumb (new pathless `BreadcrumbItem` support).
- Actions dropdown: Edit (`productsUpdate`) reopens the drawer, Delete
(`productsDelete`) opens a danger dialog that destroys, evicts from the
Apollo cache (no post-delete 404 flash) and navigates back to the list.
- Overview tab: product information grid with copyable code and an
inline Edit entry point.
- Products list rows now link to the details overview, and each row
exposes Edit (opens the drawer prefilled) and Delete (confirm dialog)
actions, permission-gated with a composed tooltip.
- Products list layout is inset to the page gutter (padded wrapper +
`containerSize` 4 + non-sticky pager, same as the customer subscriptions
tab) so row dividers stop at the gutter per the design.

### Shared groundwork

- `src/components/drawers/createMore/`: reusable create-more system for
the upcoming product item / item filter drawers.
- Duplicate-code helpers hoisted to `~/core/form/existingCodeError.ts`
(charge drawers keep their historical import via re-export).
- `ToastContainer` moved inside `BrowserRouter`: toast messages can now
embed router links. Toasts fired from the devtools `MemoryRouter` still
render (global `toastsVar`) and navigate the main app router, which is
the intent.
- New `fade-in-right` tailwind animation; `BASE_DRAWER_CONTENT_TEST_ID`
moved to `drawers/const.ts` so drawer bodies can target the scroll
container without pulling BaseDrawer's `import.meta` chain into Jest.

## Notes

- `MainHeader.Configure` gets a `snapshotKey` covering
`description`/`invoiceDisplayName`/`attachedToPlanOrSubscription`: the
config snapshot strips the action closures, so without it a
description-only edit left the header Edit action holding the stale
product and re-saving reverted the edit (same pitfall documented in
`SubscriptionDetails`).
- Known edge case left open on purpose (product decision): clicking the
create-more toast link while the next product's form is dirty navigates
away and discards the typed data without the unsaved-changes prompt.
- 9 test suites (66 tests) cover the drawer flows, create-more system,
details page, delete dialog, breadcrumb and toast container.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Copilot AI review requested due to automatic review settings July 23, 2026 20:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces the first slice of the “Products & rate cards” initiative by adding a new Catalog & pricing section in the left sidebar, wiring new catalog-domain routes/pages, and extending permissions, filters, and activity-log handling to support new catalog resources.

Changes:

  • Add catalog routing + sidebar navigation section (feature-flagged) with placeholder pages/tabs and details shells.
  • Add product/product-item/product-item-filter list UI scaffolding (tables, filters, actions) plus delete dialogs and supporting form helpers.
  • Update GraphQL-generated surface area consumers (permissions + activity logs) and Apollo cache policies for new paginated root fields.

Reviewed changes

Copilot reviewed 104 out of 105 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/pages/settings/teamAndSecurity/roles/common/tests/snapshots/permissionsConst.test.ts.snap Snapshot updated for new permissions
src/pages/catalog/useProductItemTableColumns.tsx Product-item table column definitions
src/pages/catalog/useProductItemTableActions.tsx Product-item row actions + links
src/pages/catalog/useProductItemFilterTableColumns.tsx Product-item-filter table column definitions
src/pages/catalog/useProductItemFilterTableActions.tsx Product-item-filter row actions + links
src/pages/catalog/RateCardsList.tsx Rate cards placeholder panel
src/pages/catalog/ProductItemsList.tsx Product items list query/table
src/pages/catalog/ProductItemFiltersList.tsx Product item filters list query/table
src/pages/catalog/ProductCatalog.tsx Catalog tabs + create dropdown
src/pages/catalog/Plans.tsx Plans placeholder page
src/pages/catalog/drawers/productItemFilter/ProductItemFilterValuesEditor.tsx Multi-value editor for item-filter values
src/pages/catalog/drawers/productItemFilter/constants.ts Item-filter drawer schema/defaults
src/pages/catalog/drawers/productItemFilter/tests/ProductItemFilterValuesEditor.test.tsx Tests for values editor behavior
src/pages/catalog/drawers/productItem/constants.ts Product-item drawer defaults/types
src/pages/catalog/drawers/product/ProductDrawerContent.tsx Product drawer body + create-more reset behavior
src/pages/catalog/drawers/product/constants.ts Product drawer defaults/types
src/pages/catalog/dialogs/useDeleteProductItemFilterDialog.tsx Delete dialog + cache eviction for item-filters
src/pages/catalog/dialogs/useDeleteProductItemDialog.tsx Delete dialog + cache eviction for product-items
src/pages/catalog/dialogs/useDeleteProductDialog.tsx Delete dialog + cache eviction for products
src/pages/catalog/dialogs/tests/useDeleteProductItemFilterDialog.test.tsx Integration-ish test of centralized dialog
src/pages/catalog/dialogs/tests/useDeleteProductItemDialog.test.tsx Hook test for delete product-item dialog
src/pages/catalog/dialogs/tests/useDeleteProductDialog.test.tsx Hook test for delete product dialog
src/pages/catalog/details/ProductItemFilterDetailsOverview.tsx Item-filter details overview section
src/pages/catalog/details/ProductItemFilterActivityLogs.tsx Item-filter activity logs table
src/pages/catalog/details/ProductItemDetailsOverview.tsx Product-item details overview section
src/pages/catalog/details/ProductItemDetails.tsx Product-item details page + tabs
src/pages/catalog/details/ProductDetailsProductItems.tsx Product details “product items” preview list
src/pages/catalog/details/ProductDetailsOverview.tsx Product details overview section
src/pages/catalog/details/ProductDetails.tsx Product details page + tabs
src/pages/catalog/details/tests/ProductItemDetailsOverview.test.tsx Tests for item overview rendering/actions
src/pages/catalog/details/tests/ProductItemDetails.test.tsx Tests for item details header/tabs/actions
src/pages/catalog/details/tests/ProductDetailsOverview.test.tsx Tests for product overview rendering/actions
src/pages/catalog/details/tests/ProductDetails.test.tsx Tests for product details header/tabs/actions
src/pages/catalog/tests/useProductItemTableColumns.test.tsx Tests for item columns hook
src/pages/catalog/tests/useProductItemFilterTableColumns.test.tsx Tests for item-filter columns hook
src/pages/catalog/tests/useProductItemFilterTableActions.test.tsx Tests for item-filter actions hook
src/pages/catalog/tests/RateCardsList.test.tsx Rate cards placeholder test
src/pages/catalog/tests/ProductCatalog.test.tsx Catalog tabs + create dropdown tests
src/pages/catalog/tests/Plans.test.tsx Plans placeholder test
src/layouts/MainNavLayout/MainNavMenuSections.tsx Add “Catalog & pricing” nav section
src/layouts/MainNavLayout/tests/MainNavMenuSections.test.tsx Tests for new nav section ordering/visibility
src/hooks/usePermissions.ts Add catalog permissions to permissions query
src/hooks/activityLogs/useActivityLogsInformation.ts Map new catalog activity/resource types
src/hooks/activityLogs/tests/useActivityLogsInformation.test.ts Tests for new activity/resource mappings
src/core/router/index.tsx Register and re-export catalog routes
src/core/router/CatalogRoutes.tsx New catalog domain routes/constants
src/core/form/existingCodeError.ts Shared “duplicate code” form helper
src/core/form/tests/existingCodeError.test.tsx Tests for duplicate-code helper
src/core/constants/tabsOptions.ts Add catalog/detail tab enums
src/core/constants/filters.ts Add catalog list filter prefixes
src/core/apolloClient/cache.ts Add single-page policies for new lists
src/components/plans/drawers/common/chargeCode.ts Re-export duplicate-code helpers from core
src/components/MainHeader/types.ts BreadcrumbItem.path made optional
src/components/MainHeader/Breadcrumb.tsx Support static (pathless) breadcrumb items
src/components/MainHeader/tests/Breadcrumb.test.tsx Tests for pathless breadcrumb behavior
src/components/form/NameAndCodeGroup/NameAndCodeGroup.tsx Auto-clear duplicate-code error on edit
src/components/form/NameAndCodeGroup/tests/NameAndCodeGroup.test.tsx Test for clearing duplicate-code error
src/components/drawers/createMore/useCreateMore.tsx New reusable “create more” toggle/reset signal
src/components/drawers/createMore/CreateMoreControl.tsx UI control for “create more” toggle
src/components/drawers/createMore/tests/useCreateMore.test.tsx Tests for hook + reset iteration
src/components/drawers/createMore/tests/CreateMoreControl.test.tsx Tests for toggle control
src/components/drawers/const.ts Export drawer content selectors/test ids
src/components/drawers/BaseDrawer.tsx Apply content attr + re-export test id
src/components/developers/activityLogs/ActivityLogDetails.tsx Add id selection for new union members
src/components/designSystem/Toasts/tests/ToastContainer.test.tsx ToastContainer behavior tests
src/components/designSystem/Filters/utils.ts Add catalog filter formatting + display
src/components/designSystem/Filters/types.ts Add catalog filter enums + sentinels
src/components/designSystem/Filters/FiltersPanelItemTypeSwitch.tsx Wire new filter UI components
src/components/designSystem/Filters/filtersElements/utils.ts Add labeled multi-filter parsing helper
src/components/designSystem/Filters/filtersElements/FiltersItemProductItemType.tsx Filter UI: product-item type
src/components/designSystem/Filters/filtersElements/FiltersItemProductItemProduct.tsx Filter UI: product for product-items
src/components/designSystem/Filters/filtersElements/FiltersItemProductItemFilterProductItem.tsx Filter UI: product item for item-filters
src/components/designSystem/Filters/filtersElements/FiltersItemProductItemFilterProduct.tsx UI-only filter: product for item-filters
src/components/designSystem/Filters/filtersElements/tests/utils.test.ts Tests for new labeled parser
src/components/designSystem/Filters/filtersElements/tests/FiltersItemProductItemType.test.tsx Tests for item-type filter UI
src/components/designSystem/Filters/filtersElements/tests/FiltersItemProductItemProduct.test.tsx Tests for product filter UI
src/components/designSystem/Filters/filtersElements/tests/FiltersItemProductItemFilterProductItem.test.tsx Tests for product-item filter UI
src/components/designSystem/Filters/filtersElements/tests/FiltersItemProductItemFilterProduct.test.tsx Tests for UI-only product filter
src/App.tsx Move ToastContainer inside BrowserRouter
packages/configs/tailwind.config.ts Add fade-in-right animation
.agents/skills/migrate-formik-to-tanstack/SKILL.md Document duplicate-code error handling
.agents/docs/typescript-conventions.md Guidance on extracting render logic

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/drawers/createMore/useCreateMore.tsx
Comment thread src/core/router/CatalogRoutes.tsx Outdated
Copilot AI review requested due to automatic review settings July 24, 2026 15:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 104 out of 105 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (5)

src/core/router/CatalogRoutes.tsx:18

  • PR description says the Plans entry should route to /plan-catalog, but this route constant is set to /plan-pricing, so the sidebar link and route won't match the intended URL.
    src/core/router/CatalogRoutes.tsx:31
  • This route should be gated by the ProductCatalog feature flag (same as the nav), otherwise users can access it directly even when the flag is disabled.
    src/core/router/CatalogRoutes.tsx:37
  • This route should be gated by the ProductCatalog feature flag (same as the nav), otherwise users can access it directly even when the flag is disabled.
    src/core/router/CatalogRoutes.tsx:43
  • This route should be gated by the ProductCatalog feature flag (same as the nav), otherwise users can access it directly even when the flag is disabled.
    src/core/router/CatalogRoutes.tsx:49
  • This route should be gated by the ProductCatalog feature flag (same as the nav), otherwise users can access it directly even when the flag is disabled.

Comment thread src/core/router/CatalogRoutes.tsx
Comment thread src/core/router/CatalogRoutes.tsx
Comment thread src/components/MainHeader/Breadcrumb.tsx
Comment thread src/pages/catalog/details/ProductItemDetailsOverview.tsx Outdated
Copilot AI review requested due to automatic review settings August 6, 2026 16:06
@ansmonjol
ansmonjol force-pushed the feat/catalog-pricing-nav branch from c86b86d to 62afd1f Compare August 6, 2026 16:06
@ansmonjol ansmonjol changed the title feat: add Catalog & pricing nav section with placeholder pages feat: Catalog & pricing - categories, products, filters, rate cards Aug 6, 2026
@ansmonjol ansmonjol changed the title feat: Catalog & pricing - categories, products, filters, rate cards feat: Catalog & pricing - product categories, products & filters Aug 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 104 out of 105 changed files in this pull request and generated no new comments.

Suppressed comments (5)

src/pages/catalog/tests/ProductCatalog.test.tsx:1

  • The Products drawer hook mock returns mockOpenCreateProductCategoryDrawer instead of mockOpenCreateProductDrawer, which will wire the create-product-item action to the wrong mock and can make the test (and intent) incorrect. Update the mock to return openDrawer: mockOpenCreateProductDrawer.
    src/pages/catalog/drawers/productFilter/ProductFilterValuesEditor.tsx:1
  • The component accepts a disabled prop, but chip deletion remains enabled even when disabled is true. This allows users to modify values while the editor is supposed to be read-only/disabled. Gate deletion by omitting onDelete (and/or delete affordance) when disabled is true, or pass a disabled prop to the Chip if supported.
    src/pages/catalog/drawers/productCategory/constants.ts:1
  • PRODUCT_FORM_ID / PRODUCT_FORM_DEFAULTS are defined under productCategory/ but named generically as 'PRODUCT', which is easy to confuse with the product-item drawer constants (PRODUCT_ITEM_FORM_ID, etc.). Renaming these to PRODUCT_CATEGORY_FORM_ID / PRODUCT_CATEGORY_FORM_DEFAULTS would make call sites self-explanatory and reduce accidental cross-usage.
    src/components/MainHeader/Breadcrumb.tsx:61
  • Using item.path ?? item.label as a React key can collide when multiple breadcrumb items share the same label and have no path (e.g., repeated static segments), causing incorrect reconciliation. Prefer a guaranteed-unique key such as incorporating the index (e.g. ${index}-${item.path ?? item.label}) or a dedicated id.
      {items.map((item, index) => {
        return (
          <Fragment key={item.path ?? item.label}>
            {index > 0 && (
              <Typography variant="captionHl" color="primary600" className="shrink-0">
                /
              </Typography>
            )}
            <BreadcrumbItemContent item={item} />
          </Fragment>
        )
      })}

src/components/Filters/graphql/filtersElements/FiltersItemProductType.tsx:22

  • The default branch returns an empty translation key. If ProductTypeEnum gains new values, the dropdown will render blank/invalid options. Consider switching to an explicit mapping object (Record) and filtering out unmapped values (or providing a safe fallback label) so unknown enum values don't silently degrade the UI.
const productTypeMapping = (productType: ProductTypeEnum): string => {
  switch (productType) {
    case ProductTypeEnum.Fixed:
      return 'text_1783980718113ritmy7z94je'
    case ProductTypeEnum.Usage:
      return 'text_17839807181133l3z83156s6'
    default:
      return ''
  }
}

3-level rename to match backend: ProductItem->Product, Product->ProductCategory, ProductItemFilter->ProductFilter (+ camelCase, plurals, permissions, gql, files/dirs). Routes/URLs migrated too: path segments, route consts, tab slugs. ProductCatalog feature name preserved; filter-query prefixes (pit/pif) kept as opaque codes. graphql.tsx regenerated via codegen.
Tabs/menu/drawers/toasts to product category > product > product filter; fix interpolation placeholders; add missing product-filter drawer title+description (2 new keys); clean mangled comments.
Copilot AI review requested due to automatic review settings August 7, 2026 08:01
@ansmonjol
ansmonjol force-pushed the feat/catalog-pricing-nav branch from 62afd1f to 0713b1f Compare August 7, 2026 08:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 104 out of 105 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/core/router/CatalogRoutes.tsx:50

  • MainNavMenuSections hides the Catalog section when FeatureFlagEnum.ProductCatalog is off, but these routes are not feature-flagged. Because useLocationHistory enforces routeConfig.featureFlag, direct navigation to /product-catalog/* or /plan-pricing would still be allowed when the feature flag is disabled. Add featureFlag: FeatureFlagEnum.ProductCatalog to the relevant catalogRoutes entries (including details routes and plan-pricing) to make the gating consistent.
    src/pages/catalog/ProductCatalog.tsx:32
  • useLocation() returns a slug-aware location where pathname still includes /:organizationSlug, while the tab route constants are slug-unaware. This pathname === PRODUCT_CATALOG_ROUTE check will not match on real org-scoped URLs (e.g. /acme/product-catalog), so the base-route redirect may never run. Use strippedPathname (or alias it) for the comparison.

Copilot AI review requested due to automatic review settings August 7, 2026 08:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 104 out of 105 changed files in this pull request and generated no new comments.

Suppressed comments (6)

src/core/router/CatalogRoutes.tsx:2

  • Catalog pages are hidden behind the ProductCatalog feature flag in the main nav, but the routes themselves aren’t feature-flagged. That allows deep-link access when the flag is off. Consider importing FeatureFlagEnum here so each catalog route can add featureFlag: FeatureFlagEnum.ProductCatalog (same pattern as QuotesRoutes).
    src/core/router/CatalogRoutes.tsx:26
  • This route should be gated by the ProductCatalog feature flag (to match the main nav gating) so users can’t deep-link into catalog details when the flag is off.
    src/core/router/CatalogRoutes.tsx:32
  • This route should be gated by the ProductCatalog feature flag (to match the main nav gating) so users can’t deep-link into catalog details when the flag is off.
    src/core/router/CatalogRoutes.tsx:38
  • This route should be gated by the ProductCatalog feature flag (to match the main nav gating) so users can’t deep-link into catalog details when the flag is off.
    src/core/router/CatalogRoutes.tsx:44
  • This route should be gated by the ProductCatalog feature flag (to match the main nav gating) so users can’t deep-link into the catalog when the flag is off.
    src/core/router/CatalogRoutes.tsx:50
  • If “Plan pricing” is part of the Catalog & pricing feature-flagged area (it’s currently hidden in the nav when ProductCatalog is off), this route should also be feature-flagged to prevent deep-link access while the flag is off.

@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
8.2% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants