Add new component to support Tree view

Add treeview component to show subset of policies by prefix (using .)

Issue-ID: POLICY-3165
Signed-off-by: sebdet <sebastien.determe@intl.att.com>
Change-Id: I1365695f03086beda36a6bafddd9ad0f52944b6d
diff --git a/ui-react/package.json b/ui-react/package.json
index 8d11044..a62cf67 100644
--- a/ui-react/package.json
+++ b/ui-react/package.json
@@ -36,6 +36,7 @@
 		"react-router-dom": "5.2.0",
 		"@material-ui/core": "4.11.3",
 		"@material-ui/icons": "4.11.2",
+		"@material-ui/lab": "4.0.0-alpha.57",
 		"material-table": "1.68.1",
 		"react-select": "4.2.1",
 		"react-uuid": "1.0.2"
diff --git a/ui-react/src/components/dialogs/Policy/PoliciesTreeViewer.js b/ui-react/src/components/dialogs/Policy/PoliciesTreeViewer.js
new file mode 100644
index 0000000..9c2f102
--- /dev/null
+++ b/ui-react/src/components/dialogs/Policy/PoliciesTreeViewer.js
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP POLICY-CLAMP
+ * ================================================================================
+ * Copyright (C) 2021 AT&T Intellectual Property. All rights
+ *                             reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+import React, { forwardRef } from 'react'
+import TreeView from '@material-ui/lab/TreeView';
+import TreeItem from '@material-ui/lab/TreeItem';
+import FolderIcon from '@material-ui/icons/Folder';
+import FolderOpenIcon from '@material-ui/icons/FolderOpen';
+import DescriptionIcon from '@material-ui/icons/Description';
+
+
+export default class PoliciesTreeViewer extends React.Component {
+
+    separator = ".";
+
+    nodesList = new Map();
+
+    constructor(props, context) {
+        super(props, context);
+        this.createPoliciesTree = this.createPoliciesTree.bind(this);
+        this.handleTreeItemClick = this.handleTreeItemClick.bind(this);
+        this.buildNameWithParent = this.buildNameWithParent.bind(this);
+
+    }
+
+    state = {
+            policiesTreeData: this.createPoliciesTree(this.props.policiesData),
+    }
+
+    componentDidUpdate(prevProps) {
+      if (prevProps.policiesData !== this.props.policiesData) {
+        this.setState({policiesTreeData: this.createPoliciesTree(this.props.policiesData)})
+      }
+    }
+
+    createPoliciesTree(policiesArray) {
+        // put my policies array in a Json
+        let nodeId = 1;
+        let root = {id:nodeId, policyCount:0, name:"ROOT", children:[], parent: undefined};
+        this.nodesList.set(nodeId++, root);
+
+        policiesArray.forEach(policy => {
+            let currentTreeNode = root;
+            policy[this.props.valueForTreeCreation].split(this.separator).forEach((policyNamePart, index, policyNamePartsArray) => {
+                let node = currentTreeNode["children"].find(element => element.name === policyNamePart);
+                if (typeof(node) === "undefined") {
+                    node = {id:nodeId, policyCount:0, children:[], name:policyNamePart, parent:currentTreeNode};
+                    this.nodesList.set(nodeId++, node);
+                    currentTreeNode["children"].push(node);
+                }
+                if ((index+1) === policyNamePartsArray.length) {
+                    ++currentTreeNode["policyCount"];
+                }
+                currentTreeNode = node;
+            })
+        })
+        return root;
+    }
+
+    buildNameWithParent(node) {
+        let nameToBuild = node.name;
+        if (node.parent  !== undefined) {
+            nameToBuild = this.buildNameWithParent(node.parent) + this.separator + node.name;
+        }
+        return nameToBuild;
+    }
+
+    handleTreeItemClick(event, value) {
+        let fullName = this.buildNameWithParent(this.nodesList.get(value[0])).substring(5);
+        this.props.policiesFilterFunction(fullName);
+    }
+
+    renderTreeItems(nodes) {
+        return (<TreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name + "("+ nodes.policyCount + ")"} onNodeSelect={this.handleTreeItemClick}>
+          {
+            Array.isArray(nodes.children) ? nodes.children.map((node) => this.renderTreeItems(node)) : null
+          }
+        </TreeItem>);
+    };
+
+    render() {
+         return (
+                <TreeView defaultExpanded={['root']} defaultCollapseIcon={<FolderOpenIcon />}
+                defaultExpandIcon={<FolderIcon />} defaultEndIcon={<DescriptionIcon />} onNodeSelect={this.handleTreeItemClick} multiSelect>
+                  {this.renderTreeItems(this.state.policiesTreeData)}
+                </TreeView>
+          );
+    }
+}
\ No newline at end of file
diff --git a/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js b/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
index f986dff..0fd0d13 100644
--- a/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
+++ b/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
@@ -44,7 +44,6 @@
 import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
 import AddIcon from '@material-ui/icons/Add';
 import PublishIcon from '@material-ui/icons/Publish';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
 import Switch from '@material-ui/core/Switch';
 import MaterialTable from "material-table";
 import PolicyService from '../../../api/PolicyService';
