diff --git a/.env.example b/.env.example
index e3779db3f..70b5f46f5 100644
--- a/.env.example
+++ b/.env.example
@@ -12,7 +12,7 @@ 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_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_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"
EMAIL_SCOPES="clients/read templates/read templates/write emails/read"
FILE_UPLOAD_SCOPES="files/upload"
diff --git a/src/actions/sponsor-purchases-actions.js b/src/actions/sponsor-purchases-actions.js
new file mode 100644
index 000000000..41af07774
--- /dev/null
+++ b/src/actions/sponsor-purchases-actions.js
@@ -0,0 +1,81 @@
+/**
+ * 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 {
+ authErrorHandler,
+ createAction,
+ getRequest,
+ startLoading,
+ stopLoading
+} from "openstack-uicore-foundation/lib/utils/actions";
+
+import { escapeFilterValue, getAccessTokenSafely } from "../utils/methods";
+import {
+ DEFAULT_CURRENT_PAGE,
+ DEFAULT_ORDER_DIR,
+ DEFAULT_PER_PAGE
+} from "../utils/constants";
+
+export const REQUEST_SPONSOR_PURCHASES = "REQUEST_SPONSOR_PURCHASES";
+export const RECEIVE_SPONSOR_PURCHASES = "RECEIVE_SPONSOR_PURCHASES";
+
+export const getSponsorPurchases =
+ (
+ term = "",
+ page = DEFAULT_CURRENT_PAGE,
+ perPage = DEFAULT_PER_PAGE,
+ order = "id",
+ orderDir = DEFAULT_ORDER_DIR
+ ) =>
+ async (dispatch, getState) => {
+ const { currentSummitState, currentSponsorState } = getState();
+ const { currentSummit } = currentSummitState;
+ const { entity: sponsor } = currentSponsorState;
+ const accessToken = await getAccessTokenSafely();
+ const filter = [];
+
+ dispatch(startLoading());
+
+ if (term) {
+ const escapedTerm = escapeFilterValue(term);
+ filter.push(
+ `number==${escapedTerm},purchased_by_email=@${escapedTerm},purchased_by_full_name=@${escapedTerm}`
+ );
+ }
+
+ const params = {
+ page,
+ per_page: perPage,
+ access_token: accessToken
+ };
+
+ if (filter.length > 0) {
+ params["filter[]"] = filter;
+ }
+
+ // order
+ if (order != null && orderDir != null) {
+ const orderDirSign = orderDir === 1 ? "" : "-";
+ params.order = `${orderDirSign}${order}`;
+ }
+
+ return getRequest(
+ createAction(REQUEST_SPONSOR_PURCHASES),
+ createAction(RECEIVE_SPONSOR_PURCHASES),
+ `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/purchases`,
+ authErrorHandler,
+ { order, orderDir, page, perPage, term }
+ )(params)(dispatch).then(() => {
+ dispatch(stopLoading());
+ });
+ };
diff --git a/src/i18n/en.json b/src/i18n/en.json
index e6ca66683..6b08980ac 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -2470,6 +2470,15 @@
"pay_cc": "pay with credit card or ach",
"pay_invoice": "pay with invoice"
},
+ "purchase_tab": {
+ "order": "Order",
+ "purchased": "Purchased",
+ "payment_method": "Payment Method",
+ "status": "Status",
+ "amount": "Amount",
+ "details": "Details",
+ "purchases": "purchases"
+ },
"placeholders": {
"select_sponsorship": "Select a Sponsorship",
"sponsorship_type": "Start typing to choose a Tier...",
diff --git a/src/pages/sponsors/edit-sponsor-page.js b/src/pages/sponsors/edit-sponsor-page.js
index 3a4e9a709..effffd9f5 100644
--- a/src/pages/sponsors/edit-sponsor-page.js
+++ b/src/pages/sponsors/edit-sponsor-page.js
@@ -45,6 +45,7 @@ import SponsorBadgeScans from "./sponsor-badge-scans";
import SponsorCartTab from "./sponsor-cart-tab";
import SponsorFormsManageItems from "./sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items";
import { SPONSOR_TABS } from "../../utils/constants";
+import SponsorPurchasesTab from "./sponsor-purchases-tab";
const CustomTabPanel = (props) => {
const { children, value, index, ...other } = props;
@@ -225,6 +226,9 @@ const EditSponsorPage = (props) => {
+
+
+
diff --git a/src/pages/sponsors/sponsor-purchases-tab/index.js b/src/pages/sponsors/sponsor-purchases-tab/index.js
new file mode 100644
index 000000000..e3cbc28df
--- /dev/null
+++ b/src/pages/sponsors/sponsor-purchases-tab/index.js
@@ -0,0 +1,204 @@
+/**
+ * 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,
+ Button,
+ Grid2,
+ IconButton,
+ MenuItem,
+ Select
+} from "@mui/material";
+import MenuIcon from "@mui/icons-material/Menu";
+import { getSponsorPurchases } from "../../../actions/sponsor-purchases-actions";
+import SearchInput from "../../../components/mui/search-input";
+import MuiTable from "../../../components/mui/table/mui-table";
+import {
+ DEFAULT_CURRENT_PAGE,
+ PURCHASE_STATUS
+} from "../../../utils/constants";
+
+const SponsorPurchasesTab = ({
+ purchases,
+ term,
+ order,
+ orderDir,
+ currentPage,
+ perPage,
+ totalCount,
+ getSponsorPurchases
+}) => {
+ useEffect(() => {
+ getSponsorPurchases();
+ }, []);
+
+ const handlePageChange = (page) => {
+ getSponsorPurchases(term, page, perPage, order, orderDir);
+ };
+
+ const handleSort = (key, dir) => {
+ getSponsorPurchases(term, currentPage, perPage, key, dir);
+ };
+
+ const handlePerPageChange = (newPerPage) => {
+ getSponsorPurchases(
+ term,
+ DEFAULT_CURRENT_PAGE,
+ newPerPage,
+ order,
+ orderDir
+ );
+ };
+
+ const handleSearch = (searchTerm) => {
+ getSponsorPurchases(searchTerm);
+ };
+
+ const handleDetails = (item) => {
+ console.log("DETAILS : ", item);
+ };
+
+ const handleMenu = (item) => {
+ console.log("MENU : ", item);
+ };
+
+ const handleStatusChange = (stat) => {
+ console.log("STATUS : ", stat);
+ };
+
+ const tableColumns = [
+ {
+ columnKey: "number",
+ header: T.translate("edit_sponsor.purchase_tab.order"),
+ sortable: true
+ },
+ {
+ columnKey: "purchased",
+ header: T.translate("edit_sponsor.purchase_tab.purchased"),
+ sortable: true
+ },
+ {
+ columnKey: "payment_method",
+ header: T.translate("edit_sponsor.purchase_tab.payment_method"),
+ sortable: true
+ },
+ {
+ columnKey: "status",
+ header: T.translate("edit_sponsor.purchase_tab.status"),
+ sortable: true,
+ render: (row) => {
+ if (row.status === PURCHASE_STATUS.PENDING) {
+ return (
+
+ );
+ }
+
+ return row.status;
+ }
+ },
+ {
+ columnKey: "amount",
+ header: T.translate("edit_sponsor.purchase_tab.amount"),
+ sortable: true
+ },
+ {
+ columnKey: "details",
+ header: "",
+ width: 100,
+ align: "center",
+ render: (row) => (
+
+ )
+ },
+ {
+ columnKey: "menu",
+ header: "",
+ width: 100,
+ align: "center",
+ render: (row) => (
+ handleMenu(row)}>
+
+
+ )
+ }
+ ];
+
+ return (
+
+
+
+
+ {totalCount} {T.translate("edit_sponsor.purchase_tab.purchases")}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const mapStateToProps = ({ sponsorPagePurchaseListState }) => ({
+ ...sponsorPagePurchaseListState
+});
+
+export default connect(mapStateToProps, {
+ getSponsorPurchases
+})(SponsorPurchasesTab);
diff --git a/src/reducers/sponsors/sponsor-page-purchase-list-reducer.js b/src/reducers/sponsors/sponsor-page-purchase-list-reducer.js
new file mode 100644
index 000000000..e962508b1
--- /dev/null
+++ b/src/reducers/sponsors/sponsor-page-purchase-list-reducer.js
@@ -0,0 +1,85 @@
+/**
+ * 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 moment from "moment-timezone";
+import { amountFromCents } from "openstack-uicore-foundation/lib/utils/money";
+import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions";
+import {
+ REQUEST_SPONSOR_PURCHASES,
+ RECEIVE_SPONSOR_PURCHASES
+} from "../../actions/sponsor-purchases-actions";
+import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions";
+import { MILLISECONDS_TO_SECONDS } from "../../utils/constants";
+
+const DEFAULT_STATE = {
+ purchases: [],
+ order: "order",
+ orderDir: 1,
+ currentPage: 1,
+ lastPage: 1,
+ perPage: 10,
+ totalCount: 0,
+ term: ""
+};
+
+const sponsorPagePurchaseListReducer = (state = DEFAULT_STATE, action) => {
+ const { type, payload } = action;
+
+ switch (type) {
+ case SET_CURRENT_SUMMIT:
+ case LOGOUT_USER: {
+ return DEFAULT_STATE;
+ }
+ case REQUEST_SPONSOR_PURCHASES: {
+ const { order, orderDir, page, perPage, term } = payload;
+
+ return {
+ ...state,
+ order,
+ orderDir,
+ forms: [],
+ currentPage: page,
+ perPage,
+ term
+ };
+ }
+ case RECEIVE_SPONSOR_PURCHASES: {
+ const {
+ current_page: currentPage,
+ total,
+ last_page: lastPage
+ } = payload.response;
+
+ const purchases = payload.response.data.map((a) => ({
+ ...a,
+ order: a.order_number,
+ amount: `$${amountFromCents(a.raw_amount)}`,
+ purchased: moment(a.created * MILLISECONDS_TO_SECONDS).format(
+ "YYYY/MM/DD HH:mm a"
+ )
+ }));
+
+ return {
+ ...state,
+ purchases,
+ currentPage,
+ totalCount: total,
+ lastPage
+ };
+ }
+ default:
+ return state;
+ }
+};
+
+export default sponsorPagePurchaseListReducer;
diff --git a/src/store.js b/src/store.js
index b3289558e..d994d7617 100644
--- a/src/store.js
+++ b/src/store.js
@@ -167,6 +167,7 @@ import sponsorPageFormsListReducer from "./reducers/sponsors/sponsor-page-forms-
import sponsorCustomizedFormReducer from "./reducers/sponsors/sponsor-customized-form-reducer.js";
import sponsorPageCartListReducer from "./reducers/sponsors/sponsor-page-cart-list-reducer";
import sponsorCustomizedFormItemsListReducer from "./reducers/sponsors/sponsor-customized-form-items-list-reducer.js";
+import sponsorPagePurchaseListReducer from "./reducers/sponsors/sponsor-page-purchase-list-reducer.js";
// default: localStorage if web, AsyncStorage if react-native
@@ -255,6 +256,7 @@ const reducers = persistCombineReducers(config, {
sponsorPageCartListState: sponsorPageCartListReducer,
sponsorCustomizedFormState: sponsorCustomizedFormReducer,
sponsorCustomizedFormItemsListState: sponsorCustomizedFormItemsListReducer,
+ sponsorPagePurchaseListState: sponsorPagePurchaseListReducer,
currentSponsorPromocodeListState: sponsorPromocodeListReducer,
currentSponsorExtraQuestionState: sponsorExtraQuestionReducer,
currentSponsorAdvertisementState: sponsorAdvertisementReducer,
@@ -305,7 +307,7 @@ const reducers = persistCombineReducers(config, {
sponsoredProjectState: sponsoredProjectReducer,
sponsoredProjectSponsorshipTypeState: sponsoredProjectSponsorshipTypeReducer,
sponsoredProjectSponsorshipTypeSupportingCompanyState:
- sponsoredProjectSponsorshipTypeSupportingCompanyReducer,
+ sponsoredProjectSponsorshipTypeSupportingCompanyReducer,
scheduleSettingsState: scheduleSettingsReducer,
scheduleSettingsListState: scheduleSettingsListReducer,
currentSelectionPlanExtraQuestionState: selectionPlanExtraQuestionReducer,
diff --git a/src/utils/constants.js b/src/utils/constants.js
index 730ea805d..36d281fb0 100644
--- a/src/utils/constants.js
+++ b/src/utils/constants.js
@@ -240,3 +240,9 @@ export const fieldTypesWithOptions = [
"ComboBox",
"RadioButtonList"
];
+
+export const PURCHASE_STATUS = {
+ PENDING: "Pending",
+ PAID: "Paid",
+ CANCELLED: "Cancelled"
+};