{
)}
{/* cfp_settings kept in DOM always - contains TextEditorV3 */}
@@ -300,8 +420,12 @@ SelectionPlanForm.propTypes = {
track_groups: PropTypes.arrayOf(PropTypes.shape({})),
event_types: PropTypes.arrayOf(PropTypes.shape({})),
extra_questions: PropTypes.arrayOf(PropTypes.shape({})),
+ extraQuestionsOrder: PropTypes.string,
+ extraQuestionsOrderDir: PropTypes.number,
track_chair_rating_types: PropTypes.arrayOf(PropTypes.shape({})),
- allowed_presentation_action_types: PropTypes.arrayOf(PropTypes.shape({}))
+ allowed_presentation_action_types: PropTypes.arrayOf(PropTypes.shape({})),
+ actionTypesOrder: PropTypes.string,
+ actionTypesOrderDir: PropTypes.number
}).isRequired,
errors: PropTypes.shape({}),
currentSummit: PropTypes.shape({
@@ -309,10 +433,6 @@ SelectionPlanForm.propTypes = {
time_zone_id: PropTypes.string.isRequired,
slug: PropTypes.string
}).isRequired,
- extraQuestionsOrder: PropTypes.string.isRequired,
- extraQuestionsOrderDir: PropTypes.number.isRequired,
- actionTypesOrder: PropTypes.string.isRequired,
- actionTypesOrderDir: PropTypes.number.isRequired,
allowedMembers: PropTypes.shape({
data: PropTypes.arrayOf(
PropTypes.shape({ id: PropTypes.number, email: PropTypes.string })
@@ -320,31 +440,53 @@ SelectionPlanForm.propTypes = {
currentPage: PropTypes.number.isRequired,
lastPage: PropTypes.number.isRequired
}).isRequired,
+ history: PropTypes.shape({ push: PropTypes.func.isRequired }).isRequired,
onSave: PropTypes.func.isRequired,
- onTrackGroupLink: PropTypes.func.isRequired,
- onTrackGroupUnLink: PropTypes.func.isRequired,
- onAddEventType: PropTypes.func.isRequired,
- onDeleteEventType: PropTypes.func.isRequired,
- onAddRatingType: PropTypes.func.isRequired,
- onEditRatingType: PropTypes.func.isRequired,
- onDeleteRatingType: PropTypes.func.isRequired,
- onEditExtraQuestion: PropTypes.func.isRequired,
- onDeleteExtraQuestion: PropTypes.func.isRequired,
- onAddNewExtraQuestion: PropTypes.func.isRequired,
- onAssignExtraQuestion2SelectionPlan: PropTypes.func.isRequired,
- onAssignProgressFlag2SelectionPlan: PropTypes.func.isRequired,
- onUnassignProgressFlag: PropTypes.func.isRequired,
- onUpdateProgressFlagOrder: PropTypes.func.isRequired,
- onUpdateRatingTypeOrder: PropTypes.func.isRequired,
- updateExtraQuestionOrder: PropTypes.func.isRequired,
- onImportAllowedMembers: PropTypes.func.isRequired,
- onAllowedMemberAdd: PropTypes.func.isRequired,
- onAllowedMemberDelete: PropTypes.func.isRequired,
- onAllowedMembersPageChange: PropTypes.func.isRequired
+ addTrackGroupToSelectionPlan: PropTypes.func.isRequired,
+ removeTrackGroupFromSelectionPlan: PropTypes.func.isRequired,
+ addEventTypeSelectionPlan: PropTypes.func.isRequired,
+ deleteEventTypeSelectionPlan: PropTypes.func.isRequired,
+ deleteSelectionPlanExtraQuestion: PropTypes.func.isRequired,
+ updateSelectionPlanExtraQuestionOrder: PropTypes.func.isRequired,
+ assignExtraQuestion2SelectionPlan: PropTypes.func.isRequired,
+ deleteRatingType: PropTypes.func.isRequired,
+ updateRatingTypeOrder: PropTypes.func.isRequired,
+ assignProgressFlag2SelectionPlan: PropTypes.func.isRequired,
+ unassignProgressFlagFromSelectionPlan: PropTypes.func.isRequired,
+ updateProgressFlagOrder: PropTypes.func.isRequired,
+ addAllowedMemberToSelectionPlan: PropTypes.func.isRequired,
+ removeAllowedMemberFromSelectionPlan: PropTypes.func.isRequired,
+ getAllowedMembers: PropTypes.func.isRequired,
+ importAllowedMembersCSV: PropTypes.func.isRequired
};
SelectionPlanForm.defaultProps = {
errors: {}
};
-export default SelectionPlanForm;
+const mapStateToProps = ({
+ currentSummitState,
+ currentSelectionPlanState
+}) => ({
+ currentSummit: currentSummitState.currentSummit,
+ ...currentSelectionPlanState
+});
+
+export default connect(mapStateToProps, {
+ addTrackGroupToSelectionPlan,
+ removeTrackGroupFromSelectionPlan,
+ addEventTypeSelectionPlan,
+ deleteEventTypeSelectionPlan,
+ deleteSelectionPlanExtraQuestion,
+ updateSelectionPlanExtraQuestionOrder,
+ assignExtraQuestion2SelectionPlan,
+ deleteRatingType,
+ updateRatingTypeOrder,
+ assignProgressFlag2SelectionPlan,
+ unassignProgressFlagFromSelectionPlan,
+ updateProgressFlagOrder,
+ addAllowedMemberToSelectionPlan,
+ removeAllowedMemberFromSelectionPlan,
+ getAllowedMembers,
+ importAllowedMembersCSV
+})(SelectionPlanForm);
diff --git a/src/layouts/selection-plan-id-layout.js b/src/layouts/selection-plan-id-layout.js
index 04e41a5ed..583c7c800 100644
--- a/src/layouts/selection-plan-id-layout.js
+++ b/src/layouts/selection-plan-id-layout.js
@@ -1,9 +1,10 @@
-import React, { Suspense, useEffect } from "react";
+import React, { Suspense, useEffect, useState } from "react";
import { connect } from "react-redux";
import { Redirect, Route, Switch } from "react-router-dom";
import { Breadcrumb } from "react-breadcrumbs";
import T from "i18n-react";
import AjaxLoader from "openstack-uicore-foundation/lib/components/ajaxloader";
+import NoMatchPage from "../pages/no-match-page";
import {
getSelectionPlan,
resetSelectionPlanForm
@@ -11,6 +12,9 @@ import {
import { getMarketingSettingsBySelectionPlan } from "../actions/marketing-actions";
import { MAX_PER_PAGE } from "../utils/constants";
+const EditSelectionPlanPage = React.lazy(() =>
+ import("../pages/selection-plans/edit-selection-plan-page")
+);
const SelectionPlanExtraQuestionsLayout = React.lazy(() =>
import("./selection-plan-extra-questions-layout")
);
@@ -26,31 +30,53 @@ const SelectionPlanIdLayout = ({
resetSelectionPlanForm,
getMarketingSettingsBySelectionPlan
}) => {
+ const [hasLoaded, setHasLoaded] = useState(false);
+ const [hasError, setHasError] = useState(false);
const selectionPlanId = match.params.selection_plan_id;
const breadcrumb = selectionPlanId
? currentSelectionPlan.name
: T.translate("general.new");
useEffect(() => {
+ setHasLoaded(false);
+ setHasError(false);
if (!selectionPlanId) {
resetSelectionPlanForm();
+ setHasLoaded(true);
} else {
- getSelectionPlan(selectionPlanId).then(() =>
- getMarketingSettingsBySelectionPlan(
- selectionPlanId,
- null,
- 1,
- MAX_PER_PAGE
+ getSelectionPlan(selectionPlanId)
+ .then(() =>
+ getMarketingSettingsBySelectionPlan(
+ selectionPlanId,
+ null,
+ 1,
+ MAX_PER_PAGE
+ )
)
- );
+ .then(() => setHasLoaded(true))
+ .catch(() => setHasError(true));
}
}, [selectionPlanId]);
+ if (hasError) {
+ return ;
+ }
+
+ if (!hasLoaded || currentSelectionPlan.id !== Number(selectionPlanId || 0)) {
+ return null;
+ }
+
return (
}>
+
-
+
diff --git a/src/layouts/selection-plan-layout.js b/src/layouts/selection-plan-layout.js
index 3a4043345..b5db7329c 100644
--- a/src/layouts/selection-plan-layout.js
+++ b/src/layouts/selection-plan-layout.js
@@ -12,14 +12,14 @@
* */
import React from "react";
-import { connect } from "react-redux";
-import { Redirect, Route, Switch } from "react-router-dom";
+import { Route, Switch } from "react-router-dom";
import T from "i18n-react/dist/i18n-react";
import { Breadcrumb } from "react-breadcrumbs";
import SelectionPlanListPage from "../pages/selection-plans/selection-plan-list-page";
import SelectionPlanIdLayout from "./selection-plan-id-layout";
+import NoMatchPage from "../pages/no-match-page";
-const SelectionPlanLayout = ({ match, currentSummit }) => (
+const SelectionPlanLayout = ({ match }) => (
(
strict
exact
path={`${match.url}/new`}
- render={() => }
+ component={SelectionPlanIdLayout}
/>
-
+
);
-const mapStateToProps = ({ currentSummitState }) => ({
- ...currentSummitState
-});
-
-export default connect(mapStateToProps, {})(SelectionPlanLayout);
+export default SelectionPlanLayout;
diff --git a/src/pages/selection-plans/__tests__/edit-selection-plan-page.test.js b/src/pages/selection-plans/__tests__/edit-selection-plan-page.test.js
new file mode 100644
index 000000000..54b42bc16
--- /dev/null
+++ b/src/pages/selection-plans/__tests__/edit-selection-plan-page.test.js
@@ -0,0 +1,207 @@
+/**
+ * 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 { screen, act } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { MemoryRouter } from "react-router-dom";
+import flushPromises from "flush-promises";
+import { renderWithRedux } from "../../../utils/test-utils";
+import EditSelectionPlanPage from "../edit-selection-plan-page";
+import {
+ saveSelectionPlan,
+ saveSelectionPlanSettings
+} from "../../../actions/selection-plan-actions";
+
+jest.mock("../../../actions/selection-plan-actions", () => ({
+ __esModule: true,
+ saveSelectionPlan: jest.fn(),
+ saveSelectionPlanSettings: jest.fn()
+}));
+
+jest.mock("i18n-react/dist/i18n-react", () => ({
+ __esModule: true,
+ default: { translate: (key) => key }
+}));
+
+// Stub the real form: it needs a fuller entity/marketing-settings shape than
+// set up here. Exposes onSave so the page's save/redirect logic can be
+// exercised directly. The form now connects to the store itself for its
+// entity, so the stub reads it from there too, mirroring the real component.
+jest.mock("../../../components/forms/selection-plan-form", () => {
+ const { useSelector } = jest.requireActual("react-redux");
+ return {
+ __esModule: true,
+ default: ({ onSave }) => {
+ const entity = useSelector(
+ (state) => state.currentSelectionPlanState.entity
+ );
+ return (
+
+ onSave({ id: entity.id, marketing_settings: {} })}
+ >
+ save
+
+
+ );
+ }
+ };
+});
+
+const mockHistory = { push: jest.fn() };
+
+const stateFor = (entity) => ({
+ currentSummitState: { currentSummit: { id: 1 } },
+ currentSelectionPlanState: {
+ entity,
+ allowedMembers: { data: [], currentPage: 1, lastPage: 1 },
+ errors: {}
+ }
+});
+
+describe("EditSelectionPlanPage", () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("saves and redirects to the list on create", async () => {
+ saveSelectionPlan.mockReturnValue(() => Promise.resolve({ id: 42 }));
+ saveSelectionPlanSettings.mockReturnValue(() => Promise.resolve());
+
+ renderWithRedux(
+
+
+ ,
+ {
+ initialState: stateFor({ id: 0 })
+ }
+ );
+
+ await act(async () => {
+ await userEvent.click(screen.getByRole("button", { name: "save" }));
+ await flushPromises();
+ });
+
+ expect(saveSelectionPlan).toHaveBeenCalledWith({
+ id: 0,
+ marketing_settings: {}
+ });
+ expect(saveSelectionPlanSettings).toHaveBeenCalledWith({}, 42);
+ expect(mockHistory.push).toHaveBeenCalledWith(
+ "/app/summits/1/selection-plans"
+ );
+ });
+
+ it("saves an existing plan and redirects to the list", async () => {
+ saveSelectionPlan.mockReturnValue(() => Promise.resolve({ id: 5 }));
+ saveSelectionPlanSettings.mockReturnValue(() => Promise.resolve());
+
+ renderWithRedux(
+
+
+ ,
+ {
+ initialState: stateFor({ id: 5 })
+ }
+ );
+
+ await act(async () => {
+ await userEvent.click(screen.getByRole("button", { name: "save" }));
+ await flushPromises();
+ });
+
+ expect(saveSelectionPlan).toHaveBeenCalledWith({
+ id: 5,
+ marketing_settings: {}
+ });
+ expect(mockHistory.push).toHaveBeenCalledWith(
+ "/app/summits/1/selection-plans"
+ );
+ });
+
+ it("still redirects and re-enables the save button when saving marketing settings fails", async () => {
+ saveSelectionPlan.mockReturnValue(() => Promise.resolve({ id: 42 }));
+ saveSelectionPlanSettings.mockReturnValue(() =>
+ Promise.reject(new Error("settings failed"))
+ );
+
+ renderWithRedux(
+
+
+ ,
+ {
+ initialState: stateFor({ id: 0 })
+ }
+ );
+
+ await act(async () => {
+ await userEvent.click(screen.getByRole("button", { name: "save" }));
+ await flushPromises();
+ await flushPromises();
+ });
+
+ expect(mockHistory.push).toHaveBeenCalledWith(
+ "/app/summits/1/selection-plans"
+ );
+ expect(
+ screen.getByRole("button", { name: "general.save" })
+ ).not.toBeDisabled();
+ });
+
+ it("does not save twice while a save is already in flight", async () => {
+ let resolveSave;
+ saveSelectionPlan.mockReturnValue(
+ () =>
+ new Promise((resolve) => {
+ resolveSave = resolve;
+ })
+ );
+
+ renderWithRedux(
+
+
+ ,
+ {
+ initialState: stateFor({ id: 5 })
+ }
+ );
+
+ const saveButton = screen.getByRole("button", { name: "save" });
+ await userEvent.click(saveButton);
+ await userEvent.click(saveButton);
+
+ expect(saveSelectionPlan).toHaveBeenCalledTimes(1);
+
+ await act(async () => {
+ resolveSave({ id: 5 });
+ await flushPromises();
+ });
+ });
+
+ it("does not show the Add New action for an unsaved plan", () => {
+ renderWithRedux(
+
+
+ ,
+ {
+ initialState: stateFor({ id: 0 })
+ }
+ );
+
+ expect(
+ screen.queryByRole("button", { name: "general.add_new" })
+ ).not.toBeInTheDocument();
+ });
+});
diff --git a/src/pages/selection-plans/__tests__/selection-plan-list-page.test.js b/src/pages/selection-plans/__tests__/selection-plan-list-page.test.js
index 76207e585..8f025236b 100644
--- a/src/pages/selection-plans/__tests__/selection-plan-list-page.test.js
+++ b/src/pages/selection-plans/__tests__/selection-plan-list-page.test.js
@@ -7,25 +7,12 @@ import { renderWithRedux } from "../../../utils/test-utils";
import SelectionPlanListPage from "../selection-plan-list-page";
import {
getSelectionPlans,
- getSelectionPlan,
- deleteSelectionPlan,
- resetSelectionPlanForm,
- saveSelectionPlan,
- saveSelectionPlanSettings
+ deleteSelectionPlan
} from "../../../actions/selection-plan-actions";
-import { getMarketingSettingsBySelectionPlan } from "../../../actions/marketing-actions";
jest.mock("../../../actions/selection-plan-actions", () => ({
getSelectionPlans: jest.fn(),
- getSelectionPlan: jest.fn(),
- deleteSelectionPlan: jest.fn(),
- resetSelectionPlanForm: jest.fn(),
- saveSelectionPlan: jest.fn(),
- saveSelectionPlanSettings: jest.fn()
-}));
-
-jest.mock("../../../actions/marketing-actions", () => ({
- getMarketingSettingsBySelectionPlan: jest.fn()
+ deleteSelectionPlan: jest.fn()
}));
jest.mock("openstack-uicore-foundation/lib/components/mui/table", () => ({
@@ -50,23 +37,12 @@ jest.mock(
})
);
-jest.mock("../edit-selection-plan-page", () => ({
- __esModule: true,
- default: ({ onSave }) => (
-
- onSave({ marketing_settings: {} })}>
- popup-save
-
-
- )
-}));
-
jest.mock("i18n-react/dist/i18n-react", () => ({
__esModule: true,
default: { translate: (key) => key }
}));
-const mockHistory = { replace: jest.fn() };
+const mockHistory = { push: jest.fn(), replace: jest.fn() };
const mockMatch = { params: {} };
const initialState = {
@@ -83,10 +59,6 @@ const initialState = {
term: "",
order: "id",
orderDir: 1
- },
- currentSelectionPlanState: {
- entity: { id: 0, name: "" },
- errors: {}
}
};
@@ -94,39 +66,24 @@ describe("SelectionPlanListPage", () => {
beforeEach(() => {
jest.clearAllMocks();
getSelectionPlans.mockReturnValue(() => Promise.resolve());
- getSelectionPlan.mockReturnValue(() => Promise.resolve());
deleteSelectionPlan.mockReturnValue(() => Promise.resolve());
- resetSelectionPlanForm.mockReturnValue({
- type: "RESET_SELECTION_PLAN_FORM"
- });
- getMarketingSettingsBySelectionPlan.mockReturnValue(() =>
- Promise.resolve()
- );
- saveSelectionPlan.mockReturnValue(() => Promise.resolve({ id: 1 }));
- saveSelectionPlanSettings.mockReturnValue(() => Promise.resolve());
});
- it("reloads the list after a successful save", async () => {
+ it("navigates to the new selection plan route", async () => {
renderWithRedux(
,
{ initialState }
);
- // Open dialog
await userEvent.click(
screen.getByRole("button", {
name: "selection_plan_list.add_selection_plan"
})
);
- expect(screen.getByTestId("edit-selection-plan")).toBeInTheDocument();
-
- await act(async () => {
- await userEvent.click(screen.getByRole("button", { name: "popup-save" }));
- await flushPromises();
- });
- // Call 1: useEffect on mount; call 2: handleSave → refreshSelectionPlans
- expect(getSelectionPlans).toHaveBeenCalledTimes(2);
+ expect(mockHistory.push).toHaveBeenCalledWith(
+ "/app/summits/1/selection-plans/new"
+ );
});
it("reloads the list after a successful delete", async () => {
diff --git a/src/pages/selection-plans/edit-selection-plan-page.js b/src/pages/selection-plans/edit-selection-plan-page.js
index 7eb80ef62..6724dfdec 100644
--- a/src/pages/selection-plans/edit-selection-plan-page.js
+++ b/src/pages/selection-plans/edit-selection-plan-page.js
@@ -10,203 +10,82 @@
* See the License for the specific language governing permissions and
* limitations under the License.
* */
-import React from "react";
+import React, { useState } from "react";
import { connect } from "react-redux";
import T from "i18n-react/dist/i18n-react";
-import Swal from "sweetalert2";
+import { Button, Grid2 } from "@mui/material";
+import AddNewButtonMui from "../../components/buttons/add-new-button-mui";
import SelectionPlanForm from "../../components/forms/selection-plan-form";
import {
- addAllowedMemberToSelectionPlan,
- addEventTypeSelectionPlan,
- addTrackGroupToSelectionPlan,
- assignExtraQuestion2SelectionPlan,
- assignProgressFlag2SelectionPlan,
- deleteEventTypeSelectionPlan,
- deleteRatingType,
- deleteSelectionPlanExtraQuestion,
- getAllowedMembers,
- importAllowedMembersCSV,
- removeAllowedMemberFromSelectionPlan,
- removeTrackGroupFromSelectionPlan,
- unassignProgressFlagFromSelectionPlan,
- updateProgressFlagOrder,
- updateRatingTypeOrder,
- updateSelectionPlanExtraQuestionOrder
+ saveSelectionPlan,
+ saveSelectionPlanSettings
} from "../../actions/selection-plan-actions";
const EditSelectionPlanPage = ({
currentSummit,
entity,
- allowedMembers,
- errors,
- onSave,
history,
- extraQuestionsOrder,
- extraQuestionsOrderDir,
- updateSelectionPlanExtraQuestionOrder,
- unassignProgressFlagFromSelectionPlan,
- deleteSelectionPlanExtraQuestion,
- updateRatingTypeOrder,
- deleteRatingType,
- updateProgressFlagOrder,
- addTrackGroupToSelectionPlan,
- removeTrackGroupFromSelectionPlan,
- addAllowedMemberToSelectionPlan,
- addEventTypeSelectionPlan,
- assignExtraQuestion2SelectionPlan,
- assignProgressFlag2SelectionPlan,
- deleteEventTypeSelectionPlan,
- getAllowedMembers,
- importAllowedMembersCSV,
- removeAllowedMemberFromSelectionPlan
+ saveSelectionPlan,
+ saveSelectionPlanSettings
}) => {
- const onDeleteExtraQuestion = (questionId) => {
- const extraQuestion = entity.extra_questions.find(
- (t) => t.id === questionId
- );
- Swal.fire({
- title: T.translate("general.are_you_sure"),
- text: `${T.translate(
- "edit_selection_plan.extra_question_remove_warning"
- )} ${extraQuestion.name}`,
- type: "warning",
- showCancelButton: true,
- confirmButtonColor: "#DD6B55",
- confirmButtonText: T.translate("general.yes_delete")
- }).then((result) => {
- if (result.value) {
- deleteSelectionPlanExtraQuestion(entity.id, questionId);
- }
- });
- };
-
- const onUpdateExtraQuestionOrder = (questions, questionId, newOrder) => {
- updateSelectionPlanExtraQuestionOrder(
- entity.id,
- questions,
- questionId,
- newOrder
- );
- };
-
- const onEditExtraQuestion = (questionId) => {
- history.push(
- `/app/summits/${currentSummit.id}/selection-plans/${entity.id}/extra-questions/${questionId}`
- );
- };
-
- const onAddNewExtraQuestion = () => {
- history.push(
- `/app/summits/${currentSummit.id}/selection-plans/${entity.id}/extra-questions/new`
- );
- };
-
- const onAddRatingType = () => {
- history.push(
- `/app/summits/${currentSummit.id}/selection-plans/${entity.id}/rating-types/new`
- );
- };
-
- const onEditRatingType = (ratingTypeId) => {
- history.push(
- `/app/summits/${currentSummit.id}/selection-plans/${entity.id}/rating-types/${ratingTypeId}`
- );
- };
-
- const onUpdateRatingTypeOrder = (ratingTypes, ratingTypeId, newOrder) => {
- updateRatingTypeOrder(entity.id, ratingTypes, ratingTypeId, newOrder);
- };
-
- const onDeleteRatingType = (ratingTypeId) => {
- const ratingType = entity.track_chair_rating_types.find(
- (t) => t.id === ratingTypeId
- );
- Swal.fire({
- title: T.translate("general.are_you_sure"),
- text: `${T.translate("edit_selection_plan.rating_type_remove_warning")} ${
- ratingType.name
- }`,
- type: "warning",
- showCancelButton: true,
- confirmButtonColor: "#DD6B55",
- confirmButtonText: T.translate("general.yes_delete")
- }).then((result) => {
- if (result.value) {
- deleteRatingType(entity.id, ratingTypeId);
- }
- });
- };
+ const [isSaving, setIsSaving] = useState(false);
- const onAddProgressFlag = () => {
- history.push(`/app/summits/${currentSummit.id}/progress-flags`);
+ const onSave = (values) => {
+ if (isSaving) return Promise.resolve();
+ setIsSaving(true);
+ return saveSelectionPlan(values)
+ .then((savedEntity) => {
+ if (!savedEntity?.id) return null;
+ return saveSelectionPlanSettings(
+ values.marketing_settings ?? {},
+ savedEntity.id
+ )
+ .catch(() => {})
+ .then(() =>
+ history.push(`/app/summits/${currentSummit.id}/selection-plans`)
+ );
+ })
+ .catch(() => {})
+ .finally(() => setIsSaving(false));
};
- const onEditProgressFlag = (progressFlagId) => {
- history.push(
- `/app/summits/${currentSummit.id}/progress-flags#flag_id=${progressFlagId}`
- );
- };
-
- const onUpdateProgressFlagOrder = (
- progressFlags,
- progressFlagId,
- newOrder
- ) => {
- updateProgressFlagOrder(entity.id, progressFlags, progressFlagId, newOrder);
- };
-
- const onUnassignProgressFlag = (progressFlagId) => {
- const ratingType = entity.allowed_presentation_action_types.find(
- (t) => t.id === progressFlagId
- );
- Swal.fire({
- title: T.translate("general.are_you_sure"),
- text: `${T.translate(
- "edit_selection_plan.presentation_action_type_remove_warning"
- )} ${ratingType.label}`,
- type: "warning",
- showCancelButton: true,
- confirmButtonColor: "#DD6B55",
- confirmButtonText: T.translate("general.yes_delete")
- }).then((result) => {
- if (result.value) {
- unassignProgressFlagFromSelectionPlan(entity.id, progressFlagId);
- }
- });
- };
+ const title = entity?.id
+ ? T.translate("general.edit")
+ : T.translate("general.add");
return (
-
+
+
+
+
+ {title} {T.translate("edit_selection_plan.selection_plan")}
+
+
+ {entity?.id > 0 && (
+
+
+
+ )}
+
+
+
+
+
+ {T.translate("general.save")}
+
+
+
);
};
@@ -215,24 +94,10 @@ const mapStateToProps = ({
currentSelectionPlanState
}) => ({
currentSummit: currentSummitState.currentSummit,
- ...currentSelectionPlanState
+ entity: currentSelectionPlanState.entity
});
export default connect(mapStateToProps, {
- addTrackGroupToSelectionPlan,
- removeTrackGroupFromSelectionPlan,
- addEventTypeSelectionPlan,
- deleteEventTypeSelectionPlan,
- updateSelectionPlanExtraQuestionOrder,
- deleteSelectionPlanExtraQuestion,
- updateRatingTypeOrder,
- deleteRatingType,
- assignExtraQuestion2SelectionPlan,
- assignProgressFlag2SelectionPlan,
- updateProgressFlagOrder,
- unassignProgressFlagFromSelectionPlan,
- addAllowedMemberToSelectionPlan,
- removeAllowedMemberFromSelectionPlan,
- getAllowedMembers,
- importAllowedMembersCSV
+ saveSelectionPlan,
+ saveSelectionPlanSettings
})(EditSelectionPlanPage);
diff --git a/src/pages/selection-plans/selection-plan-list-page.js b/src/pages/selection-plans/selection-plan-list-page.js
index d6aaa79ce..64eaa2e57 100644
--- a/src/pages/selection-plans/selection-plan-list-page.js
+++ b/src/pages/selection-plans/selection-plan-list-page.js
@@ -11,7 +11,7 @@
* limitations under the License.
* */
-import React, { useCallback, useEffect, useState } from "react";
+import React, { useEffect } from "react";
import { connect } from "react-redux";
import T from "i18n-react/dist/i18n-react";
import Box from "@mui/material/Box";
@@ -21,55 +21,23 @@ import MuiTable from "openstack-uicore-foundation/lib/components/mui/table";
import GridToolbar from "../../components/mui/grid-toolbar";
import {
deleteSelectionPlan,
- getSelectionPlan,
- getSelectionPlans,
- resetSelectionPlanForm,
- saveSelectionPlan,
- saveSelectionPlanSettings
+ getSelectionPlans
} from "../../actions/selection-plan-actions";
-import { getMarketingSettingsBySelectionPlan } from "../../actions/marketing-actions";
-import { DEFAULT_CURRENT_PAGE, MAX_PER_PAGE } from "../../utils/constants";
-import SelectionPlanPopup from "./selection-plan-popup";
+import { DEFAULT_CURRENT_PAGE } from "../../utils/constants";
const SelectionPlanListPage = ({
currentSummit,
history,
selectionPlans,
- currentSelectionPlan,
totalSelectionPlans,
perPage,
term,
order,
orderDir,
currentPage,
- getSelectionPlan,
getSelectionPlans,
- resetSelectionPlanForm,
- getMarketingSettingsBySelectionPlan,
- deleteSelectionPlan,
- saveSelectionPlan,
- saveSelectionPlanSettings
+ deleteSelectionPlan
}) => {
- const [openSelectionPlanPopup, setOpenSelectionPlanPopup] = useState(false);
-
- const openEditModal = useCallback(
- (selectionPlanId) => {
- if (!selectionPlanId) return;
-
- getSelectionPlan(selectionPlanId)
- .then(() =>
- getMarketingSettingsBySelectionPlan(
- selectionPlanId,
- null,
- DEFAULT_CURRENT_PAGE,
- MAX_PER_PAGE
- )
- )
- .then(() => setOpenSelectionPlanPopup(true));
- },
- [getMarketingSettingsBySelectionPlan, getSelectionPlan]
- );
-
useEffect(() => {
if (currentSummit?.id) {
getSelectionPlans(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir);
@@ -81,7 +49,9 @@ const SelectionPlanListPage = ({
const handleEdit = (selectionPlan) => {
if (!selectionPlan?.id) return;
- openEditModal(selectionPlan.id);
+ history.push(
+ `/app/summits/${currentSummit.id}/selection-plans/${selectionPlan.id}`
+ );
};
const handleDelete = (id) => {
@@ -93,26 +63,9 @@ const SelectionPlanListPage = ({
};
const handleNew = () => {
- resetSelectionPlanForm();
- setOpenSelectionPlanPopup(true);
+ history.push(`/app/summits/${currentSummit.id}/selection-plans/new`);
};
- const handleClosePopup = () => {
- resetSelectionPlanForm();
- setOpenSelectionPlanPopup(false);
- };
-
- const handleSave = (entity) =>
- saveSelectionPlan(entity)
- .then((savedEntity) => {
- if (!savedEntity?.id) return null;
- return saveSelectionPlanSettings(
- entity.marketing_settings ?? {},
- savedEntity.id
- );
- })
- .then(() => refreshSelectionPlans());
-
const handleSort = (key, dir) => {
getSelectionPlans(term, currentPage, perPage, key, dir);
};
@@ -209,35 +162,19 @@ const SelectionPlanListPage = ({
/>
)}
-
- {openSelectionPlanPopup && (
-
- )}
);
};
const mapStateToProps = ({
currentSummitState,
- currentSelectionPlanListState,
- currentSelectionPlanState
+ currentSelectionPlanListState
}) => ({
currentSummit: currentSummitState.currentSummit,
- ...currentSelectionPlanListState,
- currentSelectionPlan: currentSelectionPlanState.entity
+ ...currentSelectionPlanListState
});
export default connect(mapStateToProps, {
getSelectionPlans,
- getSelectionPlan,
- resetSelectionPlanForm,
- getMarketingSettingsBySelectionPlan,
- deleteSelectionPlan,
- saveSelectionPlan,
- saveSelectionPlanSettings
+ deleteSelectionPlan
})(SelectionPlanListPage);
diff --git a/src/pages/selection-plans/selection-plan-popup.js b/src/pages/selection-plans/selection-plan-popup.js
deleted file mode 100644
index 879b97290..000000000
--- a/src/pages/selection-plans/selection-plan-popup.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * Copyright 2019 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, { useState } from "react";
-import PropTypes from "prop-types";
-import T from "i18n-react/dist/i18n-react";
-import Button from "@mui/material/Button";
-import Dialog from "@mui/material/Dialog";
-import DialogActions from "@mui/material/DialogActions";
-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 EditSelectionPlanPage from "./edit-selection-plan-page";
-
-const SelectionPlanPopup = ({ isEditing, onClose, onSave, history }) => {
- const [isSaving, setIsSaving] = useState(false);
-
- const handleClose = () => {
- if (isSaving) return;
- onClose();
- };
-
- const handleSave = (values) => {
- if (isSaving) return Promise.resolve();
- setIsSaving(true);
- return Promise.resolve(onSave(values))
- .then(() => onClose())
- .catch(() => {})
- .finally(() => setIsSaving(false));
- };
-
- return (
-
-
- {isEditing ? T.translate("general.edit") : T.translate("general.add")}{" "}
- {T.translate("edit_selection_plan.selection_plan")}
-
-
-
-
-
-
-
-
-
-
-
- {T.translate("general.save")}
-
-
-
- );
-};
-
-SelectionPlanPopup.propTypes = {
- isEditing: PropTypes.bool,
- onClose: PropTypes.func.isRequired,
- onSave: PropTypes.func.isRequired,
- history: PropTypes.shape({ push: PropTypes.func }).isRequired
-};
-
-SelectionPlanPopup.defaultProps = {
- isEditing: false
-};
-
-export default SelectionPlanPopup;
diff --git a/src/reducers/selection_plans/selection-plan-reducer.js b/src/reducers/selection_plans/selection-plan-reducer.js
index a5ec47fbb..c0c6c1d0b 100644
--- a/src/reducers/selection_plans/selection-plan-reducer.js
+++ b/src/reducers/selection_plans/selection-plan-reducer.js
@@ -87,6 +87,8 @@ export const DEFAULT_ENTITY = {
track_chair_rating_types: [],
allow_track_change_requests: true,
allowed_presentation_action_types: [],
+ actionTypesOrder: "order",
+ actionTypesOrderDir: 1,
allowed_presentation_questions: DEFAULT_ALLOWED_QUESTIONS.map((q) => q.value),
allowed_presentation_editable_questions:
DEFAULT_ALLOWED_EDITABLE_QUESTIONS.map((q) => q.value),