Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/actions/form-template-item-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
};

Expand Down
7 changes: 0 additions & 7 deletions src/actions/inventory-item-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
};

Expand Down
20 changes: 0 additions & 20 deletions src/actions/sponsor-forms-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 === "")
Expand Down Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/components/mui/formik-inputs/item-price-tiers.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const ItemPriceTiers = ({ readOnly = false }) => {
<MuiFormikPriceField
name={field}
fullWidth
inCents
Comment thread
santipalenque marked this conversation as resolved.
disabled={readOnly}
/>
) : (
Expand Down
30 changes: 27 additions & 3 deletions src/components/mui/formik-inputs/mui-formik-pricefield.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment thread
santipalenque marked this conversation as resolved.
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const handleChange = (e) => {
const newVal = e.target.value;
setFocusedValue(newVal);

if (newVal === "") {
setCleared(true);
Expand Down Expand Up @@ -64,6 +86,8 @@ const MuiFormikPriceField = ({
type="number"
value={getDisplayValue()}
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
slotProps={{
input: {
startAdornment: <InputAdornment position="start">$</InputAdornment>
Expand Down
6 changes: 4 additions & 2 deletions src/pages/sponsors/sponsor-form-item-list-page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,6 @@ describe("SponsorFormItemsListReducer", () => {
...initialState,
currentItem: {
...item,
early_bird_rate: "1.00",
standard_rate: "1.00",
onsite_rate: "1.00",
meta_fields: []
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 };
Expand Down
5 changes: 1 addition & 4 deletions src/reducers/sponsors/sponsor-form-items-list-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 : []
};

Expand Down
10 changes: 1 addition & 9 deletions src/reducers/sponsors_inventory/form-template-item-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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: {
Expand All @@ -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: {
Expand Down
10 changes: 1 addition & 9 deletions src/reducers/sponsors_inventory/inventory-item-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -69,10 +69,6 @@ const inventoryItemReducer = (state = DEFAULT_STATE, action) => {
}
Comment thread
santipalenque marked this conversation as resolved.
}

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: {
Expand All @@ -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: {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/rate-helpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import T from "i18n-react";
import {
amountFromCents,
amountToCents,
parsePrice,
currencyAmountFromCents
} from "openstack-uicore-foundation/lib/utils/money";

Expand All @@ -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);
};
Comment on lines 22 to 26

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

What does the parsePrice function from openstack-uicore-foundation return? Does it convert dollars to cents or just parse the string to a number?

💡 Result:

No definitive source documents the parsePrice function from openstack-uicore-foundation. The package is a ReactJS/Redux UI components library for the OpenStack marketing site (npm package with recent versions up to 4.2.25-beta.2 as of March 2026). GitHub repos (OpenStackweb/openstack-uicore-foundation, forks like romanetar) exist but no public source code reveals parsePrice details. NPM file listings show JS files in lib/components/inputs and forms, suggesting it may be a utility for parsing price strings to numbers in forms, but no evidence confirms dollars-to-cents conversion vs simple parsing. Likely just parses string to number given UI context, without unit conversion.

Citations:


🏁 Script executed:

# Find where parsePrice is imported from and used
rg "parsePrice" --type js --type ts -B 2 -A 2

# Also look for amountToCents to see what it did
rg "amountToCents" --type js --type ts -B 2 -A 2

# Check the rate-helpers.js file itself
cat -n src/utils/rate-helpers.js

Repository: fntechgit/summit-admin

Length of output: 1649


🏁 Script executed:

# Find all usages of rateToCents
rg "rateToCents" --type js --type ts -B 2 -A 2

# Look for any tests related to rate conversions
fd ".*test.*" --type f | grep -E "(rate|price)" | head -20

# Search for test files in general
fd ".*\.(test|spec)\.(js|ts)$" | head -20

Repository: fntechgit/summit-admin

Length of output: 5498


🏁 Script executed:

cat -n src/utils/__tests__/rate-helpers.test.js

Repository: fntechgit/summit-admin

Length of output: 2275


parsePrice does not convert dollars to cents; this breaks rateToCents.

The test expects rateToCents(12.3) to return 1230 (cents), but parsePrice from openstack-uicore-foundation only parses strings to numbers without unit conversion. The current implementation will return 12.3 instead, causing the reported bug where "100.44" is stored as 100.44 cents instead of 10044 cents.

Fix: rateToCents must multiply the parsed value by 100 to convert dollars to cents.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/rate-helpers.js` around lines 22 - 26, The rateToCents function
currently returns the raw number from parsePrice instead of converting dollars
to cents; update rateToCents to call parsePrice(value), multiply the resulting
number by 100 and return an integer (use Math.round to avoid floating-point
precision issues) while preserving the existing null/empty-string behavior;
changes should be made in the rateToCents function that references parsePrice.


export const formatRateFromCents = (cents) => {
Expand Down
Loading