Skip to content

feat: add add-on types pages, dialog, actions, reducers, tests#999

Open
tomrndom wants to merge 2 commits into
masterfrom
feature/add-on-types-crud
Open

feat: add add-on types pages, dialog, actions, reducers, tests#999
tomrndom wants to merge 2 commits into
masterfrom
feature/add-on-types-crud

Conversation

@tomrndom

@tomrndom tomrndom commented Jun 30, 2026

Copy link
Copy Markdown

ref: https://app.clickup.com/t/9014802374/86bamaxc8

Signed-off-by: Tomás Castillo tcastilloboireau@gmail.com

Summary by CodeRabbit

  • New Features
    • Added an “Add-On Types” entry to the sponsor inventory navigation and a dedicated, permission-protected page.
    • Users can view, search, paginate, add, edit, and delete Add-On Types from a list page with a validated modal dialog.
  • Bug Fixes
    • Improved reliability of loading/refresh behavior so the UI stops loading and reloads the list appropriately after successful or failed operations.
  • Tests
    • Added Jest coverage for add-on types actions, reducers, and dialog/list page behavior.

…yout

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0e711df-53eb-44ca-b4d6-9fa5bddae440

📥 Commits

Reviewing files that changed from the base of the PR and between 929c5b5 and ed099f4.

📒 Files selected for processing (4)
  • src/pages/sponsors-global/add-on-types/add-on-types-list-page.js
  • src/reducers/__tests__/add-on-types-reducers.test.js
  • src/reducers/sponsors_inventory/add-on-type-reducer.js
  • src/reducers/sponsors_inventory/add-on-types-list-reducer.js

📝 Walkthrough

Walkthrough

Adds an Add-On Types CRUD feature to the sponsors inventory section: Redux actions and reducers, a dialog and list page, app routing and menu wiring, translation strings, and Jest coverage for the new flow.

Changes

Add-On Types CRUD Feature

