From 02e2764373ee3b1b979527afe6b3b51bd781f93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 27 Nov 2025 12:33:41 -0300 Subject: [PATCH 01/16] feat: add new page and route for sponsor customized form managed items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-forms-actions.js | 233 +++++++++++ src/i18n/en.json | 20 +- src/layouts/sponsor-id-layout.js | 9 +- src/pages/sponsors/edit-sponsor-page.js | 28 +- .../sponsor-form-item-from-inventory.js | 271 +++++++++++++ .../sponsor-forms-manage-items.js | 383 ++++++++++++++++++ src/pages/sponsors/sponsor-forms-tab/index.js | 5 +- ...nsor-customized-form-items-list-reducer.js | 214 ++++++++++ src/store.js | 4 +- src/utils/constants.js | 2 + 10 files changed, 1163 insertions(+), 6 deletions(-) create mode 100644 src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js create mode 100644 src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js create mode 100644 src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index e54fc2134..f5a8f3d9c 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -65,6 +65,23 @@ export const SPONSOR_CUSTOMIZED_FORM_DELETED = export const SPONSOR_CUSTOMIZED_FORM_ARCHIVED_CHANGED = "SPONSOR_CUSTOMIZED_FORM_ARCHIVED_CHANGED"; +export const RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS = + "RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS"; +export const REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS = + "REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS"; +export const SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED = + "SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED"; +export const SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED = + "SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED"; +export const SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED = + "SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED"; +export const UPDATE_SPONSOR_FORM_MANAGED_ITEM = + "UPDATE_SPONSOR_FORM_MANAGED_ITEM"; +export const SPONSOR_FORM_MANAGED_ITEM_UPDATED = + "SPONSOR_FORM_MANAGED_ITEM_UPDATED"; +export const SPONSOR_FORM_MANAGED_ITEM_ADDED = + "SPONSOR_FORM_MANAGED_ITEM_ADDED"; + // ITEMS export const REQUEST_SPONSOR_FORM_ITEMS = "REQUEST_SPONSOR_FORM_ITEMS"; export const RECEIVE_SPONSOR_FORM_ITEMS = "RECEIVE_SPONSOR_FORM_ITEMS"; @@ -633,6 +650,60 @@ export const getSponsorCustomizedForm = }); }; +export const getSponsorCustomizedFormItems = + ( + formId, + term = "", + page = DEFAULT_CURRENT_PAGE, + perPage = DEFAULT_PER_PAGE, + order = "id", + orderDir = DEFAULT_ORDER_DIR, + hideArchived = false + ) => + async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + const accessToken = await getAccessTokenSafely(); + const filter = []; + + dispatch(startLoading()); + + if (term) { + const escapedTerm = escapeFilterValue(term); + filter.push(`name=@${escapedTerm},code=@${escapedTerm}`); + } + + const params = { + page, + per_page: perPage, + access_token: accessToken + }; + + if (hideArchived) filter.push("is_archived==0"); + + if (filter.length > 0) { + params["filter[]"] = filter; + } + + // order + if (order != null && orderDir != null) { + const orderDirSign = orderDir === 1 ? "" : "-"; + params.order = `${orderDirSign}${order}`; + } + + return getRequest( + createAction(REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS), + createAction(RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items`, + authErrorHandler + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); + }; + export const saveSponsorCustomizedForm = (entity) => async (dispatch, getState) => { const { currentSummitState, currentSponsorState } = getState(); @@ -1205,3 +1276,165 @@ export const addInventoryItems = dispatch(stopLoading()); }); }; + +export const saveSponsorFormManagedItem = + (formId, entity) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const accessToken = await getAccessTokenSafely(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + + dispatch(startLoading()); + + const params = { + access_token: accessToken + }; + + if (entity.id) { + putRequest( + createAction(UPDATE_SPONSOR_FORM_MANAGED_ITEM), + createAction(SPONSOR_FORM_MANAGED_ITEM_UPDATED), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${entity.id}`, + entity, + snackbarErrorHandler, + entity + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate( + "edit_sponsor.forms_tab.form_manage_items.item_updated" + ) + }) + ); + }); + } else { + const successMessage = { + title: T.translate("general.done"), + html: T.translate( + "edit_sponsor.forms_tab.form_manage_items.item_created" + ), + type: "success" + }; + + postRequest( + createAction(UPDATE_SPONSOR_FORM_MANAGED_ITEM), + createAction(SPONSOR_FORM_MANAGED_ITEM_ADDED), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${entity.id}`, + entity, + snackbarErrorHandler, + entity + )(params)(dispatch).then(() => { + dispatch(showMessage(successMessage)); + }); + } + }; + +export const getSponsorFormManagedItem = + (formId, itemId) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const accessToken = await getAccessTokenSafely(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + + dispatch(startLoading()); + + const params = { + access_token: accessToken + }; + + return getRequest( + null, + createAction(RECEIVE_SPONSOR_CUSTOMIZED_FORM), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${itemId}`, + authErrorHandler + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); + }; + +export const addSponsorFormItems = + (formId, itemIds) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const accessToken = await getAccessTokenSafely(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + + dispatch(startLoading()); + + const params = { + access_token: accessToken + }; + + return postRequest( + null, + createAction(SPONSOR_FORM_ITEMS_ADDED), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/clone`, + { inventory_item_ids: itemIds }, + snackbarErrorHandler + )(params)(dispatch) + .then(() => { + dispatch(getSponsorCustomizedFormItems(formId)); + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate( + "sponsor_form_item_list.add_from_inventory.items_added" + ) + }) + ); + }) + .catch(console.log) // need to catch promise reject + .finally(() => { + dispatch(stopLoading()); + }); + }; + +export const archiveSponsorCustomizedFormItem = + (formId, itemId) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const accessToken = await getAccessTokenSafely(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + const params = { access_token: accessToken }; + + return putRequest( + null, + createAction(SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${itemId}/archive`, + null, + snackbarErrorHandler + )(params)(dispatch); + }; + +export const unarchiveSponsorCustomizedFormItem = + (formId, itemId) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const accessToken = await getAccessTokenSafely(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + const params = { access_token: accessToken }; + + dispatch(startLoading()); + + return deleteRequest( + null, + createAction(SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED)({ itemId }), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${itemId}/archive`, + null, + snackbarErrorHandler + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); + }; diff --git a/src/i18n/en.json b/src/i18n/en.json index 3ae951780..313beca31 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2432,6 +2432,24 @@ "error": "There was a problem creating the forms, please try again.", "archived": "Form successfully archived.", "unarchived": "Form successfully unarchived." + }, + "form_manage_items": { + "add_item": "Add Item", + "add_item_inventory": "Add Item from Inventory", + "alert_info": "You can add or archive items from the list. To edit an item click on the item's Edit botton. You can also change only a rate by clicking on it.", + "select_items": "Select items", + "code": "Code", + "name": "Name", + "early_bird_rate": "Early Bird Rate", + "standard_rate": "Standard Rate", + "onsite_rate": "On site rate", + "default_quantity": "Default Quantity", + "add_selected": "Add Selected Items", + "item_updated": "Form item created successfully", + "item_created": "Form item {item} updated successfully", + "placeholder": { + "search": "Search..." + } } }, "cart_tab": { @@ -2584,7 +2602,7 @@ "code": "Code", "name": "Name", "early_bird_rate": "Early bird rate", - "standard_rate": "Standad rate", + "standard_rate": "Standard rate", "onsite_rate": "On site rate", "save": "Add selected items", "items_added": "Items added successfully." diff --git a/src/layouts/sponsor-id-layout.js b/src/layouts/sponsor-id-layout.js index ae3eee227..e8c25a1d0 100644 --- a/src/layouts/sponsor-id-layout.js +++ b/src/layouts/sponsor-id-layout.js @@ -3,6 +3,7 @@ import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; import { Breadcrumb } from "react-breadcrumbs"; import { Switch, Route } from "react-router-dom"; +import { Breadcrumb } from "react-breadcrumbs"; import EditSponsorPage from "../pages/sponsors/edit-sponsor-page"; import { getSponsor, resetSponsorForm } from "../actions/sponsor-actions"; import EditAdSponsorPage from "../pages/sponsors/edit-advertisement-sponsor-page"; @@ -141,7 +142,13 @@ class SponsorIdLayout extends React.Component { )} /> - + + + + diff --git a/src/pages/sponsors/edit-sponsor-page.js b/src/pages/sponsors/edit-sponsor-page.js index a69defbb8..254a2388e 100644 --- a/src/pages/sponsors/edit-sponsor-page.js +++ b/src/pages/sponsors/edit-sponsor-page.js @@ -43,6 +43,8 @@ import SponsorUsersListPerSponsorPage from "./sponsor-users-list-per-sponsor"; import SponsorFormsTab from "./sponsor-forms-tab"; import SponsorBadgeScans from "./sponsor-badge-scans"; import SponsorCartTab from "./sponsor-cart-tab"; +import SponsorFormsManageItems from "./sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items"; +import { FOUR } from "../../utils/constants"; const CustomTabPanel = (props) => { const { children, value, index, ...other } = props; @@ -69,6 +71,9 @@ const EditSponsorPage = (props) => { const { entity, member, + history, + location, + match, currentSummit, resetSponsorForm, getSponsorAdvertisements, @@ -92,10 +97,16 @@ const EditSponsorPage = (props) => { getExtraQuestionMeta } = props; - const [selectedTab, setSelectedTab] = useState(0); + const [selectedTab, setSelectedTab] = useState( + location.pathname.includes("/sponsor-forms/") && + location.pathname.includes("/items") + ? FOUR + : 0 + ); const handleTabChange = (event, newValue) => { setSelectedTab(newValue); + history.push(`/app/summits/${currentSummit.id}/sponsors/${entity.id}`); }; useEffect(() => { @@ -126,6 +137,10 @@ const EditSponsorPage = (props) => { { label: T.translate("edit_sponsor.tab.badge_scans"), value: 7 } ]; + const sponsorFormItemRoute = + location.pathname.includes("/sponsor-forms/") && + location.pathname.includes("/items"); + return ( @@ -145,6 +160,7 @@ const EditSponsorPage = (props) => { key={t.value} label={t.label} value={t.value} + onClick={() => handleTabChange(null, t.value)} sx={{ fontSize: "1.4rem", lineHeight: "1.8rem", @@ -184,7 +200,15 @@ const EditSponsorPage = (props) => { - + {sponsorFormItemRoute ? ( + + ) : ( + + )} diff --git a/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js new file mode 100644 index 000000000..f5d29aaab --- /dev/null +++ b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js @@ -0,0 +1,271 @@ +import React, { useEffect, useState } from "react"; +import T from "i18n-react/dist/i18n-react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { + Box, + Button, + Checkbox, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Divider, + FormControlLabel, + Grid2, + IconButton, + Tooltip, + Typography +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import ImageIcon from "@mui/icons-material/Image"; +import SwapVertIcon from "@mui/icons-material/SwapVert"; +import SearchInput from "../../../../../components/mui/search-input"; +import { + DEFAULT_CURRENT_PAGE, + DEFAULT_PER_PAGE +} from "../../../../../utils/constants"; + +import { getInventoryItems } from "../../../../../actions/inventory-item-actions"; +import MuiTable from "../../../../../components/mui/table/mui-table"; +import { amountFromCents } from "../../../../../utils/currency"; +import MenuButton from "../../../../../components/mui/menu-button"; + +const SponsorFormItemFromInventoryPopup = ({ + open, + inventoryItems, + term, + order, + perPage, + orderDir, + currentPage, + totalInventoryItems, + onSave, + onClose, + getInventoryItems +}) => { + const [selectedRows, setSelectedRows] = useState([]); + + useEffect(() => { + getInventoryItems("", 1, DEFAULT_PER_PAGE, "id", 1); + }, []); + + const handleSort = (key, dir) => { + getInventoryItems(term, 1, DEFAULT_PER_PAGE, key, dir); + }; + + const handlePageChange = (page) => { + getInventoryItems(term, page, perPage, order, orderDir); + }; + + const handlePerPageChange = (newPerPage) => { + getInventoryItems(term, DEFAULT_CURRENT_PAGE, newPerPage, order, orderDir); + }; + + const handleClose = () => { + setSelectedRows([]); + onClose(); + }; + + const handleOnCheck = (rowId, checked) => { + if (checked) { + setSelectedRows([...selectedRows, rowId]); + } else { + setSelectedRows(selectedRows.filter((r) => r !== rowId)); + } + }; + + const handleOnSearch = (searchTerm) => { + getInventoryItems(searchTerm, 1, DEFAULT_PER_PAGE, "id", 1); + }; + + const handleOnSave = () => { + onSave(selectedRows); + }; + + const columns = [ + { + columnKey: "select", + header: "", + width: 30, + align: "center", + render: (row) => ( + handleOnCheck(row.id, ev.target.checked)} + /> + } + /> + ) + }, + { + columnKey: "code", + header: T.translate("edit_sponsor.forms_tab.form_manage_items.code"), + sortable: false + }, + { + columnKey: "name", + header: T.translate("edit_sponsor.forms_tab.form_manage_items.name"), + sortable: false + }, + { + columnKey: "early_bird_rate", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.early_bird_rate" + ), + sortable: false, + render: (row) => `$ ${amountFromCents(row.early_bird_rate)}` + }, + { + columnKey: "standard_rate", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.standard_rate" + ), + sortable: false, + render: (row) => `$ ${amountFromCents(row.standard_rate)}` + }, + { + columnKey: "onsite_rate", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.onsite_rate" + ), + sortable: false, + render: (row) => `$ ${amountFromCents(row.onsite_rate)}` + }, + { + columnKey: "default_quantity", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.default_quantity" + ), + sortable: false + }, + { + columnKey: "images", + header: "", + width: 40, + align: "center", + render: (row) => + row.images?.length > 0 ? ( + + + + window.open( + row.images[0].file_url, + "_blank", + "noopener,noreferrer" + ) + } + /> + + + ) : null + } + ]; + + const tableOptions = { + sortCol: order, + sortDir: orderDir + }; + + return ( + + + + {T.translate( + "edit_sponsor.forms_tab.form_manage_items.add_item_inventory" + )} + + handleClose()}> + + + + + + + + {selectedRows.length} items selected + + + + handleSort("name", 1) }, + { label: "Z-A", onClick: () => handleSort("name", 0) } + ]} + > + sort by + + + + + + + + + {inventoryItems.length > 0 && ( + + + + )} + + + + + + + ); +}; + +SponsorFormItemFromInventoryPopup.propTypes = { + onClose: PropTypes.func.isRequired, + onSave: PropTypes.func.isRequired +}; + +const mapStateToProps = ({ currentInventoryItemListState }) => ({ + ...currentInventoryItemListState +}); + +export default connect(mapStateToProps, { + getInventoryItems +})(SponsorFormItemFromInventoryPopup); 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 new file mode 100644 index 000000000..d23b06abc --- /dev/null +++ b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js @@ -0,0 +1,383 @@ +/** + * Copyright 2024 OpenStack Foundation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ + +import React, { useEffect, useState } from "react"; +import { connect } from "react-redux"; +import T from "i18n-react/dist/i18n-react"; +import { + Box, + Button, + Checkbox, + FormControlLabel, + FormGroup, + Grid2, + IconButton, + Tooltip +} from "@mui/material"; +import AddIcon from "@mui/icons-material/Add"; +import ImageIcon from "@mui/icons-material/Image"; +import { + addSponsorFormItems, + archiveSponsorCustomizedFormItem, + getSponsorCustomizedFormItems, + saveSponsorFormManagedItem, + unarchiveSponsorCustomizedFormItem, + getSponsorFormManagedItem +} from "../../../../../actions/sponsor-forms-actions"; +import { resetInventoryItemForm } from "../../../../../actions/inventory-item-actions"; +import CustomAlert from "../../../../../components/mui/custom-alert"; +import SearchInput from "../../../../../components/mui/search-input"; +import MuiTableEditable from "../../../../../components/mui/editable-table/mui-table-editable"; +import SponsorInventoryDialog from "../../../../sponsors_inventory/popup/sponsor-inventory-popup"; +import SponsorFormItemFromInventoryPopup from "./sponsor-form-item-from-inventory"; +import { parsePrice } from "../../../../../utils/currency"; +import { ONE_HUNDRED } from "../../../../../utils/constants"; +// import FormTemplateDialog from "../../../../sponsors_inventory/popup/form-template-popup"; + +const SponsorFormsManageItems = ({ + term, + match, + hideArchived, + items, + order, + orderDir, + perPage, + currentPage, + totalCount, + getSponsorCustomizedFormItems, + currentInventoryItem, + resetInventoryItemForm, + addSponsorFormItems, + saveSponsorFormManagedItem, + archiveSponsorCustomizedFormItem, + unarchiveSponsorCustomizedFormItem, + getSponsorFormManagedItem +}) => { + const [openPopup, setOpenPopup] = useState(null); + + const handleClose = () => { + setOpenPopup(null); + }; + + const formId = match.params.form_id; + + useEffect(() => { + getSponsorCustomizedFormItems(formId); + }, []); + + const handleManagedPageChange = (page) => { + const { perPage, order, orderDir } = items; + getSponsorCustomizedFormItems( + formId, + term, + page, + perPage, + order, + orderDir, + hideArchived + ); + }; + + const handleManagedSort = (key, dir) => { + const { currentPage, perPage } = items; + getSponsorCustomizedFormItems( + formId, + term, + currentPage, + perPage, + key, + dir, + hideArchived + ); + }; + + const handleSearch = (searchTerm) => { + getSponsorCustomizedFormItems( + formId, + searchTerm, + currentPage, + perPage, + order, + orderDir, + hideArchived + ); + }; + + const handleItemSave = (item) => { + saveSponsorFormManagedItem(formId, item).then(() => + getSponsorCustomizedFormItems( + formId, + term, + currentPage, + perPage, + order, + orderDir, + hideArchived + ) + ); + setOpenPopup(null); + }; + + const handleOpenItemPopup = () => { + resetInventoryItemForm(); + setOpenPopup("add_item"); + }; + + const handleArchiveItem = (item) => + item.is_archived + ? unarchiveSponsorCustomizedFormItem(formId, item.id) + : archiveSponsorCustomizedFormItem(formId, item.id); + + const handleHideArchivedItens = (ev) => { + getSponsorCustomizedFormItems( + formId, + term, + items.currentPage, + items.perPage, + items.order, + items.orderDir, + ev.target.checked + ); + }; + + const handleAddFromInventory = (itemsId) => { + addSponsorFormItems(formId, itemsId).then(() => handleClose()); + }; + + const handleCellEdit = (rowId, column, value) => { + const tmpEntity = { + id: rowId, + [column]: Math.round(parsePrice(value) * ONE_HUNDRED) + }; + saveSponsorFormManagedItem(formId, tmpEntity); + }; + + const handleRowEdit = (row) => { + getSponsorFormManagedItem(formId, row.id).then(() => + setOpenPopup("add_item") + ); + }; + + const sponsorItemColumns = [ + { + columnKey: "code", + header: T.translate("edit_sponsor.forms_tab.form_manage_items.code"), + sortable: false + }, + { + columnKey: "name", + header: T.translate("edit_sponsor.forms_tab.form_manage_items.name"), + sortable: false + }, + { + columnKey: "early_bird_rate", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.early_bird_rate" + ), + sortable: false, + editable: true + }, + { + columnKey: "standard_rate", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.standard_rate" + ), + sortable: false, + editable: true + }, + { + columnKey: "onsite_rate", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.onsite_rate" + ), + sortable: false, + editable: true + }, + { + columnKey: "default_quantity", + header: T.translate( + "edit_sponsor.forms_tab.form_manage_items.default_quantity" + ), + sortable: false + }, + { + columnKey: "images", + header: "", + width: 40, + align: "center", + render: (row) => + row.images?.length > 0 ? ( + + + + window.open( + row.images[0].file_url, + "_blank", + "noopener,noreferrer" + ) + } + /> + + + ) : null + }, + { + columnKey: "archive", + header: "", + width: 70, + align: "center", + render: (row) => ( + + ), + dottedBorder: true + } + ]; + + return ( + + + + + {totalCount} items + + + + + } + label={T.translate("edit_sponsor.forms_tab.hide_archived")} + /> + + + + + + + + + + + + + +
+ +
+ + {/* ADD ITEM */} + + + +
+ ); +}; + +const mapStateToProps = ({ sponsorCustomizedFormItemsListState }) => ({ + ...sponsorCustomizedFormItemsListState, + currentInventoryItem: sponsorCustomizedFormItemsListState.currentItem +}); + +export default connect(mapStateToProps, { + getSponsorCustomizedFormItems, + resetInventoryItemForm, + addSponsorFormItems, + saveSponsorFormManagedItem, + getSponsorFormManagedItem, + archiveSponsorCustomizedFormItem, + unarchiveSponsorCustomizedFormItem +})(SponsorFormsManageItems); diff --git a/src/pages/sponsors/sponsor-forms-tab/index.js b/src/pages/sponsors/sponsor-forms-tab/index.js index 06920a737..b4b679cf8 100644 --- a/src/pages/sponsors/sponsor-forms-tab/index.js +++ b/src/pages/sponsors/sponsor-forms-tab/index.js @@ -41,6 +41,7 @@ import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants"; const SponsorFormsTab = ({ term, + history, hideArchived, managedForms, customizedForms, @@ -111,7 +112,9 @@ const SponsorFormsTab = ({ : archiveSponsorCustomizedForm(item.id); const handleManageItems = (item) => { - console.log("MANAGE ITEMS : ", item); + history.push( + `/app/summits/${summitId}/sponsors/${sponsor.id}/sponsor-forms/${item.id}/items` + ); }; const handleCustomizedEdit = (item) => { diff --git a/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js b/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js new file mode 100644 index 000000000..1d5aab02a --- /dev/null +++ b/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js @@ -0,0 +1,214 @@ +/** + * Copyright 2019 OpenStack Foundation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ + +import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; +import { + RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS, + REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS, + RECEIVE_SPONSOR_CUSTOMIZED_FORM, + // RECEIVE_SPONSOR_FORM_ITEM, + // RESET_SPONSOR_FORM_ITEM, + SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED, + SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED, + SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED, + SPONSOR_FORM_MANAGED_ITEM_UPDATED +} from "../../actions/sponsor-forms-actions"; +import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; +import { CENTS_FACTOR, DECIMAL_DIGITS } from "../../utils/constants"; + +const DEFAULT_STATE = { + items: [], + hideArchived: false, + order: "name", + orderDir: 1, + currentPage: 1, + lastPage: 1, + perPage: 10, + totalCount: 0, + currentItem: { + code: "", + name: "", + description: "", + early_bird_rate: "", + standard_rate: "", + onsite_rate: "", + quantity_limit_per_show: "", + quantity_limit_per_sponsor: "", + default_quantity: "", + images: [], + meta_fields: [ + { + name: "", + type: "Text", + is_required: false, + values: [] + } + ] + } +}; + +const sponsorCustomizedFormItemsListReducer = ( + state = DEFAULT_STATE, + action +) => { + const { type, payload } = action; + + switch (type) { + case SET_CURRENT_SUMMIT: + case LOGOUT_USER: { + return DEFAULT_STATE; + } + case REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS: { + const { order, orderDir, page, hideArchived } = payload; + + return { + ...state, + order, + orderDir, + items: [], + currentPage: page, + hideArchived + }; + } + case RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS: { + const { + current_page: currentPage, + total, + last_page: lastPage + } = payload.response; + + const items = payload.response.data.map((a) => ({ + id: a.id, + code: a.code, + name: a.name, + early_bird_rate: `$${(a.early_bird_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + standard_rate: `$${(a.standard_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + onsite_rate: `$${(a.onsite_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + default_quantity: a.default_quantity, + is_archived: a.is_archived, + images: a.images + })); + + return { + ...state, + items, + currentPage, + totalCount: total, + lastPage + }; + } + case RECEIVE_SPONSOR_CUSTOMIZED_FORM: { + const item = payload.response; + + const currentItem = { + ...item, + early_bird_rate: `$${(item.early_bird_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + standard_rate: `$${(item.standard_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + onsite_rate: `$${(item.onsite_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + meta_fields: + item.meta_fields.length > 0 + ? item.meta_fields + : [ + { + name: "", + type: "Text", + is_required: false, + values: [] + } + ] + }; + return { ...state, currentItem }; + } + case SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED: { + const { itemId } = payload; + const items = state.items.filter((it) => it.id !== itemId); + + return { ...state, items }; + } + case SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED: { + const { id: itemId } = payload.response; + + const items = state.items.map((item) => + item.id === itemId ? { ...item, is_archived: true } : item + ); + + return { ...state, items }; + } + case SPONSOR_CUSTOMIZED_FORM_ITEM_UNARCHIVED: { + const { itemId } = payload; + + const items = state.items.map((item) => + item.id === itemId ? { ...item, is_archived: false } : item + ); + + return { ...state, items }; + } + case SPONSOR_FORM_MANAGED_ITEM_UPDATED: { + const updatedItem = payload.response; + const items = state.items.map((item) => + item.id === updatedItem.id + ? { + id: updatedItem.id, + code: updatedItem.code, + name: updatedItem.name, + early_bird_rate: `$${( + updatedItem.early_bird_rate / CENTS_FACTOR + ).toFixed(DECIMAL_DIGITS)}`, + standard_rate: `$${( + updatedItem.standard_rate / CENTS_FACTOR + ).toFixed(DECIMAL_DIGITS)}`, + onsite_rate: `$${(updatedItem.onsite_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + default_quantity: updatedItem.default_quantity, + is_archived: updatedItem.is_archived, + images: updatedItem.images + } + : { + id: item.id, + code: item.code, + name: item.name, + early_bird_rate: `$${( + item.early_bird_rate / CENTS_FACTOR + ).toFixed(DECIMAL_DIGITS)}`, + standard_rate: `$${(item.standard_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + onsite_rate: `$${(item.onsite_rate / CENTS_FACTOR).toFixed( + DECIMAL_DIGITS + )}`, + default_quantity: item.default_quantity, + is_archived: item.is_archived, + images: item.images + } + ); + return { ...state, items }; + } + default: + return state; + } +}; + +export default sponsorCustomizedFormItemsListReducer; diff --git a/src/store.js b/src/store.js index 0d224d75f..a0d03ee3d 100644 --- a/src/store.js +++ b/src/store.js @@ -165,6 +165,7 @@ import eventRSVPReducer from "./reducers/events/event-rsvp-reducer.js"; import sponsorPageFormsListReducer from "./reducers/sponsors/sponsor-page-forms-list-reducer.js"; import sponsorCustomizedFormReducer from "./reducers/sponsors/sponsor-customized-form-reducer.js"; import sponsorPageCartListReducer from "./reducers/sponsors/sponsor-page-cart-list-reducer"; +import sponsorCustomizedFormItemsListReducer from "./reducers/sponsors/sponsor-customized-form-items-list-reducer.js"; // default: localStorage if web, AsyncStorage if react-native @@ -252,6 +253,7 @@ const reducers = persistCombineReducers(config, { sponsorPageFormsListState: sponsorPageFormsListReducer, sponsorPageCartListState: sponsorPageCartListReducer, sponsorCustomizedFormState: sponsorCustomizedFormReducer, + sponsorCustomizedFormItemsListState: sponsorCustomizedFormItemsListReducer, currentSponsorPromocodeListState: sponsorPromocodeListReducer, currentSponsorExtraQuestionState: sponsorExtraQuestionReducer, currentSponsorAdvertisementState: sponsorAdvertisementReducer, @@ -302,7 +304,7 @@ const reducers = persistCombineReducers(config, { sponsoredProjectState: sponsoredProjectReducer, sponsoredProjectSponsorshipTypeState: sponsoredProjectSponsorshipTypeReducer, sponsoredProjectSponsorshipTypeSupportingCompanyState: - sponsoredProjectSponsorshipTypeSupportingCompanyReducer, + sponsoredProjectSponsorshipTypeSupportingCompanyReducer, scheduleSettingsState: scheduleSettingsReducer, scheduleSettingsListState: scheduleSettingsListReducer, currentSelectionPlanExtraQuestionState: selectionPlanExtraQuestionReducer, diff --git a/src/utils/constants.js b/src/utils/constants.js index a6d2d7139..5e832aa9c 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -139,6 +139,8 @@ export const DECIMAL_DIGITS = 2; export const TWO = 2; +export const FOUR = 4; + export const TEN = 10; export const ONE_HUNDRED = 100; From 79d1967f34593821536cb737aa6bb7911a0c064d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Sat, 6 Dec 2025 00:54:52 -0300 Subject: [PATCH 02/16] fix: adjust ui, texts, add save and fix reducer data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-forms-actions.js | 110 ++++++++++++++++-- src/i18n/en.json | 2 +- .../sponsor-forms-manage-items.js | 49 +++++--- ...nsor-customized-form-items-list-reducer.js | 80 +++++++------ 4 files changed, 176 insertions(+), 65 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index f5a8f3d9c..efd958711 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -32,6 +32,7 @@ import { DEFAULT_PER_PAGE } from "../utils/constants"; import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions"; +import { amountToCents } from "../utils/currency"; export const REQUEST_SPONSOR_FORMS = "REQUEST_SPONSOR_FORMS"; export const RECEIVE_SPONSOR_FORMS = "RECEIVE_SPONSOR_FORMS"; @@ -69,6 +70,8 @@ export const RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS = "RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS"; export const REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS = "REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS"; +export const RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM = + "RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM"; export const SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED = "SPONSOR_CUSTOMIZED_FORM_ITEM_DELETED"; export const SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED = @@ -81,6 +84,12 @@ export const SPONSOR_FORM_MANAGED_ITEM_UPDATED = "SPONSOR_FORM_MANAGED_ITEM_UPDATED"; export const SPONSOR_FORM_MANAGED_ITEM_ADDED = "SPONSOR_FORM_MANAGED_ITEM_ADDED"; +export const SPONSOR_FORM_MANAGED_ITEM_DELETED = + "SPONSOR_FORM_MANAGED_ITEM_DELETED"; +export const SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED = + "SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED"; +export const RESET_SPONSOR_FORM_MANAGED_ITEM = + "RESET_SPONSOR_FORM_MANAGED_ITEM"; // ITEMS export const REQUEST_SPONSOR_FORM_ITEMS = "REQUEST_SPONSOR_FORM_ITEMS"; @@ -1220,6 +1229,7 @@ const normalizeItem = (entity) => { if (images) { normalizedEntity.images = images?.filter((img) => img.file_path); + console.log("CHECK!", normalizedEntity); } if (early_bird_rate === "" || typeof early_bird_rate === "undefined") @@ -1292,12 +1302,16 @@ export const saveSponsorFormManagedItem = access_token: accessToken }; + const normalizedEntity = normalizeManagedItem(entity); + + console.log("TO SAVE ENTITY NORMALIZED", normalizedEntity); + if (entity.id) { putRequest( createAction(UPDATE_SPONSOR_FORM_MANAGED_ITEM), createAction(SPONSOR_FORM_MANAGED_ITEM_UPDATED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${entity.id}`, - entity, + normalizedEntity, snackbarErrorHandler, entity )(params)(dispatch).then(() => { @@ -1323,16 +1337,92 @@ export const saveSponsorFormManagedItem = postRequest( createAction(UPDATE_SPONSOR_FORM_MANAGED_ITEM), createAction(SPONSOR_FORM_MANAGED_ITEM_ADDED), - `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${entity.id}`, - entity, + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items`, + normalizedEntity, snackbarErrorHandler, entity )(params)(dispatch).then(() => { - dispatch(showMessage(successMessage)); + dispatch(snackbarSuccessHandler(successMessage)); }); } }; +export const resetSponsorFormManagedItem = () => (dispatch) => { + dispatch(createAction(RESET_SPONSOR_FORM_MANAGED_ITEM)({})); +}; + +const normalizeManagedItem = (entity) => { + const normalizedEntity = { ...entity }; + normalizedEntity.meta_fields = normalizedEntity.meta_fields?.filter( + (mf) => mf.name + ); + normalizedEntity.images = normalizedEntity.images?.filter( + (img) => img.file_path + ); + + if ( + entity.early_bird_rate === "" || + typeof entity.early_bird_rate === "undefined" + ) + delete normalizedEntity.early_bird_rate; + else + normalizedEntity.early_bird_rate = amountToCents( + normalizedEntity.early_bird_rate + ); + + if ( + entity.standard_rate === "" || + typeof entity.standard_rate === "undefined" + ) + delete normalizedEntity.standard_rate; + else + normalizedEntity.standard_rate = amountToCents( + normalizedEntity.standard_rate + ); + + if (entity.onsite_rate === "" || typeof entity.onsite_rate === "undefined") + delete normalizedEntity.onsite_rate; + else + normalizedEntity.onsite_rate = amountToCents(normalizedEntity.onsite_rate); + + return normalizedEntity; +}; + +export const deleteSponsorFormManagedItem = + (formId, itemId) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const accessToken = await getAccessTokenSafely(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + + dispatch(startLoading()); + + const params = { + access_token: accessToken + }; + + return deleteRequest( + null, + createAction(SPONSOR_FORM_MANAGED_ITEM_DELETED)({ itemId }), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${itemId}`, + null, + snackbarErrorHandler + )(params)(dispatch) + .then(() => { + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("sponsor_forms.form_delete_success") + }) + ); + }) + .finally(() => { + dispatch(stopLoading()); + }); + }; + export const getSponsorFormManagedItem = (formId, itemId) => async (dispatch, getState) => { const { currentSummitState, currentSponsorState } = getState(); @@ -1350,7 +1440,7 @@ export const getSponsorFormManagedItem = return getRequest( null, - createAction(RECEIVE_SPONSOR_CUSTOMIZED_FORM), + createAction(RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${itemId}`, authErrorHandler )(params)(dispatch).then(() => { @@ -1358,7 +1448,7 @@ export const getSponsorFormManagedItem = }); }; -export const addSponsorFormItems = +export const addSponsorManagedFormItems = (formId, itemIds) => async (dispatch, getState) => { const { currentSummitState, currentSponsorState } = getState(); const accessToken = await getAccessTokenSafely(); @@ -1375,7 +1465,7 @@ export const addSponsorFormItems = return postRequest( null, - createAction(SPONSOR_FORM_ITEMS_ADDED), + createAction(SPONSOR_CUSTOMIZED_FORM_ITEMS_ADDED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/clone`, { inventory_item_ids: itemIds }, snackbarErrorHandler @@ -1407,13 +1497,17 @@ export const archiveSponsorCustomizedFormItem = } = currentSponsorState; const params = { access_token: accessToken }; + dispatch(startLoading()); + return putRequest( null, createAction(SPONSOR_CUSTOMIZED_FORM_ITEM_ARCHIVED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${itemId}/archive`, null, snackbarErrorHandler - )(params)(dispatch); + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); }; export const unarchiveSponsorCustomizedFormItem = diff --git a/src/i18n/en.json b/src/i18n/en.json index 313beca31..6f1c43ba8 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2445,7 +2445,7 @@ "onsite_rate": "On site rate", "default_quantity": "Default Quantity", "add_selected": "Add Selected Items", - "item_updated": "Form item created successfully", + "item_updated": "Form item updated successfully", "item_created": "Form item {item} updated successfully", "placeholder": { "search": "Search..." 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 d23b06abc..12725a8be 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 @@ -27,21 +27,22 @@ import { import AddIcon from "@mui/icons-material/Add"; import ImageIcon from "@mui/icons-material/Image"; import { - addSponsorFormItems, + addSponsorManagedFormItems, archiveSponsorCustomizedFormItem, getSponsorCustomizedFormItems, saveSponsorFormManagedItem, + deleteSponsorFormManagedItem, + resetSponsorFormManagedItem, unarchiveSponsorCustomizedFormItem, getSponsorFormManagedItem } from "../../../../../actions/sponsor-forms-actions"; -import { resetInventoryItemForm } from "../../../../../actions/inventory-item-actions"; import CustomAlert from "../../../../../components/mui/custom-alert"; import SearchInput from "../../../../../components/mui/search-input"; import MuiTableEditable from "../../../../../components/mui/editable-table/mui-table-editable"; import SponsorInventoryDialog from "../../../../sponsors_inventory/popup/sponsor-inventory-popup"; import SponsorFormItemFromInventoryPopup from "./sponsor-form-item-from-inventory"; import { parsePrice } from "../../../../../utils/currency"; -import { ONE_HUNDRED } from "../../../../../utils/constants"; +import { DEFAULT_CURRENT_PAGE } from "../../../../../utils/constants"; // import FormTemplateDialog from "../../../../sponsors_inventory/popup/form-template-popup"; const SponsorFormsManageItems = ({ @@ -56,9 +57,10 @@ const SponsorFormsManageItems = ({ totalCount, getSponsorCustomizedFormItems, currentInventoryItem, - resetInventoryItemForm, - addSponsorFormItems, + resetSponsorFormManagedItem, + addSponsorManagedFormItems, saveSponsorFormManagedItem, + deleteSponsorFormManagedItem, archiveSponsorCustomizedFormItem, unarchiveSponsorCustomizedFormItem, getSponsorFormManagedItem @@ -114,7 +116,7 @@ const SponsorFormsManageItems = ({ }; const handleItemSave = (item) => { - saveSponsorFormManagedItem(formId, item).then(() => + saveSponsorFormManagedItem(formId, item).then(() => { getSponsorCustomizedFormItems( formId, term, @@ -123,13 +125,14 @@ const SponsorFormsManageItems = ({ order, orderDir, hideArchived - ) - ); + ); + resetSponsorFormManagedItem(); + }); setOpenPopup(null); }; const handleOpenItemPopup = () => { - resetInventoryItemForm(); + resetSponsorFormManagedItem(); setOpenPopup("add_item"); }; @@ -151,13 +154,13 @@ const SponsorFormsManageItems = ({ }; const handleAddFromInventory = (itemsId) => { - addSponsorFormItems(formId, itemsId).then(() => handleClose()); + addSponsorManagedFormItems(formId, itemsId).then(() => handleClose()); }; const handleCellEdit = (rowId, column, value) => { const tmpEntity = { id: rowId, - [column]: Math.round(parsePrice(value) * ONE_HUNDRED) + [column]: parsePrice(value) }; saveSponsorFormManagedItem(formId, tmpEntity); }; @@ -168,6 +171,20 @@ const SponsorFormsManageItems = ({ ); }; + const handleRowDelete = (rowId) => { + deleteSponsorFormManagedItem(formId, rowId).then(() => + getSponsorCustomizedFormItems( + formId, + term, + DEFAULT_CURRENT_PAGE, + perPage, + order, + orderDir, + hideArchived + ) + ); + }; + const sponsorItemColumns = [ { columnKey: "code", @@ -271,7 +288,7 @@ const SponsorFormsManageItems = ({ mb: 2 }} > - + {totalCount} items @@ -311,7 +328,7 @@ const SponsorFormsManageItems = ({ {T.translate("edit_sponsor.forms_tab.form_manage_items.add_item")} - + - ), - dottedBorder: true } ]; diff --git a/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js b/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js index b2d371eee..975c1e289 100644 --- a/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js +++ b/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js @@ -172,8 +172,8 @@ const SponsorItemDialog = ({ name: "", type: "Text", is_required: false, - minimum_quantity: null, - maximum_quantity: null, + minimum_quantity: 0, + maximum_quantity: 0, values: [] } ]); @@ -182,7 +182,7 @@ const SponsorItemDialog = ({ } }; - if (fieldType.id) { + if (fieldType.id && onMetaFieldTypeDeleted) { onMetaFieldTypeDeleted(initialEntity.id, fieldType.id) .then(() => removeOrResetField()) .catch((err) => console.log("Error at delete field from API", err)); From 79952e470ae0b9fd58b30972a89623018dbddd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Tue, 16 Dec 2025 16:17:44 -0300 Subject: [PATCH 07/16] fix: adjust image validation, reload list after save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-forms-actions.js | 40 +++++++++---------- .../popup/sponsor-inventory-popup.js | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index 4ba878787..17e0e418d 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -1304,7 +1304,7 @@ export const saveSponsorFormManagedItem = const normalizedEntity = normalizeManagedItem(entity); if (entity.id) { - putRequest( + return putRequest( createAction(UPDATE_SPONSOR_FORM_MANAGED_ITEM), createAction(SPONSOR_FORM_MANAGED_ITEM_UPDATED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items/${entity.id}`, @@ -1322,26 +1322,26 @@ export const saveSponsorFormManagedItem = }) ); }); - } else { - const successMessage = { - title: T.translate("general.done"), - html: T.translate( - "edit_sponsor.forms_tab.form_manage_items.item_created" - ), - type: "success" - }; - - postRequest( - createAction(UPDATE_SPONSOR_FORM_MANAGED_ITEM), - createAction(SPONSOR_FORM_MANAGED_ITEM_ADDED), - `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items`, - normalizedEntity, - snackbarErrorHandler, - entity - )(params)(dispatch).then(() => { - dispatch(snackbarSuccessHandler(successMessage)); - }); } + + const successMessage = { + title: T.translate("general.done"), + html: T.translate( + "edit_sponsor.forms_tab.form_manage_items.item_created" + ), + type: "success" + }; + + return postRequest( + createAction(UPDATE_SPONSOR_FORM_MANAGED_ITEM), + createAction(SPONSOR_FORM_MANAGED_ITEM_ADDED), + `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items`, + normalizedEntity, + snackbarErrorHandler, + entity + )(params)(dispatch).then(() => { + dispatch(snackbarSuccessHandler(successMessage)); + }); }; export const resetSponsorFormManagedItem = () => (dispatch) => { diff --git a/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js b/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js index 975c1e289..f4d60d58d 100644 --- a/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js +++ b/src/pages/sponsors_inventory/popup/sponsor-inventory-popup.js @@ -69,7 +69,7 @@ const SponsorItemDialog = ({ code: yup.string().required(T.translate("validation.required")), name: yup.string().required(T.translate("validation.required")), description: yup.string().required(T.translate("validation.required")), - images: yup.array().min(1, T.translate("validation.required")), + images: yup.array(), early_bird_rate: decimalValidation(), standard_rate: decimalValidation(), onsite_rate: decimalValidation(), From cfeda3717d28acdff83986c36af5eefce669702a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Tue, 16 Dec 2025 16:45:28 -0300 Subject: [PATCH 08/16] fix: address changes from copilot suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-forms-actions.js | 14 +++----- src/i18n/en.json | 6 ++-- src/layouts/sponsor-id-layout.js | 2 +- src/pages/sponsors/edit-sponsor-page.js | 32 +++++++++++++------ .../sponsor-form-item-from-inventory.js | 11 ++++++- .../sponsor-forms-manage-items.js | 7 +--- ...nsor-customized-form-items-list-reducer.js | 14 +------- src/utils/constants.js | 11 +++++++ 8 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index 17e0e418d..7aa3ffe76 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -1357,27 +1357,21 @@ const normalizeManagedItem = (entity) => { (img) => img.file_path ); - if ( - entity.early_bird_rate === "" || - typeof entity.early_bird_rate === "undefined" - ) + if (entity.early_bird_rate === "" || entity.early_bird_rate === undefined) delete normalizedEntity.early_bird_rate; else normalizedEntity.early_bird_rate = amountToCents( normalizedEntity.early_bird_rate ); - if ( - entity.standard_rate === "" || - typeof entity.standard_rate === "undefined" - ) + if (entity.standard_rate === "" || entity.standard_rate === undefined) delete normalizedEntity.standard_rate; else normalizedEntity.standard_rate = amountToCents( normalizedEntity.standard_rate ); - if (entity.onsite_rate === "" || typeof entity.onsite_rate === "undefined") + if (entity.onsite_rate === "" || entity.onsite_rate === undefined) delete normalizedEntity.onsite_rate; else normalizedEntity.onsite_rate = amountToCents(normalizedEntity.onsite_rate); @@ -1478,7 +1472,7 @@ export const addSponsorManagedFormItems = }) ); }) - .catch(console.log) // need to catch promise reject + .catch(snackbarErrorHandler) // need to catch promise reject .finally(() => { dispatch(stopLoading()); }); diff --git a/src/i18n/en.json b/src/i18n/en.json index e0afda220..74d944614 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2436,7 +2436,7 @@ "form_manage_items": { "add_item": "Add Item", "add_item_inventory": "Add Item from Inventory", - "alert_info": "You can add or archive items from the list. To edit an item click on the item's Edit botton. You can also change only a rate by clicking on it.", + "alert_info": "You can add or archive items from the list. To edit an item click on the item's Edit button. You can also change only a rate by clicking on it.", "select_items": "Select items", "code": "Code", "name": "Name", @@ -2446,7 +2446,7 @@ "default_quantity": "Default Quantity", "add_selected": "Add Selected Items", "item_updated": "Form item updated successfully", - "item_created": "Form item {item} updated successfully", + "item_created": "Form item {item} created successfully", "placeholder": { "search": "Search..." } @@ -2560,7 +2560,7 @@ }, "sponsor_form_item_list": { "form_items": "Form Items", - "alert_info": "You can add or archive items from the list. To edit an item click on the item's Edit botton. You can also change only a rate by clicking on it.", + "alert_info": "You can add or archive items from the list. To edit an item click on the item's Edit button. You can also change only a rate by clicking on it.", "code": "Code", "name": "Name", "early_bird_rate": "Early bird rate", diff --git a/src/layouts/sponsor-id-layout.js b/src/layouts/sponsor-id-layout.js index e8c25a1d0..44c53d635 100644 --- a/src/layouts/sponsor-id-layout.js +++ b/src/layouts/sponsor-id-layout.js @@ -143,7 +143,7 @@ class SponsorIdLayout extends React.Component { )} /> - + { const { children, value, index, ...other } = props; @@ -100,7 +100,7 @@ const EditSponsorPage = (props) => { const [selectedTab, setSelectedTab] = useState( location.pathname.includes("/sponsor-forms/") && location.pathname.includes("/items") - ? FOUR + ? SPONSOR_TABS.FORMS : 0 ); @@ -127,14 +127,26 @@ const EditSponsorPage = (props) => { }; const tabs = [ - { label: T.translate("edit_sponsor.tab.general"), value: 0 }, - { label: T.translate("edit_sponsor.tab.users"), value: 1 }, - { label: T.translate("edit_sponsor.tab.pages"), value: 2 }, - { label: T.translate("edit_sponsor.tab.media_uploads"), value: 3 }, - { label: T.translate("edit_sponsor.tab.forms"), value: 4 }, - { label: T.translate("edit_sponsor.tab.cart"), value: 5 }, - { label: T.translate("edit_sponsor.tab.purchases"), value: 6 }, - { label: T.translate("edit_sponsor.tab.badge_scans"), value: 7 } + { + label: T.translate("edit_sponsor.tab.general"), + value: SPONSOR_TABS.GENERAL + }, + { label: T.translate("edit_sponsor.tab.users"), value: SPONSOR_TABS.USERS }, + { label: T.translate("edit_sponsor.tab.pages"), value: SPONSOR_TABS.PAGES }, + { + label: T.translate("edit_sponsor.tab.media_uploads"), + value: SPONSOR_TABS.MEDIA_UPLOADS + }, + { label: T.translate("edit_sponsor.tab.forms"), value: SPONSOR_TABS.FORMS }, + { label: T.translate("edit_sponsor.tab.cart"), value: SPONSOR_TABS.CART }, + { + label: T.translate("edit_sponsor.tab.purchases"), + value: SPONSOR_TABS.PURCHASES + }, + { + label: T.translate("edit_sponsor.tab.badge_scans"), + value: SPONSOR_TABS.BADGE_SCANS + } ]; const sponsorFormItemRoute = diff --git a/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js index f5d29aaab..4fe13fca6 100644 --- a/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js +++ b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js @@ -259,7 +259,16 @@ const SponsorFormItemFromInventoryPopup = ({ SponsorFormItemFromInventoryPopup.propTypes = { onClose: PropTypes.func.isRequired, - onSave: 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, + getInventoryItems: PropTypes.func.isRequired }; const mapStateToProps = ({ currentInventoryItemListState }) => ({ 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 1e6cb0934..c5cc4b29a 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 @@ -44,7 +44,6 @@ import SponsorInventoryDialog from "../../../../sponsors_inventory/popup/sponsor 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"; const SponsorFormsManageItems = ({ term, @@ -318,7 +317,7 @@ const SponsorFormsManageItems = ({ Date: Thu, 18 Dec 2025 11:52:30 -0300 Subject: [PATCH 09/16] fix: adjust impots on sponsor id layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/layouts/sponsor-id-layout.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layouts/sponsor-id-layout.js b/src/layouts/sponsor-id-layout.js index 44c53d635..4b117c411 100644 --- a/src/layouts/sponsor-id-layout.js +++ b/src/layouts/sponsor-id-layout.js @@ -3,7 +3,6 @@ import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; import { Breadcrumb } from "react-breadcrumbs"; import { Switch, Route } from "react-router-dom"; -import { Breadcrumb } from "react-breadcrumbs"; import EditSponsorPage from "../pages/sponsors/edit-sponsor-page"; import { getSponsor, resetSponsorForm } from "../actions/sponsor-actions"; import EditAdSponsorPage from "../pages/sponsors/edit-advertisement-sponsor-page"; From acec3bb3410c594f611db0626cc09ecdd64261d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Tue, 6 Jan 2026 15:48:15 -0300 Subject: [PATCH 10/16] fix: changes from comments, use uicore money utils, adjust reducers and actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-forms-actions.js | 40 ++++++++------- src/i18n/en.json | 4 +- .../sponsor-form-item-list-page/index.js | 37 ++------------ .../sponsor-form-item-from-inventory.js | 28 ++++++---- .../sponsor-forms-manage-items.js | 51 ++++--------------- .../popup/sponsor-inventory-popup.js | 2 +- ...nsor-customized-form-items-list-reducer.js | 36 ++++++------- src/utils/constants.js | 2 - src/utils/yup.js | 27 ++++++++++ 9 files changed, 101 insertions(+), 126 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index 7aa3ffe76..af4bbef2f 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -32,7 +32,6 @@ import { DEFAULT_PER_PAGE } from "../utils/constants"; import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions"; -import { amountToCents } from "../utils/currency"; export const REQUEST_SPONSOR_FORMS = "REQUEST_SPONSOR_FORMS"; export const RECEIVE_SPONSOR_FORMS = "RECEIVE_SPONSOR_FORMS"; @@ -707,7 +706,8 @@ export const getSponsorCustomizedFormItems = createAction(REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS), createAction(RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items`, - authErrorHandler + authErrorHandler, + { term, order, orderDir, page, hideArchived } )(params)(dispatch).then(() => { dispatch(stopLoading()); }); @@ -1311,17 +1311,20 @@ export const saveSponsorFormManagedItem = normalizedEntity, snackbarErrorHandler, entity - )(params)(dispatch).then(() => { - dispatch(stopLoading()); - dispatch( - snackbarSuccessHandler({ - title: T.translate("general.success"), - html: T.translate( - "edit_sponsor.forms_tab.form_manage_items.item_updated" - ) - }) - ); - }); + )(params)(dispatch) + .then(() => { + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate( + "edit_sponsor.forms_tab.form_manage_items.item_updated" + ) + }) + ); + }) + .finally(() => { + dispatch(stopLoading()); + }); } const successMessage = { @@ -1339,9 +1342,13 @@ export const saveSponsorFormManagedItem = normalizedEntity, snackbarErrorHandler, entity - )(params)(dispatch).then(() => { - dispatch(snackbarSuccessHandler(successMessage)); - }); + )(params)(dispatch) + .then(() => { + dispatch(snackbarSuccessHandler(successMessage)); + }) + .finally(() => { + dispatch(stopLoading()); + }); }; export const resetSponsorFormManagedItem = () => (dispatch) => { @@ -1472,7 +1479,6 @@ export const addSponsorManagedFormItems = }) ); }) - .catch(snackbarErrorHandler) // need to catch promise reject .finally(() => { dispatch(stopLoading()); }); diff --git a/src/i18n/en.json b/src/i18n/en.json index 74d944614..d382bf01d 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2446,7 +2446,9 @@ "default_quantity": "Default Quantity", "add_selected": "Add Selected Items", "item_updated": "Form item updated successfully", - "item_created": "Form item {item} created successfully", + "item_created": "Form item created successfully", + "sort_asc_label": "A-Z", + "sort_desc_label": "Z-A", "placeholder": { "search": "Search..." } 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 94a3bd0a7..e6cddeb62 100644 --- a/src/pages/sponsors/sponsor-form-item-list-page/index.js +++ b/src/pages/sponsors/sponsor-form-item-list-page/index.js @@ -15,7 +15,6 @@ import React, { useEffect, useState } from "react"; import { Breadcrumb } from "react-breadcrumbs"; import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; -import * as yup from "yup"; import { Alert, Box, @@ -29,7 +28,6 @@ import AddIcon from "@mui/icons-material/Add"; import IconButton from "@mui/material/IconButton"; import Tooltip from "@mui/material/Tooltip"; import ImageIcon from "@mui/icons-material/Image"; -import { parsePrice } from "openstack-uicore-foundation/lib/utils/money"; import { deleteSponsorFormItem, getSponsorFormItem, @@ -42,6 +40,7 @@ import ItemPopup from "./components/item-popup"; import InventoryPopup from "./components/inventory-popup"; import MuiTableEditable from "../../../components/mui/editable-table/mui-table-editable"; import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants"; +import { rateCellValidation } from "../../../utils/yup"; const SponsorFormItemListPage = ({ match, @@ -92,7 +91,8 @@ const SponsorFormItemListPage = ({ }; const handleCellEdit = (rowId, column, value) => { - const tmpEntity = { id: rowId, [column]: parsePrice(value) }; + const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); + const tmpEntity = { id: rowId, [column]: valueWithNoSign }; updateSponsorFormItem(formId, tmpEntity); }; @@ -113,37 +113,6 @@ const SponsorFormItemListPage = ({ setOpenPopup("inventory"); }; - const rateCellValidation = () => - yup - .number() - // allow $ at the start - .transform((value, originalValue) => { - if (typeof originalValue === "string") { - const cleaned = originalValue.replace(/^\$/, ""); - return cleaned === "" ? undefined : parseFloat(cleaned); - } - return value; - }) - // check if there's letters or characters - .test({ - name: "valid-format", - message: T.translate("validation.number"), - test: (value, { originalValue }) => { - if ( - originalValue === undefined || - originalValue === null || - originalValue === "" - ) - return true; - return /^\$?-?\d+(\.\d+)?$/.test(originalValue); - } - }) - .min(0, T.translate("validation.number_positive")) - .test("max-decimals", T.translate("validation.two_decimals"), (value) => { - if (value === undefined || value === null) return true; - return /^\d+(\.\d{1,2})?$/.test(value.toString()); - }); - const columns = [ { columnKey: "code", diff --git a/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js index 4fe13fca6..b7299ca8a 100644 --- a/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js +++ b/src/pages/sponsors/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js @@ -20,6 +20,7 @@ import { import CloseIcon from "@mui/icons-material/Close"; import ImageIcon from "@mui/icons-material/Image"; import SwapVertIcon from "@mui/icons-material/SwapVert"; +import { currencyAmountFromCents } from "openstack-uicore-foundation/lib/utils/money"; import SearchInput from "../../../../../components/mui/search-input"; import { DEFAULT_CURRENT_PAGE, @@ -28,7 +29,6 @@ import { import { getInventoryItems } from "../../../../../actions/inventory-item-actions"; import MuiTable from "../../../../../components/mui/table/mui-table"; -import { amountFromCents } from "../../../../../utils/currency"; import MenuButton from "../../../../../components/mui/menu-button"; const SponsorFormItemFromInventoryPopup = ({ @@ -117,7 +117,7 @@ const SponsorFormItemFromInventoryPopup = ({ "edit_sponsor.forms_tab.form_manage_items.early_bird_rate" ), sortable: false, - render: (row) => `$ ${amountFromCents(row.early_bird_rate)}` + render: (row) => currencyAmountFromCents(row.early_bird_rate) }, { columnKey: "standard_rate", @@ -125,7 +125,7 @@ const SponsorFormItemFromInventoryPopup = ({ "edit_sponsor.forms_tab.form_manage_items.standard_rate" ), sortable: false, - render: (row) => `$ ${amountFromCents(row.standard_rate)}` + render: (row) => currencyAmountFromCents(row.standard_rate) }, { columnKey: "onsite_rate", @@ -133,7 +133,7 @@ const SponsorFormItemFromInventoryPopup = ({ "edit_sponsor.forms_tab.form_manage_items.onsite_rate" ), sortable: false, - render: (row) => `$ ${amountFromCents(row.onsite_rate)}` + render: (row) => currencyAmountFromCents(row.onsite_rate) }, { columnKey: "default_quantity", @@ -207,20 +207,28 @@ const SponsorFormItemFromInventoryPopup = ({ buttonId="sort-button" menuId="sort-menu" menuItems={[ - { label: "A-Z", onClick: () => handleSort("name", 1) }, - { label: "Z-A", onClick: () => handleSort("name", 0) } + { + label: T.translate( + "edit_sponsor.forms_tab.form_manage_items.sort_asc_label" + ), + onClick: () => handleSort("name", 1) + }, + { + label: T.translate( + "edit_sponsor.forms_tab.form_manage_items.sort_desc_label" + ), + onClick: () => handleSort("name", 0) + } ]} > sort by - + 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 c5cc4b29a..520168fe4 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 @@ -14,7 +14,6 @@ import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; -import * as yup from "yup"; import { Box, Button, @@ -42,8 +41,8 @@ import SearchInput from "../../../../../components/mui/search-input"; import MuiTableEditable from "../../../../../components/mui/editable-table/mui-table-editable"; import SponsorInventoryDialog from "../../../../sponsors_inventory/popup/sponsor-inventory-popup"; import SponsorFormItemFromInventoryPopup from "./sponsor-form-item-from-inventory"; -import { parsePrice } from "../../../../../utils/currency"; import { DEFAULT_CURRENT_PAGE } from "../../../../../utils/constants"; +import { rateCellValidation } from "../../../../../utils/yup"; const SponsorFormsManageItems = ({ term, @@ -158,9 +157,10 @@ const SponsorFormsManageItems = ({ }; const handleCellEdit = (rowId, column, value) => { + const valueWithNoSign = String(value).replace(/^[^\d.-]+/, ""); const tmpEntity = { id: rowId, - [column]: parsePrice(value) + [column]: valueWithNoSign }; saveSponsorFormManagedItem(formId, tmpEntity); }; @@ -185,37 +185,6 @@ const SponsorFormsManageItems = ({ ); }; - const rateCellValidation = () => - yup - .number() - // allow $ at the start - .transform((value, originalValue) => { - if (typeof originalValue === "string") { - const cleaned = originalValue.replace(/^\$/, ""); - return cleaned === "" ? undefined : parseFloat(cleaned); - } - return value; - }) - // check if there's letters or characters - .test({ - name: "valid-format", - message: T.translate("validation.number"), - test: (value, { originalValue }) => { - if ( - originalValue === undefined || - originalValue === null || - originalValue === "" - ) - return true; - return /^\$?-?\d+(\.\d+)?$/.test(originalValue); - } - }) - .min(0, T.translate("validation.number_positive")) - .test("max-decimals", T.translate("validation.two_decimals"), (value) => { - if (value === undefined || value === null) return true; - return /^\d+(\.\d{1,2})?$/.test(value.toString()); - }); - const sponsorItemColumns = [ { columnKey: "code", @@ -386,12 +355,14 @@ const SponsorFormsManageItems = ({ {/* ADD ITEM */} - + {openPopup === "add_item" && ( + + )} - {T.translate("edit_inventory_item.images")} * + {T.translate("edit_inventory_item.images")} {formik.touched.images && formik.errors.images && ( {formik.errors.images} 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 304589c76..4aa875dda 100644 --- a/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js +++ b/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js @@ -12,6 +12,10 @@ * */ import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; +import { + amountFromCents, + currencyAmountFromCents +} from "openstack-uicore-foundation/lib/utils/money"; import { RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS, REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS, @@ -24,8 +28,6 @@ import { RESET_SPONSOR_FORM_MANAGED_ITEM } from "../../actions/sponsor-forms-actions"; import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; -import { CENTS_FACTOR, DECIMAL_DIGITS } from "../../utils/constants"; -import { amountFromCents } from "../../utils/currency"; const DEFAULT_ITEM_ENTITY = { code: "", @@ -51,6 +53,7 @@ const DEFAULT_ITEM_ENTITY = { const DEFAULT_STATE = { items: [], hideArchived: false, + term: "", order: "name", orderDir: 1, currentPage: 1, @@ -72,10 +75,11 @@ const sponsorCustomizedFormItemsListReducer = ( return DEFAULT_STATE; } case REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS: { - const { order, orderDir, page, hideArchived } = payload; + const { term, order, orderDir, page, hideArchived } = payload; return { ...state, + term, order, orderDir, items: [], @@ -94,15 +98,9 @@ const sponsorCustomizedFormItemsListReducer = ( id: a.id, code: a.code, name: a.name, - early_bird_rate: `$${(a.early_bird_rate / CENTS_FACTOR).toFixed( - DECIMAL_DIGITS - )}`, - standard_rate: `$${(a.standard_rate / CENTS_FACTOR).toFixed( - DECIMAL_DIGITS - )}`, - onsite_rate: `$${(a.onsite_rate / CENTS_FACTOR).toFixed( - DECIMAL_DIGITS - )}`, + early_bird_rate: currencyAmountFromCents(a.early_bird_rate), + standard_rate: currencyAmountFromCents(a.standard_rate), + onsite_rate: currencyAmountFromCents(a.onsite_rate), default_quantity: a.default_quantity, is_archived: a.is_archived, images: a.images @@ -170,15 +168,11 @@ const sponsorCustomizedFormItemsListReducer = ( id: updatedItem.id, code: updatedItem.code, name: updatedItem.name, - early_bird_rate: `$${( - updatedItem.early_bird_rate / CENTS_FACTOR - ).toFixed(DECIMAL_DIGITS)}`, - standard_rate: `$${( - updatedItem.standard_rate / CENTS_FACTOR - ).toFixed(DECIMAL_DIGITS)}`, - onsite_rate: `$${(updatedItem.onsite_rate / CENTS_FACTOR).toFixed( - DECIMAL_DIGITS - )}`, + early_bird_rate: currencyAmountFromCents( + updatedItem.early_bird_rate + ), + standard_rate: currencyAmountFromCents(updatedItem.standard_rate), + onsite_rate: currencyAmountFromCents(updatedItem.onsite_rate), default_quantity: updatedItem.default_quantity, is_archived: updatedItem.is_archived, images: updatedItem.images diff --git a/src/utils/constants.js b/src/utils/constants.js index faf3def51..360988a92 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -139,8 +139,6 @@ export const DECIMAL_DIGITS = 2; export const TWO = 2; -export const FOUR = 4; - export const TEN = 10; export const ONE_HUNDRED = 100; diff --git a/src/utils/yup.js b/src/utils/yup.js index 0b3999935..86c770b39 100644 --- a/src/utils/yup.js +++ b/src/utils/yup.js @@ -47,6 +47,33 @@ export const decimalValidation = () => .typeError(T.translate("validation.number")) .positive(T.translate("validation.number_positive")) .required(T.translate("validation.required")) + +export const rateCellValidation = () => + yup + .number() + // allow $ at the start + .transform((value, originalValue) => { + if (typeof originalValue === "string") { + const cleaned = originalValue.replace(/^\$/, ""); + return cleaned === "" ? undefined : parseFloat(cleaned); + } + return value; + }) + // check if there's letters or characters + .test({ + name: "valid-format", + message: T.translate("validation.number"), + test: (value, { originalValue }) => { + if ( + originalValue === undefined || + originalValue === null || + originalValue === "" + ) + return true; + return /^\$?-?\d+(\.\d+)?$/.test(originalValue); + } + }) + .positive(T.translate("validation.number_positive")) .test("max-decimals", T.translate("validation.two_decimals"), (value) => { if (value === undefined || value === null) return true; return /^\d+(\.\d{1,2})?$/.test(value.toString()); From c521db8c7c3650a6719a388637aad71bb50dabea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 7 Jan 2026 02:49:44 -0300 Subject: [PATCH 11/16] fix: normalize rates, adjust params on sponsor form manage items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-forms-actions.js | 42 +++++++------------ .../sponsor-forms-manage-items.js | 10 ++--- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index af4bbef2f..0974a4652 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -1214,9 +1214,6 @@ const normalizeItem = (entity) => { const normalizedEntity = { ...entity }; const { meta_fields, - early_bird_rate, - standard_rate, - onsite_rate, quantity_limit_per_show, quantity_limit_per_sponsor, default_quantity, @@ -1231,15 +1228,7 @@ const normalizeItem = (entity) => { normalizedEntity.images = images?.filter((img) => img.file_path); } - if (early_bird_rate === "" || typeof early_bird_rate === "undefined") - delete normalizedEntity.early_bird_rate; - else normalizedEntity.early_bird_rate = amountToCents(early_bird_rate); - if (standard_rate === "" || typeof standard_rate === "undefined") - delete normalizedEntity.standard_rate; - else normalizedEntity.standard_rate = amountToCents(standard_rate); - if (onsite_rate === "" || typeof onsite_rate === "undefined") - delete normalizedEntity.onsite_rate; - else normalizedEntity.onsite_rate = amountToCents(onsite_rate); + normalizeRates(entity, normalizedEntity); if (quantity_limit_per_show === "") delete normalizedEntity.quantity_limit_per_show; @@ -1364,26 +1353,25 @@ const normalizeManagedItem = (entity) => { (img) => img.file_path ); - if (entity.early_bird_rate === "" || entity.early_bird_rate === undefined) + normalizeRates(entity, normalizedEntity); + + return normalizedEntity; +}; + +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( - normalizedEntity.early_bird_rate - ); + else normalizedEntity.early_bird_rate = amountToCents(early_bird_rate); - if (entity.standard_rate === "" || entity.standard_rate === undefined) + if (standard_rate === "" || standard_rate === undefined) delete normalizedEntity.standard_rate; - else - normalizedEntity.standard_rate = amountToCents( - normalizedEntity.standard_rate - ); + else normalizedEntity.standard_rate = amountToCents(standard_rate); - if (entity.onsite_rate === "" || entity.onsite_rate === undefined) + if (onsite_rate === "" || onsite_rate === undefined) delete normalizedEntity.onsite_rate; - else - normalizedEntity.onsite_rate = amountToCents(normalizedEntity.onsite_rate); - - return normalizedEntity; + else normalizedEntity.onsite_rate = amountToCents(onsite_rate); }; export const deleteSponsorFormManagedItem = 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 520168fe4..25aac0d85 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 @@ -77,7 +77,6 @@ const SponsorFormsManageItems = ({ }, []); const handleManagedPageChange = (page) => { - const { perPage, order, orderDir } = items; getSponsorCustomizedFormItems( formId, term, @@ -90,7 +89,6 @@ const SponsorFormsManageItems = ({ }; const handleManagedSort = (key, dir) => { - const { currentPage, perPage } = items; getSponsorCustomizedFormItems( formId, term, @@ -144,10 +142,10 @@ const SponsorFormsManageItems = ({ getSponsorCustomizedFormItems( formId, term, - items.currentPage, - items.perPage, - items.order, - items.orderDir, + currentPage, + perPage, + order, + orderDir, ev.target.checked ); }; From e15872aa89b528b0f62a454d0fd9f960e936982a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 7 Jan 2026 10:23:27 -0300 Subject: [PATCH 12/16] fix: fix hande hide archive typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../components/manage-items/sponsor-forms-manage-items.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 25aac0d85..34eeb6af9 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 @@ -138,7 +138,7 @@ const SponsorFormsManageItems = ({ ? unarchiveSponsorCustomizedFormItem(formId, item.id) : archiveSponsorCustomizedFormItem(formId, item.id); - const handleHideArchivedItens = (ev) => { + const handleHideArchivedItems = (ev) => { getSponsorCustomizedFormItems( formId, term, @@ -285,7 +285,7 @@ const SponsorFormsManageItems = ({ control={ Date: Thu, 8 Jan 2026 10:18:46 -0300 Subject: [PATCH 13/16] fix: adjust text for 'hide archived items' on sponsor manage items page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/i18n/en.json | 1 + .../components/manage-items/sponsor-forms-manage-items.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index d382bf01d..0e9aa0523 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2437,6 +2437,7 @@ "add_item": "Add Item", "add_item_inventory": "Add Item from Inventory", "alert_info": "You can add or archive items from the list. To edit an item click on the item's Edit button. You can also change only a rate by clicking on it.", + "hide_archived": "Hide archived items", "select_items": "Select items", "code": "Code", "name": "Name", 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 34eeb6af9..b448c54a0 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 @@ -288,12 +288,14 @@ const SponsorFormsManageItems = ({ onChange={handleHideArchivedItems} inputProps={{ "aria-label": T.translate( - "edit_sponsor.forms_tab.hide_archived" + "edit_sponsor.forms_tab.form_manage_items.hide_archived" ) }} /> } - label={T.translate("edit_sponsor.forms_tab.hide_archived")} + label={T.translate( + "edit_sponsor.forms_tab.form_manage_items.hide_archived" + )} /> From 6ae5f371afe36680dca75359ab0f0b190e87ab70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 8 Jan 2026 18:03:30 -0300 Subject: [PATCH 14/16] fix: add perPage function, rename functions for pagination and sort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-forms-actions.js | 2 +- .../sponsor-forms-manage-items.js | 29 ++++++++++++++----- ...nsor-customized-form-items-list-reducer.js | 3 +- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index 0974a4652..934e81458 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -707,7 +707,7 @@ export const getSponsorCustomizedFormItems = createAction(RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEMS), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-forms/${formId}/items`, authErrorHandler, - { term, order, orderDir, page, hideArchived } + { term, order, orderDir, page, perPage, hideArchived } )(params)(dispatch).then(() => { dispatch(stopLoading()); }); 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 b448c54a0..f3dd5ae76 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 @@ -76,7 +76,7 @@ const SponsorFormsManageItems = ({ getSponsorCustomizedFormItems(formId); }, []); - const handleManagedPageChange = (page) => { + const handlePageChange = (page) => { getSponsorCustomizedFormItems( formId, term, @@ -88,11 +88,23 @@ const SponsorFormsManageItems = ({ ); }; - const handleManagedSort = (key, dir) => { + const handlePerPageChange = (newPerPage) => { getSponsorCustomizedFormItems( formId, term, - currentPage, + DEFAULT_CURRENT_PAGE, + newPerPage, + order, + orderDir, + hideArchived + ); + }; + + const handleSort = (key, dir) => { + getSponsorCustomizedFormItems( + formId, + term, + DEFAULT_CURRENT_PAGE, perPage, key, dir, @@ -104,7 +116,7 @@ const SponsorFormsManageItems = ({ getSponsorCustomizedFormItems( formId, searchTerm, - currentPage, + DEFAULT_CURRENT_PAGE, perPage, order, orderDir, @@ -117,7 +129,7 @@ const SponsorFormsManageItems = ({ getSponsorCustomizedFormItems( formId, term, - currentPage, + DEFAULT_CURRENT_PAGE, perPage, order, orderDir, @@ -142,7 +154,7 @@ const SponsorFormsManageItems = ({ getSponsorCustomizedFormItems( formId, term, - currentPage, + DEFAULT_CURRENT_PAGE, perPage, order, orderDir, @@ -345,8 +357,9 @@ const SponsorFormsManageItems = ({ perPage={perPage} totalRows={totalCount} currentPage={currentPage} - onPageChange={handleManagedPageChange} - onSort={handleManagedSort} + onPageChange={handlePageChange} + onPerPageChange={handlePerPageChange} + onSort={handleSort} onArchive={handleArchiveItem} onEdit={handleRowEdit} onDelete={handleRowDelete} 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 4aa875dda..a1f436f84 100644 --- a/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js +++ b/src/reducers/sponsors/sponsor-customized-form-items-list-reducer.js @@ -75,7 +75,7 @@ const sponsorCustomizedFormItemsListReducer = ( return DEFAULT_STATE; } case REQUEST_SPONSOR_CUSTOMIZED_FORM_ITEMS: { - const { term, order, orderDir, page, hideArchived } = payload; + const { term, order, orderDir, page, perPage, hideArchived } = payload; return { ...state, @@ -84,6 +84,7 @@ const sponsorCustomizedFormItemsListReducer = ( orderDir, items: [], currentPage: page, + perPage, hideArchived }; } From 096ffd88180408c258e7132b77e3960b88957bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 8 Jan 2026 20:04:09 -0300 Subject: [PATCH 15/16] fix: close popup after save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../components/manage-items/sponsor-forms-manage-items.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f3dd5ae76..d4a292af0 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 @@ -136,8 +136,8 @@ const SponsorFormsManageItems = ({ hideArchived ); resetSponsorFormManagedItem(); + setOpenPopup(null); }); - setOpenPopup(null); }; const handleOpenItemPopup = () => { From 2e209a65ef847a3dd8c224c35c73199c08930194 Mon Sep 17 00:00:00 2001 From: smarcet Date: Mon, 12 Jan 2026 16:54:05 -0300 Subject: [PATCH 16/16] fix(yup): fix at decimalValidation --- src/utils/yup.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils/yup.js b/src/utils/yup.js index 86c770b39..f761502ea 100644 --- a/src/utils/yup.js +++ b/src/utils/yup.js @@ -47,6 +47,10 @@ export const decimalValidation = () => .typeError(T.translate("validation.number")) .positive(T.translate("validation.number_positive")) .required(T.translate("validation.required")) + .test("max-decimals", T.translate("validation.two_decimals"), (value) => { + if (value === undefined || value === null) return true; + return /^\d+(\.\d{1,2})?$/.test(value.toString()); + }); export const rateCellValidation = () => yup