-
Notifications
You must be signed in to change notification settings - Fork 4
feat: add new page and route for sponsor customized form managed items #698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
2105784 to
fc25ec6
Compare
There was a problem hiding this comment.
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 page and routing system for managing sponsor customized form items. It introduces functionality to view, add, edit, archive, and delete items associated with sponsor forms, including the ability to add items from an existing inventory.
- Added new route
/app/summits/:summit_id/sponsors/:sponsor_id/sponsor-forms/:form_id/itemsfor managing form items - Implemented editable table with inline validation for price fields
- Added ability to clone items from inventory into sponsor forms
- Enhanced UI with Material-UI components for better user experience
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 19 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/constants.js | Added FOUR constant for tab index reference |
| src/store.js | Registered new reducer for sponsor customized form items list state |
| src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js | New reducer to manage form items list state, pagination, and CRUD operations |
| src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js | Updated popup to use price field component, fixed quantity field defaults, added dialog title translation |
| src/pages/sponsors/sponsor-forms-tab/index.js | Added navigation handler to route to manage items page |
| src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js | New component implementing items management with search, pagination, inline editing, and archiving |
| src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js | New popup component for selecting and adding items from inventory |
| src/pages/sponsors/edit-sponsor-page.js | Added conditional rendering for items route and tab navigation handling |
| src/layouts/sponsor-id-layout.js | Added new route path for form items management, improved code style |
| src/i18n/en.json | Added translations for form items management UI and fixed typo in "Standard rate" |
| src/components/mui/editable-table/mui-table-editable.js | Enhanced table with validation support for editable cells |
| src/actions/sponsor-forms-actions.js | Added actions for fetching, saving, deleting, archiving form items and cloning from inventory |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| }; | ||
|
|
||
| if (fieldType.id) { | ||
| if (fieldType.id && onMetaFieldTypeDeleted) { |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition checking for onMetaFieldTypeDeleted should be more specific. Adding a check for onMetaFieldTypeDeleted prevents calling an undefined function, but there's no feedback to the user or developer when this callback is missing. Consider logging a warning or ensuring this prop is always provided if fieldType.id exists.
| <FormControlLabel | ||
| control={ | ||
| <Checkbox | ||
| value={hideArchived} |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'hideArchived' is misleading in this context. The checkbox's 'value' prop should be 'checked' instead. When using controlled checkboxes with FormControlLabel in Material-UI, the Checkbox component should use 'checked' prop, not 'value'.
| value={hideArchived} | |
| checked={hideArchived} |
| onClose: PropTypes.func.isRequired, | ||
| onSave: PropTypes.func.isRequired |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PropTypes definition is incomplete. It only validates onClose and onSave, but the component receives many more props from Redux (open, inventoryItems, term, order, perPage, orderDir, currentPage, totalInventoryItems, getInventoryItems) that should also have PropTypes defined for better type safety and documentation.
| onClose: PropTypes.func.isRequired, | |
| onSave: PropTypes.func.isRequired | |
| open: PropTypes.bool.isRequired, | |
| inventoryItems: PropTypes.array.isRequired, | |
| term: PropTypes.string, | |
| order: PropTypes.string, | |
| perPage: PropTypes.number, | |
| orderDir: PropTypes.string, | |
| currentPage: PropTypes.number, | |
| totalInventoryItems: PropTypes.number, | |
| onClose: PropTypes.func.isRequired, | |
| onSave: PropTypes.func.isRequired, | |
| getInventoryItems: PropTypes.func.isRequired |
src/i18n/en.json
Outdated
| "default_quantity": "Default Quantity", | ||
| "add_selected": "Add Selected Items", | ||
| "item_updated": "Form item updated successfully", | ||
| "item_created": "Form item {item} updated successfully", |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The translation key has a mismatch. The message says "updated successfully" but the key is "item_created". This should either be "created" instead of "updated" or the key should be changed to reflect that it's about updating.
| "item_created": "Form item {item} updated successfully", | |
| "item_created": "Form item {item} created successfully", |
| const DEFAULT_ITEM_ENTITY = { | ||
| code: "", | ||
| name: "", | ||
| description: "", | ||
| early_bird_rate: 0, | ||
| standard_rate: 0, | ||
| onsite_rate: 0, | ||
| quantity_limit_per_show: 0, | ||
| quantity_limit_per_sponsor: 0, | ||
| default_quantity: 0, | ||
| images: [], |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The DEFAULT_ITEM_ENTITY object includes fields like 'early_bird_rate', 'standard_rate', 'onsite_rate' initialized to 0, but in the RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM action handler, these fields are converted using amountFromCents. This inconsistency could cause issues - the default values should either be in cents or the conversion should be consistent throughout.
| .test("max-decimals", T.translate("validation.two_decimals"), (value) => { | ||
| if (value === undefined || value === null) return true; | ||
| return /^\d+(\.\d{1,2})?$/.test(value.toString()); |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation schema test uses a regex that doesn't properly validate negative numbers with decimals. The pattern /^\d+(.\d{1,2})?$/ only matches positive numbers, but the previous validation allows negative numbers with the '-?' in the pattern. This inconsistency could cause valid negative decimal numbers to fail validation.
| import SponsorFormItemFromInventoryPopup from "./sponsor-form-item-from-inventory"; | ||
| import { parsePrice } from "../../../../../utils/currency"; | ||
| import { DEFAULT_CURRENT_PAGE } from "../../../../../utils/constants"; | ||
| // import FormTemplateDialog from "../../../../sponsors_inventory/popup/form-template-popup"; |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commented-out line suggests there's a placeholder or incomplete template feature. Either this should be implemented and uncommented, or the comment should be removed if it's not part of the current feature scope.
| // import FormTemplateDialog from "../../../../sponsors_inventory/popup/form-template-popup"; |
| const hasQuantities = | ||
| type === "Quantity" && (minQty != null || maxQty != null); |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation condition has a logic issue. The expression uses != instead of !== for null checks, which is inconsistent with JavaScript best practices and could lead to unexpected behavior since != performs type coercion. For example, 0 != null is false, which might not be the intended behavior when checking if minQty is set to 0.
src/actions/sponsor-forms-actions.js
Outdated
| }) | ||
| ); | ||
| }) | ||
| .catch(console.log) // need to catch promise reject |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handler is inconsistent. Line 1484 uses console.log to catch promise rejection, which silently swallows errors and makes debugging difficult. This should use a proper error handler like the snackbarErrorHandler used in other parts of the codebase.
| .catch(console.log) // need to catch promise reject | |
| .catch(snackbarErrorHandler) |
smarcet
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tomrndom please review coments left on tickets
...pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js
Outdated
Show resolved
Hide resolved
1a3472a to
4cbbe84
Compare
📝 WalkthroughWalkthroughAdds sponsor form item management: new action types and async CRUD/archival/clone flows, normalization helpers, a reducer and store registration, new management UI components and routes, i18n strings, and centralized Yup price validation. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser as EditSponsorPage
participant UI as SponsorFormsManageItems
participant Redux as Redux Store
participant API
User->>Browser: Navigate to /sponsor/:id/sponsor-forms/:form_id/items
Browser->>UI: render items management view
UI->>Redux: dispatch getSponsorCustomizedFormItems(formId,...)
Redux->>API: GET /sponsors/:id/forms/:form_id/items
API-->>Redux: items + pagination
Redux-->>UI: receive items
UI-->>User: display items table
User->>UI: Open inventory popup
UI->>Redux: dispatch getInventoryItems(...)
Redux->>API: GET /inventory/items
API-->>Redux: inventory items
Redux-->>UI: populate popup
User->>UI: Select items & Save
UI->>Redux: dispatch addSponsorManagedFormItems(formId,itemIds)
Redux->>API: POST /sponsors/:id/forms/:form_id/items/clone
API-->>Redux: cloned items
Redux-->>UI: refresh list
UI-->>User: show success
sequenceDiagram
participant User
participant UI as SponsorFormsManageItems
participant Dialog as Item Edit Dialog
participant Redux as Redux Store
participant API
User->>UI: Click edit on row
UI->>Redux: dispatch getSponsorFormManagedItem(formId,itemId)
Redux->>API: GET /sponsors/:id/forms/:form_id/items/:item_id
API-->>Redux: item details
Redux-->>Dialog: populate form
User->>Dialog: Update fields & Save
Dialog->>Redux: dispatch saveSponsorFormManagedItem(formId,entity)
Redux->>API: PUT or POST /sponsors/:id/forms/:form_id/items(/:item_id)
API-->>Redux: updated/created item
Redux-->>UI: refresh items list
UI-->>User: close dialog & notify
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/actions/sponsor-forms-actions.js (1)
25-36: Fix duplicateamountToCentsimports; choose a single source.The file imports
amountToCentstwice:import { amountToCents } from "openstack-uicore-foundation/lib/utils/money"; ... import { amountToCents } from "../utils/currency";This will fail to compile (
Identifier 'amountToCents' has already been declared) and it’s unclear which implementation you intend to use.Resolve by keeping only one import, e.g. favoring the uicore version consistently:
-import { amountToCents } from "openstack-uicore-foundation/lib/utils/money"; ... -import { amountToCents } from "../utils/currency"; +import { amountToCents } from "openstack-uicore-foundation/lib/utils/money";(or vice versa, but only one should remain).
🤖 Fix all issues with AI Agents
In @src/actions/sponsor-forms-actions.js:
- Around line 1443-1479: The .catch(snackbarErrorHandler) on the promise chain
is incorrect because snackbarErrorHandler is a thunk creator and is already
supplied to postRequest; either remove the redundant .catch entirely or replace
it with a dispatched thunk by changing it to .catch(err =>
dispatch(snackbarErrorHandler(err))); locate the promise returned by postRequest
in the anonymous async action (the function referencing postRequest,
SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED, and getSponsorCustomizedFormItems) and
apply one of these two fixes so errors are handled exactly once and the snackbar
thunk is properly dispatched.
In @src/i18n/en.json:
- Around line 2436-2453: The translation key "form_manage_items.item_created"
includes a "{item}" placeholder but callers use
T.translate("edit_sponsor.forms_tab.form_manage_items.item_created") without
passing an interpolation value, causing the literal "{item}" to appear; fix by
either removing the placeholder from the en.json value so it reads "Form item
created successfully" or update all call sites that use
T.translate("edit_sponsor.forms_tab.form_manage_items.item_created") (e.g., the
code that renders the success message) to pass a parameter object like { item:
entity.name } so interpolation works.
In @src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js:
- Line 84: The form schema has images: yup.array() (removed min(1)) but the UI
label in the SponsorInventoryPopup still renders an asterisk for a required
field; either restore the validation by changing images: yup.array().min(1, "At
least one image is required") in the same schema where images is declared, or
remove the required indicator from the label/JSX that renders the "Images" field
(remove the '*' or conditional required marker) so the UI matches images being
optional; update whichever branch you choose consistently in the
SponsorInventoryPopup component and the images schema.
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js:
- Around line 76-91: Handlers are incorrectly destructuring pagination/sort
fields from the reducer-provided items array (items) causing undefined values;
update handleManagedPageChange, handleManagedSort, and handleHideArchivedItens
to use the already-provided props/current variables (currentPage, perPage,
order, orderDir, term, formId, hideArchived) instead of pulling them from items,
and adjust getSponsorCustomizedFormItems calls to pass those props; also add
formId (and optionally getSponsorCustomizedFormItems) to the useEffect
dependency array so items reload when formId changes.
🧹 Nitpick comments (3)
src/layouts/sponsor-id-layout.js (1)
144-150: Consider adding a fallback route for unmatched paths.The nested
Switchlacks aNoMatchPagefallback route, which could lead to inconsistent behavior when paths don't match either of the two defined routes. While the parentSwitchhas a fallback at line 151, adding one here would provide clearer routing semantics.Suggested addition
<Switch> <Route exact path={match.url} component={EditSponsorPage} /> <Route path={`${match.url}/sponsor-forms/:form_id/items`} component={EditSponsorPage} /> + <Route component={NoMatchPage} /> </Switch>src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
30-49: Clone default item entity instead of storing shared reference (and consider meta_fields null‑safety).
DEFAULT_ITEM_ENTITYis stored directly inDEFAULT_STATE.currentItemand reused inRESET_SPONSOR_FORM_MANAGED_ITEM. Any in‑place mutation ofcurrentItem(especially nestedmeta_fields) will mutate the shared default object and leak into subsequent “new item” flows. Also,RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMassumesitem.meta_fieldsis always a defined array.To harden this reducer:
- Clone the default when putting it into state, and clone nested arrays/objects as needed.
- Guard
item.meta_fieldswith a fallback (item.meta_fields || []) before accessing.length.Example:
-const DEFAULT_STATE = { - ... - currentItem: DEFAULT_ITEM_ENTITY -}; +const DEFAULT_STATE = { + ... + currentItem: { ...DEFAULT_ITEM_ENTITY } +}; ... - case RESET_SPONSOR_FORM_MANAGED_ITEM: - return { ...state, currentItem: DEFAULT_ITEM_ENTITY }; + case RESET_SPONSOR_FORM_MANAGED_ITEM: + return { ...state, currentItem: { ...DEFAULT_ITEM_ENTITY } };and
- meta_fields: - item.meta_fields.length > 0 - ? item.meta_fields + meta_fields: + (item.meta_fields || []).length > 0 + ? item.meta_fields : [ { ... } ]Optionally, add a short comment on
SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDEDexplaining why it is intentionally a no‑op.Also applies to: 119-140, 190-194
src/actions/sponsor-forms-actions.js (1)
1351-1380: Normalize managed item rates with consistent source fields and minimal duplication.
normalizeManagedItemis broadly correct but a bit inconsistent in that it checksentity.*but writes intonormalizedEntity.*. That’s fine logically, but you could tighten it slightly and avoid surprises by consistently reading fromnormalizedEntityas well (after spreadingentity).Not required for correctness, but a more uniform pattern would be:
const normalizeManagedItem = (entity) => { const normalizedEntity = { ...entity }; - if (entity.early_bird_rate === "" || entity.early_bird_rate === undefined) + if (normalizedEntity.early_bird_rate === "" || normalizedEntity.early_bird_rate === undefined) delete normalizedEntity.early_bird_rate; else - normalizedEntity.early_bird_rate = amountToCents( - normalizedEntity.early_bird_rate - ); + normalizedEntity.early_bird_rate = amountToCents(normalizedEntity.early_bird_rate); // same for standard_rate and onsite_rate... };This keeps normalization self‑contained on the cloned object.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/actions/sponsor-forms-actions.jssrc/i18n/en.jsonsrc/layouts/sponsor-id-layout.jssrc/pages/sponsors/edit-sponsor-page.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.jssrc/pages/sponsors/sponsor-forms-tab/index.jssrc/pages/sponsors_inventory/popup/sponsor-inventory-popup.jssrc/reducers/sponsors/sponsor-customized-form-items-list-reducer.jssrc/store.jssrc/utils/constants.js
🧰 Additional context used
🧬 Code graph analysis (6)
src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (2)
src/actions/sponsor-forms-actions.js (18)
REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS(71-72)REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS(71-72)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS(69-70)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS(69-70)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM(73-74)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM(73-74)SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED(75-76)SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED(75-76)SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED(77-78)SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED(77-78)SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED(79-80)SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED(79-80)SPONSOR_FORM_MANAGED_ITEM_UPDATED(83-84)SPONSOR_FORM_MANAGED_ITEM_UPDATED(83-84)SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED(89-90)SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED(89-90)RESET_SPONSOR_FORM_MANAGED_ITEM(91-92)RESET_SPONSOR_FORM_MANAGED_ITEM(91-92)src/utils/constants.js (2)
DECIMAL_DIGITS(138-138)DECIMAL_DIGITS(138-138)
src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js (1)
src/components/mui/formik-inputs/mui-formik-pricefield.js (1)
MuiFormikPriceField(6-24)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js (5)
src/utils/constants.js (4)
DEFAULT_PER_PAGE(75-75)DEFAULT_PER_PAGE(75-75)DEFAULT_CURRENT_PAGE(73-73)DEFAULT_CURRENT_PAGE(73-73)src/components/mui/menu-button.js (1)
MenuButton(5-100)src/components/mui/search-input.js (1)
SearchInput(6-56)src/components/mui/table/mui-table.js (1)
MuiTable(31-293)src/pages/sponsors/sponsor-forms-tab/index.js (1)
mapStateToProps(362-364)
src/pages/sponsors/edit-sponsor-page.js (3)
src/utils/constants.js (2)
SPONSOR_TABS(229-238)SPONSOR_TABS(229-238)src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (1)
SponsorFormsManageItems(48-403)src/pages/sponsors/sponsor-forms-tab/index.js (1)
SponsorFormsTab(42-360)
src/layouts/sponsor-id-layout.js (1)
src/pages/sponsors/edit-sponsor-page.js (1)
EditSponsorPage(69-230)
src/actions/sponsor-forms-actions.js (8)
src/reducers/sponsors/sponsor-forms-list-reducer.js (1)
formId(153-153)src/utils/constants.js (6)
DEFAULT_CURRENT_PAGE(73-73)DEFAULT_CURRENT_PAGE(73-73)DEFAULT_PER_PAGE(75-75)DEFAULT_PER_PAGE(75-75)DEFAULT_ORDER_DIR(91-91)DEFAULT_ORDER_DIR(91-91)src/actions/form-template-item-actions.js (1)
normalizedEntity(177-177)src/actions/inventory-item-actions.js (1)
normalizedEntity(195-195)src/actions/form-template-actions.js (1)
normalizedEntity(168-168)src/actions/base-actions.js (2)
snackbarSuccessHandler(49-53)snackbarSuccessHandler(49-53)src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
itemId(148-148)src/reducers/sponsors/sponsor-form-items-list-reducer.js (1)
itemId(139-139)
🪛 Biome (2.1.2)
src/actions/sponsor-forms-actions.js
[error] 35-35: Shouldn't redeclare 'amountToCents'. Consider to delete it or rename it.
'amountToCents' is defined here:
(lint/suspicious/noRedeclare)
⏰ 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). (6)
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (6)
src/store.js (1)
167-167: LGTM! Redux integration follows existing patterns.The new reducer is properly imported and registered in the Redux store with a persisted state key, following the established naming conventions and patterns used by other reducers in the codebase.
Also applies to: 254-254
src/utils/constants.js (1)
229-238: LGTM! SPONSOR_TABS constant is properly defined.The SPONSOR_TABS object provides clear tab index mappings for sponsor page navigation and is consumed by the sponsor forms components as shown in the context.
src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js (3)
37-37: LGTM! Price field component properly integrated.Using
MuiFormikPriceFieldfor rate fields ensures consistent currency formatting with proper $ prefix and decimal validation (step: 0.01, min: 0). This aligns with the price handling patterns used elsewhere in the sponsor forms management flow.Also applies to: 318-323, 329-334, 340-345
197-203: Defensive check is appropriate.The check for
onMetaFieldTypeDeletedexistence before calling it prevents runtime errors when the callback is not provided. The fallback to local removal ensures the UI remains functional regardless of the callback's presence.
258-260: LGTM! Dynamic dialog title improves UX.The conditional rendering of "Edit Item" vs "New Item" based on
initialEntity.idprovides clear context to users about the current operation.src/pages/sponsors/sponsor-forms-tab/index.js (1)
44-44: Navigation implementation verified.The
historyprop is properly passed from the parent component (edit-sponsor-page.js) toSponsorFormsTab. The route construction for sponsor form item management is correctly implemented at lines 115–117 usinghistory.push(), enabling the expected user flow.
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/utils/yup.js (2)
53-66: Regex allows negative numbers butmin(0)rejects them.The regex
/^\$?-?\d+(\.\d+)?$/on line 63 permits a leading minus sign, yet line 66 enforces.min(0). This creates confusing validation feedback: a user entering-50passes the "valid-format" check only to fail with "Must be a positive number".Consider tightening the regex to disallow negatives since they are semantically invalid for rate fields:
Proposed fix
- return /^\$?-?\d+(\.\d+)?$/.test(originalValue); + return /^\$?\d+(\.\d+)?$/.test(originalValue);
67-69: Guard againstNaN.toString()producing"NaN".If
parseFloatreturnsNaN(e.g., from malformed input that slips past prior checks),value.toString()yields"NaN", which won't match the decimal regex and will incorrectly report a "Max 2 decimal places" error instead of "Must be a number".Proposed fix
.test("max-decimals", T.translate("validation.two_decimals"), (value) => { - if (value === undefined || value === null) return true; + if (value === undefined || value === null || Number.isNaN(value)) return true; return /^\d+(\.\d{1,2})?$/.test(value.toString()); });src/actions/sponsor-forms-actions.js (1)
1358-1387: Consider consolidating duplicate normalization logic.
normalizeManagedItemduplicates much of the logic fromnormalizeItem(lines 1213-1251). Both functions handlemeta_fields,images, and the three rate fields identically.Proposed refactor
Extract shared rate normalization into a helper:
const normalizeRates = (entity, normalizedEntity) => { const { early_bird_rate, standard_rate, onsite_rate } = entity; if (early_bird_rate === "" || early_bird_rate === undefined) delete normalizedEntity.early_bird_rate; else normalizedEntity.early_bird_rate = amountToCents(early_bird_rate); if (standard_rate === "" || standard_rate === undefined) delete normalizedEntity.standard_rate; else normalizedEntity.standard_rate = amountToCents(standard_rate); if (onsite_rate === "" || onsite_rate === undefined) delete normalizedEntity.onsite_rate; else normalizedEntity.onsite_rate = amountToCents(onsite_rate); };Then use it in both
normalizeItemandnormalizeManagedItem.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/actions/sponsor-forms-actions.jssrc/i18n/en.jsonsrc/pages/sponsors/sponsor-form-item-list-page/index.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.jssrc/pages/sponsors_inventory/popup/sponsor-inventory-popup.jssrc/reducers/sponsors/sponsor-customized-form-items-list-reducer.jssrc/utils/constants.jssrc/utils/yup.js
🚧 Files skipped from review as they are similar to previous changes (5)
- src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js
- src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js
- src/utils/constants.js
- src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js
- src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
⏰ 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). (6)
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (8)
src/pages/sponsors/sponsor-form-item-list-page/index.js (1)
93-97: LGTM!The cell edit handler correctly strips currency prefixes before updating. The use of the centralized
rateCellValidationfor rate columns improves consistency across the codebase.src/i18n/en.json (2)
2436-2455: LGTM!The new
form_manage_itemstranslation block provides comprehensive UI strings for the item management feature. The keys align well with the component usage patterns in the codebase.
2640-2641: LGTM!The addition of
new_itemandedit_itemkeys foredit_inventory_itemproperly supports the modal title variations.src/actions/sponsor-forms-actions.js (5)
68-92: LGTM!The new action constants are well-organized and follow the existing naming conventions in the file.
661-714: LGTM!The
getSponsorCustomizedFormItemsaction now correctly passes pagination/sort/filter metadata to the REQUEST action (line 710), ensuring reducer state stays synchronized with UI controls.
1289-1352: LGTM!The
saveSponsorFormManagedItemaction properly handles both create and update paths with consistent.finally()blocks to clear loading state regardless of success or failure.
1449-1485: LGTM!The
addSponsorManagedFormItemsaction correctly handles the cloning flow and properly uses.finally()for cleanup. The previous issue with redundant.catch(snackbarErrorHandler)has been resolved.
1487-1531: LGTM!The archive/unarchive actions for customized form items follow the established patterns and properly handle loading state cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @src/actions/sponsor-forms-actions.js:
- Around line 1361-1375: normalizeRates currently only checks for empty string
or undefined, so null values will be passed into amountToCents and may produce
incorrect results; update the checks for early_bird_rate, standard_rate, and
onsite_rate in normalizeRates to treat null the same as undefined/empty (e.g.,
use a nullish check like == null or explicit null comparison) and delete the
corresponding normalizedEntity property when the value is null, otherwise call
amountToCents as before (references: normalizeRates, amountToCents,
early_bird_rate, standard_rate, onsite_rate, normalizedEntity).
- Around line 1399-1405: The success snackbar uses the wrong translation key: it
calls T.translate("sponsor_forms.form_delete_success") which refers to form
deletion instead of item deletion; update the key to the correct one (e.g.,
"sponsor_forms.item_delete_success") in the dispatch call that invokes
snackbarSuccessHandler in src/actions/sponsor-forms-actions.js and ensure that
same key is added to the translations (src/i18n/en.json) with the proper message
so the snackbar displays the item deletion success text.
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js:
- Around line 141-151: Rename the misspelled handler function
handleHideArchivedItens to handleHideArchivedItems and update all
references/calls (including the one mentioned around the other usage at line
288) to the new name; specifically, rename the function declaration and any JSX
props or event handlers that pass handleHideArchivedItens so they point to
handleHideArchivedItems to keep names consistent.
🧹 Nitpick comments (6)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (4)
103-113: Consider resetting to page 1 when searching.Currently,
handleSearchpreservescurrentPagewhen the search term changes. This can cause confusing UX if the user is on page 5 and searches for something that has fewer results—they may see an empty page.🔎 Proposed fix
const handleSearch = (searchTerm) => { getSponsorCustomizedFormItems( formId, searchTerm, - currentPage, + DEFAULT_CURRENT_PAGE, perPage, order, orderDir, hideArchived ); };
242-259: MoveonClickhandler fromImageIcontoIconButtonfor better accessibility.The click handler is on the inner
ImageIconelement, but it should be on theIconButtonwrapper for a larger click target and proper keyboard accessibility.🔎 Proposed fix
render: (row) => row.images?.length > 0 ? ( <Tooltip title={row.images[0].file_url} placement="top" arrow> - <IconButton size="small"> - <ImageIcon - fontSize="small" - onClick={() => - window.open( - row.images[0].file_url, - "_blank", - "noopener,noreferrer" - ) - } - /> + <IconButton + size="small" + onClick={() => + window.open( + row.images[0].file_url, + "_blank", + "noopener,noreferrer" + ) + } + > + <ImageIcon fontSize="small" /> </IconButton> </Tooltip> ) : null
356-363: Redundantopenprop in conditionally rendered dialog.The
open={openPopup === "add_item"}prop is alwaystruesince the component is only rendered when that condition is met in the JSX conditional.🔎 Proposed fix
{openPopup === "add_item" && ( <SponsorInventoryDialog entity={currentInventoryItem} - open={openPopup === "add_item"} + open onSave={handleItemSave} onClose={handleClose} /> )}
157-164: Unhandled promise fromsaveSponsorFormManagedItem.The async action's promise is not awaited or chained, so any errors during cell edit will be silently ignored and no UI feedback will be provided to the user.
🔎 Proposed fix
const handleCellEdit = (rowId, column, value) => { const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); const tmpEntity = { id: rowId, [column]: valueWithNoSign }; - saveSponsorFormManagedItem(formId, tmpEntity); + saveSponsorFormManagedItem(formId, tmpEntity).then(() => { + getSponsorCustomizedFormItems( + formId, + term, + currentPage, + perPage, + order, + orderDir, + hideArchived + ); + }); };src/actions/sponsor-forms-actions.js (2)
1347-1359: Minor inconsistency in meta_fields filtering.
normalizeManagedItemusesmf.name(truthy check) whilenormalizeItemat line 1224 uses!!mf.name(explicit boolean conversion). Both behave identically, but for consistency, consider using the same pattern.
1475-1519: Consider adding success messages for archive/unarchive item operations.Unlike
archiveSponsorCustomizedForm(lines 834-867), these item-level archive/unarchive functions don't display success snackbars. This inconsistency may confuse users who expect visual feedback.If the UI handles feedback differently (e.g., immediate row state change), this may be intentional. Otherwise, consider adding success messages for consistency.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/actions/sponsor-forms-actions.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (3)
src/actions/sponsor-forms-actions.js (10)
getSponsorCustomizedFormItems(661-714)getSponsorCustomizedFormItems(661-714)saveSponsorFormManagedItem(1278-1341)saveSponsorFormManagedItem(1278-1341)unarchiveSponsorCustomizedFormItem(1498-1519)unarchiveSponsorCustomizedFormItem(1498-1519)archiveSponsorCustomizedFormItem(1475-1496)archiveSponsorCustomizedFormItem(1475-1496)addSponsorManagedFormItems(1437-1473)addSponsorManagedFormItems(1437-1473)src/utils/constants.js (2)
DEFAULT_CURRENT_PAGE(73-73)DEFAULT_CURRENT_PAGE(73-73)src/utils/yup.js (2)
rateCellValidation(41-70)rateCellValidation(41-70)
src/actions/sponsor-forms-actions.js (7)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (1)
formId(73-73)src/reducers/sponsors/sponsor-forms-list-reducer.js (1)
formId(153-153)src/actions/form-template-item-actions.js (1)
normalizedEntity(177-177)src/actions/inventory-item-actions.js (1)
normalizedEntity(195-195)src/actions/form-template-actions.js (1)
normalizedEntity(168-168)src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
itemId(146-146)src/reducers/sponsors/sponsor-form-items-list-reducer.js (1)
itemId(139-139)
⏰ 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). (6)
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (3)
src/actions/sponsor-forms-actions.js (3)
68-92: LGTM!The new action constants are well-organized and follow the existing naming conventions in the file.
661-714: LGTM!The
getSponsorCustomizedFormItemsaction now correctly passes pagination/sort metadata (line 710), addressing the previously reported issue. The implementation is consistent with similar actions in the codebase.
1278-1341: LGTM!The
saveSponsorFormManagedItemaction properly uses.finally()to ensurestopLoading()is called regardless of success or failure, addressing the previously reported issue.
| .then(() => { | ||
| dispatch( | ||
| snackbarSuccessHandler({ | ||
| title: T.translate("general.success"), | ||
| html: T.translate("sponsor_forms.form_delete_success") | ||
| }) | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect translation key for item deletion success message.
sponsor_forms.form_delete_success refers to form deletion, not item deletion. This appears to be a copy-paste error.
🔎 Proposed fix
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
- html: T.translate("sponsor_forms.form_delete_success")
+ html: T.translate("edit_sponsor.forms_tab.form_manage_items.item_deleted")
})
);Ensure the translation key exists in src/i18n/en.json.
📝 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.
| .then(() => { | |
| dispatch( | |
| snackbarSuccessHandler({ | |
| title: T.translate("general.success"), | |
| html: T.translate("sponsor_forms.form_delete_success") | |
| }) | |
| ); | |
| .then(() => { | |
| dispatch( | |
| snackbarSuccessHandler({ | |
| title: T.translate("general.success"), | |
| html: T.translate("edit_sponsor.forms_tab.form_manage_items.item_deleted") | |
| }) | |
| ); |
🤖 Prompt for AI Agents
In @src/actions/sponsor-forms-actions.js around lines 1399 - 1405, The success
snackbar uses the wrong translation key: it calls
T.translate("sponsor_forms.form_delete_success") which refers to form deletion
instead of item deletion; update the key to the correct one (e.g.,
"sponsor_forms.item_delete_success") in the dispatch call that invokes
snackbarSuccessHandler in src/actions/sponsor-forms-actions.js and ensure that
same key is added to the translations (src/i18n/en.json) with the proper message
so the snackbar displays the item deletion success text.
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
Outdated
Show resolved
Hide resolved
|
@tomrndom this looks good |
smarcet
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tomrndom please review comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js:
- Around line 242-259: The render function places the onClick on ImageIcon
instead of the wrapping IconButton; move the click handler from ImageIcon to the
IconButton (i.e., attach the window.open logic to IconButton), remove the
onClick from ImageIcon, and add an accessible label/aria-label on IconButton if
missing so the entire button area is clickable and accessible (references:
render, IconButton, ImageIcon).
- Around line 103-113: handleSearch currently calls
getSponsorCustomizedFormItems with currentPage, causing searches to start on the
existing page; change it to reset pagination to page 1 by calling
setCurrentPage(1) (if a setter exists) and pass 1 as the page argument to
getSponsorCustomizedFormItems instead of currentPage so searches always load
from the first page (update handleSearch and the getSponsorCustomizedFormItems
invocation accordingly).
- Around line 115-129: The popup is being closed immediately because
setOpenPopup(null) runs synchronously; change handleItemSave to wait for
saveSponsorFormManagedItem to complete before closing: call setOpenPopup(null)
inside the promise resolution (then) after getSponsorCustomizedFormItems and
resetSponsorFormManagedItem, and add a catch to handle failures (keep the popup
open and surface the error), or use then/catch rather than closing outside the
chain; reference saveSponsorFormManagedItem, getSponsorCustomizedFormItems,
resetSponsorFormManagedItem, and setOpenPopup when updating the logic.
- Around line 141-151: When toggling the "hide archived" checkbox, reset
pagination to page 1 before fetching items: in handleHideArchivedItems call
setCurrentPage(1) (or otherwise use 1 for the page argument) and then invoke
getSponsorCustomizedFormItems(formId, term, 1, perPage, order, orderDir,
ev.target.checked) so the fetch uses page 1 and avoids an empty/invalid current
page.
🧹 Nitpick comments (3)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (3)
75-77: Missing dependencies inuseEffecthook.The
useEffecthook usesgetSponsorCustomizedFormItemsandformIdbut neither is listed in the dependency array. While this may be intentional to only fetch on mount, the exhaustive-deps rule would flag this. Consider adding a comment or the dependencies.♻️ Suggested fix
useEffect(() => { getSponsorCustomizedFormItems(formId); + return () => resetSponsorFormManagedItem(); - }, []); + }, [formId, getSponsorCustomizedFormItems, resetSponsorFormManagedItem]);
153-155: Missing error handling foraddSponsorManagedFormItems.If the add operation fails, the popup will still close due to
.then(() => handleClose()). Consider adding.catch()to handle errors gracefully.♻️ Suggested improvement
const handleAddFromInventory = (itemsId) => { - addSponsorManagedFormItems(formId, itemsId).then(() => handleClose()); + addSponsorManagedFormItems(formId, itemsId) + .then(() => handleClose()) + .catch((error) => { + // Error is likely handled by Redux action; popup remains open for retry + console.error("Failed to add items from inventory:", error); + }); };
357-365: Redundant condition check forSponsorInventoryDialog.The condition
openPopup === "add_item"is checked twice: once in the outer&&and again in theopenprop. The inner check is redundant.♻️ Suggested simplification
{/* ADD ITEM */} {openPopup === "add_item" && ( <SponsorInventoryDialog entity={currentInventoryItem} - open={openPopup === "add_item"} + open onSave={handleItemSave} onClose={handleClose} /> )}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/i18n/en.jsonsrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (2)
src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (7)
currentPage(91-95)item(118-118)items(97-107)items(141-141)items(148-150)items(157-159)items(165-181)src/utils/constants.js (2)
DEFAULT_CURRENT_PAGE(73-73)DEFAULT_CURRENT_PAGE(73-73)
⏰ 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). (6)
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (6)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (2)
157-164: Verify currency sign stripping logic inhandleCellEdit.The regex
^[^\d.-]+strips leading non-digit characters (except.and-). This works for simple currency prefixes like$, but may not handle all currency formats (e.g., trailing symbols, thousands separators). Ensure this aligns with the expected input format from the editable table cell.
47-66: LGTM on component structure and Redux wiring.The component properly destructures props, connects to Redux state via
mapStateToProps, and binds action creators. The state mapping correctly aliasescurrentItemtocurrentInventoryItemfor clarity.Also applies to: 376-390
src/i18n/en.json (4)
2436-2456: LGTM on new i18n keys for form_manage_items.The new translation keys are well-structured with clear, descriptive text. The placeholder nested under
form_manage_items.placeholderfollows the established pattern in this file.
2552-2552: Typo fix acknowledged.The AI summary mentions the typo correction from "botton" to "button" in alert_info text. This is a good fix for user-facing text quality.
3641-3642: LGTM on new edit_inventory_item keys.The
new_itemandedit_itemkeys are appropriately added to support the item creation/editing UI flows.
2594-2594: LGTM on standard_rate key.The
standard_ratekey underadd_from_inventorysection maintains consistency with the rate naming convention used elsewhere in the file.
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
Outdated
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
Outdated
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
Show resolved
Hide resolved
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @src/actions/sponsor-forms-actions.js:
- Around line 1361-1375: normalizeRates currently only treats "" and undefined
as absent and will call amountToCents when a field is null; update the presence
checks for early_bird_rate, standard_rate, and onsite_rate in normalizeRates to
treat null the same as undefined (e.g., use == null or explicit null check) so
that when a rate is null or an empty string the corresponding normalizedEntity
property is deleted and amountToCents is only called for valid non-empty values.
🧹 Nitpick comments (6)
src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
185-187: No-op case creates unnecessary object allocation.
SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDEDreturns{ ...state }which is functionally equivalent to returningstatebut creates a new object unnecessarily, potentially triggering re-renders in connected components.Suggested fix
case SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED: { - return { ...state }; + return state; }Alternatively, if this case should actually update state (e.g., add items to the list), implement the intended logic. Otherwise, consider removing this case entirely since the default case already returns
state.src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (4)
75-77: Missing dependency array entries in useEffect.The effect uses
getSponsorCustomizedFormItemsandformIdbut the dependency array is empty. While this is a common pattern in Redux applications where actions don't change identity, it's technically incomplete and may trigger linter warnings.Suggested fix
useEffect(() => { getSponsorCustomizedFormItems(formId); - }, []); + }, [formId, getSponsorCustomizedFormItems]);
169-176: Cell edit value sanitization may pass invalid values.The regex
^[^\d.-]+only removes leading non-numeric characters. It doesn't validate that the result is a valid number. Values like"1.2.3","-..", or"-"would pass through. Also, the save operation doesn't handle promise rejection locally.Suggested improvement
const handleCellEdit = (rowId, column, value) => { const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); + const numericValue = parseFloat(valueWithNoSign); + if (isNaN(numericValue) || numericValue < 0) { + return; // Skip invalid values - validation schema should prevent this + } const tmpEntity = { id: rowId, - [column]: valueWithNoSign + [column]: numericValue }; saveSponsorFormManagedItem(formId, tmpEntity); };
254-270: Move onClick handler from ImageIcon to IconButton for better accessibility.The click handler is on the
ImageIconrather than theIconButton. While this works due to event bubbling, placing it on theIconButtonprovides a larger clickable area and better keyboard accessibility.Suggested fix
row.images?.length > 0 ? ( <Tooltip title={row.images[0].file_url} placement="top" arrow> - <IconButton size="small"> - <ImageIcon - fontSize="small" - onClick={() => - window.open( - row.images[0].file_url, - "_blank", - "noopener,noreferrer" - ) - } - /> + <IconButton + size="small" + onClick={() => + window.open( + row.images[0].file_url, + "_blank", + "noopener,noreferrer" + ) + } + > + <ImageIcon fontSize="small" /> </IconButton> </Tooltip> ) : null
370-378: Redundant conditional rendering check.The component is conditionally rendered with
openPopup === "add_item"and also receivesopen={openPopup === "add_item"}. The outer condition makes theopenprop alwaystruewhen rendered.Suggested simplification
{/* ADD ITEM */} - {openPopup === "add_item" && ( - <SponsorInventoryDialog - entity={currentInventoryItem} - open={openPopup === "add_item"} - onSave={handleItemSave} - onClose={handleClose} - /> - )} + <SponsorInventoryDialog + entity={currentInventoryItem} + open={openPopup === "add_item"} + onSave={handleItemSave} + onClose={handleClose} + />This allows the dialog to manage its own open/close transitions. If the conditional rendering is intentional to avoid mounting the component, the
openprop can simply betrue.src/actions/sponsor-forms-actions.js (1)
1437-1473: List refresh after adding items loses current filters and pagination.After successfully cloning items,
getSponsorCustomizedFormItems(formId)is called with onlyformId, resetting to default values for term, page, perPage, order, orderDir, and hideArchived. This may unexpectedly reset the user's current view.Consider accepting current filter state as parameters or retrieving them from Redux state within the action to preserve the user's context:
Suggested approach
export const addSponsorManagedFormItems = - (formId, itemIds) => async (dispatch, getState) => { + (formId, itemIds, filters = {}) => async (dispatch, getState) => { const { currentSummitState, currentSponsorState } = getState(); // ... existing code ... return postRequest( // ... existing code ... )(params)(dispatch) .then(() => { - dispatch(getSponsorCustomizedFormItems(formId)); + const { term, perPage, order, orderDir, hideArchived } = filters; + dispatch(getSponsorCustomizedFormItems( + formId, term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir, hideArchived + )); // ... rest of the code ... })
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/actions/sponsor-forms-actions.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.jssrc/reducers/sponsors/sponsor-customized-form-items-list-reducer.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
src/actions/sponsor-forms-actions.js (18)
REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS(70-71)REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS(70-71)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS(68-69)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS(68-69)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM(72-73)RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM(72-73)SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED(74-75)SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED(74-75)SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED(76-77)SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED(76-77)SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED(78-79)SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED(78-79)SPONSOR_FORM_MANAGED_ITEM_UPDATED(82-83)SPONSOR_FORM_MANAGED_ITEM_UPDATED(82-83)SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED(88-89)SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED(88-89)RESET_SPONSOR_FORM_MANAGED_ITEM(90-91)RESET_SPONSOR_FORM_MANAGED_ITEM(90-91)
src/actions/sponsor-forms-actions.js (9)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (1)
formId(73-73)src/reducers/sponsors/sponsor-forms-list-reducer.js (1)
formId(153-153)src/utils/constants.js (6)
DEFAULT_CURRENT_PAGE(73-73)DEFAULT_CURRENT_PAGE(73-73)DEFAULT_PER_PAGE(75-75)DEFAULT_PER_PAGE(75-75)DEFAULT_ORDER_DIR(91-91)DEFAULT_ORDER_DIR(91-91)src/actions/sponsor-users-actions.js (2)
accessToken(304-304)accessToken(537-537)src/actions/form-template-item-actions.js (1)
normalizedEntity(177-177)src/actions/inventory-item-actions.js (1)
normalizedEntity(195-195)src/actions/form-template-actions.js (1)
normalizedEntity(168-168)src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
itemId(147-147)src/reducers/sponsors/sponsor-form-items-list-reducer.js (1)
itemId(139-139)
⏰ 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). (3)
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (4)
src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
1-64: LGTM on reducer structure and default state definitions.The reducer setup follows Redux conventions correctly. The DEFAULT_ITEM_ENTITY and DEFAULT_STATE are well-structured with appropriate defaults for pagination, filtering, and the item entity shape.
src/actions/sponsor-forms-actions.js (3)
68-91: LGTM on new action type constants.The action type constants are well-named and follow the existing naming conventions in the file. Good separation between REQUEST/RECEIVE, ADDED/UPDATED/DELETED, and ARCHIVED/UNARCHIVED states.
661-714: LGTM on getSponsorCustomizedFormItems action.Well-structured action with proper filter handling, pagination, sorting, and loading state management. Follows the established patterns in this file.
1278-1341: LGTM on saveSponsorFormManagedItem action.The action properly handles both create (POST) and update (PUT) flows with appropriate success messaging and loading state management.
| const normalizeRates = (entity, normalizedEntity) => { | ||
| const { early_bird_rate, standard_rate, onsite_rate } = entity; | ||
|
|
||
| if (early_bird_rate === "" || early_bird_rate === undefined) | ||
| delete normalizedEntity.early_bird_rate; | ||
| else normalizedEntity.early_bird_rate = amountToCents(early_bird_rate); | ||
|
|
||
| if (standard_rate === "" || standard_rate === undefined) | ||
| delete normalizedEntity.standard_rate; | ||
| else normalizedEntity.standard_rate = amountToCents(standard_rate); | ||
|
|
||
| if (onsite_rate === "" || onsite_rate === undefined) | ||
| delete normalizedEntity.onsite_rate; | ||
| else normalizedEntity.onsite_rate = amountToCents(onsite_rate); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
normalizeRates doesn't handle null values.
The function checks for "" and undefined but not null. If a rate field is explicitly set to null, amountToCents(null) will be called, which may produce unexpected results (likely 0 or NaN depending on implementation).
Suggested fix
const normalizeRates = (entity, normalizedEntity) => {
const { early_bird_rate, standard_rate, onsite_rate } = entity;
- if (early_bird_rate === "" || early_bird_rate === undefined)
+ if (early_bird_rate === "" || early_bird_rate == null)
delete normalizedEntity.early_bird_rate;
else normalizedEntity.early_bird_rate = amountToCents(early_bird_rate);
- if (standard_rate === "" || standard_rate === undefined)
+ if (standard_rate === "" || standard_rate == null)
delete normalizedEntity.standard_rate;
else normalizedEntity.standard_rate = amountToCents(standard_rate);
- if (onsite_rate === "" || onsite_rate === undefined)
+ if (onsite_rate === "" || onsite_rate == null)
delete normalizedEntity.onsite_rate;
else normalizedEntity.onsite_rate = amountToCents(onsite_rate);
};Using == null covers both null and undefined in a single check.
🤖 Prompt for AI Agents
In @src/actions/sponsor-forms-actions.js around lines 1361 - 1375,
normalizeRates currently only treats "" and undefined as absent and will call
amountToCents when a field is null; update the presence checks for
early_bird_rate, standard_rate, and onsite_rate in normalizeRates to treat null
the same as undefined (e.g., use == null or explicit null check) so that when a
rate is null or an empty string the corresponding normalizedEntity property is
deleted and amountToCents is only called for valid non-empty values.
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
…nd actions Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
Signed-off-by: Tomás Castillo <[email protected]>
1026bf0 to
8fd699f
Compare
There was a problem hiding this 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)
src/pages/sponsors/sponsor-forms-tab/index.js (1)
261-263: Fix controlled checkbox prop.The Checkbox component should use the
checkedprop instead ofvaluefor controlled checkbox state. Currently usingvaluewill not properly control the checkbox's checked state.🐛 Proposed fix
<Checkbox - value={hideArchived} + checked={hideArchived} onChange={handleHideArchivedForms}
🤖 Fix all issues with AI agents
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js:
- Around line 127-141: The popup is being closed immediately in handleItemSave;
move the setOpenPopup(null) call inside the
saveSponsorFormManagedItem(...).then(...) callback so it only runs after the
save completes successfully. Update the handleItemSave function (references:
handleItemSave, saveSponsorFormManagedItem, getSponsorCustomizedFormItems,
resetSponsorFormManagedItem, setOpenPopup) so that
getSponsorCustomizedFormItems, resetSponsorFormManagedItem, and
setOpenPopup(null) all execute inside the .then() callback; optionally add a
.catch() to surface save errors and keep the popup open on failure.
- Around line 75-77: The useEffect that calls
getSponsorCustomizedFormItems(formId) must include formId in its dependency
array so the effect re-runs when the current formId changes; update the
useEffect surrounding getSponsorCustomizedFormItems to include formId (or both
formId and getSponsorCustomizedFormItems if that function is memoized with
useCallback) so navigating to a different form triggers a fresh fetch and avoids
stale items.
🧹 Nitpick comments (3)
src/pages/sponsors/sponsor-form-item-list-page/index.js (1)
93-97: Consider edge cases in cell value normalization.The regex
/^[^\d.-]+/strips leading non-numeric characters (allowing digits, dots, and minus signs). This correctly handles "$100" but would allow "€100" or "£100" which might then fail validation. Consider if this behavior is acceptable or if you want to restrict to dollar signs specifically.Additionally, trailing currency symbols like "100$" won't be stripped and will fail validation. This might be fine for forcing consistent formatting, but consider adding a hint to users.
Example: More explicit stripping
const handleCellEdit = (rowId, column, value) => { - const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); + // Strip leading dollar sign and any whitespace + const valueWithNoSign = String(value).trim().replace(/^\$+/, ""); const tmpEntity = { id: rowId, [column]: valueWithNoSign }; updateSponsorFormItem(formId, tmpEntity); };src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (1)
169-176: Consider more robust currency symbol stripping.The regex
/^[^\d.-]+/only removes non-numeric characters from the start of the string. WhileamountToCents()will handle remaining non-numeric characters viaparseFloat(), a more explicit pattern would improve clarity and robustness.♻️ Suggested improvement
const handleCellEdit = (rowId, column, value) => { - const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); + const valueWithNoSign = String(value).replace(/[^\d.-]/g, ""); const tmpEntity = { id: rowId, [column]: valueWithNoSign }; saveSponsorFormManagedItem(formId, tmpEntity); };This removes all non-numeric characters (except dots and minus signs) throughout the string, not just at the start.
src/actions/sponsor-forms-actions.js (1)
1231-1231: Consider reordering helper functions for clarity.The
normalizeItemfunction (line 1231) callsnormalizeRates(defined at line 1361). While this works correctly at runtime since the functions are only called after module initialization, definingnormalizeRatesbeforenormalizeItemandnormalizeManagedItemwould improve code readability by following a more natural top-down flow.Also applies to: 1361-1375
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/actions/sponsor-forms-actions.jssrc/i18n/en.jsonsrc/layouts/sponsor-id-layout.jssrc/pages/sponsors/edit-sponsor-page.jssrc/pages/sponsors/sponsor-form-item-list-page/index.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.jssrc/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.jssrc/pages/sponsors/sponsor-forms-tab/index.jssrc/pages/sponsors_inventory/popup/sponsor-inventory-popup.jssrc/reducers/sponsors/sponsor-customized-form-items-list-reducer.jssrc/store.jssrc/utils/constants.jssrc/utils/yup.js
🚧 Files skipped from review as they are similar to previous changes (5)
- src/utils/constants.js
- src/pages/sponsors/edit-sponsor-page.js
- src/layouts/sponsor-id-layout.js
- src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js
- src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/store.js (1)
src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
sponsorCustomizedFormItemsListReducer(66-193)
src/actions/sponsor-forms-actions.js (8)
src/pages/sponsors/sponsor-form-item-list-page/index.js (1)
formId(62-62)src/reducers/sponsors/sponsor-forms-list-reducer.js (1)
formId(153-153)src/utils/constants.js (6)
DEFAULT_CURRENT_PAGE(73-73)DEFAULT_CURRENT_PAGE(73-73)DEFAULT_PER_PAGE(75-75)DEFAULT_PER_PAGE(75-75)DEFAULT_ORDER_DIR(91-91)DEFAULT_ORDER_DIR(91-91)src/actions/inventory-item-actions.js (1)
normalizedEntity(195-195)src/actions/form-template-item-actions.js (1)
normalizedEntity(177-177)src/actions/form-template-actions.js (1)
normalizedEntity(168-168)src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (1)
itemId(147-147)src/reducers/sponsors/sponsor-form-items-list-reducer.js (1)
itemId(139-139)
⏰ 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). (6)
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (11)
src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js (3)
185-191: Good defensive programming on conditional deletion.The check ensures the API is only called when the field is persisted (
fieldType.id) and the callback exists (onMetaFieldTypeDeleted), preventing errors when removing unsaved fields. This is correct.
246-248: Dynamic dialog title improves UX.The conditional rendering of
edit_itemvsnew_itembased oninitialEntity.idprovides clear context to users about whether they're creating or editing.
72-72: Verify that making images optional is intentional.The validation changed from
yup.array().min(1, ...)(required) toyup.array()(optional), and the label asterisk was removed on line 587. Ensure this aligns with business requirements.src/i18n/en.json (1)
2436-2456: Complete i18n coverage for the new feature.The
form_manage_itemsblock provides all necessary strings for the sponsor form item management UI, including labels, buttons, placeholders, and success messages. Good attention to detail with the typo fix on line 2552 ("botton" → "button").src/pages/sponsors/sponsor-form-item-list-page/index.js (1)
132-143: Centralized validation improves maintainability.Using
rateCellValidation()from utils ensures consistent price/rate validation across the application. The validation correctly handles dollar signs, enforces positive values, and limits decimals.src/store.js (1)
167-167: LGTM: Standard Redux integration.The new
sponsorCustomizedFormItemsListReduceris properly imported and registered in the persisted state. This follows the established pattern for other reducers in the store.Also applies to: 254-254
src/utils/yup.js (1)
51-80: Well-designed centralized price validation.The
rateCellValidationfunction provides robust validation for rate/price fields:
- Strips leading dollar signs for user convenience
- Validates numeric format including decimals
- Enforces positive values (appropriate for prices)
- Limits to 2 decimal places
The logic correctly allows users to type minus signs (the format regex permits
-) but then rejects negative values via.positive(), providing clear validation feedback.src/pages/sponsors/sponsor-forms-tab/index.js (1)
44-44: LGTM! Navigation implementation is correct.The addition of the
historyprop and the navigation flow to the sponsor forms items page is properly implemented with the correct path parameters.Also applies to: 115-117
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (1)
210-241: LGTM! Proper validation setup for editable rate columns.The rate columns correctly implement per-cell validation using the centralized
rateCellValidation()schema, providing immediate user feedback during inline editing. The consistent pattern across all three rate fields is well-structured.src/actions/sponsor-forms-actions.js (2)
1361-1375: LGTM! Well-structured rate normalization helper.The extraction of
normalizeRatesinto a reusable helper eliminates code duplication and provides consistent handling of rate conversions to cents. The logic properly handles empty strings and undefined values by removing the properties rather than sending invalid data to the API.
1278-1341: LGTM! Proper create/update flow with clear user feedback.The
saveSponsorFormManagedItemfunction correctly implements the standard create/update pattern, differentiating between POST and PUT based onentity.id. User feedback is appropriately tailored with distinct success messages for creation versus updates.
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
Show resolved
Hide resolved
Signed-off-by: Tomás Castillo <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js:
- Around line 75-77: The useEffect that calls
getSponsorCustomizedFormItems(formId) is missing dependencies; update the
useEffect that references useEffect, getSponsorCustomizedFormItems, and formId
so that formId and getSponsorCustomizedFormItems are included in the dependency
array; if getSponsorCustomizedFormItems is defined inline or recreated on each
render, memoize it (e.g., with useCallback) so the effect only re-runs when
truly necessary; this ensures the component refetches items when formId changes
and avoids stale data.
- Around line 165-167: handleAddFromInventory currently only calls
addSponsorManagedFormItems and then closes the dialog, so the UI list isn't
refreshed; update handleAddFromInventory to, after
addSponsorManagedFormItems(formId, itemsId) resolves, trigger the component's
managed-items reload (call the existing fetch/reload helper used to load the
list or invoke a passed-in onRefresh callback) and only then call handleClose();
ensure errors are handled (e.g., catch and log) so the refresh still runs or
appropriate feedback is shown.
- Around line 148-151: handleArchiveItem currently calls
unarchiveSponsorCustomizedFormItem/archiveSponsorCustomizedFormItem but does not
refresh the displayed list; update handleArchiveItem so that after the promise
returned by unarchiveSponsorCustomizedFormItem or
archiveSponsorCustomizedFormItem resolves you call the same list refresh used by
handleRowDelete (the function that reloads the items in this component) to
re-fetch/update the UI state, and handle errors similarly to handleRowDelete.
- Around line 169-176: handleCellEdit currently calls saveSponsorFormManagedItem
without handling failures, causing UI/backend inconsistency; update
handleCellEdit to await the save (or use the returned promise) and catch errors
from saveSponsorFormManagedItem, show a user-facing notification/error (e.g.,
via existing toast or setError state) and revert the edited cell value when the
save fails (use the previous value saved in a local variable before change).
Ensure you reference handleCellEdit and saveSponsorFormManagedItem, preserve the
valueWithNoSign processing, and perform the optimistic update only if save
succeeds or roll it back on catch.
🧹 Nitpick comments (2)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (2)
127-141: Consider preserving current page after saving items.After saving an item, the list resets to page 1. If the user was viewing a different page, this might be unexpected. Consider using
currentPageinstead ofDEFAULT_CURRENT_PAGEto maintain the user's position, especially when editing existing items.💡 Suggested improvement
const handleItemSave = (item) => { saveSponsorFormManagedItem(formId, item).then(() => { getSponsorCustomizedFormItems( formId, term, - DEFAULT_CURRENT_PAGE, + item.id ? currentPage : DEFAULT_CURRENT_PAGE, perPage, order, orderDir, hideArchived ); resetSponsorFormManagedItem(); setOpenPopup(null); }); };This preserves the current page for edits but resets to page 1 for new items.
198-272: Enable sorting on key columns to improve user experience.Both the API and UI infrastructure fully support sorting on these fields. Currently, all columns are marked as
sortable: false, but enabling sorting on columns likename,code, and the rate fields would help users quickly find and organize items in large lists. ThehandleSortfunction andgetSponsorCustomizedFormItemsaction are already in place to support this.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (3)
src/actions/sponsor-forms-actions.js (2)
getSponsorCustomizedFormItems(661-714)getSponsorCustomizedFormItems(661-714)src/utils/constants.js (2)
DEFAULT_CURRENT_PAGE(73-73)DEFAULT_CURRENT_PAGE(73-73)src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js (6)
item(119-119)items(98-108)items(142-142)items(149-151)items(158-160)items(166-182)
⏰ 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). (5)
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (2)
src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js (2)
1-46: LGTM!The imports are well-organized and the Apache 2.0 license header is properly included.
389-403: LGTM!The Redux connection properly maps state and action creators. The renaming of
currentItemtocurrentInventoryItemimproves clarity.
| useEffect(() => { | ||
| getSponsorCustomizedFormItems(formId); | ||
| }, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing dependencies in useEffect hook.
The useEffect hook is missing formId and getSponsorCustomizedFormItems in its dependency array. If the formId changes (e.g., navigating to a different form), the component will not refetch the items, displaying stale data.
🔧 Proposed fix
useEffect(() => {
getSponsorCustomizedFormItems(formId);
- }, []);
+ }, [formId, getSponsorCustomizedFormItems]);📝 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.
| useEffect(() => { | |
| getSponsorCustomizedFormItems(formId); | |
| }, []); | |
| useEffect(() => { | |
| getSponsorCustomizedFormItems(formId); | |
| }, [formId, getSponsorCustomizedFormItems]); |
🤖 Prompt for AI Agents
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
around lines 75 - 77, The useEffect that calls
getSponsorCustomizedFormItems(formId) is missing dependencies; update the
useEffect that references useEffect, getSponsorCustomizedFormItems, and formId
so that formId and getSponsorCustomizedFormItems are included in the dependency
array; if getSponsorCustomizedFormItems is defined inline or recreated on each
render, memoize it (e.g., with useCallback) so the effect only re-runs when
truly necessary; this ensures the component refetches items when formId changes
and avoids stale data.
| const handleArchiveItem = (item) => | ||
| item.is_archived | ||
| ? unarchiveSponsorCustomizedFormItem(formId, item.id) | ||
| : archiveSponsorCustomizedFormItem(formId, item.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing list refresh after archive/unarchive.
After archiving or unarchiving an item, the list is not refreshed, leaving the UI out of sync with the backend. Compare this to handleRowDelete (lines 184-196), which properly refreshes the list after deletion.
🔄 Proposed fix
const handleArchiveItem = (item) =>
- item.is_archived
- ? unarchiveSponsorCustomizedFormItem(formId, item.id)
- : archiveSponsorCustomizedFormItem(formId, item.id);
+ (item.is_archived
+ ? unarchiveSponsorCustomizedFormItem(formId, item.id)
+ : archiveSponsorCustomizedFormItem(formId, item.id)
+ ).then(() =>
+ getSponsorCustomizedFormItems(
+ formId,
+ term,
+ currentPage,
+ perPage,
+ order,
+ orderDir,
+ hideArchived
+ )
+ );📝 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.
| const handleArchiveItem = (item) => | |
| item.is_archived | |
| ? unarchiveSponsorCustomizedFormItem(formId, item.id) | |
| : archiveSponsorCustomizedFormItem(formId, item.id); | |
| const handleArchiveItem = (item) => | |
| (item.is_archived | |
| ? unarchiveSponsorCustomizedFormItem(formId, item.id) | |
| : archiveSponsorCustomizedFormItem(formId, item.id) | |
| ).then(() => | |
| getSponsorCustomizedFormItems( | |
| formId, | |
| term, | |
| currentPage, | |
| perPage, | |
| order, | |
| orderDir, | |
| hideArchived | |
| ) | |
| ); |
🤖 Prompt for AI Agents
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
around lines 148 - 151, handleArchiveItem currently calls
unarchiveSponsorCustomizedFormItem/archiveSponsorCustomizedFormItem but does not
refresh the displayed list; update handleArchiveItem so that after the promise
returned by unarchiveSponsorCustomizedFormItem or
archiveSponsorCustomizedFormItem resolves you call the same list refresh used by
handleRowDelete (the function that reloads the items in this component) to
re-fetch/update the UI state, and handle errors similarly to handleRowDelete.
| const handleAddFromInventory = (itemsId) => { | ||
| addSponsorManagedFormItems(formId, itemsId).then(() => handleClose()); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing list refresh after adding items from inventory.
After adding items from inventory, the list is not refreshed, so newly added items won't appear in the UI. Users would need to manually refresh the page to see their changes.
🔄 Proposed fix
const handleAddFromInventory = (itemsId) => {
- addSponsorManagedFormItems(formId, itemsId).then(() => handleClose());
+ addSponsorManagedFormItems(formId, itemsId).then(() => {
+ getSponsorCustomizedFormItems(
+ formId,
+ term,
+ DEFAULT_CURRENT_PAGE,
+ perPage,
+ order,
+ orderDir,
+ hideArchived
+ );
+ handleClose();
+ });
};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
around lines 165 - 167, handleAddFromInventory currently only calls
addSponsorManagedFormItems and then closes the dialog, so the UI list isn't
refreshed; update handleAddFromInventory to, after
addSponsorManagedFormItems(formId, itemsId) resolves, trigger the component's
managed-items reload (call the existing fetch/reload helper used to load the
list or invoke a passed-in onRefresh callback) and only then call handleClose();
ensure errors are handled (e.g., catch and log) so the refresh still runs or
appropriate feedback is shown.
| const handleCellEdit = (rowId, column, value) => { | ||
| const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); | ||
| const tmpEntity = { | ||
| id: rowId, | ||
| [column]: valueWithNoSign | ||
| }; | ||
| saveSponsorFormManagedItem(formId, tmpEntity); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for inline cell edits.
If saveSponsorFormManagedItem fails, the UI will display the new value but the backend will retain the old value, creating a data inconsistency. Consider adding error handling to notify users and optionally revert the cell value.
⚠️ Suggested improvement with error handling
const handleCellEdit = (rowId, column, value) => {
const valueWithNoSign = String(value).replace(/^[^\d.-]+/, "");
const tmpEntity = {
id: rowId,
[column]: valueWithNoSign
};
- saveSponsorFormManagedItem(formId, tmpEntity);
+ saveSponsorFormManagedItem(formId, tmpEntity).catch((error) => {
+ // Optionally show an error notification to the user
+ console.error("Failed to save item:", error);
+ // Consider refreshing the list to revert the UI
+ getSponsorCustomizedFormItems(
+ formId,
+ term,
+ currentPage,
+ perPage,
+ order,
+ orderDir,
+ hideArchived
+ );
+ });
};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
@src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js
around lines 169 - 176, handleCellEdit currently calls
saveSponsorFormManagedItem without handling failures, causing UI/backend
inconsistency; update handleCellEdit to await the save (or use the returned
promise) and catch errors from saveSponsorFormManagedItem, show a user-facing
notification/error (e.g., via existing toast or setError state) and revert the
edited cell value when the save fails (use the previous value saved in a local
variable before change). Ensure you reference handleCellEdit and
saveSponsorFormManagedItem, preserve the valueWithNoSign processing, and perform
the optimistic update only if save succeeds or roll it back on catch.
ref:
Signed-off-by: Tomás Castillo [email protected]
Summary by CodeRabbit
New Features
Localization
Routing
Validation
State
UX Fixes
✏️ Tip: You can customize this high-level summary in your review settings.