Skip to content

Commit 174d0b3

Browse files
committed
Automate Method Form Conversion From ANGULAR to REACT
1 parent 6e85753 commit 174d0b3

File tree

15 files changed

+824
-4
lines changed

15 files changed

+824
-4
lines changed

app/controllers/miq_ae_class_controller.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,25 @@ def ae_class_for_instance_or_method(record)
804804
record.id ? record.ae_class : MiqAeClass.find(x_node.split("-").last)
805805
end
806806

807+
def validate_automate_method_data
808+
assert_privileges("miq_ae_method_edit")
809+
@edit[:new][:data] = params[:cls_method_data] if params[:cls_method_data]
810+
@edit[:new][:data] = params[:method_data] if params[:method_data]
811+
res = MiqAeMethod.validate_syntax(@edit[:new][:data])
812+
line = 0
813+
if !res
814+
render :json => {:status => true, :message => _("Data validated successfully")}
815+
else
816+
res.each do |err|
817+
line = err[0] if line.zero?
818+
render :json => {
819+
:status => false,
820+
:message => (_("Error on line %{line_num}: %{err_txt}") % {:line_num => err[0], :err_txt => err[1]})
821+
}
822+
end
823+
end
824+
end
825+
807826
def validate_method_data
808827
assert_privileges("miq_ae_method_edit")
809828
return unless load_edit("aemethod_edit__#{params[:id]}", "replace_cell__explorer")
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React, { useState, useEffect, useContext } from 'react';
2+
import { Button, Accordion, AccordionItem } from 'carbon-components-react';
3+
import { Controlled as CodeMirror } from 'react-codemirror2';
4+
import { http } from '../../../http_api';
5+
import NotificationMessage from '../../notification-message';
6+
import AutomateMethodContext from '../automate-method-context';
7+
8+
const AutomateMethodCodeMirror = () => {
9+
const { updateCodeEditor } = useContext(AutomateMethodContext);
10+
11+
const defaultEditorContents = `#\n# Description: <Method description here>\n#\n`;
12+
13+
const [data, setData] = useState({
14+
editorContents: defaultEditorContents,
15+
enableValidationButton: false,
16+
validation: undefined,
17+
});
18+
19+
useEffect(() => {
20+
updateCodeEditor(data.editorContents);
21+
}, [data.validation]);
22+
23+
const validate = () => {
24+
const formData = { cls_method_data: data.editorContents };
25+
http.post('/miq_ae_class/validate_automate_method_data/new?button=validate', formData).then((response) => {
26+
setData({
27+
...data,
28+
validation: response,
29+
});
30+
});
31+
};
32+
33+
const renderValidateButton = () => (
34+
<div className="custom-form-buttons">
35+
<Button kind="primary" size="sm" onClick={validate}>Validate</Button>
36+
</div>
37+
);
38+
39+
return (
40+
<div className="automate-code-mirror custom-form-wrapper">
41+
<Accordion align="start" className="miq-custom-form-accordion">
42+
<AccordionItem title={__('Data')} open>
43+
{
44+
data.validation && <NotificationMessage type={data.validation.status ? 'success' : 'error'} message={data.validation.message} />
45+
}
46+
<CodeMirror
47+
className="miq-codemirror miq-structured-list-code-mirror"
48+
options={{
49+
mode: 'ruby',
50+
lineNumbers: true,
51+
matchBrackets: true,
52+
theme: 'eclipse',
53+
viewportMargin: Infinity,
54+
readOnly: false,
55+
}}
56+
onBeforeChange={(_editor, _data, value) => setData({ ...data, validation: undefined, editorContents: value })}
57+
value={data.editorContents}
58+
/>
59+
{renderValidateButton()}
60+
</AccordionItem>
61+
</Accordion>
62+
</div>
63+
);
64+
};
65+
66+
export default AutomateMethodCodeMirror;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createContext } from 'react';
2+
3+
const AutomateMethodContext = createContext();
4+
5+
export default AutomateMethodContext;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { useState, useEffect, useContext } from 'react';
2+
import MiqFormRenderer from '@@ddf';
3+
import { Modal } from 'carbon-components-react';
4+
import PropTypes from 'prop-types';
5+
import { inputParameterSchema } from './schema';
6+
import { InputParameterRecordActions } from './helper';
7+
import AutomateMethodContext from '../automate-method-context';
8+
9+
const AutomateMethodInputParameterForm = ({ modalStatus }) => {
10+
/** Context to access data from parent component */
11+
const { formData, updateInputParameter } = useContext(AutomateMethodContext);
12+
13+
const [data, setData] = useState({
14+
initialValues: undefined,
15+
});
16+
17+
/** Effect hook to update initial values when selectedId changes */
18+
useEffect(() => {
19+
const { selectedId, items } = formData.inputParameter;
20+
if (selectedId) {
21+
setData({
22+
...data,
23+
initialValues: items[selectedId],
24+
});
25+
}
26+
}, [formData.inputParameter.selectedId]);
27+
28+
const addOrUpdateInputParameter = (values) => (formData.inputParameter.selectedId
29+
? updateInputParameter(InputParameterRecordActions.UPDATE, { values })
30+
: updateInputParameter(InputParameterRecordActions.ADD, { values }));
31+
32+
return (
33+
<Modal
34+
open={modalStatus}
35+
modalHeading={__('Add Input Parameters')}
36+
secondaryButtonText={__('Cancel')}
37+
onRequestClose={() => updateInputParameter(InputParameterRecordActions.CLOSE, undefined)}
38+
passiveModal
39+
>
40+
<MiqFormRenderer
41+
schema={inputParameterSchema(formData.apiResponse)}
42+
initialValues={data.initialValues}
43+
onSubmit={(values) => addOrUpdateInputParameter(values)}
44+
/>
45+
</Modal>
46+
);
47+
};
48+
49+
export default AutomateMethodInputParameterForm;
50+
51+
AutomateMethodInputParameterForm.propTypes = {
52+
modalStatus: PropTypes.bool.isRequired,
53+
};
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/** Action buttons for the input parameter table */
2+
export const InputParameterButtonActions = {
3+
EDIT: 'editInputParameter',
4+
DELETE: 'deleteInputParameter',
5+
};
6+
7+
export const InputParameterRecordActions = {
8+
OPEN: 'openModal',
9+
CLOSE: 'closeModal',
10+
ADD: 'add',
11+
UPDATE: 'update',
12+
DELETE: 'delete',
13+
};
14+
15+
const editInputParameterButton = () => ({
16+
is_button: true,
17+
title: __('Edit'),
18+
text: __('Edit'),
19+
alt: __('Edit'),
20+
kind: 'ghost',
21+
callback: InputParameterButtonActions.EDIT,
22+
});
23+
24+
const deleteInputParameterButton = () => ({
25+
is_button: true,
26+
title: __('Delete'),
27+
text: __('Delete'),
28+
alt: __('Delete'),
29+
kind: 'ghost',
30+
callback: InputParameterButtonActions.DELETE,
31+
});
32+
33+
/** Input parameter data for table */
34+
export const reformatList = (items) => items.map((item, index) => ({
35+
...item,
36+
id: index.toString(),
37+
edit: editInputParameterButton(item, index),
38+
delete: deleteInputParameterButton(item, index),
39+
}));
40+
41+
export const headers = [
42+
{ key: 'inputName', header: __('Input Name') },
43+
{ key: 'dataType', header: __('Data Type') },
44+
{ key: 'defaultValue', header: __('Default Value') },
45+
{ key: 'edit', header: __('Edit') },
46+
{ key: 'delete', header: __('Delete') },
47+
];
48+
49+
/* Function to handle the action buttons */
50+
export const handleInputParameterUpdate = (actionType, data, formData) => {
51+
const { inputParameter } = formData;
52+
53+
if (actionType !== InputParameterRecordActions.DELETE) {
54+
inputParameter.modal = false;
55+
}
56+
57+
switch (actionType) {
58+
case InputParameterRecordActions.OPEN:
59+
inputParameter.modal = true;
60+
if (data && data.selectedId) {
61+
inputParameter.selectedId = data.selectedId;
62+
}
63+
break;
64+
case InputParameterRecordActions.CLOSE:
65+
inputParameter.modal = false;
66+
inputParameter.selectedId = undefined;
67+
break;
68+
case InputParameterRecordActions.ADD:
69+
inputParameter.items.push(data.values);
70+
inputParameter.selectedId = undefined;
71+
break;
72+
case InputParameterRecordActions.UPDATE:
73+
inputParameter.items[inputParameter.selectedId] = data.values;
74+
inputParameter.selectedId = undefined;
75+
break;
76+
case InputParameterRecordActions.DELETE:
77+
inputParameter.items.splice(data.selectedId, 1);
78+
inputParameter.selectedId = undefined;
79+
break;
80+
default:
81+
console.warn(__('Unknown action'));
82+
}
83+
84+
return { ...formData.inputParameter };
85+
};
86+
87+
/** Helper function to get provider details and restructure its options */
88+
export const initialState = {
89+
manager_id: null,
90+
};
91+
92+
export const reducer = (state, action) => {
93+
switch (action.type) {
94+
case 'SET_MANAGER_ID':
95+
return {
96+
...state,
97+
manager_id: action.payload,
98+
};
99+
default:
100+
return state;
101+
}
102+
};
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React, { useContext } from 'react';
2+
import { Button, Accordion, AccordionItem } from 'carbon-components-react';
3+
import {
4+
InputParameterButtonActions, InputParameterRecordActions, headers, reformatList,
5+
} from './helper';
6+
import MiqDataTable from '../../miq-data-table';
7+
import NotificationMessage from '../../notification-message';
8+
import AutomateMethodContext from '../automate-method-context';
9+
10+
const AutomateMethodInputParameter = () => {
11+
/** Context to access data from parent component */
12+
const { formData, updateInputParameter } = useContext(AutomateMethodContext);
13+
14+
/** Input parameter selection handler */
15+
const onSelect = (item) => {
16+
if (item && item.callbackAction) {
17+
switch (item.callbackAction) {
18+
case InputParameterButtonActions.EDIT:
19+
return updateInputParameter(InputParameterRecordActions.OPEN, { selectedId: item.id });
20+
case InputParameterButtonActions.DELETE:
21+
return updateInputParameter(InputParameterRecordActions.DELETE, { selectedId: item.id });
22+
default:
23+
return undefined;
24+
}
25+
}
26+
return undefined;
27+
};
28+
29+
const renderAddButton = () => (
30+
<div className="custom-form-buttons">
31+
<Button onClick={() => updateInputParameter('openModal', undefined)} kind="primary" size="sm">
32+
{__('Add Input Parameters')}
33+
</Button>
34+
</div>
35+
36+
);
37+
38+
return (
39+
<div className="automate-custom-form custom-form-wrapper">
40+
<Accordion align="start" className="miq-custom-form-accordion">
41+
<AccordionItem title={__('Input Parameters')} open>
42+
{renderAddButton()}
43+
{
44+
formData.inputParameter.items.length > 0
45+
? (
46+
<MiqDataTable
47+
headers={headers}
48+
rows={reformatList(formData.inputParameter.items)}
49+
onCellClick={(selectedRow) => onSelect(selectedRow)}
50+
mode="button-group-list"
51+
/>
52+
)
53+
: (
54+
<>
55+
<br />
56+
<NotificationMessage type="info" message={__('Input parameters are not available.')} />
57+
</>
58+
)
59+
}
60+
</AccordionItem>
61+
</Accordion>
62+
</div>
63+
);
64+
};
65+
66+
export default AutomateMethodInputParameter;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* eslint-disable camelcase */
2+
import { componentTypes, validatorTypes } from '@@ddf';
3+
4+
/** Schema for input parameter form */
5+
export const inputParameterSchema = ({ available_datatypes }) => ({
6+
fields: [
7+
{
8+
component: componentTypes.TEXT_FIELD,
9+
id: 'inputName',
10+
name: 'inputName',
11+
label: __('Input Name'),
12+
isRequired: true,
13+
validate: [{ type: validatorTypes.REQUIRED }],
14+
},
15+
{
16+
component: componentTypes.SELECT,
17+
id: 'dataType',
18+
name: 'dataType',
19+
label: __('Choose'),
20+
options: available_datatypes.map((item) => ({ label: item, value: item })),
21+
isRequired: true,
22+
validate: [{ type: validatorTypes.REQUIRED }],
23+
},
24+
{
25+
component: componentTypes.TEXT_FIELD,
26+
id: 'defaultValue',
27+
name: 'defaultValue',
28+
label: __('Default Value'),
29+
isRequired: true,
30+
validate: [{ type: validatorTypes.REQUIRED }],
31+
},
32+
],
33+
});

0 commit comments

Comments
 (0)