From 7af9c715285a2debfc81501f55cdce2725a89c8e Mon Sep 17 00:00:00 2001 From: Gilbert Cherrie Date: Thu, 14 Nov 2024 13:12:21 -0500 Subject: [PATCH] Add tree and working demo --- app/controllers/catalog_controller.rb | 8 - .../automate-entry-points/checkbox.js | 31 ++ .../automate-entry-points/index.jsx | 419 +++++++++++++----- .../automate-entry-points/styles.css | 48 ++ .../components/visual-settings-form/index.jsx | 16 +- .../visual-settings-form.schema.js | 19 + config/routes.rb | 1 - package.json | 1 + 8 files changed, 427 insertions(+), 116 deletions(-) create mode 100644 app/javascript/components/automate-entry-points/checkbox.js create mode 100644 app/javascript/components/automate-entry-points/styles.css diff --git a/app/controllers/catalog_controller.rb b/app/controllers/catalog_controller.rb index 94b406877c4..3808add3528 100644 --- a/app/controllers/catalog_controller.rb +++ b/app/controllers/catalog_controller.rb @@ -664,14 +664,6 @@ def ae_tree_select_toggle session[:edit] = @edit end - def ae_tree_load - assert_new_or_edit_by_service_type - - require 'byebug' - byebug - session[:edit] - end - # Method to open the workflows dialog box # params[:field] => :fqname || :retire_fqname || :reconfigure_fqname # params[:selected] => Holds the value of the *_configuration_script_id diff --git a/app/javascript/components/automate-entry-points/checkbox.js b/app/javascript/components/automate-entry-points/checkbox.js new file mode 100644 index 00000000000..399c9294c85 --- /dev/null +++ b/app/javascript/components/automate-entry-points/checkbox.js @@ -0,0 +1,31 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; + +const Checkbox = ({ defaultState = false, onChange, children }) => { + const [on, setOn] = useState(defaultState); + const handleChange = (e) => { + setOn(e.target.checked); + onChange && onChange(e.target.checked); + }; + return ( +
+ +
+ ); +}; + +Checkbox.propTypes = { + defaultState: PropTypes.bool, + onChange: PropTypes.func.isRequired, + children: PropTypes.arrayOf(PropTypes.any), +}; + +Checkbox.defaultProps = { + defaultState: false, + children: [], +}; + +export default Checkbox; diff --git a/app/javascript/components/automate-entry-points/index.jsx b/app/javascript/components/automate-entry-points/index.jsx index 5cd75e3fd1f..c65e722a3c2 100644 --- a/app/javascript/components/automate-entry-points/index.jsx +++ b/app/javascript/components/automate-entry-points/index.jsx @@ -1,121 +1,338 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { Loading, Modal, ModalBody } from 'carbon-components-react'; -import MiqDataTable from '../miq-data-table'; -import { http } from '../../http_api'; - -const AutomateEntryPoints = ({ - field, selected, type, setShowModal, setSelectedValue, -}) => { - const [data, setData] = useState({ - isLoading: true, list: {}, selectedItemId: selected, - }); - - const workflowTypes = { - provision: __('Provision'), - reconfigure: __('Reconfigure'), - retire: __('Retirement'), +// import React, { useState, useEffect } from 'react'; +// import PropTypes from 'prop-types'; +// import { Loading, Modal, ModalBody } from 'carbon-components-react'; +// import MiqDataTable from '../miq-data-table'; +// import { http } from '../../http_api'; + +// const AutomateEntryPoints = ({ +// field, selected, type, setShowModal, setSelectedValue, +// }) => { +// const [data, setData] = useState({ +// isLoading: true, list: {}, selectedItemId: selected, +// }); + +// const workflowTypes = { +// provision: __('Provision'), +// reconfigure: __('Reconfigure'), +// retire: __('Retirement'), +// }; + +// useEffect(() => { +// API.get('/api/automate') +// .then((_data) => { +// console.log(data); +// setData({ +// ...data, +// isLoading: false, +// }); +// }); +// }, []); + +// /** Function to handle a row's click event. */ +// const onSelect = (selectedItemId) => { +// setData({ +// ...data, +// selectedItemId: (data.selectedItemId === selectedItemId) ? undefined : selectedItemId, +// }); +// const params = `cfp-${encodeURIComponent(selectedItemId)}&tree=automate_catalog_tree&field=${field}`; +// window.miqJqueryRequest(`/catalog/ae_tree_select/?id=${params}&typ=${type}`); +// }; +// if (data.isLoading) { +// return (); +// } +// /** Function to handle the modal box close button click event. */ +// const onCloseModal = () => { +// if (setShowModal) { +// setShowModal(false); +// } else { +// document.getElementById(`${type}-workflows`).innerHTML = ''; +// http.post('/catalog/ae_tree_select_toggle?button=cancel', {}, { headers: {}, skipJsonParsing: true }); +// } +// }; +// /** Function to handle the modal box apply button click event. */ +// const onApply = () => { +// const seletedItem = data.list.rows.find((item) => item.id === data.selectedItemId); +// const name = seletedItem.name.text; +// if (seletedItem) { +// if (setShowModal && setSelectedValue) { +// setShowModal(false); +// setSelectedValue(seletedItem); +// } else { +// const nameField = document.getElementById(field); +// const selectedField = document.getElementById(`${type}_configuration_script_id`); + +// if (nameField && selectedField) { +// nameField.value = name; +// selectedField.value = data.selectedItemId; +// http.post('/catalog/ae_tree_select_toggle?button=submit&automation_type=workflow', {}, { headers: {}, skipJsonParsing: true }) +// .then((_data) => { +// document.getElementById(`${type}-workflows`).innerHTML = ''; +// }); +// } +// } +// } +// }; +// return ( +// +// +// onSelect(selectedRow.id)} +// showPagination={false} +// truncateText={false} +// mode="automated-workflow-entry-points" +// gridChecks={[data.selectedItemId]} +// size="md" +// /> +// +// +// ); +// }; +// AutomateEntryPoints.propTypes = { +// field: PropTypes.string.isRequired, +// type: PropTypes.string.isRequired, +// selected: PropTypes.string, +// setShowModal: PropTypes.func, +// setSelectedValue: PropTypes.func, +// }; + +// AutomateEntryPoints.defaultProps = { +// selected: '', +// setShowModal: undefined, +// setSelectedValue: undefined, +// }; + +// export default AutomateEntryPoints; + +import React, { useEffect, useState } from 'react'; +import { FolderOpen16, Folder16, Document16 } from '@carbon/icons-react'; +import TreeView, { flattenTree } from 'react-accessible-treeview'; +import './styles.css'; + +const DirectoryTreeView = () => { +// const [treeData, setTreeData] = useState([{ +// id: 'root', +// name: '', +// children: [ +// { +// id: 'src_folder', +// name: 'src', +// }, +// ], +// }, +// ]); + + const folder = { + id: 'root', + name: '', + children: [ + { + id: 'src_folder', + name: 'src', + children: [ + { + id: 'index.js_file', + name: 'index.js', + metadata: { a: '1', b: '2', c: 'test' }, + }, + { + id: 'styles.css_file', + name: 'styles.css', + metadata: { a: '1', b: '2', c: 'test' }, + }], + }, + { + id: 'node_modules_folder', + name: 'node_modules', + children: [ + { + id: 'react-accessible-treeview-folder', + name: 'react-accessible-treeview', + children: [{ + id: 'index.js_file2', + name: 'index.js', + }], + }, + { + id: 'react_folder', + name: 'react', + children: [{ + id: 'index.js_file3', + name: 'index.js', + }], + }, + ], + }, + { + id: '.npmignore_file', + name: '.npmignore', + }, + { + id: 'package.json_file', + name: 'package.json', + }, + { + id: 'webpack.config.js_file', + name: 'webpack.config.js', + }, + ], }; + const data = flattenTree(folder); + console.log(data); + + const initialData = { + id: 'root', + name: '', + children: [ + { + id: 'root-node', + name: '', + }, + ], + }; + + const [rawTreeData, setRawTreeData] = useState(folder); + const [treeData, setTreeData] = useState(flattenTree(initialData)); + const [expandedIds, setExpandedIds] = useState([]); + const [key, setKey] = useState('initial'); + useEffect(() => { - http.post(`/catalog/ae_tree_load?typ=${type}`, {}, { headers: {}, skipJsonParsing: true }) - .then((_data) => { - console.log(data); - API.get('/api/automate_domains?expand=resources') - .then((response) => { - console.log(response); - // API.get('/api/automate_workspaces?expand=resources').then((response) => { - // console.log(response); - // }); - setData({ - ...data, - isLoading: false, - }); - }); - }); + setTreeData(data); }, []); - /** Function to handle a row's click event. */ - const onSelect = (selectedItemId) => { - setData({ - ...data, - selectedItemId: (data.selectedItemId === selectedItemId) ? undefined : selectedItemId, - }); - const params = `cfp-${encodeURIComponent(selectedItemId)}&tree=automate_catalog_tree&field=${field}`; - window.miqJqueryRequest(`/catalog/ae_tree_select/?id=${params}&typ=${type}`); + useEffect(() => { + console.log(rawTreeData); + setTreeData(flattenTree(rawTreeData)); + }, [rawTreeData]); + + useEffect(() => { + console.log(treeData); + if (treeData.length > 12) { + setExpandedIds(['src_folder']); + setKey('new'); + } + }, [treeData]); + + const FolderIcon = ({ isOpen }) => + (isOpen ? ( + + ) : ( + + )); + + const FileIcon = ({ filename }) => { + const extension = filename.slice(filename.lastIndexOf('.') + 1); + switch (extension) { + case 'js': + return ; + case 'css': + return ; + case 'json': + return ; + case 'npmignore': + return ; + default: + return ; + } }; - if (data.isLoading) { - return (); - } - /** Function to handle the modal box close button click event. */ - const onCloseModal = () => { - if (setShowModal) { - setShowModal(false); - } else { - document.getElementById(`${type}-workflows`).innerHTML = ''; - http.post('/catalog/ae_tree_select_toggle?button=cancel', {}, { headers: {}, skipJsonParsing: true }); + + const onSelect = (value) => { + if (value && value.isSelected === true) { + console.log(value); } }; - /** Function to handle the modal box apply button click event. */ - const onApply = () => { - const seletedItem = data.list.rows.find((item) => item.id === data.selectedItemId); - const name = seletedItem.name.text; - if (seletedItem) { - if (setShowModal && setSelectedValue) { - setShowModal(false); - setSelectedValue(seletedItem); - } else { - const nameField = document.getElementById(field); - const selectedField = document.getElementById(`${type}_configuration_script_id`); - - if (nameField && selectedField) { - nameField.value = name; - selectedField.value = data.selectedItemId; - http.post('/catalog/ae_tree_select_toggle?button=submit&automation_type=workflow', {}, { headers: {}, skipJsonParsing: true }) - .then((_data) => { - document.getElementById(`${type}-workflows`).innerHTML = ''; + + const onExpand = (value) => { + console.log(value); + const tempData = treeData; + const newChildren = []; + + if (value && value.element) { + const ids = value.element.id.split('_'); + if (ids.includes('folder')) { + tempData.forEach((item) => { + if (item.id === value.element.id) { + console.log(item.name); + API.get('/api/automate_domains?expand=resources').then((apiData) => { + console.log(apiData); + apiData.resources.forEach((domain) => { + newChildren.push({ + id: domain.id, + name: domain.name, + children: [], + parent: item.id, + metadata: {}, + }); + }); + return newChildren; + }).then((newChildrenArray) => { + const newTreeData = treeData.concat(newChildrenArray[2]); + if (treeData.includes(newChildrenArray[0]) === false) { + newTreeData[1].children = ['index.js_file', 'styles.css_file', '1177']; + if (treeData.length === 12) { + setTreeData(newTreeData); + // Send all relevant data including new children and the clicked item to a new useffect using a new state variable + // From this new use effect we can set the treedata, expandedids and the key state variables + } + } }); - } + } + }); } } }; + return ( - - - onSelect(selectedRow.id)} - showPagination={false} - truncateText={false} - mode="automated-workflow-entry-points" - gridChecks={[data.selectedItemId]} - size="md" +
+
+ { + getNodeProps(); + return ( +
+ {isBranch ? ( + + ) : ( + + )} + + {element.name} +
+ ); + }} /> - - +
+
); }; -AutomateEntryPoints.propTypes = { - field: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - selected: PropTypes.string, - setShowModal: PropTypes.func, - setSelectedValue: PropTypes.func, + +DirectoryTreeView.propTypes = { }; -AutomateEntryPoints.defaultProps = { - selected: '', - setShowModal: undefined, - setSelectedValue: undefined, +DirectoryTreeView.defaultProps = { }; -export default AutomateEntryPoints; +export default DirectoryTreeView; diff --git a/app/javascript/components/automate-entry-points/styles.css b/app/javascript/components/automate-entry-points/styles.css new file mode 100644 index 00000000000..deff728d8de --- /dev/null +++ b/app/javascript/components/automate-entry-points/styles.css @@ -0,0 +1,48 @@ +.directory { + background: #242322; + font-family: monospace; + font-size: 16px; + color: white; + user-select: none; + padding: 20px; + border-radius: 0.4em; +} + +.directory .tree, +.directory .tree-node, +.directory .tree-node-group { + list-style: none; + margin: 0; + padding: 0; +} + +.directory .tree-branch-wrapper, +.directory .tree-node__leaf { + outline: none; + outline: none; +} + +.directory .tree-node { + cursor: pointer; +} + +.directory .tree-node:hover { + background: rgba(255, 255, 255, 0.1); +} + +.directory .tree .tree-node--focused { + background: rgba(255, 255, 255, 0.2); +} + +.directory .tree .tree-node--selected { + background: rgba(48, 107, 176); +} + +.directory .tree-node__branch { + display: block; +} + +.directory .icon { + vertical-align: middle; + padding-right: 5px; +} \ No newline at end of file diff --git a/app/javascript/components/visual-settings-form/index.jsx b/app/javascript/components/visual-settings-form/index.jsx index 39e40c069f8..b2b9bd71386 100644 --- a/app/javascript/components/visual-settings-form/index.jsx +++ b/app/javascript/components/visual-settings-form/index.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Loading } from 'carbon-components-react'; import MiqFormRenderer from '@@ddf'; import createSchema from './visual-settings-form.schema'; +import DirectoryTreeView from '../automate-entry-points'; const VisualSettingsForm = ({ recordId }) => { const [{ initialValues, timezoneOptions, isLoading }, setState] = useState({ isLoading: true }); @@ -41,12 +42,15 @@ const VisualSettingsForm = ({ recordId }) => { ); } return ( - +
+ + +
); }; diff --git a/app/javascript/components/visual-settings-form/visual-settings-form.schema.js b/app/javascript/components/visual-settings-form/visual-settings-form.schema.js index 26c7c881422..0d9d915e5d0 100644 --- a/app/javascript/components/visual-settings-form/visual-settings-form.schema.js +++ b/app/javascript/components/visual-settings-form/visual-settings-form.schema.js @@ -1,5 +1,24 @@ import { componentTypes } from '@@ddf'; +// const loadData = API.get('/api/automate?depth=1&attributes=klass,id,fqname,domain_fqname,name').then((data) => { +// console.log(data); +// const tree = []; +// if (data && data.resources) { +// data.resources.forEach((domain) => { +// // console.log(domain); +// tree.push({ +// key: domain.fqname, +// icon: 'pficon pficon-folder-close', +// selectable: false, +// text: domain.name, +// tooltip: 'root node', +// state: {}, +// }); +// }); +// } +// return tree; +// }); + const createSchema = (timezoneOptions) => ({ fields: [ { diff --git a/config/routes.rb b/config/routes.rb index c5514d40cf1..2056e137c7d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -310,7 +310,6 @@ ae_tree_select ae_tree_select_discard ae_tree_select_toggle - ae_tree_load atomic_form_field_changed atomic_st_edit automate_button_field_changed diff --git a/package.json b/package.json index 0e929545bc3..42dd6f5abb0 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "prop-types": "^15.6.0", "proxy-polyfill": "^0.1.7", "react": "~16.13.1", + "react-accessible-treeview": "^2.10.0", "react-bootstrap": "~0.33.0", "react-codemirror2": "^6.0.0", "react-dom": "~16.13.1",