Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transforms haml forms to react for Add and Edit feature of Automate Class #9301

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
65 changes: 65 additions & 0 deletions app/controllers/miq_ae_class_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,9 @@ def replace_right_cell(options = {})
:serialize => @sb[:active_tab] == 'methods',
}
])
if @hide_bottom_bar
presenter.hide(:paging_div, :form_buttons_div)
end
else
# incase it was hidden for summary screen, and incase there were no records on show_list
presenter.hide(:paging_div, :form_buttons_div)
Expand Down Expand Up @@ -459,6 +462,7 @@ def edit_class
@ae_class = find_record_with_rbac(MiqAeClass, params[:id])
end
set_form_vars
@hide_bottom_bar = true
# have to get name and set node info, to load multiple tabs correctly
# rec_name = get_rec_name(@ae_class)
# get_node_info("aec-#{@ae_class.id}")
Expand All @@ -468,6 +472,24 @@ def edit_class
replace_right_cell
end

def edit_class_record
assert_privileges("miq_ae_class_edit")
unless params[:id]
obj = find_checked_items
@_params[:id] = obj[0]
end
@hide_bottom_bar = true

class_rec = MiqAeClass.find(params[:id])

render :json => {
:fqname => class_rec.fqname,
:name => class_rec.name,
:display_name => class_rec.display_name,
:description => class_rec.description
}
end

def edit_fields
assert_privileges("miq_ae_field_edit")
if params[:pressed] == "miq_ae_item_edit" # came from Namespace details screen
Expand Down Expand Up @@ -1291,6 +1313,7 @@ def new
assert_privileges("miq_ae_class_new")
@ae_class = MiqAeClass.new
set_form_vars
@hide_bottom_bar = true
@in_a_form = true
replace_right_cell
end
Expand Down Expand Up @@ -1841,8 +1864,50 @@ def ae_method_operations
end
end

def class_update
assert_privileges(params[:id].present? ? 'miq_ae_class_edit' : 'miq_ae_class_new')
@hide_bottom_bar = true
class_update_create
end

private

def class_update_create
case params[:button]
when "add", "save"
class_rec = params[:id].blank? ? MiqAeClass.new : MiqAeClass.find(params[:id]) # Get new or existing record
add_flash(_("Name is required"), :error) if params[:name].blank?
class_rec.name = params[:name]
class_rec.display_name = params[:display_name]
class_rec.description = params[:description]
class_rec.namespace_id = x_node.split('-')[1] if params[:id].blank?
begin
class_rec.save!
rescue StandardError
errors = []
class_rec.errors.each do |error|
errors.push("#{error.attribute.to_s.capitalize} #{error.message}")
end
@changed = true
render :json => {:error => errors, :status => 500}
else
edit_hash = {}
edit_hash[:new] = {:name => params[:name],
:display_name => params[:display_name], :description => params[:description]}
edit_hash[:current] = if params[:old_data]
{:name => params[:old_data][:name],
:display_name => params[:old_data][:display_name],
:description => params[:old_data][:description]}
else
{:name => nil, :display_name => nil, :description => nil}
end
AuditEvent.success(build_saved_audit(class_rec, edit_hash))
@edit = session[:edit] = nil # clean out the saved info
render :json => {:status => 200}
end
end
end

def get_template_class(location)
if location == "ansible_workflow_template"
ManageIQ::Providers::ExternalAutomationManager::ConfigurationWorkflow
Expand Down
36 changes: 36 additions & 0 deletions app/javascript/components/miq-ae-class/class-form.schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { componentTypes } from '@@ddf';

const createSchema = (fqname) => ({
fields: [
{
component: componentTypes.PLAIN_TEXT,
name: 'fqname',
label: `${__('Fully Qualified Name')}:\t ${fqname}`,
},
{
component: componentTypes.TEXT_FIELD,
id: 'name',
name: 'name',
label: __('Name'),
maxLength: 128,
validate: [{ type: 'customValidatorForNameField' }],
isRequired: true,
},
{
component: componentTypes.TEXT_FIELD,
id: 'display_name',
name: 'display_name',
label: __('Display Name'),
maxLength: 128,
},
{
component: componentTypes.TEXT_FIELD,
id: 'description',
name: 'description',
label: __('Description'),
maxLength: 255,
},
],
});

export default createSchema;
193 changes: 193 additions & 0 deletions app/javascript/components/miq-ae-class/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import React, { useState, useEffect } from 'react';
import { FormSpy } from '@data-driven-forms/react-form-renderer';
import { Button } from 'carbon-components-react';
import MiqFormRenderer, { useFormApi } from '@@ddf';
import PropTypes from 'prop-types';
import createSchema from './class-form.schema';
import miqRedirectBack from '../../helpers/miq-redirect-back';
import miqFlash from '../../helpers/miq-flash';

