diff --git a/src/actions/sponsor-purchases-actions.js b/src/actions/sponsor-purchases-actions.js
index 2b38f833d..6e9ca2d7a 100644
--- a/src/actions/sponsor-purchases-actions.js
+++ b/src/actions/sponsor-purchases-actions.js
@@ -19,7 +19,8 @@ import {
postRequest,
putRequest,
startLoading,
- stopLoading
+ stopLoading,
+ getCSV
} from "openstack-uicore-foundation/lib/utils/actions";
import T from "i18n-react/dist/i18n-react";
import { escapeFilterValue, getAccessTokenSafely } from "../utils/methods";
@@ -31,6 +32,8 @@ import {
} from "../utils/constants";
import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions";
+export const REQUEST_ALL_SPONSOR_PURCHASES = "REQUEST_ALL_SPONSOR_PURCHASES";
+export const RECEIVE_ALL_SPONSOR_PURCHASES = "RECEIVE_ALL_SPONSOR_PURCHASES";
export const REQUEST_SPONSOR_PURCHASES = "REQUEST_SPONSOR_PURCHASES";
export const RECEIVE_SPONSOR_PURCHASES = "RECEIVE_SPONSOR_PURCHASES";
export const SPONSOR_PURCHASE_STATUS_UPDATED =
@@ -40,6 +43,127 @@ export const CLEAR_SPONSOR_ORDER = "CLEAR_SPONSOR_ORDER";
export const SPONSOR_CLIENT_ADDRESS_UPDATED = "SPONSOR_CLIENT_ADDRESS_UPDATED";
export const SPONSOR_CLIENT_UPDATED = "SPONSOR_CLIENT_UPDATED";
+export const getAllSponsorPurchases =
+ (
+ term = "",
+ page = DEFAULT_CURRENT_PAGE,
+ perPage = DEFAULT_PER_PAGE,
+ order = "created",
+ orderDir = -1
+ ) =>
+ async (dispatch, getState) => {
+ const { currentSummitState } = getState();
+ const { currentSummit } = currentSummitState;
+ const accessToken = await getAccessTokenSafely();
+ const filter = [];
+
+ dispatch(startLoading());
+
+ if (term) {
+ const escapedTerm = escapeFilterValue(term);
+ filter.push(
+ `number==${escapedTerm},company_name=@${escapedTerm},purchased_by_email=@${escapedTerm},purchased_by_full_name=@${escapedTerm}`
+ );
+ }
+
+ const params = {
+ page,
+ per_page: perPage,
+ access_token: accessToken,
+ expand: "sponsor",
+ relations: "sponsor",
+ fields:
+ "number,payment_id,purchased_date,sponsor.id,sponsor.company_name,payment_method,status,net_amount"
+ };
+
+ if (filter.length > 0) {
+ params["filter[]"] = filter;
+ }
+
+ // order
+ if (order != null && orderDir != null) {
+ const orderDirSign = orderDir === 1 ? "" : "-";
+ switch (order) {
+ case "purchased":
+ params.order = `${orderDirSign}created`;
+ break;
+ case "amount":
+ params.order = `${orderDirSign}net_amount`;
+ break;
+ case "sponsor_name":
+ params.order = `${orderDirSign}sponsor_company_name`;
+ break;
+ default:
+ params.order = `${orderDirSign}${order}`;
+ }
+ }
+
+ return getRequest(
+ createAction(REQUEST_ALL_SPONSOR_PURCHASES),
+ createAction(RECEIVE_ALL_SPONSOR_PURCHASES),
+ `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/purchases`,
+ authErrorHandler,
+ { order, orderDir, page, perPage, term }
+ )(params)(dispatch).finally(() => {
+ dispatch(stopLoading());
+ });
+ };
+
+export const exportAllSponsorPurchases =
+ (term = null, order, orderDir) =>
+ async (dispatch, getState) => {
+ const { currentSummitState } = getState();
+ const accessToken = await getAccessTokenSafely();
+ const { currentSummit } = currentSummitState;
+ const filter = [];
+ const filename = `${currentSummit.id}-purchases.csv`;
+
+ if (term) {
+ const escapedTerm = escapeFilterValue(term);
+ filter.push(
+ `number==${escapedTerm},company_name=@${escapedTerm},purchased_by_email=@${escapedTerm},purchased_by_full_name=@${escapedTerm}`
+ );
+ }
+
+ const params = {
+ access_token: accessToken,
+ expand: "sponsor",
+ relations: "sponsor",
+ fields:
+ "number,payment_id,purchased_date,sponsor.id,sponsor.company_name,payment_method,status,net_amount"
+ };
+
+ if (filter.length > 0) {
+ params["filter[]"] = filter;
+ }
+
+ // order
+ if (order != null && orderDir != null) {
+ const orderDirSign = orderDir === 1 ? "" : "-";
+ switch (order) {
+ case "purchased":
+ params.order = `${orderDirSign}created`;
+ break;
+ case "amount":
+ params.order = `${orderDirSign}net_amount`;
+ break;
+ case "sponsor_name":
+ params.order = `${orderDirSign}sponsor_company_name`;
+ break;
+ default:
+ params.order = `${orderDirSign}${order}`;
+ }
+ }
+
+ dispatch(
+ getCSV(
+ `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/purchases/csv`,
+ params,
+ filename
+ )
+ );
+ };
+
export const getSponsorPurchases =
(
term = "",
@@ -82,7 +206,7 @@ export const getSponsorPurchases =
params.order = `${orderDirSign}created`;
break;
case "amount":
- params.order = `${orderDirSign}raw_amount`;
+ params.order = `${orderDirSign}net_amount`;
break;
default:
params.order = `${orderDirSign}${order}`;
@@ -101,10 +225,9 @@ export const getSponsorPurchases =
};
export const approveSponsorPurchase =
- (paymentId) => async (dispatch, getState) => {
- const { currentSummitState, currentSponsorState } = getState();
+ (sponsorId, paymentId) => async (dispatch, getState) => {
+ const { currentSummitState } = getState();
const { currentSummit } = currentSummitState;
- const { entity: sponsor } = currentSponsorState;
const accessToken = await getAccessTokenSafely();
const params = {
@@ -119,7 +242,7 @@ export const approveSponsorPurchase =
paymentId,
status: PURCHASE_STATUS.PAID
}),
- `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/payments/${paymentId}/approve`,
+ `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/payments/${paymentId}/approve`,
{},
snackbarErrorHandler
)(params)(dispatch)
@@ -138,10 +261,9 @@ export const approveSponsorPurchase =
};
export const rejectSponsorPurchase =
- (paymentId) => async (dispatch, getState) => {
- const { currentSummitState, currentSponsorState } = getState();
+ (sponsorId, paymentId) => async (dispatch, getState) => {
+ const { currentSummitState } = getState();
const { currentSummit } = currentSummitState;
- const { entity: sponsor } = currentSponsorState;
const accessToken = await getAccessTokenSafely();
const params = {
@@ -156,7 +278,7 @@ export const rejectSponsorPurchase =
paymentId,
status: PURCHASE_STATUS.CANCELLED
}),
- `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/payments/${paymentId}/cancel`,
+ `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/payments/${paymentId}/cancel`,
null,
snackbarErrorHandler
)(params)(dispatch)
diff --git a/src/components/menu/menu-definition.js b/src/components/menu/menu-definition.js
index d853d6d58..0baaa4985 100644
--- a/src/components/menu/menu-definition.js
+++ b/src/components/menu/menu-definition.js
@@ -218,6 +218,11 @@ export const getSummitItems = (summitId) => [
linkUrl: `summits/${summitId}/sponsors/pages`,
accessRoute: "admin-sponsors"
},
+ {
+ name: "sponsor_purchases",
+ linkUrl: `summits/${summitId}/sponsors/purchases`,
+ accessRoute: "admin-sponsors"
+ },
{
name: "sponsorship_list",
linkUrl: `summits/${summitId}/sponsorships`,
diff --git a/src/i18n/en.json b/src/i18n/en.json
index f5a408eb3..ff25400a5 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -90,6 +90,7 @@
"to": "To",
"from": "From",
"placeholders": {
+ "search": "Search...",
"search_speakers": "Search Speakers by Name, Email, Speaker Id or Member Id",
"select_acceptance_criteria": "Select acceptance criteria",
"select_invitation_status": "Select status"
@@ -186,6 +187,7 @@
"sponsor_list": "Sponsor List",
"sponsor_forms": "Forms",
"sponsor_pages": "Pages",
+ "sponsor_purchases": "Purchases",
"sponsorship_list": "Tiers",
"sponsor_users": "Users",
"sponsors_promocodes": "Promo Codes",
@@ -2866,6 +2868,16 @@
}
}
},
+ "sponsor_show_purchases": {
+ "order": "Order",
+ "purchased": "Purchased",
+ "sponsor": "Sponsor",
+ "payment_method": "Payment Method",
+ "status": "Status",
+ "amount": "Amount",
+ "details": "Details",
+ "purchases": "Purchases"
+ },
"sponsor_users": {
"users": "Users",
"access_request": "access request",
diff --git a/src/layouts/sponsor-layout.js b/src/layouts/sponsor-layout.js
index cba7da2e6..68dd29cd6 100644
--- a/src/layouts/sponsor-layout.js
+++ b/src/layouts/sponsor-layout.js
@@ -44,6 +44,9 @@ const SponsorUsersListPage = React.lazy(() =>
const ShowPagesListPage = React.lazy(() =>
import("../pages/sponsors/show-pages-list-page")
);
+const SponsorOrdersListPage = React.lazy(() =>
+ import("../pages/sponsors/show-purchase-list-page")
+);
const SponsorLayout = ({ match }) => (
@@ -85,20 +88,16 @@ const SponsorLayout = ({ match }) => (
/>
(
-
-
-
- )}
strict
exact
component={ShowPagesListPage}
/>
+
({
...overrides
});
-const renderWithConfirmDialog = (ui, options) => renderWithRedux(
+const renderWithConfirmDialog = (ui, options) =>
+ renderWithRedux(
<>
{ui}
@@ -90,15 +91,18 @@ describe("ShowPagesListPage", () => {
describe("Component", () => {
it("should render empty state when no pages exist", () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [],
- totalCount: 0
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [],
+ totalCount: 0
+ }
}
}
- });
+ );
expect(
screen.getByText("show_pages.no_sponsors_pages")
@@ -106,30 +110,36 @@ describe("ShowPagesListPage", () => {
});
it("should render table when pages exist", () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [createShowPage(1), createShowPage(2)],
- totalCount: 2
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [createShowPage(1), createShowPage(2)],
+ totalCount: 2
+ }
}
}
- });
+ );
expect(screen.getByText("Page 1")).toBeInTheDocument();
expect(screen.getByText("Page 2")).toBeInTheDocument();
});
it("should call getShowPage and open popup when edit is clicked", async () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [createShowPage(1)],
- totalCount: 1
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [createShowPage(1)],
+ totalCount: 1
+ }
}
}
- });
+ );
const editButton = screen.getByTestId("EditIcon").closest("button");
await act(async () => {
await userEvent.click(editButton);
@@ -145,15 +155,18 @@ describe("ShowPagesListPage", () => {
});
it("should refresh list after save", async () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [createShowPage(1)],
- totalCount: 1
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [createShowPage(1)],
+ totalCount: 1
+ }
}
}
- });
+ );
const editButton = screen.getByTestId("EditIcon").closest("button");
await act(async () => {
await userEvent.click(editButton);
@@ -178,19 +191,22 @@ describe("ShowPagesListPage", () => {
});
it("should call deleteShowPage and refresh list when delete is confirmed", async () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [
- createShowPage(1),
- createShowPage(2),
- createShowPage(3)
- ],
- totalCount: 3
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [
+ createShowPage(1),
+ createShowPage(2),
+ createShowPage(3)
+ ],
+ totalCount: 3
+ }
}
}
- });
+ );
const deleteButtons = screen.getAllByTestId("DeleteIcon");
const secondDeleteButton = deleteButtons[1].closest("button");
await act(async () => {
@@ -215,15 +231,18 @@ describe("ShowPagesListPage", () => {
});
it("should not call deleteShowPage when delete is cancelled", async () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [createShowPage(1)],
- totalCount: 1
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [createShowPage(1)],
+ totalCount: 1
+ }
}
}
- });
+ );
const deleteButton = screen.getByTestId("DeleteIcon").closest("button");
await act(async () => {
await userEvent.click(deleteButton);
@@ -243,15 +262,18 @@ describe("ShowPagesListPage", () => {
});
it("should call archiveShowPage for non-archived item", async () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [createShowPage(1, { is_archived: false })],
- totalCount: 1
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [createShowPage(1, { is_archived: false })],
+ totalCount: 1
+ }
}
}
- });
+ );
const archiveButton = screen.getByText("general.archive");
await act(async () => {
await userEvent.click(archiveButton);
@@ -260,15 +282,18 @@ describe("ShowPagesListPage", () => {
});
it("should call unarchiveShowPage for archived item", async () => {
- renderWithConfirmDialog(, {
- initialState: {
- showPagesListState: {
- ...showPagesListDefaultState,
- showPages: [createShowPage(1, { is_archived: true })],
- totalCount: 1
+ renderWithConfirmDialog(
+ ,
+ {
+ initialState: {
+ showPagesListState: {
+ ...showPagesListDefaultState,
+ showPages: [createShowPage(1, { is_archived: true })],
+ totalCount: 1
+ }
}
}
- });
+ );
const unarchiveButton = screen.getByText("general.unarchive");
await act(async () => {
await userEvent.click(unarchiveButton);
diff --git a/src/pages/sponsors/show-pages-list-page/index.js b/src/pages/sponsors/show-pages-list-page/index.js
index f9c64b527..7bddc646b 100644
--- a/src/pages/sponsors/show-pages-list-page/index.js
+++ b/src/pages/sponsors/show-pages-list-page/index.js
@@ -14,6 +14,7 @@
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import T from "i18n-react/dist/i18n-react";
+import { Breadcrumb } from "react-breadcrumbs";
import {
Box,
Button,
@@ -26,13 +27,13 @@ import AddIcon from "@mui/icons-material/Add";
import MuiTable from "openstack-uicore-foundation/lib/components/mui/table";
import SearchInput from "openstack-uicore-foundation/lib/components/mui/search-input";
import {
- getShowPages,
archiveShowPage,
- unarchiveShowPage,
+ deleteShowPage,
getShowPage,
+ getShowPages,
+ resetShowPageForm,
saveShowPage,
- deleteShowPage,
- resetShowPageForm
+ unarchiveShowPage
} from "../../../actions/show-pages-actions";
import { getSponsorships } from "../../../actions/sponsor-forms-actions";
import CustomAlert from "../../../components/mui/custom-alert";
@@ -41,6 +42,7 @@ import PageTemplatePopup from "../../sponsors-global/page-templates/page-templat
import { DEFAULT_CURRENT_PAGE, MAX_PER_PAGE } from "../../../utils/constants";
const ShowPagesListPage = ({
+ match,
showPages,
currentPage,
perPage,
@@ -179,6 +181,14 @@ const ShowPagesListPage = ({
return (
+
+
+
{T.translate("show_pages.pages")}
{
+ useEffect(() => {
+ getAllSponsorPurchases();
+ }, []);
+
+ const handlePageChange = (page) => {
+ getAllSponsorPurchases(term, page, perPage, order, orderDir);
+ };
+
+ const handleSort = (key, dir) => {
+ getAllSponsorPurchases(term, currentPage, perPage, key, dir);
+ };
+
+ const handlePerPageChange = (newPerPage) => {
+ getAllSponsorPurchases(
+ term,
+ DEFAULT_CURRENT_PAGE,
+ newPerPage,
+ order,
+ orderDir
+ );
+ };
+
+ const handleExport = () => {
+ exportAllSponsorPurchases(term, order, orderDir);
+ };
+
+ const handleSearch = (searchTerm) => {
+ getAllSponsorPurchases(searchTerm);
+ };
+
+ const handleDetails = (item) => {
+ history.push(`${item.sponsor_id}/purchases/${item.id}`);
+ };
+
+ const handleMenu = (item) => {
+ console.log("MENU : ", item);
+ };
+
+ const handleStatusChange = (sponsorId, purchaseId, newStatus) => {
+ if (newStatus === PURCHASE_STATUS.PAID)
+ approveSponsorPurchase(sponsorId, purchaseId);
+ if (newStatus === PURCHASE_STATUS.CANCELLED)
+ rejectSponsorPurchase(sponsorId, purchaseId);
+ };
+
+ const tableColumns = [
+ {
+ columnKey: "number",
+ header: T.translate("sponsor_show_purchases.order"),
+ sortable: true
+ },
+ {
+ columnKey: "purchased",
+ header: T.translate("sponsor_show_purchases.purchased"),
+ sortable: true
+ },
+ {
+ columnKey: "sponsor_name",
+ header: T.translate("sponsor_show_purchases.sponsor"),
+ sortable: true
+ },
+ {
+ columnKey: "payment_method",
+ header: T.translate("sponsor_show_purchases.payment_method"),
+ sortable: true
+ },
+ {
+ columnKey: "status",
+ header: T.translate("sponsor_show_purchases.status"),
+ sortable: true,
+ render: (row) => {
+ if (
+ row.payment_method === PURCHASE_METHODS.INVOICE &&
+ row.status === PURCHASE_STATUS.PENDING
+ ) {
+ return (
+
+ );
+ }
+
+ return row.status;
+ }
+ },
+ {
+ columnKey: "amount",
+ header: T.translate("sponsor_show_purchases.amount"),
+ sortable: true
+ },
+ {
+ columnKey: "details",
+ header: "",
+ width: 100,
+ align: "center",
+ render: (row) => (
+
+ )
+ },
+ {
+ columnKey: "menu",
+ header: "",
+ width: 100,
+ align: "center",
+ render: (row) => (
+ handleMenu(row)}
+ >
+
+
+ )
+ }
+ ];
+
+ return (
+
+
+
+
+
{T.translate("sponsor_show_purchases.purchases")}
+
+
+
+ {totalCount}{" "}
+ {T.translate("sponsor_show_purchases.purchases").toLowerCase()}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const mapStateToProps = ({ showPurchaseListState }) => ({
+ ...showPurchaseListState
+});
+
+export default connect(mapStateToProps, {
+ getAllSponsorPurchases,
+ exportAllSponsorPurchases,
+ approveSponsorPurchase,
+ rejectSponsorPurchase
+})(ShowPurchaseListPage);
diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/__tests__/sponsor-purchases-list.test.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/__tests__/sponsor-purchases-list.test.js
index 2c89b5956..564ba84ec 100644
--- a/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/__tests__/sponsor-purchases-list.test.js
+++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/__tests__/sponsor-purchases-list.test.js
@@ -247,7 +247,10 @@ describe("SponsorPurchasesTab", () => {
);
});
- expect(approveSponsorPurchase).toHaveBeenCalledWith(purchase.payment_id);
+ expect(approveSponsorPurchase).toHaveBeenCalledWith(
+ 123,
+ purchase.payment_id
+ );
expect(rejectSponsorPurchase).not.toHaveBeenCalled();
});
@@ -270,7 +273,10 @@ describe("SponsorPurchasesTab", () => {
);
});
- expect(rejectSponsorPurchase).toHaveBeenCalledWith(purchase.payment_id);
+ expect(rejectSponsorPurchase).toHaveBeenCalledWith(
+ 123,
+ purchase.payment_id
+ );
expect(approveSponsorPurchase).not.toHaveBeenCalled();
});
@@ -326,7 +332,10 @@ describe("SponsorPurchasesTab", () => {
);
});
- expect(approveSponsorPurchase).toHaveBeenCalledWith(purchase.payment_id);
+ expect(approveSponsorPurchase).toHaveBeenCalledWith(
+ 123,
+ purchase.payment_id
+ );
// Dropdown is still present — component did not unmount on error
expect(withinTableBody().getByRole("combobox")).toBeInTheDocument();
});
@@ -356,7 +365,10 @@ describe("SponsorPurchasesTab", () => {
);
});
- expect(rejectSponsorPurchase).toHaveBeenCalledWith(purchase.payment_id);
+ expect(rejectSponsorPurchase).toHaveBeenCalledWith(
+ 123,
+ purchase.payment_id
+ );
expect(withinTableBody().getByRole("combobox")).toBeInTheDocument();
});
});
diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js
index 9e7de9dfa..c7d0770a2 100644
--- a/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js
+++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js
@@ -85,9 +85,10 @@ const SponsorPurchasesTab = ({
};
const handleStatusChange = (purchaseId, newStatus) => {
- if (newStatus === PURCHASE_STATUS.PAID) approveSponsorPurchase(purchaseId);
+ if (newStatus === PURCHASE_STATUS.PAID)
+ approveSponsorPurchase(sponsor.id, purchaseId);
if (newStatus === PURCHASE_STATUS.CANCELLED)
- rejectSponsorPurchase(purchaseId);
+ rejectSponsorPurchase(sponsor.id, purchaseId);
};
const tableColumns = [
@@ -149,7 +150,7 @@ const SponsorPurchasesTab = ({
render: (row) => (