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/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..5c2bc08c2 100644 --- a/src/components/mui/formik-inputs/mui-formik-pricefield.js +++ b/src/components/mui/formik-inputs/mui-formik-pricefield.js @@ -17,23 +17,45 @@ 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; + // 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 ""; + 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); + field.onBlur(e); + if (props.onBlur) props.onBlur(e); + }; + const handleChange = (e) => { const newVal = e.target.value; + setFocusedValue(newVal); if (newVal === "") { setCleared(true); @@ -64,6 +86,8 @@ const MuiFormikPriceField = ({ type="number" value={getDisplayValue()} onChange={handleChange} + onFocus={handleFocus} + onBlur={handleBlur} slotProps={{ input: { startAdornment: $ 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/reducers/sponsors/__tests__/sponsor-form-items-list-reducer.test.js b/src/reducers/sponsors/__tests__/sponsor-form-items-list-reducer.test.js index a33b5d430..ed56c4be1 100644 --- a/src/reducers/sponsors/__tests__/sponsor-form-items-list-reducer.test.js +++ b/src/reducers/sponsors/__tests__/sponsor-form-items-list-reducer.test.js @@ -183,9 +183,6 @@ describe("SponsorFormItemsListReducer", () => { ...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/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: { 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: { 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) => {