@@ -56,6 +55,7 @@
 import PolicyEditor from './PolicyEditor';
 import ToscaViewer from './ToscaViewer';
 import PolicyDeploymentEditor from './PolicyDeploymentEditor';
+import PoliciesTreeViewer from './PoliciesTreeViewer';
 
 const DivWhiteSpaceStyled = styled.div`
     white-space: pre;
@@ -79,6 +79,18 @@
     margin-top: 20px;
 `
 
+const PoliciesTreeViewerDiv = styled.div`
+    width: 20%;
+    float: left;
+    left: 0;
+    overflow: auto;
+`
+
+const MaterialTableDiv = styled.div`
+    float: right;
+    width: 80%;
+    left: 20%;
+`
 
 const standardCellStyle = { backgroundColor: '#039be5', color: '#FFF', border: '1px solid black' };
 const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
@@ -87,13 +99,11 @@
 export default class ViewAllPolicies extends React.Component {
   state = {
         show: true,
-        showPolicyDeploymentDialog: false,
-        content: 'Please select a policy to display it',
-        selectedRowId: -1,
         policiesListData: [],
+        policiesListDataFiltered: [],
         toscaModelsListData: [],
+        toscaModelsListDataFiltered: [],
         jsonEditorForPolicy: new Map(),
-        prefixGrouping: false,
         showSuccessAlert: false,
         showFailAlert: false,
         policyColumnsDefinition: [
@@ -179,12 +189,13 @@
     constructor(props, context) {
         super(props, context);
         this.handleClose = this.handleClose.bind(this);
-        this.handlePrefixGrouping = this.handlePrefixGrouping.bind(this);
         this.handleDeletePolicy = this.handleDeletePolicy.bind(this);
         this.disableAlert = this.disableAlert.bind(this);
         this.getAllPolicies = this.getAllPolicies.bind(this);
         this.getAllToscaModels = this.getAllToscaModels.bind(this);
         this.generateAdditionalPolicyColumns = this.generateAdditionalPolicyColumns.bind(this);
+        this.filterPolicies = this.filterPolicies.bind(this);
+        this.filterTosca = this.filterTosca.bind(this);
         this.getAllPolicies();
         this.getAllToscaModels();
     }
@@ -217,14 +228,18 @@
 
     getAllToscaModels() {
         PolicyToscaService.getToscaPolicyModels().then(toscaModelsList => {
-            this.setState({ toscaModelsListData: toscaModelsList });
+            this.setState({ toscaModelsListData: toscaModelsList,
+                            toscaModelsListDataFiltered: toscaModelsList
+             });
         });
     }
 
     getAllPolicies() {
         PolicyService.getPoliciesList().then(allPolicies => {
             this.generateAdditionalPolicyColumns(allPolicies["policies"])
-            this.setState({ policiesListData: allPolicies["policies"] })
+            this.setState({ policiesListData: allPolicies["policies"],
+                            policiesListDataFiltered: allPolicies["policies"],
+            })
         });
 
     }
@@ -234,10 +249,6 @@
         this.props.history.push('/')
     }
 
-    handlePrefixGrouping(event) {
-        this.setState({prefixGrouping: event.target.checked});
-    }
-
     handleDeletePolicy(event, rowData) {
         PolicyService.deletePolicy(rowData["type"], rowData["type_version"], rowData["name"],rowData["version"]).then(
             respPolicyDeletion => {
@@ -262,74 +273,84 @@
         this.setState ({ showSuccessAlert: false, showFailAlert: false });
     }
 
+    filterPolicies(prefixForFiltering) {
+        this.setState({policiesListDataFiltered: this.state.policiesListData.filter(element => element.name.startsWith(prefixForFiltering))});
+    }
+
+    filterTosca(prefixForFiltering) {
+        this.setState({toscaModelsListDataFiltered: this.state.toscaModelsListData.filter(element => element.policyModelType.startsWith(prefixForFiltering))});
+    }
+
     renderPoliciesTab() {
         return (
                 <Tab eventKey="policies" title="Policies in Policy Framework">
                         <Modal.Body>
-                          <FormControlLabel
-                                control={<Switch checked={this.state.prefixGrouping} onChange={this.handlePrefixGrouping} />}
-                                label="Group by prefix"
+                        <div>
+                          <PoliciesTreeViewerDiv>
+                              <PoliciesTreeViewer policiesData={this.state.policiesListData} valueForTreeCreation="name" policiesFilterFunction={this.filterPolicies} />
+                          </PoliciesTreeViewerDiv>
+                          <MaterialTableDiv>
+                              <MaterialTable
+                                  title={"Policies"}
+                                  data={this.state.policiesListDataFiltered}
+                                  columns={this.state.policyColumnsDefinition}
+                                  icons={this.state.tableIcons}
+                                  onRowClick={(event, rowData, togglePanel) => togglePanel()}
+                                  options={{
+                                      grouping: true,
+                                      exportButton: true,
+                                      headerStyle:rowHeaderStyle,
+                                      actionsColumnIndex: -1
+                                  }}
+                                  detailPanel={[
+                                    {
+                                      icon: ArrowForwardIosIcon,
+                                      tooltip: 'Show Configuration',
+                                      render: rowData => {
+                                        return (
+                                            <DetailedRow>
+                                                <PolicyEditor policyModelType={rowData["type"]} policyModelTypeVersion={rowData["type_version"]}
+                                                    policyName={rowData["name"]} policyVersion={rowData["version"]} policyProperties={rowData["properties"]}
+                                                    policiesTableUpdateFunction={this.getAllPolicies} />
+                                            </DetailedRow>
+                                        )
+                                      },
+                                    },
+                                    {
+                                      icon: DehazeIcon,
+                                      openIcon: DehazeIcon,
+                                      tooltip: 'Show Raw Data',
+                                      render: rowData => {
+                                        return (
+                                            <DetailedRow>
+                                                <pre>{JSON.stringify(rowData, null, 2)}</pre>
+                                            </DetailedRow>
+                                        )
+                                      },
+                                    },
+                                    {
+                                      icon: PublishIcon,
+                                      openIcon: PublishIcon,
+                                      tooltip: 'PDP Group Deployment',
+                                      render: rowData => {
+                                        return (
+                                            <DetailedRow>
+                                                <PolicyDeploymentEditor policyData={rowData} policiesTableUpdateFunction={this.getAllPolicies} />
+                                            </DetailedRow>
+                                        )
+                                      },
+                                    }
+                                  ]}
+                                  actions={[
+                                      {
+                                        icon: forwardRef((props, ref) => <DeleteRoundedIcon {...props} ref={ref} />),
+                                        tooltip: 'Delete Policy',
+                                        onClick: (event, rowData) => this.handleDeletePolicy(event, rowData)
+                                      }
+                                  ]}
                               />
-                           <MaterialTable
-                              title={"Policies"}
-                              data={this.state.policiesListData}
-                              columns={this.state.policyColumnsDefinition}
-                              icons={this.state.tableIcons}
-                              onRowClick={(event, rowData, togglePanel) => togglePanel()}
-                              options={{
-                                  grouping: true,
-                                  exportButton: true,
-                                  headerStyle:rowHeaderStyle,
-                                  rowStyle: rowData => ({
-                                    backgroundColor: (this.state.selectedRowId !== -1 && this.state.selectedRowId === rowData.tableData.id) ? '#EEE' : '#FFF'
-                                  }),
-                                  actionsColumnIndex: -1
-                              }}
-                              detailPanel={[
-                                {
-                                  icon: ArrowForwardIosIcon,
-                                  tooltip: 'Show Configuration',
-                                  render: rowData => {
-                                    return (
-                                        <DetailedRow>
-                                            <PolicyEditor policyModelType={rowData["type"]} policyModelTypeVersion={rowData["type_version"]} policyName={rowData["name"]} policyVersion={rowData["version"]} policyProperties={rowData["properties"]} policiesTableUpdateFunction={this.getAllPolicies} />
-                                        </DetailedRow>
-                                    )
-                                  },
-                                },
-                                {
-                                  icon: DehazeIcon,
-                                  openIcon: DehazeIcon,
-                                  tooltip: 'Show Raw Data',
-                                  render: rowData => {
-                                    return (
-                                        <DetailedRow>
-                                            <pre>{JSON.stringify(rowData, null, 2)}</pre>
-                                        </DetailedRow>
-                                    )
-                                  },
-                                },
-                                {
-                                  icon: PublishIcon,
-                                  openIcon: PublishIcon,
-                                  tooltip: 'PDP Group Deployment',
-                                  render: rowData => {
-                                    return (
-                                        <DetailedRow>
-                                            <PolicyDeploymentEditor policyData={rowData} policiesTableUpdateFunction={this.getAllPolicies} />
-                                        </DetailedRow>
-                                    )
-                                  },
-                                }
-                              ]}
-                              actions={[
-                                  {
-                                    icon: forwardRef((props, ref) => <DeleteRoundedIcon {...props} ref={ref} />),
-                                    tooltip: 'Delete Policy',
-                                    onClick: (event, rowData) => this.handleDeletePolicy(event, rowData)
-                                  }
-                              ]}
-                           />
+                          </MaterialTableDiv>
+                          </div>
                         </Modal.Body>
                     </Tab>
         );
@@ -339,63 +360,63 @@
         return (
                     <Tab eventKey="tosca models" title="Tosca Models in Policy Framework">
                         <Modal.Body>
-                          <FormControlLabel
-                                control={<Switch checked={this.state.prefixGrouping} onChange={this.handlePrefixGrouping} />}
-                                label="Group by prefix"
-                              />
-                           <MaterialTable
-                              title={"Tosca Models"}
-                              data={this.state.toscaModelsListData}
-                              columns={this.state.toscaColumnsDefinition}
-                              icons={this.state.tableIcons}
-                              onRowClick={(event, rowData, togglePanel) => togglePanel()}
-                              options={{
-                                  grouping: true,
-                                  exportButton: true,
-                                  headerStyle:rowHeaderStyle,
-                                  rowStyle: rowData => ({
-                                    backgroundColor: (this.state.selectedRowId !== -1 && this.state.selectedRowId === rowData.tableData.id) ? '#EEE' : '#FFF'
-                                  }),
-                                  actionsColumnIndex: -1
-                              }}
-                              detailPanel={[
-                                {
-                                  icon: ArrowForwardIosIcon,
-                                  tooltip: 'Show Tosca',
-                                  render: rowData => {
-                                    return (
-                                        <DetailedRow>
-                                            <ToscaViewer toscaData={rowData} />
-                                        </DetailedRow>
-                                    )
-                                  },
-                                },
-                                {
-                                  icon: DehazeIcon,
-                                  openIcon: DehazeIcon,
-                                  tooltip: 'Show Raw Data',
-                                  render: rowData => {
-                                    return (
-                                        <DetailedRow>
-                                            <pre>{JSON.stringify(rowData, null, 2)}</pre>
-                                        </DetailedRow>
-                                    )
-                                  },
-                                },
-                                {
-                                  icon: AddIcon,
-                                  openIcon: AddIcon,
-                                  tooltip: 'Create a policy from this model',
-                                  render: rowData => {
-                                    return (
-                                        <DetailedRow>
-                                            <PolicyEditor policyModelType={rowData["policyModelType"]} policyModelTypeVersion={rowData["version"]} policyProperties={{}} policiesTableUpdateFunction={this.getAllPolicies} />
-                                        </DetailedRow>
-                                    )
-                                  },
-                                },
-                              ]}
-                           />
+                          <div>
+                              <PoliciesTreeViewerDiv>
+                                  <PoliciesTreeViewer policiesData={this.state.toscaModelsListData} valueForTreeCreation="policyModelType" policiesFilterFunction={this.filterTosca} />
+                              </PoliciesTreeViewerDiv>
+                              <MaterialTableDiv>
+                               <MaterialTable
+                                  title={"Tosca Models"}
+                                  data={this.state.toscaModelsListDataFiltered}
+                                  columns={this.state.toscaColumnsDefinition}
+                                  icons={this.state.tableIcons}
+                                  onRowClick={(event, rowData, togglePanel) => togglePanel()}
+                                  options={{
+                                      grouping: true,
+                                      exportButton: true,
+                                      headerStyle:rowHeaderStyle,
+                                      actionsColumnIndex: -1
+                                  }}
+                                  detailPanel={[
+                                    {
+                                      icon: ArrowForwardIosIcon,
+                                      tooltip: 'Show Tosca',
+                                      render: rowData => {
+                                        return (
+                                            <DetailedRow>
+                                                <ToscaViewer toscaData={rowData} />
+                                            </DetailedRow>
+                                        )
+                                      },
+                                    },
+                                    {
+                                      icon: DehazeIcon,
+                                      openIcon: DehazeIcon,
+                                      tooltip: 'Show Raw Data',
+                                      render: rowData => {
+                                        return (
+                                            <DetailedRow>
+                                                <pre>{JSON.stringify(rowData, null, 2)}</pre>
+                                            </DetailedRow>
+                                        )
+                                      },
+                                    },
+                                    {
+                                      icon: AddIcon,
+                                      openIcon: AddIcon,
+                                      tooltip: 'Create a policy from this model',
+                                      render: rowData => {
+                                        return (
+                                            <DetailedRow>
+                                                <PolicyEditor policyModelType={rowData["policyModelType"]} policyModelTypeVersion={rowData["version"]} policyProperties={{}} policiesTableUpdateFunction={this.getAllPolicies} />
+                                            </DetailedRow>
+                                        )
+                                      },
+                                    },
+                                  ]}
+                               />
+                              </MaterialTableDiv>
+                         </div>
                         </Modal.Body>
                     </Tab>
         );