From ac4c95575b6a2eadbff39babb6028a17d29d187b Mon Sep 17 00:00:00 2001 From: Priscila Moneo Date: Tue, 5 May 2026 18:16:13 -0300 Subject: [PATCH 1/3] feat: move selection plans grid to mui --- src/actions/selection-plan-actions.js | 24 +- src/components/forms/selection-plan-form.js | 59 ++-- src/components/inputs/import-modal/index.jsx | 2 +- src/layouts/selection-plan-id-layout.js | 13 +- src/layouts/selection-plan-layout.js | 8 +- .../edit-selection-plan-page.js | 17 +- .../selection-plan-list-page.js | 272 +++++++++++++----- .../selection-plan-list-reducer.js | 17 +- 8 files changed, 272 insertions(+), 140 deletions(-) diff --git a/src/actions/selection-plan-actions.js b/src/actions/selection-plan-actions.js index e0e48ae77..4ab993a52 100644 --- a/src/actions/selection-plan-actions.js +++ b/src/actions/selection-plan-actions.js @@ -12,7 +12,7 @@ * */ import T from "i18n-react/dist/i18n-react"; -import debounce from "lodash/debounce" +import debounce from "lodash/debounce"; import { getRequest, putRequest, @@ -35,7 +35,12 @@ import { fetchErrorHandler } from "../utils/methods"; import { saveMarketingSetting } from "./marketing-actions"; -import { DEBOUNCE_WAIT, DEFAULT_PER_PAGE } from "../utils/constants"; +import { + DEBOUNCE_WAIT, + DEFAULT_CURRENT_PAGE, + DEFAULT_ORDER_DIR, + DEFAULT_PER_PAGE +} from "../utils/constants"; URI.escapeQuerySpace = false; @@ -66,7 +71,13 @@ export const SELECTION_PLAN_PROGRESS_FLAG_ORDER_UPDATED = "SELECTION_PLAN_PROGRESS_FLAG_ORDER_UPDATED"; export const getSelectionPlans = - (term = "", page = 1, order = "id", orderDir = 1) => + ( + term = "", + page = DEFAULT_CURRENT_PAGE, + perPage = DEFAULT_PER_PAGE, + order = "id", + orderDir = DEFAULT_ORDER_DIR + ) => async (dispatch, getState) => { const { currentSummitState } = getState(); const accessToken = await getAccessTokenSafely(); @@ -84,7 +95,7 @@ export const getSelectionPlans = access_token: accessToken, relations: "none", page, - per_page: DEFAULT_PER_PAGE, + per_page: perPage, order: `${orderDir === 1 ? "" : "-"}${order}` }; @@ -93,10 +104,11 @@ export const getSelectionPlans = } return getRequest( - null, + createAction(REQUEST_SELECTION_PLANS), createAction(RECEIVE_SELECTION_PLANS), `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/selection-plans`, - authErrorHandler + authErrorHandler, + { order, orderDir, page, perPage, term } )(params)(dispatch).then(async () => { dispatch(stopLoading()); }); diff --git a/src/components/forms/selection-plan-form.js b/src/components/forms/selection-plan-form.js index eae94c098..2944f7d01 100644 --- a/src/components/forms/selection-plan-form.js +++ b/src/components/forms/selection-plan-form.js @@ -20,15 +20,13 @@ import { queryEventTypes, queryMembers } from "openstack-uicore-foundation/lib/utils/query-actions"; -import { - Input, - DateTimePicker, - SimpleLinkList, - SortableTable, - Panel, - Table, - Dropdown -} from "openstack-uicore-foundation/lib/components"; +import Input from "openstack-uicore-foundation/lib/components/inputs/text-input"; +import DateTimePicker from "openstack-uicore-foundation/lib/components/inputs/datetimepicker"; +import SimpleLinkList from "openstack-uicore-foundation/lib/components/simple-link-list"; +import SortableTable from "openstack-uicore-foundation/lib/components/mui/sortable-table"; +import Panel from "openstack-uicore-foundation/lib/components/sections/panel"; +import Table from "openstack-uicore-foundation/lib/components/mui/table"; +import Dropdown from "openstack-uicore-foundation/lib/components/inputs/dropdown"; import TextEditorV3 from "openstack-uicore-foundation/lib/components/inputs/editor-input-v3"; import Switch from "react-switch"; import { Pagination } from "react-bootstrap"; @@ -52,7 +50,6 @@ import { DEFAULT_ALLOWED_QUESTIONS, DEFAULT_CFP_PRESENTATION_EDITION_TABS } from "../../reducers/selection_plans/selection-plan-reducer"; -import history from "../../history"; class SelectionPlanForm extends React.Component { constructor(props) { @@ -194,17 +191,15 @@ class SelectionPlanForm extends React.Component { handleSubmit(ev) { ev.preventDefault(); - const entity = { ...this.state.entity }; - const { currentSummit } = this.props; + const { onSaved } = this.props; this.props.onSubmit(this.state.entity).then((e) => { this.props - .saveSelectionPlanSettings(entity.marketing_settings, e.id) + .saveSelectionPlanSettings(this.state.entity.marketing_settings, e.id) .then(() => { - if (!entity.id) - history.push( - `/app/summits/${currentSummit.id}/selection-plans/${e.id}` - ); + if (onSaved) { + onSaved(e); + } }); }); } @@ -370,15 +365,15 @@ class SelectionPlanForm extends React.Component { const extraQuestionColumns = [ { columnKey: "type", - value: T.translate("order_extra_question_list.question_type") + header: T.translate("order_extra_question_list.question_type") }, { columnKey: "label", - value: T.translate("order_extra_question_list.visible_question") + header: T.translate("order_extra_question_list.visible_question") }, { columnKey: "name", - value: T.translate("order_extra_question_list.question_id") + header: T.translate("order_extra_question_list.question_id") } ]; @@ -392,8 +387,8 @@ class SelectionPlanForm extends React.Component { }; const ratingTypesColumns = [ - { columnKey: "name", value: T.translate("rating_type_list.name") }, - { columnKey: "weight", value: T.translate("rating_type_list.weight") } + { columnKey: "name", header: T.translate("rating_type_list.name") }, + { columnKey: "weight", header: T.translate("rating_type_list.weight") } ]; const ratingTypesOptions = { @@ -404,7 +399,7 @@ class SelectionPlanForm extends React.Component { }; const actionTypesColumns = [ - { columnKey: "label", value: T.translate("progress_flags.label") } + { columnKey: "label", header: T.translate("progress_flags.label") } ]; const actionTypesOptions = { @@ -416,8 +411,8 @@ class SelectionPlanForm extends React.Component { }; const allowedMembersColumns = [ - { columnKey: "id", value: T.translate("edit_selection_plan.id") }, - { columnKey: "email", value: T.translate("edit_selection_plan.email") } + { columnKey: "id", header: T.translate("edit_selection_plan.id") }, + { columnKey: "email", header: T.translate("edit_selection_plan.email") } ]; const allowedMembersOptions = { @@ -428,8 +423,6 @@ class SelectionPlanForm extends React.Component { } }; - console.log("CHECK...", entity, currentSummit); - return (
@@ -738,8 +731,8 @@ class SelectionPlanForm extends React.Component { label: stripTags(q.label) }))} columns={extraQuestionColumns} - dropCallback={this.props.updateExtraQuestionOrder} - orderField="order" + onReorder={this.props.updateExtraQuestionOrder} + updateOrderKey="order" /> )} @@ -858,8 +851,8 @@ class SelectionPlanForm extends React.Component { options={ratingTypesOptions} data={entity.track_chair_rating_types} columns={ratingTypesColumns} - dropCallback={this.props.onUpdateRatingTypeOrder} - orderField="order" + onReorder={this.props.onUpdateRatingTypeOrder} + updateOrderKey="order" /> )} diff --git a/src/components/inputs/import-modal/index.jsx b/src/components/inputs/import-modal/index.jsx index c2f8a3a4d..210ca514e 100644 --- a/src/components/inputs/import-modal/index.jsx +++ b/src/components/inputs/import-modal/index.jsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { Modal } from "react-bootstrap"; import T from "i18n-react"; -import { UploadInput } from "openstack-uicore-foundation/lib/components"; +import UploadInput from "openstack-uicore-foundation/lib/components/inputs/upload-input"; export default ({ title, children, show, wrapperClass, onHide, onIngest }) => { const [importFile, setImportFile] = useState(null); diff --git a/src/layouts/selection-plan-id-layout.js b/src/layouts/selection-plan-id-layout.js index 4fffb8749..04e41a5ed 100644 --- a/src/layouts/selection-plan-id-layout.js +++ b/src/layouts/selection-plan-id-layout.js @@ -11,9 +11,6 @@ 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") ); @@ -54,12 +51,6 @@ const SelectionPlanIdLayout = ({ }> - - + diff --git a/src/layouts/selection-plan-layout.js b/src/layouts/selection-plan-layout.js index 57d103b6d..d0f6a62fe 100644 --- a/src/layouts/selection-plan-layout.js +++ b/src/layouts/selection-plan-layout.js @@ -38,7 +38,13 @@ const SelectionPlanLayout = ({ match, currentSummit }) => ( strict exact path={`${match.url}/new`} - component={SelectionPlanIdLayout} + render={() => } + /> + -

- {title} {T.translate("edit_selection_plan.selection_plan")} - -

-
+ {!hideHeader && ( + <> +

+ {title} {T.translate("edit_selection_plan.selection_plan")} +

+
+ + )} { + const [openSelectionPlanPopup, setOpenSelectionPlanPopup] = useState(false); + const routeSelectionPlanId = match?.params?.selection_plan_id; + + const openEditModal = (selectionPlanId) => { + if (!selectionPlanId) return; + + getSelectionPlan(selectionPlanId) + .then(() => + getMarketingSettingsBySelectionPlan( + selectionPlanId, + null, + DEFAULT_CURRENT_PAGE, + MAX_PER_PAGE + ) + ) + .then(() => setOpenSelectionPlanPopup(true)); + }; + useEffect(() => { - getSelectionPlans(); - }, []); + getSelectionPlans(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir); + }, [getSelectionPlans]); + + useEffect(() => { + if (routeSelectionPlanId) { + openEditModal(routeSelectionPlanId); + } + }, [routeSelectionPlanId]); + + const refreshSelectionPlans = () => + getSelectionPlans(term, currentPage, perPage, order, orderDir); + + const handleEdit = (selectionPlan) => { + const selectionPlanId = selectionPlan?.id || selectionPlan; + if (!selectionPlanId) return; - const handleEdit = (selectionPlanId) => { history.push( `/app/summits/${currentSummit.id}/selection-plans/${selectionPlanId}` ); }; - const handleDelete = (selectionPlanId) => { - const selectionPlan = selectionPlans.find((s) => s.id === selectionPlanId); - - Swal.fire({ - title: T.translate("general.are_you_sure"), - text: `${T.translate("selection_plan_list.remove_warning")} ${ - selectionPlan.name - }`, - type: "warning", - showCancelButton: true, - confirmButtonColor: "#DD6B55", - confirmButtonText: T.translate("general.yes_delete") - }).then((result) => { - if (result.value) { - deleteSelectionPlan(selectionPlanId); - } - }); + const handleDelete = (row) => { + const selectionPlanId = row?.id || row; + if (!selectionPlanId) return; + + deleteSelectionPlan(selectionPlanId).then(() => refreshSelectionPlans()); }; const handleNew = () => { - history.push(`/app/summits/${currentSummit.id}/selection-plans/new`); + resetSelectionPlanForm(); + setOpenSelectionPlanPopup(true); + }; + + const handleClosePopup = () => { + resetSelectionPlanForm(); + setOpenSelectionPlanPopup(false); + + if (routeSelectionPlanId) { + history.replace(`/app/summits/${currentSummit.id}/selection-plans`); + } + }; + + const handleSelectionPlanSaved = () => { + setOpenSelectionPlanPopup(false); + refreshSelectionPlans(); + + if (routeSelectionPlanId) { + history.replace(`/app/summits/${currentSummit.id}/selection-plans`); + } }; - const handleSort = (index, key, dir) => { - getSelectionPlans(term, currentPage, key, dir); + const handleSort = (key, dir) => { + getSelectionPlans(term, currentPage, perPage, key, dir); }; - const handlePageChange = (newPage) => { - getSelectionPlans(term, newPage, order, orderDir); + const handlePageChange = (page) => { + getSelectionPlans(term, page, perPage, order, orderDir); + }; + + const handlePerPageChange = (newPerPage) => { + getSelectionPlans( + term, + DEFAULT_CURRENT_PAGE, + parseInt(newPerPage, 10), + order, + orderDir + ); }; const handleSearch = (newTerm) => { - getSelectionPlans(newTerm, 1, order, orderDir); + getSelectionPlans(newTerm, DEFAULT_CURRENT_PAGE, perPage, order, orderDir); }; const columns = [ - { columnKey: "id", value: T.translate("selection_plan_list.id") }, + { + columnKey: "id", + header: T.translate("selection_plan_list.id"), + width: 120, + sortable: true + }, { columnKey: "name", - value: T.translate("selection_plan_list.name") + header: T.translate("selection_plan_list.name") }, { columnKey: "type", - value: T.translate("selection_plan_list.type") + header: T.translate("selection_plan_list.type") }, { columnKey: "is_enabled", - value: T.translate("selection_plan_list.is_enabled") + header: T.translate("selection_plan_list.is_enabled") }, { columnKey: "is_hidden", - value: T.translate("selection_plan_list.is_hidden") + header: T.translate("selection_plan_list.is_hidden") } ]; @@ -105,8 +175,7 @@ const SelectionPlanListPage = ({ sortCol: order, sortDir: orderDir, actions: { - edit: { onClick: handleEdit }, - delete: { onClick: handleDelete } + edit: { onClick: handleEdit } } }; @@ -114,66 +183,115 @@ const SelectionPlanListPage = ({ return (
-

- {" "} - {T.translate("selection_plan_list.selection_plan_list")} ( - {totalSelectionPlans}) -

- -
-
- -
-
- -
-
+

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

+ + + + {totalSelectionPlans} items + + + + + + + + + + + {selectionPlans.length === 0 && (
{T.translate("selection_plan_list.no_selection_plans")}
)} {selectionPlans.length > 0 && (
- - )} + + + + {currentSelectionPlan?.id + ? T.translate("general.edit") + : T.translate("general.add")}{" "} + {T.translate("edit_selection_plan.selection_plan")} + + + + + + + + + ); }; const mapStateToProps = ({ currentSummitState, - currentSelectionPlanListState + currentSelectionPlanListState, + currentSelectionPlanState }) => ({ currentSummit: currentSummitState.currentSummit, - ...currentSelectionPlanListState + ...currentSelectionPlanListState, + currentSelectionPlan: currentSelectionPlanState.entity, + currentSelectionPlanErrors: currentSelectionPlanState.errors }); export default connect(mapStateToProps, { getSelectionPlans, + getSelectionPlan, + resetSelectionPlanForm, + getMarketingSettingsBySelectionPlan, deleteSelectionPlan })(SelectionPlanListPage); diff --git a/src/reducers/selection_plans/selection-plan-list-reducer.js b/src/reducers/selection_plans/selection-plan-list-reducer.js index 888fa5783..875d2a62e 100644 --- a/src/reducers/selection_plans/selection-plan-list-reducer.js +++ b/src/reducers/selection_plans/selection-plan-list-reducer.js @@ -39,12 +39,20 @@ const selectionPlanListReducer = (state = DEFAULT_STATE, action) => { return DEFAULT_STATE; } case REQUEST_SELECTION_PLANS: { - const { order, orderDir } = payload; + const { order, orderDir, page, perPage, term } = payload; - return { ...state, order, orderDir }; + return { + ...state, + order, + orderDir, + currentPage: page, + perPage, + term + }; } case RECEIVE_SELECTION_PLANS: { - const { current_page, total, last_page, data } = payload.response; + const { current_page, total, last_page, per_page, data } = + payload.response; const selectionPlans = data.map((sp) => ({ ...sp, @@ -57,7 +65,8 @@ const selectionPlanListReducer = (state = DEFAULT_STATE, action) => { selectionPlans, totalSelectionPlans: total, currentPage: current_page, - lastPage: last_page + lastPage: last_page, + perPage: per_page || state.perPage }; } case SELECTION_PLAN_ADDED: { From 284b88cb6aad3dc318ccf08bdaff1d78ba012dcb Mon Sep 17 00:00:00 2001 From: Priscila Moneo Date: Wed, 6 May 2026 12:07:30 -0300 Subject: [PATCH 2/3] fix: feedback from PR --- src/components/forms/selection-plan-form.js | 2426 ++++++++--------- .../edit-selection-plan-page.js | 81 +- .../selection-plan-list-page.js | 54 +- .../selection-plan-list-reducer.js | 7 +- 4 files changed, 1215 insertions(+), 1353 deletions(-) diff --git a/src/components/forms/selection-plan-form.js b/src/components/forms/selection-plan-form.js index 2944f7d01..c1c0026cd 100644 --- a/src/components/forms/selection-plan-form.js +++ b/src/components/forms/selection-plan-form.js @@ -11,14 +11,13 @@ * limitations under the License. * */ -import React from "react"; +import React, { useState, useEffect, useRef } from "react"; import T from "i18n-react/dist/i18n-react"; import "awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css"; import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods"; import { queryTrackGroups, - queryEventTypes, - queryMembers + queryEventTypes } from "openstack-uicore-foundation/lib/utils/query-actions"; import Input from "openstack-uicore-foundation/lib/components/inputs/text-input"; import DateTimePicker from "openstack-uicore-foundation/lib/components/inputs/datetimepicker"; @@ -51,110 +50,82 @@ import { DEFAULT_CFP_PRESENTATION_EDITION_TABS } from "../../reducers/selection_plans/selection-plan-reducer"; -class SelectionPlanForm extends React.Component { - constructor(props) { - super(props); - - this.state = { - entity: { ...props.entity }, - errors: props.errors, - showSection: "main", - newMemberEmail: "", - showImportModal: false, - importFile: null - }; - - this.handleTrackGroupLink = this.handleTrackGroupLink.bind(this); - this.handleTrackGroupUnLink = this.handleTrackGroupUnLink.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleEditExtraQuestion = this.handleEditExtraQuestion.bind(this); - this.handleDeleteExtraQuestion = this.handleDeleteExtraQuestion.bind(this); - this.handleNewExtraQuestion = this.handleNewExtraQuestion.bind(this); - this.handleDeleteEventType = this.handleDeleteEventType.bind(this); - this.handleAddEventType = this.handleAddEventType.bind(this); - this.handleAddRatingType = this.handleAddRatingType.bind(this); - this.handleDeleteRatingType = this.handleDeleteRatingType.bind(this); - this.handleEditRatingType = this.handleEditRatingType.bind(this); - this.handleRemoveProgressFlag = this.handleRemoveProgressFlag.bind(this); - this.toggleSection = this.toggleSection.bind(this); - this.handleNotificationEmailTemplateChange = - this.handleNotificationEmailTemplateChange.bind(this); - this.fetchSummitSelectionPlanExtraQuestions = - this.fetchSummitSelectionPlanExtraQuestions.bind(this); - this.fetchMembers = this.fetchMembers.bind(this); - this.linkSummitSelectionPlanExtraQuestion = - this.linkSummitSelectionPlanExtraQuestion.bind(this); - this.fetchSummitPresentationActionTypes = - this.fetchSummitPresentationActionTypes.bind(this); - this.linkSummitProgressFlag = this.linkSummitProgressFlag.bind(this); - this.handleAddAllowedMember = this.handleAddAllowedMember.bind(this); - this.handleImportAllowedMembers = - this.handleImportAllowedMembers.bind(this); - this.handleDeleteAllowedMember = this.handleDeleteAllowedMember.bind(this); - this.handleAllowedMembersPageChange = - this.handleAllowedMembersPageChange.bind(this); - this.handleOnSwitchChange = this.handleOnSwitchChange.bind(this); - } - - fetchSummitSelectionPlanExtraQuestions(input, callback) { - const { currentSummit } = this.props; - - if (!input) { - return Promise.resolve({ options: [] }); +const SelectionPlanForm = (props) => { + const { + entity: propsEntity, + errors: propsErrors, + currentSummit, + extraQuestionsOrderDir, + extraQuestionsOrder, + actionTypesOrderDir, + actionTypesOrder, + allowedMembers, + onSaved, + onSubmit, + saveSelectionPlanSettings, + onTrackGroupLink, + onTrackGroupUnLink, + onAddEventType, + onDeleteEventType, + onAddRatingType, + onEditRatingType, + onDeleteRatingType, + onEditExtraQuestion, + onDeleteExtraQuestion, + onAddNewExtraQuestion, + onAssignExtraQuestion2SelectionPlan, + onAssignProgressFlag2SelectionPlan, + onUnassignProgressFlag, + onUpdateProgressFlagOrder, + onUpdateRatingTypeOrder, + updateExtraQuestionOrder, + onImportAllowedMembers, + onAllowedMemberAdd, + onAllowedMemberDelete, + onAllowedMembersPageChange + } = props; + + const [entity, setEntity] = useState({ ...propsEntity }); + const [errors, setErrors] = useState(propsErrors); + const [showSection, setShowSection] = useState("main"); + const [newMemberEmail, setNewMemberEmail] = useState(""); + const [showImportModal, setShowImportModal] = useState(false); + + const prevPropsRef = useRef({ entity: propsEntity, errors: propsErrors }); + + useEffect(() => { + const prevEntity = prevPropsRef.current.entity; + const prevErrors = prevPropsRef.current.errors; + + scrollToError(propsErrors); + + const updates = {}; + + if (!shallowEqual(prevEntity, propsEntity)) { + updates.entity = { ...propsEntity }; + updates.errors = {}; } - querySelectionPlanExtraQuestions(currentSummit.id, input, callback); - } - fetchMembers(input, callback) { - if (!input) { - return Promise.resolve({ options: [] }); + if (!shallowEqual(prevErrors, propsErrors)) { + updates.errors = { ...propsErrors }; } - queryMembers(input, callback); - } - - linkSummitSelectionPlanExtraQuestion(question) { - const { currentSummit } = this.props; - this.props.onAssignExtraQuestion2SelectionPlan( - currentSummit.id, - this.state.entity.id, - question.id - ); - } - - handleEditExtraQuestion(questionId) { - this.props.onEditExtraQuestion(questionId); - } - - handleDeleteExtraQuestion(questionId) { - this.props.onDeleteExtraQuestion(questionId); - } - handleNewExtraQuestion() { - this.props.onAddNewExtraQuestion(); - } - - componentDidUpdate(prevProps) { - const state = {}; - scrollToError(this.props.errors); - - if (!shallowEqual(prevProps.entity, this.props.entity)) { - state.entity = { ...this.props.entity }; - state.errors = {}; + if (!isEmpty(updates)) { + if (updates.entity) setEntity(updates.entity); + if (updates.errors !== undefined) setErrors(updates.errors); } - if (!shallowEqual(prevProps.errors, this.props.errors)) { - state.errors = { ...this.props.errors }; - } + prevPropsRef.current = { entity: propsEntity, errors: propsErrors }; + }, [propsEntity, propsErrors]); - if (!isEmpty(state)) { - this.setState({ ...this.state, ...state }); - } - } + const hasErrors = (field) => { + if (field in errors) return errors[field]; + return ""; + }; - handleChange(ev) { - const newEntity = { ...this.state.entity }; - const newErrors = { ...this.state.errors }; + const handleChange = (ev) => { + const newEntity = { ...entity }; + const newErrors = { ...errors }; let { value, id } = ev.target; if (ev.target.type === "checkbox") { @@ -166,7 +137,9 @@ class SelectionPlanForm extends React.Component { } if (id.startsWith("cfp_")) { - if (!newEntity.marketing_settings.hasOwnProperty(id)) { + if ( + !Object.prototype.hasOwnProperty.call(newEntity.marketing_settings, id) + ) { newEntity.marketing_settings[id] = { value: "" }; } newEntity.marketing_settings[id].value = value; @@ -175,1295 +148,1208 @@ class SelectionPlanForm extends React.Component { newEntity[id] = value; } - this.setState({ entity: newEntity, errors: newErrors }); - } + setEntity(newEntity); + setErrors(newErrors); + }; - handleNotificationEmailTemplateChange(ev) { - const newEntity = { ...this.state.entity }; - const newErrors = { ...this.state.errors }; + const handleNotificationEmailTemplateChange = (ev) => { const { value, id } = ev.target; + setEntity((prev) => ({ ...prev, [id]: value })); + setErrors((prev) => ({ ...prev, [id]: "" })); + }; - newErrors[id] = ""; - newEntity[id] = value; - this.setState({ ...this.state, entity: newEntity, errors: newErrors }); - } - - handleSubmit(ev) { + const handleSubmit = (ev) => { ev.preventDefault(); - - const { onSaved } = this.props; - - this.props.onSubmit(this.state.entity).then((e) => { - this.props - .saveSelectionPlanSettings(this.state.entity.marketing_settings, e.id) - .then(() => { - if (onSaved) { - onSaved(e); + return onSubmit(entity) + .then((e) => { + if (!e?.id) return null; + return saveSelectionPlanSettings(entity.marketing_settings, e.id).then( + () => { + if (onSaved) onSaved(e); } - }); - }); - } - - hasErrors(field) { - const { errors } = this.state; - if (field in errors) { - return errors[field]; - } - - return ""; - } - - handleTrackGroupLink(value) { - const { entity } = this.state; - this.props.onTrackGroupLink(entity.id, value); - } - - handleTrackGroupUnLink(valueId) { - const { entity } = this.state; - this.props.onTrackGroupUnLink(entity.id, valueId); - } - - handleAddEventType(value) { - const { entity } = this.state; - this.props.onAddEventType(entity.id, value); - } - - handleDeleteEventType(valueId) { - const { entity } = this.state; - this.props.onDeleteEventType(entity.id, valueId); - } - - handleAddRatingType() { - this.props.onAddRatingType(); - } - - handleEditRatingType(ratingTypeId) { - this.props.onEditRatingType(ratingTypeId); - } - - handleDeleteRatingType(ratingTypeId) { - this.props.onDeleteRatingType(ratingTypeId); - } + ); + }) + .catch(() => { + // errors are surfaced via error handler + }); + }; + + const handleTrackGroupLink = (value) => onTrackGroupLink(entity.id, value); + const handleTrackGroupUnLink = (valueId) => + onTrackGroupUnLink(entity.id, valueId); + const handleAddEventType = (value) => onAddEventType(entity.id, value); + const handleDeleteEventType = (valueId) => + onDeleteEventType(entity.id, valueId); + const handleAddRatingType = () => onAddRatingType(); + const handleEditRatingType = (ratingTypeId) => onEditRatingType(ratingTypeId); + const handleDeleteRatingType = (ratingTypeId) => + onDeleteRatingType(ratingTypeId); + const handleEditExtraQuestion = (questionId) => + onEditExtraQuestion(questionId); + const handleDeleteExtraQuestion = (questionId) => + onDeleteExtraQuestion(questionId); + const handleNewExtraQuestion = () => onAddNewExtraQuestion(); + const handleRemoveProgressFlag = (progressFlagId) => + onUnassignProgressFlag(progressFlagId); + const handleDeleteAllowedMember = (valueId) => + onAllowedMemberDelete(entity.id, valueId); + const handleAllowedMembersPageChange = (page) => + onAllowedMembersPageChange(entity.id, page); + + const handleAddAllowedMember = () => + onAllowedMemberAdd(entity.id, newMemberEmail); + + const handleImportAllowedMembers = (importFile) => { + if (importFile) onImportAllowedMembers(entity.id, importFile); + setShowImportModal(false); + }; + + const fetchSummitSelectionPlanExtraQuestions = (input, callback) => { + if (!input) return Promise.resolve({ options: [] }); + querySelectionPlanExtraQuestions(currentSummit.id, input, callback); + }; - fetchSummitPresentationActionTypes(input, callback) { - const { currentSummit } = this.props; + const linkSummitSelectionPlanExtraQuestion = (question) => { + onAssignExtraQuestion2SelectionPlan( + currentSummit.id, + entity.id, + question.id + ); + }; - if (!input) { - return Promise.resolve({ options: [] }); - } + const fetchSummitPresentationActionTypes = (input, callback) => { + if (!input) return Promise.resolve({ options: [] }); querySummitProgressFlags(currentSummit.id, input, callback); - } + }; - linkSummitProgressFlag(progressFlag) { - const { currentSummit } = this.props; - this.props.onAssignProgressFlag2SelectionPlan( + const linkSummitProgressFlag = (progressFlag) => { + onAssignProgressFlag2SelectionPlan( currentSummit.id, - this.state.entity.id, + entity.id, progressFlag.id ); - } - - handleRemoveProgressFlag(progressFlagId) { - this.props.onUnassignProgressFlag(progressFlagId); - } - - handleImportAllowedMembers(importFile) { - if (importFile) { - this.props.onImportAllowedMembers(this.state.entity.id, importFile); - } - this.setState({ ...this.state, showImportModal: false }); - } - - handleAddAllowedMember() { - const { entity, newMemberEmail } = this.state; - this.props.onAllowedMemberAdd(entity.id, newMemberEmail); - } - - handleDeleteAllowedMember(valueId) { - const { entity } = this.state; - this.props.onAllowedMemberDelete(entity.id, valueId); - } - - handleAllowedMembersPageChange(page) { - const { entity } = this.state; - this.props.onAllowedMembersPageChange(entity.id, page); - } - - toggleSection(section) { - const { showSection } = this.state; - const newShowSection = showSection === section ? "main" : section; - this.setState({ showSection: newShowSection }); - } - - handleOnSwitchChange(setting, value) { - const newEntity = { ...this.state.entity }; - const newErrors = { ...this.state.errors }; - - if (!newEntity.marketing_settings.hasOwnProperty(setting)) { + }; + + const handleOnSwitchChange = (setting, value) => { + const newEntity = { ...entity }; + if ( + !Object.prototype.hasOwnProperty.call( + newEntity.marketing_settings, + setting + ) + ) { newEntity.marketing_settings[setting] = { value: "" }; } - newEntity.marketing_settings[setting].value = value; - - this.setState({ entity: newEntity, errors: newErrors }); - } - - render() { - const { entity, showSection, newMemberEmail, showImportModal } = this.state; - const { - currentSummit, - extraQuestionsOrderDir, - extraQuestionsOrder, - actionTypesOrderDir, - actionTypesOrder, - allowedMembers - } = this.props; - - const trackGroupsColumns = [ - { columnKey: "name", value: T.translate("edit_selection_plan.name") }, - { - columnKey: "description", - value: T.translate("edit_selection_plan.description") - } - ]; - - const trackGroupsOptions = { - valueKey: "name", - labelKey: "name", - defaultOptions: true, - actions: { - search: (input, callback) => { - queryTrackGroups(currentSummit.id, input, callback); - }, - delete: { onClick: this.handleTrackGroupUnLink }, - add: { onClick: this.handleTrackGroupLink } - } - }; - - const eventTypesColumns = [ - { columnKey: "name", value: T.translate("edit_selection_plan.name") } - ]; - - const eventTypesOptions = { - valueKey: "name", - labelKey: "name", - defaultOptions: true, - actions: { - search: (input, callback) => { - queryEventTypes( - currentSummit.id, - input, - callback, - PresentationTypeClassName - ); - }, - delete: { onClick: this.handleDeleteEventType }, - add: { onClick: this.handleAddEventType } - } - }; - - const extraQuestionColumns = [ - { - columnKey: "type", - header: T.translate("order_extra_question_list.question_type") + setEntity(newEntity); + }; + + const toggleSection = (section) => { + setShowSection((prev) => (prev === section ? "main" : section)); + }; + + const trackGroupsColumns = [ + { columnKey: "name", value: T.translate("edit_selection_plan.name") }, + { + columnKey: "description", + value: T.translate("edit_selection_plan.description") + } + ]; + + const trackGroupsOptions = { + valueKey: "name", + labelKey: "name", + defaultOptions: true, + actions: { + search: (input, callback) => { + queryTrackGroups(currentSummit.id, input, callback); }, - { - columnKey: "label", - header: T.translate("order_extra_question_list.visible_question") + delete: { onClick: handleTrackGroupUnLink }, + add: { onClick: handleTrackGroupLink } + } + }; + + const eventTypesColumns = [ + { columnKey: "name", value: T.translate("edit_selection_plan.name") } + ]; + + const eventTypesOptions = { + valueKey: "name", + labelKey: "name", + defaultOptions: true, + actions: { + search: (input, callback) => { + queryEventTypes( + currentSummit.id, + input, + callback, + PresentationTypeClassName + ); }, - { - columnKey: "name", - header: T.translate("order_extra_question_list.question_id") - } - ]; - - const extraQuestionsOptions = { - sortCol: extraQuestionsOrder, - sortDir: extraQuestionsOrderDir, - actions: { - edit: { onClick: this.handleEditExtraQuestion }, - delete: { onClick: this.handleDeleteExtraQuestion } - } - }; - - const ratingTypesColumns = [ - { columnKey: "name", header: T.translate("rating_type_list.name") }, - { columnKey: "weight", header: T.translate("rating_type_list.weight") } - ]; - - const ratingTypesOptions = { - actions: { - edit: { onClick: this.handleEditRatingType }, - delete: { onClick: this.handleDeleteRatingType } - } - }; - - const actionTypesColumns = [ - { columnKey: "label", header: T.translate("progress_flags.label") } - ]; + delete: { onClick: handleDeleteEventType }, + add: { onClick: handleAddEventType } + } + }; + + const extraQuestionColumns = [ + { + columnKey: "type", + header: T.translate("order_extra_question_list.question_type") + }, + { + columnKey: "label", + header: T.translate("order_extra_question_list.visible_question") + }, + { + columnKey: "name", + header: T.translate("order_extra_question_list.question_id") + } + ]; + + const extraQuestionsOptions = { + sortCol: extraQuestionsOrder, + sortDir: extraQuestionsOrderDir, + actions: { + edit: { onClick: handleEditExtraQuestion }, + delete: { onClick: handleDeleteExtraQuestion } + } + }; - const actionTypesOptions = { - sortCol: actionTypesOrder, - sortDir: actionTypesOrderDir, - actions: { - delete: { onClick: this.handleRemoveProgressFlag } - } - }; + const ratingTypesColumns = [ + { columnKey: "name", header: T.translate("rating_type_list.name") }, + { columnKey: "weight", header: T.translate("rating_type_list.weight") } + ]; - const allowedMembersColumns = [ - { columnKey: "id", header: T.translate("edit_selection_plan.id") }, - { columnKey: "email", header: T.translate("edit_selection_plan.email") } - ]; + const ratingTypesOptions = { + actions: { + edit: { onClick: handleEditRatingType }, + delete: { onClick: handleDeleteRatingType } + } + }; - const allowedMembersOptions = { - sortCol: "email", - sortDir: 1, - actions: { - delete: { onClick: this.handleDeleteAllowedMember } - } - }; + const actionTypesColumns = [ + { columnKey: "label", header: T.translate("progress_flags.label") } + ]; - return ( - - -
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
+ const actionTypesOptions = { + sortCol: actionTypesOrder, + sortDir: actionTypesOrderDir, + actions: { + delete: { onClick: handleRemoveProgressFlag } + } + }; + + const allowedMembersColumns = [ + { columnKey: "id", header: T.translate("edit_selection_plan.id") }, + { columnKey: "email", header: T.translate("edit_selection_plan.email") } + ]; + + const allowedMembersOptions = { + sortCol: "email", + sortDir: 1, + actions: { + delete: { onClick: handleDeleteAllowedMember } + } + }; + + return ( + + +
+
+ +
- -
-
- - +
+ -
-
-
-
-
- - +
+ -
-
-