diff --git a/src/actions/ticket-actions.js b/src/actions/ticket-actions.js index 3730f26ce..18ebfb589 100644 --- a/src/actions/ticket-actions.js +++ b/src/actions/ticket-actions.js @@ -47,6 +47,7 @@ import { EXPORT_PAGE_SIZE_100, TEN } from "../utils/constants"; +import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions"; export const REQUEST_TICKETS = "REQUEST_TICKETS"; export const RECEIVE_TICKETS = "RECEIVE_TICKETS"; @@ -1186,6 +1187,7 @@ export const deleteRefundPolicy = export const getPaymentProfiles = ( + term = "", page = DEFAULT_CURRENT_PAGE, perPage = DEFAULT_PER_PAGE, order = "id", @@ -1195,6 +1197,7 @@ export const getPaymentProfiles = const { currentSummitState } = getState(); const accessToken = await getAccessTokenSafely(); const { currentSummit } = currentSummitState; + const filter = []; dispatch(startLoading()); @@ -1204,19 +1207,30 @@ export const getPaymentProfiles = access_token: accessToken }; + if (term) { + const escapedTerm = escapeFilterValue(term); + filter.push( + `provider=@${escapedTerm},id=@${escapedTerm},application_type=@${escapedTerm}` + ); + } + // order if (order != null && orderDir != null) { const orderDirSign = orderDir === DEFAULT_ORDER_DIR ? "+" : "-"; params.order = `${orderDirSign}${order}`; } + if (filter.length > 0) { + params["filter[]"] = filter; + } + return getRequest( createAction(REQUEST_PAYMENT_PROFILES), createAction(RECEIVE_PAYMENT_PROFILES), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles`, - authErrorHandler, - { page, perPage, order, orderDir } + snackbarErrorHandler, + { term, page, perPage, order, orderDir } )(params)(dispatch).then(() => { dispatch(stopLoading()); }); @@ -1232,41 +1246,34 @@ export const savePaymentProfile = (entity) => async (dispatch, getState) => { }; if (entity.id) { - putRequest( + return putRequest( createAction(UPDATE_PAYMENT_PROFILE), createAction(PAYMENT_PROFILE_UPDATED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${entity.id}`, entity, - authErrorHandler, + snackbarErrorHandler, entity )(params)(dispatch).then(() => { dispatch( - showSuccessMessage( - T.translate("edit_payment_profile.payment_profile_saved") - ) + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("edit_payment_profile.payment_profile_saved") + }) ); }); - return; } - const success_message = { - title: T.translate("general.done"), - html: T.translate("edit_payment_profile.payment_profile_created"), - type: "success" - }; - - postRequest( + return postRequest( createAction(UPDATE_PAYMENT_PROFILE), createAction(PAYMENT_PROFILE_ADDED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles`, entity, - authErrorHandler - )(params)(dispatch).then((payload) => { + snackbarErrorHandler + )(params)(dispatch).then(() => { dispatch( - showMessage(success_message, () => { - history.push( - `/app/summits/${currentSummit.id}/payment-profiles/${payload.response.id}` - ); + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("edit_payment_profile.payment_profile_created") }) ); }); @@ -1287,7 +1294,7 @@ export const deletePaymentProfile = createAction(PAYMENT_PROFILE_DELETED)({ paymentProfileId }), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}`, null, - authErrorHandler + snackbarErrorHandler )(params)(dispatch).then(() => { dispatch(stopLoading()); }); @@ -1309,7 +1316,7 @@ export const getPaymentProfile = null, createAction(RECEIVE_PAYMENT_PROFILE), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}`, - authErrorHandler + snackbarErrorHandler )(params)(dispatch).then(() => { dispatch(stopLoading()); }); @@ -1347,7 +1354,7 @@ export const getPaymentFeeTypes = createAction(RECEIVE_PAYMENT_FEE_TYPES), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}/fee-types`, - authErrorHandler, + snackbarErrorHandler, { page, perPage, order, orderDir } )(params)(dispatch).then(() => { dispatch(stopLoading()); @@ -1366,45 +1373,44 @@ export const savePaymentFeeType = (entity) => async (dispatch, getState) => { access_token: accessToken }; + dispatch(startLoading()); + if (entity.id) { - putRequest( + return putRequest( createAction(UPDATE_PAYMENT_FEE_TYPE), createAction(PAYMENT_FEE_TYPE_UPDATED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}/fee-types/${entity.id}`, entity, - authErrorHandler, + snackbarErrorHandler, entity - )(params)(dispatch).then(() => { - dispatch( - showSuccessMessage( - T.translate("edit_payment_fee_type.payment_fee_type_saved") - ) - ); - }); - return; + )(params)(dispatch) + .then(() => { + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("edit_payment_fee_type.payment_fee_type_saved") + }) + ); + }) + .finally(() => dispatch(stopLoading())); } - const success_message = { - title: T.translate("general.done"), - html: T.translate("edit_payment_fee_type.payment_fee_type_created"), - type: "success" - }; - - postRequest( + return postRequest( createAction(UPDATE_PAYMENT_FEE_TYPE), createAction(PAYMENT_FEE_TYPE_ADDED), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}/fee-types`, entity, - authErrorHandler - )(params)(dispatch).then(() => { - dispatch( - showMessage(success_message, () => { - history.push( - `/app/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}` - ); - }) - ); - }); + snackbarErrorHandler + )(params)(dispatch) + .then(() => { + dispatch( + snackbarSuccessHandler({ + title: T.translate("general.success"), + html: T.translate("edit_payment_fee_type.payment_fee_type_created") + }) + ); + }) + .finally(() => dispatch(stopLoading())); }; export const deletePaymentFeeType = @@ -1425,7 +1431,7 @@ export const deletePaymentFeeType = createAction(PAYMENT_FEE_TYPE_DELETED)({ paymentFeeTypeId }), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}/fee-types/${paymentFeeTypeId}`, null, - authErrorHandler + snackbarErrorHandler )(params)(dispatch).then(() => { dispatch(stopLoading()); }); @@ -1450,7 +1456,7 @@ export const getPaymentFeeType = null, createAction(RECEIVE_PAYMENT_FEE_TYPE), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}/fee-types/${paymentFeeTypeId}`, - authErrorHandler + snackbarErrorHandler )(params)(dispatch).then(() => { dispatch(stopLoading()); }); diff --git a/src/i18n/en.json b/src/i18n/en.json index 22c593037..047b457e6 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1922,9 +1922,13 @@ "active": "Is Active?", "payment_profiles_list": "Payment Profiles List", "payment_profiles": "Payment Profiles", + "payment_profile": "Payment Profile", "add_payment_profile": "Add Payment Profile", "no_payment_profiles": "No Payment Profiles found for current search criteria.", - "remove_warning": "Are you sure you want to delete this Payment Profile?" + "remove_warning": "Are you sure you want to delete profile #{name}", + "placeholders": { + "search_profiles": "Search payment profiles" + } }, "edit_payment_profile": { "payment_profile": "Payment Profile", @@ -1944,7 +1948,9 @@ "send_email_receipt": "Send Stripe Email receipt?", "merchant_account_id": "Merchant Account Id", "payment_type_fee": "Payment Type Fee", + "payment_type_fees": "Payment Type Fees", "new_fee_type": "Add New Fee Type", + "save_fee_type": "Save Fee Type", "payment_type_fee_name": "Name", "payment_type_fee_kind": "Kind", "payment_type_fee_method": "Method", diff --git a/src/layouts/payment-profile-layout.js b/src/layouts/payment-profile-layout.js index 3950ab6d0..f5eeef8bf 100644 --- a/src/layouts/payment-profile-layout.js +++ b/src/layouts/payment-profile-layout.js @@ -17,10 +17,8 @@ import T from "i18n-react/dist/i18n-react"; import { Breadcrumb } from "react-breadcrumbs"; import Restrict from "../routes/restrict"; -import PaymentProfileListPage from "../pages/tickets/payment-profile-list-page"; -import EditPaymentProfilePage from "../pages/tickets/edit-payment-profile-page"; +import PaymentProfileListPage from "../pages/tickets/payment-profile/payment-profile-list-page"; import NoMatchPage from "../pages/no-match-page"; -import EditPaymentFeeTypePage from "../pages/tickets/edit-payment-fee-type-page"; const PaymentProfileLayout = ({ match }) => (
@@ -32,31 +30,6 @@ const PaymentProfileLayout = ({ match }) => ( /> - - - - -
diff --git a/src/pages/tickets/edit-payment-fee-type-page.js b/src/pages/tickets/edit-payment-fee-type-page.js deleted file mode 100644 index a8d156a79..000000000 --- a/src/pages/tickets/edit-payment-fee-type-page.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * 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 React, { useEffect } from "react"; -import { connect } from "react-redux"; -import { Breadcrumb } from "react-breadcrumbs"; -import T from "i18n-react/dist/i18n-react"; -import PaymentFeeTypeForm from "../../components/forms/payment-fee-type-form"; -import { - getPaymentFeeType, - resetPaymentFeeTypeForm, - savePaymentFeeType -} from "../../actions/ticket-actions"; -import AddNewButton from "../../components/buttons/add-new-button"; - -const EditPaymentFeeType = ({ - currentSummit, - entity, - errors, - match, - getPaymentFeeType, - resetPaymentFeeTypeForm, - savePaymentFeeType -}) => { - useEffect(() => { - const { params } = match; - if (params.payment_fee_type_id) { - getPaymentFeeType(params.payment_fee_type_id); - } else { - resetPaymentFeeTypeForm(); - } - }, [match.params.payment_fee_type_id]); - - const title = entity.id - ? T.translate("general.edit") - : T.translate("general.add"); - const breadcrumb = entity.id ? entity.name : T.translate("general.new"); - - return ( -
- -

- {title} {T.translate("edit_payment_fee_type.payment_fee_type")} - -

-
- {currentSummit && ( - - )} -
- ); -}; - -const mapStateToProps = ({ - currentSummitState, - currentPaymentFeeTypeState -}) => ({ - currentSummit: currentSummitState.currentSummit, - ...currentPaymentFeeTypeState -}); - -export default connect(mapStateToProps, { - getPaymentFeeType, - resetPaymentFeeTypeForm, - savePaymentFeeType -})(EditPaymentFeeType); diff --git a/src/pages/tickets/edit-payment-profile-page.js b/src/pages/tickets/edit-payment-profile-page.js deleted file mode 100644 index 9630768a6..000000000 --- a/src/pages/tickets/edit-payment-profile-page.js +++ /dev/null @@ -1,131 +0,0 @@ -/** - * 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 React from "react"; -import { connect } from "react-redux"; -import { Breadcrumb } from "react-breadcrumbs"; -import T from "i18n-react/dist/i18n-react"; -import Swal from "sweetalert2"; -import PaymentProfile from "../../components/forms/payment-profile-form"; -import { getSummitById } from "../../actions/summit-actions"; -import { - getPaymentProfile, - resetPaymentProfileForm, - savePaymentProfile, - getPaymentFeeTypes, - deletePaymentFeeType -} from "../../actions/ticket-actions"; -import AddNewButton from "../../components/buttons/add-new-button"; - -class EditPaymentProfilePage extends React.Component { - constructor(props) { - const paymentProfileId = props.match.params.payment_profile_id; - super(props); - - if (!paymentProfileId) { - props.resetPaymentProfileForm(); - } else { - props - .getPaymentProfile(paymentProfileId) - .then(props.getPaymentFeeTypes(paymentProfileId)); - } - - this.handleDeletePaymentFeeType = - this.handleDeletePaymentFeeType.bind(this); - } - - componentDidUpdate(prevProps) { - const oldId = prevProps.match.params.payment_profile_id; - const newId = this.props.match.params.payment_profile_id; - - if (newId !== oldId) { - if (!newId) { - this.props.resetPaymentProfileForm(); - } else { - this.props.getPaymentProfile(newId); - } - } - } - - handleDeletePaymentFeeType(feeTypeId) { - const { paymentFeeTypes, deletePaymentFeeType } = this.props; - const feeType = paymentFeeTypes.paymentFeeTypes.find( - (r) => r.id === feeTypeId - ); - - Swal.fire({ - title: T.translate("general.are_you_sure"), - text: `${T.translate("edit_location.remove_room_warning")} ${ - feeType.name - }`, - type: "warning", - showCancelButton: true, - confirmButtonColor: "#DD6B55", - confirmButtonText: T.translate("general.yes_delete") - }).then((result) => { - if (result.value) { - deletePaymentFeeType(feeTypeId); - } - }); - } - - render() { - const { currentSummit, paymentFeeTypes, history, entity, errors, match } = - this.props; - const title = entity.id - ? T.translate("general.edit") - : T.translate("general.add"); - const breadcrumb = entity.id ? entity.name : T.translate("general.new"); - - return ( -
- -

- {title} {T.translate("edit_payment_profile.payment_profile")} - -

-
- {currentSummit && ( - - )} -
- ); - } -} - -const mapStateToProps = ({ - currentSummitState, - currentPaymentProfileState, - currentPaymentFeeListTypeState -}) => ({ - currentSummit: currentSummitState.currentSummit, - paymentFeeTypes: currentPaymentFeeListTypeState, - ...currentPaymentProfileState -}); - -export default connect(mapStateToProps, { - getSummitById, - getPaymentProfile, - resetPaymentProfileForm, - savePaymentProfile, - getPaymentFeeTypes, - deletePaymentFeeType -})(EditPaymentProfilePage); diff --git a/src/pages/tickets/payment-profile-list-page.js b/src/pages/tickets/payment-profile-list-page.js deleted file mode 100644 index 465176ccb..000000000 --- a/src/pages/tickets/payment-profile-list-page.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2020 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 from "react"; -import { connect } from "react-redux"; -import T from "i18n-react/dist/i18n-react"; -import Table from "openstack-uicore-foundation/lib/components/table"; -import Swal from "sweetalert2"; -import { - getPaymentProfiles, - savePaymentProfile, - deletePaymentProfile -} from "../../actions/ticket-actions"; -import { MAX_PER_PAGE } from "../../utils/constants"; - -class PaymentProfileListPage 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.handleNewPaymentProfile = this.handleNewPaymentProfile.bind(this); - this.state = {}; - } - - componentDidMount() { - const { currentSummit } = this.props; - if (currentSummit) { - this.props.getPaymentProfiles(1, MAX_PER_PAGE); - } - } - - handleEdit(paymentProfileId) { - const { currentSummit, history } = this.props; - history.push( - `/app/summits/${currentSummit.id}/payment-profiles/${paymentProfileId}` - ); - } - - handleDelete(paymentProfileId) { - const { deletePaymentProfile } = this.props; - - Swal.fire({ - title: T.translate("general.are_you_sure"), - text: T.translate("payment_profiles.remove_warning"), - type: "warning", - showCancelButton: true, - confirmButtonColor: "#DD6B55", - confirmButtonText: T.translate("general.yes_delete") - }).then((result) => { - if (result.value) { - deletePaymentProfile(paymentProfileId); - } - }); - } - - handleSort(index, key, dir) { - this.props.getPaymentProfiles(1, MAX_PER_PAGE, key, dir); - } - - handleNewPaymentProfile() { - const { currentSummit, history } = this.props; - history.push(`/app/summits/${currentSummit.id}/payment-profiles/new`); - } - - render() { - const { - currentSummit, - paymentProfiles, - order, - orderDir, - totalPaymentProfiles - } = this.props; - - const columns = [ - { - columnKey: "id", - value: T.translate("payment_profiles.id"), - sortable: true - }, - { - columnKey: "application_type", - value: T.translate("payment_profiles.application_type") - }, - { - columnKey: "provider", - value: T.translate("payment_profiles.provider") - }, - { - columnKey: "is_active", - value: T.translate("payment_profiles.active"), - render: (row) => (row.is_active ? "Yes" : "No") - } - ]; - - const table_options = { - sortCol: order, - sortDir: orderDir, - actions: { - edit: { onClick: this.handleEdit }, - delete: { onClick: this.handleDelete } - } - }; - - if (!currentSummit.id) return
; - - return ( -
-

- {" "} - {T.translate("payment_profiles.payment_profiles_list")} ( - {totalPaymentProfiles}) -

-
-
- -
-
- - {paymentProfiles.length === 0 && ( -
{T.translate("payment_profiles.no_payment_profiles")}
- )} - - {paymentProfiles.length > 0 && ( - - )} - - ); - } -} - -const mapStateToProps = ({ - currentSummitState, - currentPaymentProfileListState -}) => ({ - currentSummit: currentSummitState.currentSummit, - ...currentPaymentProfileListState -}); - -export default connect(mapStateToProps, { - savePaymentProfile, - deletePaymentProfile, - getPaymentProfiles -})(PaymentProfileListPage); diff --git a/src/pages/tickets/payment-profile/components/payment-profile-dialog.js b/src/pages/tickets/payment-profile/components/payment-profile-dialog.js new file mode 100644 index 000000000..c6c0caf3a --- /dev/null +++ b/src/pages/tickets/payment-profile/components/payment-profile-dialog.js @@ -0,0 +1,590 @@ +import React, { useState } from "react"; +import PropTypes from "prop-types"; +import T from "i18n-react/dist/i18n-react"; +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Button, + Divider, + Grid2, + IconButton, + MenuItem, + Box, + InputLabel, + Tooltip +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import AddIcon from "@mui/icons-material/Add"; +import InfoIcon from "@mui/icons-material/Info"; +import { useFormik, FormikProvider } from "formik"; +import * as yup from "yup"; +import MuiTable from "openstack-uicore-foundation/lib/components/mui/table"; +import MuiFormikSelect from "openstack-uicore-foundation/lib/components/mui/formik-inputs/select"; +import MuiFormikTextField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/textfield"; +import MuiFormikCheckbox from "openstack-uicore-foundation/lib/components/mui/formik-inputs/checkbox"; +import useScrollToError from "../../../../hooks/useScrollToError"; + +const InfoTooltip = ({ title }) => ( + + + +); + +const APPLICATION_TYPE_OPTIONS = [ + { label: "Registration", value: "Registration" }, + { label: "Bookable Rooms", value: "BookableRooms" }, + { label: "Sponsor Services", value: "SponsorServices" } +]; + +const PROVIDER_OPTIONS = [ + { label: "Stripe", value: "Stripe" }, + { label: "LawPay", value: "LawPay" } +]; + +const PAYMENT_TYPE_FEE_KIND = [ + { label: "Rate", value: "Rate" }, + { label: "Amount", value: "Amount" } +]; + +const PAYMENT_TYPE_FEE_METHOD = [ + // # Cards & wallets + { label: "Card", value: "card" }, + { label: "Link", value: "link" }, + { label: "CashApp", value: "cashapp" }, + { label: "Paypal", value: "paypal" }, + // # Bank debits + { label: "UsBankAccount", value: "us_bank_account" }, + { label: "SepaDebit", value: "sepa_debit" }, + { label: "BacsDebit", value: "bacs_debit" }, + { label: "AuBecsDebit", value: "au_becs_debit" }, + { label: "AcssDebit", value: "acss_debit" }, + // # Bank redirects + { label: "Ideal", value: "ideal" }, + { label: "Sofort", value: "sofort" }, + { label: "Bancontact", value: "bancontact" }, + { label: "Giropay", value: "giropay" }, + { label: "Eps", value: "eps" }, + { label: "P24", value: "p24" }, + { label: "Blik", value: "blik" }, + // # Buy Now, Pay Later + { label: "Klarna", value: "klarna" }, + { label: "AfterpayClearpay", value: "afterpay_clearpay" }, + { label: "Affirm", value: "affirm" }, + // # Regional / other + { label: "Alipay", value: "alipay" }, + { label: "WechatPay", value: "wechat_pay" }, + { label: "Grabpay", value: "grabpay" }, + { label: "Oxxo", value: "oxxo" }, + { label: "Boleto", value: "boleto" }, + { label: "Konbini", value: "konbini" } +]; + +const PaymentProfileDialog = ({ + onSave, + onClose, + entity: initialEntity, + paymentFeeTypes, + onDeleteFeeType, + onSaveFeeType +}) => { + const [showFeeTypeForm, setShowFeeTypeForm] = useState(false); + + const formik = useFormik({ + initialValues: { + id: initialEntity?.id || 0, + application_type: initialEntity?.application_type || "", + provider: initialEntity?.provider || "", + is_active: initialEntity?.is_active || false, + test_mode_enabled: initialEntity?.test_mode_enabled || false, + send_email_receipt: initialEntity?.send_email_receipt || false, + merchant_account_id: initialEntity?.merchant_account_id || "", + live_secret_key: initialEntity?.live_secret_key || "", + live_publishable_key: initialEntity?.live_publishable_key || "", + test_secret_key: initialEntity?.test_secret_key || "", + test_publishable_key: initialEntity?.test_publishable_key || "" + }, + // validationSchema: yup.object().shape({ + // application_type: yup.string().required(T.translate("validation.required")), + // provider: yup.string().required(T.translate("validation.required")), + // live_secret_key: yup.string().required(T.translate("validation.required")), + // live_publishable_key: yup.string().required(T.translate("validation.required")) + // }), + enableReinitialize: true, + onSubmit: (values) => onSave(values) + }); + + const feeTypeFormik = useFormik({ + initialValues: { + id: 0, + name: "", + kind: "", + payment_method: "", + value: "", + max_cents: "", + min_cents: "" + }, + validationSchema: yup.object().shape({ + name: yup.string().required(T.translate("validation.required")), + kind: yup.string().required(T.translate("validation.required")), + payment_method: yup.string().required(T.translate("validation.required")), + value: yup.number().required(T.translate("validation.required")), + max_cents: yup.number().required(T.translate("validation.required")), + min_cents: yup.number().required(T.translate("validation.required")) + }), + onSubmit: (values) => onSaveFeeType(values).then(() => { + feeTypeFormik.resetForm(); + setShowFeeTypeForm(false); + }) + }); + + useScrollToError(formik, true); + + const title = initialEntity.id + ? `${T.translate("general.edit")} ${T.translate( + "payment_profiles.payment_profile" + )}` + : `${T.translate("general.add")} ${T.translate( + "payment_profiles.payment_profile" + )}`; + + const fee_types_options = { + sortCol: paymentFeeTypes.order, + sortDir: paymentFeeTypes.orderDir + }; + + const fee_types_columns = [ + { + columnKey: "name", + header: T.translate("edit_payment_profile.payment_type_fee_name") + }, + { + columnKey: "kind", + header: T.translate("edit_payment_profile.payment_type_fee_kind") + }, + { + columnKey: "payment_method", + header: T.translate("edit_payment_profile.payment_type_fee_method") + }, + { + columnKey: "value", + header: T.translate("edit_payment_profile.payment_type_fee_value") + }, + { + columnKey: "max_cents", + header: T.translate("edit_payment_profile.payment_type_fee_max_cents") + }, + { + columnKey: "min_cents", + header: T.translate("edit_payment_profile.payment_type_fee_min_cents") + } + ]; + + const handleClose = () => { + formik.resetForm(); + onClose(); + }; + + const handleFeeTypeDelete = (feeTypeId) => { + onDeleteFeeType(feeTypeId); + }; + + const handleFeeTypeEdit = (feeType) => { + feeTypeFormik.setValues({ ...feeType }); + setShowFeeTypeForm(true); + }; + + const handleNewFeeType = () => { + feeTypeFormik.resetForm(); + setShowFeeTypeForm(true); + }; + + const handleCancelFeeType = () => { + feeTypeFormik.resetForm(); + setShowFeeTypeForm(false); + }; + + return ( + + + {title} + + + + + + + + + + + {T.translate("edit_payment_profile.application_type")} + + + {APPLICATION_TYPE_OPTIONS.map((opt) => ( + + {opt.label} + + ))} + + + + + {T.translate("edit_payment_profile.provider")} + + + {PROVIDER_OPTIONS.map((opt) => ( + + {opt.label} + + ))} + + + + + + + + + {T.translate("edit_payment_profile.test_mode_enabled")} + + + } + /> + + {formik.values.provider === "Stripe" && ( + + + {T.translate("edit_payment_profile.send_email_receipt")} + + + } + /> + + )} + + {formik.values.provider === "LawPay" && ( + + + {T.translate("edit_payment_profile.merchant_account_id")} + + + + )} + + + + {T.translate("edit_payment_profile.live_secret_key")} + + + + + + + {T.translate("edit_payment_profile.live_publishable_key")} + + + + + + + {T.translate("edit_payment_profile.test_secret_key")} + + + + + + + {T.translate("edit_payment_profile.test_publishable_key")} + + + + + + {formik.values.id !== 0 && + formik.values.provider === "Stripe" && + formik.values.application_type === "SponsorServices" && ( + <> + + +
+ {T.translate("edit_payment_profile.payment_type_fee")} +
+ + + {paymentFeeTypes.totalPaymentFeeTypes}{" "} + {T.translate("edit_payment_profile.payment_type_fees")} + + + {!showFeeTypeForm && ( + + )} + + + + {showFeeTypeForm && ( + + + + {T.translate( + "edit_payment_profile.payment_type_fee_name" + )} + + + + + + {T.translate( + "edit_payment_profile.payment_type_fee_kind" + )} + + + {PAYMENT_TYPE_FEE_KIND.map((opt) => ( + + {opt.label} + + ))} + + + + + {T.translate( + "edit_payment_profile.payment_type_fee_method" + )} + + { + if (!selected) + return ( + + {T.translate( + "edit_payment_profile.payment_type_fee_method" + )} + + ); + const opt = PAYMENT_TYPE_FEE_METHOD.find( + (o) => o.value === selected + ); + return opt ? opt.label : selected; + }} + > + {PAYMENT_TYPE_FEE_METHOD.map((opt) => ( + + {opt.label} + + ))} + + + + + {T.translate( + "edit_payment_profile.payment_type_fee_value" + )} + + + + + + {T.translate( + "edit_payment_profile.payment_type_fee_max_cents" + )} + + + + + + {T.translate( + "edit_payment_profile.payment_type_fee_min_cents" + )} + + + + + + + + + )} + + +
+ + )} +
+ + + + +
+
+ ); +}; + +PaymentProfileDialog.propTypes = { + onClose: PropTypes.func.isRequired, + onSave: PropTypes.func.isRequired, + entity: PropTypes.object, + paymentFeeTypes: PropTypes.array, + onSaveFeeType: PropTypes.func.isRequired, + onDeleteFeeType: PropTypes.func.isRequired +}; + +PaymentProfileDialog.defaultProps = { + entity: {} +}; + +export default PaymentProfileDialog; diff --git a/src/pages/tickets/payment-profile/payment-profile-list-page.js b/src/pages/tickets/payment-profile/payment-profile-list-page.js new file mode 100644 index 000000000..b3d691d4a --- /dev/null +++ b/src/pages/tickets/payment-profile/payment-profile-list-page.js @@ -0,0 +1,256 @@ +/** + * Copyright 2020 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, useState } from "react"; +import { connect } from "react-redux"; +import T from "i18n-react/dist/i18n-react"; +import { Box, Button, Grid2 } from "@mui/material"; +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 { + getPaymentProfiles, + getPaymentProfile, + savePaymentProfile, + deletePaymentProfile, + getPaymentFeeTypes, + deletePaymentFeeType, + savePaymentFeeType, + resetPaymentProfileForm +} from "../../../actions/ticket-actions"; +import { DEFAULT_CURRENT_PAGE } from "../../../utils/constants"; +import PaymentProfileDialog from "./components/payment-profile-dialog"; + +const PaymentProfileListPage = ({ + currentSummit, + paymentProfiles, + term, + currentPage, + perPage, + order, + orderDir, + totalPaymentProfiles, + getPaymentProfiles, + getPaymentProfile, + savePaymentProfile, + deletePaymentProfile, + resetPaymentProfileForm, + currentPaymentProfile, + paymentFeeTypes, + getPaymentFeeTypes, + savePaymentFeeType, + deletePaymentFeeType +}) => { + useEffect(() => { + if (currentSummit?.id) + getPaymentProfiles("", DEFAULT_CURRENT_PAGE, perPage); + }, [currentSummit?.id]); + + const [paymentProfilePopup, setPaymentProfilePopup] = useState(false); + + const handleEdit = (paymentProfile) => { + getPaymentProfile(paymentProfile.id).then(() => { + getPaymentFeeTypes(paymentProfile.id); + setPaymentProfilePopup(true); + }); + }; + + const handleSave = (entity) => { + savePaymentProfile(entity).then(() => { + getPaymentProfiles("", DEFAULT_CURRENT_PAGE, perPage); + setPaymentProfilePopup(false); + }); + }; + + const handleDelete = (paymentProfileId) => + deletePaymentProfile(paymentProfileId).then(() => + getPaymentProfiles("", DEFAULT_CURRENT_PAGE, perPage) + ); + + const handleSearch = (searchTerm) => { + getPaymentProfiles(searchTerm, DEFAULT_CURRENT_PAGE, perPage); + }; + + const handlePageChange = (page) => { + getPaymentProfiles(term, page, perPage, order, orderDir); + }; + const handlePerPageChange = (newPerPage) => { + getPaymentProfiles(term, DEFAULT_CURRENT_PAGE, newPerPage, order, orderDir); + }; + + const handleSort = (key, dir) => { + getPaymentProfiles(term, DEFAULT_CURRENT_PAGE, perPage, key, dir); + }; + + const handleClose = () => { + resetPaymentProfileForm(); + setPaymentProfilePopup(false); + }; + + const handleSaveFeeType = (entity) => + savePaymentFeeType(entity).then(() => { + getPaymentFeeTypes(currentPaymentProfile.id); + setPaymentProfilePopup(false); + }); + + const handleDeleteFeeType = (feeTypeId) => + deletePaymentFeeType(feeTypeId).then(() => { + getPaymentFeeTypes(currentPaymentProfile.id); + }); + + const handleNewPaymentProfile = () => { + resetPaymentProfileForm(); + setPaymentProfilePopup(true); + }; + + const columns = [ + { + columnKey: "id", + header: T.translate("payment_profiles.id"), + sortable: true + }, + { + columnKey: "application_type", + header: T.translate("payment_profiles.application_type") + }, + { + columnKey: "provider", + header: T.translate("payment_profiles.provider") + }, + { + columnKey: "is_active", + header: T.translate("payment_profiles.active"), + render: (row) => (row.is_active ? "Yes" : "No") + } + ]; + + const table_options = { + sortCol: order, + sortDir: orderDir + }; + + if (!currentSummit.id) return
; + + return ( +
+

{T.translate("payment_profiles.payment_profiles_list")}

+ + + + {totalPaymentProfiles}{" "} + {T.translate("payment_profiles.payment_profiles")} + + + + + + + + + + + {paymentProfiles.length === 0 && ( +
{T.translate("payment_profiles.no_payment_profiles")}
+ )} + + {paymentProfiles.length > 0 && ( + row.id} + deleteDialogBody={(id) => + T.translate("payment_profiles.remove_warning", { name: id }) + } + /> + )} + + {paymentProfilePopup && ( + + )} +
+ ); +}; + +const mapStateToProps = ({ + currentSummitState, + currentPaymentProfileListState, + currentPaymentProfileState, + currentPaymentFeeListTypeState +}) => ({ + currentSummit: currentSummitState.currentSummit, + paymentFeeTypes: currentPaymentFeeListTypeState, + currentPaymentProfile: currentPaymentProfileState.entity, + ...currentPaymentProfileListState +}); + +export default connect(mapStateToProps, { + getPaymentProfiles, + getPaymentProfile, + savePaymentProfile, + deletePaymentProfile, + resetPaymentProfileForm, + getPaymentFeeTypes, + savePaymentFeeType, + deletePaymentFeeType +})(PaymentProfileListPage); diff --git a/src/reducers/payment_profiles/payment-profile-list-reducer.js b/src/reducers/payment_profiles/payment-profile-list-reducer.js index d1837784f..4d87563e6 100644 --- a/src/reducers/payment_profiles/payment-profile-list-reducer.js +++ b/src/reducers/payment_profiles/payment-profile-list-reducer.js @@ -24,8 +24,12 @@ import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; const DEFAULT_STATE = { paymentProfiles: [], + term: "", order: "id", orderDir: 1, + currentPage: 1, + lastPage: 1, + perPage: 10, totalPaymentProfiles: 0 }; @@ -41,13 +45,19 @@ const paymentProfileListReducer = (state = DEFAULT_STATE, action) => { return { ...state, order, orderDir }; } case RECEIVE_PAYMENT_PROFILES: { - const { total } = payload.response; + const { + current_page: currentPage, + total, + last_page: lastPage + } = payload.response; const paymentProfiles = payload.response.data; return { ...state, paymentProfiles, - totalPaymentProfiles: total + totalPaymentProfiles: total, + currentPage, + lastPage }; } case PAYMENT_PROFILE_ADDED: {