Merge "Increase code coverage on gui.editors.apex.rest"
diff --git a/gui-clamp/.gitignore b/gui-clamp/.gitignore
new file mode 100644
index 0000000..a4bc4e7
--- /dev/null
+++ b/gui-clamp/.gitignore
@@ -0,0 +1,26 @@
+# Ignore files and directories that start with .
+.*
+.*/
+
+target
+/bin/
+
+# IntelliJ files
+*.iml
+**/*.iml
+
+# Node, NPM and Yarn files
+node
+node_modules
+package-lock.json
+yarn.lock
+
+# React
+*.log
+logs
+**/*.backup.*
+**/*.back.*
+bower_components
+
+# Exclude .gitignore from ignore list
+!/.gitignore
diff --git a/gui-clamp/pom.xml b/gui-clamp/pom.xml
index 44d2028..a1219e1 100644
--- a/gui-clamp/pom.xml
+++ b/gui-clamp/pom.xml
@@ -16,18 +16,217 @@
 
   SPDX-License-Identifier: Apache-2.0
   ============LICENSE_END=========================================================
--->
+ -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.onap.policy.gui</groupId>
         <artifactId>policy-gui</artifactId>
         <version>2.1.0-SNAPSHOT</version>
     </parent>
-
     <artifactId>gui-clamp</artifactId>
     <packaging>pom</packaging>
     <name>${project.artifactId}</name>
     <description>This module contains the implementation code for the CLAMP GUIs in the Policy Framework</description>
+    <properties>
+        <ui.react.src>ui-react</ui.react.src>
+        <ui.react.lib.src>ui-react-lib</ui.react.lib.src>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>3.2.0</version>
+                <executions>
+                    <execution>
+                        <id>copy-resources-1</id>
+                        <!-- here the phase you need -->
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/${ui.react.src}</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>${ui.react.src}</directory>
+                                    <excludes>
+                                        <exclude>node_modules</exclude>
+                                    </excludes>
+                                    <includes>
+                                        <include>src/**</include>
+                                        <include>public/**</include>
+                                        <include>package.json</include>
+                                        <include>package-lock.json</include>
+                                    </includes>
+                                    <filtering>true</filtering>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>copy-resources-2</id>
+                        <!-- here the phase you need -->
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/${ui.react.src}</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>${ui.react.src}</directory>
+                                    <includes>
+                                        <include>src/**</include>
+                                    </includes>
+                                    <excludes>
+                                        <exclude>**/__snapshots__/**</exclude>
+                                        <exclude>**/**.test.js</exclude>
+                                    </excludes>
+                                    <filtering>true</filtering>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>copy-resources-3</id>
+                        <!-- here the phase you need -->
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/${ui.react.lib.src}</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>${ui.react.lib.src}</directory>
+                                    <includes>
+                                        <include>**</include>
+                                    </includes>
+                                    <excludes>
+                                        <exclude>node_modules/**</exclude>
+                                        <exclude>package-lock.json</exclude>
+                                    </excludes>
+                                    <filtering>true</filtering>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>com.github.eirslett</groupId>
+                <artifactId>frontend-maven-plugin</artifactId>
+                <version>1.11.3</version>
+                <configuration>
+                    <installDirectory>${project.build.directory}/${ui.react.src}</installDirectory>
+                    <npmDownloadRoot>${repo.npm}</npmDownloadRoot>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>install node and npm</id>
+                        <goals>
+                            <goal>install-node-and-npm</goal>
+                        </goals>
+                        <phase>generate-sources</phase>
+                        <configuration>
+                            <nodeVersion>v14.17.0</nodeVersion>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>npm_install</id>
+                        <goals>
+                            <goal>npm</goal>
+                        </goals>
+                        <phase>compile</phase>
+                        <configuration>
+                            <workingDirectory>${project.build.directory}/${ui.react.src}</workingDirectory>
+                            <arguments>install</arguments>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>npm_test</id>
+                        <goals>
+                            <goal>npm</goal>
+                        </goals>
+                        <phase>test</phase>
+                        <configuration>
+                            <skip>${maven.test.skip}</skip>
+                            <arguments>run-script test:coverage</arguments>
+                            <workingDirectory>${project.build.directory}/${ui.react.src}</workingDirectory>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>npm_install_lib</id>
+                        <goals>
+                            <goal>npm</goal>
+                        </goals>
+                        <phase>deploy</phase>
+                        <configuration>
+                            <workingDirectory>${project.build.directory}/${ui.react.lib.src}</workingDirectory>
+                            <arguments>install</arguments>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>npm_build_lib</id>
+                        <goals>
+                            <goal>npm</goal>
+                        </goals>
+                        <phase>deploy</phase>
+                        <configuration>
+                            <workingDirectory>${project.build.directory}/${ui.react.lib.src}</workingDirectory>
+                            <arguments>run build</arguments>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>npm_publish</id>
+                        <goals>
+                            <goal>npm</goal>
+                        </goals>
+                        <phase>deploy</phase>
+                        <configuration>
+                            <workingDirectory>${project.build.directory}/${ui.react.lib.src}</workingDirectory>
+                            <arguments>publish</arguments>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>3.0.0</version>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <directory>${ui.react.lib.src}/node</directory>
+                        </fileset>
+                        <fileset>
+                            <directory>${ui.react.src}/node</directory>
+                        </fileset>
+                        <fileset>
+                            <directory>${ui.react.src}/node_modules</directory>
+                        </fileset>
+                        <fileset>
+                            <directory>${ui.react.lib.src}/node_modules</directory>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>11</source>
+                    <target>11</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>versions-maven-plugin</artifactId>
+                <version>1.3.1</version>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/gui-clamp/ui-react-lib/libExportExclusions.dat b/gui-clamp/ui-react-lib/libExportExclusions.dat
new file mode 100644
index 0000000..7194fc2
--- /dev/null
+++ b/gui-clamp/ui-react-lib/libExportExclusions.dat
@@ -0,0 +1,4 @@
+./src/theme/globalStyle.js
+./src/index.js
+./src/setupTests.js
+./src/OnapClamp.js
diff --git a/gui-clamp/ui-react-lib/libIndex.js b/gui-clamp/ui-react-lib/libIndex.js
new file mode 100644
index 0000000..46c331f
--- /dev/null
+++ b/gui-clamp/ui-react-lib/libIndex.js
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export { default as CsvToJson } from './src/utils/CsvToJson';
+export { default as CreateLoopModal } from './src/components/dialogs/Loop/CreateLoopModal';
+export { default as DeployLoopModal } from './src/components/dialogs/Loop/DeployLoopModal';
+export { default as LoopActionService } from './src/api/LoopActionService';
+export { default as LoopCache } from './src/api/LoopCache';
+export { default as PoliciesListCache } from './src/api/PoliciesListCache';
+export { default as LoopLogs } from './src/components/loop_viewer/logs/LoopLogs';
+export { default as LoopPropertiesModal } from './src/components/dialogs/Loop/LoopPropertiesModal';
+export { default as LoopService } from './src/api/LoopService';
+export { default as PolicyService } from './src/api/PolicyService';
+export { default as LoopStatus } from './src/components/loop_viewer/status/LoopStatus';
+export { default as LoopUI } from './src/LoopUI';
+export { default as ManageDictionaries } from './src/components/dialogs/ManageDictionaries/ManageDictionaries';
+export { default as MenuBar } from './src/components/menu/MenuBar';
+export { default as ModifyLoopModal } from './src/components/dialogs/Loop/ModifyLoopModal';
+export { default as NotFound } from './src/NotFound';
+export { default as OnapConstants } from './src/utils/OnapConstants';
+export { default as OnapUtils } from './src/utils/OnapUtils';
+export { default as OpenLoopModal } from './src/components/dialogs/Loop/OpenLoopModal';
+export { default as PerformActions } from './src/components/dialogs/PerformActions';
+export { default as PolicyModal } from './src/components/dialogs/Policy/PolicyModal';
+export { default as ToscaViewer } from './src/components/dialogs/Policy/ToscaViewer';
+export { default as PolicyEditor } from './src/components/dialogs/Policy/PolicyEditor';
+export { default as PolicyToscaService } from './src/api/PolicyToscaService';
+export { default as RefreshStatus } from './src/components/dialogs/RefreshStatus';
+export { default as SvgGenerator } from './src/components/loop_viewer/svg/SvgGenerator';
+export { default as TemplateService } from './src/api/TemplateService';
+export { default as UserInfoModal } from './src/components/dialogs/UserInfoModal';
+export { default as UserService } from './src/api/UserService';
+export { default as ViewLoopTemplatesModal } from './src/components/dialogs/Tosca/ViewLoopTemplatesModal';
+export { default as ViewAllPolicies } from './src/components/dialogs/Policy/ViewAllPolicies';
diff --git a/gui-clamp/ui-react-lib/package.json b/gui-clamp/ui-react-lib/package.json
new file mode 100644
index 0000000..b1efe2f
--- /dev/null
+++ b/gui-clamp/ui-react-lib/package.json
@@ -0,0 +1,54 @@
+{
+	"name": "onap-policy-clamp-ui-lib",
+	"version": "${project.version}",
+	"description": "ONAP Policy Clamp UI Library",
+	"author": "ONAP Policy Team",
+	"license": "Apache-2.0",
+	"publishConfig": {
+		"registry": "${npm.publish.url}"
+	},
+	"main": "dist/index.js",
+	"module": "dist/esindex.js",
+	"scripts": {
+		"build": "rollup -c"
+	},
+	"files": [
+		"dist"
+	],
+	"peerDependencies": {
+		"@json-editor/json-editor": "1.4.0-beta.0",
+		"@material-ui/core": "4.6.0",
+		"@material-ui/icons": "4.5.1",
+		"bootstrap-css-only": "4.3.1",
+		"material-table": "1.53.0",
+		"react": "16.9.0",
+		"react-bootstrap": "1.0.0-beta.14",
+		"react-dom": "16.9.0",
+		"react-router-dom": "5.0.1",
+		"react-scripts": "3.1.1",
+		"react-select": "3.0.8",
+		"rollup": "^1.29.1",
+		"styled-components": "4.3.2"
+	},
+	"devDependencies": {
+		"@babel/cli": "7.7.5",
+		"@babel/core": "7.7.5",
+		"@babel/helper-plugin-utils": "7.0.0",
+		"@babel/plugin-external-helpers": "^7.7.4",
+		"@babel/plugin-proposal-class-properties": "7.5.5",
+		"@babel/plugin-transform-runtime": "7.7.6",
+		"@babel/preset-env": "7.5.5",
+		"@babel/preset-react": "7.0.0",
+		"rollup": "1.29.1",
+		"@rollup/plugin-commonjs": "^11.0.1",
+		"@rollup/plugin-image": "2.0.0",
+		"@rollup/plugin-node-resolve": "7.0.0",
+		"@rollup/plugin-replace": "2.2.1",
+		"@rollup/plugin-url": "4.0.0",
+		"@svgr/rollup": "5.1.0",
+		"babel-plugin-external-helpers": "6.22.0",
+		"rollup-plugin-babel": "4.3.3",
+		"rollup-plugin-peer-deps-external": "2.2.0",
+		"rollup-plugin-postcss": "2.0.3"
+	}
+}
diff --git a/gui-clamp/ui-react-lib/rollup.config.js b/gui-clamp/ui-react-lib/rollup.config.js
new file mode 100644
index 0000000..c671779
--- /dev/null
+++ b/gui-clamp/ui-react-lib/rollup.config.js
@@ -0,0 +1,48 @@
+import babel from 'rollup-plugin-babel'
+import commonjs from '@rollup/plugin-commonjs'
+import postcss from 'rollup-plugin-postcss'
+import resolve from '@rollup/plugin-node-resolve'
+import external from 'rollup-plugin-peer-deps-external'
+import img from '@rollup/plugin-image'
+
+import pkg from './package.json'
+
+export default {
+	input: './libIndex.js',
+	output: [
+		{
+			file: pkg.main,
+			format: 'cjs',
+			sourcemap: true
+		},
+		{
+			file: pkg.module,
+			format: 'es',
+			sourcemap: true
+		}
+	],
+	plugins: [
+		img(),
+		external(),
+		postcss({
+			modules: true
+		}),
+		babel({
+			exclude: 'node_modules/**',
+			presets: [
+	
+				"@babel/preset-env", {},
+				"@babel/preset-react", {}
+			],
+			plugins: [
+					[
+						"@babel/plugin-proposal-class-properties",
+						{
+							"loose": true
+						}
+					]
+			]
+		}),
+		resolve({ preferBuiltins: true, mainFields: ['browser'] })
+	]
+}
diff --git a/gui-clamp/ui-react/package.json b/gui-clamp/ui-react/package.json
new file mode 100644
index 0000000..8d11044
--- /dev/null
+++ b/gui-clamp/ui-react/package.json
@@ -0,0 +1,98 @@
+{
+	"name": "onap-policy-clamp-ui",
+	"version": "${project.version}",
+	"description": "ONAP Policy Clamp UI",
+	"author": "ONAP Policy Team",
+	"license": "Apache-2.0",
+	"publishConfig": {
+		"registry": "${npm.publish.url}"
+	},
+	"main": "index.js",
+	"proxy": "https://localhost:8443",
+	"scripts": {
+		"start": "HTTPS=true react-scripts start",
+		"build": "react-scripts build",
+		"test": "jest",
+		"test:watch": "jest --watch",
+		"test:coverage": "jest --coverage",
+		"eject": "react-scripts eject"
+	},
+	"files": [
+		"src/*.js",
+		"src/*.png",
+		"src/api",
+		"src/components",
+		"src/theme"
+	],
+	"dependencies": {
+		"@json-editor/json-editor": "2.5.2",
+		"@fortawesome/fontawesome-free": "5.15.2",
+		"react": "17.0.1",
+		"react-dom": "17.0.1",
+		"react-scripts": "4.0.3",
+		"react-bootstrap": "1.5.2",
+		"bootstrap-css-only": "4.3.1",
+		"styled-components": "5.2.1",
+		"react-router-dom": "5.2.0",
+		"@material-ui/core": "4.11.3",
+		"@material-ui/icons": "4.11.2",
+		"material-table": "1.68.1",
+		"react-select": "4.2.1",
+		"react-uuid": "1.0.2"
+	},
+	"devDependencies": {
+		"jest": "26.6.0",
+		"babel-jest": "26.6.0",
+		"@babel/preset-env": "7.13.10",
+		"@babel/preset-react": "7.12.13",
+		"@babel/plugin-proposal-class-properties": "7.13.0",
+		"enzyme": "3.11.0",
+		"enzyme-adapter-react-17-updated": "1.0.2",
+		"enzyme-to-json": "3.6.1",
+		"jest-fetch-mock": "3.0.3"
+	},
+	"browserslist": [
+		">0.2%",
+		"not dead",
+		"not ie <= 11",
+		"not op_mini all"
+	],
+	"jest": {
+		"verbose": true,
+		"coverageDirectory": "${project.build.directory}/${ui.react.src}/coverage",
+		"collectCoverageFrom": [
+			"**/*.{js,jsx}"
+		],
+		"rootDir": "${project.build.directory}/${ui.react.src}",
+		"coverageReporters": [
+			"lcov"
+		],
+		"moduleNameMapper": {
+			"\\.(css|png)$": "identity-obj-proxy"
+		},
+		"setupFiles": [
+			"./src/setupTests.js"
+		],
+		"snapshotSerializers": [
+			"enzyme-to-json/serializer"
+		]
+	},
+	"babel": {
+		"presets": [
+			"@babel/preset-env",
+			"@babel/preset-react"
+		],
+		"plugins": [
+			[
+				"@babel/plugin-proposal-class-properties",
+				{
+					"loose": true
+				}
+			],
+			[
+				"@babel/plugin-transform-runtime"
+			]
+		]
+	}
+}
+
diff --git a/gui-clamp/ui-react/public/index.html b/gui-clamp/ui-react/public/index.html
new file mode 100644
index 0000000..bab3e7a
--- /dev/null
+++ b/gui-clamp/ui-react/public/index.html
@@ -0,0 +1,40 @@
+<!--
+  ============LICENSE_START=======================================================
+  ONAP CLAMP
+  ================================================================================
+  Copyright (C) 2019 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============================================
+  ===================================================================
+
+  -->
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport"
+    content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<meta name="theme-color" content="#000000">
+<link rel="manifest" href="manifest.json">
+<link rel="shortcut icon" href="onap.ico">
+
+<title>Clamp Designer UI</title>
+</head>
+<body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+</body>
+</html>
diff --git a/gui-clamp/ui-react/public/manifest.json b/gui-clamp/ui-react/public/manifest.json
new file mode 100644
index 0000000..8210c4e
--- /dev/null
+++ b/gui-clamp/ui-react/public/manifest.json
@@ -0,0 +1,15 @@
+{
+  "short_name": "Clamp Designer UI",
+  "name": "Clamp Designer UI",
+  "icons": [
+    {
+      "src": "onap.ico",
+      "sizes": "64x64 32x32 24x24 16x16",
+      "type": "image/x-icon"
+    }
+  ],
+  "start_url": "./index.html",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}
diff --git a/gui-clamp/ui-react/public/onap.ico b/gui-clamp/ui-react/public/onap.ico
new file mode 100644
index 0000000..85e168a
--- /dev/null
+++ b/gui-clamp/ui-react/public/onap.ico
Binary files differ
diff --git a/gui-clamp/ui-react/src/LoopUI.js b/gui-clamp/ui-react/src/LoopUI.js
new file mode 100644
index 0000000..d998762
--- /dev/null
+++ b/gui-clamp/ui-react/src/LoopUI.js
@@ -0,0 +1,420 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019, 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 from 'react';
+import styled from 'styled-components';
+import MenuBar from './components/menu/MenuBar';
+import Navbar from 'react-bootstrap/Navbar';
+import logo from './logo.png';
+import { GlobalClampStyle } from './theme/globalStyle.js';
+import OnapConstants from './utils/OnapConstants';
+
+import SvgGenerator from './components/loop_viewer/svg/SvgGenerator';
+import LoopLogs from './components/loop_viewer/logs/LoopLogs';
+import LoopStatus from './components/loop_viewer/status/LoopStatus';
+import UserService from './api/UserService';
+import LoopCache from './api/LoopCache';
+import LoopActionService from './api/LoopActionService';
+
+import { Route } from 'react-router-dom'
+import CreateLoopModal from './components/dialogs/Loop/CreateLoopModal';
+import OpenLoopModal from './components/dialogs/Loop/OpenLoopModal';
+import ModifyLoopModal from './components/dialogs/Loop/ModifyLoopModal';
+import PolicyModal from './components/dialogs/Policy/PolicyModal';
+import ViewAllPolicies from './components/dialogs/Policy/ViewAllPolicies';
+import LoopPropertiesModal from './components/dialogs/Loop/LoopPropertiesModal';
+import UserInfoModal from './components/dialogs/UserInfoModal';
+import LoopService from './api/LoopService';
+import ViewLoopTemplatesModal from './components/dialogs/Tosca/ViewLoopTemplatesModal';
+import ManageDictionaries from './components/dialogs/ManageDictionaries/ManageDictionaries';
+import PerformAction from './components/dialogs/PerformActions';
+import RefreshStatus from './components/dialogs/RefreshStatus';
+import DeployLoopModal from './components/dialogs/Loop/DeployLoopModal';
+import Alert from 'react-bootstrap/Alert';
+import Spinner from 'react-bootstrap/Spinner';
+
+import { Link } from 'react-router-dom';
+
+const StyledMainDiv = styled.div`
+  background-color: ${ props => props.theme.backgroundColor };
+`
+
+const StyledSpinnerDiv = styled.div`
+  justify-content: center !important;
+  display: flex !important;
+`;
+
+const ProjectNameStyled = styled.a`
+  vertical-align: middle;
+  padding-left: 30px;
+  font-size: 36px;
+  font-weight: bold;
+`
+
+const StyledRouterLink = styled(Link)`
+  color: ${ props => props.theme.menuFontColor };
+  background-color: ${ props => props.theme.backgroundColor };
+`
+
+const StyledLoginInfo = styled.a`
+  color: ${ props => props.theme.menuFontColor };
+  background-color: ${ props => props.theme.backgroundColor };
+`
+
+const LoopViewDivStyled = styled.div`
+  height: 100%;
+  overflow: hidden;
+  margin-left: 10px;
+  margin-right: 10px;
+  margin-bottom: 10px;
+  color: ${ props => props.theme.loopViewerFontColor };
+  background-color: ${ props => props.theme.loopViewerBackgroundColor };
+  border: 1px solid transparent;
+  border-color: ${ props => props.theme.loopViewerHeaderBackgroundColor };
+`
+
+const LoopViewHeaderDivStyled = styled.div`
+  background-color: ${ props => props.theme.loopViewerHeaderBackgroundColor };
+  padding: 10px 10px;
+  color: ${ props => props.theme.loopViewerHeaderFontColor };
+`
+
+const LoopViewBodyDivStyled = styled.div`
+  background-color: ${ props => (props.theme.loopViewerBackgroundColor) };
+  padding: 10px 10px;
+  color: ${ props => (props.theme.loopViewerHeaderFontColor) };
+  height: 95%;
+`
+
+export default class LoopUI extends React.Component {
+
+  state = {
+    userName: null,
+    loopName: OnapConstants.defaultLoopName,
+    loopCache: new LoopCache({}),
+    showSucAlert: false,
+    showFailAlert: false,
+    busyLoadingCount: 0
+  };
+
+  constructor() {
+    super();
+    this.getUser = this.getUser.bind(this);
+    this.updateLoopCache = this.updateLoopCache.bind(this);
+    this.loadLoop = this.loadLoop.bind(this);
+    this.closeLoop = this.closeLoop.bind(this);
+    this.showSucAlert = this.showSucAlert.bind(this);
+    this.showFailAlert = this.showFailAlert.bind(this);
+    this.disableAlert = this.disableAlert.bind(this);
+    this.setBusyLoading = this.setBusyLoading.bind(this);
+    this.clearBusyLoading = this.clearBusyLoading.bind(this);
+    this.isBusyLoading = this.isBusyLoading.bind(this);
+    this.renderGlobalStyle = this.renderGlobalStyle.bind(this);
+    this.renderSvg = this.renderSvg.bind(this);
+  }
+
+  componentWillMount() {
+    this.getUser();
+  }
+
+  getUser() {
+    UserService.login().then(user => {
+      this.setState({ userName: user })
+    });
+  }
+
+  renderMenuNavBar() {
+    return (
+      <MenuBar loopName={ this.state.loopName }/>
+    );
+  }
+
+  renderUserLoggedNavBar() {
+    return (
+      <Navbar.Text>
+        <StyledLoginInfo>Signed in as: </StyledLoginInfo>
+        <StyledRouterLink to="/userInfo">{ this.state.userName }</StyledRouterLink>
+      </Navbar.Text>
+    );
+  }
+
+  renderLogoNavBar() {
+    return (
+      <Navbar.Brand>
+        <img height="50px" width="234px" src={ logo } alt=""/>
+        <ProjectNameStyled>CLAMP</ProjectNameStyled>
+      </Navbar.Brand>
+    );
+  }
+
+  renderAlertBar() {
+    return (
+      <div>
+        <Alert variant="success" show={ this.state.showSucAlert } onClose={ this.disableAlert } dismissible>
+          { this.state.showMessage }
+        </Alert>
+        <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+          { this.state.showMessage }
+        </Alert>
+      </div>
+    );
+  }
+
+  renderNavBar() {
+    return (
+      <Navbar>
+        { this.renderLogoNavBar() }
+        <Navbar.Toggle aria-controls="responsive-navbar-nav"/>
+        { this.renderMenuNavBar() }
+        { this.renderUserLoggedNavBar() }
+      </Navbar>
+    );
+  }
+
+  renderLoopViewHeader() {
+    return (
+      <LoopViewHeaderDivStyled>
+        Loop Viewer - { this.state.loopName } - ({ this.state.loopCache.getTemplateName() })
+      </LoopViewHeaderDivStyled>
+    );
+  }
+
+  renderSvg() {
+    return (
+      <SvgGenerator loopCache={ this.state.loopCache } clickable={ true } generatedFrom={ SvgGenerator.GENERATED_FROM_INSTANCE } isBusyLoading={ this.isBusyLoading }/>
+    )
+  }
+
+  renderLoopViewBody() {
+    return (
+      <LoopViewBodyDivStyled>
+        { this.renderSvg() }
+        <LoopStatus loopCache={ this.state.loopCache }/>
+        <LoopLogs loopCache={ this.state.loopCache }/>
+      </LoopViewBodyDivStyled>
+    );
+  }
+
+  getLoopCache() {
+    return this.state.loopCache;
+
+  }
+
+  renderLoopViewer() {
+    return (
+      <LoopViewDivStyled>
+        { this.renderLoopViewHeader() }
+        { this.renderLoopViewBody() }
+      </LoopViewDivStyled>
+    );
+  }
+
+  updateLoopCache(loopJson) {
+
+    // If call with an empty object for loopJson, this is a reset to empty
+    // from someplace like PerformActions for the case where we are "deleting"
+    // a Control Loop model. Set the loopName to the default.
+
+    if (loopJson === null) {
+      this.setState({ loopName: OnapConstants.defaultLoopName });
+      this.setState({ loopCache: new LoopCache({}) });
+    } else {
+      this.setState({ loopCache: new LoopCache(loopJson) });
+      this.setState({ loopName: this.state.loopCache.getLoopName() });
+    }
+    console.info(this.state.loopName + " loop loaded successfully");
+  }
+
+  showSucAlert(message) {
+    this.setState({ showSucAlert: true, showMessage: message });
+  }
+
+  showFailAlert(message) {
+    this.setState({ showFailAlert: true, showMessage: message });
+  }
+
+  disableAlert() {
+    this.setState({ showSucAlert: false, showFailAlert: false });
+  }
+
+  loadLoop(loopName) {
+    this.setBusyLoading();
+    LoopService.getLoop(loopName).then(loop => {
+      console.debug("Updating loopCache");
+      LoopActionService.refreshStatus(loopName).then(data => {
+        this.updateLoopCache(data);
+        this.clearBusyLoading();
+        this.props.history.push('/');
+      })
+        .catch(error => {
+          this.updateLoopCache(loop);
+          this.clearBusyLoading();
+          this.props.history.push('/');
+        });
+    });
+  }
+
+  setBusyLoading() {
+    this.setState((state, props) => ({ busyLoadingCount: ++state.busyLoadingCount }));
+  }
+
+  clearBusyLoading() {
+    this.setState((state, props) => ({ busyLoadingCount: --state.busyLoadingCount }));
+  }
+
+  isBusyLoading() {
+    if (this.state.busyLoadingCount === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  closeLoop() {
+    this.setState({ loopCache: new LoopCache({}), loopName: OnapConstants.defaultLoopName });
+    this.props.history.push('/');
+  }
+
+  renderRoutes() {
+    return (
+      <React.Fragment>
+        <Route path="/viewLoopTemplatesModal" render={ (routeProps) => (<ViewLoopTemplatesModal { ...routeProps } />) }/>
+        <Route path="/manageDictionaries" render={ (routeProps) => (<ManageDictionaries { ...routeProps } />) }/>
+        <Route path="/viewAllPolicies" render={ (routeProps) => (<ViewAllPolicies { ...routeProps } />) }/>
+        <Route path="/policyModal/:policyInstanceType/:policyName" render={ (routeProps) => (<PolicyModal { ...routeProps }
+                                                                                                          loopCache={ this.getLoopCache() }
+                                                                                                          loadLoopFunction={ this.loadLoop }/>) }
+        />
+        <Route path="/createLoop" render={ (routeProps) => (<CreateLoopModal { ...routeProps }
+                                                                             loadLoopFunction={ this.loadLoop }/>) }
+        />
+        <Route path="/openLoop" render={ (routeProps) => (<OpenLoopModal { ...routeProps }
+                                                                         loadLoopFunction={ this.loadLoop }/>) }
+        />
+        <Route path="/loopProperties" render={ (routeProps) => (<LoopPropertiesModal { ...routeProps }
+                                                                                     loopCache={ this.getLoopCache() }
+                                                                                     loadLoopFunction={ this.loadLoop }/>) }
+        />
+        <Route path="/modifyLoop" render={ (routeProps) => (<ModifyLoopModal { ...routeProps }
+                                                                             loopCache={ this.getLoopCache() }
+                                                                             loadLoopFunction={ this.loadLoop }/>) }
+        />
+
+        <Route path="/userInfo" render={ (routeProps) => (<UserInfoModal { ...routeProps } />) }/>
+        <Route path="/closeLoop" render={ this.closeLoop }/>
+
+        <Route path="/submit" render={ (routeProps) => (<PerformAction { ...routeProps }
+                                                                       loopAction="submit"
+                                                                       loopCache={ this.getLoopCache() }
+                                                                       updateLoopFunction={ this.updateLoopCache }
+                                                                       showSucAlert={ this.showSucAlert }
+                                                                       showFailAlert={ this.showFailAlert }
+                                                                       setBusyLoading={ this.setBusyLoading }
+                                                                       clearBusyLoading={ this.clearBusyLoading }/>) }
+        />
+        <Route path="/stop" render={ (routeProps) => (<PerformAction { ...routeProps }
+                                                                     loopAction="stop"
+                                                                     loopCache={ this.getLoopCache() }
+                                                                     updateLoopFunction={ this.updateLoopCache }
+                                                                     showSucAlert={ this.showSucAlert }
+                                                                     showFailAlert={ this.showFailAlert }
+                                                                     setBusyLoading={ this.setBusyLoading }
+                                                                     clearBusyLoading={ this.clearBusyLoading }/>) }
+        />
+        <Route path="/restart" render={ (routeProps) => (<PerformAction { ...routeProps }
+                                                                        loopAction="restart"
+                                                                        loopCache={ this.getLoopCache() }
+                                                                        updateLoopFunction={ this.updateLoopCache }
+                                                                        showSucAlert={ this.showSucAlert }
+                                                                        showFailAlert={ this.showFailAlert }
+                                                                        setBusyLoading={ this.setBusyLoading }
+                                                                        clearBusyLoading={ this.clearBusyLoading }/>) }
+        />
+        <Route path="/delete" render={ (routeProps) => (<PerformAction { ...routeProps }
+                                                                       loopAction="delete"
+                                                                       loopCache={ this.getLoopCache() }
+                                                                       updateLoopFunction={ this.updateLoopCache }
+                                                                       showSucAlert={ this.showSucAlert }
+                                                                       showFailAlert={ this.showFailAlert }
+                                                                       setBusyLoading={ this.setBusyLoading }
+                                                                       clearBusyLoading={ this.clearBusyLoading }/>) }
+        />
+        <Route path="/undeploy" render={ (routeProps) => (<PerformAction { ...routeProps }
+                                                                         loopAction="undeploy"
+                                                                         loopCache={ this.getLoopCache() }
+                                                                         updateLoopFunction={ this.updateLoopCache }
+                                                                         showSucAlert={ this.showSucAlert }
+                                                                         showFailAlert={ this.showFailAlert }
+                                                                         setBusyLoading={ this.setBusyLoading }
+                                                                         clearBusyLoading={ this.clearBusyLoading }/>) }
+        />
+        <Route path="/deploy" render={ (routeProps) => (<DeployLoopModal { ...routeProps }
+                                                                         loopCache={ this.getLoopCache() }
+                                                                         updateLoopFunction={ this.updateLoopCache }
+                                                                         showSucAlert={ this.showSucAlert }
+                                                                         showFailAlert={ this.showFailAlert }/>) }
+        />
+        <Route path="/refreshStatus" render={ (routeProps) => (<RefreshStatus { ...routeProps }
+                                                                              loopCache={ this.getLoopCache() }
+                                                                              updateLoopFunction={ this.updateLoopCache }
+                                                                              showSucAlert={ this.showSucAlert }
+                                                                              showFailAlert={ this.showFailAlert }/>) }
+        />
+      </React.Fragment>
+    );
+  }
+
+  renderGlobalStyle() {
+    return (
+      <GlobalClampStyle/>
+    );
+  };
+
+
+  renderSpinner() {
+    if (this.isBusyLoading()) {
+      return (
+        <StyledSpinnerDiv>
+          <Spinner animation="border" role="status">
+            <span className="sr-only">Loading...</span>
+          </Spinner>
+        </StyledSpinnerDiv>
+      );
+    } else {
+      return (<div></div>);
+    }
+  }
+
+  render() {
+    return (
+      <StyledMainDiv id="main_div">
+        { this.renderGlobalStyle() }
+        { this.renderRoutes() }
+        { this.renderSpinner() }
+        { this.renderAlertBar() }
+        { this.renderNavBar() }
+        { this.renderLoopViewer() }
+      </StyledMainDiv>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/LoopUI.test.js b/gui-clamp/ui-react/src/LoopUI.test.js
new file mode 100644
index 0000000..47ade44
--- /dev/null
+++ b/gui-clamp/ui-react/src/LoopUI.test.js
@@ -0,0 +1,173 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import LoopUI from './LoopUI';
+import OnapConstants from './utils/OnapConstants';
+
+import LoopCache from './api/LoopCache';
+import LoopActionService from './api/LoopActionService';
+import LoopService from './api/LoopService';
+
+describe('Verify LoopUI', () => {
+  beforeEach(() => {
+    fetch.resetMocks();
+    fetch.mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        text: () => "testUser"
+
+      });
+    });
+  })
+
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+    "components": {
+      "POLICY": {
+        "componentState": {
+          "stateName": "UNKNOWN",
+          "description": "The policies defined have NOT yet been created on the policy engine"
+        }
+      },
+      "DCAE": {
+        "componentState": {
+          "stateName": "BLUEPRINT_DEPLOYED",
+          "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+        }
+      }
+    }
+  });
+
+  it('Test the render method', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+
+    const component = shallow(<LoopUI/>)
+    component.setState({
+      loopName: "testLoopName",
+      showSucAlert: false,
+      showFailAlert: false
+    });
+    await flushPromises();
+    expect(component).toMatchSnapshot();
+  });
+
+  test('Test closeLoop method', () => {
+    const historyMock = { push: jest.fn() };
+    const component = shallow(<LoopUI history={ historyMock }/>)
+    const instance = component.instance();
+    instance.closeLoop();
+
+    expect(component.state('loopName')).toEqual(OnapConstants.defaultLoopName);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  })
+
+  test('Test loadLoop method refresh suc', async () => {
+    const historyMock = { push: jest.fn() };
+    LoopService.getLoop = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        text: () => {
+        }
+      });
+    });
+
+    LoopActionService.refreshStatus = jest.fn().mockImplementation(() => {
+      return Promise.resolve({ name: "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca" });
+    });
+
+    const flushPromises = () => new Promise(setImmediate);
+    const component = shallow(<LoopUI history={ historyMock }/>)
+    const instance = component.instance();
+    instance.loadLoop("LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca");
+
+    await flushPromises();
+
+    const resLoopCache = instance.getLoopCache();
+
+    expect(resLoopCache.getComponentStates()).toBeUndefined();
+    expect(component.state('loopName')).toEqual("LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca");
+  })
+
+  test('Test loadLoop method refresh fail', async () => {
+    const historyMock = { push: jest.fn() };
+    LoopService.getLoop = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        name: "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+        "components": {
+          "POLICY": {
+            "componentState": {
+              "stateName": "UNKNOWN",
+              "description": "The policies defined have NOT yet been created on the policy engine"
+            }
+          },
+          "DCAE": {
+            "componentState": {
+              "stateName": "BLUEPRINT_DEPLOYED",
+              "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+            }
+          }
+        }
+      });
+    });
+
+    LoopActionService.refreshStatus = jest.fn().mockImplementation(() => {
+      return Promise.reject({ error: "whatever" });
+    });
+
+    const flushPromises = () => new Promise(setImmediate);
+    const component = shallow(<LoopUI history={ historyMock }/>)
+    const instance = component.instance();
+    instance.loadLoop("LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca");
+
+    await flushPromises();
+
+    const resLoopCache = instance.getLoopCache();
+
+    expect(resLoopCache).toEqual(loopCache);
+    expect(component.state('loopName')).toEqual("LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca");
+  })
+
+  test('Test alert methods', () => {
+    const component = shallow(<LoopUI/>)
+    expect(component.state('showSucAlert')).toEqual(false);
+
+    const instance = component.instance();
+    instance.showSucAlert("testAlert");
+    expect(component.state('showSucAlert')).toEqual(true);
+    expect(component.state('showFailAlert')).toEqual(false);
+    expect(component.state('showMessage')).toEqual("testAlert");
+
+    instance.disableAlert();
+
+    expect(component.state('showSucAlert')).toEqual(false);
+    expect(component.state('showFailAlert')).toEqual(false);
+
+    instance.showFailAlert("testAlert2");
+    expect(component.state('showSucAlert')).toEqual(false);
+    expect(component.state('showFailAlert')).toEqual(true);
+    expect(component.state('showMessage')).toEqual("testAlert2");
+  })
+});
diff --git a/gui-clamp/ui-react/src/NotFound.js b/gui-clamp/ui-react/src/NotFound.js
new file mode 100644
index 0000000..fb6d10a
--- /dev/null
+++ b/gui-clamp/ui-react/src/NotFound.js
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react'
+
+
+export default class NotFound extends React.Component {
+  render() {
+    return (
+      <div id='main'>
+        <div class="divRow"><b>Page Not Found!</b></div>
+        <div class="divRow">Please cick <a href="/">here</a> to go back to the main page.</div>
+      </div>
+
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/NotFound.test.js b/gui-clamp/ui-react/src/NotFound.test.js
new file mode 100644
index 0000000..3b4dcb0
--- /dev/null
+++ b/gui-clamp/ui-react/src/NotFound.test.js
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import NotFound from './NotFound';
+
+describe('Verify OnapClamp', () => {
+
+  it('Test the render method', () => {
+
+    const component = shallow(<NotFound/>)
+
+    expect(component).toMatchSnapshot();
+  });
+
+});
diff --git a/gui-clamp/ui-react/src/OnapClamp.js b/gui-clamp/ui-react/src/OnapClamp.js
new file mode 100644
index 0000000..b8615ac
--- /dev/null
+++ b/gui-clamp/ui-react/src/OnapClamp.js
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import LoopUI from './LoopUI'
+import { ThemeProvider } from 'styled-components';
+import { DefaultClampTheme } from './theme/globalStyle.js';
+
+export default class OnapClamp extends LoopUI {
+
+  render() {
+    console.info("Onap Clamp UI starting");
+    return (
+      <ThemeProvider theme={ DefaultClampTheme }>
+        { super.render() }
+      </ThemeProvider>);
+  }
+}
+
diff --git a/gui-clamp/ui-react/src/OnapClamp.test.js b/gui-clamp/ui-react/src/OnapClamp.test.js
new file mode 100644
index 0000000..c711a95
--- /dev/null
+++ b/gui-clamp/ui-react/src/OnapClamp.test.js
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import OnapClamp from './OnapClamp';
+
+describe('Verify OnapClamp', () => {
+
+  it('Test the render method', () => {
+
+    const component = shallow(<OnapClamp/>)
+
+    expect(component).toMatchSnapshot();
+  });
+
+});
diff --git a/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap b/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap
new file mode 100644
index 0000000..322c931
--- /dev/null
+++ b/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap
@@ -0,0 +1,188 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify LoopUI Test the render method 1`] = `
+<styled.div
+  id="main_div"
+>
+  <Memo(l) />
+  <Route
+    path="/viewLoopTemplatesModal"
+    render={[Function]}
+  />
+  <Route
+    path="/manageDictionaries"
+    render={[Function]}
+  />
+  <Route
+    path="/viewAllPolicies"
+    render={[Function]}
+  />
+  <Route
+    path="/policyModal/:policyInstanceType/:policyName"
+    render={[Function]}
+  />
+  <Route
+    path="/createLoop"
+    render={[Function]}
+  />
+  <Route
+    path="/openLoop"
+    render={[Function]}
+  />
+  <Route
+    path="/loopProperties"
+    render={[Function]}
+  />
+  <Route
+    path="/modifyLoop"
+    render={[Function]}
+  />
+  <Route
+    path="/userInfo"
+    render={[Function]}
+  />
+  <Route
+    path="/closeLoop"
+    render={[Function]}
+  />
+  <Route
+    path="/submit"
+    render={[Function]}
+  />
+  <Route
+    path="/stop"
+    render={[Function]}
+  />
+  <Route
+    path="/restart"
+    render={[Function]}
+  />
+  <Route
+    path="/delete"
+    render={[Function]}
+  />
+  <Route
+    path="/undeploy"
+    render={[Function]}
+  />
+  <Route
+    path="/deploy"
+    render={[Function]}
+  />
+  <Route
+    path="/refreshStatus"
+    render={[Function]}
+  />
+  <div />
+  <div>
+    <Alert
+      closeLabel="Close alert"
+      dismissible={true}
+      onClose={[Function]}
+      show={false}
+      transition={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "appear": false,
+            "in": false,
+            "mountOnEnter": false,
+            "timeout": 300,
+            "unmountOnExit": false,
+          },
+          "render": [Function],
+        }
+      }
+      variant="success"
+    />
+    <Alert
+      closeLabel="Close alert"
+      dismissible={true}
+      onClose={[Function]}
+      show={false}
+      transition={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "appear": false,
+            "in": false,
+            "mountOnEnter": false,
+            "timeout": 300,
+            "unmountOnExit": false,
+          },
+          "render": [Function],
+        }
+      }
+      variant="danger"
+    />
+  </div>
+  <Navbar
+    collapseOnSelect={false}
+    expand={true}
+    variant="light"
+  >
+    <NavbarBrand>
+      <img
+        alt=""
+        height="50px"
+        src={null}
+        width="234px"
+      />
+      <styled.a>
+        CLAMP
+      </styled.a>
+    </NavbarBrand>
+    <NavbarToggle
+      aria-controls="responsive-navbar-nav"
+      label="Toggle navigation"
+    />
+    <MenuBar
+      loopName="testLoopName"
+    />
+    <NavbarText>
+      <styled.a>
+        Signed in as: 
+      </styled.a>
+      <Styled(Link)
+        to="/userInfo"
+      >
+        testUser
+      </Styled(Link)>
+    </NavbarText>
+  </Navbar>
+  <styled.div>
+    <styled.div>
+      Loop Viewer - 
+      testLoopName
+       - (
+      )
+    </styled.div>
+    <styled.div>
+      <withRouter(SvgGenerator)
+        clickable={true}
+        generatedFrom="INSTANCE"
+        isBusyLoading={[Function]}
+        loopCache={
+          LoopCache {
+            "loopJsonCache": Object {},
+          }
+        }
+      />
+      <LoopStatus
+        loopCache={
+          LoopCache {
+            "loopJsonCache": Object {},
+          }
+        }
+      />
+      <LoopLogs
+        loopCache={
+          LoopCache {
+            "loopJsonCache": Object {},
+          }
+        }
+      />
+    </styled.div>
+  </styled.div>
+</styled.div>
+`;
diff --git a/gui-clamp/ui-react/src/__snapshots__/NotFound.test.js.snap b/gui-clamp/ui-react/src/__snapshots__/NotFound.test.js.snap
new file mode 100644
index 0000000..86bcfd1
--- /dev/null
+++ b/gui-clamp/ui-react/src/__snapshots__/NotFound.test.js.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify OnapClamp Test the render method 1`] = `
+<div
+  id="main"
+>
+  <div
+    class="divRow"
+  >
+    <b>
+      Page Not Found!
+    </b>
+  </div>
+  <div
+    class="divRow"
+  >
+    Please cick 
+    <a
+      href="/"
+    >
+      here
+    </a>
+     to go back to the main page.
+  </div>
+</div>
+`;
diff --git a/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap b/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap
new file mode 100644
index 0000000..353bc11
--- /dev/null
+++ b/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap
@@ -0,0 +1,216 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify OnapClamp Test the render method 1`] = `
+<Component
+  theme={
+    Object {
+      "backgroundColor": "#eeeeee",
+      "fontDanger": "#eb238e",
+      "fontDark": "#888888",
+      "fontFamily": "Arial, Sans-serif",
+      "fontHighlight": "#ffff00",
+      "fontLight": "#ffffff",
+      "fontNormal": "black",
+      "fontSize": "16px",
+      "fontWarning": "#eb238e",
+      "loopLogsHeaderBackgroundColor": "white",
+      "loopLogsHeaderFontColor": "black",
+      "loopViewerBackgroundColor": "white",
+      "loopViewerFontColor": "yellow",
+      "loopViewerHeaderBackgroundColor": "#337ab7",
+      "loopViewerHeaderFontColor": "white",
+      "menuBackgroundColor": "white",
+      "menuFontColor": "black",
+      "menuHighlightedBackgroundColor": "#337ab7",
+      "menuHighlightedFontColor": "white",
+      "policyEditorBackgroundColor": "white",
+      "policyEditorFontSize": "13px",
+      "toscaTextareaBackgroundColor": "white",
+      "toscaTextareaFontSize": "13px",
+    }
+  }
+>
+  <styled.div
+    id="main_div"
+  >
+    <Memo(l) />
+    <Route
+      path="/viewLoopTemplatesModal"
+      render={[Function]}
+    />
+    <Route
+      path="/manageDictionaries"
+      render={[Function]}
+    />
+    <Route
+      path="/viewAllPolicies"
+      render={[Function]}
+    />
+    <Route
+      path="/policyModal/:policyInstanceType/:policyName"
+      render={[Function]}
+    />
+    <Route
+      path="/createLoop"
+      render={[Function]}
+    />
+    <Route
+      path="/openLoop"
+      render={[Function]}
+    />
+    <Route
+      path="/loopProperties"
+      render={[Function]}
+    />
+    <Route
+      path="/modifyLoop"
+      render={[Function]}
+    />
+    <Route
+      path="/userInfo"
+      render={[Function]}
+    />
+    <Route
+      path="/closeLoop"
+      render={[Function]}
+    />
+    <Route
+      path="/submit"
+      render={[Function]}
+    />
+    <Route
+      path="/stop"
+      render={[Function]}
+    />
+    <Route
+      path="/restart"
+      render={[Function]}
+    />
+    <Route
+      path="/delete"
+      render={[Function]}
+    />
+    <Route
+      path="/undeploy"
+      render={[Function]}
+    />
+    <Route
+      path="/deploy"
+      render={[Function]}
+    />
+    <Route
+      path="/refreshStatus"
+      render={[Function]}
+    />
+    <div />
+    <div>
+      <Alert
+        closeLabel="Close alert"
+        dismissible={true}
+        onClose={[Function]}
+        show={false}
+        transition={
+          Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "defaultProps": Object {
+              "appear": false,
+              "in": false,
+              "mountOnEnter": false,
+              "timeout": 300,
+              "unmountOnExit": false,
+            },
+            "render": [Function],
+          }
+        }
+        variant="success"
+      />
+      <Alert
+        closeLabel="Close alert"
+        dismissible={true}
+        onClose={[Function]}
+        show={false}
+        transition={
+          Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "defaultProps": Object {
+              "appear": false,
+              "in": false,
+              "mountOnEnter": false,
+              "timeout": 300,
+              "unmountOnExit": false,
+            },
+            "render": [Function],
+          }
+        }
+        variant="danger"
+      />
+    </div>
+    <Navbar
+      collapseOnSelect={false}
+      expand={true}
+      variant="light"
+    >
+      <NavbarBrand>
+        <img
+          alt=""
+          height="50px"
+          src={null}
+          width="234px"
+        />
+        <styled.a>
+          CLAMP
+        </styled.a>
+      </NavbarBrand>
+      <NavbarToggle
+        aria-controls="responsive-navbar-nav"
+        label="Toggle navigation"
+      />
+      <MenuBar
+        loopName="Empty (NO loop loaded yet)"
+      />
+      <NavbarText>
+        <styled.a>
+          Signed in as: 
+        </styled.a>
+        <Styled(Link)
+          to="/userInfo"
+        />
+      </NavbarText>
+    </Navbar>
+    <styled.div>
+      <styled.div>
+        Loop Viewer - 
+        Empty (NO loop loaded yet)
+         - (
+        )
+      </styled.div>
+      <styled.div>
+        <withRouter(SvgGenerator)
+          clickable={true}
+          generatedFrom="INSTANCE"
+          isBusyLoading={[Function]}
+          loopCache={
+            LoopCache {
+              "loopJsonCache": Object {},
+            }
+          }
+        />
+        <LoopStatus
+          loopCache={
+            LoopCache {
+              "loopJsonCache": Object {},
+            }
+          }
+        />
+        <LoopLogs
+          loopCache={
+            LoopCache {
+              "loopJsonCache": Object {},
+            }
+          }
+        />
+      </styled.div>
+    </styled.div>
+  </styled.div>
+</Component>
+`;
diff --git a/gui-clamp/ui-react/src/api/LoopActionService.js b/gui-clamp/ui-react/src/api/LoopActionService.js
new file mode 100644
index 0000000..f3695c2
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/LoopActionService.js
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class LoopActionService {
+
+  static performAction(cl_name, uiAction) {
+    console.info("LoopActionService perform action: " + uiAction + " closedloopName=" + cl_name);
+    const svcAction = uiAction.toLowerCase();
+    return fetch(window.location.pathname + "restservices/clds/v2/loop/" + svcAction + "/" + cl_name, {
+      method: 'PUT',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        if (response.ok) {
+          return response.json();
+        } else {
+          return Promise.reject("Perform action failed with code:" + response.status);
+        }
+      })
+      .then(function (data) {
+        console.info("Action Successful: " + uiAction);
+        return data;
+      })
+      .catch(function (error) {
+        console.info("Action Failure: " + uiAction);
+        return Promise.reject(error);
+      });
+  }
+
+
+  static refreshStatus(cl_name) {
+    console.info("Refresh the status for closedloopName=" + cl_name);
+
+    return fetch(window.location.pathname + "restservices/clds/v2/loop/getstatus/" + cl_name, {
+      method: 'GET',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        if (response.ok) {
+          return response.json();
+        } else {
+          return Promise.reject("Refresh status failed with code:" + response.status);
+        }
+      })
+      .then(function (data) {
+        console.info("Refresh status Successful");
+        return data;
+      })
+      .catch(function (error) {
+        console.info("Refresh status failed:", error);
+        return Promise.reject(error);
+      });
+  }
+}
diff --git a/gui-clamp/ui-react/src/api/LoopCache.js b/gui-clamp/ui-react/src/api/LoopCache.js
new file mode 100644
index 0000000..ac2da07
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/LoopCache.js
@@ -0,0 +1,252 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class LoopCache {
+  loopJsonCache;
+
+  constructor(loopJson) {
+    this.loopJsonCache = loopJson;
+  }
+
+  updateMicroServiceProperties(name, newMsProperties) {
+    for (var policy in this.loopJsonCache["microServicePolicies"]) {
+      if (this.loopJsonCache["microServicePolicies"][policy]["name"] === name) {
+        this.loopJsonCache["microServicePolicies"][policy]["configurationsJson"] = newMsProperties;
+      }
+    }
+  }
+
+  updateMicroServicePdpGroup(name, pdpGroup, pdpSubgroup) {
+    for (var policy in this.loopJsonCache["microServicePolicies"]) {
+      if (this.loopJsonCache["microServicePolicies"][policy]["name"] === name) {
+        this.loopJsonCache["microServicePolicies"][policy]["pdpGroup"] = pdpGroup;
+        this.loopJsonCache["microServicePolicies"][policy]["pdpSubgroup"] = pdpSubgroup;
+      }
+    }
+  }
+
+  updateGlobalProperties(newGlobalProperties) {
+    this.loopJsonCache["globalPropertiesJson"] = newGlobalProperties;
+  }
+
+  updateOperationalPolicyProperties(name, newOpProperties) {
+    for (var policy in this.loopJsonCache["operationalPolicies"]) {
+      if (this.loopJsonCache["operationalPolicies"][policy]["name"] === name) {
+        this.loopJsonCache["operationalPolicies"][policy]["configurationsJson"] = newOpProperties;
+      }
+    }
+  }
+
+  updateOperationalPolicyPdpGroup(name, pdpGroup, pdpSubgroup) {
+    for (var policy in this.loopJsonCache["operationalPolicies"]) {
+      if (this.loopJsonCache["operationalPolicies"][policy]["name"] === name) {
+        this.loopJsonCache["operationalPolicies"][policy]["pdpGroup"] = pdpGroup;
+        this.loopJsonCache["operationalPolicies"][policy]["pdpSubgroup"] = pdpSubgroup;
+      }
+    }
+  }
+
+  getLoopName() {
+    return this.loopJsonCache["name"];
+  }
+
+  getOperationalPolicyJsonSchema() {
+    return this.loopJsonCache["operationalPolicies"]["0"]["jsonRepresentation"];
+  }
+
+  getOperationalPolicies() {
+    return this.loopJsonCache["operationalPolicies"];
+  }
+
+  getOperationalPoliciesNoJsonSchema() {
+    var operationalPolicies = JSON.parse(JSON.stringify(this.loopJsonCache["operationalPolicies"]));
+    delete operationalPolicies[0]["jsonRepresentation"];
+    return operationalPolicies;
+  }
+
+  getGlobalProperties() {
+    return this.loopJsonCache["globalPropertiesJson"];
+  }
+
+  getDcaeDeploymentProperties() {
+    return this.loopJsonCache["globalPropertiesJson"]["dcaeDeployParameters"];
+  }
+
+  getMicroServicePolicies() {
+    return this.loopJsonCache["microServicePolicies"];
+  }
+
+  getOperationalPolicyForName(name) {
+    var opProperties = this.getOperationalPolicies();
+    for (var policy in opProperties) {
+      if (opProperties[policy]["name"] === name) {
+        return opProperties[policy];
+      }
+    }
+    return null;
+  }
+
+  getOperationalPolicyPropertiesForName(name) {
+    var opConfig = this.getOperationalPolicyForName(name);
+    if (opConfig !== null) {
+      return opConfig["configurationsJson"];
+    }
+    return null;
+  }
+
+  getOperationalPolicyJsonRepresentationForName(name) {
+    var opConfig = this.getOperationalPolicyForName(name);
+    if (opConfig !== null) {
+      return opConfig["jsonRepresentation"];
+    }
+    return null;
+  }
+
+  getOperationalPolicySupportedPdpGroup(name) {
+    var opConfig = this.getOperationalPolicyForName(name);
+    if (opConfig !== null) {
+      if (opConfig["policyModel"]["policyPdpGroup"] !== undefined && opConfig["policyModel"]["policyPdpGroup"]["supportedPdpGroups"] !== undefined) {
+        return opConfig["policyModel"]["policyPdpGroup"]["supportedPdpGroups"];
+      }
+    }
+    return [];
+  }
+
+  getOperationalPolicyPdpGroup(name) {
+    var opConfig = this.getOperationalPolicyForName(name);
+    if (opConfig !== null) {
+      return opConfig["pdpGroup"];
+    }
+    return null;
+  }
+
+  getOperationalPolicyPdpSubgroup(name) {
+    var opConfig = this.getOperationalPolicyForName(name);
+    if (opConfig !== null) {
+      return opConfig["pdpSubgroup"];
+    }
+    return null;
+  }
+
+  getMicroServiceSupportedPdpGroup(name) {
+    var microService = this.getMicroServiceForName(name);
+    if (microService !== null) {
+      if (microService["policyModel"]["policyPdpGroup"] !== undefined && microService["policyModel"]["policyPdpGroup"]["supportedPdpGroups"] !== undefined) {
+        return microService["policyModel"]["policyPdpGroup"]["supportedPdpGroups"];
+      }
+    }
+    return [];
+  }
+
+  getMicroServicePdpGroup(name) {
+    var microService = this.getMicroServiceForName(name);
+    if (microService !== null) {
+      return microService["pdpGroup"];
+    }
+    return null;
+  }
+
+  getMicroServicePdpSubgroup(name) {
+    var microService = this.getMicroServiceForName(name);
+    if (microService !== null) {
+      return microService["pdpSubgroup"];
+    }
+    return null;
+  }
+
+  getMicroServiceForName(name) {
+    var msProperties = this.getMicroServicePolicies();
+    for (var policy in msProperties) {
+      if (msProperties[policy]["name"] === name) {
+        return msProperties[policy];
+      }
+    }
+    return null;
+  }
+
+  getMicroServicePropertiesForName(name) {
+    var msConfig = this.getMicroServiceForName(name);
+    if (msConfig !== null) {
+      return msConfig["configurationsJson"];
+    }
+    return null;
+  }
+
+  getMicroServiceJsonRepresentationForName(name) {
+    var msConfig = this.getMicroServiceForName(name);
+    if (msConfig !== null) {
+      return msConfig["jsonRepresentation"];
+    }
+    return null;
+  }
+
+  getResourceDetailsVfProperty() {
+    return this.loopJsonCache["modelService"]["resourceDetails"]["VF"];
+  }
+
+  getResourceDetailsVfModuleProperty() {
+    return this.loopJsonCache["modelService"]["resourceDetails"]["VFModule"];
+  }
+
+  getLoopLogsArray() {
+    return this.loopJsonCache.loopLogs;
+  }
+
+  getComputedState() {
+    return this.loopJsonCache.lastComputedState;
+  }
+
+  getComponentStates() {
+    return this.loopJsonCache.components;
+  }
+
+  getTemplateName() {
+    if (this.getLoopTemplate() !== undefined) {
+      return this.getLoopTemplate().name;
+    }
+    return null;
+  }
+
+  getLoopTemplate() {
+    return this.loopJsonCache["loopTemplate"];
+  }
+
+  isOpenLoopTemplate() {
+    var loopTemplate = this.getLoopTemplate();
+    if (loopTemplate != null && loopTemplate["allowedLoopType"] === "OPEN") {
+      return true;
+    }
+    return false;
+  }
+
+  getAllLoopElementModels() {
+    var loopTemplate = this.getLoopTemplate();
+    var loopElementModels = [];
+    if (loopTemplate != null) {
+      for (var element of loopTemplate['loopElementModelsUsed']) {
+        loopElementModels.push(element['loopElementModel'])
+      }
+    }
+    return loopElementModels;
+  }
+}
diff --git a/gui-clamp/ui-react/src/api/LoopCache.test.js b/gui-clamp/ui-react/src/api/LoopCache.test.js
new file mode 100644
index 0000000..76f819c
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/LoopCache.test.js
@@ -0,0 +1,305 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 LoopCache from '../api/LoopCache';
+
+const json = require('./LoopCacheMockFile.json');
+
+describe('Verify LoopCache functions', () => {
+    const loopCache = new LoopCache(json);
+		it('getLoopName', () => {
+      expect(loopCache.getLoopName()).toBe("LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca");
+		});
+
+    it('getOperationalPolicies', () => {
+      const opPolicy = [{
+        "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+        "configurationsJson": {
+          "operational_policy": {
+            "controlLoop": {},
+            "policies": []
+          }
+        },
+        "pdpGroup": "pdpGroupTest",
+        "pdpSubgroup": "pdpSubgroupTest",
+        "jsonRepresentation": {
+        "schema": {}
+      }
+      }];
+      expect(loopCache.getOperationalPolicies()).toStrictEqual(opPolicy);
+    });
+
+    it('getOperationalPoliciesNoJsonSchema', () => {
+      const opPolicy = [{
+        "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+        "configurationsJson": {
+          "operational_policy": {
+            "controlLoop": {},
+            "policies": []
+          }
+        },
+        "pdpGroup": "pdpGroupTest",
+        "pdpSubgroup": "pdpSubgroupTest",
+      }];
+      expect(loopCache.getOperationalPoliciesNoJsonSchema()).toStrictEqual(opPolicy);
+    });
+
+    it('getOperationalPolicyJsonSchema', () => {
+      const jsonSchema = {
+          "schema": {}
+      };
+
+      expect(loopCache.getOperationalPolicyJsonSchema()).toStrictEqual(jsonSchema);
+    });
+    it('getGlobalProperties', () => {
+      const globelProp = {
+        "dcaeDeployParameters": {
+          "location_id": "",
+          "service_id": "",
+          "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+        }
+      };
+      expect(loopCache.getGlobalProperties()).toStrictEqual(globelProp);
+    });
+
+    it('getDcaeDeploymentProperties', () => {
+      const deploymentProp = {
+          "location_id": "",
+          "service_id": "",
+          "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+      };
+      expect(loopCache.getDcaeDeploymentProperties()).toStrictEqual(deploymentProp);
+    });
+
+    it('getMicroServiceForName', () => {
+      const msJson = {
+          "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+          "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+          "configurationsJson": {"domain": "measurementsForVfScaling"},
+          "shared": false,
+          "pdpGroup": "pdpGroupTest",
+          "pdpSubgroup": "pdpSubgroupTest",
+          "policyModel": {"policyPdpGroup": {"supportedPdpGroups": "supportedPdpGroupsTest"}},
+          "jsonRepresentation": {"schema": {}}
+      };
+      expect(loopCache.getMicroServiceForName("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJson);
+      expect(loopCache.getMicroServiceForName("TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2")).toBeNull();
+    });
+
+    it('getMicroServicePropertiesForName', () => {
+      const msProp = {"domain": "measurementsForVfScaling"};
+      expect(loopCache.getMicroServicePropertiesForName("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msProp);
+      expect(loopCache.getMicroServicePropertiesForName("TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2")).toBeNull();
+    });
+
+    it('getMicroServiceJsonRepresentationForName', () => {
+      const msJsonRepresentation = {"schema": {}};
+      expect(loopCache.getMicroServiceJsonRepresentationForName("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJsonRepresentation);
+    });
+
+    it('getResourceDetailsVfProperty', () => {
+      const resourceVF = {
+        "vLoadBalancerMS 0": {
+          "resourceVendor": "Test",
+          "resourceVendorModelNumber": "",
+          "name": "vLoadBalancerMS",
+          "description": "vLBMS",
+          "invariantUUID": "1a31b9f2-e50d-43b7-89b3-a040250cf506",
+          "subcategory": "Load Balancer",
+          "category": "Application L4+",
+          "type": "VF",
+          "UUID": "b4c4f3d7-929e-4b6d-a1cd-57e952ddc3e6",
+          "version": "1.0",
+          "resourceVendorRelease": "1.0",
+          "customizationUUID": "465246dc-7748-45f4-a013-308d92922552"
+        }
+      };
+      expect(loopCache.getResourceDetailsVfProperty()).toStrictEqual(resourceVF);
+    });
+
+    it('getResourceDetailsVfModuleProperty', () => {
+      const vfModule = {
+        "Vloadbalancerms..vpkg..module-1": {
+          "vfModuleModelInvariantUUID": "ca052563-eb92-4b5b-ad41-9111768ce043",
+          "vfModuleModelVersion": "1",
+          "vfModuleModelName": "Vloadbalancerms..vpkg..module-1",
+          "vfModuleModelUUID": "1e725ccc-b823-4f67-82b9-4f4367070dbc",
+          "vfModuleModelCustomizationUUID": "1bffdc31-a37d-4dee-b65c-dde623a76e52",
+          "min_vf_module_instances": 0,
+          "vf_module_label": "vpkg",
+          "max_vf_module_instances": 1,
+          "vf_module_type": "Expansion",
+          "isBase": false,
+          "initial_count": 0,
+          "volume_group": false
+        }
+      };
+      expect(loopCache.getResourceDetailsVfModuleProperty()).toStrictEqual(vfModule);
+    });
+
+    it('getLoopLogsArray', () => {
+      const logs = [
+        {
+          "id": 1,
+          "logType": "INFO",
+          "logComponent": "CLAMP",
+          "message": "Operational policies UPDATED",
+          "logInstant": "2019-07-08T09:44:37Z"
+        }
+      ];
+      expect(loopCache.getLoopLogsArray()).toStrictEqual(logs);
+    });
+
+    it('getComponentStates', () => {
+      const component = {
+        "POLICY": {
+          "componentState": {
+            "stateName": "NOT_SENT",
+            "description": "The policies defined have NOT yet been created on the policy engine"
+          }
+        },
+        "DCAE": {
+          "componentState": {
+            "stateName": "BLUEPRINT_DEPLOYED",
+            "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+          }
+        }
+      };
+      expect(loopCache.getComponentStates()).toStrictEqual(component);
+    });
+
+    it('getOperationalPolicyForName', () => {
+      const opPolicy = {
+      "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+      "configurationsJson": {
+        "operational_policy": {
+          "controlLoop": {},
+          "policies": []
+        }
+      },
+      "pdpGroup": "pdpGroupTest",
+      "pdpSubgroup": "pdpSubgroupTest",
+      "jsonRepresentation": {
+        "schema": {}
+      }
+    };
+      expect(loopCache.getOperationalPolicyForName("OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(opPolicy);
+      expect(loopCache.getOperationalPolicyForName("Not_Exist")).toBeNull();
+    });
+
+    it('getOperationalPolicyPropertiesForName', () => {
+      const opPolicyJson = {
+        "operational_policy": {
+          "controlLoop": {},
+          "policies": []
+        }};
+      expect(loopCache.getOperationalPolicyPropertiesForName("OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(opPolicyJson);
+      expect(loopCache.getOperationalPolicyPropertiesForName("Not_Exist")).toBeNull();
+    });
+
+    it('getOperationalPolicyJsonRepresentationForName', () => {
+      const opPolicySchema = {
+        "schema": {}
+      };
+      expect(loopCache.getOperationalPolicyJsonRepresentationForName("OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(opPolicySchema);
+      expect(loopCache.getOperationalPolicyJsonRepresentationForName("Not_Exist")).toBeNull();
+    });
+
+    it('getOperationalPolicySupportedPdpGroup', () => {
+      expect(loopCache.getOperationalPolicySupportedPdpGroup("Not_Exist")).toStrictEqual([]);
+    });
+
+    it('getOperationalPolicyPdpGroup', () => {
+      expect(loopCache.getOperationalPolicyPdpGroup("OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual("pdpGroupTest");
+      expect(loopCache.getOperationalPolicyPdpGroup("Not_Exist")).toBeNull();
+    });
+
+    it('getOperationalPolicyPdpSubgroup', () => {
+      expect(loopCache.getOperationalPolicyPdpSubgroup("OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual("pdpSubgroupTest");
+      expect(loopCache.getOperationalPolicyPdpSubgroup("Not_Exist")).toBeNull();
+    });
+
+    it('getMicroServiceSupportedPdpGroup', () => {
+      expect(loopCache.getMicroServiceSupportedPdpGroup("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual("supportedPdpGroupsTest");
+      expect(loopCache.getMicroServiceSupportedPdpGroup("Not_Exist")).toStrictEqual([]);
+    });
+
+    it('getMicroServicePdpGroup', () => {
+      expect(loopCache.getMicroServicePdpGroup("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual("pdpGroupTest");
+      expect(loopCache.getMicroServicePdpGroup("Not_Exist")).toBeNull();
+    });
+
+    it('getMicroServicePdpSubgroup', () => {
+      expect(loopCache.getMicroServicePdpSubgroup("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual("pdpSubgroupTest");
+      expect(loopCache.getMicroServicePdpSubgroup("Not_Exist")).toBeNull();
+    });
+
+    it('getMicroServiceJsonRepresentationForName', () => {
+      const msPolicySchema = {
+        "schema": {}
+      };
+      expect(loopCache.getMicroServiceJsonRepresentationForName("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msPolicySchema);
+      expect(loopCache.getMicroServiceJsonRepresentationForName("Not_Exist")).toBeNull();
+    });
+
+    it('getTemplateName', () => {
+      expect(loopCache.getTemplateName()).toStrictEqual("loopTemplateTest");
+    });
+
+    it('updateGlobalProperties', () => {
+      const newGlobalProps = {
+        "dcaeDeployParameters": {
+          "location_id": "newLocation",
+          "service_id": "newServiceId",
+          "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2"
+        }
+      };
+      loopCache.updateGlobalProperties(newGlobalProps);
+      expect(loopCache.getGlobalProperties()).toStrictEqual(newGlobalProps);
+    });
+
+    it('updateOperationalPolicyProperties', () => {
+      const newOpPolicy = {
+          "operational_policy": {
+            "controlLoop": {},
+            "policies": []
+          }
+        };
+      loopCache.updateOperationalPolicyProperties("OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",newOpPolicy);
+      expect(loopCache.getOperationalPolicyPropertiesForName("OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(newOpPolicy);
+    });
+
+    it('updateMicroServiceProperties', () => {
+      const newMsPolicyProperties = {"domain": "measurementsForVfScalingNew"};
+      loopCache.updateMicroServiceProperties("TCA_h2NMX_v1_0_ResourceInstanceName1_tca", newMsPolicyProperties);
+      expect(loopCache.getMicroServicePropertiesForName("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(newMsPolicyProperties);
+    });
+
+    it('updateMicroServicePdpGroup', () => {
+      const newMsPolicyProperties = {"domain": "measurementsForVfScalingNew"};
+      loopCache.updateMicroServicePdpGroup("TCA_h2NMX_v1_0_ResourceInstanceName1_tca", "pdpGroupTest1", "pdpSubgroupTest1");
+      expect(loopCache.getMicroServicePdpGroup("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual("pdpGroupTest1");
+      expect(loopCache.getMicroServicePdpGroup("Not_Exist")).toBeNull();
+      expect(loopCache.getMicroServicePdpSubgroup("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual("pdpSubgroupTest1");
+      expect(loopCache.getMicroServicePdpSubgroup("Not_Exist")).toBeNull();
+    });
+ });
diff --git a/gui-clamp/ui-react/src/api/LoopCacheMockFile.json b/gui-clamp/ui-react/src/api/LoopCacheMockFile.json
new file mode 100644
index 0000000..e5a6702
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/LoopCacheMockFile.json
@@ -0,0 +1,135 @@
+{
+  "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+  "dcaeBlueprintId": "typeId-3a942643-a8f7-4e54-b2c1-eea8daba2b17",
+  "globalPropertiesJson": {
+    "dcaeDeployParameters": {
+      "location_id": "",
+      "service_id": "",
+      "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+    }
+  },
+  "loopTemplate": {
+    "name": "loopTemplateTest"
+  },
+  "modelService": {
+    "serviceDetails": {
+      "serviceType": "",
+      "namingPolicy": "",
+      "environmentContext": "General_Revenue-Bearing",
+      "serviceEcompNaming": "true",
+      "serviceRole": "",
+      "name": "vLoadBalancerMS",
+      "description": "vLBMS",
+      "invariantUUID": "30ec5b59-4799-48d8-ac5f-1058a6b0e48f",
+      "ecompGeneratedNaming": "true",
+      "category": "Network L4+",
+      "type": "Service",
+      "UUID": "63cac700-ab9a-4115-a74f-7eac85e3fce0",
+      "instantiationType": "A-la-carte"
+    },
+    "resourceDetails": {
+      "CP": {},
+      "VL": {},
+      "VF": {
+        "vLoadBalancerMS 0": {
+          "resourceVendor": "Test",
+          "resourceVendorModelNumber": "",
+          "name": "vLoadBalancerMS",
+          "description": "vLBMS",
+          "invariantUUID": "1a31b9f2-e50d-43b7-89b3-a040250cf506",
+          "subcategory": "Load Balancer",
+          "category": "Application L4+",
+          "type": "VF",
+          "UUID": "b4c4f3d7-929e-4b6d-a1cd-57e952ddc3e6",
+          "version": "1.0",
+          "resourceVendorRelease": "1.0",
+          "customizationUUID": "465246dc-7748-45f4-a013-308d92922552"
+        }
+      },
+      "CR": {},
+      "VFC": {},
+      "PNF": {},
+      "Service": {},
+      "CVFC": {},
+      "Service Proxy": {},
+      "Configuration": {},
+      "AllottedResource": {},
+      "VFModule": {
+        "Vloadbalancerms..vpkg..module-1": {
+          "vfModuleModelInvariantUUID": "ca052563-eb92-4b5b-ad41-9111768ce043",
+          "vfModuleModelVersion": "1",
+          "vfModuleModelName": "Vloadbalancerms..vpkg..module-1",
+          "vfModuleModelUUID": "1e725ccc-b823-4f67-82b9-4f4367070dbc",
+          "vfModuleModelCustomizationUUID": "1bffdc31-a37d-4dee-b65c-dde623a76e52",
+          "min_vf_module_instances": 0,
+          "vf_module_label": "vpkg",
+          "max_vf_module_instances": 1,
+          "vf_module_type": "Expansion",
+          "isBase": false,
+          "initial_count": 0,
+          "volume_group": false
+        }
+      }
+    }
+  },
+  "lastComputedState": "DESIGN",
+  "components": {
+    "POLICY": {
+      "componentState": {
+        "stateName": "NOT_SENT",
+        "description": "The policies defined have NOT yet been created on the policy engine"
+      }
+    },
+    "DCAE": {
+      "componentState": {
+        "stateName": "BLUEPRINT_DEPLOYED",
+        "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+      }
+    }
+  },
+  "operationalPolicies": [
+    {
+      "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+      "configurationsJson": {
+        "operational_policy": {
+          "controlLoop": {},
+          "policies": []
+        }
+      },
+      "pdpGroup": "pdpGroupTest",
+      "pdpSubgroup": "pdpSubgroupTest",
+      "jsonRepresentation": {
+        "schema": {}
+      }
+    }
+  ],
+  "microServicePolicies": [
+    {
+      "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+      "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+      "configurationsJson": {
+        "domain": "measurementsForVfScaling"
+      },
+      "shared": false,
+      "pdpGroup": "pdpGroupTest",
+      "pdpSubgroup": "pdpSubgroupTest",
+      "policyModel": {
+        "policyPdpGroup": {
+          "supportedPdpGroups": "supportedPdpGroupsTest"
+        }
+      },
+      "jsonRepresentation": {
+        "schema": {}
+      }
+    }
+  ],
+  "loopLogs": [
+    {
+      "id": 1,
+      "logType": "INFO",
+      "logComponent": "CLAMP",
+      "message": "Operational policies UPDATED",
+      "logInstant": "2019-07-08T09:44:37Z"
+    }
+  ]
+}
diff --git a/gui-clamp/ui-react/src/api/LoopService.js b/gui-clamp/ui-react/src/api/LoopService.js
new file mode 100644
index 0000000..16f1bec
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/LoopService.js
@@ -0,0 +1,244 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class LoopService {
+  static getLoopNames() {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/getAllNames', { method: 'GET', credentials: 'same-origin' })
+      .then(function (response) {
+        console.debug("GetLoopNames response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("GetLoopNames query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("GetLoopNames error received", error);
+        return {};
+      });
+  }
+
+  static createLoop(loopName, templateName) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/create/' + loopName + '?templateName=' + templateName, {
+      method: 'POST',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("CreateLoop response received: ", response.status);
+        return response.json();
+      })
+      .catch(function (error) {
+        console.error("CreateLoop error received", error);
+        return "";
+      });
+  }
+
+  static getLoop(loopName) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/' + loopName, {
+      method: 'GET',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("GetLoop response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("GetLoop query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("GetLoop error received", error);
+        return {};
+      });
+  }
+
+  static setMicroServiceProperties(loopName, jsonData) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/updateMicroservicePolicy/' + loopName, {
+      method: 'POST',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      body: JSON.stringify(jsonData)
+    })
+      .then(function (response) {
+        console.debug("updateMicroservicePolicy response received: ", response.status);
+        if (response.ok) {
+          return response.text();
+        } else {
+          console.error("updateMicroservicePolicy query failed");
+          return "";
+        }
+      })
+      .catch(function (error) {
+        console.error("updateMicroservicePolicy error received", error);
+        return "";
+      });
+  }
+
+  static setOperationalPolicyProperties(loopName, jsonData) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/updateOperationalPolicies/' + loopName, {
+      method: 'POST',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      body: JSON.stringify(jsonData)
+    })
+      .then(function (response) {
+        console.debug("updateOperationalPolicies response received: ", response.status);
+        if (response.ok) {
+          return response.text();
+        } else {
+          console.error("updateOperationalPolicies query failed");
+          return "";
+        }
+      })
+      .catch(function (error) {
+        console.error("updateOperationalPolicies error received", error);
+        return "";
+      });
+  }
+
+  static updateGlobalProperties(loopName, jsonData) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/updateGlobalProperties/' + loopName, {
+      method: 'POST',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      body: JSON.stringify(jsonData)
+    })
+      .then(function (response) {
+        console.debug("updateGlobalProperties response received: ", response.status);
+        if (response.ok) {
+          return response.text();
+        } else {
+          console.error("updateGlobalProperties query failed");
+          return "";
+        }
+      })
+      .catch(function (error) {
+        console.error("updateGlobalProperties error received", error);
+        return "";
+      });
+  }
+
+  static refreshOperationalPolicyJson(loopName, operationalPolicyName) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/refreshOperationalPolicyJsonSchema/' + loopName + '/' + operationalPolicyName, {
+      method: 'PUT',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("Refresh Operational Policy Json Schema response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("Refresh Operational Policy Json Schema query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("Refresh Operational Policy Json Schema error received", error);
+        return {};
+      });
+  }
+
+  static refreshMicroServicePolicyJson(loopName, microServicePolicyName) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/refreshMicroServicePolicyJsonSchema/' + loopName + '/' + microServicePolicyName, {
+      method: 'PUT',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("Refresh Operational Policy Json Schema response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("Refresh Operational Policy Json Schema query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("Refresh Operational Policy Json Schema error received", error);
+        return {};
+      });
+  }
+
+  static addOperationalPolicyType(loopName, policyType, policyVersion) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/addOperationaPolicy/' + loopName + '/policyModel/' + policyType + '/' + policyVersion, {
+      method: 'PUT',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("Add Operational Policy response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          return response.text();
+        }
+      })
+      .catch(function (error) {
+        console.error("Add Operational Policy query failed");
+        throw new Error(error);
+      })
+  }
+
+  static removeOperationalPolicyType(loopName, policyType, policyVersion, policyName) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/removeOperationaPolicy/' + loopName + '/policyModel/' + policyType + '/' + policyVersion + '/' + policyName, {
+      method: 'PUT',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("Remove Operational Policy response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("Remove Operational Policy query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("Remove Operational Policy error received", error);
+        return {};
+      });
+  }
+}
diff --git a/gui-clamp/ui-react/src/api/PoliciesListCache.js b/gui-clamp/ui-react/src/api/PoliciesListCache.js
new file mode 100644
index 0000000..265dab4
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/PoliciesListCache.js
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class PoliciesListCache {
+  policiesJsonCache;
+
+  constructor(policiesJson) {
+    this.policiesJsonCache = policiesJson;
+  }
+
+  getAllPolicies() {
+    return this.policiesJsonCache["policies"];
+  }
+}
diff --git a/gui-clamp/ui-react/src/api/PoliciesListCacheMockFile.json b/gui-clamp/ui-react/src/api/PoliciesListCacheMockFile.json
new file mode 100644
index 0000000..14d5cc6
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/PoliciesListCacheMockFile.json
@@ -0,0 +1,215 @@
+{
+  "policies": [
+    {
+      "MICROSERVICE_vLoadBalancerMS_v1_0_tcagen2_1_0_0_AV0": {
+        "type": "onap.policies.monitoring.tcagen2",
+        "type_version": "1.0.0",
+        "properties": {
+          "tca.policy": {
+            "domain": "measurementsForVfScaling",
+            "metricsPerEventName": [
+              {
+                "policyScope": "DCAE",
+                "thresholds": [
+                  {
+                    "version": "1.0.2",
+                    "severity": "MAJOR",
+                    "thresholdValue": 200,
+                    "closedLoopEventStatus": "ONSET",
+                    "closedLoopControlName": "LOOP_test",
+                    "direction": "LESS_OR_EQUAL",
+                    "fieldPath": "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedTotalPacketsDelta"
+                  }
+                ],
+                "eventName": "vLoadBalancer",
+                "policyVersion": "v0.0.1",
+                "controlLoopSchemaType": "VM",
+                "policyName": "DCAE.Config_tca-hi-lo"
+              }
+            ]
+          }
+        },
+        "name": "MICROSERVICE_vLoadBalancerMS_v1_0_tcagen2_1_0_0_AV0",
+        "version": "1.0.0",
+        "metadata": {
+          "policy-id": "MICROSERVICE_vLoadBalancerMS_v1_0_tcagen2_1_0_0_AV0",
+          "policy-version": "1.0.0"
+        },
+        "pdpGroupInfo": {
+          "pdpGroup": "pdpGroup2",
+          "pdpSubGroup": "subGroup2"
+        },
+        "supportedPdpGroups": [
+          {
+            "pdpGroup2": [
+              "subGroup2",
+              "subGroup3"
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "OPERATIONAL_vLoadBalancerMS_v1_0_Drools_1_0_0_7xd": {
+        "type": "onap.policies.controlloop.operational.common.Drools",
+        "type_version": "1.0.0",
+        "properties": {
+          "abatement": false,
+          "operations": [
+            {
+              "failure_retries": "final_failure_retries",
+              "id": "test1",
+              "failure_timeout": "final_failure_timeout",
+              "failure": "final_failure",
+              "operation": {
+                "payload": {
+                  "artifact_name": "baseconfiguration",
+                  "artifact_version": "1.0.0",
+                  "mode": "async",
+                  "data": "{\"resource-assignment-properties\":{\"request-id\":\"\",\"service-instance-id\":\"\",\"hostname\":\"\",\"request-info\":{\"prop1\":\"\",\"prop2\":\"\"}}}"
+                },
+                "target": {
+                  "entityIds": {
+                    "resourceID": "Vloadbalancerms..vdns..module-3",
+                    "modelInvariantId": "4c10ba9b-f88f-415e-9de3-5d33336047fa",
+                    "modelVersionId": "4fa73b49-8a6c-493e-816b-eb401567b720",
+                    "modelName": "Vloadbalancerms..vdns..module-3",
+                    "modelVersion": "1",
+                    "modelCustomizationId": "bafcdab0-801d-4d81-9ead-f464640a38b1"
+                  },
+                  "targetType": "VNF"
+                },
+                "actor": "SDNR",
+                "operation": "BandwidthOnDemand"
+              },
+              "failure_guard": "final_failure_guard",
+              "retries": 0,
+              "timeout": 0,
+              "failure_exception": "final_failure_exception",
+              "description": "test",
+              "success": "final_success"
+            }
+          ],
+          "trigger": "test1",
+          "timeout": 0,
+          "id": "LOOP_test"
+        },
+        "name": "OPERATIONAL_vLoadBalancerMS_v1_0_Drools_1_0_0_7xd",
+        "version": "1.0.0",
+        "metadata": {
+          "policy-id": "OPERATIONAL_vLoadBalancerMS_v1_0_Drools_1_0_0_7xd",
+          "policy-version": "1.0.0"
+        },
+        "pdpGroupInfo": {
+          "pdpGroup": "pdpGroup2",
+          "pdpSubGroup": "subGroup3"
+        },
+        "supportedPdpGroups": [
+          {
+            "pdpGroup2": [
+              "subGroup2",
+              "subGroup3"
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "SDNC_Policy.ONAP_NF_NAMING_TIMESTAMP": {
+        "type": "onap.policies.Naming",
+        "type_version": "1.0.0",
+        "properties": {
+          "naming-models": [
+            {
+              "naming-type": "VNF",
+              "naming-recipe": "AIC_CLOUD_REGION|DELIMITER|CONSTANT|DELIMITER|TIMESTAMP",
+              "name-operation": "to_lower_case()",
+              "naming-properties": [
+                {
+                  "property-name": "AIC_CLOUD_REGION"
+                },
+                {
+                  "property-name": "CONSTANT",
+                  "property-value": "onap-nf"
+                },
+                {
+                  "property-name": "TIMESTAMP"
+                },
+                {
+                  "property-value": "-",
+                  "property-name": "DELIMITER"
+                }
+              ]
+            },
+            {
+              "naming-type": "VNFC",
+              "naming-recipe": "VNF_NAME|DELIMITER|NFC_NAMING_CODE|DELIMITER|SEQUENCE",
+              "name-operation": "to_lower_case()",
+              "naming-properties": [
+                {
+                  "property-name": "VNF_NAME"
+                },
+                {
+                  "property-name": "SEQUENCE",
+                  "increment-sequence": {
+                    "max": "zzz",
+                    "scope": "ENTIRETY",
+                    "start-value": "1",
+                    "length": "3",
+                    "increment": "1",
+                    "sequence-type": "alpha-numeric"
+                  }
+                },
+                {
+                  "property-name": "NFC_NAMING_CODE"
+                },
+                {
+                  "property-value": "-",
+                  "property-name": "DELIMITER"
+                }
+              ]
+            },
+            {
+              "naming-type": "VF-MODULE",
+              "naming-recipe": "VNF_NAME|DELIMITER|VF_MODULE_LABEL|DELIMITER|VF_MODULE_TYPE|DELIMITER|SEQUENCE",
+              "name-operation": "to_lower_case()",
+              "naming-properties": [
+                {
+                  "property-name": "VNF_NAME"
+                },
+                {
+                  "property-value": "-",
+                  "property-name": "DELIMITER"
+                },
+                {
+                  "property-name": "VF_MODULE_LABEL"
+                },
+                {
+                  "property-name": "VF_MODULE_TYPE"
+                },
+                {
+                  "property-name": "SEQUENCE",
+                  "increment-sequence": {
+                    "max": "zzz",
+                    "scope": "PRECEEDING",
+                    "start-value": "1",
+                    "length": "3",
+                    "increment": "1",
+                    "sequence-type": "alpha-numeric"
+                  }
+                }
+              ]
+            }
+          ],
+          "policy-instance-name": "ONAP_NF_NAMING_TIMESTAMP"
+        },
+        "name": "SDNC_Policy.ONAP_NF_NAMING_TIMESTAMP",
+        "version": "1.0.0",
+        "metadata": {
+          "policy-id": "SDNC_Policy.ONAP_NF_NAMING_TIMESTAMP",
+          "policy-version": "1.0.0"
+        }
+      }
+    }
+  ]
+}
diff --git a/gui-clamp/ui-react/src/api/PolicyService.js b/gui-clamp/ui-react/src/api/PolicyService.js
new file mode 100644
index 0000000..046f789
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/PolicyService.js
@@ -0,0 +1,98 @@
+/*-
+ * ============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============================================
+ * ===================================================================
+ *
+ */
+
+export default class PolicyService {
+  static getPoliciesList() {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policies', {
+      method: 'GET',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("getPoliciesList response received: ", response.status);
+        if (response.ok) {
+          console.info("getPoliciesList query successful");
+          return response.json();
+        } else {
+          return response.text().then(responseBody => {
+            throw new Error("HTTP " + response.status + "," + responseBody);
+          })
+        }
+      })
+      .catch(function (error) {
+        console.error("getPoliciesList error occurred ", error);
+        alert("getPoliciesList error occurred " + error);
+        return undefined;
+      })
+  }
+
+  static createNewPolicy(policyModelType, policyModelVersion, policyName, policyVersion, policyJson) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policies/' + policyModelType + '/'
+      + policyModelVersion + '/' + policyName + '/' + policyVersion, {
+      method: 'POST',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      body: JSON.stringify(policyJson)
+    })
+      .then(function (response) {
+        console.debug("createNewPolicy response received: ", response.status);
+        if (response.ok) {
+          console.info("createNewPolicy query successful");
+          return response.text();
+        } else {
+          return response.text().then(responseBody => {
+            throw new Error("HTTP " + response.status + "," + responseBody);
+          })
+        }
+      })
+      .catch(function (error) {
+        console.error("createNewPolicy error occurred ", error);
+        alert("createNewPolicy error occurred " + error);
+        return undefined;
+      });
+  }
+
+  static deletePolicy(policyModelType, policyModelVersion, policyName, policyVersion) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policies/' + policyModelType + '/'
+      + policyModelVersion + '/' + policyName + '/' + policyVersion, {
+      method: 'DELETE',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("deletePolicy response received: ", response.status);
+        if (response.ok) {
+          console.info("deletePolicy query successful");
+          return response.text();
+        } else {
+          return response.text().then(responseBody => {
+            throw new Error("HTTP " + response.status + "," + responseBody);
+          })
+        }
+      })
+      .catch(function (error) {
+        console.error("deletePolicy error occurred ", error);
+        alert("deletePolicy error occurred " + error);
+        return undefined;
+      });
+  }
+}
diff --git a/gui-clamp/ui-react/src/api/PolicyToscaService.js b/gui-clamp/ui-react/src/api/PolicyToscaService.js
new file mode 100644
index 0000000..b5282f9
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/PolicyToscaService.js
@@ -0,0 +1,136 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019, 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class PolicyToscaService {
+  static getToscaPolicyModels() {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policyToscaModels', { method: 'GET', credentials: 'same-origin' })
+      .then(function (response) {
+        console.debug("getToscaPolicyModels response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("getToscaPolicyModels query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("getToscaPolicyModels error received", error);
+        return {};
+      });
+  }
+
+  static getToscaPolicyModelYaml(policyModelType, policyModelVersion) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policyToscaModels/yaml/' + policyModelType + "/" + policyModelVersion, {
+      method: 'GET',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("getToscaPolicyModelYaml response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("getToscaPolicyModelYaml query failed");
+          return "";
+        }
+      })
+      .catch(function (error) {
+        console.error("getToscaPolicyModelYaml error received", error);
+        return "";
+      });
+  }
+
+  static getToscaPolicyModel(policyModelType, policyModelVersion) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policyToscaModels/' + policyModelType + "/" + policyModelVersion, {
+      method: 'GET',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("getToscaPolicyModel response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("getToscaPolicyModel query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("getToscaPolicyModel error received", error);
+        return {};
+      });
+  }
+
+  static createPolicyModelFromToscaModel(jsonData) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policyToscaModels', {
+      method: 'POST',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "a",
+      },
+      body: JSON.stringify(jsonData)
+    })
+      .then(function (response) {
+        console.debug("createPolicyModelFromToscaModel response received: ", response.status);
+        if (response.ok) {
+          var message = {
+            status: response.status,
+            message: 'Tosca Policy Model successfully uploaded'
+          };
+          return message;
+        } else {
+          console.error("createPolicyModelFromToscaModel failed");
+          return response.text();
+        }
+      })
+      .catch(function (error) {
+        console.error("createPolicyModelFromToscaModel error received", error);
+        return "";
+      });
+  }
+
+  static updatePolicyModelTosca(policyModelType, policyModelVersion, jsonData) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/policyToscaModels/' + policyModelType + '/' + policyModelVersion, {
+      method: 'PUT',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "a",
+      },
+      body: JSON.stringify(jsonData)
+    })
+      .then(function (response) {
+        console.debug("updatePolicyModelTosca response received: ", response.status);
+        if (response.ok) {
+          var message = {
+            status: response.status,
+            message: 'Tosca Policy Model successfully uploaded'
+          };
+          return message;
+        } else {
+          console.error("updatePolicyModelTosca failed");
+          return response.text();
+        }
+      })
+      .catch(function (error) {
+        console.error("updatePolicyModelTosca error received", error);
+        return "";
+      });
+  }
+}
diff --git a/gui-clamp/ui-react/src/api/TemplateService.js b/gui-clamp/ui-react/src/api/TemplateService.js
new file mode 100644
index 0000000..a6e386e
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/TemplateService.js
@@ -0,0 +1,197 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class TemplateService {
+
+  static getLoopNames() {
+    return fetch(window.location.pathname + 'restservices/clds/v2/loop/getAllNames', { method: 'GET', credentials: 'same-origin' })
+      .then(function (response) {
+        console.debug("getLoopNames response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("getLoopNames query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("getLoopNames error received", error);
+        return {};
+      });
+  }
+
+  static getAllLoopTemplates() {
+    return fetch(window.location.pathname + 'restservices/clds/v2/templates', { method: 'GET', credentials: 'same-origin', })
+      .then(function (response) {
+        console.debug("getAllLoopTemplates response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("getAllLoopTemplates query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("getAllLoopTemplates error received", error);
+        return {};
+      });
+  }
+
+  static getDictionary() {
+    return fetch(window.location.pathname + 'restservices/clds/v2/dictionary/', { method: 'GET', credentials: 'same-origin', })
+      .then(function (response) {
+        console.debug("getDictionary response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("getDictionary query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("getDictionary error received", error);
+        return {};
+      });
+  }
+
+  static getDictionaryElements(dictionaryName) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/dictionary/' + dictionaryName, {
+      method: 'GET',
+      headers: {
+        "Content-Type": "application/json",
+      },
+      credentials: 'same-origin',
+    })
+      .then(function (response) {
+        console.debug("getDictionaryElements response received: ", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          console.error("getDictionaryElements query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("getDictionaryElements error received", error);
+        return {};
+      });
+  }
+
+  static insDictionary(jsonData) {
+    console.log("dictionaryName is", jsonData.name)
+    return fetch(window.location.pathname + 'restservices/clds/v2/dictionary/', {
+      method: 'PUT',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(jsonData)
+    })
+      .then(function (response) {
+        console.debug("insDictionary response received: ", response.status);
+        if (response.ok) {
+          return response.status;
+        } else {
+          var errorMessage = response.status;
+          console.error("insDictionary query failed", response.status);
+          return errorMessage;
+        }
+      })
+      .catch(function (error) {
+        console.error("insDictionary error received", error);
+        return "";
+      });
+  }
+
+  static insDictionaryElements(jsonData) {
+    console.log("dictionaryName is", jsonData.name)
+    return fetch(window.location.pathname + 'restservices/clds/v2/dictionary/' + jsonData.name, {
+      method: 'PUT',
+      credentials: 'same-origin',
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(jsonData)
+    })
+      .then(function (response) {
+        console.debug("insDictionary response received: ", response.status);
+        if (response.ok) {
+          return response.status;
+        } else {
+          var errorMessage = response.status;
+          console.error("insDictionary query failed", response.status);
+          return errorMessage;
+        }
+      })
+      .catch(function (error) {
+        console.error("insDictionary error received", error);
+        return "";
+      });
+  }
+
+  static deleteDictionary(dictionaryName) {
+    console.log("inside templaemenu service", dictionaryName)
+    return fetch(window.location.pathname + 'restservices/clds/v2/dictionary/' + dictionaryName, {
+      method: 'DELETE',
+      headers: {
+        "Content-Type": "application/json",
+      },
+      credentials: 'same-origin',
+    })
+      .then(function (response) {
+        console.debug("deleteDictionary response received: ", response.status);
+        if (response.ok) {
+          return response.status;
+        } else {
+          console.error("deleteDictionary query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("deleteDictionary error received", error);
+        return {};
+      });
+  }
+
+  static deleteDictionaryElements(dictionaryData) {
+    return fetch(window.location.pathname + 'restservices/clds/v2/dictionary/' + dictionaryData.name + '/elements/' + dictionaryData.shortName, {
+      method: 'DELETE',
+      headers: {
+        "Content-Type": "application/json",
+      },
+      credentials: 'same-origin',
+    })
+      .then(function (response) {
+        console.debug("deleteDictionary response received: ", response.status);
+        if (response.ok) {
+          return response.status;
+        } else {
+          console.error("deleteDictionary query failed");
+          return {};
+        }
+      })
+      .catch(function (error) {
+        console.error("deleteDictionary error received", error);
+        return {};
+      });
+  }
+}
diff --git a/gui-clamp/ui-react/src/api/UserService.js b/gui-clamp/ui-react/src/api/UserService.js
new file mode 100644
index 0000000..bcf46ea
--- /dev/null
+++ b/gui-clamp/ui-react/src/api/UserService.js
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class UserService {
+  static notLoggedUserName = 'Anonymous';
+
+  static login() {
+    return fetch(window.location.pathname + 'restservices/clds/v1/user/getUser', {
+      method: 'GET',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("getUser response received, status code:", response.status);
+        if (response.ok) {
+          return response.text();
+        } else {
+          console.error("getUser response is nok");
+          return UserService.notLoggedUserName;
+        }
+      })
+      .then(function (data) {
+        console.info("User connected:", data)
+        return data;
+      })
+      .catch(function (error) {
+        console.warn("getUser error received, user set to: ", UserService.notLoggedUserName);
+        console.error("getUser error:", error);
+        return UserService.notLoggedUserName;
+      });
+  }
+
+  static getUserInfo() {
+    return fetch(window.location.pathname + 'restservices/clds/v2/clampInformation', {
+      method: 'GET',
+      credentials: 'same-origin'
+    })
+      .then(function (response) {
+        console.debug("getUserInfo response received, status code:", response.status);
+        if (response.ok) {
+          return response.json();
+        } else {
+          return {}
+        }
+      })
+      .then(function (data) {
+        console.info("User info received:", data)
+        return data;
+      })
+      .catch(function (error) {
+        console.warn("getUserInfo error received, user set to: ", UserService.notLoggedUserName);
+        console.error("getUserInfo error:", error);
+        return {};
+      });
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.js
new file mode 100644
index 0000000..690dcbb
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.js
@@ -0,0 +1,193 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 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 from 'react'
+import Select from 'react-select';
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+import TemplateService from '../../../api/TemplateService';
+import LoopCache from '../../../api/LoopCache';
+import SvgGenerator from '../../loop_viewer/svg/SvgGenerator';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+
+const ErrMsgStyled = styled.div`
+  color: red;
+`
+
+export default class CreateLoopModal extends React.Component {
+  constructor(props, context) {
+    super(props, context);
+
+    this.getAllLoopTemplates = this.getAllLoopTemplates.bind(this);
+    this.handleCreate = this.handleCreate.bind(this);
+    this.handleModelName = this.handleModelName.bind(this);
+    this.handleClose = this.handleClose.bind(this);
+    this.handleDropDownListChange = this.handleDropDownListChange.bind(this);
+    this.renderSvg = this.renderSvg.bind(this);
+    this.state = {
+      show: true,
+      chosenTemplateName: '',
+      modelInputErrMsg: '',
+      modelName: '',
+      templateNames: [],
+      fakeLoopCacheWithTemplate: new LoopCache({})
+    };
+  }
+
+  async componentDidMount() {
+    await this.getAllLoopTemplates();
+    await this.getModelNames();
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    this.props.history.push('/');
+  }
+
+  handleDropDownListChange(e) {
+    if (typeof e.value !== "undefined") {
+      this.setState({
+        fakeLoopCacheWithTemplate:
+          new LoopCache({
+            "loopTemplate": e.templateObject,
+            "name": "fakeLoop"
+          }),
+        chosenTemplateName: e.value
+      })
+    } else {
+      this.setState({ fakeLoopCacheWithTemplate: new LoopCache({}) })
+    }
+  }
+
+  getAllLoopTemplates() {
+    TemplateService.getAllLoopTemplates().then(templatesData => {
+      const templateOptions = templatesData.map((templateData) => {
+        return { label: templateData.name, value: templateData.name, templateObject: templateData }
+      });
+      this.setState({
+        templateNames: templateOptions
+      })
+    });
+  }
+
+  getModelNames() {
+    TemplateService.getLoopNames().then(loopNames => {
+      if (!loopNames) {
+        loopNames = [];
+      }
+      // Remove LOOP_ prefix
+      let trimmedLoopNames = loopNames.map(str => str.replace('LOOP_', ''));
+      this.setState({ modelNames: trimmedLoopNames });
+    });
+  }
+
+  handleCreate() {
+    if (!this.state.modelName) {
+      alert("A model name is required");
+      return;
+    }
+    console.debug("Create Model " + this.state.modelName + ", Template " + this.state.chosenTemplateName + " is chosen");
+    this.setState({ show: false });
+    LoopService.createLoop("LOOP_" + this.state.modelName, this.state.chosenTemplateName).then(text => {
+      console.debug("CreateLoop response received: ", text);
+      try {
+        this.props.history.push('/');
+        this.props.loadLoopFunction("LOOP_" + this.state.modelName);
+      } catch (err) {
+        alert(text);
+        this.props.history.push('/');
+      }
+    })
+      .catch(error => {
+        console.debug("Create Loop failed");
+      });
+  }
+
+  handleModelName(event) {
+    if (this.state.modelNames.includes(event.target.value)) {
+      this.setState({
+        modelInputErrMsg: 'A model named "' + event.target.value + '" already exists. Please pick another name.',
+        modelName: event.target.value
+      });
+      return;
+    } else {
+      this.setState({
+        modelInputErrMsg: '',
+        modelName: event.target.value
+      });
+    }
+  }
+
+  renderSvg() {
+    return (
+      <SvgGenerator loopCache={ this.state.fakeLoopCacheWithTemplate } clickable={ false } generatedFrom={ SvgGenerator.GENERATED_FROM_TEMPLATE }/>
+    );
+  }
+
+  render() {
+    return (
+      <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+          <Modal.Title>Create Model</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          <Form.Group as={ Row } controlId="formPlaintextEmail">
+            <Form.Label column sm="2">Template Name:</Form.Label>
+            <Col sm="10">
+              <Select onChange={ this.handleDropDownListChange } options={ this.state.templateNames }/>
+            </Col>
+          </Form.Group>
+          <Form.Group as={ Row } style={ { alignItems: 'center' } } controlId="formSvgPreview">
+            <Form.Label column sm="2">Model Preview:</Form.Label>
+            <Col sm="10">
+              { this.renderSvg() }
+            </Col>
+          </Form.Group>
+          <Form.Group as={ Row } controlId="formPlaintextEmail">
+            <Form.Label column sm="2">Model Name:</Form.Label>
+            <input sm="5" type="text" style={ { width: '50%', marginLeft: '1em' } }
+                   value={ this.state.modelName }
+                   onChange={ this.handleModelName }
+            />
+            <span sm="5"/>
+          </Form.Group>
+          <Form.Group as={ Row } controlId="formPlaintextEmail">
+            <Form.Label column sm="2"> </Form.Label>
+            <ErrMsgStyled>{ this.state.modelInputErrMsg }</ErrMsgStyled>
+          </Form.Group>
+        </Modal.Body>
+        <Modal.Footer>
+          <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button>
+          <Button variant="primary" type="submit" onClick={ this.handleCreate }>Create</Button>
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js
new file mode 100644
index 0000000..401bb6a
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js
@@ -0,0 +1,144 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 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 from 'react';
+import { shallow } from 'enzyme';
+import CreateLoopModal from './CreateLoopModal';
+import LoopService from '../../../api/LoopService';
+import TemplateService from '../../../api/TemplateService';
+
+describe('Verify CreateLoopModal', () => {
+
+  it('Test the render method', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    TemplateService.getAllLoopTemplates = jest.fn().mockImplementation(() => {
+      return Promise.resolve([{ "name": "template1" }, { "name": "template2" }]);
+    });
+    TemplateService.getLoopNames = jest.fn().mockImplementation(() => {
+      return Promise.resolve([]);
+    });
+
+    const component = shallow(<CreateLoopModal/>);
+    expect(component).toMatchSnapshot();
+    await flushPromises();
+    component.update();
+    expect(component.state('templateNames')).toStrictEqual([{ "label": "template1", "value": "template1", "templateObject": { "name": "template1" } }, {
+      "label": "template2",
+      "value": "template2",
+      "templateObject": { "name": "template2" }
+    }]);
+  });
+
+  it('handleDropdownListChange event', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+
+    const component = shallow(<CreateLoopModal/>);
+    component.find('StateManager').simulate('change', { value: 'template1', templateObject: { "name": "template1" } });
+    await flushPromises();
+    component.update();
+    expect(component.state('chosenTemplateName')).toEqual("template1");
+    expect(component.state('fakeLoopCacheWithTemplate').getLoopTemplate()['name']).toEqual("template1");
+    expect(component.state('fakeLoopCacheWithTemplate').getLoopName()).toEqual("fakeLoop");
+
+    component.find('StateManager').simulate('change', { value: 'template2', templateObject: { "name": "template2" } });
+    await flushPromises();
+    component.update();
+    expect(component.state('chosenTemplateName')).toEqual("template2");
+    expect(component.state('fakeLoopCacheWithTemplate').getLoopTemplate()['name']).toEqual("template2");
+    expect(component.state('fakeLoopCacheWithTemplate').getLoopName()).toEqual("fakeLoop");
+  });
+
+  it('handleModelName event', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    TemplateService.getAllLoopTemplates = jest.fn().mockImplementation(() => {
+      return Promise.resolve([{ "name": "template1" }, { "name": "template2" }]);
+    });
+    TemplateService.getLoopNames = jest.fn().mockImplementation(() => {
+      return Promise.resolve([]);
+    });
+    const event = { target: { value: "model1" } };
+    const component = shallow(<CreateLoopModal/>);
+    await flushPromises();
+    component.find('input').simulate('change', event);
+    component.update();
+    expect(component.state('modelName')).toEqual("model1");
+  });
+
+  it('Test handleClose', () => {
+    const historyMock = { push: jest.fn() };
+    const handleClose = jest.spyOn(CreateLoopModal.prototype, 'handleClose');
+    const component = shallow(<CreateLoopModal history={ historyMock }/>)
+
+    component.find('[variant="secondary"]').prop('onClick')();
+
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+
+    handleClose.mockClear();
+  });
+
+  it('Test handleCreate Fail', () => {
+    const handleCreate = jest.spyOn(CreateLoopModal.prototype, 'handleCreate');
+    const component = shallow(<CreateLoopModal/>)
+
+    component.find('[variant="primary"]').prop('onClick')();
+
+    expect(handleCreate).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(true);
+
+    handleCreate.mockClear();
+  });
+
+  it('Test handleCreate Suc', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const historyMock = { push: jest.fn() };
+    const loadLoopFunction = jest.fn();
+
+    LoopService.createLoop = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+
+    const handleCreate = jest.spyOn(CreateLoopModal.prototype, 'handleCreate');
+    const component = shallow(<CreateLoopModal history={ historyMock } loadLoopFunction={ loadLoopFunction }/>)
+    component.setState({
+      modelName: "modelNameTest",
+      chosenTemplateName: "template1"
+    });
+
+    component.find('[variant="primary"]').prop('onClick')();
+    await flushPromises();
+    component.update();
+
+    expect(handleCreate).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+
+    handleCreate.mockClear();
+  });
+
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.js
new file mode 100644
index 0000000..b7ab8a5
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.js
@@ -0,0 +1,183 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import LoopActionService from '../../../api/LoopActionService';
+import LoopService from '../../../api/LoopService';
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Tabs from 'react-bootstrap/Tabs';
+import Tab from 'react-bootstrap/Tab';
+import styled from 'styled-components';
+import Spinner from 'react-bootstrap/Spinner'
+
+const StyledSpinnerDiv = styled.div`
+  justify-content: center !important;
+  display: flex !important;
+`;
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+const FormStyled = styled(Form.Group)`
+  padding: .25rem 1.5rem;
+`
+export default class DeployLoopModal extends React.Component {
+
+  constructor(props, context) {
+    super(props, context);
+
+    this.handleSave = this.handleSave.bind(this);
+    this.handleClose = this.handleClose.bind(this);
+    this.handleChange = this.handleChange.bind(this);
+    this.refreshStatus = this.refreshStatus.bind(this);
+    this.renderDeployParam = this.renderDeployParam.bind(this);
+    this.renderSpinner = this.renderSpinner.bind(this);
+
+    const propertiesJson = JSON.parse(JSON.stringify(this.props.loopCache.getGlobalProperties()));
+    this.state = {
+      loopCache: this.props.loopCache,
+      temporaryPropertiesJson: propertiesJson,
+      show: true,
+      key: this.getInitialKeyValue(propertiesJson)
+    };
+  }
+
+  getInitialKeyValue(temporaryPropertiesJson) {
+    const deployJsonList = temporaryPropertiesJson["dcaeDeployParameters"];
+    let initialKey;
+    Object.keys(deployJsonList)
+      .filter((obj) => Object.keys(deployJsonList).indexOf(obj) === 0)
+      .map(obj =>
+        initialKey = obj
+      );
+    return initialKey;
+  }
+
+  componentWillReceiveProps(newProps) {
+    this.setState({
+      loopName: newProps.loopCache.getLoopName(),
+      show: true
+    });
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    this.props.history.push('/');
+  }
+
+  renderSpinner() {
+    if (this.state.deploying) {
+      return (
+        <StyledSpinnerDiv>
+          <Spinner animation="border" role="status">
+            <span className="sr-only">Loading...</span>
+          </Spinner>
+        </StyledSpinnerDiv>
+      );
+    } else {
+      return (<div></div>);
+    }
+  }
+
+  handleSave() {
+    const loopName = this.props.loopCache.getLoopName();
+    // save the global propserties
+    this.setState({ deploying: true });
+    LoopService.updateGlobalProperties(loopName, this.state.temporaryPropertiesJson).then(resp => {
+      LoopActionService.performAction(loopName, "deploy").then(pars => {
+        this.props.showSucAlert("Action deploy successfully performed");
+        // refresh status and update loop logs
+        this.refreshStatus(loopName);
+      })
+        .catch(error => {
+          this.props.showFailAlert("Action deploy failed");
+          // refresh status and update loop logs
+          this.refreshStatus(loopName);
+        });
+    });
+  }
+
+  refreshStatus(loopName) {
+    LoopActionService.refreshStatus(loopName).then(data => {
+      this.props.updateLoopFunction(data);
+      this.setState({ show: false, deploying: false });
+      this.props.history.push('/');
+    })
+      .catch(error => {
+        this.props.showFailAlert("Refresh status failed");
+        this.setState({ show: false, deploying: false });
+        this.props.history.push('/');
+      });
+  }
+
+  handleChange(event) {
+    let deploymentParam = this.state.temporaryPropertiesJson["dcaeDeployParameters"];
+    deploymentParam[this.state.key][event.target.name] = event.target.value;
+
+    this.setState({ temporaryPropertiesJson: { dcaeDeployParameters: deploymentParam } });
+  }
+
+  renderDeployParamTabs() {
+    if (typeof (this.state.temporaryPropertiesJson) === "undefined") {
+      return "";
+    }
+
+    const deployJsonList = this.state.temporaryPropertiesJson["dcaeDeployParameters"];
+    var indents = [];
+    Object.keys(deployJsonList).map((item, key) =>
+      indents.push(<Tab eventKey={ item } title={ item }>
+        { this.renderDeployParam(deployJsonList[item]) }
+      </Tab>)
+    );
+    return indents;
+  }
+
+  renderDeployParam(deployJson) {
+    var indents = [];
+    Object.keys(deployJson).map((item, key) =>
+      indents.push(<FormStyled>
+        <Form.Label>{ item }</Form.Label>
+        <Form.Control type="text" name={ item } onChange={ this.handleChange } defaultValue={ deployJson[item] }></Form.Control>
+      </FormStyled>));
+    return indents;
+  }
+
+  render() {
+    return (
+      <ModalStyled size="lg" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+          <Modal.Title>Deployment parameters</Modal.Title>
+        </Modal.Header>
+        <Tabs id="controlled-tab-example" activeKey={ this.state.key } onSelect={ key => this.setState({ key }) }>
+          { this.renderDeployParamTabs() }
+        </Tabs>
+        { this.renderSpinner() }
+        <Modal.Footer>
+          <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button>
+          <Button variant="primary" type="submit" onClick={ this.handleSave }>Deploy</Button>
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js
new file mode 100644
index 0000000..0c68f23
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import DeployLoopModal from './DeployLoopModal';
+import LoopCache from '../../../api/LoopCache';
+import LoopActionService from '../../../api/LoopActionService';
+import LoopService from '../../../api/LoopService';
+
+describe('Verify DeployLoopModal', () => {
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+    "globalPropertiesJson": {
+      "dcaeDeployParameters": {
+        "testMs": {
+          "location_id": "",
+          "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+        }
+      }
+    }
+  });
+
+  it('Test the render method', () => {
+    const component = shallow(
+      <DeployLoopModal loopCache={ loopCache }/>
+    )
+
+    expect(component).toMatchSnapshot();
+  });
+
+  it('Test handleClose', () => {
+    const historyMock = { push: jest.fn() };
+    const handleClose = jest.spyOn(DeployLoopModal.prototype, 'handleClose');
+    const component = shallow(<DeployLoopModal history={ historyMock } loopCache={ loopCache }/>)
+
+    component.find('[variant="secondary"]').prop('onClick')();
+
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Test handleSave successful', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const historyMock = { push: jest.fn() };
+    const updateLoopFunction = jest.fn();
+    const showSucAlert = jest.fn();
+    const showFailAlert = jest.fn();
+    const handleSave = jest.spyOn(DeployLoopModal.prototype, 'handleSave');
+    LoopService.updateGlobalProperties = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        text: () => "OK"
+      });
+    });
+    LoopActionService.performAction = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+    LoopActionService.refreshStatus = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+
+    const component = shallow(<DeployLoopModal history={ historyMock }
+                                               loopCache={ loopCache } updateLoopFunction={ updateLoopFunction } showSucAlert={ showSucAlert } showFailAlert={ showFailAlert }/>)
+
+    component.find('[variant="primary"]').prop('onClick')();
+    await flushPromises();
+    component.update();
+
+    expect(handleSave).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+    handleSave.mockClear();
+  });
+
+  it('Onchange event', () => {
+    const event = { target: { name: "location_id", value: "testLocation" } };
+    const component = shallow(<DeployLoopModal loopCache={ loopCache }/>);
+
+    component.find('[name="location_id"]').simulate('change', event);
+    component.update();
+    expect(component.state('temporaryPropertiesJson').dcaeDeployParameters.testMs.location_id).toEqual("testLocation");
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.js
new file mode 100644
index 0000000..6917713
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.js
@@ -0,0 +1,118 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+export default class LoopPropertiesModal extends React.Component {
+
+  state = {
+    show: true,
+    loopCache: this.props.loopCache,
+    temporaryPropertiesJson: JSON.parse(JSON.stringify(this.props.loopCache.getGlobalProperties()))
+  };
+
+  constructor(props, context) {
+    super(props, context);
+
+    this.handleClose = this.handleClose.bind(this);
+    this.handleSave = this.handleSave.bind(this);
+    this.handleChange = this.handleChange.bind(this);
+
+    this.renderDcaeParameters = this.renderDcaeParameters.bind(this);
+    this.renderAllParameters = this.renderAllParameters.bind(this);
+    this.getDcaeParameters = this.getDcaeParameters.bind(this);
+    this.readOnly = props.readOnly !== undefined ? props.readOnly : false;
+  }
+
+  componentWillReceiveProps(newProps) {
+    this.setState({
+      loopCache: newProps.loopCache,
+      temporaryPropertiesJson: JSON.parse(JSON.stringify(newProps.loopCache.getGlobalProperties()))
+    });
+  }
+
+  handleClose() {
+    this.props.history.push('/');
+  }
+
+  handleSave(event) {
+    LoopService.updateGlobalProperties(this.state.loopCache.getLoopName(), this.state.temporaryPropertiesJson).then(resp => {
+      this.setState({ show: false });
+      this.props.history.push('/');
+      this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+    });
+  }
+
+  handleChange(event) {
+    this.setState({ temporaryPropertiesJson: { [event.target.name]: JSON.parse(event.target.value) } });
+  }
+
+  renderAllParameters() {
+    return (<Modal.Body>
+        <Form>
+          { this.renderDcaeParameters() }
+        </Form>
+      </Modal.Body>
+    );
+  }
+
+  getDcaeParameters() {
+    if (typeof (this.state.temporaryPropertiesJson) !== "undefined") {
+      return JSON.stringify(this.state.temporaryPropertiesJson["dcaeDeployParameters"]);
+    } else {
+      return "";
+    }
+
+  }
+
+  renderDcaeParameters() {
+    return (
+      <Form.Group>
+        <Form.Label>Deploy Parameters</Form.Label>
+        <Form.Control as="textarea" rows="3" name="dcaeDeployParameters" onChange={ this.handleChange } defaultValue={ this.getDcaeParameters() }></Form.Control>
+      </Form.Group>
+    );
+  }
+
+  render() {
+    return (
+      <ModalStyled size="lg" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+          <Modal.Title>Model Properties</Modal.Title>
+        </Modal.Header>
+        { this.renderAllParameters() }
+        <Modal.Footer>
+          <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button>
+          <Button variant="primary" type="submit" disabled={ this.readOnly } onClick={ this.handleSave }>Save Changes</Button>
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.test.js
new file mode 100644
index 0000000..a9c5903
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.test.js
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import LoopPropertiesModal from './LoopPropertiesModal';
+import LoopCache from '../../../api/LoopCache';
+import LoopService from '../../../api/LoopService';
+
+describe('Verify LoopPropertiesModal', () => {
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+    "globalPropertiesJson": {
+      "dcaeDeployParameters": {
+        "location_id": "",
+        "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+      }
+    }
+  });
+
+  it('Test the render method', () => {
+    const component = shallow(
+      <LoopPropertiesModal loopCache={ loopCache }/>
+    )
+    component.setState({
+      show: true,
+      temporaryPropertiesJson: {
+        "dcaeDeployParameters": {
+          "location_id": "",
+          "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+        }
+      }
+    });
+
+    expect(component.state('temporaryPropertiesJson')).toEqual({
+      "dcaeDeployParameters": {
+        "location_id": "",
+        "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+      }
+    });
+    expect(component.state('show')).toEqual(true);
+
+    expect(component).toMatchSnapshot();
+  });
+
+  it('Test handleClose', () => {
+    const historyMock = { push: jest.fn() };
+    const handleClose = jest.spyOn(LoopPropertiesModal.prototype, 'handleClose');
+    const component = shallow(<LoopPropertiesModal history={ historyMock } loopCache={ loopCache }/>)
+
+    component.find('[variant="secondary"]').prop('onClick')();
+
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Test handleSave successful', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const historyMock = { push: jest.fn() };
+    const loadLoopFunction = jest.fn();
+    const handleSave = jest.spyOn(LoopPropertiesModal.prototype, 'handleSave');
+    LoopService.updateGlobalProperties = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        text: () => "OK"
+      });
+    });
+
+    const component = shallow(<LoopPropertiesModal history={ historyMock }
+                                                   loopCache={ loopCache } loadLoopFunction={ loadLoopFunction }/>)
+
+    component.find('[variant="primary"]').prop('onClick')();
+    await flushPromises();
+    component.update();
+
+    expect(handleSave).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Onchange event', () => {
+    const event = { target: { name: "dcaeDeployParameters", value: "{\"location_id\": \"testLocation\",\"policy_id\": \"TCA_h2NMX_v1_0_ResourceInstanceName1_tca\"}" } };
+    const component = shallow(<LoopPropertiesModal loopCache={ loopCache }/>);
+
+    component.find('FormControl').simulate('change', event);
+    component.update();
+
+    expect(component.state('temporaryPropertiesJson').dcaeDeployParameters.location_id).toEqual("testLocation");
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.js
new file mode 100644
index 0000000..8f8b74d
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.js
@@ -0,0 +1,269 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 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 MaterialTable from "material-table";
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import styled from 'styled-components';
+import PolicyToscaService from '../../../api/PolicyToscaService';
+import ArrowUpward from '@material-ui/icons/ArrowUpward';
+import ChevronLeft from '@material-ui/icons/ChevronLeft';
+import ChevronRight from '@material-ui/icons/ChevronRight';
+import Clear from '@material-ui/icons/Clear';
+import FirstPage from '@material-ui/icons/FirstPage';
+import LastPage from '@material-ui/icons/LastPage';
+import Search from '@material-ui/icons/Search';
+import LoopService from '../../../api/LoopService';
+import Tabs from 'react-bootstrap/Tabs';
+import Tab from 'react-bootstrap/Tab';
+import Alert from 'react-bootstrap/Alert';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+const TextModal = styled.textarea`
+  margin-top: 20px;
+  white-space: pre;
+  background-color: ${ props => props.theme.toscaTextareaBackgroundColor };
+  text-align: justify;
+  font-size: ${ props => props.theme.toscaTextareaFontSize };
+  width: 100%;
+  height: 300px;
+`
+const cellStyle = { border: '1px solid black' };
+const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
+const rowHeaderStyle = { backgroundColor: '#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black' };
+
+export default class ModifyLoopModal extends React.Component {
+
+  state = {
+    show: true,
+    loopCache: this.props.loopCache,
+    content: 'Please select Tosca model to view the details',
+    selectedRowData: {},
+    toscaPolicyModelsData: [],
+    selectedPolicyModelsData: [],
+    key: 'add',
+    showFailAlert: false,
+    toscaColumns: [
+      {
+        title: "#", field: "index", render: rowData => rowData.tableData.id + 1,
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Policy Model Type", field: "policyModelType",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Policy Acronym", field: "policyAcronym",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Policy Name", field: "policyName",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Version", field: "version",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Uploaded By", field: "updatedBy",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Uploaded Date", field: "updatedDate", editable: 'never',
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Created Date", field: "createdDate", editable: 'never',
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      }
+    ],
+    tableIcons: {
+      FirstPage: forwardRef((props, ref) => <FirstPage { ...props } ref={ ref }/>),
+      LastPage: forwardRef((props, ref) => <LastPage { ...props } ref={ ref }/>),
+      NextPage: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+      PreviousPage: forwardRef((props, ref) => <ChevronLeft { ...props } ref={ ref }/>),
+      ResetSearch: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+      Search: forwardRef((props, ref) => <Search { ...props } ref={ ref }/>),
+      SortArrow: forwardRef((props, ref) => <ArrowUpward { ...props } ref={ ref }/>)
+    }
+  };
+
+  constructor(props, context) {
+    super(props, context);
+    this.handleClose = this.handleClose.bind(this);
+    this.initializeToscaPolicyModelsInfo = this.initializeToscaPolicyModelsInfo.bind(this);
+    this.handleYamlContent = this.handleYamlContent.bind(this);
+    this.getToscaPolicyModelYaml = this.getToscaPolicyModelYaml.bind(this);
+    this.handleAdd = this.handleAdd.bind(this);
+    this.handleRemove = this.handleRemove.bind(this);
+    this.initializeToscaPolicyModelsInfo();
+  }
+
+  componentWillReceiveProps(newProps) {
+    this.setState({
+      loopCache: newProps.loopCache,
+      temporaryPropertiesJson: JSON.parse(JSON.stringify(newProps.loopCache.getGlobalProperties()))
+    });
+  }
+
+  initializeToscaPolicyModelsInfo() {
+    var operationalPolicies = this.state.loopCache.getOperationalPolicies();
+    var selectedPolicyModels = [];
+    for (var policy in operationalPolicies) {
+      var newRow = operationalPolicies[policy]["policyModel"];
+      newRow["policyName"] = operationalPolicies[policy].name;
+      selectedPolicyModels.push(newRow);
+    }
+
+    PolicyToscaService.getToscaPolicyModels().then(allToscaModels => {
+      this.setState({
+        toscaPolicyModelsData: allToscaModels,
+        selectedPolicyModelsData: selectedPolicyModels
+      });
+    });
+  }
+
+  getToscaPolicyModelYaml(policyModelType, policyModelVersion) {
+    if (typeof policyModelType !== "undefined") {
+      PolicyToscaService.getToscaPolicyModelYaml(policyModelType, policyModelVersion).then(toscaYaml => {
+        if (toscaYaml.length !== 0) {
+          this.setState({ content: toscaYaml })
+        } else {
+          this.setState({ content: 'No Tosca model Yaml available' })
+        }
+      });
+    } else {
+      this.setState({ content: 'Please select Tosca model to view the details' })
+    }
+  }
+
+  handleYamlContent(event) {
+    this.setState({ content: event.target.value });
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    this.props.history.push('/');
+  }
+
+  renderAlert() {
+    return (
+      <div>
+        <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+          { this.state.showMessage }
+        </Alert>
+      </div>
+    );
+  }
+
+  handleAdd() {
+    LoopService.addOperationalPolicyType(this.state.loopCache.getLoopName(), this.state.selectedRowData.policyModelType, this.state.selectedRowData.version)
+      .then(pars => {
+        this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+        this.handleClose();
+      })
+      .catch(error => {
+        this.setState({ showFailAlert: true, showMessage: "Adding failed with error: " + error.message });
+      });
+  }
+
+  handleRemove() {
+    LoopService.removeOperationalPolicyType(this.state.loopCache.getLoopName(), this.state.selectedRowData.policyModelType, this.state.selectedRowData.version, this.state.selectedRowData.policyName);
+    this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+    this.handleClose();
+  }
+
+  render() {
+    return (
+      <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+          <Modal.Title>Modify Loop Operational Policies</Modal.Title>
+        </Modal.Header>
+        <Tabs id="controlled-tab-example" activeKey={ this.state.key } onSelect={ key => this.setState({ key, selectedRowData: {} }) }>
+          <Tab eventKey="add" title="Add Operational Policies">
+            <Modal.Body>
+              <MaterialTable
+                title={ "View Tosca Policy Models" }
+                data={ this.state.toscaPolicyModelsData }
+                columns={ this.state.toscaColumns }
+                icons={ this.state.tableIcons }
+                onRowClick={ (event, rowData) => {
+                  this.getToscaPolicyModelYaml(rowData.policyModelType, rowData.version);
+                  this.setState({ selectedRowData: rowData })
+                } }
+                options={ {
+                  headerStyle: rowHeaderStyle,
+                  rowStyle: rowData => ({
+                    backgroundColor: (this.state.selectedRowData !== {} && this.state.selectedRowData.tableData !== undefined
+                      && this.state.selectedRowData.tableData.id === rowData.tableData.id) ? '#EEE' : '#FFF'
+                  })
+                } }
+              />
+              <div>
+                <TextModal value={ this.state.content } onChange={ this.handleYamlContent }/>
+              </div>
+            </Modal.Body>
+            { this.renderAlert() }
+          </Tab>
+          <Tab eventKey="remove" title="Remove Operational Policies">
+            <Modal.Body>
+              <MaterialTable
+                title={ "Tosca Policy Models already added" }
+                data={ this.state.selectedPolicyModelsData }
+                columns={ this.state.toscaColumns }
+                icons={ this.state.tableIcons }
+                onRowClick={ (event, rowData) => {
+                  this.setState({ selectedRowData: rowData })
+                } }
+                options={ {
+                  headerStyle: rowHeaderStyle,
+                  rowStyle: rowData => ({
+                    backgroundColor: (this.state.selectedRowData !== {} && this.state.selectedRowData.tableData !== undefined
+                      && this.state.selectedRowData.tableData.id === rowData.tableData.id) ? '#EEE' : '#FFF'
+                  })
+                } }
+              />
+            </Modal.Body>
+          </Tab>
+        </Tabs>
+        <Modal.Footer>
+          <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button>
+          <Button variant="primary" disabled={ (this.state.key === "remove") } type="submit" onClick={ this.handleAdd }>Add</Button>
+          <Button variant="primary" disabled={ (this.state.key === "add") } type="submit" onClick={ this.handleRemove }>Remove</Button>
+        </Modal.Footer>
+
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js
new file mode 100644
index 0000000..79267af
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 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 from 'react';
+import { mount } from 'enzyme';
+import ModifyLoopModal from './ModifyLoopModal';
+import LoopCache from '../../../api/LoopCache';
+import LoopService from '../../../api/LoopService';
+import PolicyToscaService from '../../../api/PolicyToscaService';
+
+describe('Verify ModifyLoopModal', () => {
+  beforeEach(() => {
+    PolicyToscaService.getToscaPolicyModels = jest.fn().mockImplementation(() => {
+      return Promise.resolve([{
+        "policyModelType": "test",
+        "policyAcronym": "test",
+        "version": "1.0.0",
+        "updatedBy": "",
+        "updatedDate": ""
+      }]);
+    });
+    PolicyToscaService.getToscaPolicyModelYaml = jest.fn().mockImplementation(() => {
+      return Promise.resolve("OK");
+    });
+  })
+
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+    "microServicePolicies": [{
+      "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+      "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+      "properties": { "domain": "measurementsForVfScaling" },
+      "shared": false,
+      "jsonRepresentation": { "schema": {} }
+    }],
+    "globalPropertiesJson": {
+      "dcaeDeployParameters": {
+        "testMs": {
+          "location_id": "",
+          "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+        }
+      }
+    }
+  });
+  const historyMock = { push: jest.fn() };
+  const flushPromises = () => new Promise(setImmediate);
+
+  it('Test handleClose', () => {
+    const handleClose = jest.spyOn(ModifyLoopModal.prototype, 'handleClose');
+    const component = mount(<ModifyLoopModal history={ historyMock } loopCache={ loopCache }/>)
+
+    component.find('[variant="secondary"]').get(0).props.onClick();
+
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Test getToscaPolicyModelYaml', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const component = mount(<ModifyLoopModal history={ historyMock } loopCache={ loopCache }/>)
+    component.setState({
+      "selectedRowData": { "tableData": { "id": 0 } }
+    });
+    const instance = component.instance();
+
+    instance.getToscaPolicyModelYaml("", "1.0.0");
+    expect(component.state('content')).toEqual("Please select Tosca model to view the details");
+
+    instance.getToscaPolicyModelYaml("test", "1.0.0");
+    await flushPromises();
+    expect(component.state('content')).toEqual("OK");
+
+    PolicyToscaService.getToscaPolicyModelYaml = jest.fn().mockImplementation(() => {
+      return Promise.resolve("");
+    });
+    instance.getToscaPolicyModelYaml("test", "1.0.0");
+    await flushPromises();
+    expect(component.state('content')).toEqual("No Tosca model Yaml available");
+  });
+
+  it('Test handleYamlContent', async () => {
+    const component = mount(<ModifyLoopModal loopCache={ loopCache }/>)
+    const instance = component.instance();
+
+    const event = { "target": { "value": "testValue" } }
+    instance.handleYamlContent(event);
+    expect(component.state('content')).toEqual("testValue");
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.js
new file mode 100644
index 0000000..b6407fb
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.js
@@ -0,0 +1,139 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react'
+import Select from 'react-select';
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import FormCheck from 'react-bootstrap/FormCheck'
+import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+import SvgGenerator from '../../loop_viewer/svg/SvgGenerator';
+import LoopCache from '../../../api/LoopCache';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+const CheckBoxStyled = styled(FormCheck.Input)`
+  margin-left: 3rem;
+`
+
+export default class OpenLoopModal extends React.Component {
+  constructor(props, context) {
+    super(props, context);
+
+    this.getLoopNames = this.getLoopNames.bind(this);
+    this.handleOpen = this.handleOpen.bind(this);
+    this.handleClose = this.handleClose.bind(this);
+    this.handleDropDownListChange = this.handleDropDownListChange.bind(this);
+    this.renderSvg = this.renderSvg.bind(this);
+    this.showReadOnly = props.showReadOnly !== undefined ? props.showReadOnly : true;
+    this.state = {
+      show: true,
+      chosenLoopName: '',
+      loopNames: [],
+      loopCacheOpened: new LoopCache({})
+    };
+  }
+
+  componentWillMount() {
+    this.getLoopNames();
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    this.props.history.push('/');
+  }
+
+  handleDropDownListChange(e) {
+    LoopService.getLoop(e.value).then(loop => {
+      this.setState({
+        chosenLoopName: e.value,
+        loopCacheOpened: new LoopCache(loop)
+      });
+    });
+  }
+
+  getLoopNames() {
+    LoopService.getLoopNames().then(loopNames => {
+      if (Object.entries(loopNames).length !== 0) {
+        const loopOptions = loopNames.filter(loopName => loopName !== 'undefined').map((loopName) => {
+          return { label: loopName, value: loopName }
+        });
+        this.setState({ loopNames: loopOptions })
+      }
+    });
+  }
+
+  handleOpen() {
+    console.info("Loop " + this.state.chosenLoopName + " is chosen");
+    this.handleClose();
+    this.props.loadLoopFunction(this.state.chosenLoopName);
+  }
+
+  renderSvg() {
+    return (
+      <SvgGenerator loopCache={ this.state.loopCacheOpened } clickable={ false } generatedFrom={ SvgGenerator.GENERATED_FROM_INSTANCE }/>
+    );
+  }
+
+  render() {
+    return (
+      <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+          <Modal.Title>Open Model</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          <Form.Group as={ Row } controlId="formPlaintextEmail">
+            <Form.Label column sm="2">Model Name:</Form.Label>
+            <Col sm="10">
+              <Select onChange={ this.handleDropDownListChange }
+                      options={ this.state.loopNames }/>
+            </Col>
+          </Form.Group>
+          <Form.Group as={ Row } style={ { alignItems: 'center' } } controlId="formSvgPreview">
+            <Form.Label column sm="2">Model Preview:</Form.Label>
+            <Col sm="10">
+              { this.renderSvg() }
+            </Col>
+          </Form.Group>
+          { this.showReadOnly === true ?
+            <Form.Group as={ Row } controlId="formBasicCheckbox">
+              <Form.Check>
+                <FormCheck.Label>Read Only Mode:</FormCheck.Label>
+                <CheckBoxStyled style={ { marginLeft: '3.5em' } } type="checkbox"/>
+              </Form.Check>
+            </Form.Group>
+            : null }
+        </Modal.Body>
+        <Modal.Footer>
+          <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button>
+          <Button variant="primary" type="submit" onClick={ this.handleOpen }>Open</Button>
+        </Modal.Footer>
+      </ModalStyled>
+
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.test.js
new file mode 100644
index 0000000..6b2dc4a
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.test.js
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import OpenLoopModal from './OpenLoopModal';
+import LoopService from '../../../api/LoopService';
+
+describe('Verify OpenLoopModal', () => {
+
+  beforeEach(() => {
+    fetch.resetMocks();
+    fetch.mockResponse(JSON.stringify([
+      "LOOP_gmtAS_v1_0_ResourceInstanceName1_tca",
+      "LOOP_gmtAS_v1_0_ResourceInstanceName1_tca_3",
+      "LOOP_gmtAS_v1_0_ResourceInstanceName2_tca_2"
+    ]));
+  });
+
+  it('Test the render method', () => {
+
+    const component = shallow(<OpenLoopModal/>);
+    expect(component).toMatchSnapshot();
+  });
+
+  it('Onchange event', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    LoopService.getLoop = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+    const event = { value: 'LOOP_gmtAS_v1_0_ResourceInstanceName1_tca_3' };
+    const component = shallow(<OpenLoopModal/>);
+    component.find('StateManager').simulate('change', event);
+    await flushPromises();
+    component.update();
+    expect(component.state('chosenLoopName')).toEqual("LOOP_gmtAS_v1_0_ResourceInstanceName1_tca_3");
+  });
+
+
+  it('Test handleClose', () => {
+    const historyMock = { push: jest.fn() };
+    const handleClose = jest.spyOn(OpenLoopModal.prototype, 'handleClose');
+    const component = shallow(<OpenLoopModal history={ historyMock }/>)
+
+    component.find('[variant="secondary"]').prop('onClick')();
+
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+
+    handleClose.mockClear();
+  });
+
+  it('Test handleSubmit', () => {
+    const historyMock = { push: jest.fn() };
+    const loadLoopFunction = jest.fn();
+    const handleOpen = jest.spyOn(OpenLoopModal.prototype, 'handleOpen');
+    const component = shallow(<OpenLoopModal history={ historyMock } loadLoopFunction={ loadLoopFunction }/>)
+
+    component.find('[variant="primary"]').prop('onClick')();
+
+    expect(handleOpen).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+
+    handleOpen.mockClear();
+  });
+
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap
new file mode 100644
index 0000000..1ba9dba
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap
@@ -0,0 +1,167 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify CreateLoopModal Test the render method 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="xl"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      Create Model
+    </ModalTitle>
+  </ModalHeader>
+  <ModalBody>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formPlaintextEmail"
+    >
+      <FormLabel
+        column={true}
+        sm="2"
+        srOnly={false}
+      >
+        Template Name:
+      </FormLabel>
+      <Col
+        sm="10"
+      >
+        <StateManager
+          defaultInputValue=""
+          defaultMenuIsOpen={false}
+          defaultValue={null}
+          onChange={[Function]}
+          options={Array []}
+        />
+      </Col>
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formSvgPreview"
+      style={
+        Object {
+          "alignItems": "center",
+        }
+      }
+    >
+      <FormLabel
+        column={true}
+        sm="2"
+        srOnly={false}
+      >
+        Model Preview:
+      </FormLabel>
+      <Col
+        sm="10"
+      >
+        <withRouter(SvgGenerator)
+          clickable={false}
+          generatedFrom="TEMPLATE"
+          loopCache={
+            LoopCache {
+              "loopJsonCache": Object {},
+            }
+          }
+        />
+      </Col>
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formPlaintextEmail"
+    >
+      <FormLabel
+        column={true}
+        sm="2"
+        srOnly={false}
+      >
+        Model Name:
+      </FormLabel>
+      <input
+        onChange={[Function]}
+        sm="5"
+        style={
+          Object {
+            "marginLeft": "1em",
+            "width": "50%",
+          }
+        }
+        type="text"
+        value=""
+      />
+      <span
+        sm="5"
+      />
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formPlaintextEmail"
+    >
+      <FormLabel
+        column={true}
+        sm="2"
+        srOnly={false}
+      >
+         
+      </FormLabel>
+      <styled.div />
+    </FormGroup>
+  </ModalBody>
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="null"
+      variant="secondary"
+    >
+      Cancel
+    </Button>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="submit"
+      variant="primary"
+    >
+      Create
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap
new file mode 100644
index 0000000..4779ced
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap
@@ -0,0 +1,83 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify DeployLoopModal Test the render method 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="lg"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      Deployment parameters
+    </ModalTitle>
+  </ModalHeader>
+  <Tabs
+    activeKey="testMs"
+    id="controlled-tab-example"
+    mountOnEnter={false}
+    onSelect={[Function]}
+    unmountOnExit={false}
+    variant="tabs"
+  >
+    <Tab
+      eventKey="testMs"
+      title="testMs"
+    >
+      <Styled(FormGroup)>
+        <FormLabel
+          column={false}
+          srOnly={false}
+        >
+          location_id
+        </FormLabel>
+        <FormControl
+          defaultValue=""
+          name="location_id"
+          onChange={[Function]}
+          type="text"
+        />
+      </Styled(FormGroup)>
+      <Styled(FormGroup)>
+        <FormLabel
+          column={false}
+          srOnly={false}
+        >
+          policy_id
+        </FormLabel>
+        <FormControl
+          defaultValue="TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+          name="policy_id"
+          onChange={[Function]}
+          type="text"
+        />
+      </Styled(FormGroup)>
+    </Tab>
+  </Tabs>
+  <div />
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="null"
+      variant="secondary"
+    >
+      Cancel
+    </Button>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="submit"
+      variant="primary"
+    >
+      Deploy
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/LoopPropertiesModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/LoopPropertiesModal.test.js.snap
new file mode 100644
index 0000000..3baaa57
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/LoopPropertiesModal.test.js.snap
@@ -0,0 +1,61 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify LoopPropertiesModal Test the render method 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="lg"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      Model Properties
+    </ModalTitle>
+  </ModalHeader>
+  <ModalBody>
+    <Form
+      inline={false}
+    >
+      <FormGroup>
+        <FormLabel
+          column={false}
+          srOnly={false}
+        >
+          Deploy Parameters
+        </FormLabel>
+        <FormControl
+          as="textarea"
+          defaultValue="{\\"location_id\\":\\"\\",\\"policy_id\\":\\"TCA_h2NMX_v1_0_ResourceInstanceName1_tca\\"}"
+          name="dcaeDeployParameters"
+          onChange={[Function]}
+          rows="3"
+        />
+      </FormGroup>
+    </Form>
+  </ModalBody>
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="null"
+      variant="secondary"
+    >
+      Cancel
+    </Button>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="submit"
+      variant="primary"
+    >
+      Save Changes
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/OpenLoopModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/OpenLoopModal.test.js.snap
new file mode 100644
index 0000000..581fd0e
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/OpenLoopModal.test.js.snap
@@ -0,0 +1,137 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify OpenLoopModal Test the render method 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="xl"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      Open Model
+    </ModalTitle>
+  </ModalHeader>
+  <ModalBody>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formPlaintextEmail"
+    >
+      <FormLabel
+        column={true}
+        sm="2"
+        srOnly={false}
+      >
+        Model Name:
+      </FormLabel>
+      <Col
+        sm="10"
+      >
+        <StateManager
+          defaultInputValue=""
+          defaultMenuIsOpen={false}
+          defaultValue={null}
+          onChange={[Function]}
+          options={Array []}
+        />
+      </Col>
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formSvgPreview"
+      style={
+        Object {
+          "alignItems": "center",
+        }
+      }
+    >
+      <FormLabel
+        column={true}
+        sm="2"
+        srOnly={false}
+      >
+        Model Preview:
+      </FormLabel>
+      <Col
+        sm="10"
+      >
+        <withRouter(SvgGenerator)
+          clickable={false}
+          generatedFrom="INSTANCE"
+          loopCache={
+            LoopCache {
+              "loopJsonCache": Object {},
+            }
+          }
+        />
+      </Col>
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formBasicCheckbox"
+    >
+      <FormCheck>
+        <FormCheckLabel>
+          Read Only Mode:
+        </FormCheckLabel>
+        <Styled(FormCheckInput)
+          style={
+            Object {
+              "marginLeft": "3.5em",
+            }
+          }
+          type="checkbox"
+        />
+      </FormCheck>
+    </FormGroup>
+  </ModalBody>
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="null"
+      variant="secondary"
+    >
+      Cancel
+    </Button>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="submit"
+      variant="primary"
+    >
+      Open
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/ManageDictionaries.js b/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/ManageDictionaries.js
new file mode 100644
index 0000000..d7ba8d1
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/ManageDictionaries.js
@@ -0,0 +1,637 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import styled from 'styled-components';
+import TemplateMenuService from '../../../api/TemplateService';
+import CsvToJson from '../../../utils/CsvToJson';
+import MaterialTable, { MTableToolbar } from "material-table";
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+import AddBox from '@material-ui/icons/AddBox';
+import ArrowUpward from '@material-ui/icons/ArrowUpward';
+import Check from '@material-ui/icons/Check';
+import ChevronLeft from '@material-ui/icons/ChevronLeft';
+import VerticalAlignTopIcon from '@material-ui/icons/VerticalAlignTop';
+import VerticalAlignBottomIcon from '@material-ui/icons/VerticalAlignBottom';
+import ChevronRight from '@material-ui/icons/ChevronRight';
+import Clear from '@material-ui/icons/Clear';
+import DeleteOutline from '@material-ui/icons/DeleteOutline';
+import Edit from '@material-ui/icons/Edit';
+import FilterList from '@material-ui/icons/FilterList';
+import FirstPage from '@material-ui/icons/FirstPage';
+import LastPage from '@material-ui/icons/LastPage';
+import Remove from '@material-ui/icons/Remove';
+import Search from '@material-ui/icons/Search';
+import ViewColumn from '@material-ui/icons/ViewColumn';
+
+
+const ModalStyled = styled(Modal)`
+  @media (min-width: 1200px) {
+    .modal-xl {
+      max-width: 96%;
+    }
+  }
+  background-color: transparent;
+`
+
+const MTableToolbarStyled = styled(MTableToolbar)`
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+`
+const ColPullLeftStyled = styled(Col)`
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  margin-left: -40px;
+`
+
+const cellStyle = { border: '1px solid black' };
+const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
+const rowHeaderStyle = { backgroundColor: '#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black' };
+
+let dictList = [];
+let subDictFlag = false;
+
+function SelectSubDictType(props) {
+  const { onChange } = props;
+  const selectedValues = (e) => {
+    let options = e.target.options;
+    let SelectedDictTypes = '';
+    for (let dictType = 0, values = options.length; dictType < values; dictType++) {
+      if (options[dictType].selected) {
+        SelectedDictTypes = SelectedDictTypes.concat(options[dictType].value);
+        SelectedDictTypes = SelectedDictTypes.concat('|');
+      }
+    }
+    SelectedDictTypes = SelectedDictTypes.slice(0, -1);
+    onChange(SelectedDictTypes);
+  }
+  // When the subDictFlag is true, we need to disable selection of element "type"
+  return (
+    <div>
+      <select disabled={ subDictFlag } multiple={ true } onChange={ selectedValues }>
+        <option value="string">string</option>
+        <option value="number">number</option>
+        <option value="datetime">datetime</option>
+        <option value="map">map</option>
+        <option value="json">json</option>
+      </select>
+    </div>
+  );
+}
+
+function SubDict(props) {
+  const { onChange } = props;
+  const subDicts = [];
+  subDicts.push('none');
+  if (dictList !== undefined && dictList.length > 0) {
+    let item;
+    for (item in dictList) {
+      if (dictList[item].secondLevelDictionary === 1) {
+        subDicts.push(dictList[item].name);
+      }
+    }
+  }
+  let optionItems = [];
+  for (let i = 0; i < subDicts.length; ++i) {
+    if (i === 0) {
+      optionItems.push(<option selected key={ subDicts[i] }>{ subDicts[i] }</option>);
+    } else {
+      optionItems.push(<option key={ subDicts[i] }>{ subDicts[i] }</option>);
+    }
+  }
+
+  function selectedValue(e) {
+    onChange(e.target.value);
+  }
+
+  // When the subDictFlag is true, we need to disable selection of
+  // the sub-dictionary flag
+  return (
+    <select disabled={ subDictFlag } onChange={ selectedValue }>
+      { optionItems }
+    </select>
+  );
+}
+
+export default class ManageDictionaries extends React.Component {
+  constructor(props, context) {
+    super(props, context);
+    this.addDictionaryElementRow = this.addDictionaryElementRow.bind(this);
+    this.addDictionaryRow = this.addDictionaryRow.bind(this);
+    this.addReplaceDictionaryRequest = this.addReplaceDictionaryRequest.bind(this);
+    this.clickHandler = this.clickHandler.bind(this);
+    this.deleteDictionaryElementRow = this.deleteDictionaryElementRow.bind(this);
+    this.deleteDictionaryRequest = this.deleteDictionaryRequest.bind(this);
+    this.deleteDictionaryRow = this.deleteDictionaryRow.bind(this);
+    this.fileSelectedHandler = this.fileSelectedHandler.bind(this);
+    this.getDictionaries = this.getDictionaries.bind(this);
+    this.getDictionaryElements = this.getDictionaryElements.bind(this);
+    this.handleClose = this.handleClose.bind(this);
+    this.handleDictionaryRowClick = this.handleDictionaryRowClick.bind(this);
+    this.importCsvData = this.importCsvData.bind(this);
+    this.updateDictionaryElementRow = this.updateDictionaryElementRow.bind(this);
+    this.updateDictionaryElementsRequest = this.updateDictionaryElementsRequest.bind(this);
+    this.updateDictionaryRow = this.updateDictionaryRow.bind(this);
+    this.readOnly = props.readOnly !== undefined ? props.readOnly : false;
+    this.state = {
+      show: true,
+      currentSelectedDictionary: null,
+      exportFilename: '',
+      content: null,
+      dictionaryElements: [],
+      tableIcons: {
+        Add: forwardRef((props, ref) => <AddBox { ...props } ref={ ref }/>),
+        Delete: forwardRef((props, ref) => <DeleteOutline { ...props } ref={ ref }/>),
+        DetailPanel: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+        Edit: forwardRef((props, ref) => <Edit { ...props } ref={ ref }/>),
+        Check: forwardRef((props, ref) => <Check { ...props } ref={ ref }/>),
+        Clear: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+        Export: forwardRef((props, ref) => <VerticalAlignBottomIcon { ...props } ref={ ref }/>),
+        Filter: forwardRef((props, ref) => <FilterList { ...props } ref={ ref }/>),
+        FirstPage: forwardRef((props, ref) => <FirstPage { ...props } ref={ ref }/>),
+        LastPage: forwardRef((props, ref) => <LastPage { ...props } ref={ ref }/>),
+        NextPage: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+        PreviousPage: forwardRef((props, ref) => <ChevronLeft { ...props } ref={ ref }/>),
+        ResetSearch: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+        Search: forwardRef((props, ref) => <Search { ...props } ref={ ref }/>),
+        SortArrow: forwardRef((props, ref) => <ArrowUpward { ...props } ref={ ref }/>),
+        ThirdStateCheck: forwardRef((props, ref) => <Remove { ...props } ref={ ref }/>),
+        ViewColumn: forwardRef((props, ref) => <ViewColumn { ...props } ref={ ref }/>)
+      },
+      dictColumns: [
+        {
+          title: "Dictionary Name", field: "name", editable: 'onAdd',
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Sub Dictionary ?", field: "secondLevelDictionary", lookup: { 0: 'No', 1: 'Yes' },
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Dictionary Type", field: "subDictionaryType", lookup: { string: 'string', number: 'number' },
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Updated By", field: "updatedBy", editable: 'never',
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Last Updated Date", field: "updatedDate", editable: 'never',
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        }
+      ],
+      dictElementColumns: [
+        {
+          title: "Element Short Name", field: "shortName", editable: 'onAdd',
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Element Name", field: "name",
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Element Description", field: "description",
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Element Type", field: "type",
+          editComponent: props => (
+            <div>
+              <SelectSubDictType value={ props.value } onChange={ props.onChange }/>
+            </div>
+          ),
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Sub-Dictionary", field: "subDictionary",
+          editComponent: props => (
+            <div>
+              <SubDict value={ props.value } onChange={ props.onChange }/>
+            </div>
+          ),
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Updated By", field: "updatedBy", editable: 'never',
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        },
+        {
+          title: "Updated Date", field: "updatedDate", editable: 'never',
+          cellStyle: cellStyle,
+          headerStyle: headerStyle
+        }
+      ]
+    }
+  }
+
+  componentDidMount() {
+    this.getDictionaries();
+  }
+
+  getDictionaries() {
+    TemplateMenuService.getDictionary().then(arrayOfdictionaries => {
+      this.setState({ dictionaries: arrayOfdictionaries, currentSelectedDictionary: null })
+      // global variable setting used functional components in this file
+      dictList = arrayOfdictionaries;
+    }).catch(() => {
+      console.error('Failed to retrieve dictionaries');
+      this.setState({ dictionaries: [], currentSelectedDictionary: null })
+    });
+  }
+
+  getDictionaryElements(dictionaryName) {
+    TemplateMenuService.getDictionaryElements(dictionaryName).then(dictionaryElements => {
+      this.setState({ dictionaryElements: dictionaryElements.dictionaryElements });
+      this.setState({ currentSelectDictionary: dictionaryName });
+    }).catch(() => console.error('Failed to retrieve dictionary elements'))
+  }
+
+  clickHandler(rowData) {
+    this.getDictionaries();
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    this.props.history.push('/');
+  }
+
+  addReplaceDictionaryRequest(dictionaryEntry) {
+    TemplateMenuService.insDictionary(dictionaryEntry)
+      .then(resp => {
+        this.getDictionaries();
+      })
+      .catch(() => console.error('Failed to insert new dictionary elements'));
+  }
+
+  updateDictionaryElementsRequest(dictElements) {
+    let reqData = { "name": this.state.currentSelectedDictionary, 'dictionaryElements': dictElements };
+    TemplateMenuService.insDictionaryElements(reqData)
+      .then(resp => {
+        this.getDictionaryElements(this.state.currentSelectedDictionary)
+      })
+      .catch(() => console.error('Failed to update dictionary elements'));
+  }
+
+  deleteDictionaryRequest(dictionaryName) {
+    TemplateMenuService.deleteDictionary(dictionaryName)
+      .then(resp => {
+        this.getDictionaries();
+      })
+      .catch(() => console.error('Failed to delete dictionary'));
+  }
+
+  deleteDictionaryElementRequest(dictionaryName, elemenetShortName) {
+    TemplateMenuService.deleteDictionaryElements({ 'name': dictionaryName, 'shortName': elemenetShortName })
+      .then(resp => {
+        this.getDictionaryElements(dictionaryName);
+      })
+      .catch(() => console.error('Failed to delete dictionary elements'));
+  }
+
+  fileSelectedHandler = (event) => {
+
+    if (event.target.files[0].type === 'text/csv' || event.target.files[0].type === 'application/vnd.ms-excel') {
+      if (event.target.files && event.target.files[0]) {
+        const reader = new FileReader();
+        reader.onload = (e) => {
+          let errorMessages = this.importCsvData(reader.result);
+          if (errorMessages !== '') {
+            alert(errorMessages);
+          }
+        }
+        reader.readAsText(event.target.files[0]);
+      }
+    } else {
+      alert('Please upload .csv extention files only.');
+    }
+  }
+
+  importCsvData(rawCsvData) {
+
+    const jsonKeyNames = ['shortName', 'name', 'description', 'type', 'subDictionary'];
+    const userHeaderNames = ['Element Short Name', 'Element Name', 'Element Description', 'Element Type', 'Sub-Dictionary'];
+    const validTypes = ['string', 'number', 'datetime', 'json', 'map'];
+
+    let mandatory;
+
+    if (subDictFlag) {
+      mandatory = [true, true, true, false, false];
+    } else {
+      mandatory = [true, true, true, true, false];
+    }
+
+    let result = CsvToJson(rawCsvData, ',', '||||', userHeaderNames, jsonKeyNames, mandatory);
+
+    let errorMessages = result.errorMessages;
+    let jsonObjArray = result.jsonObjArray;
+
+    let validTypesErrorMesg = '';
+
+    for (let i = 0; i < validTypes.length; ++i) {
+      if (i === 0) {
+        validTypesErrorMesg = validTypes[i];
+      } else {
+        validTypesErrorMesg += ',' + validTypes[i];
+      }
+    }
+
+    if (errorMessages !== '') {
+      return errorMessages;
+    }
+
+    // Perform further checks on data that is now in JSON form
+    let subDictionaries = [];
+
+    // NOTE: dictList is a global variable  maintained faithfully
+    //       by the getDictionaries() method outside this import
+    //       functionality.
+    let item;
+    for (item in dictList) {
+      if (dictList[item].secondLevelDictionary === 1) {
+        subDictionaries.push(dictList[item].name);
+      }
+    }
+    ;
+
+    // Check for valid Sub-Dictionary and Element Type values
+    subDictionaries = subDictionaries.toString();
+    let row = 2;
+    let dictElem;
+    for (dictElem of jsonObjArray) {
+      let itemKey;
+      for (itemKey in dictElem) {
+        let value = dictElem[itemKey].trim();
+        let keyIndex = jsonKeyNames.indexOf(itemKey);
+        if (itemKey === 'shortName' && /[^a-zA-Z0-9-_.]/.test(value)) {
+          errorMessages += '\n' + userHeaderNames[keyIndex] +
+            ' at row #' + row +
+            ' can only contain alphanumeric characters and periods, hyphens or underscores';
+        }
+        if (itemKey === 'type' && validTypes.indexOf(value) < 0) {
+          errorMessages += '\nInvalid value of "' + value + '" for "' + userHeaderNames[keyIndex] + '" at row #' + row;
+          errorMessages += '\nValid types are: ' + validTypesErrorMesg;
+        }
+        if (value !== "" && itemKey === 'subDictionary' && subDictionaries.indexOf(value) < 0) {
+          errorMessages += '\nInvalid Sub-Dictionary value of "' + value + '" at row #' + row;
+        }
+      }
+      ++row;
+    }
+    if (errorMessages === '') {
+      // We made it through all the checks. Send it to back end
+      this.updateDictionaryElementsRequest(jsonObjArray);
+    }
+
+    return errorMessages;
+  }
+
+  addDictionaryRow(newData) {
+    let validData = true;
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        if (/[^a-zA-Z0-9-_.]/.test(newData.name)) {
+          validData = false;
+          alert('Please enter alphanumeric input. Only allowed special characters are:(period, hyphen, underscore)');
+          reject();
+        }
+        for (let i = 0; i < this.state.dictionaries.length; i++) {
+          if (this.state.dictionaries[i].name === newData.name) {
+            validData = false;
+            alert(newData.name + ' dictionary name already exists')
+            reject();
+          }
+        }
+        if (validData) {
+          this.addReplaceDictionaryRequest(newData);
+        }
+        resolve();
+      }, 1000);
+    });
+  }
+
+
+  updateDictionaryRow(newData, oldData) {
+    let validData = true;
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        if (/[^a-zA-Z0-9-_.]/.test(newData.name)) {
+          validData = false;
+          alert('Please enter alphanumberic input. Only allowed special characters are:(period, hyphen, underscore)');
+          reject();
+        }
+        if (validData) {
+          this.addReplaceDictionaryRequest(newData);
+        }
+        resolve();
+      }, 1000);
+    });
+  }
+
+  deleteDictionaryRow(oldData) {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        this.deleteDictionaryRequest(oldData.name);
+        resolve();
+      }, 1000);
+    });
+  }
+
+  addDictionaryElementRow(newData) {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        let dictionaryElements = this.state.dictionaryElements;
+        let errorMessages = '';
+        for (let i = 0; i < this.state.dictionaryElements.length; i++) {
+          if (this.state.dictionaryElements[i].shortName === newData.shortName) {
+            alert('Short Name "' + newData.shortName + '" already exists');
+            reject("");
+          }
+        }
+        // MaterialTable returns no property at all if the user has not touched a
+        // new column, so we want to add the property with an emptry string
+        // for several cases if that is the case to simplify other checks.
+        if (newData.description === undefined) {
+          newData.description = "";
+        }
+        if (newData.subDictionary === undefined) {
+          newData.subDictionary = null;
+        }
+        if (newData.type === undefined) {
+          newData.type = "";
+        }
+        if (!newData.shortName && /[^a-zA-Z0-9-_.]/.test(newData.shortName)) {
+          errorMessages += '\nShort Name is limited to alphanumeric characters and also period, hyphen, and underscore';
+        }
+        if (!newData.shortName) {
+          errorMessages += '\nShort Name must be specified';
+        }
+        if (!newData.name) {
+          errorMessages += '\nElement Name must be specified';
+        }
+        if (!newData.type && !subDictFlag) {
+          errorMessages += '\nElement Type must be specified';
+        }
+        if (errorMessages === '') {
+          dictionaryElements.push(newData);
+          this.updateDictionaryElementsRequest([newData]);
+          resolve();
+        } else {
+          alert(errorMessages);
+          reject("");
+        }
+      }, 1000);
+    });
+  }
+
+  updateDictionaryElementRow(newData, oldData) {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        let dictionaryElements = this.state.dictionaryElements;
+        let validData = true;
+        if (!newData.type) {
+          validData = false;
+          alert('Element Type cannot be null');
+          reject();
+        }
+        if (validData) {
+          const index = dictionaryElements.indexOf(oldData);
+          dictionaryElements[index] = newData;
+          this.updateDictionaryElementsRequest([newData]);
+        }
+        resolve();
+      }, 1000);
+    });
+  }
+
+
+  deleteDictionaryElementRow(oldData) {
+    return new Promise((resolve) => {
+      setTimeout(() => {
+        this.deleteDictionaryElementRequest(this.state.currentSelectedDictionary, oldData.shortName);
+        resolve();
+      }, 1000);
+    });
+  }
+
+  handleDictionaryRowClick(event, rowData) {
+    subDictFlag = rowData.secondLevelDictionary === 1 ? true : false;
+    this.setState({
+      currentSelectedDictionary: rowData.name,
+      exportFilename: rowData.name
+    })
+    this.getDictionaryElements(rowData.name);
+  }
+
+  render() {
+    return (
+      <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+          <Modal.Title>Manage Dictionaries</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          { this.state.currentSelectedDictionary === null ?
+            <MaterialTable
+              title={ "Dictionary List" }
+              data={ this.state.dictionaries }
+              columns={ this.state.dictColumns }
+              icons={ this.state.tableIcons }
+              onRowClick={ this.handleDictionaryRowClick }
+              options={ {
+                headerStyle: rowHeaderStyle,
+              } }
+              editable={ !this.readOnly ?
+                {
+                  onRowAdd: this.addDictionaryRow,
+                  onRowUpdate: this.updateDictionaryRow,
+                  onRowDelete: this.deleteDictionaryRow
+                } : undefined }
+            /> : null
+          }
+          { this.state.currentSelectedDictionary !== null ?
+            <MaterialTable
+              title={ 'Dictionary Elements List for ' + (subDictFlag ? 'Sub-Dictionary "' : '"') + this.state.currentSelectedDictionary + '"' }
+              data={ this.state.dictionaryElements }
+              columns={ this.state.dictElementColumns }
+              icons={ this.state.tableIcons }
+              options={ {
+                exportAllData: true,
+                exportButton: true,
+                exportFileName: this.state.exportFilename,
+                headerStyle: { backgroundColor: 'white', fontSize: '15pt', text: 'bold', border: '1px solid black' }
+              } }
+              components={ {
+                Toolbar: props => (
+                  <Row>
+                    <Col sm="11">
+                      <MTableToolbarStyled { ...props } />
+                    </Col>
+                    <ColPullLeftStyled sm="1">
+                      <Tooltip title="Import" placement="bottom">
+                        <IconButton aria-label="import" disabled={ this.readOnly } onClick={ () => this.fileUpload.click() }>
+                          <VerticalAlignTopIcon/>
+                        </IconButton>
+                      </Tooltip>
+                      <input type="file" ref={ (fileUpload) => {
+                        this.fileUpload = fileUpload;
+                      } }
+                             style={ { visibility: 'hidden', width: '1px' } } onChange={ this.fileSelectedHandler }/>
+                    </ColPullLeftStyled>
+                  </Row>
+                )
+              } }
+              editable={ !this.readOnly ?
+                {
+                  onRowAdd: this.addDictionaryElementRow,
+                  onRowUpdate: this.updateDictionaryElementRow,
+                  onRowDelete: this.deleteDictionaryElementRow
+                } : undefined
+              }
+            /> : null
+          }
+          { this.state.currentSelectedDictionary !== null ? <button onClick={ this.clickHandler } style={ { marginTop: '25px' } }>Go Back to Dictionaries List</button> : "" }
+        </Modal.Body>
+        <Modal.Footer>
+          <Button variant="secondary" type="null" onClick={ this.handleClose }>Close</Button>
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/ManageDictionaries.test.js b/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/ManageDictionaries.test.js
new file mode 100644
index 0000000..2552d7a
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/ManageDictionaries.test.js
@@ -0,0 +1,465 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
+import { render } from 'enzyme';
+import ManageDictionaries from './ManageDictionaries';
+import TemplateMenuService from '../../../api/TemplateService'
+
+const TestDictionaryElements = {
+  name: "test",
+  secondLevelDictionary: 0,
+  subDictionaryType: "",
+  dictionaryElements: [
+    {
+      shortName: "alertType",
+      name: "Alert Type",
+      description: "Type of Alert",
+      type: "string",
+      subDictionary: "",
+      createdDate: "2020-06-12T13:58:51.443931Z",
+      updatedDate: "2020-06-13T16:27:57.084870Z",
+      updatedBy: "admin",
+      createdBy: "admin"
+    }
+  ]
+};
+
+const TestDictionaries =
+  [
+    {
+      name: "test",
+      secondLevelDictionary: 0,
+      subDictionaryType: "string",
+      dictionaryElements: [TestDictionaryElements],
+      createdDate: "2020-06-14T21:00:33.231166Z",
+      updatedDate: "2020-06-14T21:00:33.231166Z",
+      updatedBy: "admin",
+      createdBy: "admin"
+    },
+    {
+      name: "testSub1",
+      secondLevelDictionary: 1,
+      subDictionaryType: "string",
+      dictionaryElements: [
+        {
+          shortName: "subElem",
+          name: "Sub Element",
+          description: "Sub Element Description",
+          type: "string",
+          createdDate: "2020-06-14T21:04:44.402287Z",
+          updatedDate: "2020-06-14T21:04:44.402287Z",
+          updatedBy: "admin",
+          createdBy: "admin"
+        }
+      ],
+      createdDate: "2020-06-14T21:01:16.390250Z",
+      updatedDate: "2020-06-14T21:01:16.390250Z",
+      updatedBy: "admin",
+      createdBy: "admin"
+    }
+  ];
+
+
+const historyMock = { push: jest.fn() };
+
+let errorMessage = '';
+
+window.alert = jest.fn().mockImplementation((mesg) => {
+  errorMessage = mesg;
+  return
+});
+
+TemplateMenuService.getDictionary = jest.fn().mockImplementation(() => {
+  return Promise.resolve(TestDictionaries);
+});
+
+TemplateMenuService.insDictionary = jest.fn().mockImplementation(() => {
+  return Promise.resolve({ ok: true, status: 200 });
+});
+
+TemplateMenuService.deleteDictionary = jest.fn().mockImplementation(() => {
+  return Promise.resolve("200");
+});
+
+TemplateMenuService.getDictionaryElements = jest.fn().mockImplementation(() => {
+  return Promise.resolve(TestDictionaryElements);
+});
+
+TemplateMenuService.deleteDictionaryElements = jest.fn().mockImplementation(() => {
+  return Promise.resolve("200");
+});
+
+TemplateMenuService.insDictionaryElements = jest.fn().mockImplementation(() => {
+  return Promise.resolve("200");
+});
+
+
+describe('Verify ManageDictionaries', () => {
+
+  beforeEach(() => {
+    fetch.resetMocks();
+  });
+
+  it('Test API Successful', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "name": "vtest",
+            "secondLevelDictionary": 1,
+            "subDictionaryType": "string",
+            "updatedBy": "test",
+            "updatedDate": "05-07-2019 19:09:42"
+          });
+        }
+      });
+    });
+    const component = shallow(<ManageDictionaries/>);
+    expect(component).toMatchSnapshot();
+  });
+
+  it('Test API Exception', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: false,
+        status: 500,
+        json: () => {
+          return Promise.resolve({
+            "name": "vtest",
+            "secondLevelDictionary": 1,
+            "subDictionaryType": "string",
+            "updatedBy": "test",
+            "updatedDate": "05-07-2019 19:09:42"
+          });
+        }
+      });
+    });
+    const component = shallow(<ManageDictionaries/>);
+  });
+
+  it('Test Table icons', () => {
+
+    const component = mount(<ManageDictionaries/>);
+    expect(component.find('[className="MuiSelect-icon MuiTablePagination-selectIcon"]')).toBeTruthy();
+  });
+
+  test('Test add/replace and delete dictionary requests', async () => {
+
+    const component = shallow(<ManageDictionaries history={ historyMock }/>)
+    const instance = component.instance();
+
+    const flushPromises = () => new Promise(setImmediate);
+
+    instance.addReplaceDictionaryRequest({ name: "newdict", secondLevelDictionary: 0, subDictionaryType: "string" });
+    instance.deleteDictionaryRequest("test");
+
+    await flushPromises();
+
+    expect(component.state('currentSelectedDictionary')).toEqual(null);
+    expect(component.state('dictionaries')).toEqual(TestDictionaries);
+  });
+
+  test('Test update dictionary row', async () => {
+
+    const component = shallow(<ManageDictionaries history={ historyMock }/>)
+    const instance = component.instance();
+    const rowData = { name: "newdict", secondLevelDictionary: 0, subDictionaryType: "string" };
+
+    await expect(instance.updateDictionaryRow(rowData, rowData)).resolves.toEqual(undefined);
+
+  }, 2000);
+
+  test('Test add dictionary row', async () => {
+
+    const addReplaceRequest = jest.spyOn(ManageDictionaries.prototype, 'addReplaceDictionaryRequest');
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    const rowData = { name: "newdict", secondLevelDictionary: 0, subDictionaryType: "string" };
+
+    await instance.addDictionaryRow(rowData);
+    expect(addReplaceRequest).toHaveBeenCalledWith(rowData);
+
+  }, 2000);
+
+  test('Test add dictionary row with errors name already exists', async () => {
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    let rowData = { name: "test", secondLevelDictionary: 0, subDictionaryType: "" };
+
+    await expect(instance.addDictionaryRow(rowData)).rejects.toEqual(undefined);
+
+  }, 2000);
+
+  test('Test add dictionary row with errors illegal chars in name', async () => {
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    let rowData = { name: "test@@", secondLevelDictionary: 0, subDictionaryType: "" };
+
+    await expect(instance.addDictionaryRow(rowData)).rejects.toEqual(undefined);
+
+  }, 2000);
+
+  test('Test update dictionary row with errors illegal chars in name', async () => {
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    let rowData = { name: "test@@", secondLevelDictionary: 0, subDictionaryType: "" };
+
+    await expect(instance.updateDictionaryRow(rowData)).rejects.toEqual(undefined);
+  });
+
+
+  test('Test add dictionary row with errors (illegal chars)', async () => {
+
+    const addReplaceRequest = jest.spyOn(ManageDictionaries.prototype, 'addReplaceDictionaryRequest');
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    let rowData = { name: "test@@", secondLevelDictionary: 0, subDictionaryType: "" };
+
+    await expect(instance.addDictionaryRow(rowData)).rejects.toEqual(undefined);
+
+  }, 2000);
+
+
+  test('Test delete dictionary row', async () => {
+
+    const deleteRequest = jest.spyOn(ManageDictionaries.prototype, 'deleteDictionaryRequest');
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    const rowData = { name: "newdict", secondLevelDictionary: 0, subDictionaryType: "string" };
+
+    await instance.deleteDictionaryRow(rowData);
+    expect(deleteRequest).toHaveBeenCalledWith("newdict");
+
+  }, 2000);
+
+  test('Test handle select dictionary row click', async () => {
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    const rowData = { name: "newdict", secondLevelDictionary: 0, subDictionaryType: "string" };
+
+    instance.handleDictionaryRowClick("event", rowData);
+    expect(component.state('currentSelectedDictionary')).toEqual("newdict");
+  }, 2000);
+
+  test('Test dictionary element row add, update, delete', async () => {
+
+    const rowData = {
+      createdBy: "admin",
+      createdDate: "2020-06-15T13:59:20.467381Z",
+      description: "Description",
+      name: "Some Elem",
+      shortName: "someElem",
+      type: "string",
+      updatedBy: "admin",
+      updatedDate: "2020-06-15T13:59:20.467381Z"
+    };
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+
+    const badRowData = {
+      description: "Description",
+      name: "Some Elem",
+      shortName: "someElem",
+      type: "string"
+    };
+
+    await instance.clickHandler();
+    await instance.getDictionaryElements("test");
+
+    await expect(instance.addDictionaryElementRow(rowData)).resolves.toEqual(undefined);
+    await expect(instance.updateDictionaryElementRow(rowData, rowData)).resolves.toEqual(undefined);
+    await expect(instance.deleteDictionaryElementRow(rowData)).resolves.toEqual(undefined);
+  });
+
+  test('Test dictionary element row add with errors', async () => {
+
+    const badRowData = {
+      description: "",
+      name: "",
+      shortName: "some#Elem",
+      type: ""
+    };
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+
+    await expect(instance.addDictionaryElementRow(badRowData)).rejects.toEqual("");
+  });
+
+  test('Test dictionary element update with error illegal name', async () => {
+
+    const badRowData = {
+      description: "",
+      name: "test@@",
+      shortName: "some#Elem",
+      type: ""
+    };
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+
+    await expect(instance.updateDictionaryElementRow(badRowData)).rejects.toEqual(undefined);
+  });
+
+  test('Test dictionary element addition with duplicate name error', async () => {
+
+    const badRowData = {
+      description: "description",
+      name: "Alert Type",
+      shortName: "alertType",
+      type: "string"
+    };
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+
+    component.setState({ currentSelectedDictionary: 'test' });
+
+    await instance.getDictionaryElements();
+    await expect(instance.addDictionaryElementRow(badRowData)).rejects.toEqual("");
+  });
+
+  test('Test dictionary element addition with empty name error', async () => {
+
+    const badRowData = {
+      description: "description",
+      name: "Alert Type",
+      shortName: "",
+      type: "string"
+    };
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+
+    component.setState({ currentSelectedDictionary: 'test' });
+
+    await instance.getDictionaryElements();
+    await expect(instance.addDictionaryElementRow(badRowData)).rejects.toEqual("");
+  });
+
+
+  it('Test Import CSV Sunny Day', async () => {
+
+    TemplateMenuService.insDictionaryElements = jest.fn().mockImplementation(() => {
+      return Promise.resolve({ ok: true, status: 200 });
+    });
+
+    let rawCsvData = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsvData += '"alertType","Alert Type","Alert Type Description","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let expectedResult = [
+      {
+        description: "Alert Type Description",
+        name: "Alert Type",
+        shortName: "alertType",
+        subDictionary: "",
+        type: "string"
+      }
+    ];
+
+    const updateDictionaryElementsRequest = jest.spyOn(ManageDictionaries.prototype, 'updateDictionaryElementsRequest');
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+
+    await expect(instance.importCsvData(rawCsvData)).toEqual('');
+    expect(updateDictionaryElementsRequest).toHaveBeenCalledWith(expectedResult);
+  });
+
+  it('Test Import CSV Mandatory Field Check Errors', () => {
+
+    let rawCsvData = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsvData += '"","","","","","",""';
+
+    // The empty values for all the fields in row 1 of the rawCsvData will trigger a bunch of errors.
+    // Getting Enzyme to properly match them with embedded newlines turned out to be impossible
+    // and maybe not desirable anyway; so our test for "success" here is simply that the
+    // routine returns a non-empty error string.
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+    expect(instance.importCsvData(rawCsvData)).not.toEqual('');
+  });
+
+  it('Test Import CSV Errors in Row Data', async () => {
+
+    TemplateMenuService.insDictionaryElements = jest.fn().mockImplementation(() => {
+      return Promise.resolve({ ok: true, status: 200 });
+    });
+
+    let rawCsvData = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsvData += '"alert@Type","Alert Type","Alert Type Description","strin","subby","admin","2020-06-11T13:56:14.927437Z"';
+
+    let expectedResult = [
+      {
+        description: "Alert Type Description",
+        name: "Alert Type",
+        shortName: "alertType",
+        subDictionary: "",
+        type: "string"
+      }
+    ];
+
+    const updateDictionaryElementsRequest = jest.spyOn(ManageDictionaries.prototype, 'updateDictionaryElementsRequest');
+
+    const component = shallow(<ManageDictionaries/>)
+    const instance = component.instance();
+
+    await expect(instance.importCsvData(rawCsvData)).not.toEqual('');
+  });
+
+
+  it('Test handleClose', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "name": "vtest",
+            "secondLevelDictionary": 1,
+            "subDictionaryType": "string",
+            "updatedBy": "test",
+            "updatedDate": "05-07-2019 19:09:42"
+          });
+        }
+      });
+    });
+    const handleClose = jest.spyOn(ManageDictionaries.prototype, 'handleClose');
+    const component = shallow(<ManageDictionaries history={ historyMock }/>)
+    component.find('[variant="secondary"]').prop('onClick')();
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+    handleClose.mockClear();
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/__snapshots__/ManageDictionaries.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/__snapshots__/ManageDictionaries.test.js.snap
new file mode 100644
index 0000000..6b58363
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ManageDictionaries/__snapshots__/ManageDictionaries.test.js.snap
@@ -0,0 +1,196 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify ManageDictionaries Test API Successful 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="xl"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      Manage Dictionaries
+    </ModalTitle>
+  </ModalHeader>
+  <ModalBody>
+    <WithStyles(Component)
+      columns={
+        Array [
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "editable": "onAdd",
+            "field": "name",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "Dictionary Name",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "field": "secondLevelDictionary",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "lookup": Object {
+              "0": "No",
+              "1": "Yes",
+            },
+            "title": "Sub Dictionary ?",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "field": "subDictionaryType",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "lookup": Object {
+              "number": "number",
+              "string": "string",
+            },
+            "title": "Dictionary Type",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "editable": "never",
+            "field": "updatedBy",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "Updated By",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "editable": "never",
+            "field": "updatedDate",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "Last Updated Date",
+          },
+        ]
+      }
+      editable={
+        Object {
+          "onRowAdd": [Function],
+          "onRowDelete": [Function],
+          "onRowUpdate": [Function],
+        }
+      }
+      icons={
+        Object {
+          "Add": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Check": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Clear": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Delete": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "DetailPanel": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Edit": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Export": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Filter": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "FirstPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "LastPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "NextPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "PreviousPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "ResetSearch": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Search": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "SortArrow": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "ThirdStateCheck": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "ViewColumn": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+        }
+      }
+      onRowClick={[Function]}
+      options={
+        Object {
+          "headerStyle": Object {
+            "backgroundColor": "#ddd",
+            "border": "1px solid black",
+            "fontSize": "15pt",
+            "text": "bold",
+          },
+        }
+      }
+      title="Dictionary List"
+    />
+  </ModalBody>
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="null"
+      variant="secondary"
+    >
+      Close
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/PerformActions.js b/gui-clamp/ui-react/src/components/dialogs/PerformActions.js
new file mode 100644
index 0000000..ba7c99d
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/PerformActions.js
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import LoopActionService from '../../api/LoopActionService';
+
+
+export default class PerformActions extends React.Component {
+  state = {
+    loopName: this.props.loopCache.getLoopName(),
+    loopAction: this.props.loopAction
+  };
+
+  constructor(props, context) {
+    super(props, context);
+    this.refreshStatus = this.refreshStatus.bind(this);
+  }
+
+  componentWillReceiveProps(newProps) {
+    this.setState({
+      loopName: newProps.loopCache.getLoopName(),
+      loopAction: newProps.loopAction
+    });
+  }
+
+  componentDidMount() {
+    const action = this.state.loopAction;
+    const loopName = this.state.loopName;
+
+    if (action === 'delete') {
+      if (window.confirm('You are about to remove Control Loop Model "' + loopName +
+        '". Select OK to continue with deletion or Cancel to keep the model.') === false) {
+        return;
+      }
+    }
+
+    this.props.setBusyLoading(); // Alert top level to start block user clicks
+
+    LoopActionService.performAction(loopName, action)
+      .then(pars => {
+        this.props.showSucAlert("Action " + action + " successfully performed");
+        if (action === 'delete') {
+          this.props.updateLoopFunction(null);
+          this.props.history.push('/');
+        } else {
+          // refresh status and update loop logs
+          this.refreshStatus(loopName);
+        }
+      })
+      .catch(error => {
+        this.props.showFailAlert("Action " + action + " failed");
+        // refresh status and update loop logs
+        this.refreshStatus(loopName);
+      })
+      .finally(() => this.props.clearBusyLoading());
+  }
+
+  refreshStatus(loopName) {
+
+    this.props.setBusyLoading();
+
+    LoopActionService.refreshStatus(loopName)
+      .then(data => {
+        this.props.updateLoopFunction(data);
+        this.props.history.push('/');
+      })
+      .catch(error => {
+        this.props.history.push('/');
+      })
+      .finally(() => this.props.clearBusyLoading());
+  }
+
+  render() {
+    return null;
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/PerformActions.test.js b/gui-clamp/ui-react/src/components/dialogs/PerformActions.test.js
new file mode 100644
index 0000000..74c9145
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/PerformActions.test.js
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import PerformActions from './PerformActions';
+import LoopCache from '../../api/LoopCache';
+import LoopActionService from '../../api/LoopActionService';
+
+describe('Verify PerformActions', () => {
+
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca"
+  });
+
+  it('Test the render method action failed', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const historyMock = { push: jest.fn() };
+    const updateLoopFunction = jest.fn();
+    const showSucAlert = jest.fn();
+    const showFailAlert = jest.fn();
+    const setBusyLoading = jest.fn();
+    const clearBusyLoading = jest.fn();
+
+    LoopActionService.refreshStatus = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+    const component = shallow(<PerformActions loopCache={ loopCache }
+                                              loopAction="submit" history={ historyMock } updateLoopFunction={ updateLoopFunction } showSucAlert={ showSucAlert } showFailAlert={ showFailAlert }
+                                              setBusyLoading={ setBusyLoading } clearBusyLoading={ clearBusyLoading }/>)
+    await flushPromises();
+    component.update();
+
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Test the render method action successful', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const historyMock = { push: jest.fn() };
+    const updateLoopFunction = jest.fn();
+    const showSucAlert = jest.fn();
+    const showFailAlert = jest.fn();
+    const setBusyLoading = jest.fn();
+    const clearBusyLoading = jest.fn();
+
+    LoopActionService.performAction = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+    LoopActionService.refreshStatus = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+    const component = shallow(<PerformActions loopCache={ loopCache }
+                                              loopAction="submit" history={ historyMock } updateLoopFunction={ updateLoopFunction } showSucAlert={ showSucAlert } showFailAlert={ showFailAlert }
+                                              setBusyLoading={ setBusyLoading } clearBusyLoading={ clearBusyLoading }/>)
+    await flushPromises();
+    component.update();
+
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyEditor.js b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyEditor.js
new file mode 100644
index 0000000..525d66b
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyEditor.js
@@ -0,0 +1,217 @@
+/*-
+ * ============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 from 'react'
+import PolicyToscaService from '../../../api/PolicyToscaService';
+import { JSONEditor } from '@json-editor/json-editor/dist/nonmin/jsoneditor.js';
+import "@fortawesome/fontawesome-free/css/all.css"
+import styled from 'styled-components';
+import Button from 'react-bootstrap/Button';
+import TextField from '@material-ui/core/TextField';
+import Alert from 'react-bootstrap/Alert';
+import PolicyService from '../../../api/PolicyService';
+import OnapUtils from '../../../utils/OnapUtils';
+import uuid from 'react-uuid';
+
+//const JSONEditor = require("@json-editor/json-editor").JSONEditor;
+const DivWhiteSpaceStyled = styled.div`
+  white-space: pre;
+`
+
+const JsonEditorDiv = styled.div`
+  margin-top: 20px;
+  background-color: ${ props => props.theme.loopViewerBackgroundColor };
+  text-align: justify;
+  font-size: ${ props => props.theme.policyEditorFontSize };
+  border: 1px solid #C0C0C0;
+`
+const PanelDiv = styled.div`
+  margin-top: 20px;
+  text-align: justify;
+  font-size: ${ props => props.theme.policyEditorFontSize };
+  background-color: ${ props => props.theme.loopViewerBackgroundColor };
+`
+
+export default class PolicyEditor extends React.Component {
+
+  state = {
+    policyModelType: this.props.policyModelType,
+    policyModelTypeVersion: this.props.policyModelTypeVersion,
+    policyName: (typeof this.props.policyName !== "undefined") ? this.props.policyName : "org.onap.policy.new",
+    policyVersion: (typeof this.props.policyVersion !== "undefined") ? this.props.policyVersion : "0.0.1",
+    policyProperties: this.props.policyProperties,
+    showSuccessAlert: false,
+    showFailAlert: false,
+    jsonEditor: null,
+    jsonEditorDivId: uuid(),
+  }
+
+  constructor(props, context) {
+    super(props, context);
+    this.createJsonEditor = this.createJsonEditor.bind(this);
+    this.getToscaModelForPolicy = this.getToscaModelForPolicy.bind(this);
+    this.disableAlert = this.disableAlert.bind(this);
+    this.handleCreateNewVersion = this.handleCreateNewVersion.bind(this);
+    this.handleChangePolicyName = this.handleChangePolicyName.bind(this);
+    this.handleChangePolicyVersion = this.handleChangePolicyVersion.bind(this);
+  }
+
+  disableAlert() {
+    this.setState({ showSuccessAlert: false, showFailAlert: false });
+  }
+
+  customValidation(editorData) {
+    // method for sub-classes to override with customized validation
+    return [];
+  }
+
+  handleCreateNewVersion() {
+    var editorData = this.state.jsonEditor.getValue();
+    var errors = this.state.jsonEditor.validate();
+    errors = errors.concat(this.customValidation(editorData));
+
+    if (errors.length !== 0) {
+      console.error("Errors detected during policy data validation ", errors);
+      this.setState({
+        showFailAlert: true,
+        showMessage: 'Errors detected during policy data validation:\n' + OnapUtils.jsonEditorErrorFormatter(errors)
+      });
+      return;
+    } else {
+      console.info("NO validation errors found in policy data");
+      PolicyService.createNewPolicy(this.state.policyModelType, this.state.policyModelTypeVersion,
+        this.state.policyName, this.state.policyVersion, editorData).then(respPolicyCreation => {
+        if (typeof (respPolicyCreation) === "undefined") {
+          //it indicates a failure
+          this.setState({
+            showFailAlert: true,
+            showMessage: 'Policy Creation Failure'
+          });
+        } else {
+          this.setState({
+            showSuccessAlert: true,
+            showMessage: 'Policy ' + this.state.policyName + '/' + this.state.policyVersion + ' created successfully'
+          });
+          this.props.policyUpdateFunction();
+        }
+      })
+    }
+  }
+
+  bumpVersion(versionToBump) {
+    let semVer = versionToBump.split(".");
+    return parseInt(semVer[0]) + 1 + "." + semVer[1] + "." + semVer[2];
+  }
+
+  getToscaModelForPolicy() {
+    PolicyToscaService.getToscaPolicyModel(this.state.policyModelType, this.state.policyModelTypeVersion).then(respJsonPolicyTosca => {
+      if (respJsonPolicyTosca !== {}) {
+        this.setState({
+          jsonSchemaPolicyTosca: respJsonPolicyTosca,
+          jsonEditor: this.createJsonEditor(respJsonPolicyTosca, this.state.policyProperties),
+        })
+      }
+    });
+  }
+
+  componentDidMount() {
+    this.getToscaModelForPolicy();
+  }
+
+  createJsonEditor(toscaModel, editorData) {
+    /*JSONEditor.defaults.themes.myBootstrap4 = JSONEditor.defaults.themes.bootstrap4.extend({
+            getTab: function(text,tabId) {
+                var liel = document.createElement('li');
+                liel.classList.add('nav-item');
+                var ael = document.createElement("a");
+                ael.classList.add("nav-link");
+                ael.setAttribute("style",'padding:10px;max-width:160px;');
+                ael.setAttribute("href", "#" + tabId);
+                ael.setAttribute('data-toggle', 'tab');
+                text.setAttribute("style",'word-wrap:break-word;');
+                ael.appendChild(text);
+                liel.appendChild(ael);
+                return liel;
+            }
+        });*/
+
+    return new JSONEditor(document.getElementById(this.state.jsonEditorDivId),
+      {
+        schema: toscaModel,
+        startval: editorData,
+        //theme: 'myBootstrap4',
+        theme: 'bootstrap4',
+        iconlib: 'fontawesome5',
+        object_layout: 'grid',
+        disable_properties: false,
+        disable_edit_json: false,
+        disable_array_reorder: true,
+        disable_array_delete_last_row: true,
+        disable_array_delete_all_rows: false,
+        array_controls_top: true,
+        keep_oneof_values: false,
+        collapsed: true,
+        show_errors: 'always',
+        display_required_only: false,
+        show_opt_in: false,
+        prompt_before_delete: true,
+        required_by_default: false
+      })
+  }
+
+  handleChangePolicyName(event) {
+    this.setState({
+      policyName: event.target.value,
+    });
+  }
+
+  handleChangePolicyVersion(event) {
+    this.setState({
+      policyVersion: event.target.value,
+    });
+  }
+
+  render() {
+    return (
+      <PanelDiv>
+        <Alert variant="success" show={ this.state.showSuccessAlert } onClose={ this.disableAlert } dismissible>
+          <DivWhiteSpaceStyled>
+            { this.state.showMessage }
+          </DivWhiteSpaceStyled>
+        </Alert>
+        <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+          <DivWhiteSpaceStyled>
+            { this.state.showMessage }
+          </DivWhiteSpaceStyled>
+        </Alert>
+        <TextField required id="policyName" label="Required" defaultValue={ this.state.policyName }
+                   onChange={ this.handleChangePolicyName } variant="outlined" size="small"/>
+        <TextField required id="policyVersion" label="Required" defaultValue={ this.state.policyVersion }
+                   onChange={ this.handleChangePolicyVersion } size="small" variant="outlined"/>
+        <Button variant="secondary" title="Create a new policy version from the defined parameters"
+                onClick={ this.handleCreateNewVersion }>Create New Version</Button>
+        <JsonEditorDiv id={ this.state.jsonEditorDivId } title="Policy Properties"/>
+      </PanelDiv>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.js b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.js
new file mode 100644
index 0000000..105b2c5
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.js
@@ -0,0 +1,364 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP POLICY-CLAMP
+ * ================================================================================
+ * Copyright (C) 2020-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 from 'react'
+import Button from 'react-bootstrap/Button';
+import Form from 'react-bootstrap/Form';
+import Col from 'react-bootstrap/Col';
+import Row from 'react-bootstrap/Row';
+import Select from 'react-select';
+import Modal from 'react-bootstrap/Modal';
+import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+import LoopCache from '../../../api/LoopCache';
+import { JSONEditor } from '@json-editor/json-editor/dist/jsoneditor.js';
+import "@fortawesome/fontawesome-free/css/all.css"
+import Alert from 'react-bootstrap/Alert';
+import OnapConstant from '../../../utils/OnapConstants';
+import OnapUtils from '../../../utils/OnapUtils';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+
+const DivWhiteSpaceStyled = styled.div`
+  white-space: pre;
+`
+
+export default class PolicyModal extends React.Component {
+
+  state = {
+    show: true,
+    loopCache: this.props.loopCache,
+    jsonEditor: null,
+    policyName: this.props.match.params.policyName,
+    // This is to indicate whether it's an operational or config policy (in terms of loop instance)
+    policyInstanceType: this.props.match.params.policyInstanceType,
+    pdpGroup: null,
+    pdpGroupList: [],
+    pdpSubgroupList: [],
+    chosenPdpGroup: '',
+    chosenPdpSubgroup: '',
+    showSucAlert: false,
+    showFailAlert: false
+  };
+
+  constructor(props, context) {
+    super(props, context);
+    this.handleClose = this.handleClose.bind(this);
+    this.handleSave = this.handleSave.bind(this);
+    this.renderJsonEditor = this.renderJsonEditor.bind(this);
+    this.handlePdpGroupChange = this.handlePdpGroupChange.bind(this);
+    this.handlePdpSubgroupChange = this.handlePdpSubgroupChange.bind(this);
+    this.createJsonEditor = this.createJsonEditor.bind(this);
+    this.handleRefresh = this.handleRefresh.bind(this);
+    this.disableAlert = this.disableAlert.bind(this);
+    this.renderPdpGroupDropDown = this.renderPdpGroupDropDown.bind(this);
+    this.renderOpenLoopMessage = this.renderOpenLoopMessage.bind(this);
+    this.renderModalTitle = this.renderModalTitle.bind(this);
+    this.readOnly = props.readOnly !== undefined ? props.readOnly : false;
+  }
+
+  handleSave() {
+    var editorData = this.state.jsonEditor.getValue();
+    var errors = this.state.jsonEditor.validate();
+    errors = errors.concat(this.customValidation(editorData, this.state.loopCache.getTemplateName()));
+
+    if (errors.length !== 0) {
+      console.error("Errors detected during policy data validation ", errors);
+      this.setState({
+        showFailAlert: true,
+        showMessage: 'Errors detected during policy data validation:\n' + OnapUtils.jsonEditorErrorFormatter(errors)
+      });
+      return;
+    } else {
+      console.info("NO validation errors found in policy data");
+      if (this.state.policyInstanceType === OnapConstant.microServiceType) {
+        this.state.loopCache.updateMicroServiceProperties(this.state.policyName, editorData);
+        this.state.loopCache.updateMicroServicePdpGroup(this.state.policyName, this.state.chosenPdpGroup, this.state.chosenPdpSubgroup);
+        LoopService.setMicroServiceProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getMicroServiceForName(this.state.policyName)).then(resp => {
+          this.setState({ show: false });
+          this.props.history.push('/');
+          this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+        });
+      } else if (this.state.policyInstanceType === OnapConstant.operationalPolicyType) {
+        this.state.loopCache.updateOperationalPolicyProperties(this.state.policyName, editorData);
+        this.state.loopCache.updateOperationalPolicyPdpGroup(this.state.policyName, this.state.chosenPdpGroup, this.state.chosenPdpSubgroup);
+        LoopService.setOperationalPolicyProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getOperationalPolicies()).then(resp => {
+          this.setState({ show: false });
+          this.props.history.push('/');
+          this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+        });
+      }
+    }
+  }
+
+  customValidation(editorData, templateName) {
+    // method for sub-classes to override with customized validation
+    return [];
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    this.props.history.push('/');
+  }
+
+  componentDidMount() {
+    this.renderJsonEditor();
+  }
+
+  componentDidUpdate() {
+    if (this.state.showSucAlert === true || this.state.showFailAlert === true) {
+      let modalElement = document.getElementById("policyModal")
+      if (modalElement) {
+        modalElement.scrollTo(0, 0);
+      }
+    }
+  }
+
+  createJsonEditor(toscaModel, editorData) {
+    /*JSONEditor.defaults.themes.myBootstrap4 = JSONEditor.defaults.themes.bootstrap4.extend({
+            getTab: function(text,tabId) {
+                var liel = document.createElement('li');
+                liel.classList.add('nav-item');
+                var ael = document.createElement("a");
+                ael.classList.add("nav-link");
+                ael.setAttribute("style",'padding:10px;max-width:160px;');
+                ael.setAttribute("href", "#" + tabId);
+                ael.setAttribute('data-toggle', 'tab');
+                text.setAttribute("style",'word-wrap:break-word;');
+                ael.appendChild(text);
+                liel.appendChild(ael);
+                return liel;
+            }
+        });*/
+    return new JSONEditor(document.getElementById("editor"),
+      {
+        schema: toscaModel,
+        startval: editorData,
+        theme: 'bootstrap4',
+        iconlib: 'fontawesome5',
+        object_layout: 'grid',
+        disable_properties: false,
+        disable_edit_json: false,
+        disable_array_reorder: true,
+        disable_array_delete_last_row: true,
+        disable_array_delete_all_rows: false,
+        array_controls_top: true,
+        keep_oneof_values: false,
+        collapsed: true,
+        show_errors: 'always',
+        display_required_only: false,
+        show_opt_in: false,
+        prompt_before_delete: true,
+        required_by_default: false
+      })
+  }
+
+  renderJsonEditor() {
+    console.debug("Rendering PolicyModal ", this.state.policyName);
+    var toscaModel = {};
+    var editorData = {};
+    var pdpGroupValues = {};
+    var chosenPdpGroupValue, chosenPdpSubgroupValue;
+    if (this.state.policyInstanceType === OnapConstant.microServiceType) {
+      toscaModel = this.state.loopCache.getMicroServiceJsonRepresentationForName(this.state.policyName);
+      editorData = this.state.loopCache.getMicroServicePropertiesForName(this.state.policyName);
+      pdpGroupValues = this.state.loopCache.getMicroServiceSupportedPdpGroup(this.state.policyName);
+      chosenPdpGroupValue = this.state.loopCache.getMicroServicePdpGroup(this.state.policyName);
+      chosenPdpSubgroupValue = this.state.loopCache.getMicroServicePdpSubgroup(this.state.policyName);
+    } else if (this.state.policyInstanceType === OnapConstant.operationalPolicyType) {
+      toscaModel = this.state.loopCache.getOperationalPolicyJsonRepresentationForName(this.state.policyName);
+      editorData = this.state.loopCache.getOperationalPolicyPropertiesForName(this.state.policyName);
+      pdpGroupValues = this.state.loopCache.getOperationalPolicySupportedPdpGroup(this.state.policyName);
+      chosenPdpGroupValue = this.state.loopCache.getOperationalPolicyPdpGroup(this.state.policyName);
+      chosenPdpSubgroupValue = this.state.loopCache.getOperationalPolicyPdpSubgroup(this.state.policyName);
+    }
+
+    if (toscaModel == null) {
+      return;
+    }
+
+    var pdpSubgroupValues = [];
+    if (typeof (chosenPdpGroupValue) !== "undefined") {
+      var selectedPdpGroup = pdpGroupValues.filter(entry => (Object.keys(entry)[0] === chosenPdpGroupValue));
+      pdpSubgroupValues = selectedPdpGroup[0][chosenPdpGroupValue].map((pdpSubgroup) => {
+        return { label: pdpSubgroup, value: pdpSubgroup }
+      });
+    }
+    this.setState({
+      jsonEditor: this.createJsonEditor(toscaModel, editorData),
+      pdpGroup: pdpGroupValues,
+      pdpGroupList: pdpGroupValues.map(entry => {
+        return { label: Object.keys(entry)[0], value: Object.keys(entry)[0] };
+      }),
+      pdpSubgroupList: pdpSubgroupValues,
+      chosenPdpGroup: chosenPdpGroupValue,
+      chosenPdpSubgroup: chosenPdpSubgroupValue
+    })
+  }
+
+  handlePdpGroupChange(e) {
+    var selectedPdpGroup = this.state.pdpGroup.filter(entry => (Object.keys(entry)[0] === e.value));
+    const pdpSubgroupValues = selectedPdpGroup[0][e.value].map((pdpSubgroup) => {
+      return { label: pdpSubgroup, value: pdpSubgroup }
+    });
+    if (this.state.chosenPdpGroup !== e.value) {
+      this.setState({
+        chosenPdpGroup: e.value,
+        chosenPdpSubgroup: '',
+        pdpSubgroupList: pdpSubgroupValues
+      });
+    }
+  }
+
+  handlePdpSubgroupChange(e) {
+    this.setState({ chosenPdpSubgroup: e.value });
+  }
+
+  handleRefresh() {
+    var newLoopCache, toscaModel, editorData;
+    if (this.state.policyInstanceType === OnapConstant.microServiceType) {
+      LoopService.refreshMicroServicePolicyJson(this.state.loopCache.getLoopName(), this.state.policyName).then(data => {
+        newLoopCache = new LoopCache(data);
+        toscaModel = newLoopCache.getMicroServiceJsonRepresentationForName(this.state.policyName);
+        editorData = newLoopCache.getMicroServicePropertiesForName(this.state.policyName);
+        document.getElementById("editor").innerHTML = "";
+        this.setState({
+          loopCache: newLoopCache,
+          jsonEditor: this.createJsonEditor(toscaModel, editorData),
+          showSucAlert: true,
+          showMessage: "Successfully refreshed"
+        });
+      })
+        .catch(error => {
+          console.error("Error while refreshing the Operational Policy Json Representation");
+          this.setState({
+            showFailAlert: true,
+            showMessage: "Refreshing of UI failed"
+          });
+        });
+    } else if (this.state.policyInstanceType === OnapConstant.operationalPolicyType) {
+      LoopService.refreshOperationalPolicyJson(this.state.loopCache.getLoopName(), this.state.policyName).then(data => {
+        var newLoopCache = new LoopCache(data);
+        toscaModel = newLoopCache.getOperationalPolicyJsonRepresentationForName(this.state.policyName);
+        editorData = newLoopCache.getOperationalPolicyPropertiesForName(this.state.policyName);
+        document.getElementById("editor").innerHTML = "";
+        this.setState({
+          loopCache: newLoopCache,
+          jsonEditor: this.createJsonEditor(toscaModel, editorData),
+          showSucAlert: true,
+          showMessage: "Successfully refreshed"
+        });
+      })
+        .catch(error => {
+          console.error("Error while refreshing the Operational Policy Json Representation");
+          this.setState({
+            showFailAlert: true,
+            showMessage: "Refreshing of UI failed"
+          });
+        });
+    }
+  }
+
+  disableAlert() {
+    this.setState({ showSucAlert: false, showFailAlert: false });
+  }
+
+  renderPdpGroupDropDown() {
+    if (this.state.policyInstanceType !== OnapConstant.operationalPolicyType || !this.state.loopCache.isOpenLoopTemplate()) {
+      return (
+        <Form.Group as={ Row } controlId="formPlaintextEmail">
+          <Form.Label column sm="2">Pdp Group Info</Form.Label>
+          <Col sm="3">
+            <Select value={ { label: this.state.chosenPdpGroup, value: this.state.chosenPdpGroup } } onChange={ this.handlePdpGroupChange } options={ this.state.pdpGroupList }/>
+          </Col>
+          <Col sm="3">
+            <Select value={ { label: this.state.chosenPdpSubgroup, value: this.state.chosenPdpSubgroup } } onChange={ this.handlePdpSubgroupChange } options={ this.state.pdpSubgroupList }/>
+          </Col>
+        </Form.Group>
+      );
+    }
+  }
+
+  renderOpenLoopMessage() {
+    if (this.state.policyInstanceType === OnapConstant.operationalPolicyType && this.state.loopCache.isOpenLoopTemplate()) {
+      return (
+        "Operational Policy cannot be configured as only Open Loop is supported for this Template!"
+      );
+    }
+  }
+
+  renderModalTitle() {
+    return (
+      <Modal.Title>Edit the policy</Modal.Title>
+    );
+  }
+
+  renderButton() {
+    var allElement = [(<Button variant="secondary" onClick={ this.handleClose }>
+      Close
+    </Button>)];
+    if (this.state.policyInstanceType !== OnapConstant.operationalPolicyType || !this.state.loopCache.isOpenLoopTemplate()) {
+      allElement.push((
+        <Button variant="primary" disabled={ this.readOnly } onClick={ this.handleSave }>
+          Save Changes
+        </Button>
+      ));
+      allElement.push((
+        <Button variant="primary" disabled={ this.readOnly } onClick={ this.handleRefresh }>
+          Refresh
+        </Button>
+      ));
+    }
+    return allElement;
+  }
+
+  render() {
+    return (
+      <ModalStyled size="xl" backdrop="static" keyboard={ false } show={ this.state.show } onHide={ this.handleClose }>
+        <Modal.Header closeButton>
+          { this.renderModalTitle() }
+        </Modal.Header>
+        <Alert variant="success" show={ this.state.showSucAlert } onClose={ this.disableAlert } dismissible>
+          <DivWhiteSpaceStyled>
+            { this.state.showMessage }
+          </DivWhiteSpaceStyled>
+        </Alert>
+        <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+          <DivWhiteSpaceStyled>
+            { this.state.showMessage }
+          </DivWhiteSpaceStyled>
+        </Alert>
+        <Modal.Body>
+          { this.renderOpenLoopMessage() }
+          <div id="editor"/>
+          { this.renderPdpGroupDropDown() }
+        </Modal.Body>
+        <Modal.Footer>
+          { this.renderButton() }
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.test.js
new file mode 100644
index 0000000..734f22e
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.test.js
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 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 from 'react';
+import { mount } from 'enzyme';
+import PolicyModal from './PolicyModal';
+import LoopCache from '../../../api/LoopCache';
+import LoopService from '../../../api/LoopService';
+import OnapConstant from '../../../utils/OnapConstants';
+
+describe('Verify PolicyModal', () => {
+  beforeEach(() => {
+    fetch.resetMocks();
+    fetch.mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        text: () => "OK"
+      });
+    });
+  })
+  const loopCacheStr = {
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+    "operationalPolicies": [{
+      "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+      "configurationsJson": {
+        "operational_policy": {
+          "controlLoop": {},
+          "policies": []
+        }
+      },
+      "policyModel": { "policyPdpGroup": { "supportedPdpGroups": [{ "monitoring": ["xacml"] }] } },
+      "jsonRepresentation": { "schema": {} }
+    }]
+  };
+  const loopCache = new LoopCache(loopCacheStr);
+  const historyMock = { push: jest.fn() };
+  const flushPromises = () => new Promise(setImmediate);
+  const match = { params: { policyName: "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca", policyInstanceType: OnapConstant.operationalPolicyType } }
+
+  it('Test handleClose', () => {
+    const handleClose = jest.spyOn(PolicyModal.prototype, 'handleClose');
+    const component = mount(<PolicyModal history={ historyMock } match={ match } loopCache={ loopCache }/>)
+
+    component.find('[variant="secondary"]').prop('onClick')();
+
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Test handleSave', async () => {
+    const loadLoopFunction = jest.fn();
+    const handleSave = jest.spyOn(PolicyModal.prototype, 'handleSave');
+    const component = mount(<PolicyModal history={ historyMock }
+                                         loopCache={ loopCache } match={ match } loadLoopFunction={ loadLoopFunction }/>)
+
+    component.find('[variant="primary"]').get(0).props.onClick();
+    await flushPromises();
+    component.update();
+
+    expect(handleSave).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Test handleRefresh', async () => {
+    LoopService.refreshOperationalPolicyJson = jest.fn().mockImplementation(() => {
+      return Promise.resolve(loopCacheStr);
+    });
+    const updateLoopFunction = jest.fn();
+    const handleRefresh = jest.spyOn(PolicyModal.prototype, 'handleRefresh');
+    const component = mount(<PolicyModal loopCache={ loopCache } match={ match } updateLoopFunction={ updateLoopFunction }/>)
+
+    component.find('[variant="primary"]').get(1).props.onClick();
+    await flushPromises();
+    component.update();
+
+    expect(handleRefresh).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(true);
+    expect(component.state('showSucAlert')).toEqual(true);
+    expect(component.state('showMessage')).toEqual("Successfully refreshed");
+  });
+
+  it('Test handlePdpGroupChange', () => {
+    const component = mount(<PolicyModal loopCache={ loopCache } match={ match }/>)
+    component.setState({
+      "pdpGroup": [{ "option1": ["subPdp1", "subPdp2"] }],
+      "chosenPdpGroup": "option2"
+    });
+    expect(component.state('chosenPdpGroup')).toEqual("option2");
+
+    const instance = component.instance();
+    const event = { label: "option1", value: "option1" }
+    instance.handlePdpGroupChange(event);
+    expect(component.state('chosenPdpGroup')).toEqual("option1");
+    expect(component.state('chosenPdpSubgroup')).toEqual("");
+    expect(component.state('pdpSubgroupList')).toEqual([{ label: "subPdp1", value: "subPdp1" }, { label: "subPdp2", value: "subPdp2" }]);
+  });
+
+  it('Test handlePdpSubgroupChange', () => {
+    const component = mount(<PolicyModal loopCache={ loopCache } match={ match }/>)
+
+    const instance = component.instance();
+    const event = { label: "option1", value: "option1" }
+    instance.handlePdpSubgroupChange(event);
+    expect(component.state('chosenPdpSubgroup')).toEqual("option1");
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/ToscaViewer.js b/gui-clamp/ui-react/src/components/dialogs/Policy/ToscaViewer.js
new file mode 100644
index 0000000..ce4edce
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/ToscaViewer.js
@@ -0,0 +1,67 @@
+/*-
+ * ============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 from 'react'
+import PolicyToscaService from '../../../api/PolicyToscaService';
+import styled from 'styled-components';
+import Button from 'react-bootstrap/Button';
+
+const JsonEditorDiv = styled.div`
+  margin-top: 20px;
+  background-color: ${ props => props.theme.toscaTextareaBackgroundColor };
+  text-align: justify;
+  font-size: ${ props => props.theme.toscaTextareaFontSize };
+  width: 100%;
+  height: 30%;
+`
+
+export default class ToscaViewer extends React.Component {
+
+  state = {
+    toscaData: this.props.toscaData,
+    yamlPolicyTosca: this.getToscaModelYamlFor(this.props.toscaData),
+  }
+
+  constructor(props, context) {
+    super(props, context);
+    this.getToscaModelYamlFor = this.getToscaModelYamlFor.bind(this);
+  }
+
+  getToscaModelYamlFor(toscaData) {
+    PolicyToscaService.getToscaPolicyModelYaml(toscaData["policyModelType"], toscaData["version"]).then(respYamlPolicyTosca => {
+      this.setState({
+        yamlPolicyTosca: respYamlPolicyTosca,
+      })
+    });
+  }
+
+  render() {
+    return (
+      <JsonEditorDiv>
+        <pre>{ this.state.yamlPolicyTosca }</pre>
+        <Button variant="secondary" title="Create a new policy version from the defined parameters"
+                onClick={ this.handleCreateNewVersion }>Create New Version</Button>
+      </JsonEditorDiv>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js b/gui-clamp/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
new file mode 100644
index 0000000..a97deea
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
@@ -0,0 +1,427 @@
+/*-
+ * ============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 Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import styled from 'styled-components';
+import AddBox from '@material-ui/icons/AddBox';
+import ArrowDownward from '@material-ui/icons/ArrowDownward';
+import Check from '@material-ui/icons/Check';
+import ChevronLeft from '@material-ui/icons/ChevronLeft';
+import ChevronRight from '@material-ui/icons/ChevronRight';
+import Clear from '@material-ui/icons/Clear';
+import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded';
+import Edit from '@material-ui/icons/Edit';
+import FilterList from '@material-ui/icons/FilterList';
+import FirstPage from '@material-ui/icons/FirstPage';
+import LastPage from '@material-ui/icons/LastPage';
+import Remove from '@material-ui/icons/Remove';
+import SaveAlt from '@material-ui/icons/SaveAlt';
+import Search from '@material-ui/icons/Search';
+import ViewColumn from '@material-ui/icons/ViewColumn';
+import DehazeIcon from '@material-ui/icons/Dehaze';
+import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
+import AddIcon from '@material-ui/icons/Add';
+import FormControlLabel from '@material-ui/core/FormControlLabel';
+import Switch from '@material-ui/core/Switch';
+import MaterialTable from "material-table";
+import PolicyService from '../../../api/PolicyService';
+import PolicyToscaService from '../../../api/PolicyToscaService';
+import Select from 'react-select';
+import Alert from 'react-bootstrap/Alert';
+import Tabs from 'react-bootstrap/Tabs';
+import Tab from 'react-bootstrap/Tab';
+import PolicyEditor from './PolicyEditor';
+import ToscaViewer from './ToscaViewer';
+
+const DivWhiteSpaceStyled = styled.div`
+  white-space: pre;
+`
+
+const ModalStyled = styled(Modal)`
+  @media (min-width: 1000px) {
+    .modal-xl {
+      max-width: 96%;
+    }
+  }
+  background-color: transparent;
+`
+const DetailedRow = styled.div`
+  margin: 0 auto;
+  background-color: ${ props => props.theme.policyEditorBackgroundColor };
+  font-size: ${ props => props.theme.policyEditorFontSize };
+  width: 97%;
+  margin-left: auto;
+  margin-right: 0;
+`
+
+
+const standardCellStyle = { backgroundColor: '#039be5', color: '#FFF', border: '1px solid black' };
+const cellPdpGroupStyle = { backgroundColor: '#039be5', color: '#FFF', border: '1px solid black' };
+const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
+const rowHeaderStyle = { backgroundColor: '#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black' };
+
+export default class ViewAllPolicies extends React.Component {
+  state = {
+    show: true,
+    content: 'Please select a policy to display it',
+    selectedRowId: -1,
+    policiesListData: [],
+    toscaModelsListData: [],
+    jsonEditorForPolicy: new Map(),
+    prefixGrouping: false,
+    showSuccessAlert: false,
+    showFailAlert: false,
+    policyColumnsDefinition: [
+      {
+        title: "Policy Name", field: "name",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Policy Version", field: "version",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Policy Type", field: "type",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Policy Type Version", field: "type_version",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Deployed in PDP", field: "pdpGroupInfo.pdpGroup",
+        cellStyle: cellPdpGroupStyle,
+        headerStyle: headerStyle,
+        render: rowData => this.renderPdpGroupDropBox(rowData),
+        grouping: false
+      },
+      {
+        title: "PDP Group", field: "pdpGroupInfo.pdpGroup",
+        cellStyle: cellPdpGroupStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "PDP SubGroup", field: "pdpGroupInfo.pdpSubGroup",
+        cellStyle: cellPdpGroupStyle,
+        headerStyle: headerStyle
+      }
+    ],
+    toscaColumnsDefinition: [
+      {
+        title: "Policy Model Type", field: "policyModelType",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Policy Acronym", field: "policyAcronym",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Version", field: "version",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Uploaded By", field: "updatedBy",
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Uploaded Date", field: "updatedDate", editable: 'never',
+        cellStyle: standardCellStyle,
+        headerStyle: headerStyle
+      }
+    ],
+    tableIcons: {
+      Add: forwardRef((props, ref) => <AddBox { ...props } ref={ ref }/>),
+      Check: forwardRef((props, ref) => <Check { ...props } ref={ ref }/>),
+      Clear: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+      Delete: forwardRef((props, ref) => <DeleteRoundedIcon { ...props } ref={ ref }/>),
+      DetailPanel: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+      Edit: forwardRef((props, ref) => <Edit { ...props } ref={ ref }/>),
+      Export: forwardRef((props, ref) => <SaveAlt { ...props } ref={ ref }/>),
+      Filter: forwardRef((props, ref) => <FilterList { ...props } ref={ ref }/>),
+      FirstPage: forwardRef((props, ref) => <FirstPage { ...props } ref={ ref }/>),
+      LastPage: forwardRef((props, ref) => <LastPage { ...props } ref={ ref }/>),
+      NextPage: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+      PreviousPage: forwardRef((props, ref) => <ChevronLeft { ...props } ref={ ref }/>),
+      ResetSearch: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+      Search: forwardRef((props, ref) => <Search { ...props } ref={ ref }/>),
+      SortArrow: forwardRef((props, ref) => <ArrowDownward { ...props } ref={ ref }/>),
+      ThirdStateCheck: forwardRef((props, ref) => <Remove { ...props } ref={ ref }/>),
+      ViewColumn: forwardRef((props, ref) => <ViewColumn { ...props } ref={ ref }/>)
+    }
+  };
+
+  constructor(props, context) {
+    super(props, context);
+    this.handleClose = this.handleClose.bind(this);
+    this.renderPdpGroupDropBox = this.renderPdpGroupDropBox.bind(this);
+    this.handlePdpGroupChange = this.handlePdpGroupChange.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.getAllPolicies();
+    this.getAllToscaModels();
+  }
+
+  getAllToscaModels() {
+    PolicyToscaService.getToscaPolicyModels().then(toscaModelsList => {
+      this.setState({ toscaModelsListData: toscaModelsList });
+    });
+  }
+
+  handlePdpGroupChange(e) {
+    let pdpSplit = e.value.split("/");
+    let selectedPdpGroup = pdpSplit[0];
+    let selectedSubPdpGroup = pdpSplit[1];
+    if (typeof selectedSubPdpGroup !== "undefined") {
+      let temp = this.state.policiesListData;
+      temp[this.state.selectedRowId]["pdpGroupInfo"] = { "pdpGroup": selectedPdpGroup, "pdpSubGroup": selectedSubPdpGroup };
+      this.setState({ policiesListData: temp });
+    } else {
+      delete this.state.policiesListData[this.state.selectedRowId]["pdpGroupInfo"];
+    }
+  }
+
+  renderPdpGroupDropBox(dataRow) {
+    let optionItems = [{ label: "NOT DEPLOYED", value: "NOT DEPLOYED" }];
+    let selectedItem = { label: "NOT DEPLOYED", value: "NOT DEPLOYED" };
+    if (typeof dataRow.supportedPdpGroups !== "undefined") {
+      for (const pdpGroup of dataRow["supportedPdpGroups"]) {
+        for (const pdpSubGroup of Object.values(pdpGroup)[0]) {
+          optionItems.push({
+            label: Object.keys(pdpGroup)[0] + "/" + pdpSubGroup,
+            value: Object.keys(pdpGroup)[0] + "/" + pdpSubGroup
+          });
+        }
+      }
+    }
+    if (typeof dataRow.pdpGroupInfo !== "undefined") {
+      selectedItem = {
+        label: dataRow["pdpGroupInfo"]["pdpGroup"] + "/" + dataRow["pdpGroupInfo"]["pdpSubGroup"],
+        value: dataRow["pdpGroupInfo"]["pdpGroup"] + "/" + dataRow["pdpGroupInfo"]["pdpSubGroup"]
+      };
+    }
+    return (<div style={ { width: '250px' } }><Select value={ selectedItem } options={ optionItems } onChange={ this.handlePdpGroupChange }/></div>);
+  }
+
+  getAllPolicies() {
+    PolicyService.getPoliciesList().then(allPolicies => {
+      this.setState({ policiesListData: allPolicies["policies"] })
+    });
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    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 => {
+        if (typeof (respPolicyDeletion) === "undefined") {
+          //it indicates a failure
+          this.setState({
+            showFailAlert: true,
+            showMessage: 'Policy Deletion Failure'
+          });
+        } else {
+          this.setState({
+            showSuccessAlert: true,
+            showMessage: 'Policy successfully Deleted'
+          });
+        }
+        this.getAllPolicies();
+      }
+    )
+  }
+
+  disableAlert() {
+    this.setState({ showSuccessAlert: false, showFailAlert: false });
+  }
+
+  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"
+          />
+          <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"] } policyUpdateFunction={ this.getAllPolicies }/>
+                    </DetailedRow>
+                  )
+                },
+              },
+              {
+                icon: DehazeIcon,
+                tooltip: 'Show Raw Data',
+                render: rowData => {
+                  return (
+                    <DetailedRow>
+                      <pre>{ JSON.stringify(rowData, null, 2) }</pre>
+                    </DetailedRow>
+                  )
+                },
+              },
+            ] }
+            actions={ [
+              {
+                icon: forwardRef((props, ref) => <DeleteRoundedIcon { ...props } ref={ ref }/>),
+                tooltip: 'Delete Policy',
+                onClick: (event, rowData) => this.handleDeletePolicy(event, rowData)
+              }
+            ] }
+          />
+        </Modal.Body>
+      </Tab>
+    );
+  }
+
+  renderToscaTab() {
+    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,
+                tooltip: 'Show Raw Data',
+                render: rowData => {
+                  return (
+                    <DetailedRow>
+                      <pre>{ JSON.stringify(rowData, null, 2) }</pre>
+                    </DetailedRow>
+                  )
+                },
+              },
+              {
+                icon: AddIcon,
+                tooltip: 'Create a policy from this model',
+                render: rowData => {
+                  return (
+                    <DetailedRow>
+                      <PolicyEditor policyModelType={ rowData["policyModelType"] } policyModelTypeVersion={ rowData["version"] } policyProperties={ {} } policyUpdateFunction={ this.getAllPolicies }/>
+                    </DetailedRow>
+                  )
+                },
+              },
+            ] }
+          />
+        </Modal.Body>
+      </Tab>
+    );
+  }
+
+  render() {
+    return (
+      <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+        </Modal.Header>
+        <Tabs id="controlled-tab-example" activeKey={ this.state.key } onSelect={ key => this.setState({ key, selectedRowData: {} }) }>
+          { this.renderPoliciesTab() }
+          { this.renderToscaTab() }
+        </Tabs>
+        <Alert variant="success" show={ this.state.showSuccessAlert } onClose={ this.disableAlert } dismissible>
+          <DivWhiteSpaceStyled>
+            { this.state.showMessage }
+          </DivWhiteSpaceStyled>
+        </Alert>
+        <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+          <DivWhiteSpaceStyled>
+            { this.state.showMessage }
+          </DivWhiteSpaceStyled>
+        </Alert>
+        <Modal.Footer>
+          <Button variant="secondary" onClick={ this.handleClose }>Close</Button>
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/RefreshStatus.js b/gui-clamp/ui-react/src/components/dialogs/RefreshStatus.js
new file mode 100644
index 0000000..e23ab3f
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/RefreshStatus.js
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import LoopActionService from '../../api/LoopActionService';
+import Spinner from 'react-bootstrap/Spinner';
+import styled from 'styled-components';
+
+const StyledSpinnerDiv = styled.div`
+  justify-content: center !important;
+  display: flex !important;
+`;
+
+export default class RefreshStatus extends React.Component {
+  state = {
+    loopName: this.props.loopCache.getLoopName()
+  };
+
+  componentWillReceiveProps(newProps) {
+    this.setState({
+      loopName: newProps.loopCache.getLoopName()
+    });
+  }
+
+  componentDidMount() {
+    // refresh status and update loop logs
+    LoopActionService.refreshStatus(this.state.loopName).then(data => {
+      this.props.showSucAlert("Status successfully refreshed");
+      this.props.updateLoopFunction(data);
+      this.props.history.push('/');
+    })
+      .catch(error => {
+        this.props.showFailAlert("Status refreshing failed");
+        this.props.history.push('/');
+      });
+  }
+
+  render() {
+    return (
+      <StyledSpinnerDiv>
+        <Spinner animation="border" role="status">
+        </Spinner>
+      </StyledSpinnerDiv>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/RefreshStatus.test.js b/gui-clamp/ui-react/src/components/dialogs/RefreshStatus.test.js
new file mode 100644
index 0000000..7736ffd
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/RefreshStatus.test.js
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import RefreshStatus from './RefreshStatus';
+import LoopCache from '../../api/LoopCache';
+import LoopActionService from '../../api/LoopActionService';
+
+describe('Verify RefreshStatus', () => {
+
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca"
+  });
+
+  it('Test refresh status failed', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const historyMock = { push: jest.fn() };
+    const showSucAlert = jest.fn();
+    const showFailAlert = jest.fn();
+
+    const component = shallow(<RefreshStatus loopCache={ loopCache } history={ historyMock } showSucAlert={ showSucAlert } showFailAlert={ showFailAlert }/>)
+    await flushPromises();
+    component.update();
+
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+  it('Test refresh status successful', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    const historyMock = { push: jest.fn() };
+    const updateLoopFunction = jest.fn();
+    const showSucAlert = jest.fn();
+    const showFailAlert = jest.fn();
+
+    LoopActionService.refreshStatus = jest.fn().mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+        }
+      });
+    });
+
+    const component = shallow(<RefreshStatus loopCache={ loopCache }
+                                             loopAction="submit" history={ historyMock } updateLoopFunction={ updateLoopFunction } showSucAlert={ showSucAlert } showFailAlert={ showFailAlert }/>)
+    await flushPromises();
+    component.update();
+
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+  });
+
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js b/gui-clamp/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js
new file mode 100644
index 0000000..7257337
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js
@@ -0,0 +1,173 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import styled from 'styled-components';
+import TemplateService from '../../../api/TemplateService';
+import ArrowUpward from '@material-ui/icons/ArrowUpward';
+import ChevronLeft from '@material-ui/icons/ChevronLeft';
+import ChevronRight from '@material-ui/icons/ChevronRight';
+import Clear from '@material-ui/icons/Clear';
+import FirstPage from '@material-ui/icons/FirstPage';
+import LastPage from '@material-ui/icons/LastPage';
+import Search from '@material-ui/icons/Search';
+import MaterialTable from "material-table";
+import LoopCache from '../../../api/LoopCache';
+import SvgGenerator from '../../loop_viewer/svg/SvgGenerator';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+
+const cellStyle = { border: '1px solid black' };
+const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
+const rowHeaderStyle = { backgroundColor: '#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black' };
+
+export default class ViewLoopTemplatesModal extends React.Component {
+  state = {
+    show: true,
+    content: 'Please select a loop template to display it',
+    selectedRow: -1,
+    loopTemplatesData: [],
+    fakeLoopCacheWithTemplate: new LoopCache({}),
+    loopTemplateColumnsDefinition: [
+      {
+        title: "#", field: "index", render: rowData => rowData.tableData.id + 1,
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Template Name", field: "name",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Service Model Name", field: "modelService.serviceDetails.name",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Loop Type Allowed", field: "allowedLoopType",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "# Instances Allowed", field: "maximumInstancesAllowed",
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      },
+      {
+        title: "Modified Date", field: "updatedDate", editable: 'never',
+        cellStyle: cellStyle,
+        headerStyle: headerStyle
+      }
+    ],
+    tableIcons: {
+      FirstPage: forwardRef((props, ref) => <FirstPage { ...props } ref={ ref }/>),
+      LastPage: forwardRef((props, ref) => <LastPage { ...props } ref={ ref }/>),
+      NextPage: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+      PreviousPage: forwardRef((props, ref) => <ChevronLeft { ...props } ref={ ref }/>),
+      ResetSearch: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+      Search: forwardRef((props, ref) => <Search { ...props } ref={ ref }/>),
+      SortArrow: forwardRef((props, ref) => <ArrowUpward { ...props } ref={ ref }/>)
+    }
+  };
+
+  constructor(props, context) {
+    super(props, context);
+    this.handleClose = this.handleClose.bind(this);
+    this.renderSvg = this.renderSvg.bind(this);
+    this.getLoopTemplate = this.getLoopTemplate.bind(this);
+    this.getAllLoopTemplates();
+  }
+
+  getAllLoopTemplates() {
+    TemplateService.getAllLoopTemplates().then(templatesData => {
+      // replace -1 in maximumInstancesAllowed with more meaningful 'No Limit'
+      for (let item in templatesData) {
+        if (templatesData[item].maximumInstancesAllowed === -1) {
+          templatesData[item].maximumInstancesAllowed = 'No Limit';
+        }
+      }
+      this.setState({ loopTemplatesData: templatesData })
+    });
+  }
+
+  getLoopTemplate(templateIdInDataArray) {
+    if (typeof templateIdInDataArray !== "undefined") {
+      this.setState({
+        fakeLoopCacheWithTemplate:
+          new LoopCache({
+            "loopTemplate": this.state.loopTemplatesData[templateIdInDataArray],
+            "name": "fakeLoop"
+          })
+      })
+    } else {
+      this.setState({ fakeLoopCacheWithTemplate: new LoopCache({}) })
+    }
+  }
+
+  handleClose() {
+    this.setState({ show: false });
+    this.props.history.push('/')
+  }
+
+  renderSvg() {
+    return (
+      <SvgGenerator loopCache={ this.state.fakeLoopCacheWithTemplate } clickable={ false } generatedFrom={ SvgGenerator.GENERATED_FROM_TEMPLATE }/>
+    )
+  }
+
+  render() {
+    return (
+      <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+        <Modal.Header closeButton>
+        </Modal.Header>
+        <Modal.Body>
+          <MaterialTable
+            title={ "View Blueprint MicroService Templates" }
+            data={ this.state.loopTemplatesData }
+            columns={ this.state.loopTemplateColumnsDefinition }
+            icons={ this.state.tableIcons }
+            onRowClick={ (event, rowData) => {
+              this.getLoopTemplate(rowData.tableData.id);
+              this.setState({ selectedRow: rowData.tableData.id })
+            } }
+            options={ {
+              headerStyle: rowHeaderStyle,
+              rowStyle: rowData => ({
+                backgroundColor: (this.state.selectedRow !== -1 && this.state.selectedRow === rowData.tableData.id) ? '#EEE' : '#FFF'
+              })
+            } }
+          />
+          { this.renderSvg() }
+        </Modal.Body>
+        <Modal.Footer>
+          <Button variant="secondary" onClick={ this.handleClose }>Close</Button>
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.test.js
new file mode 100644
index 0000000..d93f0b0
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.test.js
@@ -0,0 +1,163 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import ViewLoopTemplatesModal from './ViewLoopTemplatesModal';
+import { mount } from 'enzyme';
+import { BrowserRouter as Router } from 'react-router-dom';
+
+describe('Verify ViewLoopTemplatesModal', () => {
+  beforeEach(() => {
+    fetch.resetMocks();
+  });
+
+  it('Test API Successful', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "index": "1",
+            "name": "MTCA version 1",
+            "modelService.serviceDetails.name": "MTCA",
+            "allowedLoopType": "CLOSED",
+            "maximumInstancesAllowed": 1,
+            "updatedDate": "2019-09-06 19:09:42"
+          });
+        }
+      });
+    });
+    const component = shallow(<ViewLoopTemplatesModal/>);
+  });
+
+  it('Test API Exception', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: false,
+        status: 500,
+        json: () => {
+          return Promise.resolve({
+            "index": "1",
+            "name": "MTCA version 1",
+            "modelService.serviceDetails.name": "MTCA",
+            "allowedLoopType": "CLOSED",
+            "maximumInstancesAllowed": 1,
+            "updatedDate": "2019-09-06 19:09:42"
+          });
+        }
+      });
+    });
+    const component = shallow(<ViewLoopTemplatesModal/>);
+  });
+
+  it('Test API Rejection', () => {
+    const myMockFunc = fetch.mockImplementationOnce(() => Promise.reject('error'));
+    setTimeout(() => myMockFunc().catch(e => {
+        console.info(e);
+      }),
+      100
+    );
+    const component = shallow(<ViewLoopTemplatesModal/>);
+    expect(myMockFunc.mock.calls.length).toBe(1);
+  });
+
+  it('Test the tosca model view render method', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "index": "1",
+            "name": "MTCA version 1",
+            "modelService.serviceDetails.name": "MTCA",
+            "allowedLoopType": "CLOSED",
+            "maximumInstancesAllowed": 1,
+            "updatedDate": "2019-09-06 19:09:42"
+          });
+        }
+      });
+    });
+    const component = shallow(<ViewLoopTemplatesModal/>);
+    component.setState({
+      loopTemplateData: {
+        "index": "1",
+        "name": "MTCA version 1",
+        "modelService.serviceDetails.name": "MTCA",
+        "allowedLoopType": "CLOSED",
+        "maximumInstancesAllowed": 1,
+        "updatedDate": "2019-09-06 19:09:42"
+      }
+    });
+    expect(component).toMatchSnapshot();
+  });
+
+  it('Test Table icons', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "index": "1",
+            "name": "MTCA version 1",
+            "modelService.serviceDetails.name": "MTCA",
+            "allowedLoopType": "CLOSED",
+            "maximumInstancesAllowed": 1,
+            "updatedDate": "2019-09-06 19:09:42"
+          });
+        }
+      });
+    });
+    const component = mount(<Router><ViewLoopTemplatesModal/></Router>);
+    expect(component.find('[className="MuiSelect-icon MuiTablePagination-selectIcon"]')).toBeTruthy();
+  });
+
+  it('Test handleClose', () => {
+    fetch.mockImplementationOnce(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "index": "1",
+            "name": "MTCA version 1",
+            "modelService.serviceDetails.name": "MTCA",
+            "allowedLoopType": "CLOSED",
+            "maximumInstancesAllowed": 1,
+            "updatedDate": "2019-09-06 19:09:42"
+          });
+        }
+      });
+    });
+    const historyMock = { push: jest.fn() };
+    const handleClose = jest.spyOn(ViewLoopTemplatesModal.prototype, 'handleClose');
+    const component = shallow(<ViewLoopTemplatesModal history={ historyMock }/>)
+    component.find('[variant="secondary"]').prop('onClick')();
+    expect(handleClose).toHaveBeenCalledTimes(1);
+    expect(component.state('show')).toEqual(false);
+    expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+    handleClose.mockClear();
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Tosca/__snapshots__/ViewLoopTemplatesModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Tosca/__snapshots__/ViewLoopTemplatesModal.test.js.snap
new file mode 100644
index 0000000..73f6596
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Tosca/__snapshots__/ViewLoopTemplatesModal.test.js.snap
@@ -0,0 +1,157 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify ViewLoopTemplatesModal Test the tosca model view render method 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="xl"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  />
+  <ModalBody>
+    <WithStyles(Component)
+      columns={
+        Array [
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "field": "index",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "render": [Function],
+            "title": "#",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "field": "name",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "Template Name",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "field": "modelService.serviceDetails.name",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "Service Model Name",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "field": "allowedLoopType",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "Loop Type Allowed",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "field": "maximumInstancesAllowed",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "# Instances Allowed",
+          },
+          Object {
+            "cellStyle": Object {
+              "border": "1px solid black",
+            },
+            "editable": "never",
+            "field": "updatedDate",
+            "headerStyle": Object {
+              "backgroundColor": "#ddd",
+              "border": "2px solid black",
+            },
+            "title": "Modified Date",
+          },
+        ]
+      }
+      data={Array []}
+      icons={
+        Object {
+          "FirstPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "LastPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "NextPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "PreviousPage": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "ResetSearch": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "Search": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+          "SortArrow": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "render": [Function],
+          },
+        }
+      }
+      onRowClick={[Function]}
+      options={
+        Object {
+          "headerStyle": Object {
+            "backgroundColor": "#ddd",
+            "border": "1px solid black",
+            "fontSize": "15pt",
+            "text": "bold",
+          },
+          "rowStyle": [Function],
+        }
+      }
+      title="View Blueprint MicroService Templates"
+    />
+    <withRouter(SvgGenerator)
+      clickable={false}
+      generatedFrom="TEMPLATE"
+      loopCache={
+        LoopCache {
+          "loopJsonCache": Object {},
+        }
+      }
+    />
+  </ModalBody>
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      variant="secondary"
+    >
+      Close
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/UserInfoModal.js b/gui-clamp/ui-react/src/components/dialogs/UserInfoModal.js
new file mode 100644
index 0000000..e58c61a
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/UserInfoModal.js
@@ -0,0 +1,115 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import styled from 'styled-components';
+import UserService from '../../api/UserService';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+
+export default class UserInfoModal extends React.Component {
+
+  constructor(props, context) {
+    super(props, context);
+
+    this.handleClose = this.handleClose.bind(this);
+    this.renderPermissions = this.renderPermissions.bind(this);
+    this.renderUserName = this.renderUserName.bind(this);
+    this.state = {
+      show: true,
+      userInfo: {}
+    };
+  }
+
+  componentWillMount() {
+    UserService.getUserInfo().then(userInfo => {
+      this.setState({ userInfo: userInfo })
+    });
+  }
+
+  handleClose() {
+    this.props.history.push('/');
+  }
+
+  renderPermissions() {
+    if (this.state.userInfo["allPermissions"]) {
+      var listOfPermissions = this.state.userInfo["allPermissions"].map(function (perm) {
+        return <Form.Control plaintext readOnly defaultValue={ perm }/>;
+      })
+      return listOfPermissions;
+    } else {
+      return;
+    }
+  }
+
+  renderUserName() {
+    if (this.state.userInfo["userName"]) {
+      return <Form.Control plaintext readOnly defaultValue={ this.state.userInfo["userName"] }/>
+    } else {
+      return;
+    }
+  }
+
+  renderVersion() {
+    if (this.state.userInfo["cldsVersion"]) {
+      return <Form.Control plaintext readOnly defaultValue={ this.state.userInfo["cldsVersion"] }/>
+    } else {
+      return;
+    }
+  }
+
+  render() {
+    return (
+      <ModalStyled size="lg" show={ this.state.show } onHide={ this.handleClose }>
+        <Modal.Header closeButton>
+          <Modal.Title>User Info</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          <Form.Group as={ Row } controlId="userName">
+            <Form.Label column sm="3">Current User:</Form.Label>
+            <Col>{ this.renderUserName() }</Col>
+          </Form.Group>
+          <Form.Group as={ Row } controlId="cldsVersion">
+            <Form.Label column sm="3">CLDS Version:</Form.Label>
+            <Col>{ this.renderVersion() }</Col>
+          </Form.Group>
+          <Form.Group as={ Row } controlId="userPermissions">
+            <Form.Label column sm="3">User Permissions:</Form.Label>
+            <Col>
+              { this.renderPermissions() }
+            </Col>
+          </Form.Group>
+        </Modal.Body>
+        <Modal.Footer>
+          <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button>
+        </Modal.Footer>
+      </ModalStyled>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/UserInfoModal.test.js b/gui-clamp/ui-react/src/components/dialogs/UserInfoModal.test.js
new file mode 100644
index 0000000..7168792
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/UserInfoModal.test.js
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import UserInfoModal from './UserInfoModal';
+
+describe('Verify UserInfoModal', () => {
+
+  beforeEach(() => {
+    fetch.resetMocks();
+    fetch.mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "userName": "test",
+            "cldsVersion": "1.0.0"
+          });
+        }
+      });
+    });
+  })
+
+  it('Test the render method full permission', () => {
+    const component = shallow(<UserInfoModal/>)
+    component.setState({
+      userInfo: {
+        "userName": "test",
+        "cldsVersion": "1.0.0",
+        "allPermissions": ["permission1", "permission2"]
+      }
+    });
+    expect(component).toMatchSnapshot();
+  });
+
+  it('Test the render method no permission', () => {
+    const component = shallow(<UserInfoModal/>)
+    component.setState({
+      userInfo: {}
+    });
+
+    expect(component.find('FormControl').length).toEqual(0);
+  });
+
+  it('Test the render method read permission', () => {
+    const component = shallow(<UserInfoModal/>)
+    component.setState({
+      userInfo: {
+        "userName": "test",
+        "cldsVersion": "1.0.0",
+        "allPermissions": ["permission1", "permission2"]
+      }
+    });
+
+    expect(component.find('FormControl').length).toEqual(4);
+
+    const forms = component.find('FormControl');
+    expect(forms.get(0).props.defaultValue).toEqual("test");
+    expect(forms.get(1).props.defaultValue).toEqual("1.0.0");
+    expect(forms.get(2).props.defaultValue).toEqual("permission1");
+    expect(forms.get(3).props.defaultValue).toEqual("permission2");
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/__snapshots__/UserInfoModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/__snapshots__/UserInfoModal.test.js.snap
new file mode 100644
index 0000000..7c725bc
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/__snapshots__/UserInfoModal.test.js.snap
@@ -0,0 +1,117 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify UserInfoModal Test the render method full permission 1`] = `
+<Styled(Modal)
+  onHide={[Function]}
+  show={true}
+  size="lg"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      User Info
+    </ModalTitle>
+  </ModalHeader>
+  <ModalBody>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="userName"
+    >
+      <FormLabel
+        column={true}
+        sm="3"
+        srOnly={false}
+      >
+        Current User:
+      </FormLabel>
+      <Col>
+        <FormControl
+          defaultValue="test"
+          plaintext={true}
+          readOnly={true}
+        />
+      </Col>
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="cldsVersion"
+    >
+      <FormLabel
+        column={true}
+        sm="3"
+        srOnly={false}
+      >
+        CLDS Version:
+      </FormLabel>
+      <Col>
+        <FormControl
+          defaultValue="1.0.0"
+          plaintext={true}
+          readOnly={true}
+        />
+      </Col>
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="userPermissions"
+    >
+      <FormLabel
+        column={true}
+        sm="3"
+        srOnly={false}
+      >
+        User Permissions:
+      </FormLabel>
+      <Col>
+        <FormControl
+          defaultValue="permission1"
+          plaintext={true}
+          readOnly={true}
+        />
+        <FormControl
+          defaultValue="permission2"
+          plaintext={true}
+          readOnly={true}
+        />
+      </Col>
+    </FormGroup>
+  </ModalBody>
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="null"
+      variant="secondary"
+    >
+      Cancel
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/loop_viewer/logs/LoopLogs.js b/gui-clamp/ui-react/src/components/loop_viewer/logs/LoopLogs.js
new file mode 100644
index 0000000..b03b740
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/loop_viewer/logs/LoopLogs.js
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import Table from 'react-bootstrap/Table';
+import LoopCache from '../../../api/LoopCache';
+import styled from 'styled-components';
+
+const LoopLogsHeaderDivStyled = styled.div`
+  background-color: ${ props => props.theme.loopLogsHeaderBackgroundColor };
+  padding: 10px 10px;
+  color: ${ props => props.theme.loopLogsHeaderFontColor };
+`
+const TableStyled = styled(Table)`
+
+  overflow: auto;
+`
+const TableRow = ({ logRow }) => (
+  <tr>
+    <td>{ logRow.logInstant }</td>
+    <td>{ logRow.logType }</td>
+    <td>{ logRow.logComponent }</td>
+    <td>{ logRow.message }</td>
+  </tr>
+
+)
+
+export default class LoopLogs extends React.Component {
+
+  state = {
+    loopCache: new LoopCache({})
+  }
+
+  constructor(props) {
+    super(props);
+    this.renderLogs = this.renderLogs.bind(this);
+    this.state.loopCache = props.loopCache;
+  }
+
+  shouldComponentUpdate(nextProps, nextState) {
+    return this.state.loopCache !== nextState.loopCache;
+  }
+
+  componentWillReceiveProps(newProps) {
+    this.setState({
+      loopCache: newProps.loopCache
+    });
+  }
+
+  renderLogs() {
+    if (this.state.loopCache.getLoopLogsArray() != null) {
+      return (
+        this.state.loopCache.getLoopLogsArray().map(row => <TableRow logRow={ row }/>)
+      )
+    }
+  }
+
+  render() {
+    return (
+      <LoopLogsHeaderDivStyled>
+        <label>Loop Logs</label>
+        <TableStyled striped hover variant responsive>
+          <thead>
+          <tr>
+            <th><span align="left">Date</span></th>
+            <th><span align="left">Type</span></th>
+            <th><span align="left">Component</span></th>
+            <th><span align="right">Log</span></th>
+          </tr>
+          </thead>
+          <tbody>
+          { this.renderLogs() }
+          </tbody>
+        </TableStyled>
+      </LoopLogsHeaderDivStyled>
+
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/loop_viewer/logs/LoopLogs.test.js b/gui-clamp/ui-react/src/components/loop_viewer/logs/LoopLogs.test.js
new file mode 100644
index 0000000..d3a21d8
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/loop_viewer/logs/LoopLogs.test.js
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import LoopLogs from './LoopLogs';
+import LoopCache from '../../../api/LoopCache';
+
+describe('Verify LoopLogs', () => {
+
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+    "loopLogs": [
+      {
+        "id": 1,
+        "logType": "INFO",
+        "logComponent": "CLAMP",
+        "message": "Operational policies UPDATED",
+        "logInstant": "2019-07-08T09:44:37Z"
+      }
+    ]
+  });
+
+  it('Test the render method', () => {
+    const component = shallow(<LoopLogs loopCache={ loopCache }/>)
+    expect(component).toMatchSnapshot();
+
+    const loopCacheUpdated = new LoopCache({
+      "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+      "loopLogs": [
+        {
+          "id": 1,
+          "logType": "INFO",
+          "logComponent": "CLAMP",
+          "message": "Operational policies UPDATED",
+          "logInstant": "2019-07-08T09:44:37Z"
+        },
+        {
+          "id": 2,
+          "logType": "INFO",
+          "logComponent": "CLAMP",
+          "message": "Operational policies UPDATED",
+          "logInstant": "2019-07-08T09:44:50Z"
+        }
+      ]
+    });
+
+    component.setProps({ loopCache: loopCacheUpdated });
+    expect(component.find('TableRow').length).toEqual(2);
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/loop_viewer/logs/__snapshots__/LoopLogs.test.js.snap b/gui-clamp/ui-react/src/components/loop_viewer/logs/__snapshots__/LoopLogs.test.js.snap
new file mode 100644
index 0000000..75b817b
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/loop_viewer/logs/__snapshots__/LoopLogs.test.js.snap
@@ -0,0 +1,61 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify LoopLogs Test the render method 1`] = `
+<styled.div>
+  <label>
+    Loop Logs
+  </label>
+  <Styled(Component)
+    hover={true}
+    responsive={true}
+    striped={true}
+    variant={true}
+  >
+    <thead>
+      <tr>
+        <th>
+          <span
+            align="left"
+          >
+            Date
+          </span>
+        </th>
+        <th>
+          <span
+            align="left"
+          >
+            Type
+          </span>
+        </th>
+        <th>
+          <span
+            align="left"
+          >
+            Component
+          </span>
+        </th>
+        <th>
+          <span
+            align="right"
+          >
+            Log
+          </span>
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <TableRow
+        logRow={
+          Object {
+            "id": 1,
+            "logComponent": "CLAMP",
+            "logInstant": "2019-07-08T09:44:37Z",
+            "logType": "INFO",
+            "message": "Operational policies UPDATED",
+          }
+        }
+      />
+    </tbody>
+  </Styled(Component)>
+</styled.div>
+`;
diff --git a/gui-clamp/ui-react/src/components/loop_viewer/status/LoopStatus.js b/gui-clamp/ui-react/src/components/loop_viewer/status/LoopStatus.js
new file mode 100644
index 0000000..2994c84
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/loop_viewer/status/LoopStatus.js
@@ -0,0 +1,109 @@
+/*-
+* ============LICENSE_START=======================================================
+* ONAP CLAMP
+* ================================================================================
+* Copyright (C) 2019 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 from 'react';
+import Table from 'react-bootstrap/Table';
+import styled from 'styled-components';
+import LoopCache from '../../../api/LoopCache';
+
+const LoopStatusViewDivStyled = styled.div`
+  background-color: ${ props => props.theme.loopViewerHeaderBackgroundColor };
+  padding: 10px 10px;
+  color: ${ props => props.theme.loopViewerHeaderFontColor };
+`
+
+const TableStyled = styled(Table)`
+  overflow: auto;
+`
+
+const TableRow = ({ statusRow }) => (
+  <tr>
+    <td>{ statusRow.componentName }</td>
+    <td>{ statusRow.stateName }</td>
+    <td>{ statusRow.description }</td>
+  </tr>
+
+)
+
+export default class LoopStatus extends React.Component {
+  state = {
+    loopCache: new LoopCache({})
+  }
+
+  constructor(props) {
+    super(props);
+    this.renderStatus = this.renderStatus.bind(this);
+    this.state.loopCache = props.loopCache;
+  }
+
+
+  renderStatus() {
+    if (this.state.loopCache.getComponentStates() != null) {
+      return Object.keys(this.state.loopCache.getComponentStates()).map((key) => {
+        console.debug("Adding status for: ", key);
+        var res = {}
+        res[key] = this.state.loopCache.getComponentStates()[key];
+        return (<TableRow statusRow={ {
+          'componentName': key,
+          'stateName': this.state.loopCache.getComponentStates()[key].componentState.stateName,
+          'description': this.state.loopCache.getComponentStates()[key].componentState.description
+        } }/>)
+      })
+
+    }
+  }
+
+  shouldComponentUpdate(nextProps, nextState) {
+    return this.state.loopCache !== nextState.loopCache;
+  }
+
+  componentWillReceiveProps(newProps) {
+    this.setState({
+      loopCache: newProps.loopCache
+    });
+  }
+
+  render() {
+    return (
+      <LoopStatusViewDivStyled>
+        <label>Loop Status: { this.state.loopCache.getComputedState() }
+        </label>
+
+        <div>
+          <TableStyled striped hover variant responsive>
+            <thead>
+            <tr>
+              <th><span align="left">Component Name</span></th>
+              <th><span align="left">Component State</span></th>
+              <th><span align="right">Description</span></th>
+            </tr>
+            </thead>
+            <tbody>
+            { this.renderStatus() }
+            </tbody>
+          </TableStyled>
+        </div>
+      </LoopStatusViewDivStyled>
+    );
+  }
+}
+
diff --git a/gui-clamp/ui-react/src/components/loop_viewer/status/LoopStatus.test.js b/gui-clamp/ui-react/src/components/loop_viewer/status/LoopStatus.test.js
new file mode 100644
index 0000000..b84067e
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/loop_viewer/status/LoopStatus.test.js
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import LoopStatus from './LoopStatus';
+import LoopCache from '../../../api/LoopCache';
+
+describe('Verify LoopStatus', () => {
+
+  const loopCache = new LoopCache({
+    "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+    "lastComputedState": "DESIGN",
+    "components": {
+      "POLICY": {
+        "componentState": {
+          "stateName": "NOT_SENT",
+          "description": "The policies defined have NOT yet been created on the policy engine"
+        }
+      },
+      "DCAE": {
+        "componentState": {
+          "stateName": "BLUEPRINT_DEPLOYED",
+          "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+        }
+      }
+    }
+  });
+
+  it('Test the render method', () => {
+    const component = shallow(<LoopStatus loopCache={ loopCache }/>)
+
+    expect(component).toMatchSnapshot();
+
+    const loopCacheUpdated = new LoopCache({
+      "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+      "lastComputedState": "SUBMIT",
+      "components": {
+        "POLICY": {
+          "componentState": {
+            "stateName": "SENT",
+            "description": "The policies defined have NOT yet been created on the policy engine"
+          }
+        },
+        "DCAE": {
+          "componentState": {
+            "stateName": "BLUEPRINT_DEPLOYED",
+            "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+          }
+        }
+      }
+    });
+    component.setProps({ loopCache: loopCacheUpdated });
+
+    const forms = component.find('TableRow');
+    expect(forms.get(0).props.statusRow.stateName).toEqual("SENT");
+    expect(component.find('label').text()).toContain('SUBMIT');
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/loop_viewer/status/__snapshots__/LoopStatus.test.js.snap b/gui-clamp/ui-react/src/components/loop_viewer/status/__snapshots__/LoopStatus.test.js.snap
new file mode 100644
index 0000000..73da5ff
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/loop_viewer/status/__snapshots__/LoopStatus.test.js.snap
@@ -0,0 +1,64 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify LoopStatus Test the render method 1`] = `
+<styled.div>
+  <label>
+    Loop Status: 
+    DESIGN
+  </label>
+  <div>
+    <Styled(Component)
+      hover={true}
+      responsive={true}
+      striped={true}
+      variant={true}
+    >
+      <thead>
+        <tr>
+          <th>
+            <span
+              align="left"
+            >
+              Component Name
+            </span>
+          </th>
+          <th>
+            <span
+              align="left"
+            >
+              Component State
+            </span>
+          </th>
+          <th>
+            <span
+              align="right"
+            >
+              Description
+            </span>
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <TableRow
+          statusRow={
+            Object {
+              "componentName": "POLICY",
+              "description": "The policies defined have NOT yet been created on the policy engine",
+              "stateName": "NOT_SENT",
+            }
+          }
+        />
+        <TableRow
+          statusRow={
+            Object {
+              "componentName": "DCAE",
+              "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop",
+              "stateName": "BLUEPRINT_DEPLOYED",
+            }
+          }
+        />
+      </tbody>
+    </Styled(Component)>
+  </div>
+</styled.div>
+`;
diff --git a/gui-clamp/ui-react/src/components/loop_viewer/svg/SvgGenerator.js b/gui-clamp/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
new file mode 100644
index 0000000..f5f5047
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
@@ -0,0 +1,246 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react'
+import styled from 'styled-components';
+import { withRouter } from "react-router-dom";
+import LoopCache from '../../../api/LoopCache';
+import OnapConstant from '../../../utils/OnapConstants';
+
+const DivStyled = styled.div`
+	overflow-x: scroll;
+	  display: flex;
+    width: 100%;
+    height: 100%;
+`
+
+const emptySvg = (<svg> <text x="60" y="40">No LOOP (SVG)</text> </svg>);
+
+class SvgGenerator extends React.Component {
+    boxWidth = 200;
+    boxHeight = 100;
+    boxSpace = 50;
+
+    static GENERATED_FROM_INSTANCE = "INSTANCE";
+    static GENERATED_FROM_TEMPLATE = "TEMPLATE";
+
+	state = {
+		loopCache: new LoopCache({}),
+		clickable: false,
+		generatedFrom: SvgGenerator.GENERATED_FROM_INSTANCE, // INSTANCE / TEMPLATE
+	}
+
+	constructor(props) {
+		super(props);
+		this.state.loopCache = props.loopCache;
+		this.state.clickable = props.clickable;
+		this.state.generatedFrom = props.generatedFrom;
+		this.handleSvgClick = this.handleSvgClick.bind(this);
+		this.renderSvg = this.renderSvg.bind(this);
+	}
+
+	shouldComponentUpdate(nextProps, nextState) {
+		return this.state.loopCache !== nextState.loopCache;
+	}
+
+	componentWillReceiveProps(newProps) {
+		if (this.state.loopCache !== newProps.loopCache) {
+			this.setState({
+				loopCache: newProps.loopCache,
+			});
+		}
+	}
+
+	handleSvgClick(event) {
+		console.debug("svg click event received");
+		if (this.state.clickable) {
+			var elementName = event.target.parentNode.getAttribute('policyId');
+			console.info("SVG element clicked", elementName);
+			// Only allow movement to policy editing IF there busyLoadingCOunt is 0,
+			// meaning we are not waiting for refreshStatus to complete, for example
+			if (elementName !== null && !this.props.isBusyLoading()) {
+				this.props.history.push("/policyModal/"+event.target.parentNode.getAttribute('policyType')+"/"+elementName);
+			}
+		}
+	}
+
+    createVesBox (xPos) {
+        return this.createOneBox(xPos,null,null,'VES Collector','VES',null);
+    }
+
+    createOneArrow(xPos) {
+        return (
+         <svg width={this.boxSpace} height={this.boxHeight} x={xPos}>
+           <defs>
+            		<marker viewBox="0 0 20 20" markerWidth="20" markerHeight="20" orient="auto" refX="8.5" refY="5" id="arrow">
+            			<path d="m 1 5 l 0 -3 l 7 3 l -7 3 z"
+            				stroke-width= "1" stroke-linecap= "butt" stroke-dasharray= "10000, 1"
+            				fill="#000000" stroke="#000000" />
+            		</marker>
+           </defs>
+           <line x1="0" y1="50%" x2="100%" y2="50%" stroke-width="2" color="black" stroke="black" marker-end="url(#arrow)"/>
+         </svg>
+        );
+    }
+
+    createBeginCircle(xPos, text) {
+            return (
+            <svg width={this.boxWidth} height={this.boxHeight} x={xPos}>
+                <circle cx={this.boxWidth-30} cy="50%" r="30" stroke-width="1" color="black" stroke="black" fill="#27ae60"/>
+                <text x={this.boxWidth-30} y="50%" text-anchor="middle" dominant-baseline="middle" textLength="20%" lengthAdjust="spacingAndGlyphs" >{text}</text>
+            </svg>
+            );
+    }
+
+    createEndCircle(xPos, text) {
+            return (
+            <svg width={this.boxWidth} height={this.boxHeight} x={xPos}>
+                <circle cx={30} cy="50%" r="30" stroke-width="2" color="black" stroke="black" fill="#27ae60"/>
+                <text x={30} y="50%" text-anchor="middle" dominant-baseline="middle" textLength="20%" lengthAdjust="spacingAndGlyphs" >{text}</text>
+            </svg>
+            );
+    }
+
+    createOneBox(xPos, policyId, loopElementModelId , name, title, policyType) {
+        return (
+        <svg width={this.boxWidth} height={this.boxHeight} x={xPos} title="test">
+            <g policyId={policyId} loopElementModelId={loopElementModelId} policyType={policyType}>
+                <rect width="100%" height="100%" stroke-width="2" color="black" stroke="black" fill="#1abc9c"/>
+                <text x="50%" y="15%" color="white" fill="white" dominant-baseline="middle" text-anchor="middle" textLength="50%" lengthAdjust="spacingAndGlyphs">{title}</text>
+                <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" textLength="80%" lengthAdjust="spacingAndGlyphs" >{name}</text>
+                <text x="50%" y="80%" text-anchor="middle" dominant-baseline="middle" textLength="110%" lengthAdjust="spacingAndGlyphs" >{policyId}</text>
+            </g>
+        </svg>
+        );
+    }
+
+    createSvgFromTemplate() {
+        const allElements = [];
+        var xPos = 0;
+
+        allElements.push(this.createBeginCircle(xPos,"Start"))
+        xPos+=(this.boxWidth+this.boxSpace);
+
+        allElements.push(this.createOneArrow(xPos-this.boxSpace));
+
+        allElements.push(this.createVesBox(xPos));
+        xPos+=(this.boxWidth+this.boxSpace);
+
+        allElements.push(this.createOneArrow(xPos-this.boxSpace));
+        //createOneBox(xPos, policyId, loopElementModelId , name, title, policyType)
+        for (var loopElement of this.state.loopCache.getAllLoopElementModels()) {
+
+            allElements.push(this.createOneBox(xPos,
+                loopElement['name'],
+                loopElement['name'],
+                loopElement['shortName'],
+                loopElement['loopElementType'],
+                loopElement['loopElementType']))
+            xPos+=(this.boxWidth+this.boxSpace);
+            allElements.push(this.createOneArrow(xPos-this.boxSpace));
+        }
+
+        allElements.push(this.createEndCircle(xPos, "End"))
+        xPos+=(this.boxWidth+this.boxSpace);
+
+        return allElements;
+    }
+
+    createSvgFromInstance() {
+        const allElements = [];
+        var xPos = 0;
+
+        allElements.push(this.createBeginCircle(xPos,"Start"))
+        xPos+=(this.boxWidth+this.boxSpace);
+
+        allElements.push(this.createOneArrow(xPos-this.boxSpace));
+
+        allElements.push(this.createVesBox(xPos));
+        xPos+=(this.boxWidth+this.boxSpace);
+
+        allElements.push(this.createOneArrow(xPos-this.boxSpace));
+
+        for (var msPolicy in this.state.loopCache.getMicroServicePolicies()) {
+            var loopElementModelName =  this.state.loopCache.getMicroServicePolicies()[msPolicy]['loopElementModel'];
+            if (loopElementModelName !== undefined) {
+                loopElementModelName = loopElementModelName['name'];
+            }
+            allElements.push(this.createOneBox(xPos,
+                this.state.loopCache.getMicroServicePolicies()[msPolicy]['name'],
+                loopElementModelName,
+                this.state.loopCache.getMicroServicePolicies()[msPolicy]['policyModel']['policyAcronym'],
+                'microservice',
+                OnapConstant.microServiceType))
+            xPos+=(this.boxWidth+this.boxSpace);
+            allElements.push(this.createOneArrow(xPos-this.boxSpace));
+        }
+
+        for (var opPolicy in this.state.loopCache.getOperationalPolicies()) {
+            loopElementModelName =  this.state.loopCache.getOperationalPolicies()[opPolicy]['loopElementModel'];
+            if (loopElementModelName !== undefined) {
+                loopElementModelName = loopElementModelName['name'];
+            }
+            allElements.push(this.createOneBox(xPos,
+                this.state.loopCache.getOperationalPolicies()[opPolicy]['name'],
+                loopElementModelName,
+                this.state.loopCache.getOperationalPolicies()[opPolicy]['policyModel']['policyAcronym'],
+                'operational',
+                OnapConstant.operationalPolicyType))
+            xPos+=(this.boxWidth+this.boxSpace);
+            allElements.push(this.createOneArrow(xPos-this.boxSpace));
+        }
+
+        allElements.push(this.createEndCircle(xPos, "End"))
+        xPos+=(this.boxWidth+this.boxSpace);
+
+        return allElements;
+    }
+
+    renderSvg() {
+        if (this.state.loopCache.getLoopName() === undefined) {
+            return [emptySvg];
+        }
+        if (this.state.generatedFrom === SvgGenerator.GENERATED_FROM_INSTANCE) {
+            return this.createSvgFromInstance();
+        } else if (this.state.generatedFrom === SvgGenerator.GENERATED_FROM_TEMPLATE) {
+            return this.createSvgFromTemplate();
+        }
+    }
+
+    render() {
+        var allTheElements = this.renderSvg();
+        var svgWidth = this.boxWidth*allTheElements.length;
+        var svgHeight = this.boxHeight+50;
+        return (
+
+            <DivStyled onClick={this.handleSvgClick} >
+                <svg height={svgHeight} width={svgWidth}  viewBox="0,0,{svgWidth},{svgHeight}" preserveAspectRatio="none">
+									<svg x="-50" y="25">
+                    {allTheElements}
+									</svg>
+                </svg>
+            </DivStyled>
+        );
+    }
+}
+
+export default withRouter(SvgGenerator);
diff --git a/gui-clamp/ui-react/src/components/menu/MenuBar.js b/gui-clamp/ui-react/src/components/menu/MenuBar.js
new file mode 100644
index 0000000..8956c8b
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/menu/MenuBar.js
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP POLICY-CLAMP
+ * ================================================================================
+ * Copyright (C) 2019, 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 from 'react';
+import Nav from 'react-bootstrap/Nav';
+import Navbar from 'react-bootstrap/Navbar';
+import NavDropdown from 'react-bootstrap/NavDropdown';
+import OnapConstants from '../../utils/OnapConstants';
+import 'bootstrap-css-only/css/bootstrap.min.css';
+import styled from 'styled-components';
+import { Link } from 'react-router-dom';
+
+const StyledLink = styled(Link)`
+  color: ${ props => props.theme.menuFontColor };
+  background-color: ${ props => props.theme.menuBackgroundColor };
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ${ props => props.theme.menuHighlightedBackgroundColor };
+    color: ${ props => props.theme.menuHighlightedFontColor };
+  }
+`;
+const StyledNavLink = styled(Nav.Link)`
+  color: ${ props => props.theme.menuFontColor };
+  background-color: ${ props => props.theme.menuBackgroundColor };
+  font-weight: normal;
+  padding: .25rem 1.5rem;
+
+  :hover {
+    background-color: ${ props => props.theme.menuHighlightedBackgroundColor };
+    color: ${ props => props.theme.menuHighlightedFontColor };
+  }
+`;
+
+const StyledNavDropdown = styled(NavDropdown)`
+  color: ${ props => props.theme.menuFontColor };
+
+  & .dropdown-toggle {
+    color: ${ props => props.theme.menuFontColor };
+    background-color: ${ props => props.theme.backgroundColor };
+    font-weight: normal;
+
+    :hover {
+      font-weight: bold;
+    }
+  }
+`;
+
+export default class MenuBar extends React.Component {
+  state = {
+    loopName: this.props.loopName,
+    disabled: true
+  };
+
+  componentWillReceiveProps(newProps) {
+    if (newProps.loopName !== OnapConstants.defaultLoopName) {
+      this.setState({ disabled: false });
+    } else {
+      this.setState({ disabled: true });
+    }
+  }
+
+  render() {
+    return (
+      <Navbar.Collapse>
+        <StyledNavDropdown title="POLICY Framework">
+          <NavDropdown.Item as={ StyledLink } to="/viewAllPolicies">View All Policies</NavDropdown.Item>
+        </StyledNavDropdown>
+        <StyledNavDropdown title="CLAMP Options">
+          <NavDropdown.Item as={ StyledLink } to="/manageDictionaries">Tosca Metadata Dictionaries</NavDropdown.Item>
+          <NavDropdown.Divider/>
+          <NavDropdown.Item as={ StyledLink } to="/viewLoopTemplatesModal">View All Loop Templates</NavDropdown.Item>
+        </StyledNavDropdown>
+        <StyledNavDropdown title="LOOP Instance">
+          <NavDropdown.Item as={ StyledLink } to="/createLoop">Create</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/openLoop">Open</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/closeLoop" disabled={ this.state.disabled }>Close</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/modifyLoop" disabled={ this.state.disabled }>Modify</NavDropdown.Item>
+          <NavDropdown.Divider/>
+          <NavDropdown.Item as={ StyledLink } to="/loopProperties" disabled={ this.state.disabled }>Properties</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/refreshStatus" disabled={ this.state.disabled }>Refresh Status</NavDropdown.Item>
+        </StyledNavDropdown>
+        <StyledNavDropdown title="LOOP Operations">
+          <NavDropdown.Item as={ StyledLink } to="/submit" disabled={ this.state.disabled }>Create and deploy to Policy Framework (SUBMIT)</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/stop" disabled={ this.state.disabled }>Undeploy from Policy Framework (STOP)</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/restart" disabled={ this.state.disabled }>ReDeploy to Policy Framework (RESTART)</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/delete" disabled={ this.state.disabled }>Delete loop instance (DELETE)</NavDropdown.Item>
+          <NavDropdown.Divider/>
+          <NavDropdown.Item as={ StyledLink } to="/deploy" disabled={ this.state.disabled }>Deploy to DCAE (DEPLOY)</NavDropdown.Item>
+          <NavDropdown.Item as={ StyledLink } to="/undeploy" disabled={ this.state.disabled }>UnDeploy to DCAE (UNDEPLOY)</NavDropdown.Item>
+        </StyledNavDropdown>
+        <StyledNavDropdown title="Help">
+          <StyledNavLink href="https://wiki.onap.org/" target="_blank">Wiki</StyledNavLink>
+          <StyledNavLink href="mailto:onap-discuss@lists.onap.org?subject=CLAMP&body=Please send us suggestions or feature enhancements or defect. If possible, please send us the steps to replicate any defect.">Contact
+            Us</StyledNavLink>
+          <NavDropdown.Item as={ StyledLink } to="/userInfo">User Info</NavDropdown.Item>
+        </StyledNavDropdown>
+      </Navbar.Collapse>
+    );
+  }
+}
diff --git a/gui-clamp/ui-react/src/components/menu/MenuBar.test.js b/gui-clamp/ui-react/src/components/menu/MenuBar.test.js
new file mode 100644
index 0000000..81117bb
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/menu/MenuBar.test.js
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import { shallow } from 'enzyme';
+import MenuBar from './MenuBar';
+
+describe('Verify MenuBar', () => {
+
+  it('Test the render method', () => {
+    const component = shallow(<MenuBar/>)
+
+    expect(component).toMatchSnapshot();
+  });
+
+  it('Update loopName', () => {
+    const component = shallow(<MenuBar/>)
+    component.setProps({ loopName: "newLoop" });
+    expect(component.state('disabled')).toBe(false);
+  });
+
+  it('Default loopName', () => {
+    const component = shallow(<MenuBar/>)
+    component.setProps({ loopName: "Empty (NO loop loaded yet)" });
+    expect(component.state('disabled')).toBe(true);
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap b/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap
new file mode 100644
index 0000000..317c5a0
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap
@@ -0,0 +1,1086 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify MenuBar Test the render method 1`] = `
+<NavbarCollapse>
+  <Styled(NavDropdown)
+    title="POLICY Framework"
+  >
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={false}
+      to="/viewAllPolicies"
+    >
+      View All Policies
+    </DropdownItem>
+  </Styled(NavDropdown)>
+  <Styled(NavDropdown)
+    title="CLAMP Options"
+  >
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={false}
+      to="/manageDictionaries"
+    >
+      Tosca Metadata Dictionaries
+    </DropdownItem>
+    <DropdownDivider
+      role="separator"
+    />
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={false}
+      to="/viewLoopTemplatesModal"
+    >
+      View All Loop Templates
+    </DropdownItem>
+  </Styled(NavDropdown)>
+  <Styled(NavDropdown)
+    title="LOOP Instance"
+  >
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={false}
+      to="/createLoop"
+    >
+      Create
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={false}
+      to="/openLoop"
+    >
+      Open
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/closeLoop"
+    >
+      Close
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/modifyLoop"
+    >
+      Modify
+    </DropdownItem>
+    <DropdownDivider
+      role="separator"
+    />
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/loopProperties"
+    >
+      Properties
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/refreshStatus"
+    >
+      Refresh Status
+    </DropdownItem>
+  </Styled(NavDropdown)>
+  <Styled(NavDropdown)
+    title="LOOP Operations"
+  >
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/submit"
+    >
+      Create and deploy to Policy Framework (SUBMIT)
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/stop"
+    >
+      Undeploy from Policy Framework (STOP)
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/restart"
+    >
+      ReDeploy to Policy Framework (RESTART)
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/delete"
+    >
+      Delete loop instance (DELETE)
+    </DropdownItem>
+    <DropdownDivider
+      role="separator"
+    />
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/deploy"
+    >
+      Deploy to DCAE (DEPLOY)
+    </DropdownItem>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={true}
+      to="/undeploy"
+    >
+      UnDeploy to DCAE (UNDEPLOY)
+    </DropdownItem>
+  </Styled(NavDropdown)>
+  <Styled(NavDropdown)
+    title="Help"
+  >
+    <Styled(NavLink)
+      href="https://wiki.onap.org/"
+      target="_blank"
+    >
+      Wiki
+    </Styled(NavLink)>
+    <Styled(NavLink)
+      href="mailto:onap-discuss@lists.onap.org?subject=CLAMP&body=Please send us suggestions or feature enhancements or defect. If possible, please send us the steps to replicate any defect."
+    >
+      Contact Us
+    </Styled(NavLink)>
+    <DropdownItem
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "attrs": Array [],
+          "componentStyle": e {
+            "baseHash": 1014106698,
+            "baseStyle": undefined,
+            "componentId": "sc-bdfBwQ",
+            "isStatic": false,
+            "rules": Array [
+              "
+  color: ",
+              [Function],
+              ";
+  background-color: ",
+              [Function],
+              ";
+  font-weight: normal;
+  display: block;
+  width: 100%;
+  padding: .25rem 1.5rem;
+  clear: both;
+  text-align: inherit;
+  white-space: nowrap;
+  border: 0;
+
+  :hover {
+    text-decoration: none;
+    background-color: ",
+              [Function],
+              ";
+    color: ",
+              [Function],
+              ";
+  }
+",
+            ],
+            "staticRulesId": "",
+          },
+          "foldedComponentIds": Array [],
+          "render": [Function],
+          "shouldForwardProp": undefined,
+          "styledComponentId": "sc-bdfBwQ",
+          "target": Object {
+            "$$typeof": Symbol(react.forward_ref),
+            "propTypes": Object {
+              "innerRef": [Function],
+              "onClick": [Function],
+              "replace": [Function],
+              "target": [Function],
+              "to": [Function],
+            },
+            "render": [Function],
+          },
+          "toString": [Function],
+          "warnTooManyClasses": [Function],
+          "withComponent": [Function],
+        }
+      }
+      disabled={false}
+      to="/userInfo"
+    >
+      User Info
+    </DropdownItem>
+  </Styled(NavDropdown)>
+</NavbarCollapse>
+`;
diff --git a/gui-clamp/ui-react/src/index.js b/gui-clamp/ui-react/src/index.js
new file mode 100644
index 0000000..9baeaee
--- /dev/null
+++ b/gui-clamp/ui-react/src/index.js
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 from 'react';
+import ReactDOM from 'react-dom';
+import OnapClamp from './OnapClamp';
+import { Route, MemoryRouter } from 'react-router-dom'
+
+
+const routing = (
+  <MemoryRouter forceRefresh={ false }>
+    <Route path="/" component={ OnapClamp }/>
+  </MemoryRouter>
+);
+
+export var mainClamp = ReactDOM.render(
+  routing,
+  document.getElementById('root')
+)
diff --git a/gui-clamp/ui-react/src/logo.png b/gui-clamp/ui-react/src/logo.png
new file mode 100644
index 0000000..c6f6857
--- /dev/null
+++ b/gui-clamp/ui-react/src/logo.png
Binary files differ
diff --git a/gui-clamp/ui-react/src/setupTests.js b/gui-clamp/ui-react/src/setupTests.js
new file mode 100644
index 0000000..f553583
--- /dev/null
+++ b/gui-clamp/ui-react/src/setupTests.js
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP POLICY-CLAMP
+ * ================================================================================
+ * Copyright (C) 2019, 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 { configure } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-17-updated';
+
+configure({ adapter: new Adapter() });
+global.fetch = require('jest-fetch-mock');
\ No newline at end of file
diff --git a/gui-clamp/ui-react/src/theme/globalStyle.js b/gui-clamp/ui-react/src/theme/globalStyle.js
new file mode 100644
index 0000000..5245aae
--- /dev/null
+++ b/gui-clamp/ui-react/src/theme/globalStyle.js
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 { createGlobalStyle } from 'styled-components';
+
+export const GlobalClampStyle = createGlobalStyle`
+  body {
+    padding: 0;
+    margin: 0;
+    font-family: ${ props => props.theme.fontFamily };
+    font-size: ${ props => props.theme.fontSize };
+    font-weight: normal;
+  }
+
+  span {
+    font-family: ${ props => props.theme.fontFamily };
+    font-size: ${ props => props.theme.fontSize };
+    font-weight: bold;
+  }
+
+  a {
+    font-family: ${ props => props.theme.fontFamily };
+    font-size: ${ props => props.theme.fontSize };
+    font-weight: bold;
+  }
+
+  div {
+    font-family: ${ props => props.theme.fontFamily };
+    font-size: ${ props => props.theme.fontSize };
+    border-radius: 4px;
+    margin-top: 1px;
+  }
+
+  label {
+    font-family: ${ props => props.theme.fontFamily };
+    font-size: ${ props => props.theme.fontSize };
+    font-weight: bold;
+  }
+
+  button {
+    font-family: ${ props => props.theme.fontFamily };
+    font-size: ${ props => props.theme.fontSize };
+    font-weight: bold;
+  }
+
+`
+
+export const DefaultClampTheme = {
+  fontDanger: '#eb238e',
+  fontWarning: '#eb238e',
+  fontLight: '#ffffff',
+  fontDark: '#888888',
+  fontHighlight: '#ffff00',
+  fontNormal: 'black',
+
+  backgroundColor: '#eeeeee',
+  fontFamily: 'Arial, Sans-serif',
+  fontSize: '16px',
+
+  loopViewerBackgroundColor: 'white',
+  loopViewerFontColor: 'yellow',
+  loopViewerHeaderBackgroundColor: '#337ab7',
+  loopViewerHeaderFontColor: 'white',
+
+  loopLogsHeaderBackgroundColor: 'white',
+  loopLogsHeaderFontColor: 'black',
+
+  menuBackgroundColor: 'white',
+  menuFontColor: 'black',
+  menuHighlightedBackgroundColor: '#337ab7',
+  menuHighlightedFontColor: 'white',
+
+  toscaTextareaBackgroundColor: 'white',
+  toscaTextareaFontSize: '13px',
+
+  policyEditorBackgroundColor: 'white',
+  policyEditorFontSize: '13px'
+};
diff --git a/gui-clamp/ui-react/src/utils/CsvToJson.js b/gui-clamp/ui-react/src/utils/CsvToJson.js
new file mode 100644
index 0000000..6d95b31
--- /dev/null
+++ b/gui-clamp/ui-react/src/utils/CsvToJson.js
@@ -0,0 +1,204 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default function CsvToJson(rawCsvData, delimiter, internalDelimiter, csvHeaderNames, jsonKeyNames, mandatory) {
+
+  let printDictKeys = '';
+  let result = { jsonObjArray: [], errorMessages: '' };
+
+  // Validate that all parallel arrays passed in have same number of elements;
+  // this would be a developer error.
+
+  let checkLength = csvHeaderNames.length;
+
+  if (checkLength !== jsonKeyNames.length || checkLength !== mandatory.length) {
+    result.errorMessages = 'interanl error: csvHeaderNames, jsonKeyNames, and mandatory arrays parameters are not the same length';
+    return result;
+  }
+
+  if (checkLength < 1) {
+    result.errorMessages = 'interanl error: csvHeaderNames, jsonKeyNames, and mandatory arrays have no entries';
+    return result;
+  }
+
+  // Make a nice string to print in the error case to tell user what is the
+  //  required heaer row format
+
+  for (let i = 0; i < csvHeaderNames.length; ++i) {
+    if (i === 0) {
+      printDictKeys = csvHeaderNames[i];
+    } else {
+      printDictKeys += ',' + csvHeaderNames[i];
+    }
+  }
+
+  let dictElems = rawCsvData.split('\n');
+  let numColumns = 0;
+  let filteredDictElems = [];
+
+  // The task of the following loop is to convert raw CSV rows into easily parseable
+  // and streamlined versions of the rows with an internalDelimiter replacing the standard
+  // comma; it is presumed (and checked) that the internalDelimiter cannot exist as a valid
+  // sequence of characters in the user's data.
+
+  // This conversion process also strips leading and trailing whitespace from each row,
+  // discards empty rows, correctly interprets and removes all double quotes that programs like
+  // Excel use to support user columns that contain special characters, most notably, the comma
+  // delimiter. A double-quote that is contained within a double-quoted column value
+  // must appear in this raw data as a sequence of two double quotes. Furthermore, any column
+  // value in the raw CSV data that does not contain a delimiter may or may not be enclosed in
+  // double quotes. It is the Excel convention to not use double qoutes unless necessary, and
+  // there is no reasonable way to tell Excel to surround every column value with double quotes.
+  // Any files that were directly "exported" by CLAMP itself from the Managing Dictionaries
+  // capability, surround all columns with double quotes.
+
+  for (let i = 0; i < dictElems.length; i++) {
+
+    let oneRow = dictElems[i].trim();
+    let j = 0;
+    let inQuote = false
+    let nextChar = undefined;
+    let prevChar = null;
+
+
+    if (oneRow === '') {
+      continue; // Skip blank rows
+    } else if (oneRow.indexOf(internalDelimiter) !== -1) {
+      result.errorMessages += '\nRow #' + i + ' contains illegal sequence of characters (' + internalDelimiter + ')';
+      break;
+    } else {
+      nextChar = oneRow[1];
+    }
+
+    let newStr = '';
+    numColumns = 1;
+
+    // This "while loop" performs the very meticulous task of removing double quotes that
+    // are used by Excel to encase special characters as user string value data,
+    // and manages to correctly identify columns that are defined with or without
+    // double quotes and to process the comma delimiter correctly when encountered
+    // as a user value within a column. Such a column would have to be encased in
+    // double quotes; a comma found outside double quotes IS a delimiter.
+
+    while (j < oneRow.length) {
+      if (oneRow[j] === '"') {
+        if (inQuote === false) {
+          if (prevChar !== delimiter && prevChar !== null) {
+            result.errorMessages += '\nMismatched double quotes or illegal whitespace around delimiter at row #' + (i + 1) + ' near column #' + numColumns;
+            break;
+          } else {
+            inQuote = true;
+          }
+        } else {
+          if (nextChar === '"') {
+            newStr += '"';
+            ++j;
+          } else if ((nextChar !== delimiter) && (nextChar !== undefined)) {
+            result.errorMessages += '\nRow #' + (i + 1) + ' is badly formatted at column #' + numColumns + '. Perhaps an unescaped double quote.';
+            break;
+          } else if (nextChar === delimiter) {
+            ++numColumns;
+            inQuote = false;
+            newStr += internalDelimiter;
+            prevChar = delimiter;
+            j += 2;
+            nextChar = oneRow[j + 1];
+            continue;
+          } else {
+            ++numColumns;
+            inQuote = false;
+            break;
+          }
+        }
+      } else {
+        if (oneRow[j] === delimiter && inQuote === false) {
+          newStr += internalDelimiter;
+          ++numColumns;
+        } else {
+          newStr += oneRow[j];
+        }
+      }
+      prevChar = oneRow[j];
+      ++j;
+      nextChar = oneRow[j + 1]; // can result in undefined at the end
+    }
+
+    if (result.errorMessages === '' && inQuote !== false) {
+      result.errorMessages += '\nMismatched double quotes at row #' + (i + 1);
+      break;
+    } else if (result.errorMessages === '' && numColumns < jsonKeyNames.length) {
+      result.errorMessages += '\nNot enough columns (' + jsonKeyNames.length + ') at row #' + (i + 1);
+      break;
+    }
+
+    filteredDictElems.push(newStr);
+  }
+
+  if (result.errorMessages !== '') {
+    return result;
+  }
+
+  // Perform further checks on data that is now in JSON form
+  if (filteredDictElems.length < 2) {
+    result.errorMessages += '\nNot enough row data found in import file. Need at least a header row and one row of data';
+    return result;
+  }
+
+  // Now that we have something reliably parsed into sanitized columns lets run some checks
+  // and convert it all into an array of JSON objects to push to the back end if all the
+  // checks pass.
+
+  let headers = filteredDictElems[0].split(internalDelimiter);
+
+  // check that headers are included in proper order
+  for (let i = 0; i < jsonKeyNames.length; ++i) {
+    if (csvHeaderNames[i] !== headers[i]) {
+      result.errorMessages += 'Row 1 header key at column #' + (i + 1) + ' is a mismatch. Expected row header must contain at least:\n' + printDictKeys;
+      return result;
+    }
+  }
+
+  // Convert the ASCII rows of data into an array of JSON obects that omit the header
+  // row which is not sent to the back end.
+
+  for (let i = 1; i < filteredDictElems.length; i++) {
+    let data = filteredDictElems[i].split(internalDelimiter);
+    let obj = {};
+    for (let j = 0; j < data.length && j < jsonKeyNames.length; j++) {
+      let value = data[j].trim();
+      if (mandatory[j] === true && value === '') {
+        result.errorMessages += '\n' + csvHeaderNames[j] + ' at row #' + (i + 1) + ' is empty but requires a value.';
+      }
+      obj[jsonKeyNames[j]] = value;
+    }
+    result.jsonObjArray.push(obj);
+  }
+
+  if (result.errorMessages !== '') {
+    // If we have errors, return empty parse result even though some things
+    // may have parsed properly. We do not want to encourage the caller
+    // to think the data is good for use.
+    result.jsonObjArray = [];
+  }
+
+  return result;
+}
diff --git a/gui-clamp/ui-react/src/utils/CsvToJson.test.js b/gui-clamp/ui-react/src/utils/CsvToJson.test.js
new file mode 100644
index 0000000..e36e855
--- /dev/null
+++ b/gui-clamp/ui-react/src/utils/CsvToJson.test.js
@@ -0,0 +1,268 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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 CsvToJson from './CsvToJson'
+
+describe('Verify CsvToJson', () => {
+
+  const hdrNames = [
+    "Element Short Name",
+    "Element Name",
+    "Element Description",
+    "Element Type",
+    "Sub-Dictionary"
+  ];
+
+  const jsonKeyNames = [
+    "shortName",
+    "name",
+    "description",
+    "type",
+    "subDictionary"
+  ];
+
+  const mandatory = [true, true, true, true, false];
+
+  it('Test CsvToJson No Error Case, Quoted Columns', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Type of Alert","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let expectedResult = {
+      errorMessages: '',
+      jsonObjArray: [
+        {
+          description: "Type of Alert",
+          name: "Alert Type",
+          shortName: "alertType",
+          subDictionary: "",
+          type: "string"
+        }
+      ]
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson No Error Case, Unquoted Columns', () => {
+
+    let rawCsv = 'Element Short Name,Element Name,Element Description,Element Type,Sub-Dictionary\n';
+    rawCsv += 'alertType,Alert Type,Type of Alert,string,,admin,2020-06-11T13:56:14.927437Z';
+
+    let expectedResult = {
+      errorMessages: '',
+      jsonObjArray: [
+        {
+          description: "Type of Alert",
+          name: "Alert Type",
+          shortName: "alertType",
+          subDictionary: "",
+          type: "string"
+        }
+      ]
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Properly Escaped Double Quote and Delimiter', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert ""Type""","Type of Alert, Varies","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let errorMessage = '';
+
+    let expectedResult = {
+      errorMessages: errorMessage,
+      jsonObjArray: [
+        {
+          description: "Type of Alert, Varies",
+          name: 'Alert "Type"',
+          shortName: 'alertType',
+          subDictionary: "",
+          type: "string",
+        }
+
+      ]
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+
+  it('Test CsvToJson Error Header Mismatch Error Case', () => {
+
+    let rawCsv = '"Element Short Names","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Type of Alert","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let errorMessage = 'Row 1 header key at column #1 is a mismatch. Expected row header must contain at least:\n';
+    errorMessage += 'Element Short Name,Element Name,Element Description,Element Type,Sub-Dictionary';
+
+    let expectedResult = {
+      errorMessages: errorMessage,
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Mismatched Double Quotes in Column', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alert"Type","Alert Type","Type of Alert","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let errorMessage = '\nRow #2 is badly formatted at column #1. Perhaps an unescaped double quote.'
+
+    let expectedResult = {
+      errorMessages: errorMessage,
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Illegal Whitespace', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += 'alertType ,  "Alert Type","Type of Alert","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let errorMessage = '\nMismatched double quotes or illegal whitespace around delimiter at row #2 near column #2';
+
+    let expectedResult = {
+      errorMessages: errorMessage,
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Too Few Data Columns', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Type of Alert"';
+
+    let errorMessage = '\nNot enough columns (5) at row #2';
+
+    let expectedResult = {
+      errorMessages: errorMessage,
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Wrong Header Column Order', () => {
+
+    let rawCsv = '"Element Name","Element Short Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Type of Alert","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let errorMessage = 'Row 1 header key at column #1 is a mismatch. Expected row header must contain at least:\n';
+    errorMessage += 'Element Short Name,Element Name,Element Description,Element Type,Sub-Dictionary';
+
+    let expectedResult = {
+      errorMessages: errorMessage,
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Not Enough Rows', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+
+    let errorMessage = '\nNot enough row data found in import file. Need at least a header row and one row of data';
+
+    let expectedResult = {
+      errorMessages: errorMessage,
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Mandatory Field Is Empty', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"","Alert Type","Type of Alert","string","","admin","2020-06-11T13:56:14.927437Z"';
+
+    let expectedResult = {
+      errorMessages: '\nElement Short Name at row #2 is empty but requires a value.',
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '|', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Mismatched Double Quotes At End', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Alert Type Description","string","admin","2020-06-11T13:56:14.927437Z';
+
+    let expectedResult = {
+      errorMessages: '\nMismatched double quotes at row #2',
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '||', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Mismatched Mandatory Array Parameters', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Alert Type Description","string","admin","2020-06-11T13:56:14.927437Z';
+
+    let expectedResult = {
+      errorMessages: 'interanl error: csvHeaderNames, jsonKeyNames, and mandatory arrays parameters are not the same length',
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '||', hdrNames, jsonKeyNames, [true])).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Empty Mandatory Array Parameters', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Alert Type Description","string","admin","2020-06-11T13:56:14.927437Z';
+
+    let expectedResult = {
+      errorMessages: 'interanl error: csvHeaderNames, jsonKeyNames, and mandatory arrays have no entries',
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '||', [], [], [])).toEqual(expectedResult);
+  });
+
+  it('Test CsvToJson Error Illegal Data Contains Internal Delimiter', () => {
+
+    let rawCsv = '"Element Short Name","Element Name","Element Description","Element Type","Sub-Dictionary"\n';
+    rawCsv += '"alertType","Alert Type","Alert Type||Description","string","admin","2020-06-11T13:56:14.927437Z';
+
+    let expectedResult = {
+      errorMessages: '\nRow #1 contains illegal sequence of characters (||)',
+      jsonObjArray: []
+    };
+
+    expect(CsvToJson(rawCsv, ',', '||', hdrNames, jsonKeyNames, mandatory)).toEqual(expectedResult);
+  });
+})
diff --git a/gui-clamp/ui-react/src/utils/OnapConstants.js b/gui-clamp/ui-react/src/utils/OnapConstants.js
new file mode 100644
index 0000000..c5b96d4
--- /dev/null
+++ b/gui-clamp/ui-react/src/utils/OnapConstants.js
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+// Maintain a list of ONAP CLAMP UI "constants" that can be used by any componenet within CLAMP
+
+const OnapConstants = {
+  defaultLoopName: "Empty (NO loop loaded yet)",
+  microServiceType: "MICRO-SERVICE-POLICY",
+  operationalPolicyType: "OPERATIONAL_POLICY_TYPE"
+};
+
+export default OnapConstants;
diff --git a/gui-clamp/ui-react/src/utils/OnapUtils.js b/gui-clamp/ui-react/src/utils/OnapUtils.js
new file mode 100644
index 0000000..9ba9382
--- /dev/null
+++ b/gui-clamp/ui-react/src/utils/OnapUtils.js
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class OnapUtils {
+
+  constructor() {
+    this.clickBlocked = false;
+  }
+
+  static jsonEditorErrorFormatter(errors) {
+
+    let messages = [];
+    let messagesOutputString = null;
+
+    // errors is an array of JSON Editor "error" objects, where each
+    // object looks like this:
+
+    // {
+    //   message: "Please populate the required property "Threshold""
+    //   path: "root.signatures.0"
+    //   property: "required"
+    // }
+
+    // In this function we concatenate all the messages, removing any duplicates,
+    // and adding a newline between each message. The result returned is a single
+    // string that can be displayed to the user in an alert message
+
+    if (!Array.isArray(errors)) {
+      console.error('jsoneEditorErrorFormatter was passed a non-array argument');
+    } else {
+      for (let ii = 0; ii < errors.length; ++ii) {
+        if (!messages.includes(errors[ii].message)) {
+          messages.push(errors[ii].message);
+          if (messagesOutputString) {
+            messagesOutputString += '\n' + errors[ii].message;
+          } else {
+            messagesOutputString = errors[ii].message;
+          }
+        }
+      }
+    }
+
+    return messagesOutputString;
+  }
+}
diff --git a/gui-editors/gui-editor-apex/pom.xml b/gui-editors/gui-editor-apex/pom.xml
index 2090e9e..b52631a 100644
--- a/gui-editors/gui-editor-apex/pom.xml
+++ b/gui-editors/gui-editor-apex/pom.xml
@@ -159,7 +159,7 @@
                 <artifactId>frontend-maven-plugin</artifactId>
                 <version>1.9.1</version>
                 <configuration>
-                    <nodeVersion>v12.18.0</nodeVersion>
+                    <nodeVersion>v14.17.0</nodeVersion>
                     <installDirectory>${webapp.dir}</installDirectory>
                     <workingDirectory>${webapp.dir}</workingDirectory>
                     <npmDownloadRoot>${repo.npm}</npmDownloadRoot>
diff --git a/gui-pdp-monitoring/pom.xml b/gui-pdp-monitoring/pom.xml
index 2108d91..4fe4491 100644
--- a/gui-pdp-monitoring/pom.xml
+++ b/gui-pdp-monitoring/pom.xml
@@ -87,7 +87,7 @@
                 <artifactId>frontend-maven-plugin</artifactId>
                 <version>1.9.1</version>
                 <configuration>
-                    <nodeVersion>v12.18.0</nodeVersion>
+                    <nodeVersion>v14.17.0</nodeVersion>
                     <installDirectory>${webapp.dir}</installDirectory>
                     <workingDirectory>${webapp.dir}</workingDirectory>
                     <npmDownloadRoot>${repo.npm}</npmDownloadRoot>
diff --git a/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js b/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js
index 2744469..94a18df 100644
--- a/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js
+++ b/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js
@@ -26,36 +26,36 @@
 function createChart(data, container, title, unit, lineStroke, nodeColour) {
     // Set the dimensions of the canvas
     var margin = {
-        top : 30,
-        right : 20,
-        bottom : 30,
-        left : 50
+        top: 30,
+        right: 20,
+        bottom: 30,
+        left: 50
     }, width = 600 - margin.left - margin.right, height = 270 - margin.top
-            - margin.bottom;
+        - margin.bottom;
 
     // Set the ranges
-    var x = d3.time.scale().range([ 0, width ]);
-    var y = d3.scale.linear().range([ height, 0 ]);
+    var x = d3.time.scale().range([0, width]);
+    var y = d3.scale.linear().range([height, 0]);
 
     // Define the axes
     var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5).innerTickSize(
-            -height).outerTickSize(0).tickPadding(10);
+        -height).outerTickSize(0).tickPadding(10);
 
     var yAxis = d3.svg.axis().scale(y).orient("left").ticks(10).innerTickSize(
-            -width).outerTickSize(0).tickPadding(10);
+        -width).outerTickSize(0).tickPadding(10);
 
     // Define the line
-    var valueline = d3.svg.line().x(function(d) {
+    var valueline = d3.svg.line().x(function (d) {
         return x(d.timestamp);
-    }).y(function(d) {
+    }).y(function (d) {
         return y(d.value);
     });
 
     // Add the svg canvas to the container
     var svg = d3.select(container).append("svg").attr("preserveAspectRatio",
-            "xMinYMin meet").attr("viewBox", "0 0 600 400").classed(
-            "svg-content-responsive", true).append("g").attr("transform",
-            "translate(" + margin.left + "," + margin.top + ")");
+        "xMinYMin meet").attr("viewBox", "0 0 600 400").classed(
+        "svg-content-responsive", true).append("g").attr("transform",
+        "translate(" + margin.left + "," + margin.top + ")");
 
     // Set the unit for the value
     svg.attr("unit", unit);
@@ -63,12 +63,12 @@
     data = formatDataForChart(data);
 
     // Scale the range of the data
-    x.domain(d3.extent(data, function(d) {
+    x.domain(d3.extent(data, function (d) {
         return d.timestamp;
     }));
-    y.domain([ 0, d3.max(data, function(d) {
+    y.domain([0, d3.max(data, function (d) {
         return Math.ceil((d.value + 1) / 10) * 10;
-    }) ]);
+    })]);
 
     // Set the colour of the line
     if (!lineStroke) {
@@ -82,36 +82,36 @@
 
     // Add the valueline path
     svg.append("path").attr("class", "line").data(data).attr("unit", unit)
-            .attr("stroke", lineStroke).attr("d", valueline(data));
+        .attr("stroke", lineStroke).attr("d", valueline(data));
 
     // Add the scatterplot
     svg.selectAll("circle").data(data).enter().append("circle").attr("r", 3.5)
-            .attr("class", "circle").attr("fill", nodeColour).attr("cx",
-                    function(d) {
-                        return x(d.timestamp);
-                    }).attr("cy", function(d) {
-                return y(d.value);
-            })
+        .attr("class", "circle").attr("fill", nodeColour).attr("cx",
+        function (d) {
+            return x(d.timestamp);
+        }).attr("cy", function (d) {
+        return y(d.value);
+    })
 
-            // Apply the tooltip to each node
-            .on("mouseover", handleMouseOver)
-            .on("mouseout", handleMouseOut);
+        // Apply the tooltip to each node
+        .on("mouseover", handleMouseOver)
+        .on("mouseout", handleMouseOut);
 
     // Add the X Axis
     svg.append("g").attr("class", "x axis").attr("transform",
-            "translate(0," + height + ")").call(xAxis);
+        "translate(0," + height + ")").call(xAxis);
 
     // Add the Y Axis
     svg.append("g").attr("class", "y axis").call(yAxis);
 
     // Add the title
     svg.append("text").attr("x", (width / 2)).attr("y", 0 - (margin.top / 2))
-            .attr("text-anchor", "middle").style("font-size", "16px").style(
-                    "text-decoration", "underline").text(title);
+        .attr("text-anchor", "middle").style("font-size", "16px").style(
+        "text-decoration", "underline").text(title);
 
     // Add the background
     svg.selectAll(".tick:not(:first-of-type) line").attr("stroke", "#777")
-            .attr("stroke-dasharray", "2,2");
+        .attr("stroke-dasharray", "2,2");
 }
 
 /*
@@ -122,8 +122,8 @@
     var data = [];
     for (var i = 0; i < 30; i++) {
         data.push({
-            timestamp : new Date().getTime() - (i * 5000),
-            value : Math.floor(Math.random() * 100) + 1
+            timestamp: new Date().getTime() - (i * 5000),
+            value: Math.floor(Math.random() * 100) + 1
         });
     }
     return data;
@@ -134,12 +134,12 @@
  */
 function updateChart(container, data, nodeColour) {
     var margin = {
-        top : 30,
-        right : 20,
-        bottom : 30,
-        left : 50
+        top: 30,
+        right: 20,
+        bottom: 30,
+        left: 50
     }, width = 600 - margin.left - margin.right, height = 270 - margin.top
-            - margin.bottom;
+        - margin.bottom;
 
     data = formatDataForChart(data);
 
@@ -147,28 +147,28 @@
     var svg = d3.select(container);
 
     // Set the ranges
-    var x = d3.time.scale().range([ 0, width ]);
-    var y = d3.scale.linear().range([ height, 0 ]);
+    var x = d3.time.scale().range([0, width]);
+    var y = d3.scale.linear().range([height, 0]);
 
     // Define the axes
     var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5).innerTickSize(
-            -height).outerTickSize(0).tickPadding(10);
+        -height).outerTickSize(0).tickPadding(10);
 
     var yAxis = d3.svg.axis().scale(y).orient("left").ticks(10).innerTickSize(
-            -width).outerTickSize(0).tickPadding(10);
+        -width).outerTickSize(0).tickPadding(10);
 
     // Scale the range of the data