const MiqAeClass = ({ classRecord, fqname }) => {
const [data, setData] = useState({
isLoading: true,
initialValues: undefined,
});

const isEdit = !!(classRecord && classRecord.id);

useEffect(() => {
if (isEdit) {
http.get(`/miq_ae_class/edit_class_record/${classRecord.id}/`).then((recordValues) => {
if (recordValues) {
setData({ ...data, isLoading: false, initialValues: recordValues });
}
});
} else {
const initialValues = {
fqname,
name: classRecord && classRecord.name,
display_name: classRecord && classRecord.display_name,
description: classRecord && classRecord.description,
};
setData({ ...data, isLoading: false, initialValues });
}
}, [classRecord]);

const onSubmit = (values) => {
miqSparkleOn();

const params = {
action: isEdit ? 'edit' : 'create',
name: values.name,
display_name: values.display_name,
description: values.description,
old_data: data.initialValues,
button: classRecord.id ? 'save' : 'add',
};

const request = isEdit
? http.post(`/miq_ae_class/class_update/${classRecord.id}`, params)
: http.post(`/miq_ae_class/class_update/`, params);

request
.then((response) => {
if (response.status === 200) {
const confirmation = isEdit ? __(`Class "${values.name}" was saved`) : __(`Class "${values.name}" was added`);
Copy link
Member

Choose a reason for hiding this comment

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

For translations in the UI code, you have to use placeholders within the translated text and then fill them in afterwards with something like sprintf. See https://github.com/ManageIQ/guides/blob/master/i18n.md#javascript, particularly the caveats section. I think this might work:

Suggested change
const confirmation = isEdit ? __(`Class "${values.name}" was saved`) : __(`Class "${values.name}" was added`);
let confirmation = isEdit ? __(`Class "%s" was saved`) : __(`Class "%s" was added`);
confirmation = sprintf(confirmation, value.name);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing that out. I've updated that part.

miqRedirectBack(sprintf(confirmation, values.name), 'success', '/miq_ae_class/explorer');
} else {
miqSparkleOff();
miqFlash('error', response.error);
}
})
.catch(miqSparkleOff);
};

const onCancel = () => {
const confirmation = classRecord.id ? __(`Edit of Class "${classRecord.name}" cancelled by the user`)
Copy link
Member

Choose a reason for hiding this comment

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

Similar case here with i18n

: __(`Add of new Class was cancelled by the user`);
const message = sprintf(
confirmation
);
miqRedirectBack(message, 'warning', '/miq_ae_class/explorer');
};

const customValidatorMapper = {
customValidatorForNameField: () => (value) => {
if (!value) {
return __('Required');
}
if (!value.match('^[a-zA-Z0-9_.-]*$')) {
return __('Name may contain only alphanumeric and _ . - characters');
}
return false;
},
};

return (!data.isLoading
? (
<div className="dialog-provision-form">
<MiqFormRenderer
schema={createSchema(fqname)}
initialValues={data.initialValues}
validatorMapper={customValidatorMapper}
onSubmit={onSubmit}
onCancel={onCancel}
canReset={!!classRecord.id}
validate={() => {}}
FormTemplate={(props) => <FormTemplate {...props} recId={classRecord.id} />}
/>
</div>
) : null
);
};

const FormTemplate = ({
formFields, recId,
}) => {
const {
handleSubmit, onReset, onCancel, getState,
} = useFormApi();
const { valid, pristine } = getState();
const submitLabel = !!recId ? __('Save') : __('Add');
return (
<form onSubmit={handleSubmit}>
{formFields}
<FormSpy>
{() => (
<div className="custom-button-wrapper">
{ !recId
? (
<Button
disabled={!valid}
kind="primary"
className="btnRight"
type="submit"
variant="contained"
>
{submitLabel}
</Button>
) : (
<Button
disabled={!valid || pristine}
kind="primary"
className="btnRight"
type="submit"
variant="contained"
>
{submitLabel}
</Button>
)}
{!!recId
? (
<Button
disabled={pristine}
kind="secondary"
className="btnRight"
variant="contained"
onClick={onReset}
type="button"
>
{ __('Reset')}
</Button>
) : null}

<Button variant="contained" type="button" onClick={onCancel} kind="secondary">
{ __('Cancel')}
</Button>
</div>
)}
</FormSpy>
</form>
);
};

MiqAeClass.propTypes = {
classRecord: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
display_name: PropTypes.string,
description: PropTypes.string,
}),
fqname: PropTypes.string.isRequired,
};

MiqAeClass.defaultProps = {
classRecord: undefined,
};

FormTemplate.propTypes = {
formFields: PropTypes.arrayOf(
PropTypes.shape({ id: PropTypes.number }),
PropTypes.shape({ name: PropTypes.string }),
PropTypes.shape({ display_name: PropTypes.string }),
PropTypes.shape({ description: PropTypes.string }),
),
recId: PropTypes.number,
};

FormTemplate.defaultProps = {
formFields: undefined,
recId: undefined,
};

export default MiqAeClass;
2 changes: 2 additions & 0 deletions app/javascript/packs/component-definitions-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ import WorkflowPayload from '../components/workflows/workflow_payload';
import WorkflowRepositoryForm from '../components/workflow-repository-form';
import XmlHolder from '../components/XmlHolder';
import ZoneForm from '../components/zone-form';
import MiqAeClass from '../components/miq-ae-class';

/**
* Add component definitions to this file.
Expand Down Expand Up @@ -363,3 +364,4 @@ ManageIQ.component.addReact('WorkflowPayload', WorkflowPayload);
ManageIQ.component.addReact('WorkflowRepositoryForm', WorkflowRepositoryForm);
ManageIQ.component.addReact('XmlHolder', XmlHolder);
ManageIQ.component.addReact('ZoneForm', ZoneForm);
ManageIQ.component.addReact('MiqAeClass', MiqAeClass);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MiqAeClass Form Component should render add class form correctly 1`] = `""`;

exports[`MiqAeClass Form Component should render edit class form correctly 1`] = `""`;
Loading