From 27125bdebee2c9c8f68f133263aaa5aae5f5da9a Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Thu, 5 Feb 2026 18:32:56 -0300 Subject: [PATCH 1/7] feat: sponsor media upload tab - WIP --- .env.example | 2 +- src/actions/sponsor-mu-actions.js | 70 +++++++ src/pages/sponsors/edit-sponsor-page.js | 4 + .../sponsor-media-upload-tab/index.js | 186 ++++++++++++++++++ .../sponsors/sponsor-page-mu-list-reducer.js | 97 +++++++++ src/store.js | 2 + src/utils/constants.js | 6 + 7 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 src/actions/sponsor-mu-actions.js create mode 100644 src/pages/sponsors/sponsor-media-upload-tab/index.js create mode 100644 src/reducers/sponsors/sponsor-page-mu-list-reducer.js diff --git a/.env.example b/.env.example index 89c72cad4..3ea8ce1cc 100644 --- a/.env.example +++ b/.env.example @@ -17,7 +17,7 @@ SPONSOR_USERS_API_SCOPES="show-medata/read show-medata/write access-requests/rea EMAIL_SCOPES="clients/read templates/read templates/write emails/read" FILE_UPLOAD_SCOPES="files/upload" SPONSOR_PAGES_API_URL=https://sponsor-pages-api.dev.fnopen.com -SPONSOR_PAGES_SCOPES="page-template/read page-template/write show-page/read show-page/write" +SPONSOR_PAGES_SCOPES="page-template/read page-template/write show-page/read show-page/write media-upload/read" SCOPES="profile openid offline_access ${SPONSOR_USERS_API_SCOPES} ${PURCHASES_API_SCOPES} ${EMAIL_SCOPES} ${FILE_UPLOAD_SCOPES} ${SPONSOR_PAGES_SCOPES} ${SCOPES_BASE_REALM}/summits/delete-event ${SCOPES_BASE_REALM}/summits/write ${SCOPES_BASE_REALM}/summits/write-event ${SCOPES_BASE_REALM}/summits/read/all ${SCOPES_BASE_REALM}/summits/read ${SCOPES_BASE_REALM}/summits/publish-event ${SCOPES_BASE_REALM}/members/read ${SCOPES_BASE_REALM}/members/read/me ${SCOPES_BASE_REALM}/speakers/write ${SCOPES_BASE_REALM}/attendees/write ${SCOPES_BASE_REALM}/members/write ${SCOPES_BASE_REALM}/organizations/write ${SCOPES_BASE_REALM}/organizations/read ${SCOPES_BASE_REALM}/summits/write-presentation-materials ${SCOPES_BASE_REALM}/summits/registration-orders/update ${SCOPES_BASE_REALM}/summits/registration-orders/delete ${SCOPES_BASE_REALM}/summits/registration-orders/create/offline ${SCOPES_BASE_REALM}/summits/badge-scans/read entity-updates/publish ${SCOPES_BASE_REALM}/audit-logs/read" GOOGLE_API_KEY= ALLOWED_USER_GROUPS="super-admins administrators summit-front-end-administrators summit-room-administrators track-chairs-admins sponsors" diff --git a/src/actions/sponsor-mu-actions.js b/src/actions/sponsor-mu-actions.js new file mode 100644 index 000000000..0841905a7 --- /dev/null +++ b/src/actions/sponsor-mu-actions.js @@ -0,0 +1,70 @@ +/** + * Copyright 2018 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 { + createAction, + getRequest, + startLoading, + stopLoading +} from "openstack-uicore-foundation/lib/utils/actions"; +import { getAccessTokenSafely } from "../utils/methods"; +import { + DEFAULT_CURRENT_PAGE, + DEFAULT_ORDER_DIR, + DEFAULT_PER_PAGE +} from "../utils/constants"; +import { snackbarErrorHandler } from "./base-actions"; + +export const REQUEST_SPONSOR_MEDIA_UPLOADS = "REQUEST_SPONSOR_MEDIA_UPLOADS"; +export const RECEIVE_SPONSOR_MEDIA_UPLOADS = "RECEIVE_SPONSOR_MEDIA_UPLOADS"; + +export const getSponsorMURequests = + ( + currentPage = DEFAULT_CURRENT_PAGE, + perPage = DEFAULT_PER_PAGE, + order = "id", + orderDir = DEFAULT_ORDER_DIR + ) => + async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const { currentSummit } = currentSummitState; + const summitTZ = currentSummit.time_zone.name; + const { entity: sponsor } = currentSponsorState; + const accessToken = await getAccessTokenSafely(); + + dispatch(startLoading()); + + const params = { + page: currentPage, + // fields: "id,code,name,level,expire_date", + // relations: "add_ons", + per_page: perPage, + access_token: accessToken + }; + + // order + if (order != null && orderDir != null) { + const orderDirSign = orderDir === 1 ? "" : "-"; + params.order = `${orderDirSign}${order}`; + } + + return getRequest( + createAction(REQUEST_SPONSOR_MEDIA_UPLOADS), + createAction(RECEIVE_SPONSOR_MEDIA_UPLOADS), + `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/custom-media-request-modules`, + snackbarErrorHandler, + { order, orderDir, currentPage, perPage, summitTZ } + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); + }; diff --git a/src/pages/sponsors/edit-sponsor-page.js b/src/pages/sponsors/edit-sponsor-page.js index 417907aac..782a07f6e 100644 --- a/src/pages/sponsors/edit-sponsor-page.js +++ b/src/pages/sponsors/edit-sponsor-page.js @@ -47,6 +47,7 @@ import SponsorPagesTab from "./sponsor-pages-tab"; import SponsorFormsManageItems from "./sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items"; import { SPONSOR_TABS } from "../../utils/constants"; import SponsorPurchasesTab from "./sponsor-purchases-tab"; +import SponsorMediaUploadTab from "./sponsor-media-upload-tab"; export const tabsToFragmentMap = [ "general", @@ -256,6 +257,9 @@ const EditSponsorPage = (props) => { history={history} /> + + + {isNestedFormItemRoute ? ( diff --git a/src/pages/sponsors/sponsor-media-upload-tab/index.js b/src/pages/sponsors/sponsor-media-upload-tab/index.js new file mode 100644 index 000000000..a87c401b2 --- /dev/null +++ b/src/pages/sponsors/sponsor-media-upload-tab/index.js @@ -0,0 +1,186 @@ +/** + * 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 } from "react"; +import { connect } from "react-redux"; +import T from "i18n-react/dist/i18n-react"; +import { Box, Chip, IconButton } from "@mui/material"; +import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward"; +import DownloadIcon from "@mui/icons-material/Download"; +import DeleteIcon from "@mui/icons-material/Delete"; +import EditIcon from "@mui/icons-material/Edit"; +import { getSponsorMURequests } from "../../../actions/sponsor-mu-actions"; +import CustomAlert from "../../../components/mui/custom-alert"; +import MuiTable from "../../../components/mui/table/mui-table"; +import { SPONSOR_MEDIA_UPLOAD_STATUS } from "../../../utils/constants"; + +const SponsorMediaUploadTab = ({ sponsorRequests, getSponsorMURequests }) => { + useEffect(() => { + getSponsorMURequests(); + }, []); + + const handleSponsorPageChange = (page) => { + const { perPage, order, orderDir } = sponsorRequests; + getSponsorMURequests(page, perPage, order, orderDir); + }; + + const handleSponsorSort = (key, dir) => { + const { currentPage, perPage } = sponsorRequests; + getSponsorMURequests(currentPage, perPage, key, dir); + }; + + const handleSponsorDelete = (itemId) => { + console.log("DELETE : ", itemId); + }; + + const handleSponsorView = (item) => { + console.log("VIEW : ", item); + }; + + const handleSponsorUpload = (item) => { + console.log("UPLOAD : ", item); + }; + + const handleSponsorDownload = (item) => { + console.log("DOWNLOAD : ", item); + }; + + const sponsorColumns = [ + { + columnKey: "name", + header: T.translate("edit_sponsor.mu_tab.sponsor_request"), + sortable: true + }, + { + columnKey: "add_on", + header: T.translate("edit_sponsor.mu_tab.add_on") + }, + { + columnKey: "max_size", + header: T.translate("edit_sponsor.mu_tab.max_size") + }, + { + columnKey: "format", + header: T.translate("edit_sponsor.mu_tab.format") + }, + { + columnKey: "deadline", + header: T.translate("edit_sponsor.mu_tab.deadline"), + sortable: true + }, + { + columnKey: "status", + header: T.translate("edit_sponsor.mu_tab.status"), + sortable: true, + render: (row) => ( + + ) + }, + { + columnKey: "view", + header: "", + width: 80, + align: "center", + render: (row) => ( + handleSponsorView(row)} + > + + + ) + }, + { + columnKey: "download", + header: "", + width: 80, + align: "center", + render: (row) => ( + handleSponsorDownload(row)} + > + + + ) + }, + { + columnKey: "upload_delete", + header: "", + width: 80, + align: "center", + render: (row) => { + if (row.file) { + return ( + handleSponsorDelete(row.id)} + > + + + ); + } + return ( + handleSponsorUpload(row)}> + + + ); + } + } + ]; + + return ( + + +
+ + {sponsorRequests.totalCount}{" "} + {T.translate("edit_sponsor.mu_tab.media_upload")} + + +
+
+ ); +}; + +const mapStateToProps = ({ sponsorPageMUListState }) => ({ + ...sponsorPageMUListState +}); + +export default connect(mapStateToProps, { + getSponsorMURequests +})(SponsorMediaUploadTab); diff --git a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js new file mode 100644 index 000000000..333331e16 --- /dev/null +++ b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js @@ -0,0 +1,97 @@ +/** + * 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 { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods"; +import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; +import { + RECEIVE_SPONSOR_MEDIA_UPLOADS, + REQUEST_SPONSOR_MEDIA_UPLOADS +} from "../../actions/sponsor-mu-actions"; + +const DEFAULT_STATE = { + sponsorRequests: { + requests: [], + order: "name", + orderDir: 1, + currentPage: 1, + lastPage: 1, + perPage: 10, + totalCount: 0 + }, + summitTZ: "" +}; + +const sponsorPageMUListReducer = (state = DEFAULT_STATE, action) => { + const { type, payload } = action; + + switch (type) { + case SET_CURRENT_SUMMIT: + case LOGOUT_USER: { + return DEFAULT_STATE; + } + case REQUEST_SPONSOR_MEDIA_UPLOADS: { + const { order, orderDir, page, summitTZ } = payload; + + return { + ...state, + sponsorRequests: { + ...state.sponsorRequests, + order, + orderDir, + requests: [], + currentPage: page + }, + summitTZ + }; + } + case RECEIVE_SPONSOR_MEDIA_UPLOADS: { + const { + current_page: currentPage, + total, + last_page: lastPage + } = payload.response; + + const requests = payload.response.data.map((a) => { + const expiresAt = a.expires_at + ? epochToMomentTimeZone(a.expires_at, state.summitTZ)?.format( + "YYYY/MM/DD" + ) + : "N/A"; + + return { + id: a.id, + code: a.code, + name: a.name, + items_count: a.items_count, + expires_at: expiresAt + }; + }); + + return { + ...state, + sponsorRequests: { + ...state.sponsorRequests, + requests, + currentPage, + totalCount: total, + lastPage + } + }; + } + default: + return state; + } +}; + +export default sponsorPageMUListReducer; diff --git a/src/store.js b/src/store.js index 05b12a62f..5d92e92ec 100644 --- a/src/store.js +++ b/src/store.js @@ -170,6 +170,7 @@ import sponsorCustomizedFormItemsListReducer from "./reducers/sponsors/sponsor-c import showPagesListReducer from "./reducers/sponsors/show-pages-list-reducer.js"; import sponsorPagePurchaseListReducer from "./reducers/sponsors/sponsor-page-purchase-list-reducer.js"; import sponsorPagePagesListReducer from "./reducers/sponsors/sponsor-page-pages-list-reducer.js"; +import sponsorPageMUListReducer from "./reducers/sponsors/sponsor-page-mu-list-reducer.js"; // default: localStorage if web, AsyncStorage if react-native @@ -258,6 +259,7 @@ const reducers = persistCombineReducers(config, { sponsorPageFormsListState: sponsorPageFormsListReducer, sponsorPageCartListState: sponsorPageCartListReducer, sponsorPagePagesListState: sponsorPagePagesListReducer, + sponsorPageMUListState: sponsorPageMUListReducer, sponsorCustomizedFormState: sponsorCustomizedFormReducer, sponsorCustomizedFormItemsListState: sponsorCustomizedFormItemsListReducer, sponsorPagePurchaseListState: sponsorPagePurchaseListReducer, diff --git a/src/utils/constants.js b/src/utils/constants.js index 3983fbeb5..cc6604535 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -264,3 +264,9 @@ export const SPONSOR_USER_ASSIGNMENT_TYPE = { EXISTING: "existing", NEW: "new" }; + +export const SPONSOR_MEDIA_UPLOAD_STATUS = { + PENDING: "PENDING", + DEADLINE: "DEADLINE ALERT", + COMPLETE: "COMPLETE" +}; From dbbeb0c5f022af2e1307540ca32ede1cf2a4d6ae Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Fri, 6 Feb 2026 10:43:43 -0300 Subject: [PATCH 2/7] feat: build grid - WIP --- src/actions/sponsor-mu-actions.js | 4 ++-- src/i18n/en.json | 10 ++++++++++ src/pages/sponsors/sponsor-media-upload-tab/index.js | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/actions/sponsor-mu-actions.js b/src/actions/sponsor-mu-actions.js index 0841905a7..cba5d7cdb 100644 --- a/src/actions/sponsor-mu-actions.js +++ b/src/actions/sponsor-mu-actions.js @@ -46,8 +46,8 @@ export const getSponsorMURequests = const params = { page: currentPage, - // fields: "id,code,name,level,expire_date", - // relations: "add_ons", + // fields: "id,name,max_file_size,media_upload,file_type", + // relations: "media_upload,file_type", per_page: perPage, access_token: accessToken }; diff --git a/src/i18n/en.json b/src/i18n/en.json index 9db755450..513dffb91 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2501,6 +2501,16 @@ "details": "Details", "purchases": "purchases" }, + "mu_tab": { + "alert_info": "Here you can see the status of this Sponsor's Media Uploads. If an additional file upload is required, it must be requested from the specific page where it is needed.", + "media_upload": "media upload", + "sponsor_request": "Sponsor Specific Request", + "add_on": "Add-on", + "max_size": "Max Size", + "format": "Format", + "deadline": "Deadline", + "status": "Status" + }, "placeholders": { "select_sponsorship": "Select a Sponsorship", "sponsorship_type": "Start typing to choose a Tier...", diff --git a/src/pages/sponsors/sponsor-media-upload-tab/index.js b/src/pages/sponsors/sponsor-media-upload-tab/index.js index a87c401b2..bfbcae982 100644 --- a/src/pages/sponsors/sponsor-media-upload-tab/index.js +++ b/src/pages/sponsors/sponsor-media-upload-tab/index.js @@ -155,9 +155,10 @@ const SponsorMediaUploadTab = ({ sponsorRequests, getSponsorMURequests }) => { hideIcon />
- + {sponsorRequests.totalCount}{" "} {T.translate("edit_sponsor.mu_tab.media_upload")} + {sponsorRequests.totalCount === 1 ? "" : "s"} Date: Fri, 6 Feb 2026 11:21:10 -0300 Subject: [PATCH 3/7] feat: managed requests - WIP --- src/actions/sponsor-mu-actions.js | 43 +++ .../sponsor-media-upload-tab/index.js | 248 +++++++++++------- .../sponsors/sponsor-page-mu-list-reducer.js | 60 +++++ 3 files changed, 260 insertions(+), 91 deletions(-) diff --git a/src/actions/sponsor-mu-actions.js b/src/actions/sponsor-mu-actions.js index cba5d7cdb..f4f5ed8b6 100644 --- a/src/actions/sponsor-mu-actions.js +++ b/src/actions/sponsor-mu-actions.js @@ -27,6 +27,8 @@ import { snackbarErrorHandler } from "./base-actions"; export const REQUEST_SPONSOR_MEDIA_UPLOADS = "REQUEST_SPONSOR_MEDIA_UPLOADS"; export const RECEIVE_SPONSOR_MEDIA_UPLOADS = "RECEIVE_SPONSOR_MEDIA_UPLOADS"; +export const REQUEST_GENERAL_MEDIA_UPLOADS = "REQUEST_GENERAL_MEDIA_UPLOADS"; +export const RECEIVE_GENERAL_MEDIA_UPLOADS = "RECEIVE_GENERAL_MEDIA_UPLOADS"; export const getSponsorMURequests = ( @@ -68,3 +70,44 @@ export const getSponsorMURequests = dispatch(stopLoading()); }); }; + +export const getGeneralMURequests = + ( + currentPage = DEFAULT_CURRENT_PAGE, + perPage = DEFAULT_PER_PAGE, + order = "id", + orderDir = DEFAULT_ORDER_DIR + ) => + async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const { currentSummit } = currentSummitState; + const summitTZ = currentSummit.time_zone.name; + const { entity: sponsor } = currentSponsorState; + const accessToken = await getAccessTokenSafely(); + + dispatch(startLoading()); + + const params = { + page: currentPage, + // fields: "id,name,max_file_size,media_upload,file_type", + expand: "media_upload,file_type", + per_page: perPage, + access_token: accessToken + }; + + // order + if (order != null && orderDir != null) { + const orderDirSign = orderDir === 1 ? "" : "-"; + params.order = `${orderDirSign}${order}`; + } + + return getRequest( + createAction(REQUEST_GENERAL_MEDIA_UPLOADS), + createAction(RECEIVE_GENERAL_MEDIA_UPLOADS), + `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/managed-media-request-modules`, + snackbarErrorHandler, + { order, orderDir, currentPage, perPage, summitTZ } + )(params)(dispatch).then(() => { + dispatch(stopLoading()); + }); + }; diff --git a/src/pages/sponsors/sponsor-media-upload-tab/index.js b/src/pages/sponsors/sponsor-media-upload-tab/index.js index bfbcae982..69a5a38b9 100644 --- a/src/pages/sponsors/sponsor-media-upload-tab/index.js +++ b/src/pages/sponsors/sponsor-media-upload-tab/index.js @@ -19,14 +19,23 @@ import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward"; import DownloadIcon from "@mui/icons-material/Download"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; -import { getSponsorMURequests } from "../../../actions/sponsor-mu-actions"; +import { + getSponsorMURequests, + getGeneralMURequests +} from "../../../actions/sponsor-mu-actions"; import CustomAlert from "../../../components/mui/custom-alert"; import MuiTable from "../../../components/mui/table/mui-table"; import { SPONSOR_MEDIA_UPLOAD_STATUS } from "../../../utils/constants"; -const SponsorMediaUploadTab = ({ sponsorRequests, getSponsorMURequests }) => { +const SponsorMediaUploadTab = ({ + sponsorRequests, + generalRequests, + getSponsorMURequests, + getGeneralMURequests +}) => { useEffect(() => { getSponsorMURequests(); + getGeneralMURequests(); }, []); const handleSponsorPageChange = (page) => { @@ -55,98 +64,134 @@ const SponsorMediaUploadTab = ({ sponsorRequests, getSponsorMURequests }) => { console.log("DOWNLOAD : ", item); }; - const sponsorColumns = [ - { - columnKey: "name", - header: T.translate("edit_sponsor.mu_tab.sponsor_request"), - sortable: true - }, - { - columnKey: "add_on", - header: T.translate("edit_sponsor.mu_tab.add_on") - }, - { - columnKey: "max_size", - header: T.translate("edit_sponsor.mu_tab.max_size") - }, - { - columnKey: "format", - header: T.translate("edit_sponsor.mu_tab.format") - }, - { - columnKey: "deadline", - header: T.translate("edit_sponsor.mu_tab.deadline"), - sortable: true - }, - { - columnKey: "status", - header: T.translate("edit_sponsor.mu_tab.status"), - sortable: true, - render: (row) => ( - { + const { perPage, order, orderDir } = sponsorRequests; + getSponsorMURequests(page, perPage, order, orderDir); + }; + + const handleGeneralSort = (key, dir) => { + const { currentPage, perPage } = sponsorRequests; + getSponsorMURequests(currentPage, perPage, key, dir); + }; + + const handleGeneralDelete = (itemId) => { + console.log("DELETE : ", itemId); + }; + + const handleGeneralView = (item) => { + console.log("VIEW : ", item); + }; + + const handleGeneralUpload = (item) => { + console.log("UPLOAD : ", item); + }; + + const handleGeneralDownload = (item) => { + console.log("DOWNLOAD : ", item); + }; + + const getTableColumns = (type) => { + const isSponsor = type === "sponsor"; + const nameLabel = isSponsor + ? T.translate("edit_sponsor.mu_tab.sponsor_request") + : T.translate("edit_sponsor.mu_tab.general_request"); + const onView = isSponsor ? handleSponsorView : handleGeneralView; + const onDownload = isSponsor + ? handleSponsorDownload + : handleGeneralDownload; + const onDelete = isSponsor ? handleSponsorDelete : handleGeneralDelete; + const onUpload = isSponsor ? handleSponsorUpload : handleGeneralUpload; + + return [ + { + columnKey: "name", + header: nameLabel, + sortable: true + }, + { + columnKey: "add_on", + header: T.translate("edit_sponsor.mu_tab.add_on") + }, + { + columnKey: "max_size", + header: T.translate("edit_sponsor.mu_tab.max_size") + }, + { + columnKey: "format", + header: T.translate("edit_sponsor.mu_tab.format") + }, + { + columnKey: "deadline", + header: T.translate("edit_sponsor.mu_tab.deadline"), + sortable: true + }, + { + columnKey: "status", + header: T.translate("edit_sponsor.mu_tab.status"), + sortable: true, + render: (row) => ( + + ) + }, + { + columnKey: "view", + header: "", + width: 80, + align: "center", + render: (row) => ( + onView(row)} + > + + + ) + }, + { + columnKey: "download", + header: "", + width: 80, + align: "center", + render: (row) => ( + onDownload(row)} + > + + + ) + }, + { + columnKey: "upload_delete", + header: "", + width: 80, + align: "center", + render: (row) => { + if (row.file) { + return ( + onDelete(row.id)}> + + + ); } - label={row.status} - /> - ) - }, - { - columnKey: "view", - header: "", - width: 80, - align: "center", - render: (row) => ( - handleSponsorView(row)} - > - - - ) - }, - { - columnKey: "download", - header: "", - width: 80, - align: "center", - render: (row) => ( - handleSponsorDownload(row)} - > - - - ) - }, - { - columnKey: "upload_delete", - header: "", - width: 80, - align: "center", - render: (row) => { - if (row.file) { return ( - handleSponsorDelete(row.id)} - > - + onUpload(row)}> + ); } - return ( - handleSponsorUpload(row)}> - - - ); } - } - ]; + ]; + }; return ( @@ -155,13 +200,13 @@ const SponsorMediaUploadTab = ({ sponsorRequests, getSponsorMURequests }) => { hideIcon />
- + {sponsorRequests.totalCount}{" "} {T.translate("edit_sponsor.mu_tab.media_upload")} {sponsorRequests.totalCount === 1 ? "" : "s"} { onSort={handleSponsorSort} />
+
+ + {generalRequests.totalCount}{" "} + {T.translate("edit_sponsor.mu_tab.media_upload")} + {generalRequests.totalCount === 1 ? "" : "s"} + + +
); }; @@ -183,5 +248,6 @@ const mapStateToProps = ({ sponsorPageMUListState }) => ({ }); export default connect(mapStateToProps, { - getSponsorMURequests + getSponsorMURequests, + getGeneralMURequests })(SponsorMediaUploadTab); diff --git a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js index 333331e16..682b57fe7 100644 --- a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js @@ -15,7 +15,9 @@ import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods"; import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; import { + RECEIVE_GENERAL_MEDIA_UPLOADS, RECEIVE_SPONSOR_MEDIA_UPLOADS, + REQUEST_GENERAL_MEDIA_UPLOADS, REQUEST_SPONSOR_MEDIA_UPLOADS } from "../../actions/sponsor-mu-actions"; @@ -29,6 +31,15 @@ const DEFAULT_STATE = { perPage: 10, totalCount: 0 }, + generalRequests: { + requests: [], + order: "name", + orderDir: 1, + currentPage: 1, + lastPage: 1, + perPage: 10, + totalCount: 0 + }, summitTZ: "" }; @@ -89,6 +100,55 @@ const sponsorPageMUListReducer = (state = DEFAULT_STATE, action) => { } }; } + case REQUEST_GENERAL_MEDIA_UPLOADS: { + const { order, orderDir, page, summitTZ } = payload; + + return { + ...state, + generalRequests: { + ...state.generalRequests, + order, + orderDir, + requests: [], + currentPage: page + }, + summitTZ + }; + } + case RECEIVE_GENERAL_MEDIA_UPLOADS: { + const { + current_page: currentPage, + total, + last_page: lastPage + } = payload.response; + + const requests = payload.response.data.map((a) => { + const expiresAt = a.expires_at + ? epochToMomentTimeZone(a.expires_at, state.summitTZ)?.format( + "YYYY/MM/DD" + ) + : "N/A"; + + return { + id: a.id, + code: a.code, + name: a.name, + items_count: a.items_count, + expires_at: expiresAt + }; + }); + + return { + ...state, + generalRequests: { + ...state.generalRequests, + requests, + currentPage, + totalCount: total, + lastPage + } + }; + } default: return state; } From 8180d4a3a558e187c1d4e77aa8c598b5242d2e9d Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Mon, 16 Feb 2026 16:56:20 -0300 Subject: [PATCH 4/7] feat: sponsor media upload grids --- src/i18n/en.json | 1 + .../sponsor-media-upload-tab/index.js | 34 ++++++--- .../sponsors/sponsor-page-mu-list-reducer.js | 75 +++++++++++-------- src/utils/constants.js | 8 +- src/utils/methods.js | 4 + 5 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index 513dffb91..06f84425f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2505,6 +2505,7 @@ "alert_info": "Here you can see the status of this Sponsor's Media Uploads. If an additional file upload is required, it must be requested from the specific page where it is needed.", "media_upload": "media upload", "sponsor_request": "Sponsor Specific Request", + "general_request": "General Media Request", "add_on": "Add-on", "max_size": "Max Size", "format": "Format", diff --git a/src/pages/sponsors/sponsor-media-upload-tab/index.js b/src/pages/sponsors/sponsor-media-upload-tab/index.js index 69a5a38b9..3003237c7 100644 --- a/src/pages/sponsors/sponsor-media-upload-tab/index.js +++ b/src/pages/sponsors/sponsor-media-upload-tab/index.js @@ -18,10 +18,10 @@ import { Box, Chip, IconButton } from "@mui/material"; import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward"; import DownloadIcon from "@mui/icons-material/Download"; import DeleteIcon from "@mui/icons-material/Delete"; -import EditIcon from "@mui/icons-material/Edit"; +import VisibilityIcon from "@mui/icons-material/Visibility"; import { - getSponsorMURequests, - getGeneralMURequests + getGeneralMURequests, + getSponsorMURequests } from "../../../actions/sponsor-mu-actions"; import CustomAlert from "../../../components/mui/custom-alert"; import MuiTable from "../../../components/mui/table/mui-table"; @@ -102,6 +102,19 @@ const SponsorMediaUploadTab = ({ const onDelete = isSponsor ? handleSponsorDelete : handleGeneralDelete; const onUpload = isSponsor ? handleSponsorUpload : handleGeneralUpload; + const getChipColor = (status) => { + switch (status) { + case SPONSOR_MEDIA_UPLOAD_STATUS.DEADLINE_ALERT: + return "warning"; + case SPONSOR_MEDIA_UPLOAD_STATUS.DEADLINE_MISSED: + return "error"; + case SPONSOR_MEDIA_UPLOAD_STATUS.COMPLETE: + return "success"; + default: + return "default"; + } + }; + return [ { columnKey: "name", @@ -131,11 +144,8 @@ const SponsorMediaUploadTab = ({ sortable: true, render: (row) => ( ) @@ -148,10 +158,10 @@ const SponsorMediaUploadTab = ({ render: (row) => ( onView(row)} > - + ) }, @@ -163,7 +173,7 @@ const SponsorMediaUploadTab = ({ render: (row) => ( onDownload(row)} > @@ -176,7 +186,7 @@ const SponsorMediaUploadTab = ({ width: 80, align: "center", render: (row) => { - if (row.file) { + if (row.media_upload) { return ( onDelete(row.id)}> diff --git a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js index 682b57fe7..164760d60 100644 --- a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js @@ -11,6 +11,7 @@ * limitations under the License. * */ +import moment from "moment-timezone"; import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods"; import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; @@ -20,6 +21,11 @@ import { REQUEST_GENERAL_MEDIA_UPLOADS, REQUEST_SPONSOR_MEDIA_UPLOADS } from "../../actions/sponsor-mu-actions"; +import { bytesToMb } from "../../utils/methods"; +import { + DEADLINE_ALERT_DAYS, + SPONSOR_MEDIA_UPLOAD_STATUS +} from "../../utils/constants"; const DEFAULT_STATE = { sponsorRequests: { @@ -43,6 +49,39 @@ const DEFAULT_STATE = { summitTZ: "" }; +const mapMediaObject = (mediaObject, summitTZ) => { + const deadline = mediaObject.upload_deadline + ? epochToMomentTimeZone(mediaObject.upload_deadline, summitTZ)?.format( + "YYYY/MM/DD" + ) + : "N/A"; + + let status = SPONSOR_MEDIA_UPLOAD_STATUS.COMPLETE; + if (!mediaObject.media_upload) { + if (mediaObject.upload_deadline < moment().unix()) { + status = SPONSOR_MEDIA_UPLOAD_STATUS.DEADLINE_MISSED; + } else if ( + mediaObject.upload_deadline < + moment().add(DEADLINE_ALERT_DAYS, "days").unix() + ) { + status = SPONSOR_MEDIA_UPLOAD_STATUS.DEADLINE_ALERT; + } else { + status = SPONSOR_MEDIA_UPLOAD_STATUS.PENDING; + } + } + + return { + id: mediaObject.id, + name: mediaObject.name, + add_on: mediaObject.add_ons.map((a) => a.name).join(", "), + max_size: `${bytesToMb(mediaObject.max_file_size)} MB`, + format: mediaObject.file_type?.allowed_extensions || "N/A", + media_upload: mediaObject.media_upload, + deadline, + status + }; +}; + const sponsorPageMUListReducer = (state = DEFAULT_STATE, action) => { const { type, payload } = action; @@ -73,21 +112,9 @@ const sponsorPageMUListReducer = (state = DEFAULT_STATE, action) => { last_page: lastPage } = payload.response; - const requests = payload.response.data.map((a) => { - const expiresAt = a.expires_at - ? epochToMomentTimeZone(a.expires_at, state.summitTZ)?.format( - "YYYY/MM/DD" - ) - : "N/A"; - - return { - id: a.id, - code: a.code, - name: a.name, - items_count: a.items_count, - expires_at: expiresAt - }; - }); + const requests = payload.response.data.map((a) => + mapMediaObject(a, state.summitTZ) + ); return { ...state, @@ -122,21 +149,9 @@ const sponsorPageMUListReducer = (state = DEFAULT_STATE, action) => { last_page: lastPage } = payload.response; - const requests = payload.response.data.map((a) => { - const expiresAt = a.expires_at - ? epochToMomentTimeZone(a.expires_at, state.summitTZ)?.format( - "YYYY/MM/DD" - ) - : "N/A"; - - return { - id: a.id, - code: a.code, - name: a.name, - items_count: a.items_count, - expires_at: expiresAt - }; - }); + const requests = payload.response.data.map((a) => + mapMediaObject(a, state.summitTZ) + ); return { ...state, diff --git a/src/utils/constants.js b/src/utils/constants.js index cc6604535..7a7e7e0f4 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -267,6 +267,12 @@ export const SPONSOR_USER_ASSIGNMENT_TYPE = { export const SPONSOR_MEDIA_UPLOAD_STATUS = { PENDING: "PENDING", - DEADLINE: "DEADLINE ALERT", + DEADLINE_ALERT: "DEADLINE ALERT", + DEADLINE_MISSED: "DEADLINE MISSED", COMPLETE: "COMPLETE" }; + +// eslint-disable-next-line no-magic-numbers +export const BYTES_IN_MEGABYTE = 1024 * 1024; + +export const DEADLINE_ALERT_DAYS = 3; diff --git a/src/utils/methods.js b/src/utils/methods.js index a558014ca..a73060818 100644 --- a/src/utils/methods.js +++ b/src/utils/methods.js @@ -22,6 +22,7 @@ import * as Sentry from "@sentry/react"; import T from "i18n-react/dist/i18n-react"; import { BADGE_QR_MINIMUM_EXPECTED_FIELDS, + BYTES_IN_MEGABYTE, ERROR_CODE_401, ERROR_CODE_403, ERROR_CODE_412, @@ -530,3 +531,6 @@ export const formatBadgeQR = (code, summit) => { return null; }; + +// eslint-disable-next-line no-magic-numbers +export const bytesToMb = (bytes) => (bytes / BYTES_IN_MEGABYTE).toFixed(2); From 11afe75e7ec3eeee7a0fb7a4bdd06ae18d984212 Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Wed, 18 Feb 2026 10:33:27 -0300 Subject: [PATCH 5/7] feat: upload media upload file --- .env.example | 2 +- src/actions/sponsor-mu-actions.js | 61 +++++++ src/components/upload-dialog/index.js | 165 ++++++++++++++++++ src/i18n/en.json | 6 +- .../sponsor-media-upload-tab/index.js | 93 ++++++---- .../sponsors/sponsor-page-mu-list-reducer.js | 88 ++++++++-- 6 files changed, 364 insertions(+), 51 deletions(-) create mode 100644 src/components/upload-dialog/index.js diff --git a/.env.example b/.env.example index 3ea8ce1cc..379f79ab8 100644 --- a/.env.example +++ b/.env.example @@ -17,7 +17,7 @@ SPONSOR_USERS_API_SCOPES="show-medata/read show-medata/write access-requests/rea EMAIL_SCOPES="clients/read templates/read templates/write emails/read" FILE_UPLOAD_SCOPES="files/upload" SPONSOR_PAGES_API_URL=https://sponsor-pages-api.dev.fnopen.com -SPONSOR_PAGES_SCOPES="page-template/read page-template/write show-page/read show-page/write media-upload/read" +SPONSOR_USERS_SCOPES="show-medata/read show-medata/write access-requests/read access-requests/write sponsor-users/read sponsor-users/write groups/read groups/write media-upload/write" SCOPES="profile openid offline_access ${SPONSOR_USERS_API_SCOPES} ${PURCHASES_API_SCOPES} ${EMAIL_SCOPES} ${FILE_UPLOAD_SCOPES} ${SPONSOR_PAGES_SCOPES} ${SCOPES_BASE_REALM}/summits/delete-event ${SCOPES_BASE_REALM}/summits/write ${SCOPES_BASE_REALM}/summits/write-event ${SCOPES_BASE_REALM}/summits/read/all ${SCOPES_BASE_REALM}/summits/read ${SCOPES_BASE_REALM}/summits/publish-event ${SCOPES_BASE_REALM}/members/read ${SCOPES_BASE_REALM}/members/read/me ${SCOPES_BASE_REALM}/speakers/write ${SCOPES_BASE_REALM}/attendees/write ${SCOPES_BASE_REALM}/members/write ${SCOPES_BASE_REALM}/organizations/write ${SCOPES_BASE_REALM}/organizations/read ${SCOPES_BASE_REALM}/summits/write-presentation-materials ${SCOPES_BASE_REALM}/summits/registration-orders/update ${SCOPES_BASE_REALM}/summits/registration-orders/delete ${SCOPES_BASE_REALM}/summits/registration-orders/create/offline ${SCOPES_BASE_REALM}/summits/badge-scans/read entity-updates/publish ${SCOPES_BASE_REALM}/audit-logs/read" GOOGLE_API_KEY= ALLOWED_USER_GROUPS="super-admins administrators summit-front-end-administrators summit-room-administrators track-chairs-admins sponsors" diff --git a/src/actions/sponsor-mu-actions.js b/src/actions/sponsor-mu-actions.js index f4f5ed8b6..ed1849708 100644 --- a/src/actions/sponsor-mu-actions.js +++ b/src/actions/sponsor-mu-actions.js @@ -14,6 +14,8 @@ import { createAction, getRequest, + putRequest, + deleteRequest, startLoading, stopLoading } from "openstack-uicore-foundation/lib/utils/actions"; @@ -29,6 +31,10 @@ export const REQUEST_SPONSOR_MEDIA_UPLOADS = "REQUEST_SPONSOR_MEDIA_UPLOADS"; export const RECEIVE_SPONSOR_MEDIA_UPLOADS = "RECEIVE_SPONSOR_MEDIA_UPLOADS"; export const REQUEST_GENERAL_MEDIA_UPLOADS = "REQUEST_GENERAL_MEDIA_UPLOADS"; export const RECEIVE_GENERAL_MEDIA_UPLOADS = "RECEIVE_GENERAL_MEDIA_UPLOADS"; +export const SPONSOR_MEDIA_UPLOAD_FILE_UPLOADED = + "SPONSOR_MEDIA_UPLOAD_FILE_UPLOADED"; +export const SPONSOR_MEDIA_UPLOAD_FILE_DELETED = + "SPONSOR_MEDIA_UPLOAD_FILE_DELETED"; export const getSponsorMURequests = ( @@ -111,3 +117,58 @@ export const getGeneralMURequests = dispatch(stopLoading()); }); }; + +export const uploadFileForSponsorMU = + (pageId, moduleId, fileObj) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const { currentSummit } = currentSummitState; + const { entity: sponsor } = currentSponsorState; + const accessToken = await getAccessTokenSafely(); + + dispatch(startLoading()); + + const params = { + access_token: accessToken + }; + + return putRequest( + null, + createAction("DUMMY_ACTION"), + `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/available-pages/${pageId}/modules/${moduleId}/file`, + fileObj, + snackbarErrorHandler + )(params)(dispatch) + .then(({ response }) => { + dispatch( + createAction(SPONSOR_MEDIA_UPLOAD_FILE_UPLOADED)({ + ...response, + moduleId + }) + ); + }) + .finally(() => { + dispatch(stopLoading()); + }); + }; + +export const removeFileForSponsorMU = + (pageId, moduleId) => async (dispatch, getState) => { + const { currentSummitState, currentSponsorState } = getState(); + const { currentSummit } = currentSummitState; + const { entity: sponsor } = currentSponsorState; + const accessToken = await getAccessTokenSafely(); + + const params = { + access_token: accessToken + }; + + return deleteRequest( + null, + createAction(SPONSOR_MEDIA_UPLOAD_FILE_DELETED)({ moduleId }), + `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/available-pages/${pageId}/modules/${moduleId}/file`, + null, + snackbarErrorHandler + )(params)(dispatch).finally(() => { + dispatch(stopLoading()); + }); + }; diff --git a/src/components/upload-dialog/index.js b/src/components/upload-dialog/index.js new file mode 100644 index 000000000..d5f7e05f5 --- /dev/null +++ b/src/components/upload-dialog/index.js @@ -0,0 +1,165 @@ +import React, { useState } from "react"; +import { + Box, + Button, + Dialog, + DialogContent, + DialogTitle, + Divider, + IconButton, + Typography +} from "@mui/material"; +import PropTypes from "prop-types"; +import UploadInputV2 from "openstack-uicore-foundation/lib/components/inputs/upload-input-v2"; +import T from "i18n-react/dist/i18n-react"; +import CloseIcon from "@mui/icons-material/Close"; +import NoteAddIcon from "@mui/icons-material/NoteAdd"; +import DeleteIcon from "@mui/icons-material/Delete"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import DialogActions from "@mui/material/DialogActions"; + +const MAX_PAGE_MODULE_UPLOAD_QTY = 1; + +const CurrentFile = ({ file, onRemove }) => ( + + + + + + + {file.filename} + + + {file.size} {T.translate("upload_input.complete")} + + + + + + + + + +); + +const UploadDialog = ({ + name, + value, + open, + fileMeta, + maxFiles = MAX_PAGE_MODULE_UPLOAD_QTY, + onClose, + onUpload, + onRemove +}) => { + const [uploadedFile, setUploadedFile] = useState(null); + + const mediaType = { + id: name, + max_size: fileMeta.max_file_size, + max_uploads_qty: maxFiles, + type: { + allowed_extensions: fileMeta?.allowed_extensions?.split(",") || [] + } + }; + + const handleUpload = () => { + onUpload(uploadedFile); + }; + + const handleRemove = () => { + onRemove(); + }; + + const canAddMore = () => (value?.length || 0) < maxFiles; + + const getInputValue = () => + value?.length > 0 + ? value.map((file) => ({ + ...file, + filename: + file.file_name ?? file.filename ?? file.file_path ?? file.file_url + })) + : []; + + return ( + + + {T.translate("edit_sponsor.mu_tab.upload_input.upload_file")} + + ({ + position: "absolute", + right: 8, + top: 8, + color: theme.palette.grey[500] + })} + > + + + + + + {fileMeta.name} + + + {fileMeta.description} + + {value ? ( + <> + + + + ) : ( + setUploadedFile(null)} + postUrl={`${window.FILE_UPLOAD_API_BASE_URL}/api/v1/files/upload`} + djsConfig={{ withCredentials: true }} + maxFiles={maxFiles} + canAdd={canAddMore()} + parallelChunkUploads + /> + )} + + + + + + ); +}; + +UploadDialog.propTypes = { + open: PropTypes.bool.isRequired, + name: PropTypes.string.isRequired, + onClose: PropTypes.func.isRequired +}; + +export default UploadDialog; diff --git a/src/i18n/en.json b/src/i18n/en.json index 06f84425f..b8c8735f4 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -2510,7 +2510,11 @@ "max_size": "Max Size", "format": "Format", "deadline": "Deadline", - "status": "Status" + "status": "Status", + "upload_input": { + "complete": "Complete", + "upload_file": "Upload file" + } }, "placeholders": { "select_sponsorship": "Select a Sponsorship", diff --git a/src/pages/sponsors/sponsor-media-upload-tab/index.js b/src/pages/sponsors/sponsor-media-upload-tab/index.js index 3003237c7..476a8624d 100644 --- a/src/pages/sponsors/sponsor-media-upload-tab/index.js +++ b/src/pages/sponsors/sponsor-media-upload-tab/index.js @@ -11,7 +11,7 @@ * limitations under the License. * */ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; import { Box, Chip, IconButton } from "@mui/material"; @@ -21,18 +21,26 @@ import DeleteIcon from "@mui/icons-material/Delete"; import VisibilityIcon from "@mui/icons-material/Visibility"; import { getGeneralMURequests, - getSponsorMURequests + getSponsorMURequests, + removeFileForSponsorMU, + uploadFileForSponsorMU } from "../../../actions/sponsor-mu-actions"; import CustomAlert from "../../../components/mui/custom-alert"; import MuiTable from "../../../components/mui/table/mui-table"; import { SPONSOR_MEDIA_UPLOAD_STATUS } from "../../../utils/constants"; +import UploadDialog from "../../../components/upload-dialog"; +import showConfirmDialog from "../../../components/mui/showConfirmDialog"; const SponsorMediaUploadTab = ({ sponsorRequests, generalRequests, getSponsorMURequests, - getGeneralMURequests + getGeneralMURequests, + uploadFileForSponsorMU, + removeFileForSponsorMU }) => { + const [uploadModule, setUploadModule] = useState(null); + useEffect(() => { getSponsorMURequests(); getGeneralMURequests(); @@ -48,22 +56,41 @@ const SponsorMediaUploadTab = ({ getSponsorMURequests(currentPage, perPage, key, dir); }; - const handleSponsorDelete = (itemId) => { - console.log("DELETE : ", itemId); + const handleUpload = (item) => { + setUploadModule(item); }; - const handleSponsorView = (item) => { - console.log("VIEW : ", item); + const handleUploadFile = (file) => { + uploadFileForSponsorMU(uploadModule.page_id, uploadModule.id, file).then( + () => { + setUploadModule(null); + } + ); }; - const handleSponsorUpload = (item) => { - console.log("UPLOAD : ", item); + const handleView = (item) => { + console.log("VIEW : ", item); }; - const handleSponsorDownload = (item) => { + const handleDownload = (item) => { console.log("DOWNLOAD : ", item); }; + const handleDelete = async (item) => { + const isConfirmed = await showConfirmDialog({ + title: T.translate("general.are_you_sure"), + text: `${T.translate("general.row_remove_warning")} ${item.name}`, + type: "warning", + showCancelButton: true, + confirmButtonColor: "#DD6B55", + confirmButtonText: T.translate("general.yes_delete") + }); + + if (isConfirmed) { + removeFileForSponsorMU(item.page_id, item.id); + } + }; + const handleGeneralPageChange = (page) => { const { perPage, order, orderDir } = sponsorRequests; getSponsorMURequests(page, perPage, order, orderDir); @@ -74,33 +101,11 @@ const SponsorMediaUploadTab = ({ getSponsorMURequests(currentPage, perPage, key, dir); }; - const handleGeneralDelete = (itemId) => { - console.log("DELETE : ", itemId); - }; - - const handleGeneralView = (item) => { - console.log("VIEW : ", item); - }; - - const handleGeneralUpload = (item) => { - console.log("UPLOAD : ", item); - }; - - const handleGeneralDownload = (item) => { - console.log("DOWNLOAD : ", item); - }; - const getTableColumns = (type) => { const isSponsor = type === "sponsor"; const nameLabel = isSponsor ? T.translate("edit_sponsor.mu_tab.sponsor_request") : T.translate("edit_sponsor.mu_tab.general_request"); - const onView = isSponsor ? handleSponsorView : handleGeneralView; - const onDownload = isSponsor - ? handleSponsorDownload - : handleGeneralDownload; - const onDelete = isSponsor ? handleSponsorDelete : handleGeneralDelete; - const onUpload = isSponsor ? handleSponsorUpload : handleGeneralUpload; const getChipColor = (status) => { switch (status) { @@ -159,7 +164,7 @@ const SponsorMediaUploadTab = ({ onView(row)} + onClick={() => handleView(row)} > @@ -174,7 +179,7 @@ const SponsorMediaUploadTab = ({ onDownload(row)} + onClick={() => handleDownload(row)} > @@ -188,13 +193,13 @@ const SponsorMediaUploadTab = ({ render: (row) => { if (row.media_upload) { return ( - onDelete(row.id)}> + handleDelete(row)}> ); } return ( - onUpload(row)}> + handleUpload(row)}> ); @@ -249,6 +254,18 @@ const SponsorMediaUploadTab = ({ onSort={handleGeneralSort} />
+ setUploadModule(null)} + onUpload={handleUploadFile} + value={uploadModule?.media_upload} + fileMeta={{ + ...(uploadModule?.file_type || {}), + max_file_size: uploadModule?.max_file_size + }} + maxFiles={1} + /> ); }; @@ -259,5 +276,7 @@ const mapStateToProps = ({ sponsorPageMUListState }) => ({ export default connect(mapStateToProps, { getSponsorMURequests, - getGeneralMURequests + getGeneralMURequests, + uploadFileForSponsorMU, + removeFileForSponsorMU })(SponsorMediaUploadTab); diff --git a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js index 164760d60..1c61ba840 100644 --- a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js @@ -19,7 +19,9 @@ import { RECEIVE_GENERAL_MEDIA_UPLOADS, RECEIVE_SPONSOR_MEDIA_UPLOADS, REQUEST_GENERAL_MEDIA_UPLOADS, - REQUEST_SPONSOR_MEDIA_UPLOADS + REQUEST_SPONSOR_MEDIA_UPLOADS, + SPONSOR_MEDIA_UPLOAD_FILE_DELETED, + SPONSOR_MEDIA_UPLOAD_FILE_UPLOADED } from "../../actions/sponsor-mu-actions"; import { bytesToMb } from "../../utils/methods"; import { @@ -49,13 +51,7 @@ const DEFAULT_STATE = { summitTZ: "" }; -const mapMediaObject = (mediaObject, summitTZ) => { - const deadline = mediaObject.upload_deadline - ? epochToMomentTimeZone(mediaObject.upload_deadline, summitTZ)?.format( - "YYYY/MM/DD" - ) - : "N/A"; - +const getStatus = (mediaObject) => { let status = SPONSOR_MEDIA_UPLOAD_STATUS.COMPLETE; if (!mediaObject.media_upload) { if (mediaObject.upload_deadline < moment().unix()) { @@ -70,15 +66,23 @@ const mapMediaObject = (mediaObject, summitTZ) => { } } + return status; +}; + +const mapMediaObject = (mediaObject, summitTZ) => { + const deadline = mediaObject.upload_deadline + ? epochToMomentTimeZone(mediaObject.upload_deadline, summitTZ)?.format( + "YYYY/MM/DD" + ) + : "N/A"; + return { - id: mediaObject.id, - name: mediaObject.name, + ...mediaObject, add_on: mediaObject.add_ons.map((a) => a.name).join(", "), max_size: `${bytesToMb(mediaObject.max_file_size)} MB`, format: mediaObject.file_type?.allowed_extensions || "N/A", - media_upload: mediaObject.media_upload, deadline, - status + status: getStatus(mediaObject) }; }; @@ -164,6 +168,66 @@ const sponsorPageMUListReducer = (state = DEFAULT_STATE, action) => { } }; } + case SPONSOR_MEDIA_UPLOAD_FILE_UPLOADED: { + const { moduleId, ...file } = payload; + return { + ...state, + sponsorRequests: { + ...state.sponsorRequests, + requests: state.sponsorRequests.requests.map((r) => + r.id === moduleId + ? { + ...r, + media_upload: file, + status: getStatus({ ...r, media_upload: true }) + } + : r + ) + }, + generalRequests: { + ...state.generalRequests, + requests: state.generalRequests.requests.map((r) => + r.id === moduleId + ? { + ...r, + media_upload: file, + status: getStatus({ ...r, media_upload: true }) + } + : r + ) + } + }; + } + case SPONSOR_MEDIA_UPLOAD_FILE_DELETED: { + const { moduleId } = payload; + return { + ...state, + sponsorRequests: { + ...state.sponsorRequests, + requests: state.sponsorRequests.requests.map((r) => + r.id === moduleId + ? { + ...r, + media_upload: null, + status: getStatus({ ...r, media_upload: false }) + } + : r + ) + }, + generalRequests: { + ...state.generalRequests, + requests: state.generalRequests.requests.map((r) => + r.id === moduleId + ? { + ...r, + media_upload: null, + status: getStatus({ ...r, media_upload: false }) + } + : r + ) + } + }; + } default: return state; } From 0897cc3907de3b4521b4b4d6f8bead8f92ea0dad Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Fri, 27 Feb 2026 11:20:46 -0300 Subject: [PATCH 6/7] fix: pr feedback --- .env.example | 10 +++++----- src/actions/sponsor-mu-actions.js | 6 ++++-- src/components/upload-dialog/index.js | 4 +++- src/pages/sponsors/sponsor-media-upload-tab/index.js | 5 +++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index 379f79ab8..d1a92df06 100644 --- a/.env.example +++ b/.env.example @@ -12,13 +12,15 @@ PRINT_APP_URL=https://badge-print-app.dev.fnopen.com PUB_API_BASE_URL= OS_BASE_URL= SCOPES_BASE_REALM=${API_BASE_URL} +PURCHASES_API_URL=https://purchases-api.dev.fnopen.com PURCHASES_API_SCOPES="purchases-show-medata/read purchases-show-medata/write show-form/read show-form/write customized-form/write customized-form/read carts/read carts/write purchases/read" -SPONSOR_USERS_API_SCOPES="show-medata/read show-medata/write access-requests/read access-requests/write sponsor-users/read sponsor-users/write groups/read groups/write" +SPONSOR_USERS_API_URL=https://sponsor-users-api.dev.fnopen.com +SPONSOR_USERS_SCOPES="show-medata/read show-medata/write access-requests/read access-requests/write sponsor-users/read sponsor-users/write groups/read groups/write media-upload/write" EMAIL_SCOPES="clients/read templates/read templates/write emails/read" FILE_UPLOAD_SCOPES="files/upload" SPONSOR_PAGES_API_URL=https://sponsor-pages-api.dev.fnopen.com -SPONSOR_USERS_SCOPES="show-medata/read show-medata/write access-requests/read access-requests/write sponsor-users/read sponsor-users/write groups/read groups/write media-upload/write" -SCOPES="profile openid offline_access ${SPONSOR_USERS_API_SCOPES} ${PURCHASES_API_SCOPES} ${EMAIL_SCOPES} ${FILE_UPLOAD_SCOPES} ${SPONSOR_PAGES_SCOPES} ${SCOPES_BASE_REALM}/summits/delete-event ${SCOPES_BASE_REALM}/summits/write ${SCOPES_BASE_REALM}/summits/write-event ${SCOPES_BASE_REALM}/summits/read/all ${SCOPES_BASE_REALM}/summits/read ${SCOPES_BASE_REALM}/summits/publish-event ${SCOPES_BASE_REALM}/members/read ${SCOPES_BASE_REALM}/members/read/me ${SCOPES_BASE_REALM}/speakers/write ${SCOPES_BASE_REALM}/attendees/write ${SCOPES_BASE_REALM}/members/write ${SCOPES_BASE_REALM}/organizations/write ${SCOPES_BASE_REALM}/organizations/read ${SCOPES_BASE_REALM}/summits/write-presentation-materials ${SCOPES_BASE_REALM}/summits/registration-orders/update ${SCOPES_BASE_REALM}/summits/registration-orders/delete ${SCOPES_BASE_REALM}/summits/registration-orders/create/offline ${SCOPES_BASE_REALM}/summits/badge-scans/read entity-updates/publish ${SCOPES_BASE_REALM}/audit-logs/read" +SPONSOR_PAGES_SCOPES="page-template/read page-template/write show-page/read show-page/write media-upload/read" +SCOPES="profile openid offline_access reports/all ${EMAIL_SCOPES} ${INVENTORY_API_SCOPES} ${FILE_UPLOAD_SCOPES} ${PURCHASES_API_SCOPES} ${SPONSOR_USERS_SCOPES} ${SPONSOR_PAGES_SCOPES} ${SCOPES_BASE_REALM}/summits/delete-event ${SCOPES_BASE_REALM}/companies/read ${SCOPES_BASE_REALM}/companies/write ${SCOPES_BASE_REALM}/summits/write ${SCOPES_BASE_REALM}/summits/write-event ${SCOPES_BASE_REALM}/summits/read/all ${SCOPES_BASE_REALM}/summits/read ${SCOPES_BASE_REALM}/summits/publish-event ${SCOPES_BASE_REALM}/members/read ${SCOPES_BASE_REALM}/members/read/me ${SCOPES_BASE_REALM}/speakers/write ${SCOPES_BASE_REALM}/attendees/write ${SCOPES_BASE_REALM}/members/write ${SCOPES_BASE_REALM}/organizations/write ${SCOPES_BASE_REALM}/organizations/read ${SCOPES_BASE_REALM}/summits/write-presentation-materials ${SCOPES_BASE_REALM}/summits/registration-orders/update ${SCOPES_BASE_REALM}/summits/registration-orders/delete ${SCOPES_BASE_REALM}/summits/registration-orders/create/offline ${SCOPES_BASE_REALM}/summits/badge-scans/read config-values/write ${SCOPES_BASE_REALM}/summit-administrator-groups/read ${SCOPES_BASE_REALM}/summit-administrator-groups/write ${SCOPES_BASE_REALM}/summit-media-file-types/read ${SCOPES_BASE_REALM}/summit-media-file-types/write user-roles/write entity-updates/publish ${SCOPES_BASE_REALM}/audit-logs/read filter-criteria/read filter-criteria/write" GOOGLE_API_KEY= ALLOWED_USER_GROUPS="super-admins administrators summit-front-end-administrators summit-room-administrators track-chairs-admins sponsors" APP_CLIENT_NAME="openstack" @@ -35,7 +37,5 @@ SENTRY_PROJECT= SENTRY_TRACE_SAMPLE_RATE= SENTRY_TRACE_PROPAGATION_TARGETS= CFP_APP_BASE_URL= -PURCHASES_API_URL= -SPONSOR_USERS_API_URL= S3_MEDIA_UPLOADS_ENDPOINT_URL=https://fntech.sfo2.digitaloceanspaces.com S3_MEDIA_UPLOADS_BUCKET_NAME=PresentationMediaUploads_DEV \ No newline at end of file diff --git a/src/actions/sponsor-mu-actions.js b/src/actions/sponsor-mu-actions.js index ed1849708..c3f384131 100644 --- a/src/actions/sponsor-mu-actions.js +++ b/src/actions/sponsor-mu-actions.js @@ -72,7 +72,7 @@ export const getSponsorMURequests = `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/custom-media-request-modules`, snackbarErrorHandler, { order, orderDir, currentPage, perPage, summitTZ } - )(params)(dispatch).then(() => { + )(params)(dispatch).finally(() => { dispatch(stopLoading()); }); }; @@ -113,7 +113,7 @@ export const getGeneralMURequests = `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/managed-media-request-modules`, snackbarErrorHandler, { order, orderDir, currentPage, perPage, summitTZ } - )(params)(dispatch).then(() => { + )(params)(dispatch).finally(() => { dispatch(stopLoading()); }); }; @@ -158,6 +158,8 @@ export const removeFileForSponsorMU = const { entity: sponsor } = currentSponsorState; const accessToken = await getAccessTokenSafely(); + dispatch(startLoading()); + const params = { access_token: accessToken }; diff --git a/src/components/upload-dialog/index.js b/src/components/upload-dialog/index.js index d5f7e05f5..7b65438a1 100644 --- a/src/components/upload-dialog/index.js +++ b/src/components/upload-dialog/index.js @@ -82,7 +82,9 @@ const UploadDialog = ({ }; const handleRemove = () => { - onRemove(); + if (onRemove) { + onRemove(); + } }; const canAddMore = () => (value?.length || 0) < maxFiles; diff --git a/src/pages/sponsors/sponsor-media-upload-tab/index.js b/src/pages/sponsors/sponsor-media-upload-tab/index.js index 476a8624d..83f9f17e9 100644 --- a/src/pages/sponsors/sponsor-media-upload-tab/index.js +++ b/src/pages/sponsors/sponsor-media-upload-tab/index.js @@ -93,12 +93,12 @@ const SponsorMediaUploadTab = ({ const handleGeneralPageChange = (page) => { const { perPage, order, orderDir } = sponsorRequests; - getSponsorMURequests(page, perPage, order, orderDir); + getGeneralMURequests(page, perPage, order, orderDir); }; const handleGeneralSort = (key, dir) => { const { currentPage, perPage } = sponsorRequests; - getSponsorMURequests(currentPage, perPage, key, dir); + getGeneralMURequests(currentPage, perPage, key, dir); }; const getTableColumns = (type) => { @@ -259,6 +259,7 @@ const SponsorMediaUploadTab = ({ open={!!uploadModule} onClose={() => setUploadModule(null)} onUpload={handleUploadFile} + onRemove={() => handleDelete(uploadModule)} value={uploadModule?.media_upload} fileMeta={{ ...(uploadModule?.file_type || {}), From 6feff0e2170e0ca23ac09636fc6e2006566ad02e Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Fri, 27 Feb 2026 12:11:58 -0300 Subject: [PATCH 7/7] fix: update headers --- src/actions/sponsor-mu-actions.js | 2 +- src/components/upload-dialog/index.js | 13 +++++++++++++ .../sponsors/sponsor-media-upload-tab/index.js | 2 +- .../sponsors/sponsor-page-mu-list-reducer.js | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/actions/sponsor-mu-actions.js b/src/actions/sponsor-mu-actions.js index c3f384131..6ce634e3c 100644 --- a/src/actions/sponsor-mu-actions.js +++ b/src/actions/sponsor-mu-actions.js @@ -1,5 +1,5 @@ /** - * Copyright 2018 OpenStack Foundation + * Copyright 2026 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 diff --git a/src/components/upload-dialog/index.js b/src/components/upload-dialog/index.js index 7b65438a1..858020e97 100644 --- a/src/components/upload-dialog/index.js +++ b/src/components/upload-dialog/index.js @@ -1,3 +1,16 @@ +/** + * Copyright 2026 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, { useState } from "react"; import { Box, diff --git a/src/pages/sponsors/sponsor-media-upload-tab/index.js b/src/pages/sponsors/sponsor-media-upload-tab/index.js index 83f9f17e9..85ff51b1d 100644 --- a/src/pages/sponsors/sponsor-media-upload-tab/index.js +++ b/src/pages/sponsors/sponsor-media-upload-tab/index.js @@ -1,5 +1,5 @@ /** - * Copyright 2024 OpenStack Foundation + * Copyright 2026 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 diff --git a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js index 1c61ba840..aebe47fbf 100644 --- a/src/reducers/sponsors/sponsor-page-mu-list-reducer.js +++ b/src/reducers/sponsors/sponsor-page-mu-list-reducer.js @@ -1,5 +1,5 @@ /** - * Copyright 2019 OpenStack Foundation + * Copyright 2026 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