Skip to content

Commit

Permalink
Automate Method Form Conversion From ANGULAR to REACT
Browse files Browse the repository at this point in the history
  • Loading branch information
amalvijayan03 committed Mar 12, 2024
1 parent 6e85753 commit 174d0b3
Show file tree
Hide file tree
Showing 15 changed files with 824 additions and 4 deletions.
19 changes: 19 additions & 0 deletions app/controllers/miq_ae_class_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,25 @@ def ae_class_for_instance_or_method(record)
record.id ? record.ae_class : MiqAeClass.find(x_node.split("-").last)
end

def validate_automate_method_data
assert_privileges("miq_ae_method_edit")
@edit[:new][:data] = params[:cls_method_data] if params[:cls_method_data]
@edit[:new][:data] = params[:method_data] if params[:method_data]
res = MiqAeMethod.validate_syntax(@edit[:new][:data])
line = 0
if !res
render :json => {:status => true, :message => _("Data validated successfully")}
else
res.each do |err|
line = err[0] if line.zero?
render :json => {
:status => false,
:message => (_("Error on line %{line_num}: %{err_txt}") % {:line_num => err[0], :err_txt => err[1]})
}
end
end
end

def validate_method_data
assert_privileges("miq_ae_method_edit")
return unless load_edit("aemethod_edit__#{params[:id]}", "replace_cell__explorer")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useState, useEffect, useContext } from 'react';
import { Button, Accordion, AccordionItem } from 'carbon-components-react';
import { Controlled as CodeMirror } from 'react-codemirror2';
import { http } from '../../../http_api';
import NotificationMessage from '../../notification-message';
import AutomateMethodContext from '../automate-method-context';

const AutomateMethodCodeMirror = () => {
const { updateCodeEditor } = useContext(AutomateMethodContext);

const defaultEditorContents = `#\n# Description: <Method description here>\n#\n`;

const [data, setData] = useState({
editorContents: defaultEditorContents,
enableValidationButton: false,
validation: undefined,
});

useEffect(() => {
updateCodeEditor(data.editorContents);
}, [data.validation]);

const validate = () => {
const formData = { cls_method_data: data.editorContents };
http.post('/miq_ae_class/validate_automate_method_data/new?button=validate', formData).then((response) => {
setData({
...data,
validation: response,
});
});
};

const renderValidateButton = () => (
<div className="custom-form-buttons">
<Button kind="primary" size="sm" onClick={validate}>Validate</Button>
</div>
);

return (
<div className="automate-code-mirror custom-form-wrapper">
<Accordion align="start" className="miq-custom-form-accordion">
<AccordionItem title={__('Data')} open>
{
data.validation && <NotificationMessage type={data.validation.status ? 'success' : 'error'} message={data.validation.message} />
}
<CodeMirror
className="miq-codemirror miq-structured-list-code-mirror"
options={{
mode: 'ruby',
lineNumbers: true,
matchBrackets: true,
theme: 'eclipse',
viewportMargin: Infinity,
readOnly: false,
}}
onBeforeChange={(_editor, _data, value) => setData({ ...data, validation: undefined, editorContents: value })}
value={data.editorContents}
/>
{renderValidateButton()}
</AccordionItem>
</Accordion>
</div>
);
};

export default AutomateMethodCodeMirror;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createContext } from 'react';

const AutomateMethodContext = createContext();

export default AutomateMethodContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useState, useEffect, useContext } from 'react';
import MiqFormRenderer from '@@ddf';
import { Modal } from 'carbon-components-react';
import PropTypes from 'prop-types';
import { inputParameterSchema } from './schema';
import { InputParameterRecordActions } from './helper';
import AutomateMethodContext from '../automate-method-context';

const AutomateMethodInputParameterForm = ({ modalStatus }) => {
/** Context to access data from parent component */
const { formData, updateInputParameter } = useContext(AutomateMethodContext);

const [data, setData] = useState({
initialValues: undefined,
});

/** Effect hook to update initial values when selectedId changes */
useEffect(() => {
const { selectedId, items } = formData.inputParameter;
if (selectedId) {
setData({
...data,
initialValues: items[selectedId],
});
}
}, [formData.inputParameter.selectedId]);

const addOrUpdateInputParameter = (values) => (formData.inputParameter.selectedId
? updateInputParameter(InputParameterRecordActions.UPDATE, { values })
: updateInputParameter(InputParameterRecordActions.ADD, { values }));

return (
<Modal
open={modalStatus}
modalHeading={__('Add Input Parameters')}
secondaryButtonText={__('Cancel')}
onRequestClose={() => updateInputParameter(InputParameterRecordActions.CLOSE, undefined)}
passiveModal
>
<MiqFormRenderer
schema={inputParameterSchema(formData.apiResponse)}
initialValues={data.initialValues}
onSubmit={(values) => addOrUpdateInputParameter(values)}
/>
</Modal>
);
};

export default AutomateMethodInputParameterForm;

