From cb3e53fe2e3e216b8b56498540fc7d8a811f4ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Sun, 1 Feb 2026 04:22:39 -0300 Subject: [PATCH 1/9] feat: add sponsor pages tab, add customized and managed tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/pages/sponsors/sponsor-pages-tab/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/sponsors/sponsor-pages-tab/index.js b/src/pages/sponsors/sponsor-pages-tab/index.js index 5ca762e97..7f50b4c56 100644 --- a/src/pages/sponsors/sponsor-pages-tab/index.js +++ b/src/pages/sponsors/sponsor-pages-tab/index.js @@ -305,7 +305,7 @@ const SponsorPagesTab = ({ variant="contained" size="medium" fullWidth - onClick={() => console.log("open popup new")} + onClick={() => setCustomFormEdit("new")} startIcon={} sx={{ height: "36px" }} > From 8f5c066eb36384d852665c2849c805cfb4dc0cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Mon, 2 Feb 2026 16:03:25 -0300 Subject: [PATCH 2/9] fix: adjust reducer and actions, fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/pages/sponsors/sponsor-pages-tab/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/sponsors/sponsor-pages-tab/index.js b/src/pages/sponsors/sponsor-pages-tab/index.js index 7f50b4c56..5ca762e97 100644 --- a/src/pages/sponsors/sponsor-pages-tab/index.js +++ b/src/pages/sponsors/sponsor-pages-tab/index.js @@ -305,7 +305,7 @@ const SponsorPagesTab = ({ variant="contained" size="medium" fullWidth - onClick={() => setCustomFormEdit("new")} + onClick={() => console.log("open popup new")} startIcon={} sx={{ height: "36px" }} > From 53a3e823a94c4cc09dbb9bfc8ec5d0746ddeaf33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 25 Feb 2026 17:21:14 -0300 Subject: [PATCH 3/9] fix: add no results text, prevent default on search pages 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index 35546d87d..71c581939 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -3,7 +3,7 @@ "user_not_authz": "Hold on. Your user is not authorized!.", "user_not_set": "Hold on. Can not get any valid user.", "session_expired": "Hold on. Your session expired!.", - "empty_list": "there not are any item that match your criteria.", + "empty_list": "there not are any {item} that match your criteria.", "server_error": "There was a problem with our server, please contact admin.", "empty_list_schedule_events": "Please select a Day and a Location ...", "not_allowed": "You are not allowed here, please login with another user to access this page.", From b07cffc7e6dfe133f229f648c799b05762eb493d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 26 Feb 2026 15:20:23 -0300 Subject: [PATCH 4/9] feat: wip add sponsor custom page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-pages-actions.js | 34 ++++++++++- src/pages/sponsors/sponsor-pages-tab/index.js | 58 ++++++++++++++++--- .../sponsor-page-pages-list-reducer.js | 35 +++++++++++ 3 files changed, 118 insertions(+), 9 deletions(-) diff --git a/src/actions/sponsor-pages-actions.js b/src/actions/sponsor-pages-actions.js index 6692048c5..25dd7421c 100644 --- a/src/actions/sponsor-pages-actions.js +++ b/src/actions/sponsor-pages-actions.js @@ -39,6 +39,7 @@ export const REQUEST_SPONSOR_CUSTOMIZED_PAGES = "REQUEST_SPONSOR_CUSTOMIZED_PAGES"; export const RECEIVE_SPONSOR_CUSTOMIZED_PAGES = "RECEIVE_SPONSOR_CUSTOMIZED_PAGES"; +export const SPONSOR_CUSTOMIZED_PAGE_ADDED = "SPONSOR_CUSTOMIZED_PAGE_ADDED"; export const cloneGlobalPage = (pagesIds, sponsorIds, allSponsors) => async (dispatch, getState) => { @@ -151,7 +152,7 @@ export const saveSponsorManagedPage = dispatch(startLoading()); - const normalizedEntity = normalizeSponsorManagedPage(entity); + const normalizedEntity = normalizeSponsorPage(entity); const params = { access_token: accessToken, @@ -178,7 +179,7 @@ export const saveSponsorManagedPage = }); }; -const normalizeSponsorManagedPage = (entity) => { +const normalizeSponsorPage = (entity) => { const normalizedEntity = { show_page_ids: entity.pages }; @@ -252,3 +253,32 @@ export const getSponsorCustomizedPages = dispatch(stopLoading()); }); }; + +export const saveSponsorCustomizedPage = + (entity) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + const accessToken = await getAccessTokenSafely(); + + dispatch(startLoading()); + + const normalizedEntity = normalizeSponsorPage(entity); + + const params = { + access_token: accessToken, + fields: "id,code,name,kind,modules_count,allowed_add_ons" + }; + + return postRequest( + null, + createAction(SPONSOR_CUSTOMIZED_PAGE_ADDED), + `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-pages`, + normalizedEntity, + snackbarErrorHandler + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); + }; diff --git a/src/pages/sponsors/sponsor-pages-tab/index.js b/src/pages/sponsors/sponsor-pages-tab/index.js index 5ca762e97..3a27ab286 100644 --- a/src/pages/sponsors/sponsor-pages-tab/index.js +++ b/src/pages/sponsors/sponsor-pages-tab/index.js @@ -26,13 +26,16 @@ import AddIcon from "@mui/icons-material/Add"; import { getSponsorManagedPages, getSponsorCustomizedPages, - saveSponsorManagedPage + saveSponsorManagedPage, + saveSponsorCustomizedPage } from "../../../actions/sponsor-pages-actions"; +import { getSponsorships } from "../../../actions/sponsor-forms-actions"; import CustomAlert from "../../../components/mui/custom-alert"; import SearchInput from "../../../components/mui/search-input"; import MuiTable from "../../../components/mui/table/mui-table"; -import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants"; +import { DEFAULT_CURRENT_PAGE, MAX_PER_PAGE } from "../../../utils/constants"; import AddSponsorPageTemplatePopup from "./components/add-sponsor-page-template-popup"; +import PageTemplatePopup from "../../sponsors-global/page-templates/page-template-popup"; const SponsorPagesTab = ({ sponsor, @@ -41,9 +44,13 @@ const SponsorPagesTab = ({ hideArchived, managedPages, customizedPages, + summitTZ, + sponsorships, getSponsorManagedPages, + saveSponsorManagedPage, getSponsorCustomizedPages, - saveSponsorManagedPage + saveSponsorCustomizedPage, + getSponsorships }) => { const [openPopup, setOpenPopup] = useState(null); @@ -151,6 +158,10 @@ const SponsorPagesTab = ({ setOpenPopup("template"); }; + const handleAddPage = () => { + getSponsorships(1, MAX_PER_PAGE).then(() => setOpenPopup("new")); + }; + const handleArchiveCustomizedPage = (item) => console.log("ARCHIVE CUSTOMIZED ", item); @@ -194,8 +205,30 @@ const SponsorPagesTab = ({ const handleSaveManagedPageFromTemplate = (entity) => { saveSponsorManagedPage(entity) - .then(() => getSponsorManagedPages()) - .finally(() => setOpenPopup(null)); + .then(() => { + const { perPage, order, orderDir } = managedPages; + getSponsorManagedPages( + term, + DEFAULT_CURRENT_PAGE, + perPage, + order, + orderDir + ); + }).finally(() => setOpenPopup(null)); + }; + + const handleSaveCustomizedPage = (entity) => { + saveSponsorCustomizedPage(entity) + .then(() => { + const { perPage, order, orderDir } = customizedPages; + getSponsorCustomizedPages( + term, + DEFAULT_CURRENT_PAGE, + perPage, + order, + orderDir + ); + }).finally(() => setOpenPopup(null));; }; const baseColumns = (name) => [ @@ -305,7 +338,7 @@ const SponsorPagesTab = ({ variant="contained" size="medium" fullWidth - onClick={() => console.log("open popup new")} + onClick={handleAddPage} startIcon={} sx={{ height: "36px" }} > @@ -362,6 +395,15 @@ const SponsorPagesTab = ({ onClose={() => setOpenPopup(null)} /> )} + + {openPopup === "new" && ( + setOpenPopup(null)} + sponsorships={sponsorships.items} + summitTZ={summitTZ} + /> + )} ); }; @@ -373,5 +415,7 @@ const mapStateToProps = ({ sponsorPagePagesListState }) => ({ export default connect(mapStateToProps, { getSponsorManagedPages, saveSponsorManagedPage, - getSponsorCustomizedPages + getSponsorCustomizedPages, + saveSponsorCustomizedPage, + getSponsorships })(SponsorPagesTab); diff --git a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js index d66631d71..f11666c7c 100644 --- a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js @@ -19,6 +19,7 @@ import { REQUEST_SPONSOR_CUSTOMIZED_PAGES } from "../../actions/sponsor-pages-actions"; import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; +import { RECEIVE_GLOBAL_SPONSORSHIPS } from "../../actions/sponsor-forms-actions"; const DEFAULT_STATE = { managedPages: { @@ -39,6 +40,12 @@ const DEFAULT_STATE = { perPage: 10, totalItems: 0 }, + sponsorships: { + items: [], + currentPage: 0, + lastPage: 0, + total: 0 + }, term: "", hideArchived: false, summitTZ: "" @@ -146,6 +153,34 @@ const sponsorPagePagesListReducer = (state = DEFAULT_STATE, action) => { } }; } + case RECEIVE_GLOBAL_SPONSORSHIPS: { + const { + current_page: currentPage, + last_page: lastPage, + total, + data + } = payload.response; + + const newSponsorships = data.map((s) => ({ + id: s.id, + name: s.type.name + })); + + const items = + currentPage === 1 + ? newSponsorships + : [...state.sponsorships.items, ...newSponsorships]; + + return { + ...state, + sponsorships: { + items, + currentPage, + lastPage, + total + } + }; + } default: return state; } From 6ecfb84da231d47997f0412f35fb1be5bcf62ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 26 Feb 2026 17:29:39 -0300 Subject: [PATCH 5/9] fix: add component for add ons on page popup, adjust reducer 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-pages-actions.js | 52 +++++++++++++++++-- .../page-template-popup/index.js | 34 ++++++++++-- .../add-sponsor-form-template-popup/index.js | 22 ++++---- src/pages/sponsors/sponsor-pages-tab/index.js | 16 +++--- .../sponsor-page-pages-list-reducer.js | 12 +++-- 5 files changed, 107 insertions(+), 29 deletions(-) diff --git a/src/actions/sponsor-pages-actions.js b/src/actions/sponsor-pages-actions.js index 25dd7421c..cc4500299 100644 --- a/src/actions/sponsor-pages-actions.js +++ b/src/actions/sponsor-pages-actions.js @@ -21,12 +21,14 @@ import { escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; import T from "i18n-react/dist/i18n-react"; +import moment from "moment-timezone"; import { getAccessTokenSafely } from "../utils/methods"; import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions"; import { DEFAULT_CURRENT_PAGE, DEFAULT_ORDER_DIR, - DEFAULT_PER_PAGE + DEFAULT_PER_PAGE, + PAGES_MODULE_KINDS } from "../utils/constants"; export const GLOBAL_PAGE_CLONED = "GLOBAL_PAGE_CLONED"; @@ -152,7 +154,7 @@ export const saveSponsorManagedPage = dispatch(startLoading()); - const normalizedEntity = normalizeSponsorPage(entity); + const normalizedEntity = normalizeSponsorManagedPage(entity); const params = { access_token: accessToken, @@ -179,7 +181,7 @@ export const saveSponsorManagedPage = }); }; -const normalizeSponsorPage = (entity) => { +const normalizeSponsorManagedPage = (entity) => { const normalizedEntity = { show_page_ids: entity.pages }; @@ -226,7 +228,7 @@ export const getSponsorCustomizedPages = const params = { page, - fields: "id,code,name,kind,modules_count,allowed_add_ons", + fields: "id,code,name,allowed_add_ons,is_archived,modules", per_page: perPage, access_token: accessToken }; @@ -261,11 +263,12 @@ export const saveSponsorCustomizedPage = const { entity: { id: sponsorId } } = currentSponsorState; + const summitTZ = currentSummit.time_zone.name; const accessToken = await getAccessTokenSafely(); dispatch(startLoading()); - const normalizedEntity = normalizeSponsorPage(entity); + const normalizedEntity = normalizeSponsorCustomPage(entity, summitTZ); const params = { access_token: accessToken, @@ -282,3 +285,42 @@ export const saveSponsorCustomizedPage = dispatch(stopLoading()); }); }; + +const normalizeSponsorCustomPage = (entity, summitTZ) => { + const normalizedEntity = { + ...entity, + apply_to_all_add_ons: false + }; + + if (entity.allowed_add_ons.includes("all")) { + normalizedEntity.apply_to_all_add_ons = true; + normalizedEntity.allowed_add_ons = []; + } else { + normalizedEntity.allowed_add_ons = entity.allowed_add_ons.map((e) => e.id); + } + + normalizedEntity.modules = entity.modules.map((module) => { + const normalizedModule = { ...module }; + + if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.upload_deadline) { + normalizedModule.upload_deadline = moment + .tz(module.upload_deadline, summitTZ) + .unix(); + } + + if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.file_type_id) { + normalizedModule.file_type_id = + module.file_type_id?.value || module.file_type_id; + } + + if (module.kind === PAGES_MODULE_KINDS.DOCUMENT && module.file) { + normalizedModule.file = module.file[0] || null; + } + + delete normalizedModule._tempId; + + return normalizedModule; + }); + + return normalizedEntity; +}; diff --git a/src/pages/sponsors-global/page-templates/page-template-popup/index.js b/src/pages/sponsors-global/page-templates/page-template-popup/index.js index 06930e6e9..6b75729f4 100644 --- a/src/pages/sponsors-global/page-templates/page-template-popup/index.js +++ b/src/pages/sponsors-global/page-templates/page-template-popup/index.js @@ -28,11 +28,15 @@ import { PAGE_MODULES_DOWNLOAD } from "../../../../utils/constants"; import DropdownCheckbox from "../../../../components/mui/dropdown-checkbox"; +import MuiFormikSelectGroup from "../../../../components/mui/formik-inputs/mui-formik-select-group"; +import { querySponsorAddons } from "../../../../actions/sponsor-actions"; -const PageTemplatePopup = ({ pageTemplate, onClose, onSave, sponsorships }) => { +const PageTemplatePopup = ({ pageTemplate, onClose, onSave, sponsorships, summitId, sponsorId, sponsorshipIds }) => { const showSponsorships = Array.isArray(sponsorships) && sponsorships.length > 0; + const showAllowedAddons = summitId && sponsorId && sponsorshipIds?.length > 0; + const infoModuleSchema = yup.object().shape({ kind: yup.string().equals([PAGES_MODULE_KINDS.INFO]), content: yup.string().required(T.translate("validation.required")) @@ -177,7 +181,12 @@ const PageTemplatePopup = ({ pageTemplate, onClose, onSave, sponsorships }) => { fullWidth /> - + { /> )} + {showAllowedAddons && ( + + addon.sponsorship.type.id} + getGroupLabel={(addon) => addon.sponsorship.type.type.name} + placeholder={T.translate( + "edit_sponsor.placeholders.select_add_ons" + )} + /> + + )} @@ -250,7 +275,10 @@ const PageTemplatePopup = ({ pageTemplate, onClose, onSave, sponsorships }) => { PageTemplatePopup.propTypes = { onClose: PropTypes.func.isRequired, onSave: PropTypes.func.isRequired, - sponsorships: PropTypes.array + sponsorships: PropTypes.array, + sponsorshipIds: PropTypes.array, + summitId: PropTypes.number, + sponsorId: PropTypes.number }; export default PageTemplatePopup; diff --git a/src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js b/src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js index 77d604d3e..dce024f88 100644 --- a/src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js +++ b/src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js @@ -75,18 +75,16 @@ const AddSponsorFormTemplatePopup = ({ }); useEffect(() => { - if (open) { - getSponsorForms( - term, - currentPage, - FIVE_PER_PAGE, - order, - orderDir, - false, - sponsorshipTypeIds - ); - } - }, [open]); + getSponsorForms( + term, + currentPage, + FIVE_PER_PAGE, + order, + orderDir, + false, + sponsorshipTypeIds + ); + }, []); const handlePageChange = (page) => { getSponsorForms( diff --git a/src/pages/sponsors/sponsor-pages-tab/index.js b/src/pages/sponsors/sponsor-pages-tab/index.js index 3a27ab286..cbd84c733 100644 --- a/src/pages/sponsors/sponsor-pages-tab/index.js +++ b/src/pages/sponsors/sponsor-pages-tab/index.js @@ -33,7 +33,7 @@ import { getSponsorships } from "../../../actions/sponsor-forms-actions"; import CustomAlert from "../../../components/mui/custom-alert"; import SearchInput from "../../../components/mui/search-input"; import MuiTable from "../../../components/mui/table/mui-table"; -import { DEFAULT_CURRENT_PAGE, MAX_PER_PAGE } from "../../../utils/constants"; +import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants"; import AddSponsorPageTemplatePopup from "./components/add-sponsor-page-template-popup"; import PageTemplatePopup from "../../sponsors-global/page-templates/page-template-popup"; @@ -45,12 +45,10 @@ const SponsorPagesTab = ({ managedPages, customizedPages, summitTZ, - sponsorships, getSponsorManagedPages, saveSponsorManagedPage, getSponsorCustomizedPages, - saveSponsorCustomizedPage, - getSponsorships + saveSponsorCustomizedPage }) => { const [openPopup, setOpenPopup] = useState(null); @@ -159,7 +157,7 @@ const SponsorPagesTab = ({ }; const handleAddPage = () => { - getSponsorships(1, MAX_PER_PAGE).then(() => setOpenPopup("new")); + setOpenPopup("new"); }; const handleArchiveCustomizedPage = (item) => @@ -275,6 +273,10 @@ const SponsorPagesTab = ({ ) ]; + const sponsorshipIds = sponsor.sponsorships_collection.sponsorships.map( + (e) => e.id + ); + return ( setOpenPopup(null)} - sponsorships={sponsorships.items} + sponsorshipIds={sponsorshipIds} + summitId={summitId} + sponsorId={sponsor.id} summitTZ={summitTZ} /> )} diff --git a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js index f11666c7c..610967249 100644 --- a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js @@ -20,6 +20,7 @@ import { } from "../../actions/sponsor-pages-actions"; import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; import { RECEIVE_GLOBAL_SPONSORSHIPS } from "../../actions/sponsor-forms-actions"; +import { PAGES_MODULE_KINDS } from "../../utils/constants"; const DEFAULT_STATE = { managedPages: { @@ -137,9 +138,14 @@ const sponsorPagePagesListReducer = (state = DEFAULT_STATE, action) => { code: a.code, name: a.name, allowed_add_ons: a.allowed_add_ons, - info_mod: a.modules_count.info_modules_count, - upload_mod: a.modules_count.media_request_modules_count, - download_mod: a.modules_count.document_download_modules_count + is_archived: a.is_archived, + info_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.INFO) + .length, + upload_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.MEDIA) + .length, + download_mod: a.modules.filter( + (m) => m.kind === PAGES_MODULE_KINDS.DOCUMENT + ).length })); return { From 55f2e47d2876f56c6ab8e5e7614c5ef40aad995f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Fri, 27 Feb 2026 13:14:35 -0300 Subject: [PATCH 6/9] feat: add edit functionality for customized sponsor pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-pages-actions.js | 69 ++++++++++++++++++- src/i18n/en.json | 4 +- src/pages/sponsors/sponsor-pages-tab/index.js | 31 ++++++--- .../sponsor-page-pages-list-reducer.js | 19 ++++- 4 files changed, 109 insertions(+), 14 deletions(-) diff --git a/src/actions/sponsor-pages-actions.js b/src/actions/sponsor-pages-actions.js index cc4500299..073c988e5 100644 --- a/src/actions/sponsor-pages-actions.js +++ b/src/actions/sponsor-pages-actions.js @@ -15,9 +15,9 @@ import { createAction, getRequest, postRequest, + putRequest, startLoading, stopLoading, - authErrorHandler, escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; import T from "i18n-react/dist/i18n-react"; @@ -32,6 +32,7 @@ import { } from "../utils/constants"; export const GLOBAL_PAGE_CLONED = "GLOBAL_PAGE_CLONED"; +export const RESET_EDIT_PAGE = "RESET_EDIT_PAGE"; export const REQUEST_SPONSOR_MANAGED_PAGES = "REQUEST_SPONSOR_MANAGED_PAGES"; export const RECEIVE_SPONSOR_MANAGED_PAGES = "RECEIVE_SPONSOR_MANAGED_PAGES"; @@ -41,7 +42,11 @@ export const REQUEST_SPONSOR_CUSTOMIZED_PAGES = "REQUEST_SPONSOR_CUSTOMIZED_PAGES"; export const RECEIVE_SPONSOR_CUSTOMIZED_PAGES = "RECEIVE_SPONSOR_CUSTOMIZED_PAGES"; +export const RECEIVE_SPONSOR_CUSTOMIZED_PAGE = + "RECEIVE_SPONSOR_CUSTOMIZED_PAGE"; export const SPONSOR_CUSTOMIZED_PAGE_ADDED = "SPONSOR_CUSTOMIZED_PAGE_ADDED"; +export const SPONSOR_CUSTOMIZED_PAGE_UPDATED = + "SPONSOR_CUSTOMIZED_PAGE_UPDATED"; export const cloneGlobalPage = (pagesIds, sponsorIds, allSponsors) => async (dispatch, getState) => { @@ -83,6 +88,10 @@ export const cloneGlobalPage = .finally(() => dispatch(stopLoading())); }; +export const resetSponsorPage = () => (dispatch) => { + dispatch(createAction(RESET_EDIT_PAGE)({})); +}; + /* ************************************************************************ */ /* MANAGED PAGES */ /* ************************************************************************ */ @@ -136,7 +145,7 @@ export const getSponsorManagedPages = createAction(REQUEST_SPONSOR_MANAGED_PAGES), createAction(RECEIVE_SPONSOR_MANAGED_PAGES), `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/managed-pages`, - authErrorHandler, + snackbarErrorHandler, { order, orderDir, page, perPage, term, hideArchived, summitTZ } )(params)(dispatch).then(() => { dispatch(stopLoading()); @@ -249,13 +258,39 @@ export const getSponsorCustomizedPages = createAction(REQUEST_SPONSOR_CUSTOMIZED_PAGES), createAction(RECEIVE_SPONSOR_CUSTOMIZED_PAGES), `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-pages`, - authErrorHandler, + snackbarErrorHandler, { order, orderDir, page, perPage, term, hideArchived, summitTZ } )(params)(dispatch).then(() => { dispatch(stopLoading()); }); }; +export const getSponsorCustomizedPage = + (pageId) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const { currentSummit } = currentSummitState; + const { + entity: { id: sponsorId } + } = currentSponsorState; + const accessToken = await getAccessTokenSafely(); + + dispatch(startLoading()); + + const params = { + fields: "id,code,name,allowed_add_ons,is_archived,modules", + access_token: accessToken + }; + + return getRequest( + null, + createAction(RECEIVE_SPONSOR_CUSTOMIZED_PAGE), + `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-pages/${pageId}`, + snackbarErrorHandler + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); + }; + export const saveSponsorCustomizedPage = (entity) => async (dispatch, getState) => { const { currentSummitState, currentSponsorState } = getState(); @@ -275,6 +310,28 @@ export const saveSponsorCustomizedPage = fields: "id,code,name,kind,modules_count,allowed_add_ons" }; + if (entity.id) { + return putRequest( + null, + createAction(SPONSOR_CUSTOMIZED_PAGE_UPDATED), + `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-pages/${entity.id}`, + normalizedEntity, + snackbarErrorHandler, + entity + )(params)(dispatch) + .then(() => { + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("edit_sponsor.pages_tab.custom_page_saved") + }) + ); + }) + .finally(() => { + dispatch(stopLoading()); + }); + } + return postRequest( null, createAction(SPONSOR_CUSTOMIZED_PAGE_ADDED), @@ -283,6 +340,12 @@ export const saveSponsorCustomizedPage = snackbarErrorHandler )(params)(dispatch).then(() => { dispatch(stopLoading()); + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("edit_sponsor.pages_tab.custom_page_created") + }) + ); }); }; diff --git a/src/i18n/en.json b/src/i18n/en.json index 71c581939..c9f25a6c8 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2487,7 +2487,9 @@ "no_pages": "There are no pages that match the criteria", "add_page_using_template": "Add Page Template", "add_selected_page_template": "Add Selected Page Template", - "managed_page_saved": "Managed Page updated successfully." + "managed_page_saved": "Managed Page updated successfully.", + "custom_page_saved": "Custom Sponsor Page saved", + "custom_page_created": "Custom Sponsor Page created" }, "cart_tab": { "new_form": "New Form", diff --git a/src/pages/sponsors/sponsor-pages-tab/index.js b/src/pages/sponsors/sponsor-pages-tab/index.js index cbd84c733..099c6ee6b 100644 --- a/src/pages/sponsors/sponsor-pages-tab/index.js +++ b/src/pages/sponsors/sponsor-pages-tab/index.js @@ -27,7 +27,9 @@ import { getSponsorManagedPages, getSponsorCustomizedPages, saveSponsorManagedPage, - saveSponsorCustomizedPage + saveSponsorCustomizedPage, + getSponsorCustomizedPage, + resetSponsorPage } from "../../../actions/sponsor-pages-actions"; import { getSponsorships } from "../../../actions/sponsor-forms-actions"; import CustomAlert from "../../../components/mui/custom-alert"; @@ -45,10 +47,13 @@ const SponsorPagesTab = ({ managedPages, customizedPages, summitTZ, + currentEditPage, getSponsorManagedPages, saveSponsorManagedPage, getSponsorCustomizedPages, - saveSponsorCustomizedPage + saveSponsorCustomizedPage, + getSponsorCustomizedPage, + resetSponsorPage }) => { const [openPopup, setOpenPopup] = useState(null); @@ -153,11 +158,11 @@ const SponsorPagesTab = ({ }; const handleUsingTemplate = () => { - setOpenPopup("template"); + setOpenPopup("usingTemplate"); }; const handleAddPage = () => { - setOpenPopup("new"); + setOpenPopup("pagePopup"); }; const handleArchiveCustomizedPage = (item) => @@ -175,7 +180,7 @@ const SponsorPagesTab = ({ }; const handleCustomizedEdit = (item) => { - console.log("EDIT CUSTOMIZED ", item); + getSponsorCustomizedPage(item.id).then(() => setOpenPopup("pagePopup")); }; const handleCustomizedDelete = (itemId) => { @@ -229,6 +234,11 @@ const SponsorPagesTab = ({ }).finally(() => setOpenPopup(null));; }; + const handleClosePagePopup = () => { + resetSponsorPage(); + setOpenPopup(null); + }; + const baseColumns = (name) => [ { columnKey: "name", @@ -389,7 +399,7 @@ const SponsorPagesTab = ({ /> - {openPopup === "template" && ( + {openPopup === "usingTemplate" && ( )} - {openPopup === "new" && ( + {openPopup === "pagePopup" && ( setOpenPopup(null)} + onClose={handleClosePagePopup} + pageTemplate={currentEditPage} sponsorshipIds={sponsorshipIds} summitId={summitId} sponsorId={sponsor.id} @@ -419,7 +430,9 @@ const mapStateToProps = ({ sponsorPagePagesListState }) => ({ export default connect(mapStateToProps, { getSponsorManagedPages, saveSponsorManagedPage, + getSponsorCustomizedPage, getSponsorCustomizedPages, saveSponsorCustomizedPage, - getSponsorships + getSponsorships, + resetSponsorPage })(SponsorPagesTab); diff --git a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js index 610967249..de341ace2 100644 --- a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js @@ -16,12 +16,21 @@ import { REQUEST_SPONSOR_MANAGED_PAGES, RECEIVE_SPONSOR_MANAGED_PAGES, RECEIVE_SPONSOR_CUSTOMIZED_PAGES, - REQUEST_SPONSOR_CUSTOMIZED_PAGES + REQUEST_SPONSOR_CUSTOMIZED_PAGES, + RECEIVE_SPONSOR_CUSTOMIZED_PAGE, + RESET_EDIT_PAGE } from "../../actions/sponsor-pages-actions"; import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; import { RECEIVE_GLOBAL_SPONSORSHIPS } from "../../actions/sponsor-forms-actions"; import { PAGES_MODULE_KINDS } from "../../utils/constants"; +const DEFAULT_PAGE = { + code: "", + name: "", + allowed_add_ons: [], + modules: [] +}; + const DEFAULT_STATE = { managedPages: { pages: [], @@ -41,6 +50,7 @@ const DEFAULT_STATE = { perPage: 10, totalItems: 0 }, + currentEditPage: DEFAULT_PAGE, sponsorships: { items: [], currentPage: 0, @@ -159,6 +169,13 @@ const sponsorPagePagesListReducer = (state = DEFAULT_STATE, action) => { } }; } + case RECEIVE_SPONSOR_CUSTOMIZED_PAGE: { + const customizedPage = payload.response; + return { ...state, currentEditPage: customizedPage }; + } + case RESET_EDIT_PAGE: { + return { ...state, currentEditPage: DEFAULT_PAGE }; + } case RECEIVE_GLOBAL_SPONSORSHIPS: { const { current_page: currentPage, From f7c734b36a47b18670834f62b6b9249fb45ecdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Sat, 28 Feb 2026 19:45:50 -0300 Subject: [PATCH 7/9] fix: add test cases for add and edit customized pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../__tests__/sponsor-pages-tab.test.js | 222 ++++++++++++++++++ .../sponsor-page-pages-list-reducer.js | 2 +- 2 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js diff --git a/src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js b/src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js new file mode 100644 index 000000000..10d610185 --- /dev/null +++ b/src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js @@ -0,0 +1,222 @@ +import React from "react"; +import userEvent from "@testing-library/user-event"; +import { act, screen, waitFor } from "@testing-library/react"; +import SponsorPagesTab from "../index"; +import { renderWithRedux } from "../../../../utils/test-utils"; +import { DEFAULT_STATE as sponsorPagesDefaultState } from "../../../../reducers/sponsors/sponsor-page-pages-list-reducer"; +import { getSponsorCustomizedPage } from "../../../../actions/sponsor-pages-actions"; + +// Mocks + +jest.mock( + "../components/add-sponsor-page-template-popup", + () => + function MockAddSponsorPageTemplatePopup({ onClose, onSubmit }) { + return ( +
+ + +
+ ); + } +); + +jest.mock( + "../../../sponsors-global/page-templates/page-template-popup", + () => + function MockPageTemplatePopup({ onClose, onSave, pageTemplate }) { + return ( +
+ {pageTemplate?.id} + + +
+ ); + } +); + +jest.mock("../../../../actions/sponsor-pages-actions", () => ({ + ...jest.requireActual("../../../../actions/sponsor-pages-actions"), + getSponsorManagedPages: jest.fn(() => ({ type: "MOCK_ACTION" })), + getSponsorCustomizedPages: jest.fn(() => ({ type: "MOCK_ACTION" })), + getSponsorCustomizedPage: jest.fn( + () => () => Promise.resolve({ id: 1, name: "Test Page" }) + ), + saveSponsorCustomizedPage: jest.fn(() => () => Promise.resolve()), + saveSponsorManagedPage: jest.fn(() => () => Promise.resolve()), + resetSponsorPage: jest.fn(() => ({ type: "MOCK_ACTION" })) +})); + +// Helpers + +const createSponsor = (overrides = {}) => ({ + id: 1, + sponsorships_collection: { sponsorships: [] }, + ...overrides +}); + +const createCustomizedPage = (id, overrides = {}) => ({ + id, + code: `CODE-${id}`, + name: `Page ${id}`, + is_archived: false, + ...overrides +}); + +const defaultState = { + sponsorPagePagesListState: { + ...sponsorPagesDefaultState, + managedPages: { + pages: [], + totalItems: 0, + currentPage: 1, + perPage: 10, + order: "id", + orderDir: 1 + }, + customizedPages: { + pages: [], + totalItems: 0, + currentPage: 1, + perPage: 10, + order: "id", + orderDir: 1 + }, + hideArchived: false, + term: "" + } +}; + +describe("SponsorPagesTab", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe("Component", () => { + it("should open page popup when new page button is clicked", async () => { + renderWithRedux( + , + { initialState: defaultState } + ); + + const newPageButton = screen.getByText("edit_sponsor.pages_tab.new_page"); + await act(async () => { + await userEvent.click(newPageButton); + }); + + expect(screen.getByTestId("page-template-popup")).toBeInTheDocument(); + }); + + it("should close page popup and reset form when close is clicked", async () => { + renderWithRedux( + , + { initialState: defaultState } + ); + + const newPageButton = screen.getByText("edit_sponsor.pages_tab.new_page"); + await act(async () => { + await userEvent.click(newPageButton); + }); + + expect(screen.getByTestId("page-template-popup")).toBeInTheDocument(); + + const closeButton = screen.getByText("Close"); + await act(async () => { + await userEvent.click(closeButton); + }); + + expect( + screen.queryByTestId("page-template-popup") + ).not.toBeInTheDocument(); + }); + + it("should call getSponsorCustomizedPage and open popup when edit is clicked", async () => { + renderWithRedux( + , + { + initialState: { + sponsorPagePagesListState: { + ...defaultState.sponsorPagePagesListState, + customizedPages: { + ...defaultState.sponsorPagePagesListState.customizedPages, + pages: [createCustomizedPage(1)], + totalItems: 1 + } + } + } + } + ); + + const editButton = screen.getByTestId("EditIcon").closest("button"); + await act(async () => { + await userEvent.click(editButton); + }); + + expect(getSponsorCustomizedPage).toHaveBeenCalledWith(1); + await waitFor(() => { + expect(screen.getByTestId("page-template-popup")).toBeInTheDocument(); + }); + }); + + it("should refresh customized pages list after save", async () => { + const { + getSponsorCustomizedPages + } = require("../../../../actions/sponsor-pages-actions"); + + renderWithRedux( + , + { + initialState: { + sponsorPagePagesListState: { + ...defaultState.sponsorPagePagesListState, + customizedPages: { + ...defaultState.sponsorPagePagesListState.customizedPages, + pages: [createCustomizedPage(1)], + totalItems: 1 + } + } + } + } + ); + + const editButton = screen.getByTestId("EditIcon").closest("button"); + await act(async () => { + await userEvent.click(editButton); + }); + + await waitFor(() => { + expect(screen.getByTestId("page-template-popup")).toBeInTheDocument(); + }); + + const saveButton = screen.getByText("Save"); + await act(async () => { + await userEvent.click(saveButton); + }); + + await waitFor(() => { + expect(getSponsorCustomizedPages).toHaveBeenCalledTimes(2); + }); + expect( + screen.queryByTestId("page-template-popup") + ).not.toBeInTheDocument(); + }); + }); +}); diff --git a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js index de341ace2..9c7f4eb03 100644 --- a/src/reducers/sponsors/sponsor-page-pages-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-pages-list-reducer.js @@ -31,7 +31,7 @@ const DEFAULT_PAGE = { modules: [] }; -const DEFAULT_STATE = { +export const DEFAULT_STATE = { managedPages: { pages: [], order: "name", From 5c1373e4c1394d873a5fe9bd1481f163f51a617e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Thu, 5 Mar 2026 16:10:13 -0300 Subject: [PATCH 8/9] fix: adjust fields on actions, fix formik text editor functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-pages-actions.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/actions/sponsor-pages-actions.js b/src/actions/sponsor-pages-actions.js index 073c988e5..4a3222b4e 100644 --- a/src/actions/sponsor-pages-actions.js +++ b/src/actions/sponsor-pages-actions.js @@ -237,7 +237,9 @@ export const getSponsorCustomizedPages = const params = { page, - fields: "id,code,name,allowed_add_ons,is_archived,modules", + fields: + "id,code,name,allowed_add_ons,is_archived,modules,allowed_add_ons.type,allowed_add_ons.name,allowed_add_ons.id", + expand: "allowed_add_ons", per_page: perPage, access_token: accessToken }; @@ -277,7 +279,9 @@ export const getSponsorCustomizedPage = dispatch(startLoading()); const params = { - fields: "id,code,name,allowed_add_ons,is_archived,modules", + fields: + "id,code,name,allowed_add_ons,is_archived,modules,allowed_add_ons.name,allowed_add_ons.id", + expand: "allowed_add_ons", access_token: accessToken }; From 8996f6b872f5859b0cd9e074e97b983de8929833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Mon, 23 Mar 2026 16:25:18 -0300 Subject: [PATCH 9/9] fix: remove unused code, move stopLoading to finally on promises MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/actions/sponsor-pages-actions.js | 27 ++++++++++--------- src/pages/sponsors/sponsor-pages-tab/index.js | 8 +++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/actions/sponsor-pages-actions.js b/src/actions/sponsor-pages-actions.js index 4a3222b4e..af7cdde1c 100644 --- a/src/actions/sponsor-pages-actions.js +++ b/src/actions/sponsor-pages-actions.js @@ -147,7 +147,7 @@ export const getSponsorManagedPages = `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/managed-pages`, snackbarErrorHandler, { order, orderDir, page, perPage, term, hideArchived, summitTZ } - )(params)(dispatch).then(() => { + )(params)(dispatch).finally(() => { dispatch(stopLoading()); }); }; @@ -262,7 +262,7 @@ export const getSponsorCustomizedPages = `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-pages`, snackbarErrorHandler, { order, orderDir, page, perPage, term, hideArchived, summitTZ } - )(params)(dispatch).then(() => { + )(params)(dispatch).finally(() => { dispatch(stopLoading()); }); }; @@ -290,7 +290,7 @@ export const getSponsorCustomizedPage = createAction(RECEIVE_SPONSOR_CUSTOMIZED_PAGE), `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-pages/${pageId}`, snackbarErrorHandler - )(params)(dispatch).then(() => { + )(params)(dispatch).finally(() => { dispatch(stopLoading()); }); }; @@ -342,15 +342,18 @@ export const saveSponsorCustomizedPage = `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/sponsor-pages`, normalizedEntity, snackbarErrorHandler - )(params)(dispatch).then(() => { - dispatch(stopLoading()); - dispatch( - snackbarSuccessHandler({ - title: T.translate("general.success"), - html: T.translate("edit_sponsor.pages_tab.custom_page_created") - }) - ); - }); + )(params)(dispatch) + .then(() => { + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("edit_sponsor.pages_tab.custom_page_created") + }) + ); + }) + .finally(() => { + dispatch(stopLoading()); + }); }; const normalizeSponsorCustomPage = (entity, summitTZ) => { diff --git a/src/pages/sponsors/sponsor-pages-tab/index.js b/src/pages/sponsors/sponsor-pages-tab/index.js index 099c6ee6b..9365f6cc2 100644 --- a/src/pages/sponsors/sponsor-pages-tab/index.js +++ b/src/pages/sponsors/sponsor-pages-tab/index.js @@ -31,7 +31,6 @@ import { getSponsorCustomizedPage, resetSponsorPage } from "../../../actions/sponsor-pages-actions"; -import { getSponsorships } from "../../../actions/sponsor-forms-actions"; import CustomAlert from "../../../components/mui/custom-alert"; import SearchInput from "../../../components/mui/search-input"; import MuiTable from "../../../components/mui/table/mui-table"; @@ -217,7 +216,8 @@ const SponsorPagesTab = ({ order, orderDir ); - }).finally(() => setOpenPopup(null)); + }) + .finally(() => setOpenPopup(null)); }; const handleSaveCustomizedPage = (entity) => { @@ -231,7 +231,8 @@ const SponsorPagesTab = ({ order, orderDir ); - }).finally(() => setOpenPopup(null));; + }) + .finally(() => setOpenPopup(null)); }; const handleClosePagePopup = () => { @@ -433,6 +434,5 @@ export default connect(mapStateToProps, { getSponsorCustomizedPage, getSponsorCustomizedPages, saveSponsorCustomizedPage, - getSponsorships, resetSponsorPage })(SponsorPagesTab);