From 8bef1b3c7886dacd74d3e9ef08c668f576cad6c5 Mon Sep 17 00:00:00 2001 From: Priscila Moneo Date: Wed, 6 May 2026 19:12:43 -0300 Subject: [PATCH 1/2] feat: move tax types grid to mui --- src/actions/tax-actions.js | 70 +++-- src/components/forms/tax-type-form.js | 307 +++++++++--------- src/layouts/tax-type-layout.js | 48 +-- src/pages/taxes/edit-tax-type-page.js | 96 ------ src/pages/taxes/popup/tax-type-popup.js | 67 ++++ src/pages/taxes/tax-type-list-page.js | 327 +++++++++++++------- src/reducers/taxes/tax-type-list-reducer.js | 32 +- 7 files changed, 518 insertions(+), 429 deletions(-) delete mode 100644 src/pages/taxes/edit-tax-type-page.js create mode 100644 src/pages/taxes/popup/tax-type-popup.js diff --git a/src/actions/tax-actions.js b/src/actions/tax-actions.js index 244fecdae..8d2b49121 100644 --- a/src/actions/tax-actions.js +++ b/src/actions/tax-actions.js @@ -9,10 +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 T from "i18n-react/dist/i18n-react"; -import history from "../history"; import { getRequest, putRequest, @@ -21,11 +20,16 @@ import { createAction, stopLoading, startLoading, - showMessage, showSuccessMessage, - authErrorHandler + authErrorHandler, + escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; import { getAccessTokenSafely } from "../utils/methods"; +import { + DEFAULT_CURRENT_PAGE, + DEFAULT_ORDER_DIR, + DEFAULT_PER_PAGE +} from "../utils/constants"; export const REQUEST_TAX_TYPES = "REQUEST_TAX_TYPES"; export const RECEIVE_TAX_TYPES = "RECEIVE_TAX_TYPES"; @@ -37,27 +41,47 @@ export const TAX_TYPE_ADDED = "TAX_TYPE_ADDED"; export const TAX_TYPE_DELETED = "TAX_TYPE_DELETED"; export const TAX_TICKET_ADDED = "TAX_TICKET_ADDED"; export const TAX_TICKET_REMOVED = "TAX_TICKET_REMOVED"; +const ALLOWED_TAX_TYPE_SORT_FIELDS = ["name", "rate"]; export const getTaxTypes = - (order = "name", orderDir = 1) => + ( + term = "", + page = DEFAULT_CURRENT_PAGE, + perPage = DEFAULT_PER_PAGE, + order = "name", + orderDir = DEFAULT_ORDER_DIR + ) => async (dispatch, getState) => { const { currentSummitState } = getState(); const accessToken = await getAccessTokenSafely(); const { currentSummit } = currentSummitState; + const filter = []; + const normalizedOrder = ALLOWED_TAX_TYPE_SORT_FIELDS.includes(order) + ? order + : "name"; dispatch(startLoading()); + if (term) { + const escapedTerm = escapeFilterValue(term); + filter.push(`name=@${escapedTerm}`); + } + const params = { - page: 1, - per_page: 100, + page, + per_page: perPage, access_token: accessToken, expand: "ticket_types" }; + if (filter.length > 0) { + params["filter[]"] = filter; + } + // order - if (order != null && orderDir != null) { - const orderDirSign = orderDir === 1 ? "+" : "-"; - params["order"] = `${orderDirSign}${order}`; + if (normalizedOrder != null && orderDir != null) { + const orderDirSign = orderDir === 1 ? "" : "-"; + params.order = `${orderDirSign}${normalizedOrder}`; } return getRequest( @@ -65,7 +89,7 @@ export const getTaxTypes = createAction(RECEIVE_TAX_TYPES), `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/tax-types`, authErrorHandler, - { order, orderDir } + { order: normalizedOrder, orderDir, page, perPage, term } )(params)(dispatch).then(() => { dispatch(stopLoading()); }); @@ -93,7 +117,7 @@ export const getTaxType = (taxTypeId) => async (dispatch, getState) => { }); }; -export const resetTaxTypeForm = () => (dispatch, getState) => { +export const resetTaxTypeForm = () => (dispatch) => { dispatch(createAction(RESET_TAX_TYPE_FORM)({})); }; @@ -111,7 +135,7 @@ export const saveTaxType = (entity) => async (dispatch, getState) => { const normalizedEntity = normalizeEntity(entity); if (entity.id) { - putRequest( + return putRequest( createAction(UPDATE_TAX_TYPE), createAction(TAX_TYPE_UPDATED), `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/tax-types/${entity.id}`, @@ -120,15 +144,10 @@ export const saveTaxType = (entity) => async (dispatch, getState) => { entity )(params)(dispatch).then((payload) => { dispatch(showSuccessMessage(T.translate("edit_tax_type.tax_type_saved"))); + return payload; }); - } else { - const success_message = { - title: T.translate("general.done"), - html: T.translate("edit_tax_type.tax_type_created"), - type: "success" - }; - - postRequest( + } + return postRequest( createAction(UPDATE_TAX_TYPE), createAction(TAX_TYPE_ADDED), `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/tax-types`, @@ -137,14 +156,11 @@ export const saveTaxType = (entity) => async (dispatch, getState) => { entity )(params)(dispatch).then((payload) => { dispatch( - showMessage(success_message, () => { - history.push( - `/app/summits/${currentSummit.id}/tax-types/${payload.response.id}` - ); - }) + showSuccessMessage(T.translate("edit_tax_type.tax_type_created")) ); + return payload; }); - } + }; export const deleteTaxType = (taxTypeId) => async (dispatch, getState) => { diff --git a/src/components/forms/tax-type-form.js b/src/components/forms/tax-type-form.js index a81b7955c..fd7bf3d2b 100644 --- a/src/components/forms/tax-type-form.js +++ b/src/components/forms/tax-type-form.js @@ -11,180 +11,191 @@ * limitations under the License. * */ -import React from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import T from "i18n-react/dist/i18n-react"; import "awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css"; -import { - Input, - SimpleLinkList -} from "openstack-uicore-foundation/lib/components"; +import Input from "openstack-uicore-foundation/lib/components/inputs/text-input"; +import SimpleLinkList from "openstack-uicore-foundation/lib/components/simple-link-list"; import { queryTicketTypes } from "openstack-uicore-foundation/lib/utils/query-actions"; import { isEmpty, scrollToError, shallowEqual } from "../../utils/methods"; import { MILLISECONDS_TO_SECONDS } from "../../utils/constants"; -class TaxTypeForm extends React.Component { - constructor(props) { - super(props); +const TaxTypeForm = ({ + entity: entityProp, + errors: errorsProp, + currentSummit, + onTicketLink, + onTicketUnLink, + onSubmit +}) => { + const [entity, setEntity] = useState({ ...entityProp }); + const [errors, setErrors] = useState(errorsProp); + const prevEntityRef = useRef(entityProp); + const prevErrorsRef = useRef(errorsProp); + + useEffect(() => { + scrollToError(errorsProp); - this.state = { - entity: { ...props.entity }, - errors: props.errors - }; - - this.handleTicketLink = this.handleTicketLink.bind(this); - this.handleTicketUnLink = this.handleTicketUnLink.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - } - - componentDidUpdate(prevProps) { const state = {}; - scrollToError(this.props.errors); - if (!shallowEqual(prevProps.entity, this.props.entity)) { - state.entity = { ...this.props.entity }; + if (!shallowEqual(prevEntityRef.current, entityProp)) { + state.entity = { ...entityProp }; state.errors = {}; } - if (!shallowEqual(prevProps.errors, this.props.errors)) { - state.errors = { ...this.props.errors }; + if (!shallowEqual(prevErrorsRef.current, errorsProp)) { + state.errors = { ...errorsProp }; } if (!isEmpty(state)) { - this.setState({ ...this.state, ...state }); + if (state.entity) { + setEntity(state.entity); + } + if (state.errors) { + setErrors(state.errors); + } } - } - - handleChange(ev) { - const newEntity = { ...this.state.entity }; - const newErrors = { ...this.state.errors }; - let { value, id } = ev.target; - if (ev.target.type === "checkbox") { - value = ev.target.checked; - } + prevEntityRef.current = entityProp; + prevErrorsRef.current = errorsProp; + }, [entityProp, errorsProp]); - if (ev.target.type === "datetime") { - value = value.valueOf() / MILLISECONDS_TO_SECONDS; - } + const handleChange = useCallback( + (ev) => { + const newEntity = { ...entity }; + const newErrors = { ...errors }; + let { value, id } = ev.target; - newErrors[id] = ""; - newEntity[id] = value; - this.setState({ entity: newEntity, errors: newErrors }); - } + if (ev.target.type === "checkbox") { + value = ev.target.checked; + } - handleSubmit(ev) { - const entity = { ...this.state.entity }; - ev.preventDefault(); + if (ev.target.type === "datetime") { + value = value.valueOf() / MILLISECONDS_TO_SECONDS; + } - this.props.onSubmit(entity); - } + newErrors[id] = ""; + newEntity[id] = value; + setEntity(newEntity); + setErrors(newErrors); + }, + [entity, errors] + ); + + const handleSubmit = useCallback( + (ev) => { + ev.preventDefault(); + onSubmit({ ...entity }); + }, + [entity, onSubmit] + ); + + const hasErrors = useCallback( + (field) => { + if (field in errors) { + return errors[field]; + } - hasErrors(field) { - const { errors } = this.state; - if (field in errors) { - return errors[field]; + return ""; + }, + [errors] + ); + + const handleTicketLink = useCallback( + (value) => { + onTicketLink(entity.id, value); + }, + [entity.id, onTicketLink] + ); + + const handleTicketUnLink = useCallback( + (valueId) => { + onTicketUnLink(entity.id, valueId); + }, + [entity.id, onTicketUnLink] + ); + + const ticketColumns = [ + { columnKey: "name", value: T.translate("edit_tax_type.name") }, + { + columnKey: "description", + value: T.translate("edit_tax_type.description") } - - return ""; - } - - handleTicketLink(value) { - const { entity } = this.state; - this.props.onTicketLink(entity.id, value); - } - - handleTicketUnLink(valueId) { - const { entity } = this.state; - this.props.onTicketUnLink(entity.id, valueId); - } - - render() { - const { entity } = this.state; - const { currentSummit } = this.props; - - const ticketColumns = [ - { columnKey: "name", value: T.translate("edit_tax_type.name") }, - { - columnKey: "description", - value: T.translate("edit_tax_type.description") - } - ]; - - const ticketOptions = { - title: T.translate("edit_tax_type.ticket_types"), - valueKey: "name", - labelKey: "name", - defaultOptions: true, - actions: { - search: (ev, callback) => - queryTicketTypes(currentSummit.id, { name: ev }, callback, "v2"), - delete: { onClick: this.handleTicketUnLink }, - add: { onClick: this.handleTicketLink } - } - }; - - return ( -
- -
-
- - -
+ ]; + + const ticketOptions = { + title: T.translate("edit_tax_type.ticket_types"), + valueKey: "name", + labelKey: "name", + defaultOptions: true, + actions: { + search: (ev, callback) => + queryTicketTypes(currentSummit.id, { name: ev }, callback, "v2"), + delete: { onClick: handleTicketUnLink }, + add: { onClick: handleTicketLink } + } + }; + + return ( + + +
+
+ +
-
-
- - -
-
- - -
+
+
+
+ +
- -
- {entity.id !== 0 && ( - + + +
+
+ +
+ {entity.id !== 0 && ( + + )} + +
+
+ - )} - -
-
- -
- - ); - } -} +
+ + ); +}; export default TaxTypeForm; diff --git a/src/layouts/tax-type-layout.js b/src/layouts/tax-type-layout.js index 28f326772..7eee0ea80 100644 --- a/src/layouts/tax-type-layout.js +++ b/src/layouts/tax-type-layout.js @@ -9,7 +9,7 @@ * 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 { Switch, Route, withRouter } from "react-router-dom"; @@ -18,40 +18,22 @@ import { Breadcrumb } from "react-breadcrumbs"; import Restrict from "../routes/restrict"; import TaxTypeListPage from "../pages/taxes/tax-type-list-page"; -import EditTaxTypePage from "../pages/taxes/edit-tax-type-page"; import NoMatchPage from "../pages/no-match-page"; -class TaxTypeLayout extends React.Component { - render() { - const { match } = this.props; - return ( -
- +const TaxTypeLayout = ({ match }) => ( +
+ - - - - - - -
- ); - } -} + + + + +
+); export default Restrict(withRouter(TaxTypeLayout), "taxes"); diff --git a/src/pages/taxes/edit-tax-type-page.js b/src/pages/taxes/edit-tax-type-page.js deleted file mode 100644 index 6df78e609..000000000 --- a/src/pages/taxes/edit-tax-type-page.js +++ /dev/null @@ -1,96 +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 TaxTypeForm from "../../components/forms/tax-type-form"; -import { getSummitById } from "../../actions/summit-actions"; -import { - getTaxType, - resetTaxTypeForm, - saveTaxType, - addTicketToTaxType, - removeTicketFromTaxType -} from "../../actions/tax-actions"; -import AddNewButton from "../../components/buttons/add-new-button"; - -class EditTaxTypePage extends React.Component { - constructor(props) { - const taxTypeId = props.match.params.tax_type_id; - super(props); - - if (!taxTypeId) { - props.resetTaxTypeForm(); - } else { - props.getTaxType(taxTypeId); - } - } - - componentDidUpdate(prevProps, prevState, snapshot) { - const oldId = prevProps.match.params.tax_type_id; - const newId = this.props.match.params.tax_type_id; - - if (newId !== oldId) { - if (!newId) { - this.props.resetTaxTypeForm(); - } else { - this.props.getTaxType(newId); - } - } - } - - render() { - const { currentSummit, entity, errors, match, history } = 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_tax_type.tax_type")} - -

-
- {currentSummit && ( - - )} -
- ); - } -} - -const mapStateToProps = ({ currentSummitState, currentTaxTypeState }) => ({ - currentSummit: currentSummitState.currentSummit, - ...currentTaxTypeState -}); - -export default connect(mapStateToProps, { - getSummitById, - getTaxType, - resetTaxTypeForm, - saveTaxType, - addTicketToTaxType, - removeTicketFromTaxType -})(EditTaxTypePage); diff --git a/src/pages/taxes/popup/tax-type-popup.js b/src/pages/taxes/popup/tax-type-popup.js new file mode 100644 index 000000000..0f34aad29 --- /dev/null +++ b/src/pages/taxes/popup/tax-type-popup.js @@ -0,0 +1,67 @@ +import React from "react"; +import PropTypes from "prop-types"; +import T from "i18n-react/dist/i18n-react"; +import Dialog from "@mui/material/Dialog"; +import DialogTitle from "@mui/material/DialogTitle"; +import DialogContent from "@mui/material/DialogContent"; +import IconButton from "@mui/material/IconButton"; +import Divider from "@mui/material/Divider"; +import Typography from "@mui/material/Typography"; +import CloseIcon from "@mui/icons-material/Close"; +import TaxTypeForm from "../../../components/forms/tax-type-form"; + +const TaxTypePopup = ({ + open, + onClose, + entity, + errors, + currentSummit, + onSubmit, + onTicketLink, + onTicketUnLink +}) => { + const title = entity?.id + ? T.translate("general.edit") + : T.translate("general.add"); + + return ( + + + + {title} {T.translate("edit_tax_type.tax_type")} + + + + + + + + + + + ); +}; + +TaxTypePopup.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + entity: PropTypes.object.isRequired, + errors: PropTypes.object, + currentSummit: PropTypes.object.isRequired, + onSubmit: PropTypes.func.isRequired, + onTicketLink: PropTypes.func.isRequired, + onTicketUnLink: PropTypes.func.isRequired +}; + +TaxTypePopup.defaultProps = { + errors: {} +}; + +export default TaxTypePopup; diff --git a/src/pages/taxes/tax-type-list-page.js b/src/pages/taxes/tax-type-list-page.js index d35dbe62b..96449a021 100644 --- a/src/pages/taxes/tax-type-list-page.js +++ b/src/pages/taxes/tax-type-list-page.js @@ -1,5 +1,5 @@ /** - * Copyright 2019 OpenStack Foundation + * Copyright 2026 OpenStack Foundation * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -9,133 +9,226 @@ * 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 Table from "openstack-uicore-foundation/lib/components/table"; +import Button from "@mui/material/Button"; +import Grid2 from "@mui/material/Grid2"; +import Typography from "@mui/material/Typography"; +import AddIcon from "@mui/icons-material/Add"; +import SearchInput from "openstack-uicore-foundation/lib/components/mui/search-input"; +import MuiTable from "openstack-uicore-foundation/lib/components/mui/table"; import { getSummitById } from "../../actions/summit-actions"; -import { getTaxTypes, deleteTaxType } from "../../actions/tax-actions"; - -class TaxTypeListPage 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.handleNewTaxType = this.handleNewTaxType.bind(this); - - this.state = {}; - } - - componentDidMount() { - const { currentSummit } = this.props; - if (currentSummit) { - this.props.getTaxTypes(); +import { + getTaxTypes, + deleteTaxType, + getTaxType, + resetTaxTypeForm, + saveTaxType, + addTicketToTaxType, + removeTicketFromTaxType +} from "../../actions/tax-actions"; +import TaxTypePopup from "./popup/tax-type-popup"; +import { DEFAULT_CURRENT_PAGE } from "../../utils/constants"; + +const TaxTypeListPage = ({ + currentSummit, + taxTypes, + term, + order, + orderDir, + perPage, + currentPage, + totalTaxTypes, + currentTaxType, + currentTaxTypeErrors, + getTaxTypes, + deleteTaxType, + getTaxType, + resetTaxTypeForm, + saveTaxType, + addTicketToTaxType, + removeTicketFromTaxType +}) => { + const [showTaxTypeModal, setShowTaxTypeModal] = useState(false); + + useEffect(() => { + if (currentSummit?.id) { + getTaxTypes(term, currentPage, perPage, order, orderDir); + } + }, [currentSummit]); + + const handleDelete = (taxTypeId) => { + deleteTaxType(taxTypeId); + }; + + const handleSort = (key, dir) => { + getTaxTypes(term, currentPage, perPage, key, dir); + }; + + const handleSearch = (searchTerm) => { + getTaxTypes(searchTerm, DEFAULT_CURRENT_PAGE, perPage, order, orderDir); + }; + + const handlePageChange = (page) => { + getTaxTypes(term, page, perPage, order, orderDir); + }; + + const handlePerPageChange = (newPerPage) => { + getTaxTypes(term, DEFAULT_CURRENT_PAGE, newPerPage, order, orderDir); + }; + + const handleOpenNewTaxType = () => { + resetTaxTypeForm(); + setShowTaxTypeModal(true); + }; + + const handleEdit = (row) => { + getTaxType(row.id).then(() => setShowTaxTypeModal(true)); + }; + + const handleSave = (entity) => { + saveTaxType(entity) + .then(() => getTaxTypes(term, currentPage, perPage, order, orderDir)) + .finally(() => setShowTaxTypeModal(false)); + }; + + const handleClosePopup = () => { + setShowTaxTypeModal(false); + resetTaxTypeForm(); + }; + + const columns = [ + { + columnKey: "name", + header: T.translate("tax_type_list.name"), + sortable: true + }, + { + columnKey: "rate", + header: T.translate("tax_type_list.rate") + }, + { + columnKey: "tax_id", + header: T.translate("tax_type_list.tax_id") } - } - - handleEdit(tax_type_id) { - const { currentSummit, history } = this.props; - history.push(`/app/summits/${currentSummit.id}/tax-types/${tax_type_id}`); - } - - handleDelete(taxTypeId) { - const { deleteTaxType, taxTypes } = this.props; - let taxType = taxTypes.find((t) => t.id === taxTypeId); - - Swal.fire({ - title: T.translate("general.are_you_sure"), - text: T.translate("tax_type_list.remove_warning") + " " + taxType.name, - type: "warning", - showCancelButton: true, - confirmButtonColor: "#DD6B55", - confirmButtonText: T.translate("general.yes_delete") - }).then(function (result) { - if (result.value) { - deleteTaxType(taxTypeId); - } - }); - } - - handleSort(index, key, dir, func) { - this.props.getTaxTypes(key, dir); - } - - handleNewTaxType(ev) { - const { currentSummit, history } = this.props; - history.push(`/app/summits/${currentSummit.id}/tax-types/new`); - } - - render() { - const { currentSummit, taxTypes, order, orderDir, totalTaxTypes } = - this.props; - - const columns = [ - { - columnKey: "name", - value: T.translate("tax_type_list.name"), - sortable: true - }, - { columnKey: "rate", value: T.translate("tax_type_list.rate") }, - { columnKey: "tax_id", value: T.translate("tax_type_list.tax_id") } - ]; - - const table_options = { - sortCol: order, - sortDir: orderDir, - actions: { - edit: { onClick: this.handleEdit }, - delete: { onClick: this.handleDelete } - } - }; - - if (!currentSummit.id) return
; - - return ( -
-

- {" "} - {T.translate("tax_type_list.tax_type_list")} ({totalTaxTypes}) -

-
-
- -
-
- - {taxTypes.length === 0 && ( -
{T.translate("tax_type_list.no_tax_types")}
- )} - - {taxTypes.length > 0 && ( - - )} - - ); - } -} - -const mapStateToProps = ({ currentSummitState, currentTaxTypeListState }) => ({ + + + + + + {taxTypes.length === 0 && ( +
{T.translate("tax_type_list.no_tax_types")}
+ )} + + {taxTypes.length > 0 && ( + item.name} + onSort={handleSort} + onPageChange={handlePageChange} + onPerPageChange={handlePerPageChange} + onEdit={handleEdit} + onDelete={handleDelete} + confirmButtonColor="error" + /> + )} + + {showTaxTypeModal && ( + + )} + + ); +}; + +const mapStateToProps = ({ + currentSummitState, + currentTaxTypeListState, + currentTaxTypeState +}) => ({ currentSummit: currentSummitState.currentSummit, - ...currentTaxTypeListState + ...currentTaxTypeListState, + currentTaxType: currentTaxTypeState.entity, + currentTaxTypeErrors: currentTaxTypeState.errors }); export default connect(mapStateToProps, { getSummitById, getTaxTypes, - deleteTaxType + deleteTaxType, + getTaxType, + resetTaxTypeForm, + saveTaxType, + addTicketToTaxType, + removeTicketFromTaxType })(TaxTypeListPage); diff --git a/src/reducers/taxes/tax-type-list-reducer.js b/src/reducers/taxes/tax-type-list-reducer.js index 494629820..736b76135 100644 --- a/src/reducers/taxes/tax-type-list-reducer.js +++ b/src/reducers/taxes/tax-type-list-reducer.js @@ -9,8 +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_TAX_TYPES, REQUEST_TAX_TYPES, @@ -18,12 +19,15 @@ import { } from "../../actions/tax-actions"; import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions"; -import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; const DEFAULT_STATE = { taxTypes: [], + term: "", order: "name", orderDir: 1, + currentPage: 1, + lastPage: 1, + perPage: 10, totalTaxTypes: 0 }; @@ -35,18 +39,30 @@ const taxTypeListReducer = (state = DEFAULT_STATE, action) => { return DEFAULT_STATE; } case REQUEST_TAX_TYPES: { - let { order, orderDir } = payload; + const { order, orderDir, page, ...rest } = payload; - return { ...state, order, orderDir }; + return { + ...state, + order, + orderDir, + currentPage: page, + ...rest + }; } case RECEIVE_TAX_TYPES: { - let { total } = payload.response; - let taxTypes = payload.response.data; + const { total, current_page, last_page } = payload.response; + const taxTypes = payload.response.data; - return { ...state, taxTypes: taxTypes, totalTaxTypes: total }; + return { + ...state, + taxTypes, + totalTaxTypes: total, + currentPage: current_page, + lastPage: last_page + }; } case TAX_TYPE_DELETED: { - let { taxTypeId } = payload; + const { taxTypeId } = payload; return { ...state, taxTypes: state.taxTypes.filter((t) => t.id !== taxTypeId) From 9e52a0acc309dbba7c32756abfb58df813eaac83 Mon Sep 17 00:00:00 2001 From: Priscila Moneo Date: Wed, 13 May 2026 12:30:38 -0300 Subject: [PATCH 2/2] fix: feedback from PR Signed-off-by: Priscila Moneo --- src/actions/tax-actions.js | 29 ++-- src/components/forms/tax-type-form.js | 156 +++++++++---------- src/layouts/summit-id-layout.js | 11 +- src/layouts/tax-type-layout.js | 39 ----- src/pages/taxes/popup/tax-type-popup.js | 2 +- src/pages/taxes/tax-type-list-page.js | 189 +++++++++++++----------- 6 files changed, 197 insertions(+), 229 deletions(-) delete mode 100644 src/layouts/tax-type-layout.js diff --git a/src/actions/tax-actions.js b/src/actions/tax-actions.js index 8d2b49121..e964c9ed3 100644 --- a/src/actions/tax-actions.js +++ b/src/actions/tax-actions.js @@ -89,7 +89,7 @@ export const getTaxTypes = createAction(RECEIVE_TAX_TYPES), `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/tax-types`, authErrorHandler, - { order: normalizedOrder, orderDir, page, perPage, term } + { order, orderDir, page, perPage, term } )(params)(dispatch).then(() => { dispatch(stopLoading()); }); @@ -146,21 +146,18 @@ export const saveTaxType = (entity) => async (dispatch, getState) => { dispatch(showSuccessMessage(T.translate("edit_tax_type.tax_type_saved"))); return payload; }); - } - return postRequest( - createAction(UPDATE_TAX_TYPE), - createAction(TAX_TYPE_ADDED), - `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/tax-types`, - normalizedEntity, - authErrorHandler, - entity - )(params)(dispatch).then((payload) => { - dispatch( - showSuccessMessage(T.translate("edit_tax_type.tax_type_created")) - ); - return payload; - }); - + } + return postRequest( + createAction(UPDATE_TAX_TYPE), + createAction(TAX_TYPE_ADDED), + `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/tax-types`, + normalizedEntity, + authErrorHandler, + entity + )(params)(dispatch).then((payload) => { + dispatch(showSuccessMessage(T.translate("edit_tax_type.tax_type_created"))); + return payload; + }); }; export const deleteTaxType = (taxTypeId) => async (dispatch, getState) => { diff --git a/src/components/forms/tax-type-form.js b/src/components/forms/tax-type-form.js index fd7bf3d2b..ff0def3f5 100644 --- a/src/components/forms/tax-type-form.js +++ b/src/components/forms/tax-type-form.js @@ -11,14 +11,16 @@ * limitations under the License. * */ -import React, { useCallback, useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import T from "i18n-react/dist/i18n-react"; -import "awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css"; -import Input from "openstack-uicore-foundation/lib/components/inputs/text-input"; +import TextField from "@mui/material/TextField"; +import Button from "@mui/material/Button"; +import Grid2 from "@mui/material/Grid2"; +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; import SimpleLinkList from "openstack-uicore-foundation/lib/components/simple-link-list"; import { queryTicketTypes } from "openstack-uicore-foundation/lib/utils/query-actions"; -import { isEmpty, scrollToError, shallowEqual } from "../../utils/methods"; -import { MILLISECONDS_TO_SECONDS } from "../../utils/constants"; +import { scrollToError, shallowEqual } from "../../utils/methods"; const TaxTypeForm = ({ entity: entityProp, @@ -30,52 +32,38 @@ const TaxTypeForm = ({ }) => { const [entity, setEntity] = useState({ ...entityProp }); const [errors, setErrors] = useState(errorsProp); - const prevEntityRef = useRef(entityProp); - const prevErrorsRef = useRef(errorsProp); useEffect(() => { - scrollToError(errorsProp); + setEntity({ ...entityProp }); + setErrors({}); + }, [entityProp.id]); - const state = {}; + useEffect(() => { + const nextTicketTypes = entityProp.ticket_types ?? []; - if (!shallowEqual(prevEntityRef.current, entityProp)) { - state.entity = { ...entityProp }; - state.errors = {}; - } + setEntity((prev) => { + if (prev.id !== entityProp.id) return prev; + if (shallowEqual(prev.ticket_types ?? [], nextTicketTypes)) return prev; - if (!shallowEqual(prevErrorsRef.current, errorsProp)) { - state.errors = { ...errorsProp }; - } + return { + ...prev, + ticket_types: [...nextTicketTypes] + }; + }); + }, [entityProp.id, entityProp.ticket_types]); - if (!isEmpty(state)) { - if (state.entity) { - setEntity(state.entity); - } - if (state.errors) { - setErrors(state.errors); - } + useEffect(() => { + scrollToError(errorsProp); + if (!shallowEqual(errors, errorsProp)) { + setErrors({ ...errorsProp }); } - - prevEntityRef.current = entityProp; - prevErrorsRef.current = errorsProp; - }, [entityProp, errorsProp]); + }, [errorsProp]); const handleChange = useCallback( (ev) => { - const newEntity = { ...entity }; - const newErrors = { ...errors }; - let { value, id } = ev.target; - - if (ev.target.type === "checkbox") { - value = ev.target.checked; - } - - if (ev.target.type === "datetime") { - value = value.valueOf() / MILLISECONDS_TO_SECONDS; - } - - newErrors[id] = ""; - newEntity[id] = value; + const { value, id } = ev.target; + const newEntity = { ...entity, [id]: value }; + const newErrors = { ...errors, [id]: "" }; setEntity(newEntity); setErrors(newErrors); }, @@ -137,64 +125,66 @@ const TaxTypeForm = ({ }; return ( -
+ -
-
- - + + -
-
-
-
- - + + + + + -
-
- - + + -
-
+ + -
{entity.id !== 0 && ( - + + + )} -
-
- -
-
- + + + +
); }; diff --git a/src/layouts/summit-id-layout.js b/src/layouts/summit-id-layout.js index 02e81c7ce..816bc92b7 100644 --- a/src/layouts/summit-id-layout.js +++ b/src/layouts/summit-id-layout.js @@ -48,7 +48,9 @@ const LocationLayout = React.lazy(() => import("./location-layout")); const SignagePage = React.lazy(() => import("../pages/signage")); const RsvpTemplateLayout = React.lazy(() => import("./rsvp-template-layout")); const TicketTypeLayout = React.lazy(() => import("./ticket-type-layout")); -const TaxTypeLayout = React.lazy(() => import("./tax-type-layout")); +const TaxTypeListPage = React.lazy(() => + import("../pages/taxes/tax-type-list-page") +); const RefundPolicyListPage = React.lazy(() => import("../pages/tickets/refund-policy-list-page") ); @@ -205,7 +207,12 @@ const SummitIdLayout = ({ currentSummit, loading, match, ...props }) => { path={`${match.url}/ticket-types`} component={TicketTypeLayout} /> - + ( -
- - - - - - -
-); - -export default Restrict(withRouter(TaxTypeLayout), "taxes"); diff --git a/src/pages/taxes/popup/tax-type-popup.js b/src/pages/taxes/popup/tax-type-popup.js index 0f34aad29..2a09c325f 100644 --- a/src/pages/taxes/popup/tax-type-popup.js +++ b/src/pages/taxes/popup/tax-type-popup.js @@ -30,7 +30,7 @@ const TaxTypePopup = ({ {title} {T.translate("edit_tax_type.tax_type")} - + diff --git a/src/pages/taxes/tax-type-list-page.js b/src/pages/taxes/tax-type-list-page.js index 96449a021..bf6f227fb 100644 --- a/src/pages/taxes/tax-type-list-page.js +++ b/src/pages/taxes/tax-type-list-page.js @@ -13,6 +13,7 @@ import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; +import { Breadcrumb } from "react-breadcrumbs"; import T from "i18n-react/dist/i18n-react"; import Button from "@mui/material/Button"; import Grid2 from "@mui/material/Grid2"; @@ -20,7 +21,7 @@ import Typography from "@mui/material/Typography"; import AddIcon from "@mui/icons-material/Add"; import SearchInput from "openstack-uicore-foundation/lib/components/mui/search-input"; import MuiTable from "openstack-uicore-foundation/lib/components/mui/table"; -import { getSummitById } from "../../actions/summit-actions"; +import Restrict from "../../routes/restrict"; import { getTaxTypes, deleteTaxType, @@ -50,7 +51,8 @@ const TaxTypeListPage = ({ resetTaxTypeForm, saveTaxType, addTicketToTaxType, - removeTicketFromTaxType + removeTicketFromTaxType, + match }) => { const [showTaxTypeModal, setShowTaxTypeModal] = useState(false); @@ -90,9 +92,10 @@ const TaxTypeListPage = ({ }; const handleSave = (entity) => { - saveTaxType(entity) - .then(() => getTaxTypes(term, currentPage, perPage, order, orderDir)) - .finally(() => setShowTaxTypeModal(false)); + saveTaxType(entity).then(() => { + getTaxTypes(term, currentPage, perPage, order, orderDir); + setShowTaxTypeModal(false); + }); }; const handleClosePopup = () => { @@ -124,90 +127,98 @@ const TaxTypeListPage = ({ if (!currentSummit?.id) return
; return ( -
-

{T.translate("tax_type_list.tax_type_list")}

- - + - - - {totalTaxTypes} {T.translate("general.items")} - - + /> +
+

{T.translate("tax_type_list.tax_type_list")}

+ - - + + {totalTaxTypes} {T.translate("general.items")} + - - + + + + + + + + - - - {taxTypes.length === 0 && ( -
{T.translate("tax_type_list.no_tax_types")}
- )} - - {taxTypes.length > 0 && ( - item.name} - onSort={handleSort} - onPageChange={handlePageChange} - onPerPageChange={handlePerPageChange} - onEdit={handleEdit} - onDelete={handleDelete} - confirmButtonColor="error" - /> - )} - - {showTaxTypeModal && ( - - )} -
+ + {taxTypes.length === 0 && ( +
{T.translate("tax_type_list.no_tax_types")}
+ )} + + {taxTypes.length > 0 && ( + item.name} + onSort={handleSort} + onPageChange={handlePageChange} + onPerPageChange={handlePerPageChange} + onEdit={handleEdit} + onDelete={handleDelete} + confirmButtonColor="error" + /> + )} + + {showTaxTypeModal && ( + + )} +
+ ); }; @@ -222,13 +233,15 @@ const mapStateToProps = ({ currentTaxTypeErrors: currentTaxTypeState.errors }); -export default connect(mapStateToProps, { - getSummitById, - getTaxTypes, - deleteTaxType, - getTaxType, - resetTaxTypeForm, - saveTaxType, - addTicketToTaxType, - removeTicketFromTaxType -})(TaxTypeListPage); +export default Restrict( + connect(mapStateToProps, { + getTaxTypes, + deleteTaxType, + getTaxType, + resetTaxTypeForm, + saveTaxType, + addTicketToTaxType, + removeTicketFromTaxType + })(TaxTypeListPage), + "taxes" +);