Layer / File(s) Summary
Action constants, thunks, and reducers
src/actions/add-on-types-actions.js, src/reducers/sponsors_inventory/add-on-type-reducer.js, src/reducers/sponsors_inventory/add-on-types-list-reducer.js, src/store.js
Defines add-on type action constants and thunk creators, adds item and list reducers with request/receive/delete handling, and registers both slices in the Redux store.
AddOnTypesDialog and AddOnTypesListPage
src/pages/sponsors-global/add-on-types/add-on-types-dialog.js, src/pages/sponsors-global/add-on-types/add-on-types-list-page.js
Adds the Formik/Yup add-edit dialog and the connected list page with search, pagination, sort, add/edit/delete actions, and conditional dialog rendering.
Routing, menu, and i18n
src/layouts/add-on-types-layout.js, src/layouts/primary-layout.js, src/components/menu/menu-definition.js, src/i18n/en.json
Adds the add-on types layout with breadcrumb and access control, mounts it at /app/add-on-types, and adds the matching menu and translation entries.
Tests
src/actions/__tests__/add-on-types-actions.test.js, src/reducers/__tests__/add-on-types-reducers.test.js, src/pages/sponsors-global/add-on-types/__tests__/*
Covers thunk dispatch paths, reducer state transitions, dialog submit/close behavior, and list-page mount and CRUD interactions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • smarcet

Poem

🐇 Hop hop, a new list is in sight,
Add-On Types dancing through day and night.
Thunks fetch, reducers store, the dialog saves true,
This bunny says “nice work” with a carrot or two.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change by naming the new add-on types pages, dialog, actions, reducers, and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/add-on-types-crud

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

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

Inline comments:
In `@src/actions/add-on-types-actions.js`:
- Around line 176-182: The create payload normalization is leaving the
client-side id in place, so new records can be sent with an id of 0. Update
normalizeEntity to also strip id alongside created and last_edited, and ensure
the POST flow that uses normalizeEntity in add-on-types-actions builds create
payloads without any client-generated identifier.

In `@src/components/menu/menu-definition.js`:
- Around line 56-58: The Add-on Types submenu item is missing the accessRoute
restriction, so it can appear for users who only have access to
sponsors_inventory. Update the menuDefinition entry in the add-on types submenu
to include accessRoute set to add-on-types, matching the page’s permission and
the SubMenuItem filtering behavior.

In `@src/pages/sponsors-global/add-on-types/add-on-types-dialog.js`:
- Around line 35-54: AddOnTypesDialog is receiving reducer-backed save errors
from AddOnTypesListPage but never declares or uses an errors prop, so failed
saves are not surfaced in Formik. Update AddOnTypesDialog to accept the errors
prop alongside entity/onSave/onClose, and wire those server-side field errors
into the form state in the formik setup so they display on the matching fields
after a rejected save. Make sure the handling is applied in the AddOnTypesDialog
component and any related submit/error flow that currently swallows onSave
rejections.

In `@src/pages/sponsors-global/add-on-types/add-on-types-list-page.js`:
- Around line 91-94: The add-on type save flow currently waits for the follow-up
reload before resolving, which can keep the dialog open even after a successful
save. Update handleAddOnTypeSave in addOnTypesListPage so it resolves
immediately after saveAddOnType succeeds, and trigger getAddOnTypes(term,
DEFAULT_CURRENT_PAGE, perPage, order, orderDir) separately without blocking the
dialog close; keep the refresh logic associated with the existing save flow so
the list still updates, but do not chain it into the save promise.
- Around line 170-186: The delete confirmation text in MuiTable is missing the
add-on type name because deleteDialogBody receives the resolved display name
from getName, not from the onDelete payload. Update the MuiTable usage in
add-on-types-list-page by passing the appropriate getName prop alongside
deleteDialogBody so the table can resolve the item name before showing the
delete warning.

In `@src/reducers/sponsors_inventory/add-on-types-list-reducer.js`:
- Around line 41-65: The reducer in add-on-types-list-reducer.js is keeping
addOnTypes when currentPage changes, which lets stale rows remain visible after
handleSearch resets the page back to 1. Update the branch in the addOnTypes list
reducer so it also clears addOnTypes when the action is driven by a search term
change, using the existing state update logic around currentPage, order, and
orderDir to distinguish page-only navigation from search/filter resets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ea29459-b89b-4c12-86c8-1f346bbf4ded

📥 Commits

Reviewing files that changed from the base of the PR and between 2fd5000 and 929c5b5.

📒 Files selected for processing (14)
  • src/actions/__tests__/add-on-types-actions.test.js
  • src/actions/add-on-types-actions.js
  • src/components/menu/menu-definition.js
  • src/i18n/en.json
  • src/layouts/add-on-types-layout.js
  • src/layouts/primary-layout.js
  • src/pages/sponsors-global/add-on-types/__tests__/add-on-types-dialog.test.js
  • src/pages/sponsors-global/add-on-types/__tests__/add-on-types-list-page.test.js
  • src/pages/sponsors-global/add-on-types/add-on-types-dialog.js
  • src/pages/sponsors-global/add-on-types/add-on-types-list-page.js
  • src/reducers/__tests__/add-on-types-reducers.test.js
  • src/reducers/sponsors_inventory/add-on-type-reducer.js
  • src/reducers/sponsors_inventory/add-on-types-list-reducer.js
  • src/store.js

Comment thread src/actions/add-on-types-actions.js
Comment thread src/components/menu/menu-definition.js
Comment thread src/pages/sponsors-global/add-on-types/add-on-types-dialog.js
Comment thread src/pages/sponsors-global/add-on-types/add-on-types-list-page.js
Comment thread src/pages/sponsors-global/add-on-types/add-on-types-list-page.js
Comment thread src/reducers/sponsors_inventory/add-on-types-list-reducer.js Outdated
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

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 adds a new “Add-On Types” feature to the sponsors inventory area, including Redux state management, routing/navigation, a list page with CRUD dialog, and accompanying Jest tests.

Changes:

  • Added Add-On Types Redux actions + reducers and registered them in the global store.
  • Introduced Add-On Types list page + modal dialog, and wired routing/menu navigation with permission restriction.
  • Added Jest tests covering actions, reducers, and the new page/dialog behavior.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/store.js Registers the new Add-On Types reducers in the persisted Redux store.
src/reducers/sponsors_inventory/add-on-types-list-reducer.js Adds list reducer for pagination/sorting/search + delete behavior.
src/reducers/sponsors_inventory/add-on-type-reducer.js Adds single-entity reducer for the add/edit dialog form state.
src/reducers/tests/add-on-types-reducers.test.js Unit tests for the two new reducers.
src/actions/add-on-types-actions.js Thunk actions for fetching/listing/saving/deleting Add-On Types with snackbar feedback.
src/actions/tests/add-on-types-actions.test.js Unit tests for the new actions and loading lifecycle.
src/pages/sponsors-global/add-on-types/add-on-types-list-page.js New list page wiring table actions to Redux thunks and opening the dialog.
src/pages/sponsors-global/add-on-types/add-on-types-dialog.js New validated Formik/MUI modal dialog for add/edit.
src/pages/sponsors-global/add-on-types/tests/add-on-types-list-page.test.js Component tests for list page behavior (mount fetch, open/edit/delete/save flows).
src/pages/sponsors-global/add-on-types/tests/add-on-types-dialog.test.js Component tests for dialog title, submit/disable behavior, and close behavior.
src/layouts/primary-layout.js Adds a route entry for /app/add-on-types.
src/layouts/add-on-types-layout.js New layout with breadcrumb + Restrict wrapper for the feature route.
src/components/menu/menu-definition.js Adds the new navigation item under the global sponsors inventory menu.
src/i18n/en.json Adds menu + feature-specific i18n strings for list/dialog labels and messages.

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

Comment on lines +40 to +44
term = null,
page = 1,
perPage = DEFAULT_PER_PAGE,
order = "order",
orderDir = 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Confirmed - checked every real call site of getAddOnTypes( in the app (grep across src, excluding tests): all 7 pass order explicitly, so this default is currently unreachable in production code (only exercised by an isolated action-level unit test calling getAddOnTypes() with no args). Not an active bug today, but worth fixing the default to "name" to match the reducer's DEFAULT_STATE.order and avoid a footgun for future callers.

Comment thread src/store.js
Comment on lines +348 to +349
currentAddonTypesListState: addOnTypesListReducer,
currentAddOnTypeState: addOnTypeReducer,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Confirmed, and purely cosmetic - verified it's self-consistent within this PR (mapStateToProps in the list page destructures the matching currentAddonTypesListState key), so no functional bug today. Still worth the rename for consistency with currentAddOnTypeState and the rest of the codebase's AddOn casing.

Comment on lines +90 to +93
const handleAddOnTypeSave = (item) =>
saveAddOnType(item).then(() =>
getAddOnTypes(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir)
);

@smarcet smarcet Jul 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomrndom
Confirmed - and this is the same root cause flagged independently in two other places:

  • CodeRabbit already raised this exact issue on this same line back on 2026-06-30 (feat: add add-on types pages, dialog, actions, reducers, tests #999 (comment)). That thread is marked resolved, but I checked the current code and it's unchanged - looks like it was dismissed rather than actually fixed.
  • I flagged the same pattern independently at line 90 (handleAddOnTypeSave) in this same file, plus a related issue there (page reset on edit).

Three independent reviews converging on the same bug (and one prior instance of it being waved off without a fix) is a strong signal this should get fixed before merge.

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Deep review findings — see inline comments.

}
};

const handleDelete = (addOnTypeId) =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomrndom handleDelete returns the chained promise from getAddOnTypes(...) without a .catch(). If the delete succeeds but the follow-up list refresh fails (transient network/server error), the rejection propagates back through this promise, and the failure looks identical to the delete itself failing - even though the delete already went through.

Suggested fix - don't return the refresh call, and let it fail independently:

const handleDelete = (addOnTypeId) =>
  deleteAddOnType(addOnTypeId).then(() => {
    getAddOnTypes(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir).catch(() => {});
  });

This matches the fire-and-forget refresh convention used elsewhere in the codebase (e.g. page-template-list-page.js's handleDelete).

getAddOnTypes(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir)
);

const handleAddOnTypeSave = (item) =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomrndom Two things here:

  1. Same issue as handleDelete above - this returns the getAddOnTypes(...) promise without a .catch(). If the save succeeds but the refresh fails, the rejection reaches AddOnTypesDialog's .catch(() => {}), the dialog stays open, and the user sees a generic error even though the save already succeeded - likely prompting a retry that creates a duplicate record (the dialog still holds the unsaved values, and for a new entity id is still 0).
  2. This handler is shared by create and edit and always reloads at DEFAULT_CURRENT_PAGE. For an edit, that bounces the admin back to page 1 away from the row they were just editing on a later page.

Suggested fix:

const handleAddOnTypeSave = (item) =>
  saveAddOnType(item).then(() => {
    const page = item.id ? currentPage : DEFAULT_CURRENT_PAGE;
    getAddOnTypes(term, page, perPage, order, orderDir).catch(() => {});
  });

</div>
);

export default Restrict(withRouter(AddOnTypesLayout), "add-on-types");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomrndom Restrict(withRouter(AddOnTypesLayout), "add-on-types") guards this route with the "add-on-types" key, but that key doesn't exist in src/access-routes.yml. Member.hasAccess() returns true immediately when the key is missing - before it even checks the member - so any authenticated user can reach this page and create/edit/delete Add-On Types regardless of group.

(page-template-layout.js, form-template-layout.js, and inventory-item-layout.js have the same gap already, so this isn't new to this PR — but worth closing here too, e.g. by adding an add-on-types entry to access-routes.yml or reusing the existing inventory key.)

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomrndom please review

@@ -0,0 +1,170 @@
/**

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomrndom this file is just testing standard redux procedures, what is the point of this ? Please remove this file entirely and next time ask AI for specific user cases that you want to test against, not just "make tests" because it will test everything, even nonsense

strict
exact
path={`${match.url}`}
component={AddOnTypesListPage}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why do we need a layout file here if there is only 1 view ?

<Route path="/app/inventory" component={InventoryItemLayout} />
<Route path="/app/form-templates" component={FormTemplateLayout} />
<Route path="/app/page-templates" component={PageTemplateLayout} />
<Route path="/app/add-on-types" component={AddOnTypesLayout} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

looks like this should be strict and just go to AddOnTypesListPage

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