-    x.domain(d3.extent(data, function(d) {
+    x.domain(d3.extent(data, function (d) {
         return d.timestamp;
     }));
-    y.domain([ 0, d3.max(data, function(d) {
+    y.domain([0, d3.max(data, function (d) {
         return Math.ceil((d.value + 1) / 10) * 10;
-    }) ]);
+    })]);
 
     // Update the valueline path
-    var valueline = d3.svg.line().x(function(d) {
+    var valueline = d3.svg.line().x(function (d) {
         return x(d.timestamp);
-    }).y(function(d) {
+    }).y(function (d) {
         return y(d.value);
     });
 
@@ -182,24 +182,24 @@
 
     // Make the changes
     svg.select(".line").data(data) // change the line
-    .transition().duration(750).attr("d", valueline(data));
+        .transition().duration(750).attr("d", valueline(data));
     svg.select(".x.axis") // change the x axis
-    .transition().duration(750).call(xAxis.ticks(5));
+        .transition().duration(750).call(xAxis.ticks(5));
     svg.select(".y.axis") // change the y axis
-    .transition().duration(750).call(yAxis);
+        .transition().duration(750).call(yAxis);
 
     // Redraw the nodes based on the new data
     svg.select("svg").select("g").selectAll("circle").data(data).enter()
-            .append("circle").attr("r", 3.5).attr("class", "circle").attr(
-                    "fill", nodeColour).attr("cx", function(d) {
-                return x(d.timestamp);
-            }).attr("cy", function(d) {
-                return y(d.value);
-            })
+        .append("circle").attr("r", 3.5).attr("class", "circle").attr(
+        "fill", nodeColour).attr("cx", function (d) {
+        return x(d.timestamp);
+    }).attr("cy", function (d) {
+        return y(d.value);
+    })
 
-            // Apply the tooltip to each node
-            .on("mouseover", handleMouseOver)
-            .on("mouseout", handleMouseOut);
+        // Apply the tooltip to each node
+        .on("mouseover", handleMouseOver)
+        .on("mouseout", handleMouseOut);
 
 }
 
@@ -208,7 +208,7 @@
  */
 function initTooltip() {
     d3.select("body").append("div").attr("class", "tooltip").attr("id",
-            "tooltip").style("opacity", 0);
+        "tooltip").style("opacity", 0);
 }
 
 /*
@@ -220,7 +220,7 @@
 
 function formatDataForChart(data) {
     // Format the data for the chart
-    data.forEach(function(d) {
+    data.forEach(function (d) {
         d.value = +d.value;
     });
 
@@ -231,17 +231,17 @@
     var unit = svg.select(".line").attr("unit");
 
     d3.select("body").select(".tooltip").transition()
-            .duration(50).style("opacity", 1);
+        .duration(50).style("opacity", 1);
     d3.select("body").select(".tooltip").html(
-            formatDate(new Date(d.timestamp)) + "<br/>"
-                    + d.value + (unit ? " " + unit : ""))
-            .style("left", (d3.event.pageX) + "px").style(
-                    "top", (d3.event.pageY - 28) + "px");
+        formatDate(new Date(d.timestamp)) + "<br/>"
+        + d.value + (unit ? " " + unit : ""))
+        .style("left", (d3.event.pageX) + "px").style(
+        "top", (d3.event.pageY - 28) + "px");
 }
 
 function handleMouseOut(d) {
     d3.select("body").select(".tooltip").transition()
-            .duration(500).style("opacity", 0);
+        .duration(500).style("opacity", 0);
 }
 
-export { initTooltip, createChart, updateChart, generateRandomData };
\ No newline at end of file
+export {initTooltip, createChart, updateChart, generateRandomData};