diff --git a/src/actions/sponsorship-actions.js b/src/actions/sponsorship-actions.js
index 6c97c4da5..e0f8e2f62 100644
--- a/src/actions/sponsorship-actions.js
+++ b/src/actions/sponsorship-actions.js
@@ -12,7 +12,7 @@
* */
import T from "i18n-react/dist/i18n-react";
-import * as _ from "lodash";
+import debounce from "lodash/debounce";
import {
getRequest,
putRequest,
@@ -21,15 +21,12 @@ import {
createAction,
stopLoading,
startLoading,
- showMessage,
- showSuccessMessage,
- authErrorHandler,
escapeFilterValue,
fetchResponseHandler,
fetchErrorHandler
} from "openstack-uicore-foundation/lib/utils/actions";
import URI from "urijs";
-import history from "../history";
+import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions";
import { getAccessTokenSafely } from "../utils/methods";
import {
DEBOUNCE_WAIT,
@@ -48,12 +45,19 @@ export const SPONSORSHIP_UPDATED = "SPONSORSHIP_UPDATED";
export const SPONSORSHIP_ADDED = "SPONSORSHIP_ADDED";
export const SPONSORSHIP_DELETED = "SPONSORSHIP_DELETED";
-/** **************** SPONSORS *************************************** */
+/* ***************** SPONSORS *************************************** */
export const getSponsorships =
- (page = 1, perPage = DEFAULT_PER_PAGE, order = "name", orderDir = 1) =>
+ (
+ term = "",
+ page = 1,
+ perPage = DEFAULT_PER_PAGE,
+ order = "name",
+ orderDir = 1
+ ) =>
async (dispatch) => {
const accessToken = await getAccessTokenSafely();
+ const filter = [];
dispatch(startLoading());
@@ -63,6 +67,17 @@ export const getSponsorships =
access_token: accessToken
};
+ if (term) {
+ const escapedTerm = escapeFilterValue(term);
+ filter.push(
+ `name=@${escapedTerm},label=@${escapedTerm},size=@${escapedTerm}`
+ );
+ }
+
+ if (filter.length > 0) {
+ params["filter[]"] = filter;
+ }
+
// order
if (order != null && orderDir != null) {
const orderDirSign = orderDir === 1 ? "+" : "-";
@@ -73,9 +88,9 @@ export const getSponsorships =
createAction(REQUEST_SPONSORSHIPS),
createAction(RECEIVE_SPONSORSHIPS),
`${window.API_BASE_URL}/api/v1/sponsorship-types`,
- authErrorHandler,
- { order, orderDir, page }
- )(params)(dispatch).then(() => {
+ snackbarErrorHandler,
+ { order, orderDir, page, perPage }
+ )(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
@@ -93,8 +108,8 @@ export const getSponsorship = (sponsorshipId) => async (dispatch) => {
null,
createAction(RECEIVE_SPONSORSHIP),
`${window.API_BASE_URL}/api/v1/sponsorship-types/${sponsorshipId}`,
- authErrorHandler
- )(params)(dispatch).then(() => {
+ snackbarErrorHandler
+ )(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
@@ -115,40 +130,42 @@ export const saveSponsorship = (entity) => async (dispatch) => {
const normalizedEntity = normalizeSponsorship(entity);
if (entity.id) {
- putRequest(
+ return putRequest(
createAction(UPDATE_SPONSORSHIP),
createAction(SPONSORSHIP_UPDATED),
`${window.API_BASE_URL}/api/v1/sponsorship-types/${entity.id}`,
normalizedEntity,
- authErrorHandler,
+ snackbarErrorHandler,
entity
- )(params)(dispatch).then(() => {
- dispatch(
- showSuccessMessage(T.translate("edit_sponsorship.sponsorship_saved"))
- );
- });
- } else {
- const success_message = {
- title: T.translate("general.done"),
- html: T.translate("edit_sponsorship.sponsorship_created"),
- type: "success"
- };
+ )(params)(dispatch)
+ .then(() => {
+ dispatch(
+ snackbarSuccessHandler({
+ title: T.translate("general.success"),
+ html: T.translate("edit_sponsorship.sponsorship_saved")
+ })
+ );
+ })
+ .finally(() => dispatch(stopLoading()));
+ }
- postRequest(
- createAction(UPDATE_SPONSORSHIP),
- createAction(SPONSORSHIP_ADDED),
- `${window.API_BASE_URL}/api/v1/sponsorship-types`,
- normalizedEntity,
- authErrorHandler,
- entity
- )(params)(dispatch).then((payload) => {
+ return postRequest(
+ createAction(UPDATE_SPONSORSHIP),
+ createAction(SPONSORSHIP_ADDED),
+ `${window.API_BASE_URL}/api/v1/sponsorship-types`,
+ normalizedEntity,
+ snackbarErrorHandler,
+ entity
+ )(params)(dispatch)
+ .then(() => {
dispatch(
- showMessage(success_message, () => {
- history.push(`/app/sponsorship-types/${payload.response.id}`);
+ snackbarSuccessHandler({
+ title: T.translate("general.success"),
+ html: T.translate("edit_sponsorship.sponsorship_created")
})
);
- });
- }
+ })
+ .finally(() => dispatch(stopLoading()));
};
export const deleteSponsorship = (sponsorshipId) => async (dispatch) => {
@@ -163,8 +180,8 @@ export const deleteSponsorship = (sponsorshipId) => async (dispatch) => {
createAction(SPONSORSHIP_DELETED)({ sponsorshipId }),
`${window.API_BASE_URL}/api/v1/sponsorship-types/${sponsorshipId}`,
null,
- authErrorHandler
- )(params)(dispatch).then(() => {
+ snackbarErrorHandler
+ )(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
@@ -175,7 +192,7 @@ const normalizeSponsorship = (entity) => {
return normalizedEntity;
};
-export const querySponsorships = _.debounce(async (input, callback) => {
+export const querySponsorships = debounce(async (input, callback) => {
const accessToken = await getAccessTokenSafely();
const endpoint = URI(`${window.API_BASE_URL}/api/v1/sponsorship-types`);
input = escapeFilterValue(input);
@@ -192,7 +209,7 @@ export const querySponsorships = _.debounce(async (input, callback) => {
.catch(fetchErrorHandler);
}, DEBOUNCE_WAIT);
-export const querySponsorshipsBySummit = _.debounce(
+export const querySponsorshipsBySummit = debounce(
async (input, summitId, callback) => {
const accessToken = await getAccessTokenSafely();
const endpoint = URI(
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 4599c5b52..01777ca8b 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -2886,22 +2886,25 @@
"sponsorship_types_list": "Tier List",
"sponsorship_type": "Tier",
"widget_title": "Widget Title",
- "no_sponsorships": "No sponsorships found for current search criteria.",
- "add_sponsorship": "Add Sponsorship",
+ "no_sponsorships": "No tiers found for current search criteria.",
+ "add_sponsorship": "Add Tier",
"name": "Name",
"label": "Label",
"size": "Size",
"order": "Order",
- "remove_warning": "Are you sure you want to delete sponsorship "
+ "remove_warning": "Are you sure you want to delete tier {name}",
+ "placeholders": {
+ "search": "Search tier"
+ }
},
"edit_sponsorship": {
- "sponsorship": "Sponsorship",
+ "sponsorship": "Tier",
"name": "Name",
"order": "Order",
"label": "Label",
"size": "Size",
- "sponsorship_saved": "Sponsorship saved successfully",
- "sponsorship_created": "Sponsorship created successfully",
+ "sponsorship_saved": "Tier saved successfully",
+ "sponsorship_created": "Tier created successfully",
"placeholders": {
"select_size": "Select a Size"
}
diff --git a/src/pages/sponsorship-types/__tests__/sponsorship-list-page.test.js b/src/pages/sponsorship-types/__tests__/sponsorship-list-page.test.js
new file mode 100644
index 000000000..f13547ca9
--- /dev/null
+++ b/src/pages/sponsorship-types/__tests__/sponsorship-list-page.test.js
@@ -0,0 +1,203 @@
+import React from "react";
+import { act, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { renderWithRedux } from "../../../utils/test-utils";
+import SponsorshipListPage from "../sponsorship-list-page";
+import * as sponsorshipActions from "../../../actions/sponsorship-actions";
+
+jest.mock("i18n-react/dist/i18n-react", () => ({
+ translate: jest.fn((key) => key)
+}));
+
+jest.mock("sweetalert2", () => ({
+ fire: jest.fn(() => Promise.resolve({ value: true }))
+}));
+
+jest.mock(
+ "openstack-uicore-foundation/lib/components/mui/table",
+ () =>
+ function MockMuiTable({ data, onEdit, onDelete }) {
+ return (
+
+ {data.map((row) => (
+
+
+
+
+ ))}
+
+ );
+ }
+);
+
+jest.mock(
+ "../components/sponsorship-dialog",
+ () =>
+ function MockSponsorshipDialog({ onSave, onClose }) {
+ return (
+
+
+
+
+ );
+ }
+);
+
+jest.mock("../../../actions/sponsorship-actions", () => {
+ const original = jest.requireActual("../../../actions/sponsorship-actions");
+ return {
+ __esModule: true,
+ ...original,
+ getSponsorships: jest.fn(() => () => Promise.resolve()),
+ getSponsorship: jest.fn(() => () => Promise.resolve()),
+ saveSponsorship: jest.fn(() => () => Promise.resolve()),
+ deleteSponsorship: jest.fn(() => () => Promise.resolve()),
+ resetSponsorshipForm: jest.fn(() => ({ type: "RESET_SPONSORSHIP_FORM" }))
+ };
+});
+
+const SPONSORSHIPS = [
+ { id: 1, name: "Gold", label: "Gold Tier", size: "Large" },
+ { id: 2, name: "Silver", label: "Silver Tier", size: "Medium" }
+];
+
+const buildState = (listOverrides = {}) => ({
+ currentSponsorshipListState: {
+ sponsorships: SPONSORSHIPS,
+ currentPage: 1,
+ lastPage: 1,
+ perPage: 10,
+ order: "name",
+ orderDir: 1,
+ totalSponsorships: 2,
+ ...listOverrides
+ },
+ currentSponsorshipState: {
+ entity: { id: 0, name: "", label: "", size: "", order: 0 },
+ errors: {}
+ }
+});
+
+describe("SponsorshipListPage", () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should call getSponsorships on mount", () => {
+ renderWithRedux(, { initialState: buildState() });
+ expect(sponsorshipActions.getSponsorships).toHaveBeenCalledTimes(1);
+ });
+
+ it("should render the table when sponsorships exist", () => {
+ renderWithRedux(, { initialState: buildState() });
+ expect(screen.getByTestId("mui-table")).toBeInTheDocument();
+ expect(screen.getByTestId("row-1")).toBeInTheDocument();
+ expect(screen.getByTestId("row-2")).toBeInTheDocument();
+ });
+
+ it("should not render the table when the list is empty", () => {
+ renderWithRedux(, {
+ initialState: buildState({ sponsorships: [], totalSponsorships: 0 })
+ });
+ expect(screen.queryByTestId("mui-table")).not.toBeInTheDocument();
+ });
+
+ it("should open the dialog when the Add button is clicked", async () => {
+ const user = userEvent.setup();
+ renderWithRedux(, { initialState: buildState() });
+
+ expect(screen.queryByTestId("sponsorship-dialog")).not.toBeInTheDocument();
+
+ await act(async () => {
+ await user.click(screen.getByText("sponsorship_list.add_sponsorship"));
+ });
+
+ expect(screen.getByTestId("sponsorship-dialog")).toBeInTheDocument();
+ });
+
+ it("should open the dialog and fetch the entity when a row edit button is clicked", async () => {
+ const user = userEvent.setup();
+ renderWithRedux(, { initialState: buildState() });
+
+ await act(async () => {
+ await user.click(screen.getByTestId("edit-1"));
+ });
+
+ expect(sponsorshipActions.getSponsorship).toHaveBeenCalledWith(1);
+ expect(screen.getByTestId("sponsorship-dialog")).toBeInTheDocument();
+ });
+
+ it("should close the dialog and reset form when dialog close is triggered", async () => {
+ const user = userEvent.setup();
+ renderWithRedux(, { initialState: buildState() });
+
+ await act(async () => {
+ await user.click(screen.getByText("sponsorship_list.add_sponsorship"));
+ });
+
+ await act(async () => {
+ await user.click(screen.getByTestId("dialog-close"));
+ });
+
+ expect(sponsorshipActions.resetSponsorshipForm).toHaveBeenCalled();
+ expect(screen.queryByTestId("sponsorship-dialog")).not.toBeInTheDocument();
+ });
+
+ it("should call saveSponsorship, refresh list, and close dialog when save is triggered", async () => {
+ const user = userEvent.setup();
+ renderWithRedux(, { initialState: buildState() });
+
+ await act(async () => {
+ await user.click(screen.getByText("sponsorship_list.add_sponsorship"));
+ });
+
+ await act(async () => {
+ await user.click(screen.getByTestId("dialog-save"));
+ });
+
+ expect(sponsorshipActions.saveSponsorship).toHaveBeenCalledWith({
+ id: 0,
+ name: "New Tier"
+ });
+ expect(sponsorshipActions.getSponsorships).toHaveBeenCalledTimes(2);
+ expect(screen.queryByTestId("sponsorship-dialog")).not.toBeInTheDocument();
+ });
+
+ it("should call getSponsorships with the search term when Enter is pressed", async () => {
+ const user = userEvent.setup();
+ renderWithRedux(, { initialState: buildState() });
+
+ const searchInput = screen.getByPlaceholderText(
+ "sponsorship_list.placeholders.search"
+ );
+
+ await act(async () => {
+ await user.type(searchInput, "Gold{Enter}");
+ });
+
+ expect(sponsorshipActions.getSponsorships).toHaveBeenCalledWith(
+ "Gold",
+ 1,
+ 10,
+ "name",
+ 1
+ );
+ });
+});
diff --git a/src/pages/sponsorship-types/components/__tests__/sponsorship-dialog.test.js b/src/pages/sponsorship-types/components/__tests__/sponsorship-dialog.test.js
new file mode 100644
index 000000000..0289c4784
--- /dev/null
+++ b/src/pages/sponsorship-types/components/__tests__/sponsorship-dialog.test.js
@@ -0,0 +1,121 @@
+import React from "react";
+import { act, render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import SponsorshipDialog from "../sponsorship-dialog";
+
+jest.mock("i18n-react/dist/i18n-react", () => ({
+ translate: jest.fn((key) => key)
+}));
+
+jest.mock("../../../../hooks/useScrollToError", () => jest.fn());
+
+const DEFAULT_ENTITY = { id: 0, name: "", label: "", size: "", order: 0 };
+
+const EXISTING_ENTITY = {
+ id: 5,
+ name: "Platinum",
+ label: "Plat",
+ size: "Big",
+ order: 1
+};
+
+describe("SponsorshipDialog", () => {
+ const onSave = jest.fn();
+ const onClose = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should show the Add title for a new entity", () => {
+ render(
+
+ );
+ expect(screen.getByText(/general\.add/i)).toBeInTheDocument();
+ });
+
+ it("should show the Edit title for an existing entity", () => {
+ render(
+
+ );
+ expect(screen.getByText(/general\.edit/i)).toBeInTheDocument();
+ });
+
+ it("should pre-fill fields with the existing entity values", () => {
+ render(
+
+ );
+ expect(screen.getByDisplayValue("Platinum")).toBeInTheDocument();
+ expect(screen.getByDisplayValue("Plat")).toBeInTheDocument();
+ });
+
+ it("should call onClose when the close button is clicked", async () => {
+ const user = userEvent.setup();
+ render(
+
+ );
+
+ await act(async () => {
+ await user.click(screen.getByRole("button", { name: "close" }));
+ });
+
+ expect(onClose).toHaveBeenCalledTimes(1);
+ });
+
+ it("should show a required validation error when submitted without a name", async () => {
+ const user = userEvent.setup();
+ render(
+
+ );
+
+ await act(async () => {
+ await user.click(screen.getByText("general.save"));
+ });
+
+ expect(screen.getByText("validation.required")).toBeInTheDocument();
+ expect(onSave).not.toHaveBeenCalled();
+ });
+
+ it("should call onSave with form values when submitted with a valid name", async () => {
+ const user = userEvent.setup();
+ render(
+
+ );
+
+ await act(async () => {
+ await user.type(screen.getByRole("textbox", { name: /name/i }), "Bronze");
+ });
+
+ await act(async () => {
+ await user.click(screen.getByText("general.save"));
+ });
+
+ expect(onSave).toHaveBeenCalledWith(
+ expect.objectContaining({ name: "Bronze" })
+ );
+ });
+});
diff --git a/src/pages/sponsorship-types/components/sponsorship-dialog.js b/src/pages/sponsorship-types/components/sponsorship-dialog.js
new file mode 100644
index 000000000..172c6e233
--- /dev/null
+++ b/src/pages/sponsorship-types/components/sponsorship-dialog.js
@@ -0,0 +1,147 @@
+import React from "react";
+import T from "i18n-react/dist/i18n-react";
+import PropTypes from "prop-types";
+import { FormikProvider, useFormik } from "formik";
+import * as yup from "yup";
+import {
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ Button,
+ InputLabel,
+ Box,
+ IconButton,
+ Divider,
+ Grid2,
+ MenuItem
+} from "@mui/material";
+import CloseIcon from "@mui/icons-material/Close";
+import MuiFormikTextField from "../../../components/mui/formik-inputs/mui-formik-textfield";
+import MuiFormikSelect from "../../../components/mui/formik-inputs/mui-formik-select";
+import useScrollToError from "../../../hooks/useScrollToError";
+import { requiredStringValidation } from "../../../utils/yup";
+
+const SIZE_OPTIONS = ["Small", "Medium", "Large", "Big"];
+
+const SponsorshipDialog = ({ entity: initialEntity, onClose, onSave }) => {
+ const formik = useFormik({
+ initialValues: {
+ id: initialEntity?.id ?? 0,
+ name: initialEntity?.name ?? "",
+ label: initialEntity?.label ?? "",
+ size: initialEntity?.size ?? "",
+ order: initialEntity?.order ?? 0
+ },
+ enableReinitialize: true,
+ validationSchema: yup.object().shape({
+ name: requiredStringValidation()
+ }),
+ onSubmit: (values) => onSave(values)
+ });
+
+ useScrollToError(formik);
+
+ const handleClose = () => {
+ formik.resetForm();
+ onClose();
+ };
+
+ const title = initialEntity?.id
+ ? `${T.translate("general.edit")} ${T.translate(
+ "edit_sponsorship.sponsorship"
+ )}`
+ : `${T.translate("general.add")} ${T.translate(
+ "edit_sponsorship.sponsorship"
+ )}`;
+
+ return (
+
+ );
+};
+
+SponsorshipDialog.propTypes = {
+ entity: PropTypes.object,
+ onClose: PropTypes.func.isRequired,
+ onSave: PropTypes.func.isRequired
+};
+
+export default SponsorshipDialog;
diff --git a/src/pages/sponsorship-types/sponsorship-list-page.js b/src/pages/sponsorship-types/sponsorship-list-page.js
index a0d89bd0b..21ee100fb 100644
--- a/src/pages/sponsorship-types/sponsorship-list-page.js
+++ b/src/pages/sponsorship-types/sponsorship-list-page.js
@@ -9,168 +9,239 @@
* 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 from "react";
+import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import T from "i18n-react/dist/i18n-react";
-import Swal from "sweetalert2";
-import { Pagination } from "react-bootstrap";
-import { Table } from "openstack-uicore-foundation/lib/components";
+import { Box, Button, Grid2, TextField } from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
+import SearchIcon from "@mui/icons-material/Search";
+import MuiTable from "openstack-uicore-foundation/lib/components/mui/table";
import {
getSponsorships,
- deleteSponsorship
+ getSponsorship,
+ saveSponsorship,
+ deleteSponsorship,
+ resetSponsorshipForm
} from "../../actions/sponsorship-actions";
-
-class SponsorshipListPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleEdit = this.handleEdit.bind(this);
- this.handleDelete = this.handleDelete.bind(this);
- this.handleSort = this.handleSort.bind(this);
- this.handlePageChange = this.handlePageChange.bind(this);
- this.handleNewSponsorship = this.handleNewSponsorship.bind(this);
-
- this.state = {};
- }
-
- componentDidMount() {
- const { currentPage, perPage, order, orderDir } = this.props;
- this.props.getSponsorships(currentPage, perPage, order, orderDir);
- }
-
- handleEdit(sponsorship_id) {
- const { history } = this.props;
- history.push(`/app/sponsorship-types/${sponsorship_id}`);
- }
-
- handlePageChange(page) {
- const { order, orderDir, perPage } = this.props;
- this.props.getSponsorships(page, perPage, order, orderDir);
- }
-
- handleDelete(sponsorshipId) {
- const { deleteSponsorship, sponsorships } = this.props;
- let sponsorship = sponsorships.find((t) => t.id === sponsorshipId);
-
- Swal.fire({
- title: T.translate("general.are_you_sure"),
- text:
- T.translate("sponsorship_list.remove_warning") + " " + sponsorship.name,
- type: "warning",
- showCancelButton: true,
- confirmButtonColor: "#DD6B55",
- confirmButtonText: T.translate("general.yes_delete")
- }).then(function (result) {
- if (result.value) {
- deleteSponsorship(sponsorshipId);
- }
- });
- }
-
- handleSort(index, key, dir, func) {
- const { perPage, page } = this.props;
- this.props.getSponsorships(page, perPage, key, dir);
- }
-
- handleNewSponsorship(ev) {
- const { history } = this.props;
- history.push(`/app/sponsorship-types/new`);
- }
-
- render() {
- const {
- sponsorships,
- lastPage,
- currentPage,
+import { DEFAULT_CURRENT_PAGE } from "../../utils/constants";
+import SponsorshipDialog from "./components/sponsorship-dialog";
+
+const SponsorshipListPage = ({
+ sponsorships,
+ currentSponsorship,
+ currentPage,
+ perPage,
+ order,
+ orderDir,
+ totalSponsorships,
+ getSponsorships,
+ getSponsorship,
+ saveSponsorship,
+ deleteSponsorship,
+ resetSponsorshipForm
+}) => {
+ const [open, setOpen] = useState(false);
+ const [searchTerm, setSearchTerm] = useState("");
+
+ useEffect(() => {
+ getSponsorships();
+ }, [getSponsorships]);
+
+ const handlePageChange = (page) => {
+ getSponsorships(searchTerm, page, perPage, order, orderDir);
+ };
+
+ const handlePerPageChange = (newPerPage) => {
+ getSponsorships(
+ searchTerm,
+ DEFAULT_CURRENT_PAGE,
+ newPerPage,
order,
- orderDir,
- totalSponsorships
- } = this.props;
-
- const columns = [
- {
- columnKey: "name",
- value: T.translate("sponsorship_list.name"),
- sortable: true
- },
- {
- columnKey: "label",
- value: T.translate("sponsorship_list.label"),
- sortable: true
- },
- {
- columnKey: "size",
- value: T.translate("sponsorship_list.size"),
- sortable: true
- }
- ];
-
- const table_options = {
- sortCol: order,
- sortDir: orderDir,
- actions: {
- edit: { onClick: this.handleEdit },
- delete: { onClick: this.handleDelete }
- }
- };
-
- return (
-
-
- {" "}
- {T.translate("sponsorship_list.sponsorship_types_list")} (
- {totalSponsorships})
-
-
-
-
-
-
-
- {sponsorships.length === 0 && (
-
{T.translate("sponsorship_list.no_sponsorships")}
- )}
-
- {sponsorships.length > 0 && (
- <>
-
-
- >
- )}
-
+ orderDir
);
- }
-}
-
-const mapStateToProps = ({ currentSponsorshipListState }) => ({
- ...currentSponsorshipListState
+ };
+
+ const handleSort = (key, dir) => {
+ getSponsorships(searchTerm, currentPage, perPage, key, dir);
+ };
+
+ const handleSearch = (ev) => {
+ setSearchTerm(ev.target.value);
+ if (ev.key === "Enter") {
+ getSponsorships(
+ searchTerm,
+ DEFAULT_CURRENT_PAGE,
+ perPage,
+ order,
+ orderDir
+ );
+ }
+ };
+
+ const handleRowEdit = (row) => {
+ getSponsorship(row.id).then(() => setOpen(true));
+ };
+
+ const handleNew = () => {
+ resetSponsorshipForm();
+ setOpen(true);
+ };
+
+ const handleClose = () => {
+ resetSponsorshipForm();
+ setOpen(false);
+ };
+
+ const handleSave = (entity) => {
+ saveSponsorship(entity)
+ .then(() =>
+ getSponsorships(
+ searchTerm,
+ DEFAULT_CURRENT_PAGE,
+ perPage,
+ order,
+ orderDir
+ )
+ )
+ .then(() => setOpen(false));
+ };
+
+ const handleDelete = (sponsorshipId) => {
+ deleteSponsorship(sponsorshipId).then(() =>
+ getSponsorships(
+ searchTerm,
+ DEFAULT_CURRENT_PAGE,
+ perPage,
+ order,
+ orderDir
+ )
+ );
+ };
+
+ const columns = [
+ {
+ columnKey: "name",
+ header: T.translate("sponsorship_list.name"),
+ sortable: true
+ },
+ {
+ columnKey: "label",
+ header: T.translate("sponsorship_list.label"),
+ sortable: true
+ },
+ {
+ columnKey: "size",
+ header: T.translate("sponsorship_list.size"),
+ sortable: true
+ }
+ ];
+
+ const tableOptions = { sortCol: order, sortDir: orderDir };
+
+ return (
+
+
{T.translate("sponsorship_list.sponsorship_types_list")}
+
+
+
+ {totalSponsorships}{" "}
+ {T.translate("sponsorship_list.sponsorship_types")}
+
+
+
+ }
+ }}
+ onChange={(e) => setSearchTerm(e.target.value)}
+ onKeyDown={handleSearch}
+ sx={{ "& .MuiOutlinedInput-root": { height: "36px" } }}
+ />
+ }
+ sx={{
+ height: "36px",
+ padding: "6px 16px",
+ fontSize: "1.4rem",
+ lineHeight: "2.4rem",
+ letterSpacing: "0.4px"
+ }}
+ >
+ {T.translate("sponsorship_list.add_sponsorship")}
+
+
+
+
+ {sponsorships.length > 0 && (
+
+ T.translate("sponsorship_list.remove_warning", { name })
+ }
+ />
+ )}
+
+ {sponsorships.length === 0 && (
+ {T.translate("sponsorship_list.no_sponsorships")}
+ )}
+
+ {open && (
+
+ )}
+
+ );
+};
+
+const mapStateToProps = ({
+ currentSponsorshipListState,
+ currentSponsorshipState
+}) => ({
+ ...currentSponsorshipListState,
+ currentSponsorship: currentSponsorshipState.entity
});
export default connect(mapStateToProps, {
getSponsorships,
- deleteSponsorship
+ getSponsorship,
+ saveSponsorship,
+ deleteSponsorship,
+ resetSponsorshipForm
})(SponsorshipListPage);
diff --git a/src/reducers/sponsorships/sponsorship-list-reducer.js b/src/reducers/sponsorships/sponsorship-list-reducer.js
index baa6e610a..4478b86aa 100644
--- a/src/reducers/sponsorships/sponsorship-list-reducer.js
+++ b/src/reducers/sponsorships/sponsorship-list-reducer.js
@@ -9,7 +9,9 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- **/
+ * */
+
+import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions";
import {
RECEIVE_SPONSORSHIPS,
@@ -18,7 +20,6 @@ import {
} from "../../actions/sponsorship-actions";
import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions";
-import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions";
const DEFAULT_STATE = {
sponsorships: [],
@@ -38,24 +39,24 @@ const sponsorshipListReducer = (state = DEFAULT_STATE, action) => {
return DEFAULT_STATE;
}
case REQUEST_SPONSORSHIPS: {
- let { order, orderDir } = payload;
+ const { order, orderDir, perPage } = payload;
- return { ...state, order, orderDir };
+ return { ...state, order, orderDir, perPage };
}
case RECEIVE_SPONSORSHIPS: {
- let { current_page, total, last_page } = payload.response;
- let sponsorships = payload.response.data;
+ const { current_page, total, last_page } = payload.response;
+ const sponsorships = payload.response.data;
return {
...state,
- sponsorships: sponsorships,
+ sponsorships,
totalSponsorships: total,
currentPage: current_page,
lastPage: last_page
};
}
case SPONSORSHIP_DELETED: {
- let { sponsorshipId } = payload;
+ const { sponsorshipId } = payload;
return {
...state,
sponsorships: state.sponsorships.filter((t) => t.id !== sponsorshipId)