AutomateMethodInputParameterForm.propTypes = {
modalStatus: PropTypes.bool.isRequired,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/** Action buttons for the input parameter table */
export const InputParameterButtonActions = {
EDIT: 'editInputParameter',
DELETE: 'deleteInputParameter',
};

export const InputParameterRecordActions = {
OPEN: 'openModal',
CLOSE: 'closeModal',
ADD: 'add',
UPDATE: 'update',
DELETE: 'delete',
};

const editInputParameterButton = () => ({
is_button: true,
title: __('Edit'),
text: __('Edit'),
alt: __('Edit'),
kind: 'ghost',
callback: InputParameterButtonActions.EDIT,
});

const deleteInputParameterButton = () => ({
is_button: true,
title: __('Delete'),
text: __('Delete'),
alt: __('Delete'),
kind: 'ghost',
callback: InputParameterButtonActions.DELETE,
});

/** Input parameter data for table */
export const reformatList = (items) => items.map((item, index) => ({
...item,
id: index.toString(),
edit: editInputParameterButton(item, index),
delete: deleteInputParameterButton(item, index),
}));

export const headers = [
{ key: 'inputName', header: __('Input Name') },
{ key: 'dataType', header: __('Data Type') },
{ key: 'defaultValue', header: __('Default Value') },
{ key: 'edit', header: __('Edit') },
{ key: 'delete', header: __('Delete') },
];

/* Function to handle the action buttons */
export const handleInputParameterUpdate = (actionType, data, formData) => {
const { inputParameter } = formData;

if (actionType !== InputParameterRecordActions.DELETE) {
inputParameter.modal = false;
}

switch (actionType) {
case InputParameterRecordActions.OPEN:
inputParameter.modal = true;
if (data && data.selectedId) {
inputParameter.selectedId = data.selectedId;
}
break;
case InputParameterRecordActions.CLOSE:
inputParameter.modal = false;
inputParameter.selectedId = undefined;
break;
case InputParameterRecordActions.ADD:
inputParameter.items.push(data.values);
inputParameter.selectedId = undefined;
break;
case InputParameterRecordActions.UPDATE:
inputParameter.items[inputParameter.selectedId] = data.values;
inputParameter.selectedId = undefined;
break;
case InputParameterRecordActions.DELETE:
inputParameter.items.splice(data.selectedId, 1);
inputParameter.selectedId = undefined;
break;
default:
console.warn(__('Unknown action'));
}

return { ...formData.inputParameter };
};

/** Helper function to get provider details and restructure its options */
export const initialState = {
manager_id: null,
};

export const reducer = (state, action) => {
switch (action.type) {
case 'SET_MANAGER_ID':
return {
...state,
manager_id: action.payload,
};
default:
return state;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useContext } from 'react';
import { Button, Accordion, AccordionItem } from 'carbon-components-react';
import {
InputParameterButtonActions, InputParameterRecordActions, headers, reformatList,
} from './helper';
import MiqDataTable from '../../miq-data-table';
import NotificationMessage from '../../notification-message';
import AutomateMethodContext from '../automate-method-context';

const AutomateMethodInputParameter = () => {
/** Context to access data from parent component */
const { formData, updateInputParameter } = useContext(AutomateMethodContext);

/** Input parameter selection handler */
const onSelect = (item) => {
if (item && item.callbackAction) {
switch (item.callbackAction) {
case InputParameterButtonActions.EDIT:
return updateInputParameter(InputParameterRecordActions.OPEN, { selectedId: item.id });
case InputParameterButtonActions.DELETE:
return updateInputParameter(InputParameterRecordActions.DELETE, { selectedId: item.id });
default:
return undefined;
}
}
return undefined;
};

const renderAddButton = () => (
<div className="custom-form-buttons">
<Button onClick={() => updateInputParameter('openModal', undefined)} kind="primary" size="sm">
{__('Add Input Parameters')}
</Button>
</div>

);

return (
<div className="automate-custom-form custom-form-wrapper">
<Accordion align="start" className="miq-custom-form-accordion">
<AccordionItem title={__('Input Parameters')} open>
{renderAddButton()}
{
formData.inputParameter.items.length > 0
? (
<MiqDataTable
headers={headers}
rows={reformatList(formData.inputParameter.items)}
onCellClick={(selectedRow) => onSelect(selectedRow)}
mode="button-group-list"
/>
)
: (
<>
<br />
<NotificationMessage type="info" message={__('Input parameters are not available.')} />
</>
)
}
</AccordionItem>
</Accordion>
</div>
);
};

export default AutomateMethodInputParameter;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable camelcase */
import { componentTypes, validatorTypes } from '@@ddf';

/** Schema for input parameter form */
export const inputParameterSchema = ({ available_datatypes }) => ({
fields: [
{
component: componentTypes.TEXT_FIELD,
id: 'inputName',
name: 'inputName',
label: __('Input Name'),
isRequired: true,
validate: [{ type: validatorTypes.REQUIRED }],
},
{
component: componentTypes.SELECT,
id: 'dataType',
name: 'dataType',
label: __('Choose'),
options: available_datatypes.map((item) => ({ label: item, value: item })),
isRequired: true,
validate: [{ type: validatorTypes.REQUIRED }],
},
{
component: componentTypes.TEXT_FIELD,
id: 'defaultValue',
name: 'defaultValue',
label: __('Default Value'),
isRequired: true,
validate: [{ type: validatorTypes.REQUIRED }],
},
],
});
Loading

0 comments on commit 174d0b3

Please sign in to comment.