diff --git a/src/actions/event-type-actions.js b/src/actions/event-type-actions.js index 502194260..2d72c551a 100644 --- a/src/actions/event-type-actions.js +++ b/src/actions/event-type-actions.js @@ -24,7 +24,6 @@ import { authErrorHandler, escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; -import history from "../history"; import { getAccessTokenSafely } from "../utils/methods"; import { DEFAULT_PER_PAGE, DEFAULT_CURRENT_PAGE } from "../utils/constants"; @@ -124,7 +123,7 @@ export const saveEventType = (entity) => async (dispatch, getState) => { const params = { access_token: accessToken }; if (entity.id) { - putRequest( + return putRequest( createAction(UPDATE_EVENT_TYPE), createAction(EVENT_TYPE_UPDATED), `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/event-types/${entity.id}`, @@ -135,31 +134,27 @@ export const saveEventType = (entity) => async (dispatch, getState) => { dispatch( showSuccessMessage(T.translate("edit_event_type.event_type_saved")) ); + return dispatch(getEventTypes()); }); - } else { + } const success_message = { title: T.translate("general.done"), html: T.translate("edit_event_type.event_type_created"), type: "success" }; - postRequest( + return postRequest( createAction(UPDATE_EVENT_TYPE), createAction(EVENT_TYPE_ADDED), `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/event-types`, normalizedEntity, authErrorHandler, entity - )(params)(dispatch).then((payload) => { - dispatch( - showMessage(success_message, () => { - history.push( - `/app/summits/${currentSummit.id}/event-types/${payload.response.id}` - ); - }) - ); + )(params)(dispatch).then(() => { + dispatch(showMessage(success_message, () => {})); + return dispatch(getEventTypes()); }); - } + }; export const deleteEventType = (eventTypeId) => async (dispatch, getState) => { diff --git a/src/components/forms/event-type-form.js b/src/components/forms/event-type-form.js index dea75dc08..dca560548 100644 --- a/src/components/forms/event-type-form.js +++ b/src/components/forms/event-type-form.js @@ -120,7 +120,7 @@ class EventTypeForm extends React.Component { render() { const { entity, errors, showSection } = this.state; - const { getMediaUploads, currentSummit } = this.props; + const { getMediaUploads, currentSummit, isSaving } = this.props; const event_types_ddl = [ { label: "Presentation", value: "PRESENTATION_TYPE" }, { label: "Event", value: "EVENT_TYPE" } @@ -618,6 +618,7 @@ class EventTypeForm extends React.Component { onClick={this.handleSubmit} className="btn btn-primary pull-right" value={T.translate("general.save")} + disabled={isSaving} /> diff --git a/src/layouts/event-type-layout.js b/src/layouts/event-type-layout.js index 579798981..b4ec1d484 100644 --- a/src/layouts/event-type-layout.js +++ b/src/layouts/event-type-layout.js @@ -9,49 +9,36 @@ * 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 PropTypes from "prop-types"; import { Switch, Route, withRouter } from "react-router-dom"; import T from "i18n-react/dist/i18n-react"; import { Breadcrumb } from "react-breadcrumbs"; import Restrict from "../routes/restrict"; -import EditEventTypePage from "../pages/events/edit-event-type-page"; import EventTypeListPage from "../pages/events/event-type-list-page"; import NoMatchPage from "../pages/no-match-page"; -class EventTypeLayout extends React.Component { - render() { - const { match } = this.props; - return ( -
- +const EventTypeLayout = ({ match }) => ( +
+ - - - - - - -
- ); - } -} + + + + +
+); + +EventTypeLayout.propTypes = { + match: PropTypes.shape({ url: PropTypes.string }).isRequired +}; export default Restrict(withRouter(EventTypeLayout), "events"); diff --git a/src/pages/events/components/event-type-dialog.js b/src/pages/events/components/event-type-dialog.js new file mode 100644 index 000000000..31f9e2b3f --- /dev/null +++ b/src/pages/events/components/event-type-dialog.js @@ -0,0 +1,135 @@ +/** + * 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 + * 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 PropTypes from "prop-types"; +import T from "i18n-react/dist/i18n-react"; +import Dialog from "@mui/material/Dialog"; +import DialogContent from "@mui/material/DialogContent"; +import DialogTitle from "@mui/material/DialogTitle"; +import Divider from "@mui/material/Divider"; +import IconButton from "@mui/material/IconButton"; +import CloseIcon from "@mui/icons-material/Close"; +import EventTypeForm from "../../../components/forms/event-type-form"; +import { saveEventType as saveEventTypeAction } from "../../../actions/event-type-actions"; +import { + linkToPresentationType as linkToPresentationTypeAction, + unlinkFromPresentationType as unlinkFromPresentationTypeAction, + queryMediaUploads +} from "../../../actions/media-upload-actions"; + +const EventTypeDialog = ({ + currentSummit, + entity, + errors, + loading, + onClose, + saveEventType, + linkToPresentationType, + unlinkFromPresentationType +}) => { + const isSaving = Boolean(loading); + + const handleClose = () => { + if (isSaving) return; + onClose(); + }; + + const handleOnSave = (eventTypeEntity) => { + if (isSaving) return; + saveEventType(eventTypeEntity) + .then(() => onClose()) + .catch(() => { + // keep dialog open on save error to preserve user input + }); + }; + + const getMediaUploads = (input, callback) => { + if (!input) return Promise.resolve({ options: [] }); + return queryMediaUploads(currentSummit.id, input, callback); + }; + + const isEdit = Boolean(entity.id); + const title = `${T.translate( + isEdit ? "general.edit" : "general.add" + )} ${T.translate("edit_event_type.event_type")}`; + + return ( + + + {title} + + + + + + + + + + ); +}; + +EventTypeDialog.propTypes = { + currentSummit: PropTypes.shape({ id: PropTypes.number }).isRequired, + entity: PropTypes.shape({ id: PropTypes.number }).isRequired, + errors: PropTypes.shape({}), + loading: PropTypes.number, + onClose: PropTypes.func.isRequired, + saveEventType: PropTypes.func.isRequired, + linkToPresentationType: PropTypes.func.isRequired, + unlinkFromPresentationType: PropTypes.func.isRequired +}; + +EventTypeDialog.defaultProps = { + errors: {}, + loading: 0 +}; + +const mapStateToProps = ({ + baseState, + currentSummitState, + currentEventTypeState +}) => ({ + currentSummit: currentSummitState.currentSummit, + loading: baseState.loading, + ...currentEventTypeState +}); + +export default connect(mapStateToProps, { + saveEventType: saveEventTypeAction, + linkToPresentationType: linkToPresentationTypeAction, + unlinkFromPresentationType: unlinkFromPresentationTypeAction +})(EventTypeDialog); diff --git a/src/pages/events/edit-event-type-page.js b/src/pages/events/edit-event-type-page.js deleted file mode 100644 index 7fe3782c4..000000000 --- a/src/pages/events/edit-event-type-page.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright 2017 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 { Breadcrumb } from "react-breadcrumbs"; -import EventTypeForm from "../../components/forms/event-type-form"; -import { getSummitById } from "../../actions/summit-actions"; -import { - getEventType, - resetEventTypeForm, - saveEventType -} from "../../actions/event-type-actions"; -import "../../styles/edit-event-type-page.less"; -import { - queryMediaUploads, - linkToPresentationType, - unlinkFromPresentationType -} from "../../actions/media-upload-actions"; -import AddNewButton from "../../components/buttons/add-new-button"; - -class EditEventTypePage extends React.Component { - constructor(props) { - const eventTypeId = props.match.params.event_type_id; - super(props); - - if (!eventTypeId) { - props.resetEventTypeForm(); - } else { - props.getEventType(eventTypeId); - } - - this.getMediaUploads = this.getMediaUploads.bind(this); - } - - componentDidUpdate(prevProps, prevState, snapshot) { - const oldId = prevProps.match.params.event_type_id; - const newId = this.props.match.params.event_type_id; - - if (oldId !== newId) { - if (!newId) { - this.props.resetEventTypeForm(); - } else { - this.props.getEventType(newId); - } - } - } - - getMediaUploads(input, callback) { - const { currentSummit } = this.props; - - if (!input) { - return Promise.resolve({ options: [] }); - } - - return queryMediaUploads(currentSummit.id, input, callback); - } - - render() { - const { currentSummit, 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_event_type.event_type")} - -

-
- {currentSummit && ( - - )} -
- ); - } -} - -const mapStateToProps = ({ currentSummitState, currentEventTypeState }) => ({ - currentSummit: currentSummitState.currentSummit, - ...currentEventTypeState -}); - -export default connect(mapStateToProps, { - getSummitById, - getEventType, - resetEventTypeForm, - saveEventType, - linkToPresentationType, - unlinkFromPresentationType -})(EditEventTypePage); diff --git a/src/pages/events/event-type-list-page.js b/src/pages/events/event-type-list-page.js index 72d2e2259..6157e9e9d 100644 --- a/src/pages/events/event-type-list-page.js +++ b/src/pages/events/event-type-list-page.js @@ -11,7 +11,7 @@ * limitations under the License. * */ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import PropTypes from "prop-types"; import T from "i18n-react/dist/i18n-react"; @@ -23,10 +23,13 @@ import MuiTable from "openstack-uicore-foundation/lib/components/mui/table"; import SearchInput from "openstack-uicore-foundation/lib/components/mui/search-input"; import { getEventTypes as getEventTypesAction, + getEventType as getEventTypeAction, deleteEventType as deleteEventTypeAction, - seedEventTypes as seedEventTypesAction + seedEventTypes as seedEventTypesAction, + resetEventTypeForm as resetEventTypeFormAction } from "../../actions/event-type-actions"; import { DEFAULT_CURRENT_PAGE } from "../../utils/constants"; +import EventTypeDialog from "./components/event-type-dialog"; const EventTypeListPage = ({ currentSummit, @@ -38,20 +41,24 @@ const EventTypeListPage = ({ orderDir, totalEventTypes, getEventTypes, + getEventType, deleteEventType, seedEventTypes, - history + resetEventTypeForm }) => { + const [openPopup, setOpenPopup] = useState(null); + useEffect(() => { getEventTypes(); }, []); const handleEdit = (row) => { - history.push(`/app/summits/${currentSummit.id}/event-types/${row.id}`); + getEventType(row.id).then(() => setOpenPopup("eventTypeForm")); }; const handleNew = () => { - history.push(`/app/summits/${currentSummit.id}/event-types/new`); + resetEventTypeForm(); + setOpenPopup("eventTypeForm"); }; const handleDelete = (eventTypeId) => { @@ -175,6 +182,10 @@ const EventTypeListPage = ({ {eventTypes.length === 0 && (
{T.translate("event_type_list.no_items")}
)} + + {openPopup === "eventTypeForm" && ( + setOpenPopup(null)} /> + )} ); }; @@ -196,9 +207,10 @@ EventTypeListPage.propTypes = { orderDir: PropTypes.number, totalEventTypes: PropTypes.number, getEventTypes: PropTypes.func.isRequired, + getEventType: PropTypes.func.isRequired, deleteEventType: PropTypes.func.isRequired, seedEventTypes: PropTypes.func.isRequired, - history: PropTypes.shape({ push: PropTypes.func }).isRequired + resetEventTypeForm: PropTypes.func.isRequired }; EventTypeListPage.defaultProps = { @@ -220,6 +232,8 @@ const mapStateToProps = ({ export default connect(mapStateToProps, { getEventTypes: getEventTypesAction, + getEventType: getEventTypeAction, deleteEventType: deleteEventTypeAction, - seedEventTypes: seedEventTypesAction + seedEventTypes: seedEventTypesAction, + resetEventTypeForm: resetEventTypeFormAction })(EventTypeListPage);