From 17e2b4208ebf35a77968182b4f2a5a4e72508ffb Mon Sep 17 00:00:00 2001 From: Amal-Vijayan Date: Mon, 22 Jan 2024 18:21:24 +0530 Subject: [PATCH] Automate Method Form Conversion From HAML to REACT --- .../automate-method-code-mirror/index.jsx | 46 ++++++ .../automate-method-input-parameter/helper.js | 31 ++++ .../automate-method-input-parameter/index.jsx | 131 +++++++++++++++ .../automate-method-input-parameter/schema.js | 38 +++++ .../components/automate-method-form/index.jsx | 55 +++++++ .../automate-method-form/schema.config.js | 155 ++++++++++++++++++ .../components/automate-method-form/schema.js | 87 ++++++++++ .../packs/component-definitions-common.js | 2 + app/views/miq_ae_class/_method_form.html.haml | 1 + 9 files changed, 546 insertions(+) create mode 100644 app/javascript/components/automate-method-form/automate-method-code-mirror/index.jsx create mode 100644 app/javascript/components/automate-method-form/automate-method-input-parameter/helper.js create mode 100644 app/javascript/components/automate-method-form/automate-method-input-parameter/index.jsx create mode 100644 app/javascript/components/automate-method-form/automate-method-input-parameter/schema.js create mode 100644 app/javascript/components/automate-method-form/index.jsx create mode 100644 app/javascript/components/automate-method-form/schema.config.js create mode 100644 app/javascript/components/automate-method-form/schema.js diff --git a/app/javascript/components/automate-method-form/automate-method-code-mirror/index.jsx b/app/javascript/components/automate-method-form/automate-method-code-mirror/index.jsx new file mode 100644 index 000000000000..ff20cc2b04d9 --- /dev/null +++ b/app/javascript/components/automate-method-form/automate-method-code-mirror/index.jsx @@ -0,0 +1,46 @@ +import React, { useRef, useEffect } from 'react'; +import CodeMirror from 'codemirror'; +import 'codemirror/lib/codemirror.css'; +import 'codemirror/mode/javascript/javascript'; + +const AutomateMethodCodeMirror = () => { + const codeMirrorRef = useRef(null); + + useEffect(() => { + const editor = CodeMirror.fromTextArea(codeMirrorRef.current, { + mode: 'javascript', + theme: 'material', + lineNumbers: true, + }); + + editor.on('change', (instance) => { + console.log(instance) + }); + + editor.setValue('test'); + + return () => { + editor.toTextArea(); + }; + }, []); + + return ( +
+ + +
+ ); +}; + +export default AutomateMethodCodeMirror; diff --git a/app/javascript/components/automate-method-form/automate-method-input-parameter/helper.js b/app/javascript/components/automate-method-form/automate-method-input-parameter/helper.js new file mode 100644 index 000000000000..a268ef6ac47b --- /dev/null +++ b/app/javascript/components/automate-method-form/automate-method-input-parameter/helper.js @@ -0,0 +1,31 @@ +export const InputParameterActions = { + EDIT: 'editInputParameter', + DELETE: 'deleteInputParameter', +} +const editInputParameterButton = () => ({ + is_button: true, + title: __('Edit'), + text: __('Edit'), + alt: __('Edit'), + kind: 'ghost', + callback: InputParameterActions.EDIT, +}); + +const deleteInputParameterButton = () => ({ + is_button: true, + title: __('Delete'), + text: __('Delete'), + alt: __('Delete'), + kind: 'ghost', + callback: InputParameterActions.DELETE, +}); + +export const inputParametersList = (items) => { + const rows = items.map((item, index) => ({ + ...item, + id: index.toString(), + edit: editInputParameterButton(item, index), + delete: deleteInputParameterButton(item, index), + })); + return rows; +} diff --git a/app/javascript/components/automate-method-form/automate-method-input-parameter/index.jsx b/app/javascript/components/automate-method-form/automate-method-input-parameter/index.jsx new file mode 100644 index 000000000000..eb2ae6f70039 --- /dev/null +++ b/app/javascript/components/automate-method-form/automate-method-input-parameter/index.jsx @@ -0,0 +1,131 @@ +import React, { useState } from 'react'; +import { inputParameterSchema } from './schema'; +import {Button, Modal} from 'carbon-components-react'; +import MiqFormRenderer from '@@ddf'; +import { inputParametersList, InputParameterActions } from './helper'; +import MiqDataTable from '../../miq-data-table'; +import NotificationMessage from '../../notification-message'; + +const AutomateMethodInputParameter = () => { + + 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')}, + ] + + const [data, setData] = useState({ + isModalOpen: false, + initialValues: [], + rows: [], + selectedItemId: undefined, + }); + + const addInputParameter = (values) => { + console.log('actionType=', values); + const newList = [...data.initialValues, values]; + console.log(newList); + setData({ + ...data, + initialValues: newList, + rows: inputParametersList(newList), + selectedItemId: undefined, + isModalOpen: false, + }) + }; + + const updateInputParameter = (values) => { + data.initialValues[data.selectedItemId] = values; + setData({ + ...data, + initialValues: data.initialValues, + rows: inputParametersList(data.initialValues), + selectedItemId: undefined, + isModalOpen: false, + }); + } + + const addOrUpdateInputParameter = (values) => { + data.selectedItemId + ? updateInputParameter(values) + : addInputParameter(values); + } + + const editInputParameter = (id) => { + console.log('Edit', id); + + console.log(data.initialValues); + setData({ + ...data, + isModalOpen: true, + selectedItemId: id, + }); + } + + const deleteInputParameter = (id) => { + console.log('Delete', id); + data.initialValues.splice(id, 1); + setData({ + ...data, + initialValues: data.initialValues, + rows: inputParametersList(data.initialValues), + }) + } + + const onSelect = (item) => { + console.log(item); + if (item && item.callbackAction) { + switch(item.callbackAction) { + case InputParameterActions.EDIT: + return editInputParameter(item.id); + case InputParameterActions.DELETE: + return deleteInputParameter(item.id); + } + } + } + + console.log("data====", data); + + return ( +
+ + + { + data.rows.length > 0 + ? onSelect(selectedRow)} + mode="button-group-list" + /> + : + } + + { + data.isModalOpen && + setData({...data, isModalOpen: false})} + passiveModal + > + addOrUpdateInputParameter(values)} /> + + } + +
+ ) +} + +export default AutomateMethodInputParameter diff --git a/app/javascript/components/automate-method-form/automate-method-input-parameter/schema.js b/app/javascript/components/automate-method-form/automate-method-input-parameter/schema.js new file mode 100644 index 000000000000..62dc49962477 --- /dev/null +++ b/app/javascript/components/automate-method-form/automate-method-input-parameter/schema.js @@ -0,0 +1,38 @@ +import { componentTypes, validatorTypes } from '@@ddf'; + +const options = [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + { label: 'Option 3', value: 'option3' }, +] + +export const inputParameterSchema = { + 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: options, + isRequired: true, + validate: [{ type: validatorTypes.REQUIRED }], + }, + { + component: componentTypes.TEXT_FIELD, + id: 'defaultValue', + name: 'defaultValue', + label: __('Default Value'), + isRequired: true, + validate: [{ type: validatorTypes.REQUIRED }], + }, + ], +}; + diff --git a/app/javascript/components/automate-method-form/index.jsx b/app/javascript/components/automate-method-form/index.jsx new file mode 100644 index 000000000000..2ee2e99f98a4 --- /dev/null +++ b/app/javascript/components/automate-method-form/index.jsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import { Dropdown } from 'carbon-components-react'; +import MiqFormRenderer from '@@ddf'; +import {createSchema} from './schema'; +import componentMapper from '../../forms/mappers/componentMapper'; +import AutomateMethodInputParameter from './automate-method-input-parameter'; +import AutomateMethodCodeMirror from './automate-method-code-mirror'; + +const AutomateMethodForm = (props) => { + + const mapper = { + ...componentMapper, + 'automate-method-inline': AutomateMethodCodeMirror, + 'automate-method-input-parameter': AutomateMethodInputParameter, + }; + + console.log('Initial props',props) + const [selectedOption, setSelectedOption] = useState(null); + + const options = props.availableLocations.map((item) => ({ id: item[1], label: item[0] })); + console.log('options=', options); + + const renderMainInfo = () => { + console.log('renderMainInfo'. options); + return
+

Main Info

+ (item ? item.label : '')} + onChange={({ selectedItem }) => setSelectedOption(selectedItem)} + titleText={selectedOption ? selectedOption.label : ''} + /> +
+ } + + const renderInputParameters = () => () + + return ( + <> + {renderMainInfo()} + {selectedOption && ( + + )} + + + + ); +}; + +export default AutomateMethodForm; diff --git a/app/javascript/components/automate-method-form/schema.config.js b/app/javascript/components/automate-method-form/schema.config.js new file mode 100644 index 000000000000..f84866edf7f6 --- /dev/null +++ b/app/javascript/components/automate-method-form/schema.config.js @@ -0,0 +1,155 @@ +import { componentTypes } from '@@ddf'; + +export const ansibleFields = [ + { + component: componentTypes.SELECT, + id: 'provider', + name: 'provider', + label: __('Provider'), + // options: options, + }, + { + component: componentTypes.SELECT, + id: 'workflowTemplate', + name: 'workflowTemplate', + label:__('Workflow Template'), + // options: options, + }, +]; + +const ansibleFieldsCommon = [ + { + component: componentTypes.TEXT_FIELD, + id: 'maxttl', + name: 'maxttl', + label: __('Max TTL(mins)'), + }, + { + component: componentTypes.SELECT, + id: 'loggingOutput', + name: 'loggingOutput', + label: __('Logging Output'), + }, +]; + +const additionalFields = [ + { + id: 'hostValue', + component: componentTypes.RADIO, + label: __('Hosts'), + name: 'hostValue', + options: [ + { value: 'localhost', label: 'Localhost' }, + { value: 'specify', label: 'Specify host values' }, + ], + }, + { + component: componentTypes.TEXTAREA, + name: 'specify-details', + label: __('Specify details'), + condition: { + and: [{ when: 'hostValue', is: 'specify' }], + }, + }, +]; + +const builtInFields = [ + { + component: componentTypes.TEXT_FIELD, + id: 'built-in', + name: 'built-in', + label: __('Builtin name'), + helperText: 'Optional, if not specified, method name is used', + }, +]; + +const expressionFields = [ + { + component: componentTypes.SELECT, + id: 'expressionObject', + name: 'expressionObject', + label: __('Expression Object'), + options: [], + }, + { + component: componentTypes.TEXTAREA, + id: 'editexpression', + name: 'editexpression', + label: __('Placeholder For Edit Expression'), + }, + { + component: componentTypes.TEXTAREA, + id: 'editselected', + name: 'editselected', + label: __('Placeholder For Edit Selected Element'), + } +]; + +const playBookFields = [ + { + component: componentTypes.SELECT, + id: 'repository', + name: 'repository', + label: __('Repository'), + placeholder: __(''), + options: [], + }, + { + component: componentTypes.SELECT, + id: 'playbook', + name: 'playbook', + label: __('PlayBook'), + condition: { + when: "repository", + isNotEmpty: true + } + }, + { + component: componentTypes.SELECT, + id: 'machineCredential', + name: 'machineCredential', + label: __('Machine Credential'), + condition: { + when: "repository", + isNotEmpty: true + } + }, + { + component: componentTypes.SELECT, + id: 'vaultCredential', + name: 'vaultCredential', + label: __('Vault Credential'), + condition: { + when: "repository", + isNotEmpty: true + } + }, + { + component: componentTypes.SELECT, + id: 'cloudType', + name: 'cloudType', + label: __('Cloud Type'), + condition: { + when: "repository", + isNotEmpty: true + } + } +]; + +const verbosityField = [ + { + component: componentTypes.SELECT, + id: 'verbosity', + name: 'verbosity', + label: __('Verbosity'), + options: [], + } +]; + +export const schemaConfig = { + ansibleJobTemplate: [...ansibleFields, ...additionalFields, ...ansibleFieldsCommon], + ansibleWorkflowTemplate: [...ansibleFields, ...ansibleFieldsCommon], + builtIn: [...builtInFields], + expression: [...expressionFields], + playbook: [...playBookFields, ...additionalFields, ...ansibleFieldsCommon, ...verbosityField], +} diff --git a/app/javascript/components/automate-method-form/schema.js b/app/javascript/components/automate-method-form/schema.js new file mode 100644 index 000000000000..5edfe23b3a9c --- /dev/null +++ b/app/javascript/components/automate-method-form/schema.js @@ -0,0 +1,87 @@ +import { componentTypes } from '@@ddf'; +import { schemaConfig } from './schema.config'; + +/* Updated Schema */ +export const createSchema = (selectedOption) => { + + let selectedFields = []; + + const commonFields = [ + { + component: componentTypes.TEXT_FIELD, + id: 'type', + name: 'type', + label: __('Type'), + }, + { + component: componentTypes.TEXT_FIELD, + id: 'fully-qualified-name', + name: 'fully-qualified-name', + label: __('Fully Qualified Name'), + initialValue: '', + }, + { + component: componentTypes.TEXT_FIELD, + id: 'name', + name: 'name', + label: __('Name'), + initialValue: '', + }, + { + component: componentTypes.TEXT_FIELD, + id: 'displayName', + name: 'displayname', + label: __('Display Name'), + initialValue: '', + }, + ]; + + const inputParametersField = [ + { + component: 'automate-method-input-parameter', + id: 'inputParameter', + name: 'inputParameter', + label: __('Input Parameter'), + initialValue: 'test', + }, + ] + + const automateFields = (conditionalFields) => [ + ...commonFields, + ...conditionalFields, + ...inputParametersField, + ] + + switch (selectedOption.id) { + case 'ansible_job_template': + selectedFields = automateFields(schemaConfig.ansibleJobTemplate); + break; + case 'ansible_workflow_template': + selectedFields = automateFields(schemaConfig.ansibleWorkflowTemplate); + break; + case 'builtin': + selectedFields = automateFields(schemaConfig.builtIn); + break; + case 'expression': + selectedFields = automateFields(schemaConfig.expression); + break; + case 'inline': + selectedFields = [...commonFields, ...inputParametersField]; + break; + case 'playbook': + selectedFields = automateFields(schemaConfig.playbook); + break; + default: + selectedFields = []; + } + return { + fields: [ + { + component: componentTypes.SUB_FORM, + id: 'name-wrapper', + name: 'subform-1', + fields: selectedFields, + } + ] + } +} diff --git a/app/javascript/packs/component-definitions-common.js b/app/javascript/packs/component-definitions-common.js index 6b7e9f572a73..24d934aebd94 100644 --- a/app/javascript/packs/component-definitions-common.js +++ b/app/javascript/packs/component-definitions-common.js @@ -18,6 +18,7 @@ import AnsiblePlaybookWorkflow from '../components/ansible-playbook-workflow'; import AnsibleRepositoryForm from '../components/ansible-repository-form'; import AttachDetachCloudVolumeForm from '../components/cloud-volume-form/attach-detach-cloud-volume-form'; import AuthKeypairCloudForm from '../components/auth-key-pair-cloud'; +import AutomateMethodForm from '../components/automate-method-form' import AutomationSimulation from '../components/AutomationSimulation'; import ButtonList from '../components/data-tables/button-list'; import ButtonGroupList from '../components/data-tables/button-group-list'; @@ -187,6 +188,7 @@ ManageIQ.component.addReact('AnsiblePlaybookWorkflow', AnsiblePlaybookWorkflow); ManageIQ.component.addReact('AnsibleRepositoryForm', AnsibleRepositoryForm); ManageIQ.component.addReact('AttachDetachCloudVolumeForm', AttachDetachCloudVolumeForm); ManageIQ.component.addReact('AuthKeypairCloudForm', AuthKeypairCloudForm); +ManageIQ.component.addReact('AutomateMethodForm', AutomateMethodForm); ManageIQ.component.addReact('AutomationSimulation', AutomationSimulation); ManageIQ.component.addReact('BreadcrumbsBar', BreadcrumbsBar); ManageIQ.component.addReact('ButtonList', ButtonList); diff --git a/app/views/miq_ae_class/_method_form.html.haml b/app/views/miq_ae_class/_method_form.html.haml index ac7576ddf1ad..ae9eed2599a7 100644 --- a/app/views/miq_ae_class/_method_form.html.haml +++ b/app/views/miq_ae_class/_method_form.html.haml @@ -1,6 +1,7 @@ - if @sb[:active_tab] == "methods" - url = url_for_only_path(:action => 'form_method_field_changed', :id => (@ae_method.id || 'new')) - obs = {:interval => '.5', :url => url}.to_json += react('AutomateMethodForm', availableLocations: available_locations_with_labels) %h3 = _('Main Info') .form-horizontal