Skip to content

Commit 680155b

Browse files
Merge pull request #9301 from elsamaryv/class-form-converison
Transforms haml forms to react for Add and Edit feature of Automate Class
2 parents 2896bba + a82a615 commit 680155b

File tree

10 files changed

+656
-38
lines changed

10 files changed

+656
-38
lines changed

app/controllers/miq_ae_class_controller.rb

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ def set_right_cell_text(id, rec = nil)
131131
txt = rec.domain? ? _('Automate Domain') : _('Automate Namespace')
132132
@sb[:namespace_path] = rec.fqname
133133
end
134-
@sb[:namespace_path]&.gsub!(%r{\/}, " / ")
135134
@right_cell_text = "#{txt} #{_("\"%s\"") % get_rec_name(rec)}" unless %w[root aei aem].include?(nodes[0])
136135
end
137136

@@ -341,6 +340,9 @@ def replace_right_cell(options = {})
341340
:serialize => @sb[:active_tab] == 'methods',
342341
}
343342
])
343+
if @hide_bottom_bar
344+
presenter.hide(:paging_div, :form_buttons_div)
345+
end
344346
else
345347
# incase it was hidden for summary screen, and incase there were no records on show_list
346348
presenter.hide(:paging_div, :form_buttons_div)
@@ -459,6 +461,7 @@ def edit_class
459461
@ae_class = find_record_with_rbac(MiqAeClass, params[:id])
460462
end
461463
set_form_vars
464+
@hide_bottom_bar = true
462465
# have to get name and set node info, to load multiple tabs correctly
463466
# rec_name = get_rec_name(@ae_class)
464467
# get_node_info("aec-#{@ae_class.id}")
@@ -468,6 +471,24 @@ def edit_class
468471
replace_right_cell
469472
end
470473

474+
def edit_class_record
475+
assert_privileges("miq_ae_class_edit")
476+
unless params[:id]
477+
obj = find_checked_items
478+
@_params[:id] = obj[0]
479+
end
480+
@hide_bottom_bar = true
481+
482+
class_rec = MiqAeClass.find(params[:id])
483+
484+
render :json => {
485+
:fqname => class_rec.fqname,
486+
:name => class_rec.name,
487+
:display_name => class_rec.display_name,
488+
:description => class_rec.description
489+
}
490+
end
491+
471492
def edit_fields
472493
assert_privileges("miq_ae_field_edit")
473494
if params[:pressed] == "miq_ae_item_edit" # came from Namespace details screen
@@ -1291,6 +1312,7 @@ def new
12911312
assert_privileges("miq_ae_class_new")
12921313
@ae_class = MiqAeClass.new
12931314
set_form_vars
1315+
@hide_bottom_bar = true
12941316
@in_a_form = true
12951317
replace_right_cell
12961318
end
@@ -1841,8 +1863,50 @@ def ae_method_operations
18411863
end
18421864
end
18431865

1866+
def class_update
1867+
assert_privileges(params[:id].present? ? 'miq_ae_class_edit' : 'miq_ae_class_new')
1868+
@hide_bottom_bar = true
1869+
class_update_create
1870+
end
1871+
18441872
private
18451873

