From 332b3bbbc8af299308c1be385116e2bd3579a82b Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Tue, 31 Mar 2026 17:57:32 -0300 Subject: [PATCH 1/4] fix: change pricefield usage to always send a number in cents, update tests accordingly --- src/actions/sponsor-forms-actions.js | 20 -------------- .../mui/formik-inputs/item-price-tiers.js | 1 + .../formik-inputs/mui-formik-pricefield.js | 26 +++++++++++++++++-- src/pages/sponsors/edit-sponsor-page.js | 1 + .../components/sponsor-form-item-popup.js | 2 ++ .../sponsor-form-items-list-reducer.test.js | 3 --- ...nsor-customized-form-items-list-reducer.js | 5 +--- .../sponsor-form-items-list-reducer.js | 5 +--- .../inventory-item-reducer.js | 10 +------ 9 files changed, 31 insertions(+), 42 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index 9bb70cf6e..3883ed1dd 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -29,7 +29,6 @@ import { getAccessTokenSafely, normalizeSelectAllField } from "../utils/methods"; -import { rateToCents, RATE_FIELDS } from "../utils/rate-helpers"; import { DEFAULT_CURRENT_PAGE, DEFAULT_ORDER_DIR, @@ -1238,8 +1237,6 @@ const normalizeItem = (entity) => { normalizedEntity.images = images?.filter((img) => img.file_path); } - normalizeRates(entity, normalizedEntity); - if (quantity_limit_per_show === "") delete normalizedEntity.quantity_limit_per_show; if (quantity_limit_per_sponsor === "") @@ -1363,26 +1360,9 @@ const normalizeManagedItem = (entity) => { (img) => img.file_path ); - normalizeRates(entity, normalizedEntity); - return normalizedEntity; }; -const normalizeRates = (entity, normalizedEntity) => { - if (RATE_FIELDS.EARLY_BIRD in entity) - normalizedEntity[RATE_FIELDS.EARLY_BIRD] = rateToCents( - entity[RATE_FIELDS.EARLY_BIRD] - ); - if (RATE_FIELDS.STANDARD in entity) - normalizedEntity[RATE_FIELDS.STANDARD] = rateToCents( - entity[RATE_FIELDS.STANDARD] - ); - if (RATE_FIELDS.ONSITE in entity) - normalizedEntity[RATE_FIELDS.ONSITE] = rateToCents( - entity[RATE_FIELDS.ONSITE] - ); -}; - export const deleteSponsorFormManagedItem = (formId, itemId) => async (dispatch, getState) => { const { currentSummitState, currentSponsorState } = getState(); diff --git a/src/components/mui/formik-inputs/item-price-tiers.js b/src/components/mui/formik-inputs/item-price-tiers.js index 6bdd2a235..43b6f5825 100644 --- a/src/components/mui/formik-inputs/item-price-tiers.js +++ b/src/components/mui/formik-inputs/item-price-tiers.js @@ -67,6 +67,7 @@ const ItemPriceTiers = ({ readOnly = false }) => { ) : ( diff --git a/src/components/mui/formik-inputs/mui-formik-pricefield.js b/src/components/mui/formik-inputs/mui-formik-pricefield.js index 193b08637..5b2288a76 100644 --- a/src/components/mui/formik-inputs/mui-formik-pricefield.js +++ b/src/components/mui/formik-inputs/mui-formik-pricefield.js @@ -17,23 +17,43 @@ const MuiFormikPriceField = ({ // eslint-disable-next-line no-unused-vars const [field, meta, helpers] = useField(name); const [cleared, setCleared] = useState(false); + const [isFocused, setIsFocused] = useState(false); + const [focusedValue, setFocusedValue] = useState(""); const emptyValue = meta.initialValue === null ? null : 0; + const getRawString = () => { + if (cleared || field.value == null) return ""; + if (field.value === 0) return "0"; + const raw = inCents ? field.value / ONE_HUNDRED : field.value; + return String(Number(raw.toFixed(DECIMAL_DIGITS))); + }; + const getDisplayValue = () => { + if (isFocused) return focusedValue; if (cleared) return ""; if (field.value == null || field.value === 0) { return field.value === 0 ? 0 : ""; } - const raw = inCents ? field.value / ONE_HUNDRED : field.value; - const str = String(Number(raw.toFixed(DECIMAL_DIGITS))); + const str = getRawString(); const dotIdx = str.indexOf("."); if (dotIdx !== -1 && str.length - dotIdx - 1 === 1) return `${str}0`; return str; }; + const handleFocus = () => { + setIsFocused(true); + setFocusedValue(getRawString()); + }; + + const handleBlur = (e) => { + setIsFocused(false); + if (props.onBlur) props.onBlur(e); + }; + const handleChange = (e) => { const newVal = e.target.value; + setFocusedValue(newVal); if (newVal === "") { setCleared(true); @@ -64,6 +84,8 @@ const MuiFormikPriceField = ({ type="number" value={getDisplayValue()} onChange={handleChange} + onFocus={handleFocus} + onBlur={handleBlur} slotProps={{ input: { startAdornment: $ diff --git a/src/pages/sponsors/edit-sponsor-page.js b/src/pages/sponsors/edit-sponsor-page.js index 31a549070..c7ff2be0b 100644 --- a/src/pages/sponsors/edit-sponsor-page.js +++ b/src/pages/sponsors/edit-sponsor-page.js @@ -305,6 +305,7 @@ const EditSponsorPage = (props) => { )} {memberObj.hasAccess(ACCESS_ROUTES.ADMIN_SPONSORS) && ( + {console.log("HEREEEEE", isNestedFormItemRoute)} {isNestedFormItemRoute ? ( ) : ( diff --git a/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js b/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js index e62317851..1a122e677 100644 --- a/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js +++ b/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js @@ -40,6 +40,8 @@ const SponsorFormItemPopup = ({ }); }; + console.log("ITEM: ", item); + return ( { ...initialState, currentItem: { ...item, - early_bird_rate: "1.00", - standard_rate: "1.00", - onsite_rate: "1.00", meta_fields: [] } }); diff --git a/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js b/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js index 3d47f6452..fafbfbfa9 100644 --- a/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js +++ b/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js @@ -12,7 +12,7 @@ * */ import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; -import { formatRateFromCents, rateFromCents } from "../../utils/rate-helpers"; +import { formatRateFromCents } from "../../utils/rate-helpers"; import { RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS, REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS, @@ -110,9 +110,6 @@ const sponsorCustomizedFormItemsListReducer = ( const currentItem = { ...item, - early_bird_rate: rateFromCents(item.early_bird_rate), - standard_rate: rateFromCents(item.standard_rate), - onsite_rate: rateFromCents(item.onsite_rate), meta_fields: item.meta_fields.length > 0 ? item.meta_fields : [] }; return { ...state, currentItem }; diff --git a/src/reducers/sponsors/sponsor-form-items-list-reducer.js b/src/reducers/sponsors/sponsor-form-items-list-reducer.js index a9779cdfe..817e2c773 100644 --- a/src/reducers/sponsors/sponsor-form-items-list-reducer.js +++ b/src/reducers/sponsors/sponsor-form-items-list-reducer.js @@ -12,7 +12,7 @@ * */ import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; -import { formatRateFromCents, rateFromCents } from "../../utils/rate-helpers"; +import { formatRateFromCents } from "../../utils/rate-helpers"; import { RECEIVE_SPONSOR_FORM_ITEM, RECEIVE_SPONSOR_FORM_ITEMS, @@ -100,9 +100,6 @@ const sponsorFormItemsListReducer = (state = DEFAULT_STATE, action) => { const currentItem = { ...item, - early_bird_rate: rateFromCents(item.early_bird_rate), - standard_rate: rateFromCents(item.standard_rate), - onsite_rate: rateFromCents(item.onsite_rate), meta_fields: item.meta_fields.length > 0 ? item.meta_fields : [] }; diff --git a/src/reducers/sponsors_inventory/inventory-item-reducer.js b/src/reducers/sponsors_inventory/inventory-item-reducer.js index b70bf43b7..7ecf1753a 100644 --- a/src/reducers/sponsors_inventory/inventory-item-reducer.js +++ b/src/reducers/sponsors_inventory/inventory-item-reducer.js @@ -12,7 +12,7 @@ * */ import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; -import { RATE_FIELDS, rateFromCents } from "../../utils/rate-helpers"; +import { RATE_FIELDS } from "../../utils/rate-helpers"; import { RECEIVE_INVENTORY_ITEM, RESET_INVENTORY_ITEM_FORM, @@ -69,10 +69,6 @@ const inventoryItemReducer = (state = DEFAULT_STATE, action) => { } } - entity.early_bird_rate = rateFromCents(entity.early_bird_rate); - entity.standard_rate = rateFromCents(entity.standard_rate); - entity.onsite_rate = rateFromCents(entity.onsite_rate); - return { ...state, entity: { @@ -92,10 +88,6 @@ const inventoryItemReducer = (state = DEFAULT_STATE, action) => { } } - entity.early_bird_rate = rateFromCents(entity.early_bird_rate); - entity.standard_rate = rateFromCents(entity.standard_rate); - entity.onsite_rate = rateFromCents(entity.onsite_rate); - return { ...state, entity: { From 01e2a67b8647484a4d7e30f62b46e1b3055d9785 Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Tue, 31 Mar 2026 18:15:21 -0300 Subject: [PATCH 2/4] fix: remove logs --- src/pages/sponsors/edit-sponsor-page.js | 1 - .../components/sponsor-form-item-popup.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/pages/sponsors/edit-sponsor-page.js b/src/pages/sponsors/edit-sponsor-page.js index c7ff2be0b..31a549070 100644 --- a/src/pages/sponsors/edit-sponsor-page.js +++ b/src/pages/sponsors/edit-sponsor-page.js @@ -305,7 +305,6 @@ const EditSponsorPage = (props) => { )} {memberObj.hasAccess(ACCESS_ROUTES.ADMIN_SPONSORS) && ( - {console.log("HEREEEEE", isNestedFormItemRoute)} {isNestedFormItemRoute ? ( ) : ( diff --git a/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js b/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js index 1a122e677..e62317851 100644 --- a/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js +++ b/src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-item-popup.js @@ -40,8 +40,6 @@ const SponsorFormItemPopup = ({ }); }; - console.log("ITEM: ", item); - return ( Date: Wed, 1 Apr 2026 09:41:57 -0300 Subject: [PATCH 3/4] fix: missing migrations --- src/actions/form-template-item-actions.js | 6 ------ src/actions/inventory-item-actions.js | 7 ------- .../mui/formik-inputs/mui-formik-pricefield.js | 1 + .../sponsors_inventory/form-template-item-reducer.js | 10 +--------- 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/actions/form-template-item-actions.js b/src/actions/form-template-item-actions.js index 705e81930..6d6fb664e 100644 --- a/src/actions/form-template-item-actions.js +++ b/src/actions/form-template-item-actions.js @@ -22,7 +22,6 @@ import { startLoading, escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; -import { rateToCents } from "../utils/rate-helpers"; import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions"; import { getAccessTokenSafely } from "../utils/methods"; import { @@ -178,11 +177,6 @@ const normalizeEntity = (entity) => { normalizedEntity.images = normalizedEntity.images?.filter( (img) => img.file_path ); - normalizedEntity.early_bird_rate = rateToCents( - normalizedEntity.early_bird_rate - ); - normalizedEntity.standard_rate = rateToCents(normalizedEntity.standard_rate); - normalizedEntity.onsite_rate = rateToCents(normalizedEntity.onsite_rate); return normalizedEntity; }; diff --git a/src/actions/inventory-item-actions.js b/src/actions/inventory-item-actions.js index ced14ab19..86ab71f39 100644 --- a/src/actions/inventory-item-actions.js +++ b/src/actions/inventory-item-actions.js @@ -25,7 +25,6 @@ import { authErrorHandler, escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; -import { rateToCents } from "../utils/rate-helpers"; import history from "../history"; import { getAccessTokenSafely } from "../utils/methods"; import { @@ -200,12 +199,6 @@ const normalizeEntity = (entity) => { (img) => img.file_path ); - normalizedEntity.early_bird_rate = rateToCents( - normalizedEntity.early_bird_rate - ); - normalizedEntity.standard_rate = rateToCents(normalizedEntity.standard_rate); - normalizedEntity.onsite_rate = rateToCents(normalizedEntity.onsite_rate); - return normalizedEntity; }; diff --git a/src/components/mui/formik-inputs/mui-formik-pricefield.js b/src/components/mui/formik-inputs/mui-formik-pricefield.js index 5b2288a76..33313cdb3 100644 --- a/src/components/mui/formik-inputs/mui-formik-pricefield.js +++ b/src/components/mui/formik-inputs/mui-formik-pricefield.js @@ -48,6 +48,7 @@ const MuiFormikPriceField = ({ const handleBlur = (e) => { setIsFocused(false); + field.onBlur(e); if (props.onBlur) props.onBlur(e); }; diff --git a/src/reducers/sponsors_inventory/form-template-item-reducer.js b/src/reducers/sponsors_inventory/form-template-item-reducer.js index 8b900dc94..2e616a7a0 100644 --- a/src/reducers/sponsors_inventory/form-template-item-reducer.js +++ b/src/reducers/sponsors_inventory/form-template-item-reducer.js @@ -12,7 +12,7 @@ * */ import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; -import { RATE_FIELDS, rateFromCents } from "../../utils/rate-helpers"; +import { RATE_FIELDS } from "../../utils/rate-helpers"; import { RECEIVE_FORM_TEMPLATE_ITEM, RESET_FORM_TEMPLATE_ITEM_FORM, @@ -70,10 +70,6 @@ const formTemplateItemReducer = (state = DEFAULT_STATE, action) => { } } - entity.early_bird_rate = rateFromCents(entity.early_bird_rate); - entity.standard_rate = rateFromCents(entity.standard_rate); - entity.onsite_rate = rateFromCents(entity.onsite_rate); - return { ...state, entity: { @@ -93,10 +89,6 @@ const formTemplateItemReducer = (state = DEFAULT_STATE, action) => { } } - entity.early_bird_rate = rateFromCents(entity.early_bird_rate); - entity.standard_rate = rateFromCents(entity.standard_rate); - entity.onsite_rate = rateFromCents(entity.onsite_rate); - return { ...state, entity: { From 3112e1ad3986f2036e4a3b8b3ab8ce9262ca5c49 Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Wed, 1 Apr 2026 13:24:53 -0300 Subject: [PATCH 4/4] fix: pricefield empty value and inline rate edition --- src/components/mui/formik-inputs/mui-formik-pricefield.js | 3 ++- src/pages/sponsors/sponsor-form-item-list-page/index.js | 6 ++++-- .../components/manage-items/sponsor-forms-manage-items.js | 7 +++++-- src/utils/rate-helpers.js | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/mui/formik-inputs/mui-formik-pricefield.js b/src/components/mui/formik-inputs/mui-formik-pricefield.js index 33313cdb3..5c2bc08c2 100644 --- a/src/components/mui/formik-inputs/mui-formik-pricefield.js +++ b/src/components/mui/formik-inputs/mui-formik-pricefield.js @@ -20,7 +20,8 @@ const MuiFormikPriceField = ({ const [isFocused, setIsFocused] = useState(false); const [focusedValue, setFocusedValue] = useState(""); - const emptyValue = meta.initialValue === null ? null : 0; + // emptyValue is always 0 when editing this field, null is handled by N/A checkbox + const emptyValue = 0; const getRawString = () => { if (cleared || field.value == null) return ""; diff --git a/src/pages/sponsors/sponsor-form-item-list-page/index.js b/src/pages/sponsors/sponsor-form-item-list-page/index.js index 532af58c3..321f88c5f 100644 --- a/src/pages/sponsors/sponsor-form-item-list-page/index.js +++ b/src/pages/sponsors/sponsor-form-item-list-page/index.js @@ -41,6 +41,7 @@ import SponsorFormAddItemFromInventoryPopup from "./components/sponsor-form-add- import MuiTableEditable from "../../../components/mui/editable-table/mui-table-editable"; import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants"; import { rateCellValidation } from "../../../utils/yup"; +import { rateToCents } from "../../../utils/rate-helpers"; const SponsorFormItemListPage = ({ match, @@ -91,8 +92,9 @@ const SponsorFormItemListPage = ({ }; const handleCellEdit = (rowId, column, value) => { - const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); - const tmpEntity = { id: rowId, [column]: valueWithNoSign }; + // since editable cell is TextField and not PriceField, we need to convert to cents + const valueInCents = rateToCents(value); + const tmpEntity = { id: rowId, [column]: valueInCents }; updateSponsorFormItem(formId, tmpEntity); }; diff --git a/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js index 54ceb0de0..783112d06 100644 --- a/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js +++ b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js @@ -43,6 +43,7 @@ import SponsorInventoryDialog from "../../../../sponsors-global/form-templates/s import SponsorFormItemFromInventoryPopup from "./sponsor-form-item-from-inventory"; import { DEFAULT_CURRENT_PAGE } from "../../../../../utils/constants"; import { rateCellValidation } from "../../../../../utils/yup"; +import { rateToCents } from "../../../../../utils/rate-helpers"; const SponsorFormsManageItems = ({ term, @@ -167,11 +168,13 @@ const SponsorFormsManageItems = ({ }; const handleCellEdit = (rowId, column, value) => { - const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); + // since editable cell is TextField and not PriceField, we need to convert to cents + const valueInCents = rateToCents(value); const tmpEntity = { id: rowId, - [column]: valueWithNoSign + [column]: valueInCents }; + saveSponsorFormManagedItem(formId, tmpEntity); }; diff --git a/src/utils/rate-helpers.js b/src/utils/rate-helpers.js index c841e817f..3be7b9465 100644 --- a/src/utils/rate-helpers.js +++ b/src/utils/rate-helpers.js @@ -1,7 +1,7 @@ import T from "i18n-react"; import { amountFromCents, - amountToCents, + parsePrice, currencyAmountFromCents } from "openstack-uicore-foundation/lib/utils/money"; @@ -22,7 +22,7 @@ export const rateFromCents = (cents) => { export const rateToCents = (value) => { if (value === null || value === undefined) return null; if (value === "") return 0; - return amountToCents(value); + return parsePrice(value); }; export const formatRateFromCents = (cents) => {