Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

[#641] Add EditPlanNameModal for editing name and description of completed plans #663

Merged
merged 3 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions app/javascript/react/screens/App/Overview/Overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import OverviewEmptyState from './components/OverviewEmptyState/OverviewEmptySta
import componentRegistry from '../../../../components/componentRegistry';
import getMostRecentRequest from '../common/getMostRecentRequest';
import ConfirmModal from '../common/ConfirmModal';
import EditPlanNameModal from './components/EditPlanNameModal';
import { MIGRATIONS_FILTERS } from './OverviewConstants';

class Overview extends React.Component {
Expand Down Expand Up @@ -204,6 +205,9 @@ class Overview extends React.Component {
showPlanWizardAction,
mappingWizardVisible,
planWizardVisible,
editPlanNameModalVisible,
showEditPlanNameModalAction,
hideEditPlanNameModalAction,
transformationMappings,
isFetchingTransformationMappings,
isRejectedTransformationMappings,
Expand Down Expand Up @@ -355,6 +359,7 @@ class Overview extends React.Component {
showPlanWizardEditModeAction={showPlanWizardEditModeAction}
fetchTransformationMappingsUrl={fetchTransformationMappingsUrl}
fetchTransformationMappingsAction={fetchTransformationMappingsAction}
showEditPlanNameModalAction={showEditPlanNameModalAction}
/>
)}
{hasSufficientProviders ? (
Expand Down Expand Up @@ -392,6 +397,15 @@ class Overview extends React.Component {
)}
</Spinner>
<ConfirmModal show={confirmModalVisible} onCancel={hideConfirmModalAction} {...confirmModalOptions} />
<EditPlanNameModal
editPlanNameModalVisible={editPlanNameModalVisible}
hideEditPlanNameModalAction={hideEditPlanNameModalAction}
transformationPlans={transformationPlans}
archivedTransformationPlans={archivedTransformationPlans}
fetchTransformationPlansAction={fetchTransformationPlansAction}
fetchTransformationPlansUrl={fetchTransformationPlansUrl}
fetchArchivedTransformationPlansUrl={fetchArchivedTransformationPlansUrl}
/>
</div>
);

Expand Down Expand Up @@ -419,6 +433,9 @@ Overview.propTypes = {
addNotificationAction: PropTypes.func,
mappingWizardVisible: PropTypes.bool,
planWizardVisible: PropTypes.bool,
editPlanNameModalVisible: PropTypes.bool,
showEditPlanNameModalAction: PropTypes.func,
hideEditPlanNameModalAction: PropTypes.func,
transformationPlans: PropTypes.array,
allRequestsWithTasks: PropTypes.array,
reloadCard: PropTypes.bool,
Expand Down
15 changes: 15 additions & 0 deletions app/javascript/react/screens/App/Overview/OverviewActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
SHOW_DELETE_CONFIRMATION_MODAL,
SHOW_MAPPING_WIZARD,
SHOW_PLAN_WIZARD,
SHOW_EDIT_PLAN_TITLE_MODAL,
HIDE_EDIT_PLAN_TITLE_MODAL,
V2V_FETCH_CLUSTERS,
V2V_RETRY_MIGRATION,
V2V_SCHEDULE_MIGRATION,
Expand Down Expand Up @@ -76,6 +78,19 @@ export const showPlanWizardEditModeAction = id => dispatch => {
});
};

export const showEditPlanNameModalAction = id => dispatch => {
dispatch({
type: SHOW_EDIT_PLAN_TITLE_MODAL,
editingPlanId: id
});
};

export const hideEditPlanNameModalAction = () => dispatch => {
dispatch({
type: HIDE_EDIT_PLAN_TITLE_MODAL
});
};

export const fetchProvidersAction = () => dispatch => {
dispatch({
type: FETCH_PROVIDERS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const MAPPING_WIZARD_EXITED = 'MAPPING_WIZARD_EXITED';
export const SHOW_PLAN_WIZARD = 'SHOW_PLAN_WIZARD';
export const SHOW_PLAN_WIZARD_EDIT_MODE = 'SHOW_PLAN_WIZARD_EDIT_MODE';
export const HIDE_PLAN_WIZARD = 'HIDE_PLAN_WIZARD';
export const SHOW_EDIT_PLAN_TITLE_MODAL = 'SHOW_EDIT_PLAN_TITLE_MODAL';
export const HIDE_EDIT_PLAN_TITLE_MODAL = 'HIDE_EDIT_PLAN_TITLE_MODAL';
export const PLAN_WIZARD_EXITED = 'PLAN_WIZARD_EXITED';
export const PLAN_WIZARD_NEXT = 'PLAN_WIZARD_NEXT';
export const PLAN_WIZARD_BACK = 'PLAN_WIZARD_BACK';
Expand Down
7 changes: 7 additions & 0 deletions app/javascript/react/screens/App/Overview/OverviewReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
SHOW_PLAN_WIZARD,
HIDE_PLAN_WIZARD,
PLAN_WIZARD_EXITED,
SHOW_EDIT_PLAN_TITLE_MODAL,
HIDE_EDIT_PLAN_TITLE_MODAL,
FETCH_PROVIDERS,
FETCH_V2V_TRANSFORMATION_MAPPINGS,
FETCH_V2V_TRANSFORMATION_PLANS,
Expand Down Expand Up @@ -55,6 +57,7 @@ export const initialState = Immutable({
hidePlanWizard: true,
planWizardId: null, // id of infrastructure mapping to use for new plan
editingPlanId: null, // id of migration plan to edit
editPlanNameModalVisible: false,
hasSufficientProviders: false,
isRejectedProviders: false,
isFetchingProviders: false,
Expand Down Expand Up @@ -157,6 +160,10 @@ export default (state = initialState, action) => {
.set('editingPlanId', null)
.set('shouldReloadMappings', (payload && payload.shouldReloadMappings) || false);
}
case SHOW_EDIT_PLAN_TITLE_MODAL:
return state.set('editingPlanId', action.editingPlanId).set('editPlanNameModalVisible', true);
case HIDE_EDIT_PLAN_TITLE_MODAL:
return state.set('editingPlanId', null).set('editPlanNameModalVisible', false);
case PLAN_WIZARD_EXITED:
return state.set('planWizardVisible', false);
case `${FETCH_PROVIDERS}_PENDING`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Object {
"finishedWithErrorTransformationPlans": Array [],
"hideConfirmModalAction": [Function],
"hideDeleteConfirmationModalAction": [Function],
"hideEditPlanNameModalAction": [Function],
"hideMappingWizard": false,
"hidePlanWizard": false,
"isFetchingArchivedTransformationPlans": "true",
Expand Down Expand Up @@ -59,6 +60,7 @@ Object {
"setMigrationsFilterAction": [Function],
"showConfirmModalAction": [Function],
"showDeleteConfirmationModalAction": [Function],
"showEditPlanNameModalAction": [Function],
"showMappingWizardAction": [Function],
"showMappingWizardEditModeAction": [Function],
"showPlanWizardAction": [Function],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { V2V_POST_EDIT_PLAN_NAME } from './EditPlanNameConstants';
import API from '../../../../../../common/API';

export { fetchTransformationPlansAction } from '../../OverviewActions';

const _editMigrationPlansActionCreator = (url, planId, migrationPlans) => dispatch => {
const body = {
action: 'edit',
resource: { ...migrationPlans }
};
return dispatch({
type: V2V_POST_EDIT_PLAN_NAME,
payload: API.post(`${url}/${planId}`, body)
});
};

export const editMigrationPlansAction = (url, planId, migrationPlans) =>
_editMigrationPlansActionCreator(url, planId, migrationPlans);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const V2V_POST_EDIT_PLAN_NAME = 'V2V_POST_EDIT_PLAN_NAME';
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { required } from 'redux-form-validators';
import { Modal, Button, Form, Spinner, noop } from 'patternfly-react';
import { FormField } from '../../../common/forms/FormField';
import { validation } from '../../../../../../common/constants';
import { asyncValidate, onChange } from '../../screens/PlanWizard/components/PlanWizardGeneralStep/helpers';

class EditPlanNameModal extends React.Component {
onSubmit = () => {
const {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 4 locations. Consider refactoring.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 6 locations. Consider refactoring.

editPlanNameModal,
editingPlan,
editMigrationPlansAction,
editMigrationPlansUrl,
hideEditPlanNameModalAction,
fetchTransformationPlansAction,
fetchTransformationPlansUrl,
fetchArchivedTransformationPlansUrl
} = this.props;
const {
values: { name, description }
} = editPlanNameModal;
const resource = { name, description };
editMigrationPlansAction(editMigrationPlansUrl, editingPlan.id, resource).then(() => {
hideEditPlanNameModalAction();
fetchTransformationPlansAction({
url: fetchTransformationPlansUrl,
archived: false
});
fetchTransformationPlansAction({
url: fetchArchivedTransformationPlansUrl,
archived: true
});
});
};

render() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function render has 71 lines of code (exceeds 25 allowed). Consider refactoring.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function render has 60 lines of code (exceeds 25 allowed). Consider refactoring.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function render has 53 lines of code (exceeds 25 allowed). Consider refactoring.

const { editPlanNameModalVisible, hideEditPlanNameModalAction, editPlanNameModal, savingPlan } = this.props;

const disableConfirmButton = savingPlan || !!editPlanNameModal.syncErrors || !!editPlanNameModal.asyncErrors;

const formBody = (
<Form horizontal>
<Field
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identical blocks of code found in 3 locations. Consider refactoring.

name="name"
label={__('Name')}
required
component={FormField}
type="text"
help={validation.name.help}
maxLength={validation.name.maxLength}
maxLengthWarning={validation.name.maxLengthWarning}
validate={[
required({
msg: validation.name.requiredMessage
})
]}
/>
<Field
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identical blocks of code found in 3 locations. Consider refactoring.

name="description"
label={__('Description')}
component={FormField}
type="textarea"
help={validation.description.help}
maxLength={validation.description.maxLength}
maxLengthWarning={validation.description.maxLengthWarning}
/>
</Form>
);

const spinner = (
<div style={{ marginTop: 15 }}>
<Spinner loading size="lg" />
<h2 style={{ textAlign: 'center' }}>{__('Saving...')}</h2>
</div>
);

return (
<Modal show={editPlanNameModalVisible} onHide={hideEditPlanNameModalAction}>
<Modal.Header>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 3 locations. Consider refactoring.

<Modal.CloseButton onClick={hideEditPlanNameModalAction} />
<Modal.Title>{__('Edit Migration Plan')}</Modal.Title>
</Modal.Header>
<Modal.Body>{!savingPlan ? formBody : spinner}</Modal.Body>
<Modal.Footer>
<Button bsStyle="default" className="btn-cancel" onClick={hideEditPlanNameModalAction}>
{__('Cancel')}
</Button>
<Button bsStyle="primary" onClick={this.onSubmit} disabled={disableConfirmButton}>
{__('Save')}
</Button>
</Modal.Footer>
</Modal>
);
}
}

EditPlanNameModal.propTypes = {
editPlanNameModalVisible: PropTypes.bool,
hideEditPlanNameModalAction: PropTypes.func,
editPlanNameModal: PropTypes.object,
editingPlan: PropTypes.object,
editMigrationPlansAction: PropTypes.func,
editMigrationPlansUrl: PropTypes.string,
savingPlan: PropTypes.bool.isRequired,
fetchTransformationPlansAction: PropTypes.func,
fetchTransformationPlansUrl: PropTypes.string,
fetchArchivedTransformationPlansUrl: PropTypes.string
};

EditPlanNameModal.defaultProps = {
editPlanNameModalVisible: false,
hideEditPlanNameModalAction: noop,
editPlanNameModal: {},
editMigrationPlansUrl: '/api/service_templates'
};

export default reduxForm({
form: 'editPlanNameModal',
asyncValidate,
asyncBlurFields: ['name'],
onChange
})(EditPlanNameModal);
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Immutable from 'seamless-immutable';
import {
V2V_EDIT_PLAN_TITLE_SHOW_ALERT,
V2V_EDIT_PLAN_TITLE_HIDE_ALERT,
V2V_POST_EDIT_PLAN_NAME
} from './EditPlanNameConstants';

const initialState = Immutable({
alertText: '',
savingPlan: false,
savingPlanRejected: false,
savingPlanError: null
});

export default (state = initialState, action) => {
switch (action.type) {
case V2V_EDIT_PLAN_TITLE_SHOW_ALERT:
return Immutable.merge(state, action.payload);
case V2V_EDIT_PLAN_TITLE_HIDE_ALERT:
return state.set('alertText', '');
case `${V2V_POST_EDIT_PLAN_NAME}_PENDING`:
return state
.set('savingPlan', true)
.set('savingPlanRejected', false)
.set('savingPlanError', null);
case `${V2V_POST_EDIT_PLAN_NAME}_FULFILLED`:
return state
.set('savingPlan', false)
.set('savingPlanRejected', false)
.set('savingPlanError', null);
case `${V2V_POST_EDIT_PLAN_NAME}_REJECTED`:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 6 locations. Consider refactoring.

return state
.set('savingPlan', false)
.set('savingPlanRejected', true)
.set('savingPlanError', action.payload);
default:
return state;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { connect } from 'react-redux';
import EditPlanNameModal from './EditPlanNameModal';
import { findEditingPlan } from '../../screens/PlanWizard/PlanWizardSelectors';
import * as EditPlanNameActions from './EditPlanNameActions';

import reducer from './EditPlanNameReducer';

export const reducers = { editPlanName: reducer };

const mapStateToProps = ({ editPlanName, overview, form }) => {
const plans = [...overview.transformationPlans, ...overview.archivedTransformationPlans];
const editingPlan = findEditingPlan(plans, overview.editingPlanId);
return {
...editPlanName,
editPlanNameModal: form && form.editPlanNameModal,
initialValues: {
name: editingPlan ? editingPlan.name : '',
description: editingPlan ? editingPlan.description : ''
},
enableReinitialize: true,
editingPlan
};
};

export default connect(
mapStateToProps,
EditPlanNameActions
)(EditPlanNameModal);
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ const Migrations = ({
plansMutatedWithMappingInfo,
showPlanWizardEditModeAction,
fetchTransformationMappingsUrl,
fetchTransformationMappingsAction
fetchTransformationMappingsAction,
showEditPlanNameModalAction
}) => {
const filterOptions = [
MIGRATIONS_FILTERS.notStarted,
Expand Down Expand Up @@ -167,6 +168,7 @@ const Migrations = ({
plansMutatedWithMappingInfo={plansMutatedWithMappingInfo}
fetchTransformationMappingsAction={fetchTransformationMappingsAction}
fetchTransformationMappingsUrl={fetchTransformationMappingsUrl}
showEditPlanNameModalAction={showEditPlanNameModalAction}
/>
)}
{activeFilter === MIGRATIONS_FILTERS.archived && (
Expand All @@ -187,6 +189,7 @@ const Migrations = ({
fetchArchivedTransformationPlansUrl={fetchArchivedTransformationPlansUrl}
fetchTransformationMappingsAction={fetchTransformationMappingsAction}
fetchTransformationMappingsUrl={fetchTransformationMappingsUrl}
showEditPlanNameModalAction={showEditPlanNameModalAction}
/>
)}
</div>
Expand Down Expand Up @@ -229,7 +232,8 @@ Migrations.propTypes = {
plansMutatedWithMappingInfo: PropTypes.bool,
showPlanWizardEditModeAction: PropTypes.func,
fetchTransformationMappingsAction: PropTypes.func,
fetchTransformationMappingsUrl: PropTypes.string
fetchTransformationMappingsUrl: PropTypes.string,
showEditPlanNameModalAction: PropTypes.func
};
Migrations.defaultProps = {
transformationPlans: [],
Expand Down
Loading