1874+
def class_update_create
1875+
case params[:button]
1876+
when "add", "save"
1877+
class_rec = params[:id].blank? ? MiqAeClass.new : MiqAeClass.find(params[:id]) # Get new or existing record
1878+
add_flash(_("Name is required"), :error) if params[:name].blank?
1879+
class_rec.name = params[:name]
1880+
class_rec.display_name = params[:display_name]
1881+
class_rec.description = params[:description]
1882+
class_rec.namespace_id = x_node.split('-')[1] if params[:id].blank?
1883+
begin
1884+
class_rec.save!
1885+
rescue StandardError
1886+
errors = []
1887+
class_rec.errors.each do |error|
1888+
errors.push("#{error.attribute.to_s.capitalize} #{error.message}")
1889+
end
1890+
@changed = true
1891+
render :json => {:error => errors, :status => 500}
1892+
else
1893+
edit_hash = {}
1894+
edit_hash[:new] = {:name => params[:name],
1895+
:display_name => params[:display_name], :description => params[:description]}
1896+
edit_hash[:current] = if params[:old_data]
1897+
{:name => params[:old_data][:name],
1898+
:display_name => params[:old_data][:display_name],
1899+
:description => params[:old_data][:description]}
1900+
else
1901+
{:name => nil, :display_name => nil, :description => nil}
1902+
end
1903+
AuditEvent.success(build_saved_audit(class_rec, edit_hash))
1904+
@edit = session[:edit] = nil # clean out the saved info
1905+
render :json => {:status => 200}
1906+
end
1907+
end
1908+
end
1909+
18461910
def get_template_class(location)
18471911
if location == "ansible_workflow_template"
18481912
ManageIQ::Providers::ExternalAutomationManager::ConfigurationWorkflow
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { componentTypes } from '@@ddf';
2+
3+
const createSchema = (fqname) => ({
4+
fields: [
5+
{
6+
component: componentTypes.TEXT_FIELD,
7+
name: 'fqname',
8+
label: 'Fully Qualified Name',
9+
value: `${fqname}`,
10+
disabled: true,
11+
},
12+
{
13+
component: componentTypes.TEXT_FIELD,
14+
id: 'name',
15+
name: 'name',
16+
label: __('Name'),
17+
maxLength: 128,
18+
validate: [{ type: 'customValidatorForNameField' }],
19+
isRequired: true,
20+
},
21+
{
22+
component: componentTypes.TEXT_FIELD,
23+
id: 'display_name',
24+
name: 'display_name',
25+
label: __('Display Name'),
26+
maxLength: 128,
27+
},
28+
{
29+
component: componentTypes.TEXT_FIELD,
30+
id: 'description',
31+
name: 'description',
32+
label: __('Description'),
33+
maxLength: 255,
34+
},
35+
],
36+
});
37+
38+
export default createSchema;
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { FormSpy } from '@data-driven-forms/react-form-renderer';
3+
import { Button } from 'carbon-components-react';
4+
import MiqFormRenderer, { useFormApi } from '@@ddf';
5+
import PropTypes from 'prop-types';
6+
import createSchema from './class-form.schema';
7+
import miqRedirectBack from '../../helpers/miq-redirect-back';
8+
import miqFlash from '../../helpers/miq-flash';
9+
10+
const MiqAeClass = ({ classRecord, fqname }) => {
11+
const formattedFqname = fqname.replace(/\s+/g, '');
12+
const [data, setData] = useState({
13+
isLoading: true,
14+
initialValues: undefined,
15+
});
16+
17+
const isEdit = !!(classRecord && classRecord.id);
18+
19+
useEffect(() => {
20+
if (isEdit) {
21+
http.get(`/miq_ae_class/edit_class_record/${classRecord.id}/`).then((recordValues) => {
22+
if (recordValues) {
23+
setData({ ...data, isLoading: false, initialValues: recordValues });
24+
}
25+
});
26+
} else {
27+
const initialValues = {
28+
formattedFqname,
29+
name: classRecord && classRecord.name,
30+
display_name: classRecord && classRecord.display_name,
31+
description: classRecord && classRecord.description,
32+
};
33+
setData({ ...data, isLoading: false, initialValues });
34+
}
35+
}, [classRecord]);
36+
37+
const onSubmit = (values) => {
38+
miqSparkleOn();
39+
40+
const params = {
41+
action: isEdit ? 'edit' : 'create',
42+
name: values.name,
43+
display_name: values.display_name,
44+
description: values.description,
45+
old_data: data.initialValues,
46+
button: classRecord.id ? 'save' : 'add',
47+
};
48+
49+
const request = isEdit
50+
? http.post(`/miq_ae_class/class_update/${classRecord.id}`, params)
51+
: http.post(`/miq_ae_class/class_update/`, params);
52+
53+
request
54+
.then((response) => {
55+
if (response.status === 200) {
56+
const confirmation = isEdit ? __(`Class "%s" was saved`) : __(`Class "%s" was added`);
57+
const message = sprintf(confirmation, values.name);
58+
miqRedirectBack(message, 'success', '/miq_ae_class/explorer');
59+
} else {
60+
miqSparkleOff();
61+
miqFlash('error', response.error);
62+
}
63+
})
64+
.catch(miqSparkleOff);
65+
};
66+
67+
const onCancel = () => {
68+
const confirmation = classRecord.id ? __(`Edit of Class "%s" cancelled by the user`)
69+
: __(`Add of new Class was cancelled by the user`);
70+
const message = sprintf(confirmation, classRecord.name);
71+
miqRedirectBack(message, 'warning', '/miq_ae_class/explorer');
72+
};
73+
74+
const customValidatorMapper = {
75+
customValidatorForNameField: () => (value) => {
76+
if (!value) {
77+
return __('Required');
78+
}
79+
if (!value.match('^[a-zA-Z0-9_.-]*$')) {
80+
return __('Name may contain only alphanumeric and _ . - characters');
81+
}
82+
return false;
83+
},
84+
};
85+
86+
return (!data.isLoading
87+
? (
88+
<div className="dialog-provision-form">
89+
<MiqFormRenderer
90+
schema={createSchema(formattedFqname)}
91+
initialValues={data.initialValues}
92+
validatorMapper={customValidatorMapper}
93+
onSubmit={onSubmit}
94+
onCancel={onCancel}
95+
canReset={!!classRecord.id}
96+
validate={() => {}}
97+
FormTemplate={(props) => <FormTemplate {...props} recId={classRecord.id} />}
98+
/>
99+
</div>
100+
) : null
101+
);
102+
};
103+
104+
const FormTemplate = ({
105+
formFields, recId,
106+
}) => {
107+
const {
108+
handleSubmit, onReset, onCancel, getState,
109+
} = useFormApi();
110+
const { valid, pristine } = getState();
111+
const submitLabel = !!recId ? __('Save') : __('Add');
112+
return (
113+
<form onSubmit={handleSubmit}>
114+
{formFields}
115+
<FormSpy>
116+
{() => (
117+
<div className="custom-button-wrapper">
118+
{ !recId
119+
? (
120+
<Button
121+
disabled={!valid}
122+
kind="primary"
123+
className="btnRight"
124+
type="submit"
125+
variant="contained"
126+
>
127+
{submitLabel}
128+
</Button>
129+
) : (
130+
<Button
131+
disabled={!valid || pristine}
132+
kind="primary"
133+
className="btnRight"
134+
type="submit"
135+
variant="contained"
136+
>
137+
{submitLabel}
138+
</Button>
139+
)}
140+
{!!recId
141+
? (
142+
<Button
143+
disabled={pristine}
144+
kind="secondary"
145+
className="btnRight"
146+
variant="contained"
147+
onClick={onReset}
148+
type="button"
149+
>
150+
{ __('Reset')}
151+
</Button>
152+
) : null}
153+
154+
<Button variant="contained" type="button" onClick={onCancel} kind="secondary">
155+
{ __('Cancel')}
156+
</Button>
157+
</div>
158+
)}
159+
</FormSpy>
160+
</form>
161+
);
162+
};
163+
164+
MiqAeClass.propTypes = {
165+
classRecord: PropTypes.shape({
166+
id: PropTypes.number,
167+
name: PropTypes.string,
168+
display_name: PropTypes.string,
169+
description: PropTypes.string,
170+
}),
171+
fqname: PropTypes.string.isRequired,
172+
};
173+
174+
MiqAeClass.defaultProps = {
175+
classRecord: undefined,
176+
};
177+
178+
FormTemplate.propTypes = {
179+
formFields: PropTypes.arrayOf(
180+
PropTypes.shape({ id: PropTypes.number }),
181+
PropTypes.shape({ name: PropTypes.string }),
182+
PropTypes.shape({ display_name: PropTypes.string }),
183+
PropTypes.shape({ description: PropTypes.string }),
184+
),
185+
recId: PropTypes.number,
186+
};
187+
188+
FormTemplate.defaultProps = {
189+
formFields: undefined,
190+
recId: undefined,
191+
};
192+
193+
export default MiqAeClass;

app/javascript/packs/component-definitions-common.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ import WorkflowPayload from '../components/workflows/workflow_payload';
178178
import WorkflowRepositoryForm from '../components/workflow-repository-form';
179179
import XmlHolder from '../components/XmlHolder';
180180
import ZoneForm from '../components/zone-form';
181+
import MiqAeClass from '../components/miq-ae-class';
181182

182183
/**
183184
* Add component definitions to this file.
@@ -365,3 +366,4 @@ ManageIQ.component.addReact('WorkflowPayload', WorkflowPayload);
365366
ManageIQ.component.addReact('WorkflowRepositoryForm', WorkflowRepositoryForm);
366367
ManageIQ.component.addReact('XmlHolder', XmlHolder);
367368
ManageIQ.component.addReact('ZoneForm', ZoneForm);
369+
ManageIQ.component.addReact('MiqAeClass', MiqAeClass);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`MiqAeClass Form Component should render add class form correctly 1`] = `""`;
4+
5+
exports[`MiqAeClass Form Component should render edit class form correctly 1`] = `""`;

0 commit comments

Comments
 (0)