Repository: GoogleCloudDataproc/dataproc-jupyter-plugin (language: python)

Issue:
Sprint7 feedback changes



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 68bc17f9..5ea04662 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,24 @@
+*.bundle.*
+lib/
+node_modules/
+*.log
+.eslintcache
+.stylelintcache
+*.egg-info/
+.ipynb_checkpoints
+*.tsbuildinfo
+dataproc_plugin/labextension
+# Version file is handled by hatchling
+dataproc_plugin/_version.py
+
+# Integration tests
+ui-tests/test-results/
+ui-tests/playwright-report/
+
+# Created by https://www.gitignore.io/api/python
+# Edit at https://www.gitignore.io/?templates=python
+
+### Python ###
 # Byte-compiled / optimized / DLL files
 __pycache__/
 *.py[cod]
@@ -20,8 +41,8 @@ parts/
 sdist/
 var/
 wheels/
+pip-wheel-metadata/
 share/python-wheels/
-*.egg-info/
 .installed.cfg
 *.egg
 MANIFEST
@@ -44,27 +65,16 @@ htmlcov/
 .coverage.*
 .cache
 nosetests.xml
+coverage/
 coverage.xml
 *.cover
-*.py,cover
 .hypothesis/
 .pytest_cache/
-cover/
 
 # Translations
 *.mo
 *.pot
 
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
 # Scrapy stuff:
 .scrapy
 
@@ -72,62 +82,17 @@ instance/
 docs/_build/
 
 # PyBuilder
-.pybuilder/
 target/
 
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
 # pyenv
-#   For a library or package, you might want to ignore these files since the code is
-#   intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-#   However, in case of collaboration, if having platform-specific dependencies or dependencies
-#   having no cross-platform support, pipenv may install dependencies that don't work, or not
-#   install all needed dependencies.
-#Pipfile.lock
-
-# poetry
-#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
-#   This is especially recommended for binary packages to ensure reproducibility, and is more
-#   commonly ignored for libraries.
-#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
-#poetry.lock
-
-# pdm
-#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
-#pdm.lock
-#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
-#   in version control.
-#   https://pdm.fming.dev/#use-with-ide
-.pdm.toml
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
-__pypackages__/
-
-# Celery stuff
+.python-version
+
+# celery beat schedule file
 celerybeat-schedule
-celerybeat.pid
 
 # SageMath parsed files
 *.sage.py
 
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
 # Spyder project settings
 .spyderproject
 .spyproject
@@ -135,6 +100,11 @@ venv.bak/
 # Rope project settings
 .ropeproject
 
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
 # mkdocs documentation
 /site
 
@@ -146,15 +116,10 @@ dmypy.json
 # Pyre type checker
 .pyre/
 
-# pytype static type analyzer
-.pytype/
+# End of https://www.gitignore.io/api/python
 
-# Cython debug symbols
-cython_debug/
+# OSX files
+.DS_Store
 
-# PyCharm
-#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
-#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
-#  and can be added to the global gitignore or merged into this file.  For a more nuclear
-#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
-#.idea/
+# Yarn cache
+.yarn/
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..391b0424
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,6 @@
+node_modules
+**/node_modules
+**/lib
+**/package.json
+!/package.json
+dataproc_plugin
diff --git a/.yarnrc.yml b/.yarnrc.yml
new file mode 100644
index 00000000..fe1125f5
--- /dev/null
+++ b/.yarnrc.yml
@@ -0,0 +1,3 @@
+enableImmutableInstalls: false
+
+nodeLinker: node-modules
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..2d352af4
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+<!-- <START NEW CHANGELOG ENTRY> -->
+
+<!-- <END NEW CHANGELOG ENTRY> -->
diff --git a/README.md b/README.md
index 8ed54b9c..c9e81bbf 100644
--- a/README.md
+++ b/README.md
@@ -1,1 +1,138 @@
-# dataproc-jupyter-plugin
\ No newline at end of file
+# dataproc_plugin
+
+[![Github Actions Status](https://github.com/github_username/dataproc_plugin/workflows/Build/badge.svg)](https://github.com/github_username/dataproc_plugin/actions/workflows/build.yml)[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/github_username/dataproc_plugin/main?urlpath=lab)
+It is a plugin to work with dataproc services in Jupyterlab
+
+This extension is composed of a Python package named `dataproc_plugin`
+for the server extension and a NPM package named `dataproc_plugin`
+for the frontend extension.
+
+## Requirements
+
+- JupyterLab >= 4.0.0
+
+## Install
+
+To install the extension, execute:
+
+```bash
+pip install dataproc_plugin
+```
+
+## Uninstall
+
+To remove the extension, execute:
+
+```bash
+pip uninstall dataproc_plugin
+```
+
+## Troubleshoot
+
+If you are seeing the frontend extension, but it is not working, check
+that the server extension is enabled:
+
+```bash
+jupyter server extension list
+```
+
+If the server extension is installed and enabled, but you are not seeing
+the frontend extension, check the frontend extension is installed:
+
+```bash
+jupyter labextension list
+```
+
+## Contributing
+
+### Development install
+
+Note: You will need NodeJS to build the extension package.
+
+The `jlpm` command is JupyterLab's pinned version of
+[yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use
+`yarn` or `npm` in lieu of `jlpm` below.
+
+```bash
+# Clone the repo to your local environment
+# Change directory to the dataproc_plugin directory
+# Install package in development mode
+pip install -e ".[test]"
+# Link your development version of the extension with JupyterLab
+jupyter labextension develop . --overwrite
+# Server extension must be manually installed in develop mode
+jupyter server extension enable dataproc_plugin
+# Rebuild extension Typescript source after making changes
+jlpm build
+```
+
+You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.
+
+```bash
+# Watch the source directory in one terminal, automatically rebuilding when needed
+jlpm watch
+# Run JupyterLab in another terminal
+jupyter lab
+```
+
+With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).
+
+By default, the `jlpm build` command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command:
+
+```bash
+jupyter lab build --minimize=False
+```
+
+### Development uninstall
+
+```bash
+# Server extension must be manually disabled in develop mode
+jupyter server extension disable dataproc_plugin
+pip uninstall dataproc_plugin
+```
+
+In development mode, you will also need to remove the symlink created by `jupyter labextension develop`
+command. To find its location, you can run `jupyter labextension list` to figure out where the `labextensions`
+folder is located. Then you can remove the symlink named `dataproc_plugin` within that folder.
+
+### Testing the extension
+
+#### Server tests
+
+This extension is using [Pytest](https://docs.pytest.org/) for Python code testing.
+
+Install test dependencies (needed only once):
+
+```sh
+pip install -e ".[test]"
+# Each time you install the Python package, you need to restore the front-end extension link
+jupyter labextension develop . --overwrite
+```
+
+To execute them, run:
+
+```sh
+pytest -vv -r ap --cov dataproc_plugin
+```
+
+#### Frontend tests
+
+This extension is using [Jest](https://jestjs.io/) for JavaScript code testing.
+
+To execute them, execute:
+
+```sh
+jlpm
+jlpm test
+```
+
+#### Integration tests
+
+This extension uses [Playwright](https://playwright.dev/docs/intro/) for the integration tests (aka user level tests).
+More precisely, the JupyterLab helper [Galata](https://github.com/jupyterlab/jupyterlab/tree/master/galata) is used to handle testing the extension in JupyterLab.
+
+More information are provided within the [ui-tests](./ui-tests/README.md) README.
+
+### Packaging the extension
+
+See [RELEASE](RELEASE.md)
diff --git a/RELEASE.md b/RELEASE.md
new file mode 100644
index 00000000..531198b7
--- /dev/null
+++ b/RELEASE.md
@@ -0,0 +1,77 @@
+# Making a new release of dataproc_plugin
+
+The extension can be published to `PyPI` and `npm` manually or using the [Jupyter Releaser](https://github.com/jupyter-server/jupyter_releaser).
+
+## Manual release
+
+### Python package
+
+This extension can be distributed as Python packages. All of the Python
+packaging instructions are in the `pyproject.toml` file to wrap your extension in a
+Python package. Before generating a package, you first need to install some tools:
+
+```bash
+pip install build twine hatch
+```
+
+Bump the version using `hatch`. By default this will create a tag.
+See the docs on [hatch-nodejs-version](https://github.com/agoose77/hatch-nodejs-version#semver) for details.
+
+```bash
+hatch version <new-version>
+```
+
+Make sure to clean up all the development files before building the package:
+
+```bash
+jlpm clean:all
+```
+
+You could also clean up the local git repository:
+
+```bash
+git clean -dfX
+```
+
+To create a Python source package (`.tar.gz`) and the binary package (`.whl`) in the `dist/` directory, do:
+
+```bash
+python -m build
+```
+
+> `python setup.py sdist bdist_wheel` is deprecated and will not work for this package.
+
+Then to upload the package to PyPI, do:
+
+```bash
+twine upload dist/*
+```
+
+### NPM package
+
+To publish the frontend part of the extension as a NPM package, do:
+
+```bash
+npm login
+npm publish --access public
+```
+
+## Automated releases with the Jupyter Releaser
+
+The extension repository should already be compatible with the Jupyter Releaser.
+
+Check out the [workflow documentation](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html) for more information.
+
+Here is a summary of the steps to cut a new release:
+
+- Add `ADMIN_GITHUB_TOKEN`, `PYPI_TOKEN` and `NPM_TOKEN` to the [Github Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets) in the repository
+- Go to the Actions panel
+- Run the "Step 1: Prep Release" workflow
+- Check the draft changelog
+- Run the "Step 2: Publish Release" workflow
+
+## Publishing to `conda-forge`
+
+If the package is not on conda forge yet, check the documentation to learn how to add it: https://conda-forge.org/docs/maintainer/adding_pkgs.html
+
+Otherwise a bot should pick up the new version publish to PyPI, and open a new PR on the feedstock repository automatically.
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 00000000..bbe1464d
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,18 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+module.exports = require('@jupyterlab/testutils/lib/babel.config');
diff --git a/binder/environment.yml b/binder/environment.yml
new file mode 100644
index 00000000..3bb8505e
--- /dev/null
+++ b/binder/environment.yml
@@ -0,0 +1,21 @@
+# a mybinder.org-ready environment for demoing dataproc_plugin
+# this environment may also be used locally on Linux/MacOS/Windows, e.g.
+#
+#   conda env update --file binder/environment.yml
+#   conda activate dataproc-plugin-demo
+#
+name: dataproc-plugin-demo
+
+channels:
+  - conda-forge
+
+dependencies:
+  # runtime dependencies
+  - python >=3.10,<3.11.0a0
+  - jupyterlab >=4.0.0,<5
+  # labextension build dependencies
+  - nodejs >=18,<19
+  - pip
+  - wheel
+  # additional packages for demos
+  # - ipywidgets
diff --git a/binder/postBuild b/binder/postBuild
new file mode 100755
index 00000000..ed944b78
--- /dev/null
+++ b/binder/postBuild
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+""" perform a development install of dataproc_plugin
+
+    On Binder, this will run _after_ the environment has been fully created from
+    the environment.yml in this directory.
+
+    This script should also run locally on Linux/MacOS/Windows:
+
+        python3 binder/postBuild
+"""
+import subprocess
+import sys
+from pathlib import Path
+
+
+ROOT = Path.cwd()
+
+def _(*args, **kwargs):
+    """ Run a command, echoing the args
+
+        fails hard if something goes wrong
+    """
+    print("\n\t", " ".join(args), "\n")
+    return_code = subprocess.call(args, **kwargs)
+    if return_code != 0:
+        print("\nERROR", return_code, " ".join(args))
+        sys.exit(return_code)
+
+# verify the environment is self-consistent before even starting
+_(sys.executable, "-m", "pip", "check")
+
+# install the labextension
+_(sys.executable, "-m", "pip", "install", "-e", ".")
+_(sys.executable, "-m", "jupyter", "labextension", "develop", "--overwrite", ".")
+_(
+    sys.executable,
+    "-m",
+    "jupyter",
+    "server",
+    "extension",
+    "enable",
+    "dataproc_plugin",
+)
+
+# verify the environment the extension didn't break anything
+_(sys.executable, "-m", "pip", "check")
+
+# list the extensions
+_("jupyter", "server", "extension", "list")
+
+# initially list installed extensions to determine if there are any surprises
+_("jupyter", "labextension", "list")
+
+
+print("JupyterLab with dataproc_plugin is ready to run with:\n")
+print("\tjupyter lab\n")
diff --git a/contributing.md b/contributing.md
new file mode 100644
index 00000000..8956a61b
--- /dev/null
+++ b/contributing.md
@@ -0,0 +1,32 @@
+# How to Contribute
+
+We would love to accept your patches and contributions to this project.
+
+## Before you begin
+
+### Sign our Contributor License Agreement
+
+Contributions to this project must be accompanied by a
+[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
+You (or your employer) retain the copyright to your contribution; this simply
+gives us permission to use and redistribute your contributions as part of the
+project.
+
+If you or your current employer have already signed the Google CLA (even if it
+was for a different project), you probably don't need to do it again.
+
+Visit <https://cla.developers.google.com/> to see your current agreements or to
+sign a new one.
+
+### Review our Community Guidelines
+
+This project follows [Google's Open Source Community
+Guidelines](https://opensource.google/conduct/).
+
+## Contribution process
+
+### Code Reviews
+
+All submissions, including submissions by project members, require review. We
+use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
+for this purpose.
diff --git a/dataproc_plugin/__init__.py b/dataproc_plugin/__init__.py
new file mode 100644
index 00000000..e388e29c
--- /dev/null
+++ b/dataproc_plugin/__init__.py
@@ -0,0 +1,45 @@
+# Copyright 2023 Google LLC
+#
+# 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
+#
+# https://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.from ._version import __version__
+
+from .handlers import setup_handlers
+
+
+def _jupyter_labextension_paths():
+    return [{
+        "src": "labextension",
+        "dest": "dataproc_plugin"
+    }]
+
+
+def _jupyter_server_extension_points():
+    return [{
+        "module": "dataproc_plugin"
+    }]
+
+
+def _load_jupyter_server_extension(server_app):
+    """Registers the API handler to receive HTTP requests from the frontend extension.
+
+    Parameters
+    ----------
+    server_app: jupyterlab.labapp.LabApp
+        JupyterLab application instance
+    """
+    setup_handlers(server_app.web_app)
+    name = "dataproc_plugin"
+    server_app.log.info(f"Registered {name} server extension")
+
+
+# For backward compatibility with notebook server - useful for Binder/JupyterHub
+load_jupyter_server_extension = _load_jupyter_server_extension
diff --git a/dataproc_plugin/handlers.py b/dataproc_plugin/handlers.py
new file mode 100644
index 00000000..210bcf23
--- /dev/null
+++ b/dataproc_plugin/handlers.py
@@ -0,0 +1,190 @@
+# Copyright 2023 Google LLC
+#
+# 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
+#
+# https://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.
+
+import json
+from jupyter_server.base.handlers import APIHandler
+from jupyter_server.utils import url_path_join
+import tornado
+import subprocess
+from datetime import datetime
+from datetime import datetime
+from cachetools import TTLCache
+
+credentials_cache = None
+
+def get_cached_credentials():
+    global credentials_cache
+    
+    try:
+        cmd = "gcloud config config-helper --format=json"
+        process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+        output, error = process.communicate()
+        if process.returncode == 0:
+            config_data = json.loads(output)
+            credentials = {
+                'project_id': config_data['configuration']['properties']['core']['project'],
+                'region_id': config_data['configuration']['properties']['compute']['region'],
+                'access_token': config_data['credential']['access_token']
+            }
+
+            token_expiry = config_data['credential']['token_expiry']
+            utc_datetime = datetime.strptime(token_expiry, '%Y-%m-%dT%H:%M:%SZ')
+            credentials_cache = TTLCache(maxsize=1, ttl=(utc_datetime - datetime.utcnow()).total_seconds())
+            credentials_cache['credentials'] = credentials
+            return credentials
+        else:
+            cmd = "gcloud config get-value account"
+            process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+            output, error = process.communicate()
+            if output == '':
+                cmd = "gcloud config get-value project"
+                process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+                project, error = process.communicate()
+                if project == '':
+                    credentials = {
+                'project_id': '',
+                'region_id': '',
+                'access_token': '',
+                'config_error': 1,
+                'login_error': 0
+                }
+                else:
+                    cmd = "gcloud config get-value compute/region"
+                    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+                    region, error = process.communicate()
+                    if region == '':
+                        credentials = {
+                        'project_id': '',
+                        'region_id': '',
+                        'access_token': '',
+                        'config_error': 1,
+                        'login_error': 0
+                     }
+                    else:
+                        credentials = {
+                        'project_id': '',
+                        'region_id': '',
+                        'access_token': '',
+                        'config_error': 0,
+                        'login_error': 1
+                            }
+            else:
+                credentials = {
+                'project_id': '',
+                'region_id': '',
+                'access_token': '',
+                'config_error': 1,
+                'login_error': 0
+                }
+            credentials_cache = TTLCache(maxsize=1, ttl=5)
+            credentials_cache['credentials'] = credentials
+            return credentials
+    except Exception:
+        credentials = {
+            'access_token': '',
+            'config_error': 1
+        }
+        credentials_cache = TTLCache(maxsize=1, ttl=2)
+        credentials_cache['credentials'] = credentials
+
+        return credentials
+
+class TestHandler(APIHandler):
+    # The following decorator should be present on all verb methods (head, get, post,
+    # patch, put, delete, options) to ensure only authorized user can request the
+    # Jupyter server
+    @tornado.web.authenticated
+    def get(self):
+        self.finish(json.dumps({
+            "data": "This is /dataproc-plugin/get-example endpoint!"
+        }))
+
+class RouteHandler(APIHandler):
+    # The following decorator should be present on all verb methods (head, get, post,
+    # patch, put, delete, options) to ensure only authorized user can request the
+    # Jupyter server
+    credentials_cache = None
+    @tornado.web.authenticated
+    def get(self):
+        try:
+            if credentials_cache is None or 'credentials' not in credentials_cache:
+                cached_credentials = get_cached_credentials()
+                self.finish(json.dumps(cached_credentials))
+               
+            else:
+                self.finish(json.dumps(credentials_cache['credentials']))
+        except Exception:
+            print("exception gcloud")
+            cached_credentials = get_cached_credentials()
+            self.finish(json.dumps(cached_credentials))
+
+class LoginHandler(APIHandler):
+    @tornado.web.authenticated
+    def get(self):
+        cmd = "gcloud auth login"
+        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+        output, _ = process.communicate()
+         # Check if the authentication was successful
+        if process.returncode == 0:
+            self.finish({'login':'SUCCEEDED'})
+        else:
+            self.finish({'login':'FAILED'})
+
+class ConfigHandler(APIHandler):
+    @tornado.web.authenticated
+    def post(self):
+        global credentials_cache
+        input_data = self.get_json_body()
+        project_id = input_data["projectId"]
+        region = input_data["region"]
+        cmd = 'gcloud config set project '+project_id
+        project_set = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+        output, _ = project_set.communicate()
+        if project_set.returncode == 0:
+            cmd = 'gcloud config set compute/region '+region
+            region_set = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+            output, _ = region_set.communicate()
+            if region_set.returncode == 0:
+                credentials_cache = None
+                self.finish({'config':'Project and Region Updated Successfully'})
+            else:
+                self.finish({'config':'Project and Region Updation Failed'})
+        else:
+            self.finish({'config':'Project and Region Updation Failed'})
+
+
+def setup_handlers(web_app):
+    host_pattern = ".*$"
+
+    base_url = web_app.settings["base_url"]
+    route_pattern = url_path_join(base_url, "dataproc-plugin", "get-example")
+    handlers = [(route_pattern, RouteHandler)]
+    web_app.add_handlers(host_pattern, handlers)
+
+    # Prepend the base_url so that it works in a JupyterHub setting
+    route_pattern = url_path_join(base_url, "dataproc-plugin", "credentials")
+    handlers = [(route_pattern, RouteHandler)]
+    web_app.add_handlers(host_pattern, handlers)
+    
+    route_pattern = url_path_join(base_url, "dataproc-plugin", "login")
+    handlers = [(route_pattern, LoginHandler)]
+    web_app.add_handlers(host_pattern, handlers)
+
+    route_pattern = url_path_join(base_url, "dataproc-plugin", "configuration")
+    handlers = [(route_pattern, ConfigHandler)]
+    web_app.add_handlers(host_pattern, handlers)
+
+    route_pattern = url_path_join(base_url, "dataproc-plugin", "get-example")
+    handlers = [(route_pattern, TestHandler)]
+    web_app.add_handlers(host_pattern, handlers)
diff --git a/install.json b/install.json
new file mode 100644
index 00000000..4611bbd4
--- /dev/null
+++ b/install.json
@@ -0,0 +1,5 @@
+{
+  "packageManager": "python",
+  "packageName": "dataproc_plugin",
+  "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package dataproc_plugin"
+}
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 00000000..f1773263
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,45 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+const jestJupyterLab = require('@jupyterlab/testutils/lib/jest-config');
+
+const esModules = [
+  '@codemirror',
+  '@jupyter/ydoc',
+  '@jupyterlab/',
+  'lib0',
+  'nanoid',
+  'vscode-ws-jsonrpc',
+  'y-protocols',
+  'y-websocket',
+  'yjs'
+].join('|');
+
+const baseConfig = jestJupyterLab(__dirname);
+
+module.exports = {
+  ...baseConfig,
+  automock: false,
+  collectCoverageFrom: [
+    'src/**/*.{ts,tsx}',
+    '!src/**/*.d.ts',
+    '!src/**/.ipynb_checkpoints/*'
+  ],
+  coverageReporters: ['lcov', 'text'],
+  testRegex: 'src/.*/.*.spec.ts[x]?$',
+  transformIgnorePatterns: [`/node_modules/(?!${esModules}).+`]
+};
diff --git a/jupyter-config/nb-config/dataproc_plugin.json b/jupyter-config/nb-config/dataproc_plugin.json
new file mode 100644
index 00000000..cf22466a
--- /dev/null
+++ b/jupyter-config/nb-config/dataproc_plugin.json
@@ -0,0 +1,7 @@
+{
+  "NotebookApp": {
+    "nbserver_extensions": {
+      "dataproc_plugin": true
+    }
+  }
+}
diff --git a/jupyter-config/server-config/dataproc_plugin.json b/jupyter-config/server-config/dataproc_plugin.json
new file mode 100644
index 00000000..66828bc7
--- /dev/null
+++ b/jupyter-config/server-config/dataproc_plugin.json
@@ -0,0 +1,7 @@
+{
+  "ServerApp": {
+    "jpserver_extensions": {
+      "dataproc_plugin": true
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..21c80f9f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,208 @@
+{
+  "name": "dataproc_plugin",
+  "version": "1.0.3",
+  "description": "It is a plugin to work with dataproc services in Jupyterlab",
+  "keywords": [
+    "jupyter",
+    "jupyterlab",
+    "jupyterlab-extension"
+  ],
+  "homepage": "https://github.com/github_username/dataproc_plugin",
+  "bugs": {
+    "url": "https://github.com/github_username/dataproc_plugin/issues"
+  },
+  "license": "BSD-3-Clause",
+  "author": {
+    "name": "google",
+    "email": "xyz@google.com"
+  },
+  "files": [
+    "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
+    "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
+    "schema/*.json"
+  ],
+  "main": "lib/index.js",
+  "types": "lib/index.d.ts",
+  "style": "style/index.css",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/github_username/dataproc_plugin.git"
+  },
+  "workspaces": [
+    "ui-tests"
+  ],
+  "scripts": {
+    "build": "jlpm build:lib && jlpm build:labextension:dev",
+    "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
+    "build:labextension": "jupyter labextension build .",
+    "build:labextension:dev": "jupyter labextension build --development True .",
+    "build:lib": "tsc --sourceMap",
+    "build:lib:prod": "tsc",
+    "clean": "jlpm clean:lib",
+    "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
+    "clean:lintcache": "rimraf .eslintcache .stylelintcache",
+    "clean:labextension": "rimraf dataproc_plugin/labextension dataproc_plugin/_version.py",
+    "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
+    "eslint": "jlpm eslint:check --fix",
+    "eslint:check": "eslint . --cache --ext .ts,.tsx",
+    "install:extension": "jlpm build",
+    "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
+    "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
+    "prettier": "jlpm prettier:base --write --list-different",
+    "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
+    "prettier:check": "jlpm prettier:base --check",
+    "stylelint": "jlpm stylelint:check --fix",
+    "stylelint:check": "stylelint --cache \"style/**/*.css\"",
+    "test": "jest --coverage",
+    "watch": "run-p watch:src watch:labextension",
+    "watch:src": "tsc -w --sourceMap",
+    "watch:labextension": "jupyter labextension watch ."
+  },
+  "dependencies": {
+    "@jupyterlab/application": "^4.0.0",
+    "@jupyterlab/coreutils": "^6.0.0",
+    "@jupyterlab/launcher": "^4.0.0",
+    "@jupyterlab/mainmenu": "^4.0.0",
+    "@jupyterlab/services": "^7.0.0",
+    "@jupyterlab/settingregistry": "^4.0.0",
+    "react-spinners": "^0.13.8",
+    "react-table": "^7.8.0",
+    "react-tagsinput": "^3.20.3",
+    "react-toastify": "^9.1.3",
+    "semantic-ui-css": "^2.5.0",
+    "semantic-ui-react": "^2.1.4"
+  },
+  "devDependencies": {
+    "@jupyterlab/builder": "^3.5.3",
+    "@jupyterlab/testutils": "^4.0.0",
+    "@types/jest": "^29.2.0",
+    "@types/json-schema": "^7.0.11",
+    "@types/react": "^18.0.26",
+    "@types/react-table": "^7.7.14",
+    "@types/react-tagsinput": "^3.20.0",
+    "@typescript-eslint/eslint-plugin": "^5.55.0",
+    "@typescript-eslint/parser": "^5.55.0",
+    "css-loader": "^6.7.1",
+    "eslint": "^8.36.0",
+    "eslint-config-prettier": "^8.7.0",
+    "eslint-plugin-prettier": "^4.2.1",
+    "jest": "^29.2.0",
+    "mkdirp": "^1.0.3",
+    "npm-run-all": "^4.1.5",
+    "prettier": "^2.8.7",
+    "rimraf": "^4.4.1",
+    "source-map-loader": "^1.0.2",
+    "style-loader": "^3.3.1",
+    "stylelint": "^14.9.1",
+    "stylelint-config-prettier": "^9.0.4",
+    "stylelint-config-recommended": "^8.0.0",
+    "stylelint-config-standard": "^26.0.0",
+    "stylelint-prettier": "^2.0.0",
+    "typescript": "~5.0.2",
+    "yjs": "^13.5.0"
+  },
+  "sideEffects": [
+    "style/*.css",
+    "style/index.js"
+  ],
+  "styleModule": "style/index.js",
+  "publishConfig": {
+    "access": "public"
+  },
+  "jupyterlab": {
+    "discovery": {
+      "server": {
+        "managers": [
+          "pip"
+        ],
+        "base": {
+          "name": "dataproc_plugin"
+        }
+      }
+    },
+    "extension": true,
+    "outputDir": "dataproc_plugin/labextension",
+    "schemaDir": "schema"
+  },
+  "eslintIgnore": [
+    "node_modules",
+    "dist",
+    "coverage",
+    "**/*.d.ts",
+    "tests",
+    "**/__tests__",
+    "ui-tests"
+  ],
+  "eslintConfig": {
+    "extends": [
+      "eslint:recommended",
+      "plugin:@typescript-eslint/eslint-recommended",
+      "plugin:@typescript-eslint/recommended",
+      "plugin:prettier/recommended"
+    ],
+    "parser": "@typescript-eslint/parser",
+    "parserOptions": {
+      "project": "tsconfig.json",
+      "sourceType": "module"
+    },
+    "plugins": [
+      "@typescript-eslint"
+    ],
+    "rules": {
+      "@typescript-eslint/naming-convention": [
+        "error",
+        {
+          "selector": "interface",
+          "format": [
+            "PascalCase"
+          ],
+          "custom": {
+            "regex": "^I[A-Z]",
+            "match": true
+          }
+        }
+      ],
+      "@typescript-eslint/no-unused-vars": [
+        "warn",
+        {
+          "args": "none"
+        }
+      ],
+      "@typescript-eslint/no-explicit-any": "off",
+      "@typescript-eslint/no-namespace": "off",
+      "@typescript-eslint/no-use-before-define": "off",
+      "@typescript-eslint/quotes": [
+        "error",
+        "single",
+        {
+          "avoidEscape": true,
+          "allowTemplateLiterals": false
+        }
+      ],
+      "curly": [
+        "error",
+        "all"
+      ],
+      "eqeqeq": "error",
+      "prefer-arrow-callback": "error"
+    }
+  },
+  "prettier": {
+    "singleQuote": true,
+    "trailingComma": "none",
+    "arrowParens": "avoid",
+    "endOfLine": "auto"
+  },
+  "stylelint": {
+    "extends": [
+      "stylelint-config-recommended",
+      "stylelint-config-standard",
+      "stylelint-prettier/recommended"
+    ],
+    "rules": {
+      "property-no-vendor-prefix": null,
+      "selector-no-vendor-prefix": null,
+      "value-no-vendor-prefix": null
+    }
+  }
+}
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..be488a36
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,88 @@
+[build-system]
+requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version"]
+build-backend = "hatchling.build"
+
+[project]
+name = "dataproc_plugin"
+readme = "README.md"
+license = { file = "LICENSE" }
+requires-python = ">=3.8"
+classifiers = [
+    "Framework :: Jupyter",
+    "Framework :: Jupyter :: JupyterLab",
+    "Framework :: Jupyter :: JupyterLab :: 4",
+    "Framework :: Jupyter :: JupyterLab :: Extensions",
+    "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt",
+    "License :: OSI Approved :: BSD License",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+]
+dependencies = [
+    "jupyter_server>=2.0.1,<3"
+]
+dynamic = ["version", "description", "authors", "urls", "keywords"]
+
+[project.optional-dependencies]
+test = [
+    "coverage",
+    "pytest",
+    "pytest-asyncio",
+    "pytest-cov",
+    "pytest-jupyter[server]>=0.6.0"
+]
+
+[tool.hatch.version]
+source = "nodejs"
+
+[tool.hatch.metadata.hooks.nodejs]
+fields = ["description", "authors", "urls"]
+
+[tool.hatch.build.targets.sdist]
+artifacts = ["dataproc_plugin/labextension"]
+exclude = [".github", "binder"]
+
+[tool.hatch.build.targets.wheel.shared-data]
+"dataproc_plugin/labextension" = "share/jupyter/labextensions/dataproc_plugin"
+"install.json" = "share/jupyter/labextensions/dataproc_plugin/install.json"
+"jupyter-config/server-config" = "etc/jupyter/jupyter_server_config.d"
+"jupyter-config/nb-config" = "etc/jupyter/jupyter_notebook_config.d"
+
+[tool.hatch.build.hooks.version]
+path = "dataproc_plugin/_version.py"
+
+[tool.hatch.build.hooks.jupyter-builder]
+dependencies = ["hatch-jupyter-builder>=0.5"]
+build-function = "hatch_jupyter_builder.npm_builder"
+ensured-targets = [
+    "dataproc_plugin/labextension/static/style.js",
+    "dataproc_plugin/labextension/package.json",
+]
+skip-if-exists = ["dataproc_plugin/labextension/static/style.js"]
+
+[tool.hatch.build.hooks.jupyter-builder.build-kwargs]
+build_cmd = "build:prod"
+npm = ["jlpm"]
+
+[tool.hatch.build.hooks.jupyter-builder.editable-build-kwargs]
+build_cmd = "install:extension"
+npm = ["jlpm"]
+source_dir = "src"
+build_dir = "dataproc_plugin/labextension"
+
+[tool.jupyter-releaser.options]
+version_cmd = "hatch version"
+
+[tool.jupyter-releaser.hooks]
+before-build-npm = [
+    "python -m pip install 'jupyterlab>=4.0.0,<5'",
+    "jlpm",
+    "jlpm build:prod"
+]
+before-build-python = ["jlpm clean:all"]
+
+[tool.check-wheel-contents]
+ignore = ["W002"]
diff --git a/schema/plugin.json b/schema/plugin.json
new file mode 100644
index 00000000..3cfd3d2c
--- /dev/null
+++ b/schema/plugin.json
@@ -0,0 +1,8 @@
+{
+  "jupyter.lab.shortcuts": [],
+  "title": "dataproc_plugin",
+  "description": "dataproc_plugin settings.",
+  "type": "object",
+  "properties": {},
+  "additionalProperties": false
+}
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..b81a9b58
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,15 @@
+# Copyright 2023 Google LLC
+#
+# 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
+#
+# https://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.
+
+__import__('setuptools').setup()
diff --git a/src/batches/batchDetails.tsx b/src/batches/batchDetails.tsx
new file mode 100644
index 00000000..fdf22707
--- /dev/null
+++ b/src/batches/batchDetails.tsx
@@ -0,0 +1,463 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useState, useEffect } from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import LeftArrowIcon from '../../style/icons/left_arrow_icon.svg';
+import CloneJobIcon from '../../style/icons/clone_job_icon.svg';
+import ViewLogs from '../utils/viewLogs';
+import DeleteClusterIcon from '../../style/icons/delete_cluster_icon.svg';
+
+import { ClipLoader } from 'react-spinners';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  NETWORK_KEY,
+  NETWORK_LABEL,
+  SERVICE_ACCOUNT_KEY,
+  SERVICE_ACCOUNT_LABEL,
+  SUBNETWORK_KEY,
+  SUBNETWORK_LABEL
+} from '../utils/const';
+import {
+  BatchTypeValue,
+  authApi,
+  convertToDCUHours,
+  convertToGBMonths,
+  elapsedTime,
+  jobTimeFormat,
+  statusMessageBatch
+} from '../utils/utils';
+import DeletePopup from '../utils/deletePopup';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import { deleteBatchAPI } from '../utils/batchService';
+import { statusDisplay } from '../utils/statusDisplay';
+
+const iconLeftArrow = new LabIcon({
+  name: 'launcher:left-arrow-icon',
+  svgstr: LeftArrowIcon
+});
+const iconCloneJob = new LabIcon({
+  name: 'launcher:clone-job-icon',
+  svgstr: CloneJobIcon
+});
+const iconDeleteCluster = new LabIcon({
+  name: 'launcher:delete-cluster-icon',
+  svgstr: DeleteClusterIcon
+});
+
+type BatchDetailsProps = {
+  batchSelected: string;
+  setDetailedBatchView: (flag: boolean) => void;
+};
+
+function BatchDetails({
+  batchSelected,
+  setDetailedBatchView
+}: BatchDetailsProps) {
+  const [batchInfoResponse, setBatchInfoResponse] = useState({
+    uuid: '',
+    state: '',
+    createTime: '',
+    runtimeInfo: {
+      approximateUsage: { milliDcuSeconds: '', shuffleStorageGbSeconds: '' }
+    },
+    creator: '',
+    runtimeConfig: {
+      version: '',
+      properties: {
+        'spark:spark.executor.instances': '',
+        'spark:spark.driver.cores': '',
+        'spark:spark.driver.memory': '',
+        'spark:spark.executor.cores': '',
+        'spark:spark.executor.memory': '',
+        'spark:spark.dynamicAllocation.executorAllocationRatio': '',
+        'spark:spark.app.name': ''
+      }
+    },
+    sparkBatch: {
+      mainJarFileUri: '',
+      mainClass: ''
+    },
+    pysparkBatch: {
+      mainPythonFileUri: ''
+    },
+    sparkRBatch: {
+      mainRFileUri: ''
+    },
+    sparkSqlBatch: {
+      queryFileUri: ''
+    },
+    environmentConfig: {
+      executionConfig: {
+        serviceAccount: '',
+        subnetworkUri: ''
+      }
+    },
+    stateHistory: [{ state: '', stateStartTime: '' }],
+    stateTime: ''
+  });
+  const [regionName, setRegionName] = useState('');
+  const [labelDetail, setLabelDetail] = useState(['']);
+  const [isLoading, setIsLoading] = useState(true);
+  const [deletePopupOpen, setDeletePopupOpen] = useState(false);
+  const [selectedBatch, setSelectedBatch] = useState('');
+
+  const handleDetailedBatchView = () => {
+    setDetailedBatchView(false);
+  };
+
+  useEffect(() => {
+    getBatchDetails();
+  }, []);
+  const getBatchDetails = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      setRegionName(credentials.region_id || '');
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/locations/${credentials.region_id}/batches/${batchSelected}`,
+        {
+          method: 'GET',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              setBatchInfoResponse(responseResult);
+              if (responseResult.labels) {
+                const labelValue = Object.entries(responseResult.labels).map(
+                  ([key, value]) => `${key}:${value}`
+                );
+                setLabelDetail(labelValue);
+              }
+              setIsLoading(false);
+            })
+            .catch((e: Error) => {
+              console.log(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error in getting Batch details', err);
+          toast.error('Failed to fetch Batch details');
+        });
+    }
+  };
+
+  const statusMsg = statusMessageBatch(batchInfoResponse);
+  const startTime = jobTimeFormat(batchInfoResponse.createTime);
+  const startTimeElapsed = new Date(batchInfoResponse.createTime);
+  const endTime = batchInfoResponse.stateTime;
+  let jobStartTime;
+  let runTimeString = '';
+  if (batchInfoResponse.stateHistory) {
+    jobStartTime = new Date(
+      batchInfoResponse.stateHistory[
+        batchInfoResponse.stateHistory.length - 1
+      ].stateStartTime
+    );
+    runTimeString = elapsedTime(endTime, jobStartTime);
+  }
+  const batch = BatchTypeValue(batchInfoResponse);
+
+  const elapsedTimeString = elapsedTime(
+    batchInfoResponse.stateTime,
+    startTimeElapsed
+  );
+
+  const handleDeleteBatch = (batchSelected: string) => {
+    setSelectedBatch(batchSelected);
+    setDeletePopupOpen(true);
+  };
+  const handleCancelDelete = () => {
+    setDeletePopupOpen(false);
+  };
+
+  const handleDelete = async () => {
+    await deleteBatchAPI(selectedBatch);
+    handleDetailedBatchView();
+    setDeletePopupOpen(false);
+  };
+
+  return (
+    <div>
+      <ToastContainer />
+      {batchInfoResponse.uuid === '' && (
+        <div className="loader-full-style">
+          {isLoading && (
+            <div>
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={20}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+              Loading Batch Details
+            </div>
+          )}
+        </div>
+      )}
+      {deletePopupOpen && (
+        <DeletePopup
+          onCancel={() => handleCancelDelete()}
+          onDelete={() => handleDelete()}
+          deletePopupOpen={deletePopupOpen}
+          DeleteMsg={
+            'This will delete ' + selectedBatch + ' and cannot be undone.'
+          }
+        />
+      )}
+      {batchInfoResponse.uuid !== '' && (
+        <div className="scroll-comp">
+          <div className="cluster-details-header">
+            <div
+              className="back-arrow-icon"
+              onClick={() => handleDetailedBatchView()}
+            >
+              <iconLeftArrow.react tag="div" />
+            </div>
+            <div className="cluster-details-title">{batchSelected}</div>
+            <div className="action-disabled action-cluster-section">
+              <div className="action-cluster-icon">
+                <iconCloneJob.react tag="div" />
+              </div>
+              <div className="action-cluster-text">CLONE</div>
+            </div>
+
+            <div
+              className="action-cluster-section"
+              onClick={() => handleDeleteBatch(batchSelected)}
+            >
+              <div className="action-cluster-icon">
+                <iconDeleteCluster.react tag="div" />
+              </div>
+              <div className="action-cluster-text">DELETE</div>
+            </div>
+            <ViewLogs batchInfoResponse={batchInfoResponse} />
+          </div>
+
+          <div className="batch-details-container-top">
+            <div className="row-details"></div>
+            <div className="row-details">
+              <div className="details-label">Batch ID</div>
+              <div className="details-value">{batchSelected}</div>
+            </div>
+            <div className="row-details">
+              <div className="details-label">Batch UUID</div>
+              <div className="details-value">{batchInfoResponse.uuid}</div>
+            </div>
+            <div className="row-details">
+              <div className="details-label">Resource type</div>
+              <div className="details-value">Batch</div>
+            </div>
+            <div className="row-details">
+              <div className="details-label">Status</div>
+              {statusDisplay(statusMsg)}
+            </div>
+          </div>
+          <div className="cluster-details-header">
+            <div className="cluster-details-title">Details</div>
+          </div>
+          <div className="batch-details-container">
+            <div className="row-details">
+              <div className="details-label">Start time</div>
+              <div className="details-value">{startTime}</div>
+            </div>
+            <div className="row-details">
+              <div className="details-label">Elapsed time</div>
+              <div className="details-value">{elapsedTimeString}</div>
+            </div>
+            {runTimeString !== '' && (
+              <div className="row-details">
+                <div className="details-label">Run time</div>
+                <div className="details-value">{runTimeString}</div>
+              </div>
+            )}
+            {batchInfoResponse.runtimeInfo.approximateUsage &&
+              batchInfoResponse.runtimeInfo.approximateUsage
+                .milliDcuSeconds && (
+                <div className="row-details">
+                  <div className="details-label">Approximate DCU usage</div>
+                  <div className="details-value">
+                    {convertToDCUHours(
+                      batchInfoResponse.runtimeInfo.approximateUsage
+                        .milliDcuSeconds
+                    )}
+                  </div>
+                </div>
+              )}
+            {batchInfoResponse.runtimeInfo.approximateUsage &&
+              batchInfoResponse.runtimeInfo.approximateUsage
+                .shuffleStorageGbSeconds && (
+                <div className="row-details">
+                  <div className="details-label">
+                    Approximate shuffle storage usage
+                  </div>
+                  <div className="details-value">
+                    {convertToGBMonths(
+                      batchInfoResponse.runtimeInfo.approximateUsage
+                        .shuffleStorageGbSeconds
+                    )}
+                  </div>
+                </div>
+              )}
+            <div className="row-details">
+              <div className="details-label">Creator</div>
+              <div className="details-value">{batchInfoResponse.creator}</div>
+            </div>
+            <div className="row-details">
+              <div className="details-label">Version</div>
+              <div className="details-value">
+                {batchInfoResponse.runtimeConfig.version}
+              </div>
+            </div>
+            <div className="row-details">
+              <div className="details-label">Region</div>
+              <div className="details-value">{regionName}</div>
+            </div>
+            <div className="row-details">
+              <div className="details-label">Batch type</div>
+              <div className="details-value">{batch}</div>
+            </div>
+            {batch === 'Spark' &&
+              batchInfoResponse.sparkBatch.mainJarFileUri && (
+                <div className="row-details">
+                  <div className="details-label">Main jar</div>
+                  <div className="details-value">
+                    {batchInfoResponse.sparkBatch.mainJarFileUri}
+                  </div>
+                </div>
+              )}
+            {batch === 'Spark' && batchInfoResponse.sparkBatch.mainClass && (
+              <div className="row-details">
+                <div className="details-label">Main Class</div>
+                <div className="details-value">
+                  {batchInfoResponse.sparkBatch.mainClass}
+                </div>
+              </div>
+            )}
+            {batch === 'PySpark' && (
+              <div className="row-details">
+                <div className="details-label">Main python file</div>
+                <div className="details-value">
+                  {batchInfoResponse.pysparkBatch.mainPythonFileUri}
+                </div>
+              </div>
+            )}
+            {batch === 'SparkR' && (
+              <div className="row-details">
+                <div className="details-label">Main R file</div>
+                <div className="details-value">
+                  {batchInfoResponse.sparkRBatch.mainRFileUri}
+                </div>
+              </div>
+            )}
+            {batch === 'SparkSQL' && (
+              <div className="row-details">
+                <div className="details-label">Query file</div>
+                <div className="details-value">
+                  {batchInfoResponse.sparkSqlBatch.queryFileUri}
+                </div>
+              </div>
+            )}
+            <div className="row-details">
+              <div className="details-label">Properties</div>
+              <div className="details-value"></div>
+            </div>
+            {Object.entries(batchInfoResponse.runtimeConfig.properties).map(
+              ([key, value]) => (
+                <div className="row-details" key={key}>
+                  <div className="batch-details-label-level-one">{key}</div>
+                  <div className="details-value">{value}</div>
+                </div>
+              )
+            )}
+            <div className="row-details">
+              <div className="details-label">Environment config</div>
+              <div className="details-value"></div>
+            </div>
+            <div className="row-details">
+              <div className="batch-details-label-level-one">
+                Execution config
+              </div>
+              <div className="details-value"></div>
+            </div>
+            {Object.entries(
+              batchInfoResponse.environmentConfig.executionConfig
+            ).map(([key, value]) => {
+              let label;
+              if (key === SERVICE_ACCOUNT_KEY) {
+                label = SERVICE_ACCOUNT_LABEL;
+              } else if (key === NETWORK_KEY) {
+                label = NETWORK_LABEL;
+              } else if (key === SUBNETWORK_KEY) {
+                label = SUBNETWORK_LABEL;
+              } else {
+                label = '';
+              }
+              if (
+                key === SERVICE_ACCOUNT_KEY ||
+                key === NETWORK_KEY ||
+                key === SUBNETWORK_KEY
+              ) {
+                return (
+                  <div className="row-details" key={key}>
+                    <div className="batch-details-label-level-two">{label}</div>
+                    <div className="details-value">{value}</div>
+                  </div>
+                );
+              }
+            })}
+
+            <div className="row-details">
+              <div className="details-label">Encryption type</div>
+              <div className="details-value">Google-managed key</div>
+            </div>
+            <div className="batch-details-row-label">
+              <div className="details-label">Labels</div>
+              <div className="details-value">
+                <div className="job-label-style-parent">
+                  {labelDetail.length > 0
+                    ? labelDetail.map(label => {
+                        const labelParts = label.split(':');
+                        return (
+                          <div key={label} className="job-label-style">
+                            {labelParts[0]} : {labelParts[1]}
+                          </div>
+                        );
+                      })
+                    : ''}
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default BatchDetails;
diff --git a/src/batches/batches.tsx b/src/batches/batches.tsx
new file mode 100644
index 00000000..a1018eea
--- /dev/null
+++ b/src/batches/batches.tsx
@@ -0,0 +1,292 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { ReactWidget } from '@jupyterlab/apputils';
+import React, { useState, useEffect } from 'react';
+import {
+  authApi,
+  checkConfig,
+  elapsedTime,
+  jobTimeFormat,
+  jobTypeDisplay
+} from '../utils/utils';
+import {
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  POLLING_TIME_LIMIT,
+  API_HEADER_BEARER,
+  LOGIN_STATE
+} from '../utils/const';
+import ListBatches from './listBatches';
+import { LabIcon } from '@jupyterlab/ui-components';
+import deleteIcon from '../../style/icons/delete_icon.svg';
+import BatchDetails from './batchDetails';
+import ListSessions from '../sessions/listSessions';
+import { ClipLoader } from 'react-spinners';
+import DeletePopup from '../utils/deletePopup';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import { deleteBatchAPI } from '../utils/batchService';
+
+const iconDelete = new LabIcon({
+  name: 'launcher:delete-icon',
+  svgstr: deleteIcon
+});
+
+const ServerlessComponent = (): React.JSX.Element => {
+  const [batchesList, setBatchesList] = useState([]);
+  const [selectedMode, setSelectedMode] = useState('Batches');
+  const [isLoading, setIsLoading] = useState(true);
+  const [batchSelected, setBatchSelected] = useState('');
+  const [pollingDisable, setPollingDisable] = useState(false);
+  const [detailedBatchView, setDetailedBatchView] = useState(false);
+  const [loggedIn, setLoggedIn] = useState(false);
+  const [configError, setConfigError] = useState(false);
+  const [loginError, setLoginError] = useState(false);
+  const [configLoading, setConfigLoading] = useState(true);
+
+  const [deletePopupOpen, setDeletePopupOpen] = useState(false);
+  const [selectedBatch, setSelectedBatch] = useState('');
+  const [timer, setTimer] = useState<NodeJS.Timer | undefined>(undefined);
+  const pollingBatches = async (
+    pollingFunction: () => void,
+    pollingDisable: boolean
+  ) => {
+    if (pollingDisable) {
+      clearInterval(timer);
+    } else {
+      setTimer(setInterval(pollingFunction, POLLING_TIME_LIMIT));
+    }
+  };
+
+  const selectedModeChange = (mode: 'Sessions' | 'Batches') => {
+    if (mode === 'Sessions') {
+      pollingBatches(listBatchAPI, true);
+    } else {
+      pollingBatches(listBatchAPI, pollingDisable);
+    }
+    setSelectedMode(mode);
+  };
+
+  const listBatchAPI = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/locations/${credentials.region_id}/batches?orderBy=create_time desc&&pageSize=100`,
+        {
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              let transformBatchListData = [];
+              transformBatchListData = responseResult.batches.map(
+                (data: any) => {
+                  const startTimeDisplay = jobTimeFormat(data.createTime);
+                  const startTime = new Date(data.createTime);
+                  const elapsedTimeString = elapsedTime(
+                    data.stateTime,
+                    startTime
+                  );
+                  const batchType = Object.keys(data).filter(key =>
+                    key.endsWith('Batch')
+                  );
+                  const batchTypeDisplay = jobTypeDisplay(
+                    batchType[0].split('Batch')[0]
+                  );
+                  return {
+                    batchID: data.name.split('/')[5],
+                    status: data.state,
+                    location: data.name.split('/')[3],
+                    creationTime: startTimeDisplay,
+                    type: batchTypeDisplay,
+                    elapsedTime: elapsedTimeString,
+                    actions: renderActions(data)
+                  };
+                }
+              );
+              setBatchesList(transformBatchListData);
+              setIsLoading(false);
+              setLoggedIn(true);
+            })
+            .catch((e: Error) => {
+              console.log(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error listing batches', err);
+          toast.error('Failed to fetch Batches');
+        });
+    }
+  };
+  const handleBatchDetails = (selectedName: string) => {
+    pollingBatches(listBatchAPI, true);
+    setBatchSelected(selectedName);
+    setDetailedBatchView(true);
+  };
+
+  const handleDeleteBatch = (batch: string) => {
+    setSelectedBatch(batch);
+    setDeletePopupOpen(true);
+  };
+  const handleCancelDelete = () => {
+    setDeletePopupOpen(false);
+  };
+
+  const handleDelete = async () => {
+    await deleteBatchAPI(selectedBatch);
+    listBatchAPI();
+    setDetailedBatchView(false);
+    setDeletePopupOpen(false);
+  };
+
+  function renderActions(data: { name: string }) {
+    return (
+      <div className="actions-icon" role="button" aria-label="Delete Job">
+        <div
+          className="icon-buttons-style"
+          title="Delete Batch"
+          onClick={() => handleDeleteBatch(data.name.split('/')[5])}
+        >
+          <iconDelete.react tag="div" />
+        </div>
+      </div>
+    );
+  }
+
+  const toggleStyleSelection = (toggleItem: string) => {
+    if (selectedMode === toggleItem) {
+      return 'selected-header';
+    } else {
+      return 'unselected-header';
+    }
+  };
+
+  useEffect(() => {
+    checkConfig(setLoggedIn, setConfigError, setLoginError);
+    const localstorageGetInformation = localStorage.getItem('loginState');
+    setLoggedIn(localstorageGetInformation === LOGIN_STATE);
+    if (loggedIn) {
+      setConfigLoading(false);
+    }
+    listBatchAPI();
+    if (!detailedBatchView && selectedMode === 'Batches') {
+      pollingBatches(listBatchAPI, pollingDisable);
+    }
+
+    return () => {
+      pollingBatches(listBatchAPI, true);
+    };
+  }, [pollingDisable, detailedBatchView, selectedMode]);
+
+  return (
+    <div className="component-level">
+      {configLoading && !loggedIn && !configError && !loginError && (
+        <div className="spin-loaderMain">
+          <ClipLoader
+            color="#8A8A8A"
+            loading={true}
+            size={18}
+            aria-label="Loading Spinner"
+            data-testid="loader"
+          />
+          Loading Batches
+        </div>
+      )}
+      {deletePopupOpen && (
+        <DeletePopup
+          onCancel={() => handleCancelDelete()}
+          onDelete={() => handleDelete()}
+          deletePopupOpen={deletePopupOpen}
+          DeleteMsg={
+            'This will delete ' + selectedBatch + ' and cannot be undone.'
+          }
+        />
+      )}
+      {loggedIn ? (
+        <>
+          {detailedBatchView ? (
+            <BatchDetails
+              batchSelected={batchSelected}
+              setDetailedBatchView={setDetailedBatchView}
+            />
+          ) : (
+            <div className="clusters-list-component">
+              {
+                <div className="clusters-list-overlay" role="tab">
+                  <div
+                    className={toggleStyleSelection('Batches')}
+                    onClick={() => selectedModeChange('Batches')}
+                  >
+                    Batches
+                  </div>
+                  <div
+                    className={toggleStyleSelection('Sessions')}
+                    onClick={() => selectedModeChange('Sessions')}
+                  >
+                    Sessions
+                  </div>
+                </div>
+              }
+              <div>
+                {selectedMode === 'Sessions' ? (
+                  <ListSessions />
+                ) : (
+                  <ListBatches
+                    batchesList={batchesList}
+                    isLoading={isLoading}
+                    setPollingDisable={setPollingDisable}
+                    listBatchAPI={listBatchAPI}
+                    handleBatchDetails={handleBatchDetails}
+                  />
+                )}
+              </div>
+              <ToastContainer />
+            </div>
+          )}
+        </>
+      ) : (
+        loginError && (
+          <div className="login-error">Please login to continue</div>
+        )
+      )}
+      {configError && (
+        <div className="login-error">
+          Please Configure Gcloud with Account, Project ID and Region
+        </div>
+      )}
+    </div>
+  );
+};
+
+export class Serverless extends ReactWidget {
+  constructor() {
+    super();
+    this.addClass('jp-ReactWidget');
+  }
+
+  render(): React.JSX.Element {
+    return <ServerlessComponent />;
+  }
+}
diff --git a/src/batches/listBatches.tsx b/src/batches/listBatches.tsx
new file mode 100644
index 00000000..a8e88917
--- /dev/null
+++ b/src/batches/listBatches.tsx
@@ -0,0 +1,262 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { useTable, useGlobalFilter } from 'react-table';
+import { LabIcon } from '@jupyterlab/ui-components';
+import createClusterIcon from '../../style/icons/create_cluster_icon.svg';
+import filterIcon from '../../style/icons/filter_icon.svg';
+import SucceededIcon from '../../style/icons/succeeded_icon.svg';
+import clusterRunningIcon from '../../style/icons/cluster_running_icon.svg';
+import clusterErrorIcon from '../../style/icons/cluster_error_icon.svg';
+import { ClipLoader } from 'react-spinners';
+import GlobalFilter from '../utils/globalFilter';
+import {
+  // CREATE_BATCH_URL,
+  STATUS_CREATING,
+  STATUS_DELETING,
+  STATUS_FAIL,
+  STATUS_PENDING,
+  STATUS_PROVISIONING,
+  STATUS_RUNNING,
+  STATUS_SUCCESS
+} from '../utils/const';
+import TableData from '../utils/tableData';
+
+const iconCreateCluster = new LabIcon({
+  name: 'launcher:create-cluster-icon',
+  svgstr: createClusterIcon
+});
+const iconFilter = new LabIcon({
+  name: 'launcher:filter-icon',
+  svgstr: filterIcon
+});
+const iconSucceeded = new LabIcon({
+  name: 'launcher:succeeded-icon',
+  svgstr: SucceededIcon
+});
+
+const iconClusterRunning = new LabIcon({
+  name: 'launcher:cluster-running-icon',
+  svgstr: clusterRunningIcon
+});
+const iconClusterError = new LabIcon({
+  name: 'launcher:cluster-error-icon',
+  svgstr: clusterErrorIcon
+});
+
+interface IBatch {
+  batchID: string;
+  status: string;
+  location: string;
+  creationTime: string;
+  elapsedTime: string;
+  type: string;
+  actions: JSX.Element;
+}
+
+interface IListBatchesProps {
+  batchesList: IBatch[];
+  isLoading: boolean;
+  setPollingDisable: (value: boolean) => void;
+  listBatchAPI: () => void;
+  handleBatchDetails: (batchID: string) => void;
+}
+
+function ListBatches({
+  batchesList,
+  isLoading,
+  setPollingDisable,
+  listBatchAPI,
+  handleBatchDetails
+}: IListBatchesProps) {
+  const data = batchesList;
+  const columns = React.useMemo(
+    () => [
+      {
+        Header: 'Batch ID',
+        accessor: 'batchID'
+      },
+      {
+        Header: 'Status',
+        accessor: 'status'
+      },
+      {
+        Header: 'Location',
+        accessor: 'location'
+      },
+      {
+        Header: 'Creation time',
+        accessor: 'creationTime'
+      },
+      {
+        Header: 'Elapsed time',
+        accessor: 'elapsedTime'
+      },
+      {
+        Header: 'Type',
+        accessor: 'type'
+      },
+      {
+        Header: 'Actions',
+        accessor: 'actions'
+      }
+    ],
+    []
+  );
+
+  const {
+    getTableProps,
+    getTableBodyProps,
+    headerGroups,
+    rows,
+    prepareRow,
+    state,
+    //@ts-ignore
+    preGlobalFilteredRows,
+    //@ts-ignore
+    setGlobalFilter
+  } =
+    //@ts-ignore
+    useTable({ columns, data }, useGlobalFilter);
+
+  interface ICellProps {
+    getCellProps: () => React.TdHTMLAttributes<HTMLTableDataCellElement>;
+    value: any;
+    column: {
+      Header: string;
+    };
+    render: (value: string) => React.ReactNode;
+  }
+
+  const tableDataCondition = (cell: ICellProps) => {
+    if (cell.column.Header === 'Batch ID') {
+      return (
+        <td
+          {...cell.getCellProps()}
+          className="cluster-name"
+          onClick={() => handleBatchDetails(cell.value)}
+        >
+          {cell.value}
+        </td>
+      );
+    } else if (cell.column.Header === 'Status') {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          <div key="Status" className="cluster-status-parent">
+            {cell.value === STATUS_RUNNING && (
+              <iconClusterRunning.react tag="div" />
+            )}
+            {cell.value === STATUS_FAIL && <iconClusterError.react tag="div" />}
+            {cell.value === STATUS_SUCCESS && <iconSucceeded.react tag="div" />}
+            {(cell.value === STATUS_PROVISIONING ||
+              cell.value === STATUS_CREATING ||
+              cell.value === STATUS_PENDING ||
+              cell.value === STATUS_DELETING) && (
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={15}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+            )}
+            <div className="cluster-status">
+              {cell.value && cell.value.toLowerCase()}
+            </div>
+          </div>
+        </td>
+      );
+    } else {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          {cell.render('Cell')}
+        </td>
+      );
+    }
+  };
+
+  return (
+    <div>
+      <div className="batch-header-part">
+        <div
+          className="action-disabled create-batch-overlay"
+          // onClick={() => {
+          //   window.open(CREATE_BATCH_URL, '_blank');
+          // }}
+        >
+          <div className="create-cluster-icon">
+            <iconCreateCluster.react tag="div" />
+          </div>
+          <div className="create-cluster-text">Create Batch</div>
+        </div>
+      </div>
+      {batchesList.length > 0 ? (
+        <div>
+          <div className="filter-cluster-overlay">
+            <div className="filter-cluster-icon">
+              <iconFilter.react tag="div" />
+            </div>
+            <div className="filter-cluster-text"></div>
+            <div className="filter-cluster-section">
+              <GlobalFilter
+                preGlobalFilteredRows={preGlobalFilteredRows}
+                //@ts-ignore
+                globalFilter={state.globalFilter}
+                setGlobalFilter={setGlobalFilter}
+                listBatchAPI={listBatchAPI}
+                setPollingDisable={setPollingDisable}
+              />
+            </div>
+          </div>
+          <div className="clusters-list-table-parent">
+            <TableData
+              getTableProps={getTableProps}
+              headerGroups={headerGroups}
+              getTableBodyProps={getTableBodyProps}
+              isLoading={isLoading}
+              rows={rows}
+              prepareRow={prepareRow}
+              tableDataCondition={tableDataCondition}
+              fromPage="Batches"
+            />
+          </div>
+        </div>
+      ) : (
+        <div>
+          {isLoading && (
+            <div className="spin-loaderMain">
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={18}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+              Loading Batches
+            </div>
+          )}
+          {!isLoading && (
+            <div className="no-data-style">No rows to display</div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default ListBatches;
diff --git a/src/cluster/cluster.tsx b/src/cluster/cluster.tsx
new file mode 100644
index 00000000..29703245
--- /dev/null
+++ b/src/cluster/cluster.tsx
@@ -0,0 +1,470 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { ReactWidget } from '@jupyterlab/apputils';
+import React, { useState, useEffect } from 'react';
+import JobComponent from '../jobs/jobs';
+import ClusterDetails from './clusterDetails';
+import { authApi, checkConfig, statusValue } from '../utils/utils';
+import { LabIcon } from '@jupyterlab/ui-components';
+import startIcon from '../../style/icons/start_icon.svg';
+import stopIcon from '../../style/icons/stop_icon.svg';
+import restartIcon from '../../style/icons/restart_icon.svg';
+import startDisableIcon from '../../style/icons/start_icon_disable.svg';
+import stopDisableIcon from '../../style/icons/stop_icon_disable.svg';
+import restartDisableIcon from '../../style/icons/restart_icon_disable.svg';
+import {
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  POLLING_TIME_LIMIT,
+  // STATUS_RUNNING,
+  // STATUS_STOPPED,
+  API_HEADER_BEARER,
+  LOGIN_STATE,
+  ClusterStatus
+} from '../utils/const';
+import ListCluster from './listCluster';
+import { ClipLoader } from 'react-spinners';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import { startClusterApi, stopClusterApi } from '../utils/clusterServices';
+
+const iconStart = new LabIcon({
+  name: 'launcher:start-icon',
+  svgstr: startIcon
+});
+const iconStop = new LabIcon({
+  name: 'launcher:stop-icon',
+  svgstr: stopIcon
+});
+const iconRestart = new LabIcon({
+  name: 'launcher:restart-icon',
+  svgstr: restartIcon
+});
+
+const iconStartDisable = new LabIcon({
+  name: 'launcher:start-disable-icon',
+  svgstr: startDisableIcon
+});
+const iconStopDisable = new LabIcon({
+  name: 'launcher:stop-disable-icon',
+  svgstr: stopDisableIcon
+});
+const iconRestartDisable = new LabIcon({
+  name: 'launcher:restart-disable-icon',
+  svgstr: restartDisableIcon
+});
+
+const ClusterComponent = (): React.JSX.Element => {
+  type Mode = 'Clusters' | 'Serverless' | 'Jobs';
+  const [clustersList, setclustersList] = useState([]);
+  const [clusterResponse, setClusterResponse] = useState([]);
+  const [detailedJobView, setDetailedJobView] = useState(false);
+  const [submitJobView, setSubmitJobView] = useState(false);
+  const [restartEnabled, setRestartEnabled] = useState(false);
+  const [selectedMode, setSelectedMode] = useState<Mode>('Clusters');
+  const [isLoading, setIsLoading] = useState(true);
+  const [clusterSelected, setClusterSelected] = useState<string>('');
+  const [pollingDisable, setPollingDisable] = useState(false);
+  const [detailedView, setDetailedView] = useState(false);
+  const [loggedIn, setLoggedIn] = useState(false);
+  const [configError, setConfigError] = useState(false);
+  const [loginError, setLoginError] = useState(false);
+  const [configLoading, setConfigLoading] = useState(true);
+  const [projectId, setProjectId] = useState('');
+  const [timer, setTimer] = useState<NodeJS.Timer | undefined>(undefined);
+  const [selectedJobClone, setSelectedJobClone] = useState({});
+
+  const pollingClusters = async (
+    pollingFunction: () => void,
+    pollingDisable: boolean
+  ) => {
+    if (pollingDisable) {
+      clearInterval(timer);
+    } else {
+      setTimer(setInterval(pollingFunction, POLLING_TIME_LIMIT));
+    }
+  };
+
+  const selectedModeChange = (mode: Mode) => {
+    if (mode === 'Jobs') {
+      pollingClusters(listClustersAPI, true);
+    } else {
+      pollingClusters(listClustersAPI, pollingDisable);
+    }
+    setSelectedMode(mode);
+  };
+  const listClustersAPI = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      setProjectId(credentials.project_id || '');
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/clusters?pageSize=100`,
+        {
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              let transformClusterListData = [];
+              setClusterResponse(responseResult);
+
+              transformClusterListData = responseResult.clusters.map(
+                (data: any) => {
+                  const statusVal = statusValue(data);
+                  return {
+                    clusterUuid: data.clusterUuid,
+                    status: statusVal,
+                    clusterName: data.clusterName,
+                    clusterImage: data.config.softwareConfig.imageVersion,
+                    region: data.labels['goog-dataproc-location'],
+                    zone: data.config.gceClusterConfig.zoneUri.split('/')[
+                      data.config.gceClusterConfig.zoneUri.split('/').length - 1
+                    ],
+                    totalWorkersNode: data.config.workerConfig
+                      ? data.config.workerConfig.numInstances
+                      : 0,
+                    schedulesDeletion: data.config.lifecycleConfig
+                      ? 'On'
+                      : 'Off',
+                    actions: renderActions(data)
+                  };
+                }
+              );
+              setclustersList(transformClusterListData);
+              setIsLoading(false);
+              setLoggedIn(true);
+            })
+            .catch((e: Error) => {
+              console.log(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error listing clusters', err);
+          toast.error('Failed to fetch Clusters ');
+        });
+    }
+  };
+  const handleClusterDetails = (selectedName: string) => {
+    pollingClusters(listClustersAPI, true);
+    setClusterSelected(selectedName);
+    setDetailedView(true);
+  };
+
+  const statusApi = async (selectedcluster: string) => {
+    const credentials = await authApi();
+    if (credentials) {
+      await fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/clusters/${selectedcluster}`,
+        {
+          method: 'GET',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then(
+              (responseResult: {
+                status: { state: ClusterStatus };
+                clusterName: string;
+              }) => {
+                if (
+                  responseResult.status.state === ClusterStatus.STATUS_STOPPED
+                ) {
+                  startClusterApi(selectedcluster);
+                  clearInterval(timer);
+                }
+              }
+            )
+            .catch((e: Error) => console.log(e));
+        })
+        .catch((err: Error) => {
+          console.error('Error fetching status', err);
+          toast.error('Failed to Fetch the Status');
+        });
+
+      listClustersAPI();
+    }
+  };
+
+  const restartClusterApi = async (selectedcluster: string) => {
+    setRestartEnabled(true);
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/clusters/${selectedcluster}:stop`,
+        {
+          method: 'POST',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: Response) => {
+              console.log(responseResult);
+              listClustersAPI();
+              setTimer(
+                setInterval(() => {
+                  statusApi(selectedcluster);
+                }, POLLING_TIME_LIMIT)
+              );
+            })
+            .catch((e: Error) => console.log(e));
+        })
+        .catch((err: Error) => {
+          console.error('Error restarting cluster', err);
+          toast.error('Failed to Restart the Cluster');
+        });
+
+      listClustersAPI();
+      setRestartEnabled(false);
+    }
+  };
+
+  function startButton(data: {
+    status: { state: ClusterStatus };
+    clusterName: string;
+  }) {
+    return (
+      <div
+        className={
+          data.status.state === ClusterStatus.STATUS_STOPPED &&
+          restartEnabled !== true
+            ? 'icon-buttons-style'
+            : 'icon-buttons-style-disable'
+        }
+        title="Start Cluster"
+        onClick={
+          data.status.state === ClusterStatus.STATUS_STOPPED
+            ? () => startClusterApi(data.clusterName)
+            : undefined
+        }
+      >
+        {data.status.state === ClusterStatus.STATUS_STOPPED ? (
+          <iconStart.react tag="div" />
+        ) : (
+          <iconStartDisable.react tag="div" />
+        )}
+      </div>
+    );
+  }
+
+  function stopButton(data: {
+    status: { state: ClusterStatus };
+    clusterName: string;
+  }) {
+    return (
+      <div
+        className={
+          data.status.state === ClusterStatus.STATUS_RUNNING
+            ? 'icon-buttons-style'
+            : 'icon-buttons-style-disable'
+        }
+        title="Stop Cluster"
+        onClick={
+          data.status.state === ClusterStatus.STATUS_RUNNING
+            ? () => stopClusterApi(data.clusterName)
+            : undefined
+        }
+      >
+        {data.status.state === ClusterStatus.STATUS_RUNNING ? (
+          <iconStop.react tag="div" />
+        ) : (
+          <iconStopDisable.react tag="div" />
+        )}
+      </div>
+    );
+  }
+
+  function restartButton(data: {
+    status: { state: ClusterStatus };
+    clusterName: string;
+  }) {
+    return (
+      <div
+        className={
+          data.status.state === ClusterStatus.STATUS_RUNNING
+            ? 'icon-buttons-style'
+            : 'icon-buttons-style-disable'
+        }
+        title="Restart Cluster"
+        onClick={
+          data.status.state === ClusterStatus.STATUS_RUNNING
+            ? () => restartClusterApi(data.clusterName)
+            : undefined
+        }
+      >
+        {data.status.state === ClusterStatus.STATUS_RUNNING ? (
+          <iconRestart.react tag="div" />
+        ) : (
+          <iconRestartDisable.react tag="div" />
+        )}
+      </div>
+    );
+  }
+  function renderActions(data: {
+    status: { state: ClusterStatus };
+    clusterName: string;
+  }) {
+    return (
+      <div className="actions-icon">
+        {startButton(data)}
+        {stopButton(data)}
+        {restartButton(data)}
+      </div>
+    );
+  }
+
+  const toggleStyleSelection = (toggleItem: string) => {
+    if (selectedMode === toggleItem) {
+      return 'selected-header';
+    } else {
+      return 'unselected-header';
+    }
+  };
+
+  useEffect(() => {
+    checkConfig(setLoggedIn, setConfigError, setLoginError);
+    const localstorageGetInformation = localStorage.getItem('loginState');
+    setLoggedIn(localstorageGetInformation === LOGIN_STATE);
+    if (loggedIn) {
+      setConfigLoading(false);
+    }
+    listClustersAPI();
+    if (!detailedView && selectedMode === 'Clusters') {
+      pollingClusters(listClustersAPI, pollingDisable);
+    }
+
+    return () => {
+      pollingClusters(listClustersAPI, true);
+    };
+  }, [pollingDisable, detailedView, selectedMode]);
+
+  return (
+    <div className="component-level">
+      {configLoading && !loggedIn && !configError && !loginError && (
+        <div className="spin-loaderMain">
+          <ClipLoader
+            color="#8A8A8A"
+            loading={true}
+            size={18}
+            aria-label="Loading Spinner"
+            data-testid="loader"
+          />
+          Loading Clusters
+        </div>
+      )}
+      {loggedIn && !configError ? (
+        <>
+          {detailedView && (
+            <ClusterDetails
+              clusterSelected={clusterSelected}
+              setDetailedView={setDetailedView}
+              detailedJobView={detailedJobView}
+              setDetailedJobView={setDetailedJobView}
+              submitJobView={submitJobView}
+              clusterResponse={clusterResponse}
+              selectedJobClone={selectedJobClone}
+              setSelectedJobClone={setSelectedJobClone}
+              setSubmitJobView={setSubmitJobView}
+            />
+          )}
+          {!detailedView && (
+            <div className="clusters-list-component">
+              {!detailedJobView && !submitJobView && (
+                <div className="clusters-list-overlay">
+                  <div
+                    className={toggleStyleSelection('Clusters')}
+                    onClick={() => selectedModeChange('Clusters')}
+                  >
+                    Clusters
+                  </div>
+                  <div
+                    className={toggleStyleSelection('Jobs')}
+                    onClick={() => selectedModeChange('Jobs')}
+                  >
+                    Jobs
+                  </div>
+                </div>
+              )}
+              <div>
+                {selectedMode === 'Jobs' ? (
+                  <JobComponent
+                    clustersList={clustersList}
+                    detailedJobView={detailedJobView}
+                    setDetailedJobView={setDetailedJobView}
+                    submitJobView={submitJobView}
+                    setSubmitJobView={setSubmitJobView}
+                    setDetailedView={setDetailedView}
+                    clusterResponse={clusterResponse}
+                    selectedJobClone={selectedJobClone}
+                    setSelectedJobClone={setSelectedJobClone}
+                  />
+                ) : (
+                  <ListCluster
+                    clustersList={clustersList}
+                    isLoading={isLoading}
+                    setPollingDisable={setPollingDisable}
+                    listClustersAPI={listClustersAPI}
+                    handleClusterDetails={handleClusterDetails}
+                    project_id={projectId}
+                  />
+                )}
+              </div>
+              <ToastContainer />
+            </div>
+          )}
+        </>
+      ) : (
+        loginError && (
+          <div className="login-error">Please login to continue</div>
+        )
+      )}
+      {configError && (
+        <div className="login-error">
+          Please Configure Gcloud with Account, Project ID and Region
+        </div>
+      )}
+    </div>
+  );
+};
+
+export class Cluster extends ReactWidget {
+  constructor() {
+    super();
+    this.addClass('jp-ReactWidget');
+  }
+
+  render(): React.JSX.Element {
+    return <ClusterComponent />;
+  }
+}
diff --git a/src/cluster/clusterDetails.tsx b/src/cluster/clusterDetails.tsx
new file mode 100644
index 00000000..1bacceee
--- /dev/null
+++ b/src/cluster/clusterDetails.tsx
@@ -0,0 +1,413 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useState, useEffect } from 'react';
+import JobComponent from '../jobs/jobs';
+import { LabIcon } from '@jupyterlab/ui-components';
+import LeftArrowIcon from '../../style/icons/left_arrow_icon.svg';
+import StartClusterIcon from '../../style/icons/start_cluster_icon.svg';
+import StartClusterDisableIcon from '../../style/icons/start_cluster_icon_disable.svg';
+import StopClusterIcon from '../../style/icons/stop_cluster_icon.svg';
+import StopClusterDisableIcon from '../../style/icons/stop_cluster_disable_icon.svg';
+import DeleteClusterIcon from '../../style/icons/delete_cluster_icon.svg';
+import errorIcon from '../../style/icons/error_icon.svg';
+import clusterRunningIcon from '../../style/icons/cluster_running_icon.svg';
+import clusterErrorIcon from '../../style/icons/cluster_error_icon.svg';
+import stopIcon from '../../style/icons/stop_icon.svg';
+import {
+  deleteClusterApi,
+  startClusterApi,
+  stopClusterApi
+} from '../utils/clusterServices';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  POLLING_TIME_LIMIT,
+  STATUS_CREATING,
+  STATUS_DELETING,
+  STATUS_ERROR,
+  STATUS_PROVISIONING,
+  STATUS_RUNNING,
+  STATUS_STARTING,
+  STATUS_STOPPED,
+  STATUS_STOPPING
+} from '../utils/const';
+import { authApi } from '../utils/utils';
+import ClipLoader from 'react-spinners/ClipLoader';
+import ViewLogs from '../utils/viewLogs';
+import DeletePopup from '../utils/deletePopup';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import SubmitJob from '../jobs/submitJob';
+
+const iconLeftArrow = new LabIcon({
+  name: 'launcher:left-arrow-icon',
+  svgstr: LeftArrowIcon
+});
+const iconStartCluster = new LabIcon({
+  name: 'launcher:start-cluster-icon',
+  svgstr: StartClusterIcon
+});
+const iconStartClusterDisable = new LabIcon({
+  name: 'launcher:start-cluster-disable-icon',
+  svgstr: StartClusterDisableIcon
+});
+const iconStopCluster = new LabIcon({
+  name: 'launcher:stop-cluster-icon',
+  svgstr: StopClusterIcon
+});
+const iconStopClusterDisable = new LabIcon({
+  name: 'launcher:stop-cluster-disable-icon',
+  svgstr: StopClusterDisableIcon
+});
+const iconDeleteCluster = new LabIcon({
+  name: 'launcher:delete-cluster-icon',
+  svgstr: DeleteClusterIcon
+});
+const iconClusterRunning = new LabIcon({
+  name: 'launcher:cluster-running-icon',
+  svgstr: clusterRunningIcon
+});
+const iconClusterError = new LabIcon({
+  name: 'launcher:cluster-error-icon',
+  svgstr: clusterErrorIcon
+});
+const iconStop = new LabIcon({
+  name: 'launcher:stop-icon',
+  svgstr: stopIcon
+});
+const iconError = new LabIcon({
+  name: 'launcher:error-icon',
+  svgstr: errorIcon
+});
+interface IClusterDetailsProps {
+  clusterSelected: string;
+  setDetailedView: (value: boolean) => void;
+  setDetailedClusterView?: (value: boolean) => void;
+  detailedJobView?: boolean;
+  setDetailedJobView?: (value: boolean) => void;
+  setSubmitJobView?: (value: boolean) => void;
+  submitJobView: boolean;
+  clusterResponse: any;
+  selectedJobClone: any;
+  setSelectedJobClone?: (value: boolean) => void;
+}
+function ClusterDetails({
+  clusterSelected,
+  setDetailedView,
+  setDetailedClusterView,
+  detailedJobView,
+  setSubmitJobView,
+  setDetailedJobView,
+  submitJobView,
+  clusterResponse,
+  selectedJobClone,
+  setSelectedJobClone
+}: IClusterDetailsProps) {
+  const [clusterInfo, setClusterInfo] = useState({
+    status: { state: '' },
+    clusterName: '',
+    clusterUuid: ''
+  });
+  const [isLoading, setIsLoading] = useState(true);
+  const [errorView, setErrorView] = useState(false);
+  const [projectName, setProjectName] = useState('');
+  const [deletePopupOpen, setDeletePopupOpen] = useState(false);
+  const [selectedCluster, setSelectedCluster] = useState('');
+  const [timer, setTimer] = useState<NodeJS.Timer | undefined>(undefined);
+  const pollingClusterDetails = async (
+    pollingFunction: () => void,
+    pollingDisable: boolean
+  ) => {
+    if (pollingDisable) {
+      clearInterval(timer);
+    } else {
+      setTimer(setInterval(pollingFunction, POLLING_TIME_LIMIT));
+    }
+  };
+
+  const handleDetailedView = () => {
+    pollingClusterDetails(getClusterDetails, true);
+    setDetailedView(false);
+    setDetailedClusterView?.(false);
+    setDetailedJobView?.(false);
+  };
+
+  const handleDeleteCluster = (clustername: string) => {
+    setSelectedCluster(clustername);
+    setDeletePopupOpen(true);
+  };
+  const handleCancelDelete = () => {
+    setDeletePopupOpen(false);
+  };
+
+  const handleDelete = async () => {
+    await deleteClusterApi(selectedCluster);
+
+    setDeletePopupOpen(false);
+    handleDetailedView();
+  };
+
+  const getClusterDetails = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      setProjectName(credentials.project_id || '');
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/clusters/${clusterSelected}`,
+        {
+          method: 'GET',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              if (responseResult.error && responseResult.error.code === 404) {
+                setErrorView(true);
+              }
+              setClusterInfo(responseResult);
+              setIsLoading(false);
+            })
+            .catch((e: Error) => {
+              console.log(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error listing clusters Details', err);
+          toast.error('Failed to fetch Cluster Details');
+        });
+    }
+  };
+
+  const clusterDetailsAction = () => {
+    return (
+      <div className="cluster-details-action-parent">
+        <div
+          className={
+            clusterInfo.status.state === STATUS_STOPPED
+              ? 'action-cluster-section'
+              : 'action-cluster-section disabled'
+          }
+          onClick={() =>
+            clusterInfo.status.state === STATUS_STOPPED &&
+            startClusterApi(clusterInfo.clusterName)
+          }
+        >
+          <div className="action-cluster-icon">
+            {clusterInfo.status.state === STATUS_STOPPED ? (
+              <iconStartCluster.react tag="div" />
+            ) : (
+              <iconStartClusterDisable.react tag="div" />
+            )}
+          </div>
+          <div className="action-cluster-text">START</div>
+        </div>
+        <div
+          className={
+            clusterInfo.status.state === STATUS_RUNNING
+              ? 'action-cluster-section'
+              : 'action-cluster-section disabled'
+          }
+          onClick={() =>
+            clusterInfo.status.state === STATUS_RUNNING &&
+            stopClusterApi(clusterInfo.clusterName)
+          }
+        >
+          <div className="action-cluster-icon">
+            {clusterInfo.status.state === STATUS_RUNNING ? (
+              <iconStopCluster.react tag="div" />
+            ) : (
+              <iconStopClusterDisable.react tag="div" />
+            )}
+          </div>
+          <div className="action-cluster-text">STOP</div>
+        </div>
+        <div
+          className="action-cluster-section"
+          onClick={() => handleDeleteCluster(clusterInfo.clusterName)}
+        >
+          <div className="action-cluster-icon">
+            <iconDeleteCluster.react tag="div" />
+          </div>
+          <div className="action-cluster-text">DELETE</div>
+        </div>
+      </div>
+    );
+  };
+
+  useEffect(() => {
+    getClusterDetails();
+    pollingClusterDetails(getClusterDetails, false);
+
+    return () => {
+      pollingClusterDetails(getClusterDetails, true);
+    };
+  }, []);
+
+  return (
+    <div>
+      {errorView && (
+        <div className="error-view-parent">
+          <div className="back-arrow-icon" onClick={() => handleDetailedView()}>
+            <iconLeftArrow.react tag="div" />
+          </div>
+          <div className="error-view-message-parent">
+            <iconError.react tag="div" />
+            <div className="error-view-message">
+              Unable to find the resource you requested
+            </div>
+          </div>
+        </div>
+      )}
+      {submitJobView && !detailedJobView && (
+        <SubmitJob
+          setSubmitJobView={setSubmitJobView}
+          selectedJobClone={selectedJobClone}
+          clusterResponse={clusterResponse}
+        />
+      )}
+      {deletePopupOpen && (
+        <DeletePopup
+          onCancel={() => handleCancelDelete()}
+          onDelete={() => handleDelete()}
+          deletePopupOpen={deletePopupOpen}
+          DeleteMsg={
+            'This will delete ' + selectedCluster + ' and cannot be undone.'
+          }
+        />
+      )}
+      {!submitJobView &&
+      <div className="scroll-comp">
+        <ToastContainer />
+        {!errorView && clusterInfo.clusterName !== '' ? (
+          <div>
+            {!detailedJobView && (
+              <div>
+                <div className="cluster-details-header">
+                  <div
+                    className="back-arrow-icon"
+                    onClick={() => handleDetailedView()}
+                  >
+                    <iconLeftArrow.react tag="div" />
+                  </div>
+                  <div className="cluster-details-title">Cluster details</div>
+                  {clusterDetailsAction()}
+                  <ViewLogs
+                    clusterInfo={clusterInfo}
+                    projectName={projectName}
+                  />
+                </div>
+                <div className="cluster-details-container">
+                  <div className="row-details"></div>
+                  <div className="row-details">
+                    <div className="cluster-details-label">Name</div>
+                    <div className="cluster-details-value">
+                      {clusterInfo.clusterName}
+                    </div>
+                  </div>
+                  <div className="row-details">
+                    <div className="cluster-details-label">Cluster UUID</div>
+                    <div className="cluster-details-value">
+                      {clusterInfo.clusterUuid}
+                    </div>
+                  </div>
+                  <div className="row-details">
+                    <div className="cluster-details-label">Type</div>
+                    <div className="cluster-details-value">
+                      Dataproc Cluster
+                    </div>
+                  </div>
+                  <div className="row-details">
+                    <div className="cluster-details-label">Status</div>
+                    <div className="cluster-detail-status-parent">
+                      {clusterInfo.status.state === STATUS_RUNNING && (
+                        <iconClusterRunning.react tag="div" />
+                      )}
+                      {clusterInfo.status.state === STATUS_STOPPED && (
+                        <iconStop.react tag="div" />
+                      )}
+                      {clusterInfo.status.state === STATUS_ERROR && (
+                        <iconClusterError.react tag="div" />
+                      )}
+                      {(clusterInfo.status.state === STATUS_PROVISIONING ||
+                        clusterInfo.status.state === STATUS_CREATING ||
+                        clusterInfo.status.state === STATUS_STARTING ||
+                        clusterInfo.status.state === STATUS_STOPPING ||
+                        clusterInfo.status.state === STATUS_DELETING) && (
+                        <div>
+                          <ClipLoader
+                            color="#8A8A8A"
+                            loading={true}
+                            size={15}
+                            aria-label="Loading Spinner"
+                            data-testid="loader"
+                          />
+                        </div>
+                      )}
+                      <div className="cluster-status">
+                        {clusterInfo.status.state === STATUS_CREATING
+                          ? STATUS_PROVISIONING
+                          : clusterInfo.status.state.toLowerCase()}
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div className="cluster-details-header">
+                  <div className="cluster-details-title">Jobs</div>
+                </div>
+              </div>
+            )}
+            <JobComponent
+              clusterSelected={clusterSelected}
+              setDetailedView={setDetailedView}
+              detailedJobView={detailedJobView}
+              setDetailedJobView={setDetailedJobView}
+              setSubmitJobView={setSubmitJobView}
+              setSelectedJobClone={setSelectedJobClone}
+              clusterResponse={clusterResponse}
+            />
+          </div>
+        ) : (
+          <div className="loader-full-style">
+            {isLoading && (
+              <div>
+                <ClipLoader
+                  color="#8A8A8A"
+                  loading={true}
+                  size={20}
+                  aria-label="Loading Spinner"
+                  data-testid="loader"
+                />
+                Loading Cluster Details
+              </div>
+            )}
+          </div>
+        )}
+      </div>
+      }
+    </div>
+  );
+}
+
+export default ClusterDetails;
diff --git a/src/cluster/listCluster.tsx b/src/cluster/listCluster.tsx
new file mode 100644
index 00000000..561df77c
--- /dev/null
+++ b/src/cluster/listCluster.tsx
@@ -0,0 +1,284 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { useTable, useGlobalFilter } from 'react-table';
+import { LabIcon } from '@jupyterlab/ui-components';
+import createClusterIcon from '../../style/icons/create_cluster_icon.svg';
+import filterIcon from '../../style/icons/filter_icon.svg';
+import stopIcon from '../../style/icons/stop_icon.svg';
+import clusterRunningIcon from '../../style/icons/cluster_running_icon.svg';
+import clusterErrorIcon from '../../style/icons/cluster_error_icon.svg';
+import { ClipLoader } from 'react-spinners';
+import {
+  CREATE_CLUSTER_URL,
+  STATUS_CREATING,
+  STATUS_DELETING,
+  STATUS_ERROR,
+  STATUS_PROVISIONING,
+  STATUS_RUNNING,
+  STATUS_STARTING,
+  STATUS_STOPPED,
+  STATUS_STOPPING
+} from '../utils/const';
+import GlobalFilter from '../utils/globalFilter';
+import TableData from '../utils/tableData';
+
+const iconCreateCluster = new LabIcon({
+  name: 'launcher:create-cluster-icon',
+  svgstr: createClusterIcon
+});
+const iconFilter = new LabIcon({
+  name: 'launcher:filter-icon',
+  svgstr: filterIcon
+});
+
+const iconStop = new LabIcon({
+  name: 'launcher:stop-icon',
+  svgstr: stopIcon
+});
+
+const iconClusterRunning = new LabIcon({
+  name: 'launcher:cluster-running-icon',
+  svgstr: clusterRunningIcon
+});
+const iconClusterError = new LabIcon({
+  name: 'launcher:cluster-error-icon',
+  svgstr: clusterErrorIcon
+});
+interface ICluster {
+  clusterName: string;
+  status: string;
+  clusterImage: string;
+  region: string;
+  zone: string;
+  totalWorkersNode: string;
+  schedulesDeletion: string;
+  actions: React.ReactNode;
+}
+
+interface IListClusterProps {
+  clustersList: ICluster[];
+  isLoading: boolean;
+  setPollingDisable: (value: boolean) => void;
+  listClustersAPI: () => void;
+  handleClusterDetails: (clusterName: string) => void;
+  project_id: string;
+}
+function ListCluster({
+  clustersList,
+  isLoading,
+  setPollingDisable,
+  listClustersAPI,
+  handleClusterDetails,
+  project_id
+}: IListClusterProps) {
+  const data = clustersList;
+  const columns = React.useMemo(
+    () => [
+      {
+        Header: 'Name',
+        accessor: 'clusterName'
+      },
+      {
+        Header: 'Status',
+        accessor: 'status'
+      },
+      {
+        Header: 'Cluster image name',
+        accessor: 'clusterImage'
+      },
+      {
+        Header: 'Region',
+        accessor: 'region'
+      },
+      {
+        Header: 'Zone',
+        accessor: 'zone'
+      },
+      {
+        Header: 'Total worker nodes',
+        accessor: 'totalWorkersNode'
+      },
+      {
+        Header: 'Scheduled deletion',
+        accessor: 'schedulesDeletion'
+      },
+      {
+        Header: 'Actions',
+        accessor: 'actions'
+      }
+    ],
+    []
+  );
+  interface ICell {
+    render(arg0: string): React.ReactNode;
+    column: {
+      Header: string;
+    };
+
+    getCellProps: () => { [key: string]: any };
+    row: {
+      original: {
+        status: string;
+      };
+    };
+
+    value: any;
+  }
+  const tableDataCondition = (cell: ICell) => {
+    if (cell.column.Header === 'Name') {
+      return (
+        <td
+          {...cell.getCellProps()}
+          className="cluster-name"
+          onClick={() =>
+            cell.row.original.status !== STATUS_DELETING &&
+            handleClusterDetails(cell.value)
+          }
+        >
+          {cell.value}
+        </td>
+      );
+    } else if (cell.column.Header === 'Status') {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          <div key="Status" className="cluster-status-parent">
+            {cell.value === STATUS_RUNNING && (
+              <iconClusterRunning.react tag="div" />
+            )}
+            {cell.value === STATUS_STOPPED && <iconStop.react tag="div" />}
+            {cell.value === STATUS_ERROR && (
+              <iconClusterError.react tag="div" />
+            )}
+            {(cell.value === STATUS_PROVISIONING ||
+              cell.value === STATUS_CREATING ||
+              cell.value === STATUS_STARTING ||
+              cell.value === STATUS_STOPPING ||
+              cell.value === STATUS_DELETING) && (
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={15}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+            )}
+            <div className="cluster-status">
+              {cell.value && cell.value.toLowerCase()}
+            </div>
+          </div>
+        </td>
+      );
+    } else {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          {cell.render('Cell')}
+        </td>
+      );
+    }
+  };
+
+  const {
+    getTableProps,
+    getTableBodyProps,
+    headerGroups,
+    rows,
+    prepareRow,
+    state,
+    //@ts-ignore
+    preGlobalFilteredRows,
+    //@ts-ignore
+    setGlobalFilter
+  } =
+    //@ts-ignore
+    useTable({ columns, data }, useGlobalFilter);
+
+  return (
+    <div>
+      <div className="create-cluster-overlay">
+        <div
+          className="create-cluster-sub-overlay"
+          onClick={() => {
+            window.open(
+              `${CREATE_CLUSTER_URL}?project=${project_id}`,
+              '_blank'
+            );
+          }}
+        >
+          <div className="create-cluster-icon">
+            <iconCreateCluster.react tag="div" />
+          </div>
+          <div className="create-cluster-text">Create cluster</div>
+        </div>
+      </div>
+
+      {clustersList.length > 0 ? (
+        <div>
+          <div className="filter-cluster-overlay">
+            <div className="filter-cluster-icon">
+              <iconFilter.react tag="div" />
+            </div>
+            <div className="filter-cluster-text"></div>
+            <div className="filter-cluster-section">
+              <GlobalFilter
+                preGlobalFilteredRows={preGlobalFilteredRows}
+                //@ts-ignore
+                globalFilter={state.globalFilter}
+                setGlobalFilter={setGlobalFilter}
+                listClustersAPI={listClustersAPI}
+                setPollingDisable={setPollingDisable}
+              />
+            </div>
+          </div>
+          <div className="clusters-list-table-parent">
+            <TableData
+              getTableProps={getTableProps}
+              headerGroups={headerGroups}
+              getTableBodyProps={getTableBodyProps}
+              isLoading={isLoading}
+              rows={rows}
+              prepareRow={prepareRow}
+              tableDataCondition={tableDataCondition}
+              fromPage="Clusters"
+            />
+          </div>
+        </div>
+      ) : (
+        <div>
+          {isLoading && (
+            <div className="spin-loaderMain">
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={18}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+              Loading Clusters
+            </div>
+          )}
+          {!isLoading && (
+            <div className="no-data-style">No rows to display</div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default ListCluster;
diff --git a/src/handler/handler.ts b/src/handler/handler.ts
new file mode 100644
index 00000000..091b1858
--- /dev/null
+++ b/src/handler/handler.ts
@@ -0,0 +1,58 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { URLExt } from '@jupyterlab/coreutils';
+import { ServerConnection } from '@jupyterlab/services';
+
+/**
+ * Call the API extension
+ *
+ * @param endPoint API REST end point for the extension
+ * @param init Initial values for the request
+ * @returns The response body interpreted as JSON
+ */
+export async function requestAPI<T>(
+  endPoint = '',
+  init: RequestInit = {}
+): Promise<T> {
+  // Make request to Jupyter API
+  const settings = ServerConnection.makeSettings();
+  const requestUrl = URLExt.join(settings.baseUrl, 'dataproc-plugin', endPoint);
+
+  let response: Response;
+  try {
+    response = await ServerConnection.makeRequest(requestUrl, init, settings);
+  } catch (error: any) {
+    throw new ServerConnection.NetworkError(error);
+  }
+
+  let data: any = await response.text();
+
+  if (data.length > 0) {
+    try {
+      data = JSON.parse(data);
+    } catch (error) {
+      console.log('Not a JSON response body.', response);
+    }
+  }
+
+  if (!response.ok) {
+    throw new ServerConnection.ResponseError(response, data.message);
+  }
+
+  return data;
+}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 00000000..577b7c78
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,227 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import {
+  JupyterFrontEnd,
+  JupyterFrontEndPlugin
+} from '@jupyterlab/application';
+import { MainAreaWidget } from '@jupyterlab/apputils';
+import { ILauncher } from '@jupyterlab/launcher';
+import { LabIcon } from '@jupyterlab/ui-components';
+import { IMainMenu } from '@jupyterlab/mainmenu';
+import { Cluster } from './cluster/cluster';
+import { Serverless } from './batches/batches';
+import clusterIcon from '../style/icons/cluster_icon.svg';
+import serverlessIcon from '../style/icons/serverless_icon.svg';
+import { Menu } from '@lumino/widgets';
+import { AuthLogin } from './login/authLogin';
+import { KernelSpecAPI } from '@jupyterlab/services';
+import { iconDisplay } from './utils/utils';
+import { TITLE_LAUNCHER_CATEGORY } from './utils/const';
+
+const extension: JupyterFrontEndPlugin<void> = {
+  id: 'cluster',
+  autoStart: true,
+  optional: [ILauncher, IMainMenu],
+  activate: async (
+    app: JupyterFrontEnd,
+    launcher: ILauncher,
+    mainMenu: IMainMenu
+  ) => {
+    const { commands } = app;
+    const iconCluster = new LabIcon({
+      name: 'launcher:clusters-icon',
+      svgstr: clusterIcon
+    });
+    const iconServerless = new LabIcon({
+      name: 'launcher:serverless-icon',
+      svgstr: serverlessIcon
+    });
+
+    const kernelSpecs = await KernelSpecAPI.getSpecs();
+    const kernels = kernelSpecs.kernelspecs;
+
+    const createClusterComponentCommand = 'create-cluster-component';
+    commands.addCommand(createClusterComponentCommand, {
+      caption: 'Create a new Cluster Component',
+      label: 'Clusters',
+      // @ts-ignore
+      icon: args => (args['isPalette'] ? null : iconCluster),
+      execute: () => {
+        const content = new Cluster();
+        const widget = new MainAreaWidget<Cluster>({ content });
+        widget.title.label = 'Clusters';
+        widget.title.icon = iconCluster;
+        app.shell.add(widget, 'main');
+      }
+    });
+
+    const createServerlessComponentCommand = 'create-serverless-component';
+    commands.addCommand(createServerlessComponentCommand, {
+      caption: 'Create a new Serverless Component',
+      label: 'Serverless',
+      // @ts-ignore
+      icon: args => (args['isPalette'] ? null : iconServerless),
+      execute: () => {
+        const content = new Serverless();
+        const widget = new MainAreaWidget<Serverless>({ content });
+        widget.title.label = 'Serverless';
+        widget.title.icon = iconServerless;
+        app.shell.add(widget, 'main');
+      }
+    });
+
+    const createAuthLoginComponentCommand = 'create-authlogin-component';
+    commands.addCommand(createAuthLoginComponentCommand, {
+      label: 'Setup',
+      caption: 'Setup',
+      execute: () => {
+        const content = new AuthLogin();
+        const widget = new MainAreaWidget<AuthLogin>({ content });
+        widget.title.label = 'Config Setup';
+        widget.title.icon = iconCluster;
+        app.shell.add(widget, 'main');
+      }
+    });
+
+    const snippetMenu = new Menu({ commands });
+    snippetMenu.title.label = 'Dataproc';
+    snippetMenu.addItem({ command: createAuthLoginComponentCommand });
+    mainMenu.addMenu(snippetMenu);
+
+    if (launcher) {
+      Object.values(kernels).forEach((kernelsData, index) => {
+        if (
+          kernelsData?.resources.endpointParentResource &&
+          kernelsData?.resources.endpointParentResource.includes('/sessions')
+        ) {
+          const commandNotebook = `notebook:create-${kernelsData?.display_name}`;
+          commands.addCommand(commandNotebook, {
+            caption: kernelsData?.display_name,
+            label: kernelsData?.display_name,
+            icon: iconDisplay(kernelsData),
+            execute: async () => {
+              const model = await app.commands.execute(
+                'docmanager:new-untitled',
+                {
+                  type: 'notebook',
+                  path: '',
+                  kernel: { name: kernelsData?.name }
+                }
+              );
+              await app.commands.execute('docmanager:open', {
+                kernel: { name: kernelsData?.name },
+                path: model.path,
+                factory: 'notebook'
+              });
+            }
+          });
+
+          launcher.add({
+            command: commandNotebook,
+            category: 'Dataproc Serverless Notebooks',
+            //@ts-ignore
+            metadata: kernelsData?.metadata,
+            rank: index + 1,
+            //@ts-ignore
+            args: kernelsData?.argv
+          });
+        }
+      });
+      Object.values(kernels).forEach((kernelsData, index) => {
+        if (
+          kernelsData?.resources.endpointParentResource &&
+          !kernelsData?.resources.endpointParentResource.includes('/sessions')
+        ) {
+          const commandNotebook = `notebook:create-${kernelsData?.display_name}`;
+          commands.addCommand(commandNotebook, {
+            caption: kernelsData?.display_name,
+            label: kernelsData?.display_name,
+            icon: iconDisplay(kernelsData),
+            execute: async () => {
+              const model = await app.commands.execute(
+                'docmanager:new-untitled',
+                {
+                  type: 'notebook',
+                  path: '',
+                  kernel: { name: kernelsData?.name }
+                }
+              );
+              await app.commands.execute('docmanager:open', {
+                kernel: { name: kernelsData?.name },
+                path: model.path,
+                factory: 'notebook'
+              });
+            }
+          });
+
+          launcher.add({
+            command: commandNotebook,
+            category: 'Dataproc Cluster Notebooks',
+            //@ts-ignore
+            metadata: kernelsData?.metadata,
+            rank: index + 1,
+            //@ts-ignore
+            args: kernelsData?.argv
+          });
+        }
+      });
+
+      // Object.values(kernels).forEach(kernelsData => {
+      //   if (kernelsData?.resources.endpointParentResource) {
+      //     const commandConsole = `console:create-${kernelsData?.display_name}`;
+      //     commands.addCommand(commandConsole, {
+      //       caption: kernelsData?.display_name,
+      //       label: kernelsData?.display_name,
+      //       icon: iconDisplay(kernelsData),
+      //       execute: async () => {
+      //         await app.commands.execute('console:create', {
+      //           activate: true,
+      //           kernel: { name: kernelsData?.name }
+      //         });
+
+      //         await app.commands.execute('console:open', {
+      //           activate: true,
+      //           kernel: { name: kernelsData?.name }
+      //         });
+      //       }
+      //     });
+
+      //     launcher.add({
+      //       command: commandConsole,
+      //       category: 'Dataproc Console',
+      //       metadata: kernelsData?.metadata,
+      //       args: kernelsData?.argv
+      //     });
+      //   }
+      // });
+
+      launcher.add({
+        command: createClusterComponentCommand,
+        category: TITLE_LAUNCHER_CATEGORY,
+        rank: 1
+      });
+      launcher.add({
+        command: createServerlessComponentCommand,
+        category: TITLE_LAUNCHER_CATEGORY,
+        rank: 2
+      });
+    }
+  }
+};
+
+export default extension;
diff --git a/src/jobs/jobDetails.tsx b/src/jobs/jobDetails.tsx
new file mode 100644
index 00000000..6918f99c
--- /dev/null
+++ b/src/jobs/jobDetails.tsx
@@ -0,0 +1,687 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useEffect, useState } from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import LeftArrowIcon from '../../style/icons/left_arrow_icon.svg';
+// import { Icon } from 'semantic-ui-react';
+import 'semantic-ui-css/semantic.min.css';
+import CloneJobIcon from '../../style/icons/clone_job_icon.svg';
+import StopClusterIcon from '../../style/icons/stop_cluster_icon.svg';
+import StopClusterDisableIcon from '../../style/icons/stop_cluster_disable_icon.svg';
+import DeleteClusterIcon from '../../style/icons/delete_cluster_icon.svg';
+import EditIcon from '../../style/icons/edit_icon.svg';
+import EditIconDisable from '../../style/icons/edit_icon_disable.svg';
+import DeletePopup from '../utils/deletePopup';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  LABEL_TEXT,
+  POLLING_TIME_LIMIT,
+  STATUS_RUNNING
+} from '../utils/const';
+import {
+  authApi,
+  elapsedTime,
+  jobTimeFormat,
+  jobTypeDisplay,
+  jobTypeValue,
+  jobTypeValueArguments,
+  statusMessage
+} from '../utils/utils';
+
+import ClusterDetails from '../cluster/clusterDetails';
+import { ClipLoader } from 'react-spinners';
+import LabelProperties from './labelProperties';
+import SubmitJob from './submitJob';
+import ViewLogs from '../utils/viewLogs';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import { statusDisplay } from '../utils/statusDisplay';
+import { stopJobApi, deleteJobApi } from '../utils/jobServices';
+import errorIcon from '../../style/icons/error_icon.svg';
+
+const iconLeftArrow = new LabIcon({
+  name: 'launcher:left-arrow-icon',
+  svgstr: LeftArrowIcon
+});
+const iconCloneJob = new LabIcon({
+  name: 'launcher:clone-job-icon',
+  svgstr: CloneJobIcon
+});
+const iconStopClusterDisable = new LabIcon({
+  name: 'launcher:stop-cluster-disable-icon',
+  svgstr: StopClusterDisableIcon
+});
+const iconStopCluster = new LabIcon({
+  name: 'launcher:stop-cluster-icon',
+  svgstr: StopClusterIcon
+});
+const iconDeleteCluster = new LabIcon({
+  name: 'launcher:delete-cluster-icon',
+  svgstr: DeleteClusterIcon
+});
+const iconEdit = new LabIcon({
+  name: 'launcher:edit-icon',
+  svgstr: EditIcon
+});
+const iconEditDisable = new LabIcon({
+  name: 'launcher:edit-disable-icon',
+  svgstr: EditIconDisable
+});
+const iconError = new LabIcon({
+  name: 'launcher:error-icon',
+  svgstr: errorIcon
+});
+interface IJobDetailsProps {
+  jobSelected: any;
+  setDetailedJobView: (value: boolean) => void;
+  stopJobApi: (jobId: string) => Promise<void>;
+  deleteJobApi: (jobId: string) => Promise<void>;
+  region: any;
+  setDetailedView: (value: boolean) => void;
+  clusterResponse: any;
+  clustersList: object;
+}
+function JobDetails({
+  jobSelected,
+  setDetailedJobView,
+  region,
+  setDetailedView,
+  clusterResponse,
+  clustersList
+}: IJobDetailsProps) {
+  const [jobInfo, setjobInfo] = useState({
+    status: { state: '', stateStartTime: '' },
+    statusHistory: [{ stateStartTime: '' }],
+    labels: '',
+    reference: { jobId: '' },
+    jobUuid: '',
+    pysparkJob: { args: [], mainPythonFileUri: '' },
+    sparkRJob: { args: [], mainRFileUri: '' },
+    sparkJob: { args: [], mainJarFileUri: '', mainClass: '' },
+    sparkSqlJob: { queryFileUri: '', queryList: { queries: '' }, args: [] },
+    placement: { clusterName: '' }
+  });
+  const [jobInfoResponse, setjobInfoResponse] = useState({
+    status: { state: '', stateStartTime: '' },
+    statusHistory: [{ stateStartTime: '' }],
+    labels: {},
+    reference: { jobId: '' },
+    jobUuid: '',
+    pysparkJob: { args: [], mainPythonFileUri: '' },
+    sparkRJob: { args: [], mainRFileUri: '' },
+    sparkJob: { args: [], mainJarFileUri: '', mainClass: '' },
+    sparkSqlJob: { queryFileUri: '', queryList: { queries: '' }, args: [] },
+
+    placement: { clusterName: '' }
+  });
+  const key: any[] | (() => any[]) = [];
+  const value: any[] | (() => any[]) = [];
+  const [labelEditMode, setLabelEditMode] = useState(false);
+  const [labelDetail, setLabelDetail] = useState(key); //Final label value is stored
+  const [labelDetailUpdated, setLabelDetailUpdated] = useState(value); //temporary storage to validate the label data being typed
+  const [detailedClusterView, setDetailedClusterView] = useState(false);
+  const [isLoading, setIsLoading] = useState(true);
+  const [selectedJobClone, setSelectedJobClone] = useState({});
+  const [submitJobView, setSubmitJobView] = useState(false);
+  const [keyValidation, setKeyValidation] = useState(-1);
+  const [valueValidation, setvalueValidation] = useState(-1);
+  const [duplicateKeyError, setDuplicateKeyError] = useState(-1);
+
+  const [deletePopupOpen, setDeletePopupOpen] = useState(false);
+  const [selectedJobId, setSelectedJobId] = useState('');
+  const [timer, setTimer] = useState<NodeJS.Timer | undefined>(undefined);
+  const [errorView, setErrorView] = useState(false);
+
+  const pollingJobDetails = async (
+    pollingFunction: () => void,
+    pollingDisable: boolean
+  ) => {
+    if (pollingDisable) {
+      clearInterval(timer);
+    } else {
+      setTimer(setInterval(pollingFunction, POLLING_TIME_LIMIT));
+    }
+  };
+
+  const handleDetailedJobView = () => {
+    pollingJobDetails(getJobDetails, true);
+    setDetailedJobView(false);
+  };
+
+  const handleDetailedClusterView = () => {
+    pollingJobDetails(getJobDetails, true);
+    if(!clustersList){
+      setDetailedJobView(false);
+    }
+    setDetailedClusterView(true);
+  };
+
+  useEffect(() => {
+    if (labelEditMode) {
+      pollingJobDetails(getJobDetails, true);
+    } else {
+      getJobDetails();
+      pollingJobDetails(getJobDetails, false);
+    }
+
+    return () => {
+      pollingJobDetails(getJobDetails, true);
+    };
+  }, [labelEditMode]);
+
+  const handleJobLabelEdit = () => {
+    setLabelEditMode(true);
+    setLabelDetailUpdated(labelDetail);
+  };
+  const handleDeleteJob = (jobId: string) => {
+    setSelectedJobId(jobId);
+    setDeletePopupOpen(true);
+  };
+  const handleStopJob = async (jobId: string) => {
+    setSelectedJobId(jobId);
+    await stopJobApi(selectedJobId);
+  };
+  const handleCancelDelete = () => {
+    setDeletePopupOpen(false);
+  };
+
+  const handleDelete = async () => {
+    await deleteJobApi(selectedJobId);
+    setDeletePopupOpen(false);
+    handleDetailedJobView();
+  };
+
+  const updateJobDetails = async (payloadJob: object) => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/jobs/${jobSelected}?updateMask=${LABEL_TEXT}`,
+        {
+          method: 'PATCH',
+          body: JSON.stringify(payloadJob),
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResultJob: Response) => {
+              // TODO: Handle Toast here
+              console.log(responseResultJob);
+            })
+            .catch((e: Error) => console.error(e));
+        })
+        .catch((err: Error) => {
+          console.error('Error in updating job', err);
+          toast.error('Failed to Update the job ');
+        });
+    }
+  };
+
+  const handleSaveEdit = () => {
+    const payload = jobInfoResponse;
+    const labelObject: { [key: string]: string } = {};
+    labelDetailUpdated.forEach((label: string) => {
+      const key = label.split(':')[0];
+      const value = label.split(':')[1];
+      labelObject[key] = value;
+    });
+    payload.labels = labelObject;
+    updateJobDetails(payload);
+    setLabelEditMode(false);
+    getJobDetails();
+  };
+
+  const handleCancelEdit = () => {
+    setLabelEditMode(false);
+  };
+
+  const getJobDetails = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/jobs/${jobSelected}`,
+        {
+          method: 'GET',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              setjobInfoResponse(responseResult);
+              const labelValue: string[] = [];
+              if (responseResult.labels) {
+                for (const [key, value] of Object.entries(
+                  responseResult.labels
+                )) {
+                  labelValue.push(`${key}:${value}`);
+                }
+              }
+              setjobInfo(responseResult);
+              setLabelDetail(labelValue);
+              setIsLoading(false);
+              setSelectedJobClone(responseResult);
+            })
+            .catch((e: Error) => {
+              console.error(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error in getting job details', err);
+          toast.error('Failed to fetch Job details ');
+        });
+    }
+  };
+
+  const startTime = jobTimeFormat(jobInfo.statusHistory[0].stateStartTime);
+  const job = jobTypeValue(jobInfo);
+  const jobType = jobTypeDisplay(job);
+  const jobArgument = jobTypeValueArguments(jobInfo);
+  const jobTypeConcat = jobArgument + 'Job';
+  //@ts-ignore
+  const argumentsList = jobInfo[jobTypeConcat].args;
+  const statusMsg = statusMessage(jobInfo);
+
+  const endTime = jobInfo.status.stateStartTime;
+  const jobStartTime = new Date(
+    jobInfo.statusHistory[jobInfo.statusHistory.length - 1].stateStartTime
+  );
+
+  const elapsedTimeString = elapsedTime(endTime, jobStartTime);
+
+  const statusStyleSelection = (jobInfo: any) => {
+    if (jobInfo.status.state === STATUS_RUNNING) {
+      return 'action-cluster-section'; //CSS class
+    } else {
+      return 'action-cluster-section disabled';
+    }
+  };
+
+  const handleCloneJob = () => {
+    setSubmitJobView(true);
+  };
+
+  const styleJobEdit = (labelEditMode: boolean) => {
+    if (labelEditMode) {
+      return 'job-edit-button-disabled';
+    } else {
+      return 'job-edit-button';
+    }
+  };
+
+  const styleIconColor = (labelEditMode: boolean) => {
+    if (labelEditMode) {
+      return 'color-icon-disabled';
+    } else {
+      return 'color-icon';
+    }
+  };
+
+  return (
+    <div>
+      {errorView && (
+        <div className="error-view-parent">
+          <div className="back-arrow-icon" onClick={() => setErrorView(false)}>
+            <iconLeftArrow.react tag="div" />
+          </div>
+          <div className="error-view-message-parent">
+            <iconError.react tag="div" />
+            <div className="error-view-message">
+              Unable to find the resource you requested
+            </div>
+          </div>
+        </div>
+      )}
+      {submitJobView && !detailedClusterView && (
+        <SubmitJob
+          setSubmitJobView={setSubmitJobView}
+          selectedJobClone={selectedJobClone}
+          clusterResponse={clusterResponse}
+        />
+      )}
+      {deletePopupOpen && (
+        <DeletePopup
+          onCancel={() => handleCancelDelete()}
+          onDelete={() => handleDelete()}
+          deletePopupOpen={deletePopupOpen}
+          DeleteMsg={
+            'This will delete ' + selectedJobId + ' and cannot be undone.'
+          }
+        />
+      )}
+      {detailedClusterView && (
+        <ClusterDetails
+          clusterSelected={jobInfo.placement.clusterName}
+          setDetailedView={setDetailedView}
+          setDetailedClusterView={setDetailedClusterView}
+          submitJobView={submitJobView}
+          selectedJobClone={selectedJobClone}
+          clusterResponse={clusterResponse}
+          setSubmitJobView={setSubmitJobView}
+          setDetailedJobView={setDetailedJobView}
+          setSelectedJobClone={setSelectedJobClone}
+        />
+      )}
+      {!submitJobView && !detailedClusterView && !errorView && (
+        <div className="scroll-comp">
+          {jobInfo.jobUuid !== '' && (
+            <div>
+              <div className="cluster-details-header">
+                <div
+                  className="back-arrow-icon"
+                  role="button"
+                  aria-label="Delete Job"
+                  onClick={() => handleDetailedJobView()}
+                >
+                  <iconLeftArrow.react tag="div" />
+                </div>
+                <div className="cluster-details-title">Job details</div>
+                <div
+                  className="action-cluster-section"
+                  onClick={() => handleCloneJob()}
+                >
+                  <div className="action-cluster-icon">
+                    <iconCloneJob.react tag="div" />
+                  </div>
+                  <div className="action-cluster-text">CLONE</div>
+                </div>
+
+                <div
+                  className={statusStyleSelection(jobInfo)}
+                  onClick={() =>
+                    jobInfo.status.state === STATUS_RUNNING &&
+                    handleStopJob(jobInfo.reference.jobId)
+                  }
+                >
+                  <div className="action-cluster-icon">
+                    {jobInfo.status.state === STATUS_RUNNING ? (
+                      <iconStopCluster.react tag="div" />
+                    ) : (
+                      <iconStopClusterDisable.react tag="div" />
+                    )}
+                  </div>
+                  <div className="action-cluster-text">STOP</div>
+                </div>
+                <div
+                  className="action-cluster-section"
+                  onClick={() => handleDeleteJob(jobInfo.reference.jobId)}
+                >
+                  <div className="action-cluster-icon">
+                    <iconDeleteCluster.react tag="div" />
+                  </div>
+                  <div className="action-cluster-text">DELETE</div>
+                </div>
+                <ViewLogs
+                  clusterName={jobInfo.placement.clusterName}
+                  setErrorView={setErrorView}
+                />
+              </div>
+
+              <div className="cluster-details-container">
+                <div className="row-details"></div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Job ID</div>
+                  <div className="cluster-details-value">
+                    {jobInfo.reference.jobId}
+                  </div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Job UUID</div>
+                  <div className="cluster-details-value">{jobInfo.jobUuid}</div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Type</div>
+                  <div className="cluster-details-value">Dataproc Job</div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Status</div>
+                  {statusDisplay(statusMsg)}
+                </div>
+                <ToastContainer />
+              </div>
+              <div className="cluster-details-header">
+                <div className="cluster-details-title">Configuration</div>
+              </div>
+              <div className="job-edit-header">
+                <div
+                  className={styleJobEdit(labelEditMode)}
+                  onClick={() => (labelEditMode ? '' : handleJobLabelEdit())}
+                >
+                  {labelEditMode ? (
+                    <iconEditDisable.react
+                      tag="div"
+                      className={styleIconColor(labelEditMode)}
+                    />
+                  ) : (
+                    <iconEdit.react
+                      tag="div"
+                      className={styleIconColor(labelEditMode)}
+                    />
+                  )}
+                  <div
+                    className={
+                      labelEditMode ? 'job-edit-text-disabled' : 'job-edit-text'
+                    }
+                  >
+                    EDIT
+                  </div>
+                </div>
+              </div>
+              <div className="cluster-details-container">
+                <div className="row-details">
+                  <div className="cluster-details-label">Start time:</div>
+                  <div className="cluster-details-value">{startTime}</div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Elapsed time:</div>
+                  <div className="cluster-details-value">
+                    {elapsedTimeString}
+                  </div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Status:</div>
+                  {statusDisplay(statusMsg)}
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Region</div>
+                  <div className="cluster-details-value">{region.region}</div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Cluster</div>
+
+                  <div
+                    className="cluster-details-value-job"
+                    onClick={() => handleDetailedClusterView()}
+                  >
+                    {jobInfo.placement.clusterName}
+                  </div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Job type</div>
+                  <div className="cluster-details-value">{jobType}</div>
+                </div>
+                {job === 'SparkR' && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">Spark R files</div>
+                    <div className="cluster-details-value">
+                      {jobInfo.sparkRJob.mainRFileUri}
+                    </div>
+                  </div>
+                )}
+                {job === 'SparkSQL' && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">
+                      Query source type
+                    </div>
+                    {jobInfo.sparkSqlJob.queryFileUri ? (
+                      <div className="cluster-details-value">Query file</div>
+                    ) : (
+                      <div className="cluster-details-value">Query script</div>
+                    )}
+                  </div>
+                )}
+                {job === 'PySpark' && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">
+                      Main python file
+                    </div>
+                    <div className="cluster-details-value">
+                      {jobInfo.pysparkJob.mainPythonFileUri}
+                    </div>
+                  </div>
+                )}
+                {job === 'Spark' && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">
+                      Main class or jar
+                    </div>
+                    <div className="cluster-details-value">
+                      {jobInfo.sparkJob.mainJarFileUri}
+                      {jobInfo.sparkJob.mainClass}
+                    </div>
+                  </div>
+                )}
+                {job === 'SparkSQL' && jobInfo.sparkSqlJob.queryFileUri && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">Query file</div>
+                    <div className="cluster-details-value">
+                      {jobInfo.sparkSqlJob.queryFileUri}
+                    </div>
+                  </div>
+                )}
+                {job === 'SparkSQL' && jobInfo.sparkSqlJob.queryList && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">Query script</div>
+                    <div className="cluster-details-value">
+                      {jobInfo.sparkSqlJob.queryList.queries}
+                    </div>
+                  </div>
+                )}
+                {argumentsList && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">Arguments</div>
+                    <div className="cluster-details-value">
+                      {argumentsList.length > 0
+                        ? argumentsList.map((argument: string) => {
+                            return (
+                              <div
+                                key={argument}
+                                className="job-argument-style"
+                              >
+                                {argument}
+                              </div>
+                            );
+                          })
+                        : ''}
+                    </div>
+                  </div>
+                )}
+                <div className="job-details-label-row">
+                  <div className="cluster-details-label">Labels</div>
+                  {!labelEditMode ? (
+                    <div className="job-label-style-parent">
+                      {labelDetail.length > 0
+                        ? labelDetail.map(label => {
+                            return (
+                              <div key={label} className="job-label-style">
+                                {label.split(':')[0]} : {label.split(':')[1]}
+                              </div>
+                            );
+                          })
+                        : 'None'}
+                    </div>
+                  ) : (
+                    <LabelProperties
+                      labelDetail={labelDetail}
+                      setLabelDetail={setLabelDetail}
+                      labelDetailUpdated={labelDetailUpdated}
+                      setLabelDetailUpdated={setLabelDetailUpdated}
+                      selectedJobClone={selectedJobClone ? true : false}
+                      buttonText="ADD LABEL"
+                      keyValidation={keyValidation}
+                      setKeyValidation={setKeyValidation}
+                      valueValidation={valueValidation}
+                      setvalueValidation={setvalueValidation}
+                      labelEditMode={labelEditMode}
+                      duplicateKeyError={duplicateKeyError}
+                      setDuplicateKeyError={setDuplicateKeyError}
+                    />
+                  )}
+                </div>
+                {labelEditMode && (
+                  <div className="job-button-style-parent">
+                    <div className="job-save-button-style">
+                      <div
+                        onClick={() => {
+                          handleSaveEdit();
+                        }}
+                      >
+                        SAVE
+                      </div>
+                    </div>
+                    <div className="job-cancel-button-style">
+                      <div
+                        onClick={() => {
+                          handleCancelEdit();
+                        }}
+                      >
+                        CANCEL
+                      </div>
+                    </div>
+                  </div>
+                )}
+              </div>
+              <ToastContainer />
+            </div>
+          )}
+
+          {jobInfo.jobUuid === '' && (
+            <div className="loader-full-style">
+              {isLoading && (
+                <div>
+                  <ClipLoader
+                    color="#8A8A8A"
+                    loading={true}
+                    size={20}
+                    aria-label="Loading Spinner"
+                    data-testid="loader"
+                  />
+                  Loading Job Details
+                </div>
+              )}
+            </div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default JobDetails;
diff --git a/src/jobs/jobs.tsx b/src/jobs/jobs.tsx
new file mode 100644
index 00000000..32b9245f
--- /dev/null
+++ b/src/jobs/jobs.tsx
@@ -0,0 +1,548 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useState, useEffect } from 'react';
+import { useTable, useGlobalFilter } from 'react-table';
+import {
+  authApi,
+  jobTimeFormat,
+  jobTypeValue,
+  elapsedTime,
+  statusMessage,
+  jobTypeDisplay
+} from '../utils/utils';
+
+import { LabIcon } from '@jupyterlab/ui-components';
+import filterIcon from '../../style/icons/filter_icon.svg';
+import cloneIcon from '../../style/icons/clone_icon.svg';
+import stopIcon from '../../style/icons/stop_icon.svg';
+import JobDetails from './jobDetails';
+import stopDisableIcon from '../../style/icons/stop_disable_icon.svg';
+import deleteIcon from '../../style/icons/delete_icon.svg';
+import clusterRunningIcon from '../../style/icons/cluster_running_icon.svg';
+import clusterErrorIcon from '../../style/icons/cluster_error_icon.svg';
+import SucceededIcon from '../../style/icons/succeeded_icon.svg';
+import SubmitJobIcon from '../../style/icons/submit_job_icon.svg';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  ClusterStatus,
+  POLLING_TIME_LIMIT,
+  STATUS_CANCELLED,
+  STATUS_CREATING,
+  STATUS_DELETING,
+  STATUS_FAIL,
+  STATUS_PROVISIONING,
+  STATUS_STARTING,
+  STATUS_STOPPING,
+  STATUS_SUCCESS
+} from '../utils/const';
+import ClipLoader from 'react-spinners/ClipLoader';
+import SubmitJob from './submitJob';
+import GlobalFilter from '../utils/globalFilter';
+import TableData from '../utils/tableData';
+import DeletePopup from '../utils/deletePopup';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import { stopJobApi, deleteJobApi } from '../utils/jobServices';
+
+const iconFilter = new LabIcon({
+  name: 'launcher:filter-icon',
+  svgstr: filterIcon
+});
+const iconClone = new LabIcon({
+  name: 'launcher:clone-icon',
+  svgstr: cloneIcon
+});
+const iconStop = new LabIcon({
+  name: 'launcher:stop-icon',
+  svgstr: stopIcon
+});
+const iconStopDisable = new LabIcon({
+  name: 'launcher:stop-disable-icon',
+  svgstr: stopDisableIcon
+});
+const iconDelete = new LabIcon({
+  name: 'launcher:delete-icon',
+  svgstr: deleteIcon
+});
+const iconClusterRunning = new LabIcon({
+  name: 'launcher:cluster-running-icon',
+  svgstr: clusterRunningIcon
+});
+const iconClusterError = new LabIcon({
+  name: 'launcher:cluster-error-icon',
+  svgstr: clusterErrorIcon
+});
+const iconSucceeded = new LabIcon({
+  name: 'launcher:succeeded-icon',
+  svgstr: SucceededIcon
+});
+const iconSubmitJob = new LabIcon({
+  name: 'launcher:submit-job-icon',
+  svgstr: SubmitJobIcon
+});
+
+function JobComponent({
+  clusterSelected,
+  detailedJobView,
+  setDetailedJobView,
+  submitJobView,
+  setSubmitJobView,
+  setDetailedView,
+  clusterResponse,
+  selectedJobClone,
+  setSelectedJobClone,
+  clustersList
+}: any) {
+  const [jobsList, setjobsList] = useState([]);
+  const [jobSelected, setjobSelected] = useState({});
+  const [isLoading, setIsLoading] = useState(true);
+  const [pollingDisable, setPollingDisable] = useState(false);
+  const [region, setRegion] = useState('');
+  const [deletePopupOpen, setDeletePopupOpen] = useState(false);
+  const [selectedJobId, setSelectedJobId] = useState('');
+  const [timer, setTimer] = useState<NodeJS.Timer | undefined>(undefined);
+  const pollingJobs = async (
+    pollingFunction: () => void,
+    pollingDisable: boolean
+  ) => {
+    if (pollingDisable) {
+      clearInterval(timer);
+    } else {
+      setTimer(setInterval(pollingFunction, POLLING_TIME_LIMIT));
+    }
+  };
+
+  const data = jobsList;
+
+  const columns = React.useMemo(
+    () => [
+      {
+        Header: 'Job ID',
+        accessor: 'jobid'
+      },
+      {
+        Header: 'Status',
+        accessor: 'status'
+      },
+      {
+        Header: 'Region',
+        accessor: 'region'
+      },
+      {
+        Header: 'Type',
+        accessor: 'type'
+      },
+      {
+        Header: 'Start time',
+        accessor: 'starttime'
+      },
+
+      {
+        Header: 'Elapsed time',
+        accessor: 'elapsedtime'
+      },
+      {
+        Header: 'Labels',
+        accessor: 'labels'
+      },
+      {
+        Header: 'Actions',
+        accessor: 'actions'
+      }
+    ],
+    []
+  );
+
+  const jobDetails = (selectedName: string) => {
+    pollingJobs(listJobsAPI, true);
+    const filteredJobDetails = jobsList.filter((jobInfo: any) => {
+      return jobInfo.jobid === selectedName;
+    });
+    const region = filteredJobDetails[0];
+    setRegion(region);
+    setjobSelected(selectedName);
+    setDetailedJobView(true);
+  };
+
+  const handleSubmitJobOpen = () => {
+    setSubmitJobView(true);
+    setSelectedJobClone('');
+  };
+  const handleDeleteJob = (jobId: string) => {
+    setSelectedJobId(jobId);
+    setDeletePopupOpen(true);
+  };
+  const handleStopJob = async (jobId: string) => {
+    setSelectedJobId(jobId);
+    await stopJobApi(jobId);
+  };
+  const handleCancelDelete = () => {
+    setDeletePopupOpen(false);
+  };
+
+  const handleDelete = async () => {
+    await deleteJobApi(selectedJobId);
+    setDeletePopupOpen(false);
+  };
+
+  const listJobsAPI = async () => {
+    const credentials = await authApi();
+    const clusterName = clusterSelected ?? '';
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/jobs?pageSize=100&&clusterName=${clusterName}`,
+        {
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              let transformJobListData = [];
+              transformJobListData = responseResult.jobs.map((data: any) => {
+                const startTime = jobTimeFormat(
+                  data.statusHistory[0].stateStartTime
+                );
+                const job = jobTypeValue(data);
+                const jobType = jobTypeDisplay(job);
+                const endTime = data.status.stateStartTime;
+                const jobStartTime = new Date(
+                  data.statusHistory[0].stateStartTime
+                );
+
+                const elapsedTimeString = elapsedTime(endTime, jobStartTime);
+
+                const statusMsg = statusMessage(data);
+
+                const labelvalue = [];
+                if (data.labels) {
+                  for (const [key, value] of Object.entries(data.labels)) {
+                    labelvalue.push(`${key} : ${value}`);
+                  }
+                } else {
+                  labelvalue.push('None');
+                }
+                return {
+                  jobid: data.reference.jobId,
+                  status: statusMsg,
+                  region: credentials.region_id,
+                  type: jobType,
+                  starttime: startTime,
+                  elapsedtime: elapsedTimeString,
+                  labels: labelvalue,
+                  actions: renderActions(data)
+                };
+              });
+              setjobsList(transformJobListData);
+              setIsLoading(false);
+            })
+            .catch((e: Error) => {
+              console.error(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error listing jobs', err);
+          toast.error('Failed to fetch Jobs');
+        });
+    }
+  };
+
+  function handleCloneJob(data: object) {
+    setSubmitJobView(true);
+    setSelectedJobClone(data);
+  }
+  function renderActions(data: {
+    reference: { jobId: string };
+    status: { state: ClusterStatus };
+    clusterName: string;
+  }) {
+    const jobId = data.reference.jobId;
+    return (
+      <div className="actions-icon">
+        <div
+          className="icon-buttons-style"
+          title="Clone Job"
+          onClick={() => handleCloneJob(data)}
+        >
+          <iconClone.react tag="div" />
+        </div>
+        <div
+          className={
+            data.status.state === ClusterStatus.STATUS_RUNNING
+              ? 'icon-buttons-style'
+              : 'icon-buttons-style-disable'
+          }
+          title="Stop Job"
+          onClick={
+            data.status.state === ClusterStatus.STATUS_RUNNING
+              ? () => handleStopJob(jobId)
+              : undefined
+          }
+        >
+          {data.status.state === ClusterStatus.STATUS_RUNNING ? (
+            <iconStop.react tag="div" />
+          ) : (
+            <iconStopDisable.react tag="div" />
+          )}
+        </div>
+        <div
+          className={
+            data.status.state === ClusterStatus.STATUS_RUNNING
+              ? 'icon-buttons-style-disable'
+              : 'icon-buttons-style'
+          }
+          title="Delete Job"
+          onClick={
+            data.status.state !== ClusterStatus.STATUS_RUNNING
+              ? () => handleDeleteJob(jobId)
+              : undefined
+          }
+        >
+          {data.status.state === ClusterStatus.STATUS_RUNNING ? (
+            <iconDelete.react tag="div" />
+          ) : (
+            <iconDelete.react tag="div" />
+          )}
+        </div>
+      </div>
+    );
+  }
+
+  useEffect(() => {
+    listJobsAPI();
+    pollingJobs(listJobsAPI, pollingDisable);
+
+    return () => {
+      pollingJobs(listJobsAPI, true);
+    };
+  }, [pollingDisable, detailedJobView]);
+  interface ICell {
+    render(arg0: string): React.ReactNode;
+    column: {
+      Header: string;
+    };
+
+    getCellProps: () => { [key: string]: any };
+    row: {
+      original: {
+        status: string;
+      };
+    };
+
+    value: any;
+  }
+  const tableDataCondition = (cell: ICell) => {
+    if (cell.column.Header === 'Job ID') {
+      return (
+        <td
+          {...cell.getCellProps()}
+          className="cluster-name"
+          onClick={() => jobDetails(cell.value)}
+        >
+          {cell.value}
+        </td>
+      );
+    }
+    if (cell.column.Header === 'Status' && cell.value) {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          <div key="Status" className="cluster-status-parent">
+            {cell.value === ClusterStatus.STATUS_RUNNING && (
+              <iconClusterRunning.react tag="div" />
+            )}
+            {cell.value === STATUS_CANCELLED && <iconStop.react tag="div" />}
+            {cell.value === STATUS_FAIL && <iconClusterError.react tag="div" />}
+            {cell.value === STATUS_SUCCESS && <iconSucceeded.react tag="div" />}
+            {(cell.value === STATUS_PROVISIONING ||
+              cell.value === STATUS_CREATING ||
+              cell.value === STATUS_STARTING ||
+              cell.value === STATUS_STOPPING ||
+              cell.value === STATUS_DELETING) && (
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={15}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+            )}
+            <div className="cluster-status">{cell.value.toLowerCase()}</div>
+          </div>
+        </td>
+      );
+    }
+    if (cell.column.Header === 'Labels') {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          {cell.value.map((label: string) => {
+            return (
+              <div
+                key={label}
+                className={label !== 'None' ? 'job-label-style-list' : ''}
+              >
+                {label}
+              </div>
+            );
+          })}
+        </td>
+      );
+    } else {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          {cell.render('Cell')}
+        </td>
+      );
+    }
+  };
+
+  const {
+    getTableProps,
+    getTableBodyProps,
+    headerGroups,
+    rows,
+    prepareRow,
+    state,
+    //@ts-ignore
+    preGlobalFilteredRows,
+    //@ts-ignore
+    setGlobalFilter
+  } = useTable({ columns, data }, useGlobalFilter);
+
+  return (
+    <div>
+      <ToastContainer />
+      {submitJobView && !detailedJobView && (
+        <SubmitJob
+          setSubmitJobView={setSubmitJobView}
+          selectedJobClone={selectedJobClone}
+          clusterResponse={clusterResponse}
+        />
+      )}
+      {deletePopupOpen && (
+        <DeletePopup
+          onCancel={() => handleCancelDelete()}
+          onDelete={() => handleDelete()}
+          deletePopupOpen={deletePopupOpen}
+          DeleteMsg={
+            'This will delete ' + selectedJobId + ' and cannot be undone.'
+          }
+        />
+      )}
+      {!submitJobView && detailedJobView && (
+        <JobDetails
+          jobSelected={jobSelected}
+          setDetailedJobView={setDetailedJobView}
+          stopJobApi={stopJobApi}
+          deleteJobApi={deleteJobApi}
+          region={region}
+          setDetailedView={setDetailedView}
+          clusterResponse={clusterResponse}
+          clustersList={clustersList}
+        />
+      )}
+      {!submitJobView && !detailedJobView && (
+        <div>
+          {
+          // clustersList &&
+            clusterResponse &&
+            clusterResponse.clusters &&
+            clusterResponse.clusters.length > 0 && (
+              <div className="create-cluster-overlay">
+                <div
+                  className="create-cluster-sub-overlay"
+                  onClick={() => {
+                    handleSubmitJobOpen();
+                  }}
+                >
+                  <div className="create-cluster-icon">
+                    <iconSubmitJob.react tag="div" />
+                  </div>
+                  <div className="create-cluster-text">SUBMIT JOB</div>
+                </div>
+              </div>
+            )}
+          {jobsList.length > 0 ? (
+            <div>
+              <div className="filter-cluster-overlay">
+                <div className="filter-cluster-icon">
+                  <iconFilter.react tag="div" />
+                </div>
+                <div className="filter-cluster-text"></div>
+                <div className="filter-cluster-section">
+                  <GlobalFilter
+                    preGlobalFilteredRows={preGlobalFilteredRows}
+                    //@ts-ignore
+                    globalFilter={state.globalFilter}
+                    setGlobalFilter={setGlobalFilter}
+                    setPollingDisable={setPollingDisable}
+                  />
+                </div>
+              </div>
+              <div
+                className={
+                  clusterResponse
+                    ? 'jobs-list-table-parent'
+                    : 'jobs-list-table-parent-small'
+                }
+              >
+                <TableData
+                  getTableProps={getTableProps}
+                  headerGroups={headerGroups}
+                  getTableBodyProps={getTableBodyProps}
+                  isLoading={isLoading}
+                  rows={rows}
+                  prepareRow={prepareRow}
+                  tableDataCondition={tableDataCondition}
+                  fromPage="Jobs"
+                />
+              </div>
+            </div>
+          ) : (
+            <div>
+              {isLoading && (
+                <div className="spin-loaderMain">
+                  <ClipLoader
+                    color="#8A8A8A"
+                    loading={true}
+                    size={20}
+                    aria-label="Loading Spinner"
+                    data-testid="loader"
+                  />
+                  Loading Jobs
+                </div>
+              )}
+              {!isLoading && (
+                <div className="no-data-style">No rows to display</div>
+              )}
+            </div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default JobComponent;
diff --git a/src/jobs/labelProperties.tsx b/src/jobs/labelProperties.tsx
new file mode 100644
index 00000000..9a36caf2
--- /dev/null
+++ b/src/jobs/labelProperties.tsx
@@ -0,0 +1,301 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useEffect } from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import plusIcon from '../../style/icons/plus_icon.svg';
+import plusIconDisable from '../../style/icons/plus_icon_disable.svg';
+import deleteIcon from '../../style/icons/delete_icon.svg';
+import errorIcon from '../../style/icons/error_icon.svg';
+import { Input } from 'semantic-ui-react';
+
+const iconPlus = new LabIcon({
+  name: 'launcher:plus-icon',
+  svgstr: plusIcon
+});
+const iconPlusDisable = new LabIcon({
+  name: 'launcher:plus-disable-icon',
+  svgstr: plusIconDisable
+});
+const iconDelete = new LabIcon({
+  name: 'launcher:delete-icon',
+  svgstr: deleteIcon
+});
+const iconError = new LabIcon({
+  name: 'launcher:error-icon',
+  svgstr: errorIcon
+});
+
+function LabelProperties({
+  labelDetail,
+  setLabelDetail,
+  labelDetailUpdated,
+  setLabelDetailUpdated,
+  selectedJobClone,
+  buttonText,
+  keyValidation,
+  setKeyValidation,
+  valueValidation,
+  setvalueValidation,
+  duplicateKeyError,
+  setDuplicateKeyError,
+  labelEditMode
+}: any) {
+  useEffect(() => {
+    if (!labelEditMode) {
+      buttonText === 'ADD LABEL' && !selectedJobClone
+        ? setLabelDetail(['client:dataproc-jupyter-plugin'])
+        : setLabelDetail([]);
+      buttonText === 'ADD LABEL' && !selectedJobClone
+        ? setLabelDetailUpdated(['client:dataproc-jupyter-plugin'])
+        : setLabelDetail([]);
+    }
+  }, []);
+
+  const handleAddLabel = () => {
+    const labelAdd = [...labelDetail];
+    labelAdd.push(':');
+    setLabelDetailUpdated(labelAdd);
+    setLabelDetail(labelAdd);
+  };
+
+  const handleDeleteLabel = (index: any, value: string) => {
+    const labelDelete = [...labelDetail];
+    labelDelete.splice(index, 1);
+    setLabelDetailUpdated(labelDelete);
+    setLabelDetail(labelDelete);
+    setDuplicateKeyError(-1);
+    setKeyValidation(-1);
+    setvalueValidation(-1);
+  };
+
+  const handleEditLabelSwitch = (index: any) => {
+    if (duplicateKeyError === -1) {
+      setLabelDetail(labelDetailUpdated);
+    }
+  };
+  const handleEditLabel = (
+    value: string,
+    index: number,
+    keyValue: string,
+    keyValueData: string
+  ) => {
+    const labelEdit = [...labelDetail];
+    labelEdit.forEach((data, dataNumber: any) => {
+      if (index === dataNumber) {
+        if (keyValue === 'key') {
+          const regexp = /^[a-z0-9-_]+$/;
+          const check = value;
+          if (
+            check.search(regexp) === -1 ||
+            value.charAt(0) !== value.charAt(0).toLowerCase()
+          ) {
+            setKeyValidation(index);
+          } else {
+            setKeyValidation(-1);
+          }
+
+          // Check for duplicate key when editing
+          const newKey = value;
+          const duplicateIndex = labelEdit.findIndex(
+            (label, i) => i !== index && label.split(':')[0] === newKey
+          );
+          if (duplicateIndex !== -1) {
+            setDuplicateKeyError(index);
+            // Show error message or take necessary action
+          } else {
+            setDuplicateKeyError(-1);
+          }
+
+          data = data.replace(data.split(':')[0], value);
+        } else {
+          const regexp = /^[a-z0-9-_]+$/;
+          const check = value;
+          if (check.search(regexp) === -1) {
+            setvalueValidation(index);
+          } else {
+            setvalueValidation(-1);
+          }
+          if (data.split(':')[1] === '') {
+            data = data + value;
+          } else {
+            data = data.replace(data.split(':')[1], value);
+          }
+        }
+      }
+      labelEdit[dataNumber] = data;
+    });
+
+    setLabelDetailUpdated(labelEdit);
+  };
+
+  const styleAddLabelButton = (buttonText: any, labelDetail: any) => {
+    if (
+      buttonText === 'ADD LABEL' &&
+      (labelDetail.length === 0 ||
+        labelDetail[labelDetail.length - 1].split(':')[0].length > 0) &&
+      duplicateKeyError === -1
+    ) {
+      return 'job-add-label-button';
+    } else if (
+      buttonText === 'ADD LABEL' &&
+      (labelDetail.length === 0 ||
+        labelDetail[labelDetail.length - 1].split(':')[0].length === 0) &&
+      duplicateKeyError !== -1
+    ) {
+      return 'job-add-label-button-disabled';
+    } else if (
+      buttonText !== 'ADD LABEL' &&
+      (labelDetail.length === 0 ||
+        labelDetail[labelDetail.length - 1].split(':')[0].length > 0)
+    ) {
+      return 'job-add-property-button';
+    } else {
+      return 'job-add-property-button-disabled';
+    }
+  };
+
+  return (
+    <div>
+      <div className="job-label-edit-parent">
+        {labelDetail.length > 0 &&
+          labelDetail.map((label: any, index: any) => {
+            return (
+              <div key={label}>
+                <div className="job-label-edit-row">
+                  <div className="key-message-wrapper">
+                    <Input
+                      placeholder={`Key ${index + 1}*`}
+                      className="edit-input-style"
+                      disabled={
+                        label.split(':')[0] === '' ||
+                        buttonText !== 'ADD LABEL' ||
+                        duplicateKeyError !== -1
+                          ? false
+                          : true
+                      }
+                      onBlur={() => handleEditLabelSwitch(index)}
+                      onChange={e =>
+                        handleEditLabel(
+                          e.target.value,
+                          index,
+                          'key',
+                          label.split(':')[0]
+                        )
+                      }
+                      defaultValue={label.split(':')[0]}
+                    />
+
+                    {labelDetailUpdated[index].split(':')[0] === '' ? (
+                      <div className="error-key-parent">
+                        <iconError.react tag="div" />
+                        <div className="error-key-missing">key is required</div>
+                      </div>
+                    ) : (
+                      keyValidation === index &&
+                      buttonText === 'ADD LABEL' && (
+                        <div className="error-key-parent">
+                          <iconError.react tag="div" />
+                          <div className="error-key-missing">
+                            Only hyphens (-), underscores (_), lowercase
+                            characters, and numbers are allowed. Keys must start
+                            with a lowercase character. International characters
+                            are allowed.
+                          </div>
+                        </div>
+                      )
+                    )}
+                    {duplicateKeyError === index &&
+                      buttonText === 'ADD LABEL' && (
+                        <div className="error-key-parent">
+                          <iconError.react tag="div" />
+                          <div className="error-key-missing">
+                            The key is already present
+                          </div>
+                        </div>
+                      )}
+                  </div>
+                  <div className="key-message-wrapper">
+                    <Input
+                      placeholder={`Value ${index + 1}`}
+                      className="edit-input-style"
+                      onBlur={() => handleEditLabelSwitch(index)}
+                      onChange={e =>
+                        handleEditLabel(
+                          e.target.value,
+                          index,
+                          'value',
+                          label.split(':')[1]
+                        )
+                      }
+                      defaultValue={label.split(':')[1]}
+                    />
+                    {valueValidation === index &&
+                      buttonText === 'ADD LABEL' && (
+                        <div className="error-key-parent">
+                          <iconError.react tag="div" />
+                          <div className="error-key-missing">
+                            Only hyphens (-), underscores (_), lowercase
+                            characters, and numbers are allowed. International
+                            characters are allowed.
+                          </div>
+                        </div>
+                      )}
+                  </div>
+                  <div
+                    onClick={() =>
+                      handleDeleteLabel(index, label.split(':')[0])
+                    }
+                  >
+                    <iconDelete.react tag="div" />
+                  </div>
+                  <></>
+                </div>
+              </div>
+            );
+          })}
+        <div
+          className={styleAddLabelButton(buttonText, labelDetail)}
+          onClick={() => {
+            (labelDetail.length === 0 ||
+              labelDetail[labelDetail.length - 1].split(':')[0].length > 0) &&
+              handleAddLabel();
+          }}
+        >
+          {labelDetail.length === 0 ||
+          labelDetail[labelDetail.length - 1].split(':')[0].length > 0 ? (
+            <iconPlus.react tag="div" />
+          ) : (
+            <iconPlusDisable.react tag="div" />
+          )}
+          <div
+            className={
+              labelDetail.length === 0 ||
+              labelDetail[labelDetail.length - 1].split(':')[0].length > 0
+                ? 'job-edit-text'
+                : 'job-edit-text-disabled'
+            }
+          >
+            {buttonText}
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+export default LabelProperties;
diff --git a/src/jobs/submitJob.tsx b/src/jobs/submitJob.tsx
new file mode 100644
index 00000000..729f46fa
--- /dev/null
+++ b/src/jobs/submitJob.tsx
@@ -0,0 +1,1111 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useEffect, useState } from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import LeftArrowIcon from '../../style/icons/left_arrow_icon.svg';
+import { Input, Select } from 'semantic-ui-react';
+import 'semantic-ui-css/semantic.min.css';
+import LabelProperties from './labelProperties';
+import { authApi } from '../utils/utils';
+import TagsInput from 'react-tagsinput';
+import 'react-tagsinput/react-tagsinput.css';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  ARCHIVEFILESMESSAGE,
+  ARGUMENTSMESSAGE,
+  BASE_URL,
+  FILESMESSAGE,
+  JARFILEMESSAGE,
+  MAINCLASSMESSAGE,
+  MAXRESTARTMESSAGE,
+  QUERYFILEMESSAGE,
+  RESTART_JOB_URL,
+  STATUS_RUNNING
+} from '../utils/const';
+import errorIcon from '../../style/icons/error_icon.svg';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+
+const iconLeftArrow = new LabIcon({
+  name: 'launcher:left-arrow-icon',
+  svgstr: LeftArrowIcon
+});
+const iconError = new LabIcon({
+  name: 'launcher:error-icon',
+  svgstr: errorIcon
+});
+
+function jobKey(selectedJobClone: any) {
+  const jobKeys: string[] = [];
+
+  for (const key in selectedJobClone) {
+    if (key.endsWith('Job')) {
+      jobKeys.push(key);
+    }
+  }
+  return jobKeys;
+}
+function jobTypeFunction(jobKey: string) {
+  let jobType = 'spark';
+  switch (jobKey) {
+    case 'sparkRJob':
+      jobType = 'sparkR';
+      return jobType;
+    case 'pysparkJob':
+      jobType = 'pySpark';
+      return jobType;
+    case 'sparkSqlJob':
+      jobType = 'sparkSql';
+      return jobType;
+    default:
+      return jobType;
+  }
+}
+function handleOptionalFields(selectedJobClone: any, jobTypeKey: any) {
+  let args: any[] = [];
+  let jarFileUris: any[] = [];
+  let archiveUris: any[] = [];
+  let fileUris: any[] = [];
+  let maxFailuresPerHour = '';
+  let pythonFileUris: any[] = [];
+  if (selectedJobClone[jobTypeKey].hasOwnProperty('fileUris')) {
+    fileUris = [selectedJobClone[jobTypeKey].fileUris];
+  }
+
+  if (selectedJobClone[jobTypeKey].hasOwnProperty('jarFileUris')) {
+    jarFileUris = [selectedJobClone[jobTypeKey].jarFileUris];
+  }
+
+  if (selectedJobClone[jobTypeKey].hasOwnProperty('args')) {
+    args = [selectedJobClone[jobTypeKey].args];
+  }
+
+  if (selectedJobClone[jobTypeKey].hasOwnProperty('archiveUris')) {
+    archiveUris = [selectedJobClone[jobTypeKey].archiveUris];
+  }
+
+  if (selectedJobClone[jobTypeKey].hasOwnProperty('pythonFileUris')) {
+    pythonFileUris = [selectedJobClone[jobTypeKey].pythonFileUris];
+  }
+
+  if (selectedJobClone.hasOwnProperty('scheduling')) {
+    maxFailuresPerHour = selectedJobClone.scheduling.maxFailuresPerHour;
+  }
+  return {
+    fileUris,
+    jarFileUris,
+    args,
+    archiveUris,
+    pythonFileUris,
+    maxFailuresPerHour
+  };
+}
+function SubmitJob(
+  this: any,
+  { setSubmitJobView, selectedJobClone, clusterResponse }: any
+) {
+  const [clusterList, setClusterList] = useState([{}]);
+  const [jobTypeList, setJobTypeList] = useState([{}]);
+  const [querySourceTypeList, setQuerySourceTypeList] = useState([{}]);
+  const [clusterSelected, setClusterSelected] = useState('');
+  const [jobIdSelected, setJobIdSelected] = useState('');
+  const [propertyDetail, setPropertyDetail] = useState(['']);
+  const [propertyDetailUpdated, setPropertyDetailUpdated] = useState(['']);
+  const [parameterDetail, setParameterDetail] = useState(['']);
+  const [parameterDetailUpdated, setParameterDetailUpdated] = useState(['']);
+  const [hexNumber, setHexNumber] = useState('');
+  const [submitDisabled, setSubmitDisabled] = useState(true);
+  let mainJarFileUri = ' ';
+  let args: any[] = [];
+  let jarFileUris: any[] = [];
+  let archiveUris: any[] = [];
+  let fileUris: any[] = [];
+  let pythonFileUris: any[] = [];
+  let maxFailuresPerHour = '';
+  let mainRFileUri = '';
+  let jobType = 'spark';
+  let mainPythonFileUri = '';
+  let queryFileUri = '';
+  let queryType = '';
+  let queryList = '';
+  let mainClass = ' ';
+  let key: any[] | (() => any[]) = [];
+  let value: any[] | (() => any[]) = [];
+  let jobKeys: string[] = [];
+  if (Object.keys(selectedJobClone).length !== 0) {
+    jobKeys = jobKey(selectedJobClone);
+    const jobTypeKey = jobKeys[0];
+    jobType = jobTypeFunction(jobKeys[0]);
+
+    if (selectedJobClone[jobTypeKey].hasOwnProperty('queryFileUri')) {
+      queryFileUri = selectedJobClone[jobTypeKey].queryFileUri;
+      queryType = 'queryFile';
+    }
+    if (selectedJobClone[jobTypeKey].hasOwnProperty('queryList')) {
+      queryList = selectedJobClone[jobTypeKey].queryList.queries[0];
+      queryType = 'queryText';
+    }
+    mainJarFileUri = selectedJobClone[jobKeys[0]].mainJarFileUri;
+    mainClass = selectedJobClone[jobKeys[0]].mainClass;
+    mainRFileUri = selectedJobClone[jobKeys[0]].mainRFileUri;
+    mainPythonFileUri = selectedJobClone[jobKeys[0]].mainPythonFileUri;
+    ({
+      fileUris,
+      jarFileUris,
+      args,
+      archiveUris,
+      pythonFileUris,
+      maxFailuresPerHour
+    } = handleOptionalFields(selectedJobClone, jobTypeKey));
+  }
+  const initialMainClassSelected =
+    mainJarFileUri && mainJarFileUri !== ' ' ? mainJarFileUri : mainClass;
+  const [mainClassSelected, setMainClassSelected] = useState(
+    initialMainClassSelected
+  );
+  const [mainRSelected, setMainRSelected] = useState(mainRFileUri);
+  const [mainPythonSelected, setMainPythonSelected] =
+    useState(mainPythonFileUri);
+  const [jarFileSelected, setJarFileSelected] = useState([...jarFileUris]);
+  const [fileSelected, setFileSelected] = useState([...fileUris]);
+  const [archieveFileSelected, setArchieveFileSelected] = useState([
+    ...archiveUris
+  ]);
+  const [argumentSelected, setArgumentSelected] = useState([...args]);
+  const [maxRestartSelected, setMaxRestartSelected] =
+    useState(maxFailuresPerHour);
+  const [jobTypeSelected, setJobTypeSelected] = useState(jobType);
+  const [additionalPythonFileSelected, setAdditionalPythonFileSelected] =
+    useState([...pythonFileUris]);
+  const [queryFileSelected, setQueryFileSelected] = useState(queryFileUri);
+  const [querySourceSelected, setQuerySourceSelected] = useState(queryType);
+  const [queryTextSelected, setQueryTextSelected] = useState(queryList);
+  const [labelDetail, setLabelDetail] = useState(key);
+  const [labelDetailUpdated, setLabelDetailUpdated] = useState(value);
+
+  const [additionalPythonFileValidation, setAdditionalPythonFileValidation] =
+    useState(true);
+  const [jarFileValidation, setJarFileValidation] = useState(true);
+  const [fileValidation, setFileValidation] = useState(true);
+  const [archieveFileValidation, setArchieveFileValidation] = useState(true);
+  const [mainPythonValidation, setMainPythonValidation] = useState(true);
+  const [queryFileValidation, setQueryFileValidation] = useState(true);
+  const [mainRValidation, setMainRValidation] = useState(true);
+  const [mainClassValidation, setMainClassValidation] = useState(true);
+  const [generationCompleted, setGenerationCompleted] = useState(false);
+  const [keyValidation, setKeyValidation] = useState(-1);
+  const [valueValidation, setvalueValidation] = useState(-1);
+  const [jobIdValidation, setjobIdValidation] = useState(true);
+  const [duplicateKeyError, setDuplicateKeyError] = useState(-1);
+
+  const handleCancelJobButton = () => {
+    setSubmitJobView(false);
+  };
+  const handleSubmitJobView = () => {
+    if (!submitDisabled) {
+      submitJob();
+      setSubmitJobView(false);
+    }
+  };
+
+  const handleSubmitJobBackView = () => {
+    setSubmitJobView(false);
+  };
+
+  const handleClusterSelected = (event: any, data: any) => {
+    setClusterSelected(data.value);
+  };
+
+  const handleJobTypeSelected = (event: any, data: any) => {
+    setJobTypeSelected(data.value);
+    setFileSelected([]);
+    setJarFileSelected([]);
+    setAdditionalPythonFileSelected([]);
+    setArchieveFileSelected([]);
+    setArgumentSelected([]);
+    setMainPythonSelected('');
+    setMainRSelected('');
+    setQueryTextSelected('');
+    setQueryFileSelected('');
+    setMainClassSelected('');
+  };
+  const handleQuerySourceTypeSelected = (event: any, data: any) => {
+    setQuerySourceSelected(data.value);
+  };
+
+  useEffect(() => {
+    let transformClusterListData = [];
+    transformClusterListData = clusterResponse.clusters.filter((data: any) => {
+      if (data.status.state === STATUS_RUNNING) {
+        return {
+          clusterName: data.clusterName
+        };
+      }
+    });
+
+    const keyLabelStructure = transformClusterListData.map((obj: any) => ({
+      key: obj.clusterName,
+      value: obj.clusterName,
+      text: obj.clusterName
+    }));
+
+    setClusterList(keyLabelStructure);
+    const jobTypeData = [
+      { key: 'spark', value: 'spark', text: 'Spark' },
+      { key: 'sparkR', value: 'sparkR', text: 'SparkR' },
+      { key: 'sparkSql', value: 'sparkSql', text: 'SparkSql' },
+      { key: 'pySpark', value: 'pySpark', text: 'PySpark' }
+    ];
+    const querySourceData = [
+      { key: 'queryFile', value: 'queryFile', text: 'Query file' },
+      { key: 'queryText', value: 'queryText', text: 'Query text' }
+    ];
+    setJobTypeList(jobTypeData);
+    setQuerySourceTypeList(querySourceData);
+  }, []);
+  useEffect(() => {
+    submitButtonDisable();
+    generateRandomHex();
+  }, [
+    clusterSelected,
+    jobIdSelected,
+    mainClassSelected,
+    mainRSelected,
+    mainPythonSelected,
+    queryFileSelected,
+    queryTextSelected,
+    mainClassValidation,
+    jarFileValidation,
+    archieveFileValidation,
+    mainRValidation,
+    fileValidation,
+    mainPythonValidation,
+    queryFileValidation,
+    keyValidation,
+    valueValidation,
+    jobIdValidation,
+    duplicateKeyError
+  ]);
+  const submitButtonDisable = () => {
+    const isSparkJob = jobTypeSelected === 'spark';
+    const isSparkRJob = jobTypeSelected === 'sparkR';
+    const isPySparkJob = jobTypeSelected === 'pySpark';
+    const isSparkSqlJob = jobTypeSelected === 'sparkSql';
+    if (
+      clusterSelected !== '' &&
+      jobIdSelected !== '' &&
+      ((isSparkJob &&
+        mainClassSelected.trim().length !== 0 &&
+        // mainClassValidation &&
+        jarFileValidation &&
+        fileValidation &&
+        archieveFileValidation &&
+        keyValidation === -1 &&
+        valueValidation === -1 &&
+        jobIdValidation &&
+        duplicateKeyError === -1) ||
+        (isSparkRJob &&
+          mainRSelected !== '' &&
+          mainRValidation &&
+          fileValidation &&
+          keyValidation === -1 &&
+          valueValidation === -1 &&
+          jobIdValidation &&
+          duplicateKeyError === -1) ||
+        (isPySparkJob &&
+          mainPythonSelected !== '' &&
+          mainPythonValidation &&
+          additionalPythonFileValidation &&
+          jarFileValidation &&
+          fileValidation &&
+          archieveFileValidation &&
+          keyValidation === -1 &&
+          valueValidation === -1 &&
+          jobIdValidation &&
+          duplicateKeyError === -1) ||
+        (isSparkSqlJob &&
+          queryFileSelected !== '' &&
+          querySourceSelected === 'queryFile' &&
+          queryFileValidation &&
+          jarFileValidation &&
+          keyValidation === -1 &&
+          valueValidation === -1 &&
+          jobIdValidation &&
+          duplicateKeyError === -1) ||
+        (isSparkSqlJob &&
+          queryTextSelected !== '' &&
+          querySourceSelected === 'queryText' &&
+          jarFileValidation &&
+          keyValidation === -1 &&
+          valueValidation === -1 &&
+          jobIdValidation &&
+          duplicateKeyError === -1))
+    ) {
+      setSubmitDisabled(false);
+    } else {
+      setSubmitDisabled(true);
+    }
+  };
+
+  useEffect(() => {
+    let jobKeys: string[] = [];
+    if (Object.keys(selectedJobClone).length !== 0) {
+      if (selectedJobClone.hasOwnProperty('labels')) {
+        const updatedLabelDetail = Object.entries(selectedJobClone.labels).map(
+          ([k, v]) => `${k}:${v}`
+        );
+        setLabelDetail(prevLabelDetail => [
+          ...prevLabelDetail,
+          ...updatedLabelDetail
+        ]);
+        setLabelDetailUpdated(prevLabelDetailUpdated => [
+          ...prevLabelDetailUpdated,
+          ...updatedLabelDetail
+        ]);
+        for (const key in selectedJobClone) {
+          if (key.endsWith('Job')) {
+            jobKeys.push(key);
+          }
+        }
+        jobKeys = jobKey(selectedJobClone);
+        if (selectedJobClone[jobKeys[0]].hasOwnProperty('properties')) {
+          const updatedPropertyDetail = Object.entries(
+            selectedJobClone[jobKeys[0]].properties
+          ).map(([k, v]) => `${k}:${v}`);
+          setPropertyDetail(prevPropertyDetail => [
+            ...prevPropertyDetail,
+            ...updatedPropertyDetail
+          ]);
+          setPropertyDetailUpdated(prevPropertyDetailUpdated => [
+            ...prevPropertyDetailUpdated,
+            ...updatedPropertyDetail
+          ]);
+        }
+      }
+    }
+  }, []);
+
+  const generateRandomHex = () => {
+    if (!generationCompleted) {
+      const crypto = window.crypto || window.Crypto;
+      const array = new Uint32Array(1);
+      crypto.getRandomValues(array);
+      const hex = array[0].toString(16);
+      const paddedHex = hex.padStart(8, '0');
+      setHexNumber('job-' + paddedHex);
+      setJobIdSelected('job-' + paddedHex);
+      setGenerationCompleted(true);
+    }
+  };
+
+  const createPySparkPayload = (
+    mainPythonSelected: any,
+    propertyObject: any,
+    jarFileSelected: any,
+    fileSelected: any,
+    archieveFileSelected: any,
+    argumentSelected: any,
+    additionalPythonFileSelected: any
+  ) => {
+    return {
+      pysparkJob: {
+        mainPythonFileUri: mainPythonSelected,
+        ...(propertyObject && {
+          properties: propertyObject
+        }),
+        ...(jarFileSelected !== '' && {
+          jarFileUris: jarFileSelected
+        }),
+        ...(fileSelected !== '' && {
+          fileUris: fileSelected
+        }),
+        ...(archieveFileSelected !== '' && {
+          archiveUris: archieveFileSelected
+        }),
+        ...(argumentSelected !== '' && {
+          args: [argumentSelected]
+        }),
+        ...(additionalPythonFileSelected !== '' && {
+          pythonFileUris: [additionalPythonFileSelected]
+        })
+      }
+    };
+  };
+
+  const createSparkPayload = (
+    mainClassSelected: any,
+    propertyObject: any,
+    archieveFileSelected: any,
+    fileSelected: any,
+    jarFileSelected: any,
+    argumentSelected: any
+  ) => {
+    return {
+      sparkJob: {
+        mainJarFileUri: mainClassSelected,
+        ...(propertyObject && {
+          properties: propertyObject
+        }),
+        ...(archieveFileSelected !== '' && {
+          archiveUris: archieveFileSelected
+        }),
+        ...(fileSelected !== '' && {
+          fileUris: [fileSelected]
+        }),
+        ...(jarFileSelected !== '' && {
+          jarFileUris: jarFileSelected
+        }),
+        ...(argumentSelected !== '' && {
+          args: [argumentSelected]
+        })
+      }
+    };
+  };
+
+  const createSparkRPayload = (
+    mainRSelected: any,
+    propertyObject: any,
+    fileSelected: any,
+    argumentSelected: any
+  ) => {
+    return {
+      sparkRJob: {
+        mainRFileUri: mainRSelected,
+        ...(propertyObject && {
+          properties: propertyObject
+        }),
+        ...(fileSelected !== '' && {
+          fileUris: [fileSelected]
+        }),
+        ...(argumentSelected !== '' && {
+          args: [argumentSelected]
+        })
+      }
+    };
+  };
+
+  const createSparkSqlPayload = (
+    propertyObject: any,
+    jarFileSelected: any,
+    querySourceSelected: string,
+    queryFileSelected: any,
+    queryTextSelected: any
+  ) => {
+    return {
+      sparkSqlJob: {
+        ...(propertyObject && {
+          properties: propertyObject
+        }),
+        ...(jarFileSelected !== '' && {
+          jarFileUris: [jarFileSelected]
+        }),
+        scriptVariables: {},
+        ...(querySourceSelected === 'queryFile' && {
+          queryFileUri: queryFileSelected
+        }),
+        ...(querySourceSelected === 'queryText' && {
+          queryList: { queries: [queryTextSelected] }
+        })
+      }
+    };
+  };
+
+  const submitJob = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      const labelObject: { [key: string]: string } = {};
+      labelDetailUpdated.forEach((label: string) => {
+        const key = label.split(':')[0];
+        const value = label.split(':')[1];
+        labelObject[key] = value;
+      });
+      const propertyObject: { [key: string]: string } = {};
+      propertyDetailUpdated.forEach((label: string) => {
+        const key = label.split(':')[0];
+        const value = label.split(':')[1];
+        propertyObject[key] = value;
+      });
+      const parameterObject: { [key: string]: string } = {};
+      parameterDetailUpdated.forEach((label: string) => {
+        const key = label.split(':')[0];
+        const value = label.split(':')[1];
+        parameterObject[key] = value;
+      });
+      const payload = {
+        projectId: credentials.project_id,
+        region: credentials.region_id,
+        job: {
+          placement: { clusterName: clusterSelected },
+          statusHistory: [],
+          reference: { jobId: jobIdSelected, projectId: '' },
+          ...(maxRestartSelected !== '' && {
+            scheduling: { maxFailuresPerHour: maxRestartSelected }
+          }),
+          ...(labelObject && {
+            labels: labelObject
+          }),
+          ...(jobTypeSelected === 'pySpark' &&
+            createPySparkPayload(
+              mainPythonSelected,
+              propertyObject,
+              jarFileSelected,
+              fileSelected,
+              archieveFileSelected,
+              argumentSelected,
+              additionalPythonFileSelected
+            )),
+          ...(jobTypeSelected === 'spark' &&
+            createSparkPayload(
+              mainClassSelected,
+              propertyObject,
+              archieveFileSelected,
+              fileSelected,
+              jarFileSelected,
+              argumentSelected
+            )),
+          ...(jobTypeSelected === 'sparkR' &&
+            createSparkRPayload(
+              mainRSelected,
+              propertyObject,
+              fileSelected,
+              argumentSelected
+            )),
+          ...(jobTypeSelected === 'sparkSql' &&
+            createSparkSqlPayload(
+              propertyObject,
+              jarFileSelected,
+              querySourceSelected,
+              queryFileSelected,
+              queryTextSelected
+            ))
+        }
+      };
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/jobs:submit`,
+        {
+          method: 'POST',
+          body: JSON.stringify(payload),
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: any) => {
+          if (response.status === 200) {
+            response
+              .json()
+              .then((responseResult: any) => {
+                console.log(responseResult);
+              })
+              .catch((e: any) => {
+                console.log(e);
+              });
+          } else {
+            throw new Error(`API failed with status: ${response.status}`);
+          }
+        })
+        .catch((err: any) => {
+          console.error('Error submitting job', err);
+          toast.error('Failed to submit the job');
+        });
+    }
+  };
+  const handleInputChange = (event: any) => {
+    event.target.value.length > 0
+      ? setjobIdValidation(true)
+      : setjobIdValidation(false);
+    setHexNumber(event.target.value);
+    const newJobId = event.target.value;
+    setJobIdSelected(newJobId);
+  };
+
+  const handleValidationFiles = (
+    listOfFiles: any,
+    setValuesPart: any,
+    setValidationPart: any
+  ) => {
+    if (typeof listOfFiles === 'string') {
+      if (
+        listOfFiles.startsWith('file://') ||
+        listOfFiles.startsWith('gs://') ||
+        listOfFiles.startsWith('hdfs://')
+      ) {
+        setValidationPart(true);
+      } else {
+        setValidationPart(false);
+      }
+      setValuesPart(listOfFiles);
+    } else {
+      if (listOfFiles.length === 0) {
+        setValidationPart(true);
+      } else {
+        listOfFiles.forEach((fileName: any) => {
+          if (
+            fileName.startsWith('file://') ||
+            fileName.startsWith('gs://') ||
+            fileName.startsWith('hdfs://')
+          ) {
+            setValidationPart(true);
+          } else {
+            setValidationPart(false);
+          }
+        });
+      }
+      setValuesPart(listOfFiles);
+    }
+  };
+
+  return (
+    <div>
+      <div className="scroll-comp">
+        <div className="cluster-details-header">
+          <div
+            className="back-arrow-icon"
+            onClick={() => handleSubmitJobBackView()}
+          >
+            <iconLeftArrow.react tag="div" />
+          </div>
+          <div className="cluster-details-title">Submit a job</div>
+        </div>
+        <div className="submit-job-container">
+          <div className="submit-job-label-header">Cluster</div>
+          <div className="submit-job-cluster-message">
+            Choose a cluster to run your job in.
+          </div>
+          {clusterList.length === 0 ? (
+            <Input
+              className="select-job-style"
+              value="No clusters running"
+              readOnly
+            />
+          ) : (
+            <Select
+              placeholder="Cluster*"
+              onChange={handleClusterSelected}
+              className="select-job-style"
+              options={clusterList}
+              value={clusterSelected}
+            />
+          )}
+          <div className="submit-job-label-header">Job</div>
+          <div className="submit-job-cluster-message">Job ID*</div>
+          <Input
+            className="select-job-style"
+            onChange={e => handleInputChange(e)}
+            type="text"
+            value={hexNumber}
+          />
+          {!jobIdValidation && (
+            <div className="error-key-parent">
+              <iconError.react tag="div" />
+              <div className="error-key-missing">ID is required</div>
+            </div>
+          )}
+          <div className="submit-job-cluster-message">Job type*</div>
+
+          <Select
+            onChange={handleJobTypeSelected}
+            className="select-job-style"
+            options={jobTypeList}
+            value={jobTypeSelected}
+          />
+
+          {jobTypeSelected === 'sparkSql' && (
+            <>
+              <div className="submit-job-cluster-message">
+                Query source type*
+              </div>
+              <Select
+                // placeholder="Query source type*"
+                onChange={handleQuerySourceTypeSelected}
+                className="select-job-style"
+                options={querySourceTypeList}
+                value={querySourceSelected}
+              />
+            </>
+          )}
+          {querySourceSelected === 'queryFile' &&
+            jobTypeSelected === 'sparkSql' && (
+              <>
+                <div className="submit-job-cluster-message">Query file*</div>
+                <Input
+                  //placeholder="Query file*"
+                  className="select-job-style"
+                  onChange={e =>
+                    handleValidationFiles(
+                      e.target.value,
+                      setQueryFileSelected,
+                      setQueryFileValidation
+                    )
+                  }
+                  addOnBlur={true}
+                  value={queryFileSelected}
+                />
+                {!queryFileValidation && (
+                  <div className="error-key-parent">
+                    <iconError.react tag="div" />
+                    <div className="error-key-missing">
+                      File must include a valid scheme prefix: 'file://',
+                      'gs://', or 'hdfs://'
+                    </div>
+                  </div>
+                )}
+                {queryFileValidation && (
+                  <div className="submit-job-message">{QUERYFILEMESSAGE}</div>
+                )}
+              </>
+            )}
+          {querySourceSelected === 'queryText' &&
+            jobTypeSelected === 'sparkSql' && (
+              <>
+                <div className="submit-job-cluster-message">Query text*</div>
+                <Input
+                  //placeholder="Query text*"
+                  className="select-job-style"
+                  onChange={e => setQueryTextSelected(e.target.value)}
+                  value={queryTextSelected}
+                />
+                <div className="submit-job-message">The query to execute</div>
+              </>
+            )}
+          {jobTypeSelected === 'spark' && (
+            <>
+              <div className="submit-job-cluster-message">
+                Main class or jar*
+              </div>
+              <Input
+                className="select-job-style"
+                onChange={e =>
+                  handleValidationFiles(
+                    e.target.value,
+                    setMainClassSelected,
+                    setMainClassValidation
+                  )
+                }
+                addOnBlur={true}
+                value={mainClassSelected}
+              />
+              {mainClassSelected === '' && (
+                <div className="error-key-parent">
+                  <iconError.react tag="div" />
+                  <div className="error-key-missing">
+                    Main class or jar is required
+                  </div>
+                </div>
+              )}
+              {mainClassSelected !== '' && (
+                <div className="submit-job-message">{MAINCLASSMESSAGE}</div>
+              )}
+            </>
+          )}
+          {jobTypeSelected === 'sparkR' && (
+            <>
+              <div className="submit-job-cluster-message">Main R file*</div>
+              <Input
+                //placeholder="Main R file*"
+                className="select-job-style"
+                onChange={e =>
+                  handleValidationFiles(
+                    e.target.value,
+                    setMainRSelected,
+                    setMainRValidation
+                  )
+                }
+                addOnBlur={true}
+                value={mainRSelected}
+              />
+              {!mainRValidation && (
+                <div className="error-key-parent">
+                  <iconError.react tag="div" />
+                  <div className="error-key-missing">
+                    File must include a valid scheme prefix: 'file://', 'gs://',
+                    or 'hdfs://'
+                  </div>
+                </div>
+              )}
+              {mainRValidation && (
+                <div className="submit-job-message">{QUERYFILEMESSAGE}</div>
+              )}
+            </>
+          )}
+          {jobTypeSelected === 'pySpark' && (
+            <>
+              <div className="submit-job-cluster-message">
+                Main Python file*
+              </div>
+              <Input
+                //placeholder="Main Python file*"
+                className="select-job-style"
+                onChange={e =>
+                  handleValidationFiles(
+                    e.target.value,
+                    setMainPythonSelected,
+                    setMainPythonValidation
+                  )
+                }
+                addOnBlur={true}
+                value={mainPythonSelected}
+              />
+              {!mainPythonValidation && (
+                <div className="error-key-parent">
+                  <iconError.react tag="div" />
+                  <div className="error-key-missing">
+                    File must include a valid scheme prefix: 'file://', 'gs://',
+                    or 'hdfs://'
+                  </div>
+                </div>
+              )}
+              {mainPythonValidation && (
+                <div className="submit-job-message">{QUERYFILEMESSAGE}</div>
+              )}
+            </>
+          )}
+          {jobTypeSelected === 'pySpark' && (
+            <>
+              <div className="submit-job-cluster-message">
+                Additional python files
+              </div>
+              <TagsInput
+                className="select-job-style"
+                onChange={e =>
+                  handleValidationFiles(
+                    e,
+                    setAdditionalPythonFileSelected,
+                    setAdditionalPythonFileValidation
+                  )
+                }
+                addOnBlur={true}
+                value={additionalPythonFileSelected}
+                inputProps={{ placeholder: '' }}
+              />
+              {!additionalPythonFileValidation && (
+                <div className="error-key-parent">
+                  <iconError.react tag="div" />
+                  <div className="error-key-missing">
+                    All files must include a valid scheme prefix: 'file://',
+                    'gs://', or 'hdfs://'
+                  </div>
+                </div>
+              )}
+            </>
+          )}
+          {jobTypeSelected !== 'sparkR' && (
+            <>
+              <div className="submit-job-cluster-message">Jar files</div>
+              <TagsInput
+                className="select-job-style"
+                onChange={e =>
+                  handleValidationFiles(
+                    e,
+                    setJarFileSelected,
+                    setJarFileValidation
+                  )
+                }
+                addOnBlur={true}
+                value={jarFileSelected}
+                inputProps={{ placeholder: '' }}
+              />
+              {!jarFileValidation && (
+                <div className="error-key-parent">
+                  <iconError.react tag="div" />
+                  <div className="error-key-missing">
+                    All files must include a valid scheme prefix: 'file://',
+                    'gs://', or 'hdfs://'
+                  </div>
+                </div>
+              )}
+              {jarFileValidation && (
+                <div className="submit-job-message">{JARFILEMESSAGE}</div>
+              )}
+            </>
+          )}
+          {jobTypeSelected !== 'sparkSql' && (
+            <>
+              <div className="submit-job-cluster-message">Files</div>
+              <TagsInput
+                className="select-job-style"
+                onChange={e =>
+                  handleValidationFiles(e, setFileSelected, setFileValidation)
+                }
+                addOnBlur={true}
+                value={fileSelected}
+                inputProps={{ placeholder: '' }}
+              />
+              {!fileValidation && (
+                <div className="error-key-parent">
+                  <iconError.react tag="div" />
+                  <div className="error-key-missing">
+                    All files must include a valid scheme prefix: 'file://',
+                    'gs://', or 'hdfs://'
+                  </div>
+                </div>
+              )}
+              {fileValidation && (
+                <div className="submit-job-message">{FILESMESSAGE}</div>
+              )}
+            </>
+          )}
+          {(jobTypeSelected === 'spark' || jobTypeSelected === 'pySpark') && (
+            <>
+              <div className="submit-job-cluster-message">Archive files</div>
+              <TagsInput
+                className="select-job-style"
+                onChange={e =>
+                  handleValidationFiles(
+                    e,
+                    setArchieveFileSelected,
+                    setArchieveFileValidation
+                  )
+                }
+                addOnBlur={true}
+                value={archieveFileSelected}
+                inputProps={{ placeholder: '' }}
+              />
+              {!archieveFileValidation && (
+                <div className="error-key-parent">
+                  <iconError.react tag="div" />
+                  <div className="error-key-missing">
+                    All files must include a valid scheme prefix: 'file://',
+                    'gs://', or 'hdfs://'
+                  </div>
+                </div>
+              )}
+              {archieveFileValidation && (
+                <div className="submit-job-message">{ARCHIVEFILESMESSAGE}</div>
+              )}
+            </>
+          )}
+          {jobTypeSelected !== 'sparkSql' && (
+            <>
+              <div className="submit-job-cluster-message">Arguments</div>
+              <TagsInput
+                className="select-job-style"
+                onChange={e => setArgumentSelected(e)}
+                value={argumentSelected}
+                inputProps={{ placeholder: '' }}
+              />
+              <div className="submit-job-message">{ARGUMENTSMESSAGE}</div>
+            </>
+          )}
+          {querySourceSelected === 'queryFile' &&
+            jobTypeSelected === 'sparkSql' && (
+              <>
+                <div className="submit-job-label-header">Query parameters</div>
+                <LabelProperties
+                  labelDetail={parameterDetail}
+                  setLabelDetail={setParameterDetail}
+                  labelDetailUpdated={parameterDetailUpdated}
+                  setLabelDetailUpdated={setParameterDetailUpdated}
+                  selectedJobClone={selectedJobClone ? true : false}
+                  buttonText="ADD PARAMETER"
+                  keyValidation={keyValidation}
+                  setKeyValidation={setKeyValidation}
+                  valueValidation={valueValidation}
+                  setvalueValidation={setvalueValidation}
+                  duplicateKeyError={duplicateKeyError}
+                  setDuplicateKeyError={setDuplicateKeyError}
+                />
+              </>
+            )}
+          <div className="submit-job-cluster-message">
+            Max restarts per hour
+          </div>
+          <Input
+            //placeholder="Max restarts per hour"
+            className="select-job-style"
+            onChange={e => setMaxRestartSelected(e.target.value)}
+            value={maxRestartSelected}
+          />
+          <div className="submit-job-message-with-link">
+            {MAXRESTARTMESSAGE}
+            <div
+              className="submit-job-learn-more"
+              onClick={() => {
+                window.open(`${RESTART_JOB_URL}`, '_blank');
+              }}
+            >
+              Learn more
+            </div>
+          </div>
+
+          <div className="submit-job-label-header">Properties</div>
+          <LabelProperties
+            labelDetail={propertyDetail}
+            setLabelDetail={setPropertyDetail}
+            labelDetailUpdated={propertyDetailUpdated}
+            setLabelDetailUpdated={setPropertyDetailUpdated}
+            selectedJobClone={selectedJobClone ? true : false}
+            buttonText="ADD PROPERTY"
+            keyValidation={keyValidation}
+            setKeyValidation={setKeyValidation}
+            valueValidation={valueValidation}
+            setvalueValidation={setvalueValidation}
+            duplicateKeyError={duplicateKeyError}
+            setDuplicateKeyError={setDuplicateKeyError}
+          />
+          <div className="submit-job-label-header">Labels</div>
+          <LabelProperties
+            labelDetail={labelDetail}
+            setLabelDetail={setLabelDetail}
+            labelDetailUpdated={labelDetailUpdated}
+            setLabelDetailUpdated={setLabelDetailUpdated}
+            selectedJobClone={selectedJobClone ? true : false}
+            buttonText="ADD LABEL"
+            keyValidation={keyValidation}
+            setKeyValidation={setKeyValidation}
+            valueValidation={valueValidation}
+            setvalueValidation={setvalueValidation}
+            duplicateKeyError={duplicateKeyError}
+            setDuplicateKeyError={setDuplicateKeyError}
+          />
+          <div className="job-button-style-parent">
+            <div
+              className={
+                submitDisabled
+                  ? 'submit-button-disable-style'
+                  : 'submit-button-style'
+              }
+            >
+              <div
+                onClick={() => {
+                  handleSubmitJobView();
+                }}
+              >
+                SUBMIT
+              </div>
+            </div>
+            <div className="job-cancel-button-style">
+              <div
+                onClick={() => {
+                  handleCancelJobButton();
+                }}
+              >
+                CANCEL
+              </div>
+            </div>
+          </div>
+          <ToastContainer />
+        </div>
+      </div>
+    </div>
+  );
+}
+
+export default SubmitJob;
diff --git a/src/login/authLogin.tsx b/src/login/authLogin.tsx
new file mode 100644
index 00000000..845f5bed
--- /dev/null
+++ b/src/login/authLogin.tsx
@@ -0,0 +1,122 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { ReactWidget } from '@jupyterlab/apputils';
+import React, { useEffect, useState } from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import signinGoogleIcon from '../../style/icons/signin_google_icon.svg';
+import { requestAPI } from '../handler/handler';
+import ConfigSelection from './configSelection';
+import { LOGIN_STATE, STATUS_SUCCESS } from '../utils/const';
+import { checkConfig } from '../utils/utils';
+import { ClipLoader } from 'react-spinners';
+
+// Create the LabIcon instance outside of the component
+const IconsigninGoogle = new LabIcon({
+  name: 'launcher:signin_google_icon',
+  svgstr: signinGoogleIcon
+});
+
+const AuthLoginComponent = (): React.JSX.Element => {
+  const [loginState, setLoginState] = useState(false);
+  const [isloginDisabled, setIsloginDisabled] = useState(false);
+  const [configError, setConfigError] = useState(false);
+  const [loginError, setLoginError] = useState(false);
+  const [configLoading, setConfigLoading] = useState(true);
+
+  const login = async () => {
+    setIsloginDisabled(true);
+    const data = await requestAPI('login');
+    if (typeof data === 'object' && data !== null) {
+      const loginStatus = (data as { login: string }).login;
+      if (loginStatus === STATUS_SUCCESS) {
+        setLoginState(true);
+        setLoginError(false);
+        localStorage.setItem('loginState', LOGIN_STATE);
+      } else {
+        setLoginState(false);
+        localStorage.clear();
+      }
+    }
+  };
+
+  useEffect(() => {
+    checkConfig(setLoginState, setConfigError, setLoginError);
+    const localstorageGetInformation = localStorage.getItem('loginState');
+    setLoginState(localstorageGetInformation === LOGIN_STATE);
+    if (loginState) {
+      setConfigLoading(false);
+    }
+  }, []);
+
+  return (
+    <div>
+      {configLoading && !loginState && !configError && !loginError && (
+        <div className="spin-loaderMain">
+          <ClipLoader
+            color="#8A8A8A"
+            loading={true}
+            size={18}
+            aria-label="Loading Spinner"
+            data-testid="loader"
+          />
+          Loading Config Setup
+        </div>
+      )}
+      {!loginError && loginState && (
+        <ConfigSelection
+          loginState={loginState}
+          configError={configError}
+          setConfigError={setConfigError}
+        />
+      )}
+      {loginError && (
+        <>
+          <div className="login-error">Please login to continue</div>
+          <div style={{ alignItems: 'center' }}>
+            <div
+              className={
+                isloginDisabled
+                  ? 'signin-google-icon disabled'
+                  : 'signin-google-icon'
+              }
+              onClick={isloginDisabled ? undefined : login}
+            >
+              <IconsigninGoogle.react tag="div" />
+            </div>
+          </div>
+        </>
+      )}
+      {configError && (
+        <div className="login-error">
+          Please Configure Gcloud with Account, Project ID and Region
+        </div>
+      )}
+    </div>
+  );
+};
+
+export class AuthLogin extends ReactWidget {
+  constructor() {
+    super();
+    this.addClass('jp-ReactWidget');
+  }
+
+  render(): React.JSX.Element {
+    return <AuthLoginComponent />;
+  }
+}
diff --git a/src/login/configSelection.tsx b/src/login/configSelection.tsx
new file mode 100644
index 00000000..c871ad88
--- /dev/null
+++ b/src/login/configSelection.tsx
@@ -0,0 +1,392 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useState, useEffect } from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import settingsIcon from '../../style/icons/settings_icon.svg';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  PROJECT_LIST_URL,
+  REGION_URL,
+  USER_INFO_URL
+} from '../utils/const';
+import { authApi } from '../utils/utils';
+import { Select } from 'semantic-ui-react';
+import 'semantic-ui-css/semantic.min.css';
+import { requestAPI } from '../handler/handler';
+import ClipLoader from 'react-spinners/ClipLoader';
+import { ToastContainer, toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+
+function ConfigSelection({ loginState, configError, setConfigError }: any) {
+  const Iconsettings = new LabIcon({
+    name: 'launcher:settings_icon',
+    svgstr: settingsIcon
+  });
+
+  const [projectId, setProjectId] = useState('');
+  const [region, setRegion] = useState('');
+  const [projectList, setProjectList] = useState([{}]);
+  const [regionList, setRegionList] = useState([{}]);
+  const [isLoading, setIsLoading] = useState(false);
+  const [isLoadingUser, setIsLoadingUser] = useState(true);
+  const [isLoadingProject, setIsLoadingProject] = useState(true);
+  const [isLoadingRegion, setIsLoadingRegion] = useState(true);
+  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+  const [regionEmpty, SetRegionEmpty] = useState(true);
+  const [isSaveDisabled, setIsSaveDisabled] = useState(true);
+  const [userInfo, setUserInfo] = useState({
+    email: '',
+    picture: ''
+  });
+
+  const handleProjectIdChange = (event: any, data: any) => {
+    setRegionList([]);
+    SetRegionEmpty(true);
+    if (data.value === undefined) {
+      regionListAPI(data);
+    } else {
+      regionListAPI(data.value);
+      setProjectId(data.value);
+    }
+    if (projectId.length !== 0) {
+      setIsSaveDisabled(false);
+    }
+  };
+
+  const handleRegionChange = (event: any, data: any) => {
+    setRegion(data.value);
+    if (projectId) {
+      setIsSaveDisabled(false);
+    }
+    setIsDropdownOpen(false);
+  };
+
+  const handleSave = async () => {
+    setIsLoading(true);
+    setIsSaveDisabled(true);
+    const dataToSend = { projectId: projectId, region: region };
+    try {
+      const data = await requestAPI<any>('configuration', {
+        body: JSON.stringify(dataToSend),
+        method: 'POST'
+      });
+      if (typeof data === 'object' && data !== null) {
+        const configStatus = (data as { config: string }).config;
+        setIsLoading(false);
+        if (configStatus && !toast.isActive('custom-toast')) {
+          if(configStatus.includes('Failed')) {
+            toast.error(configStatus, {
+              hideProgressBar: true,
+              autoClose: false,
+              theme: "dark",
+              position: toast.POSITION.BOTTOM_CENTER,
+              toastId: 'custom-toast'
+            });
+          } else {
+            toast.success(configStatus, {
+              hideProgressBar: true,
+              autoClose: false,
+              theme: "dark",
+              position: toast.POSITION.BOTTOM_CENTER,
+              toastId: 'custom-toast'
+            });
+          }
+        }
+      }
+    } catch (reason) {
+      console.error(`Error on POST {dataToSend}.\n${reason}`);
+    }
+  };
+
+  const displayUserInfo = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(USER_INFO_URL, {
+        method: 'GET',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      })
+        .then((response: any) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              setUserInfo(responseResult);
+              setIsLoadingUser(false);
+            })
+            .catch((e: any) => console.log(e));
+        })
+        .catch((err: any) => {
+          setIsLoadingUser(false);
+          console.error('Error displaying user info', err);
+          toast.error('Failed to fetch User Information ');
+        });
+    }
+  };
+
+  const projectListAPI = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(PROJECT_LIST_URL, {
+        method: 'GET',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      })
+        .then((response: any) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              let transformedProjectList = [];
+              transformedProjectList = responseResult.projects.map(
+                (data: any) => {
+                  return {
+                    value: data.projectId,
+                    key: data.projectId,
+                    text: data.projectId
+                  };
+                }
+              );
+              setProjectList(transformedProjectList);
+              setIsLoadingProject(false);
+            })
+            .catch((e: any) => console.log(e));
+        })
+        .catch((err: any) => {
+          setIsLoadingProject(false);
+          console.error('Error fetching project list', err);
+          toast.error('Failed to fetch the project list ');
+        });
+    }
+  };
+  const regionListAPI = async (projectId: any) => {
+    try {
+      const credentials = await authApi();
+      if (credentials) {
+        fetch(`${REGION_URL}${projectId}/regions`, {
+          method: 'GET',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        })
+          .then((response: any) => {
+            if (response.ok) {
+              SetRegionEmpty(true);
+              return response.json();
+            } else {
+              setIsDropdownOpen(false);
+              setIsSaveDisabled(true);
+              SetRegionEmpty(false);
+              if (!toast.isActive('custom-toast-error')) {
+                toast.error(response.status + ' Permission Denied', {
+                  position: toast.POSITION.BOTTOM_CENTER,
+                  toastId: 'custom-toast-error'
+                });
+              }
+              throw new Error(`Request failed with status ${response.status}`);
+            }
+          })
+          .then((responseResult: any) => {
+            let transformedRegionList = [];
+            transformedRegionList = responseResult.items.map((data: any) => {
+              return {
+                value: data.name,
+                key: data.name,
+                text: data.name
+              };
+            });
+            setRegionList(transformedRegionList);
+            setIsDropdownOpen(false);
+            SetRegionEmpty(false);
+            setIsLoadingRegion(false);
+          })
+          .catch((error: any) => {
+            setIsLoadingRegion(false);
+            console.error('Error fetching region list:', error.message);
+            // Handle the error here (e.g., show an error message to the user)
+          });
+      }
+    } catch (error) {
+      setIsLoadingRegion(false);
+      console.error('Error fetching region list:');
+      toast.error('Failed to fetch the region list ');
+      // Handle the error here (e.g., show an error message to the user)
+    }
+  };
+  const handleDropdownOpen = () => {
+    if (regionEmpty) {
+      setIsDropdownOpen(true);
+    }
+  };
+
+  const fetchProjectRegion = async () => {
+    const credentials = await authApi();
+    if (credentials && credentials.project_id && credentials.region_id) {
+      handleProjectIdChange(Event, credentials.project_id);
+      setProjectId(credentials.project_id);
+      setRegion(credentials.region_id);
+      setConfigError(false);
+    } else if (credentials && credentials.config_error === 1) {
+      setConfigError(true);
+    }
+  };
+  useEffect(() => {
+    if (loginState) {
+      fetchProjectRegion();
+      projectListAPI();
+      displayUserInfo();
+    } else {
+      projectListAPI();
+      displayUserInfo();
+    }
+  }, []);
+  return (
+    <div className="Settings-component">
+      <ToastContainer />
+      {isLoadingUser && isLoadingProject && isLoadingRegion && !configError ? (
+        <div className="spin-loaderMain">
+          <ClipLoader
+            color="#8A8A8A"
+            loading={true}
+            size={20}
+            aria-label="Loading Spinner"
+            data-testid="loader"
+          />
+          Loading Config Setup
+        </div>
+      ) : (
+        !configError && (
+          <div>
+            <div className="settings-overlay">
+              <div className="settings-icon">
+                <Iconsettings.react tag="div" />
+              </div>
+              <div className="settings-text">Settings</div>
+            </div>
+            <div className="settings-seperator"></div>
+            <div className="config-overlay">
+              <div className="configForm">
+                <div className="project-overlay">
+                  <label className="project-text" htmlFor="project-id">
+                    Project ID
+                  </label>
+                  <Select
+                    placeholder={projectId}
+                    className="project-select"
+                    value={projectId}
+                    onChange={handleProjectIdChange}
+                    options={projectList}
+                  />
+                </div>
+
+                <div className="region-overlay">
+                  <label className="region-text" htmlFor="region-id">
+                    Region
+                  </label>
+
+                  <Select
+                    onClick={handleDropdownOpen}
+                    placeholder={region}
+                    className="region-select"
+                    value={region}
+                    onChange={handleRegionChange}
+                    options={regionList}
+                    isDisabled={isDropdownOpen}
+                  />
+                </div>
+                <div className="save-overlay">
+                  <button
+                    className={
+                      isSaveDisabled ? 'save-button disabled' : 'save-button'
+                    }
+                    disabled={isSaveDisabled}
+                    onClick={handleSave}
+                  >
+                    Save
+                  </button>
+                  {isLoading && (
+                    <div className="save-loader">
+                      <ClipLoader
+                        loading={true}
+                        size={25}
+                        aria-label="Loading Spinner"
+                        data-testid="loader"
+                      />
+                    </div>
+                  )}
+                </div>
+              </div>
+              {isDropdownOpen && (
+                <div className="region-loader">
+                  <ClipLoader
+                    loading={true}
+                    size={15}
+                    aria-label="Loading Spinner"
+                    data-testid="loader"
+                  />
+                </div>
+              )}
+              <div className="user-info-card">
+                <div className="google-header">
+                  This account is managed by google.com
+                </div>
+                <div className="seperator"></div>
+                <div className="user-overlay">
+                  <div className="user-image-overlay">
+                    <img
+                      src={userInfo.picture} // Path to your placeholder image
+                      alt="User Image"
+                      className="user-image"
+                    />
+                  </div>
+                  <div className="user-details">
+                    <div className="user-email">{userInfo.email}</div>
+                  </div>
+                </div>
+                <div className="seperator"></div>
+                <div className="google-header">
+                  <a
+                    href="https://policies.google.com/privacy?hl=en"
+                    target="_blank"
+                    rel="noopener noreferrer"
+                  >
+                    Privacy Policy
+                  </a>
+                  <span className="privacy-terms"> • </span>
+                  <a
+                    href="https://policies.google.com/terms?hl=en"
+                    target="_blank"
+                    rel="noopener noreferrer"
+                  >
+                    Terms of Service
+                  </a>
+                </div>
+              </div>
+            </div>
+          </div>
+        )
+      )}
+    </div>
+  );
+}
+
+export default ConfigSelection;
diff --git a/src/sessions/listSessions.tsx b/src/sessions/listSessions.tsx
new file mode 100644
index 00000000..6d2992bc
--- /dev/null
+++ b/src/sessions/listSessions.tsx
@@ -0,0 +1,397 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useState, useEffect } from 'react';
+import { useTable, useGlobalFilter } from 'react-table';
+import { LabIcon } from '@jupyterlab/ui-components';
+import filterIcon from '../../style/icons/filter_icon.svg';
+import SucceededIcon from '../../style/icons/succeeded_icon.svg';
+import clusterErrorIcon from '../../style/icons/cluster_error_icon.svg';
+import stopIcon from '../../style/icons/stop_icon.svg';
+import stopDisableIcon from '../../style/icons/stop_disable_icon.svg';
+import deleteIcon from '../../style/icons/delete_icon.svg';
+import { ClipLoader } from 'react-spinners';
+import GlobalFilter from '../utils/globalFilter';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  ClusterStatus,
+  POLLING_TIME_LIMIT,
+  STATUS_ACTIVE,
+  STATUS_CREATING,
+  STATUS_DELETING,
+  STATUS_FAIL,
+  STATUS_PENDING,
+  STATUS_PROVISIONING,
+  STATUS_TERMINATED,
+  STATUS_TERMINATING
+} from '../utils/const';
+import TableData from '../utils/tableData';
+import { authApi, elapsedTime, jobTimeFormat } from '../utils/utils';
+import SessionDetails from './sessionDetails';
+import DeletePopup from '../utils/deletePopup';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import { deleteSessionAPI, terminateSessionAPI } from '../utils/sessionService';
+
+const iconFilter = new LabIcon({
+  name: 'launcher:filter-icon',
+  svgstr: filterIcon
+});
+const iconSucceeded = new LabIcon({
+  name: 'launcher:succeeded-icon',
+  svgstr: SucceededIcon
+});
+
+const iconClusterError = new LabIcon({
+  name: 'launcher:cluster-error-icon',
+  svgstr: clusterErrorIcon
+});
+const iconStop = new LabIcon({
+  name: 'launcher:stop-icon',
+  svgstr: stopIcon
+});
+const iconStopDisable = new LabIcon({
+  name: 'launcher:stop-disable-icon',
+  svgstr: stopDisableIcon
+});
+const iconDelete = new LabIcon({
+  name: 'launcher:delete-icon',
+  svgstr: deleteIcon
+});
+
+function ListSessions() {
+  //   handleBatchDetails
+  const [sessionsList, setSessionsList] = useState([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [pollingDisable, setPollingDisable] = useState(false);
+  const [sessionSelected, setSessionSelected] = useState('');
+  const [detailedSessionView, setDetailedSessionView] = useState(false);
+
+  const [deletePopupOpen, setDeletePopupOpen] = useState(false);
+  const [selectedSessionValue, setSelectedSessionValue] = useState('');
+  const [timer, setTimer] = useState<NodeJS.Timer | undefined>(undefined);
+  const pollingSessions = async (
+    pollingFunction: () => void,
+    pollingDisable: boolean
+  ) => {
+    if (pollingDisable) {
+      clearInterval(timer);
+    } else {
+      setTimer(setInterval(pollingFunction, POLLING_TIME_LIMIT));
+    }
+  };
+
+  const data = sessionsList;
+
+  const columns = React.useMemo(
+    () => [
+      {
+        Header: 'Session ID',
+        accessor: 'sessionID'
+      },
+      {
+        Header: 'Status',
+        accessor: 'status'
+      },
+      {
+        Header: 'Location',
+        accessor: 'location'
+      },
+      {
+        Header: 'Creation time',
+        accessor: 'creationTime'
+      },
+      {
+        Header: 'Elapsed time',
+        accessor: 'elapsedTime'
+      },
+      {
+        Header: 'Actions',
+        accessor: 'actions'
+      }
+    ],
+    []
+  );
+
+  const listSessionsAPI = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/locations/${credentials.region_id}/sessions?pageSize=100`,
+        {
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              let transformSessionListData = [];
+              let sessionsListNew = responseResult.sessions;
+              sessionsListNew.sort(
+                (a: { createTime: string }, b: { createTime: string }) => {
+                  const dateA = new Date(a.createTime);
+                  const dateB = new Date(b.createTime);
+                  return Number(dateB) - Number(dateA);
+                }
+              );
+              transformSessionListData = sessionsListNew.map((data: any) => {
+                const startTimeDisplay = jobTimeFormat(data.createTime);
+                const startTime = new Date(data.createTime);
+                let elapsedTimeString = '';
+                if (
+                  data.state === STATUS_TERMINATED ||
+                  data.state === STATUS_FAIL
+                ) {
+                  elapsedTimeString = elapsedTime(data.stateTime, startTime);
+                }
+
+                return {
+                  sessionID: data.name.split('/')[5],
+                  status: data.state,
+                  location: data.name.split('/')[3],
+                  creationTime: startTimeDisplay,
+                  elapsedTime: elapsedTimeString,
+                  actions: renderActions(data)
+                };
+              });
+              setSessionsList(transformSessionListData);
+
+              setIsLoading(false);
+            })
+            .catch((e: Error) => {
+              console.log(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error listing Sessions', err);
+          toast.error('Failed to fetch Sessions');
+        });
+    }
+  };
+
+  const handleDeleteSession = (session: string) => {
+    setSelectedSessionValue(session);
+    setDeletePopupOpen(true);
+  };
+  const handleCancelDelete = () => {
+    setDeletePopupOpen(false);
+  };
+
+  const handleDelete = async () => {
+    await deleteSessionAPI(selectedSessionValue);
+    setDeletePopupOpen(false);
+  };
+  const {
+    getTableProps,
+    getTableBodyProps,
+    headerGroups,
+    rows,
+    prepareRow,
+    state,
+    //@ts-ignore
+    preGlobalFilteredRows,
+    //@ts-ignore
+    setGlobalFilter
+  } = useTable({ columns, data }, useGlobalFilter);
+
+  useEffect(() => {
+    listSessionsAPI();
+    pollingSessions(listSessionsAPI, pollingDisable);
+
+    return () => {
+      pollingSessions(listSessionsAPI, true);
+    };
+  }, [pollingDisable]);
+
+  function renderActions(data: { state: ClusterStatus; name: string }) {
+    return (
+      <div className="actions-icon">
+        <div
+          className={
+            data.state === ClusterStatus.STATUS_ACTIVE
+              ? 'icon-buttons-style'
+              : 'icon-buttons-style-disable'
+          }
+          title="Terminate Session"
+          onClick={
+            data.state === ClusterStatus.STATUS_ACTIVE
+              ? () => terminateSessionAPI(data.name.split('/')[5])
+              : undefined
+          }
+        >
+          {data.state === ClusterStatus.STATUS_ACTIVE ? (
+            <iconStop.react tag="div" />
+          ) : (
+            <iconStopDisable.react tag="div" />
+          )}
+        </div>
+        <div
+          className="icon-buttons-style"
+          title="Delete Session"
+          onClick={() => handleDeleteSession(data.name.split('/')[5])}
+        >
+          <iconDelete.react tag="div" />
+        </div>
+      </div>
+    );
+  }
+  const handleSessionDetails = (selectedName: string) => {
+    pollingSessions(listSessionsAPI, true);
+    setSessionSelected(selectedName);
+    setDetailedSessionView(true);
+  };
+  interface ICellProps {
+    getCellProps: () => React.TdHTMLAttributes<HTMLTableDataCellElement>;
+    value: any;
+    column: {
+      Header: string;
+    };
+    render: (value: string) => React.ReactNode;
+  }
+  const tableDataCondition = (cell: ICellProps) => {
+    if (cell.column.Header === 'Session ID') {
+      return (
+        <td
+          {...cell.getCellProps()}
+          className="cluster-name"
+          onClick={() => handleSessionDetails(cell.value)}
+        >
+          {cell.value}
+        </td>
+      );
+    }
+    if (cell.column.Header === 'Status') {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          <div key="Status" className="cluster-status-parent">
+            {cell.value === STATUS_FAIL && <iconClusterError.react tag="div" />}
+            {cell.value === STATUS_TERMINATED && (
+              <iconSucceeded.react tag="div" />
+            )}
+            {cell.value === STATUS_ACTIVE && <iconSucceeded.react tag="div" />}
+            {(cell.value === STATUS_PROVISIONING ||
+              cell.value === STATUS_CREATING ||
+              cell.value === STATUS_PENDING ||
+              cell.value === STATUS_TERMINATING ||
+              cell.value === STATUS_DELETING) && (
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={15}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+            )}
+            <div className="cluster-status">
+              {cell.value && cell.value.toLowerCase()}
+            </div>
+          </div>
+        </td>
+      );
+    } else {
+      return (
+        <td {...cell.getCellProps()} className="clusters-table-data">
+          {cell.render('Cell')}
+        </td>
+      );
+    }
+  };
+
+  return (
+    <div>
+      <ToastContainer />
+      {deletePopupOpen && (
+        <DeletePopup
+          onCancel={() => handleCancelDelete()}
+          onDelete={() => handleDelete()}
+          deletePopupOpen={deletePopupOpen}
+          DeleteMsg={
+            'This will delete ' +
+            selectedSessionValue +
+            ' and cannot be undone.'
+          }
+        />
+      )}
+      {detailedSessionView && (
+        <SessionDetails
+          sessionSelected={sessionSelected}
+          setDetailedSessionView={setDetailedSessionView}
+          detailedSessionView={detailedSessionView}
+        />
+      )}
+      {sessionsList.length > 0 && !detailedSessionView ? (
+        <div>
+          <div className="filter-cluster-overlay">
+            <div className="filter-cluster-icon">
+              <iconFilter.react tag="div" />
+            </div>
+            <div className="filter-cluster-text"></div>
+            <div className="filter-cluster-section">
+              <GlobalFilter
+                preGlobalFilteredRows={preGlobalFilteredRows}
+                //@ts-ignore
+                globalFilter={state.globalFilter}
+                setGlobalFilter={setGlobalFilter}
+                setPollingDisable={setPollingDisable}
+              />
+            </div>
+          </div>
+          <div className="session-list-table-parent">
+            <TableData
+              getTableProps={getTableProps}
+              headerGroups={headerGroups}
+              getTableBodyProps={getTableBodyProps}
+              isLoading={isLoading}
+              rows={rows}
+              prepareRow={prepareRow}
+              tableDataCondition={tableDataCondition}
+              fromPage="Sessions"
+            />
+          </div>
+        </div>
+      ) : (
+        <div>
+          {isLoading && (
+            <div className="spin-loaderMain">
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={18}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+              Loading Sessions
+            </div>
+          )}
+          {!isLoading && !detailedSessionView && (
+            <div className="no-data-style">No rows to display</div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default ListSessions;
diff --git a/src/sessions/sessionDetails.tsx b/src/sessions/sessionDetails.tsx
new file mode 100644
index 00000000..58ce929c
--- /dev/null
+++ b/src/sessions/sessionDetails.tsx
@@ -0,0 +1,389 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React, { useState, useEffect } from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import LeftArrowIcon from '../../style/icons/left_arrow_icon.svg';
+import StopClusterIcon from '../../style/icons/stop_cluster_icon.svg';
+import StopClusterDisableIcon from '../../style/icons/stop_cluster_disable_icon.svg';
+import SucceededIcon from '../../style/icons/succeeded_icon.svg';
+import clusterErrorIcon from '../../style/icons/cluster_error_icon.svg';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  NETWORK_KEY,
+  NETWORK_LABEL,
+  POLLING_TIME_LIMIT,
+  SERVICE_ACCOUNT_KEY,
+  SERVICE_ACCOUNT_LABEL,
+  STATUS_ACTIVE,
+  STATUS_CREATING,
+  STATUS_DELETING,
+  STATUS_ERROR,
+  STATUS_FAIL,
+  STATUS_PROVISIONING,
+  STATUS_STARTING,
+  STATUS_STOPPING,
+  STATUS_TERMINATED,
+  STATUS_TERMINATING,
+  SUBNETWORK_KEY,
+  SUBNETWORK_LABEL
+} from '../utils/const';
+import { authApi, elapsedTime, jobTimeFormat } from '../utils/utils';
+import ClipLoader from 'react-spinners/ClipLoader';
+import ViewLogs from '../utils/viewLogs';
+import { toast, ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import { terminateSessionAPI } from '../utils/sessionService';
+
+const iconLeftArrow = new LabIcon({
+  name: 'launcher:left-arrow-icon',
+  svgstr: LeftArrowIcon
+});
+const iconStopCluster = new LabIcon({
+  name: 'launcher:stop-cluster-icon',
+  svgstr: StopClusterIcon
+});
+const iconStopClusterDisable = new LabIcon({
+  name: 'launcher:stop-cluster-disable-icon',
+  svgstr: StopClusterDisableIcon
+});
+const iconSucceeded = new LabIcon({
+  name: 'launcher:succeeded-icon',
+  svgstr: SucceededIcon
+});
+
+const iconClusterError = new LabIcon({
+  name: 'launcher:cluster-error-icon',
+  svgstr: clusterErrorIcon
+});
+
+interface ISessionDetailsProps {
+  sessionSelected: string;
+  setDetailedSessionView: React.Dispatch<React.SetStateAction<boolean>>;
+  detailedSessionView: boolean;
+}
+function SessionDetails({
+  sessionSelected,
+  setDetailedSessionView,
+  detailedSessionView
+}: ISessionDetailsProps) {
+  const [sessionInfo, setSessionInfo] = useState({
+    state: '',
+    name: '',
+    uuid: '',
+    elapsedTime: '',
+    createTime: '',
+    stateTime: '',
+    stateHistory: [{ stateStartTime: '' }],
+    runtimeConfig: { properties: [] },
+    environmentConfig: {
+      executionConfig: []
+    }
+  });
+  const [isLoading, setIsLoading] = useState(true);
+  const [labelDetail, setLabelDetail] = useState(['']);
+  const [timer, setTimer] = useState<NodeJS.Timer | undefined>(undefined);
+  const pollingSessionDetails = async (
+    pollingFunction: () => void,
+    pollingDisable: boolean
+  ) => {
+    if (pollingDisable) {
+      clearInterval(timer);
+    } else {
+      setTimer(setInterval(pollingFunction, POLLING_TIME_LIMIT));
+    }
+  };
+
+  const handleDetailedView = () => {
+    pollingSessionDetails(getSessionDetails, true);
+    setDetailedSessionView(false);
+  };
+  const getSessionDetails = async () => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/locations/${credentials.region_id}/sessions/${sessionSelected}`,
+        {
+          method: 'GET',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              setSessionInfo(responseResult);
+              const labelValue: string[] = [];
+              if (responseResult.labels) {
+                for (const [key, value] of Object.entries(
+                  responseResult.labels
+                )) {
+                  labelValue.push(`${key}:${value}`);
+                }
+              }
+              setLabelDetail(labelValue);
+              setIsLoading(false);
+            })
+            .catch((e: Error) => {
+              console.log(e);
+              setIsLoading(false);
+            });
+        })
+        .catch((err: Error) => {
+          setIsLoading(false);
+          console.error('Error loading session details', err);
+          toast.error('Failed to fetch Session details');
+        });
+    }
+  };
+
+  useEffect(() => {
+    getSessionDetails();
+    pollingSessionDetails(getSessionDetails, false);
+
+    return () => {
+      pollingSessionDetails(getSessionDetails, true);
+    };
+  }, []);
+  const createTimeDisplay = jobTimeFormat(sessionInfo.createTime);
+  const parts = createTimeDisplay.split(',');
+  const createTimeString = parts.slice(0, 2).join(',');
+  const sessionStartTime = new Date(sessionInfo.createTime);
+  let elapsedTimeString = '';
+  if (
+    sessionInfo.state === STATUS_TERMINATED ||
+    sessionInfo.state === STATUS_FAIL
+  ) {
+    elapsedTimeString = elapsedTime(sessionInfo.stateTime, sessionStartTime);
+  }
+  const sessionActiveTime =
+    sessionInfo.stateHistory &&
+    sessionInfo.stateHistory.length > 1 &&
+    sessionInfo.stateHistory[1].stateStartTime
+      ? new Date(sessionInfo.stateHistory[1].stateStartTime)
+      : '';
+  let runTimeString = '';
+  if (sessionActiveTime !== '') {
+    runTimeString = elapsedTime(sessionInfo.stateTime, sessionActiveTime);
+  }
+
+  return (
+    <div>
+      <ToastContainer />
+      {sessionInfo.name !== '' ? (
+        <div className="scroll-comp">
+          {detailedSessionView && (
+            <div>
+              <div className="cluster-details-header">
+                <div
+                  className="back-arrow-icon"
+                  onClick={() => handleDetailedView()}
+                >
+                  <iconLeftArrow.react tag="div" />
+                </div>
+                <div className="cluster-details-title">Session details</div>
+                <div
+                  className={
+                    sessionInfo.state === STATUS_ACTIVE
+                      ? 'action-cluster-section'
+                      : 'action-cluster-section disabled'
+                  }
+                  onClick={() =>
+                    sessionInfo.state === STATUS_ACTIVE &&
+                    terminateSessionAPI(sessionInfo.name.split('/')[5])
+                  }
+                >
+                  <div className="action-cluster-icon">
+                    {sessionInfo.state === STATUS_ACTIVE ? (
+                      <iconStopCluster.react tag="div" />
+                    ) : (
+                      <iconStopClusterDisable.react tag="div" />
+                    )}
+                  </div>
+                  <div className="action-cluster-text">TERMINATE</div>
+                </div>
+                <ViewLogs sessionInfo={sessionInfo} />
+              </div>
+              <div className="cluster-details-container">
+                <div className="row-details"></div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Name</div>
+                  <div className="session-details-value">
+                    {sessionInfo.name.split('/')[5]}
+                  </div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">UUID</div>
+                  <div className="session-details-value">
+                    {sessionInfo.uuid}
+                  </div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Status</div>
+                  <div className="session-detail-status-parent">
+                    {sessionInfo.state === STATUS_ACTIVE && (
+                      <iconSucceeded.react tag="div" />
+                    )}
+                    {sessionInfo.state === STATUS_TERMINATED && (
+                      <iconSucceeded.react tag="div" />
+                    )}
+                    {sessionInfo.state === STATUS_ERROR && (
+                      <iconClusterError.react tag="div" />
+                    )}
+                    {sessionInfo.state === STATUS_FAIL && (
+                      <iconClusterError.react tag="div" />
+                    )}
+                    {(sessionInfo.state === STATUS_PROVISIONING ||
+                      sessionInfo.state === STATUS_CREATING ||
+                      sessionInfo.state === STATUS_STARTING ||
+                      sessionInfo.state === STATUS_STOPPING ||
+                      sessionInfo.state === STATUS_TERMINATING ||
+                      sessionInfo.state === STATUS_DELETING) && (
+                      <div>
+                        <ClipLoader
+                          color="#8A8A8A"
+                          loading={true}
+                          size={15}
+                          aria-label="Loading Spinner"
+                          data-testid="loader"
+                        />
+                      </div>
+                    )}
+                    <div className="cluster-status">
+                      {sessionInfo.state === STATUS_CREATING
+                        ? STATUS_PROVISIONING
+                        : sessionInfo.state.toLowerCase()}
+                    </div>
+                  </div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Create time</div>
+                  <div className="session-details-value">
+                    {createTimeString}
+                  </div>
+                </div>
+              </div>
+              <div className="cluster-details-header">
+                <div className="cluster-details-title">Details</div>
+              </div>
+              <div className="cluster-details-container">
+                <div className="row-details">
+                  <div className="cluster-details-label">Elapsed time</div>
+                  <div className="session-details-value">
+                    {elapsedTimeString}
+                  </div>
+                </div>
+                {(sessionInfo.state === STATUS_ACTIVE ||
+                  sessionInfo.state === STATUS_TERMINATED) && (
+                  <div className="row-details">
+                    <div className="cluster-details-label">Run time</div>
+                    <div className="session-details-value">{runTimeString}</div>
+                  </div>
+                )}
+                <div className="row-details">
+                  <div className="cluster-details-label">Properties</div>
+                </div>
+                {Object.entries(sessionInfo.runtimeConfig.properties).map(
+                  ([key, value]) => (
+                    <div className="row-details" key={key}>
+                      <div className="session-details-label">{key}</div>
+                      <div className="session-details-value">{value}</div>
+                    </div>
+                  )
+                )}
+                <div className="row-details">
+                  <div className="cluster-details-label">
+                    Environment config
+                  </div>
+                </div>
+                <div className="row-details">
+                  <div className="session-details-label">Execution config</div>
+                </div>
+                {Object.entries(
+                  sessionInfo.environmentConfig.executionConfig
+                ).map(([key, value]) => {
+                  let label;
+                  if (key === SERVICE_ACCOUNT_KEY) {
+                    label = SERVICE_ACCOUNT_LABEL;
+                  } else if (key === NETWORK_KEY) {
+                    label = NETWORK_LABEL;
+                  } else if (key === SUBNETWORK_KEY) {
+                    label = SUBNETWORK_LABEL;
+                  } else {
+                    label = '';
+                  }
+                  if (
+                    key === SERVICE_ACCOUNT_KEY ||
+                    key === NETWORK_KEY ||
+                    key === SUBNETWORK_KEY
+                  ) {
+                    return (
+                      <div className="row-details" key={key}>
+                        <div className="session-env-details-label">{label}</div>
+                        <div className="session-env-details-value">{value}</div>
+                      </div>
+                    );
+                  }
+                })}
+                <div className="row-details">
+                  <div className="session-env-details-label">Network tags</div>
+                  <div className="session-env-details-value"></div>
+                </div>
+                <div className="row-details">
+                  <div className="cluster-details-label">Labels</div>
+                  <div className="session-label-style-parent">
+                    {labelDetail.length > 0
+                      ? labelDetail.map(label => {
+                          return (
+                            <div key={label} className="job-label-style">
+                              {label.split(':')[0]} : {label.split(':')[1]}
+                            </div>
+                          );
+                        })
+                      : 'None'}
+                  </div>
+                </div>
+              </div>
+            </div>
+          )}
+        </div>
+      ) : (
+        <div className="loader-full-style">
+          {isLoading && (
+            <div className="session-loader">
+              <ClipLoader
+                color="#8A8A8A"
+                loading={true}
+                size={20}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+              Loading Session Details
+            </div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export default SessionDetails;
diff --git a/src/svg.d.ts b/src/svg.d.ts
new file mode 100644
index 00000000..c0926f98
--- /dev/null
+++ b/src/svg.d.ts
@@ -0,0 +1,21 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+declare module '*.svg' {
+    const value: string;
+    export default value;
+}
\ No newline at end of file
diff --git a/src/utils/batchService.tsx b/src/utils/batchService.tsx
new file mode 100644
index 00000000..48e783d5
--- /dev/null
+++ b/src/utils/batchService.tsx
@@ -0,0 +1,53 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import {
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  API_HEADER_BEARER
+} from '../utils/const';
+import { authApi } from '../utils/utils';
+import { toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+
+export const deleteBatchAPI = async (selectedBatch: string) => {
+  const credentials = await authApi();
+  if (credentials) {
+    fetch(
+      `${BASE_URL}/projects/${credentials.project_id}/locations/${credentials.region_id}/batches/${selectedBatch}`,
+      {
+        method: 'DELETE',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      }
+    )
+      .then((response: Response) => {
+        response
+          .json()
+          .then((responseResult: Response) => {
+            console.log(responseResult);
+          })
+          .catch((e: Error) => console.log(e));
+      })
+      .catch((err: Error) => {
+        console.error('Error deleting batches', err);
+        toast.error('Failed to Delete the Batch ');
+      });
+  }
+};
diff --git a/src/utils/clusterServices.tsx b/src/utils/clusterServices.tsx
new file mode 100644
index 00000000..acf2dce7
--- /dev/null
+++ b/src/utils/clusterServices.tsx
@@ -0,0 +1,92 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { toast } from 'react-toastify';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL
+} from '../utils/const';
+import { authApi } from '../utils/utils';
+
+export const deleteClusterApi = async (selectedcluster: string) => {
+  const credentials = await authApi();
+  if (credentials) {
+    fetch(
+      `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/clusters/${selectedcluster}`,
+      {
+        method: 'DELETE',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      }
+    )
+      .then((response: Response) => {
+        response
+          .json()
+          .then((responseResult: Response) => {
+            console.log(responseResult);
+            // listClustersAPI();
+          })
+          .catch((e: Error) => console.log(e));
+      })
+      .catch((err: Error) => {
+        console.error('Error deleting cluster', err);
+      });
+  }
+};
+
+export const startStopAPI = async (
+  selectedcluster: string,
+  operation: string
+) => {
+  const credentials = await authApi();
+  if (credentials) {
+    fetch(
+      `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/clusters/${selectedcluster}:${operation}`,
+      {
+        method: 'POST',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      }
+    )
+      .then((response: Response) => {
+        response
+          .json()
+          .then((responseResult: Response) => {
+            console.log(responseResult);
+          })
+          .catch((e: Error) => console.log(e));
+      })
+      .catch((err: Error) => {
+        console.error('Error starting/stopping cluster', err);
+        toast.error('Failed to Start/Stop the Cluster');
+      });
+
+    // listClustersAPI();
+  }
+};
+
+export const startClusterApi = async (selectedcluster: string) => {
+  startStopAPI(selectedcluster, 'start');
+};
+export const stopClusterApi = async (selectedcluster: string) => {
+  startStopAPI(selectedcluster, 'stop');
+};
diff --git a/src/utils/const.ts b/src/utils/const.ts
new file mode 100644
index 00000000..b9298259
--- /dev/null
+++ b/src/utils/const.ts
@@ -0,0 +1,103 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+export const CREATE_CLUSTER_URL =
+  'https://console.cloud.google.com/dataproc/clusters';
+export const CREATE_BATCH_URL =
+  'https://console.cloud.google.com/dataproc/batches/create';
+export const BASE_URL = 'https://dataproc.googleapis.com/v1';
+export const VIEW_LOGS_URL = 'https://console.cloud.google.com/logs';
+export const POLLING_TIME_LIMIT = 10000;
+export const API_HEADER_CONTENT_TYPE = 'application/json';
+export enum ClusterStatus {
+  STATUS_RUNNING = 'RUNNING',
+  STATUS_STOPPED = 'STOPPED',
+  STATUS_ACTIVE = 'ACTIVE'
+}
+export const STATUS_RUNNING = 'RUNNING';
+export const STATUS_ERROR = 'ERROR';
+export const STATUS_DONE = 'DONE';
+export const STATUS_CANCELLED = 'CANCELLED';
+export const STATUS_SUCCESS = 'SUCCEEDED';
+export const STATUS_FAIL = 'FAILED';
+export const STATUS_STOPPED = 'STOPPED';
+export const STATUS_STARTING = 'STARTING';
+export const STATUS_STOPPING = 'STOPPING';
+export const STATUS_DELETING = 'DELETING';
+export const STATUS_SETUP_DONE = 'SETUP_DONE';
+export const STATUS_CREATING = 'CREATING';
+export const STATUS_PENDING = 'PENDING';
+export const STATUS_PROVISIONING = 'Provisioning';
+export const STATUS_ACTIVE = 'ACTIVE';
+export const STATUS_TERMINATED = 'TERMINATED';
+export const STATUS_TERMINATING = 'TERMINATING';
+export const LABEL_TEXT = 'labels';
+export const API_HEADER_BEARER = 'Bearer ';
+export const SERVICE_ACCOUNT_KEY = 'serviceAccount';
+export const SERVICE_ACCOUNT_LABEL = 'Service account';
+export const NETWORK_KEY = 'networkUri';
+export const NETWORK_LABEL = 'Sub network';
+export const SUBNETWORK_KEY = 'subnetworkUri';
+export const SUBNETWORK_LABEL = 'Sub network';
+export const PROJECT_LIST_URL =
+  'https://cloudresourcemanager.googleapis.com/v1/projects';
+export const USER_INFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo';
+export const REGION_URL = 'https://compute.googleapis.com/compute/v1/projects/';
+export const LOGIN_STATE = '1';
+export const MONTH_NAMES = [
+  'Jan',
+  'Feb',
+  'Mar',
+  'Apr',
+  'May',
+  'Jun',
+  'Jul',
+  'Aug',
+  'Sep',
+  'Oct',
+  'Nov',
+  'Dec'
+];
+export const QUERYFILEMESSAGE =
+  'Can be a GCS file with the gs:// prefix, an HDFS file on the cluster with the hdfs:// prefix, or a local file on the cluster with the file:// prefix';
+export const MAINCLASSMESSAGE =
+  'The fully qualified name of a class in a provided or standard jar file, for example, com.example.wordcount, or a provided jar file to use the main class of that jar file';
+export const JARFILEMESSAGE =
+  'Jar files are included in the CLASSPATH. Can be a GCS file with the gs:// prefix, an HDFS file on the cluster with the hdfs:// prefix, or a local file on the cluster with the file:// prefix.';
+export const FILESMESSAGE =
+  'Files are included in the working directory of each executor. Can be a GCS file with the gs:// prefix, an HDFS file on the cluster with the hdfs:// prefix, or a local file on the cluster with the file:// prefix.';
+export const ARCHIVEFILESMESSAGE =
+  'Archive files are extracted in the Spark working directory. Can be a GCS file with the gs:// prefix, an HDFS file on the cluster with the hdfs:// prefix, or a local file on the cluster with the file:// prefix. Supported file types: .jar, .tar, .tar.gz, .tgz, .zip';
+export const ARGUMENTSMESSAGE =
+  'Additional arguments to pass to the main class. Press Return after each argument.';
+export const MAXRESTARTMESSAGE =
+  'Leave blank if you do not want to allow automatic restarts on job failure. ';
+export const VIEW_LOGS_CLUSTER_URL =
+  'https://console.cloud.google.com/logs/query;query=resource.type="cloud_dataproc_cluster" resource.labels.cluster_name=';
+export const VIEW_LOGS_SESSION_URL =
+  'https://console.cloud.google.com/logs/query;query=resource.type="cloud_dataproc_session"';
+  export const VIEW_LOGS_BATCH_URL =
+  'https://console.cloud.google.com/logs/query;query=resource.type="cloud_dataproc_batch"';
+export const RESTART_JOB_URL =
+  'https://cloud.google.com/dataproc/docs/concepts/jobs/restartable-jobs';
+export const SPARK = 'Spark';
+export const SPARKSQL = 'SparkSQL';
+export const SPARKR = 'SparkR';
+export const PYSPARK = 'PySpark';
+export const DCU_HOURS = 3600000;
+export const GB_MONTHS = 2592000;
+export const TITLE_LAUNCHER_CATEGORY = 'Dataproc Jobs and Sessions';
diff --git a/src/utils/deletePopup.tsx b/src/utils/deletePopup.tsx
new file mode 100644
index 00000000..a0e02fa2
--- /dev/null
+++ b/src/utils/deletePopup.tsx
@@ -0,0 +1,46 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { Modal } from 'semantic-ui-react';
+
+function DeletePopup({ onCancel, onDelete, deletePopupOpen, DeleteMsg }: any) {
+  const modalStyle = {
+    width: 'auto', // Set the desired width
+    backgroundColor: 'white' // Set the desired background color
+  };
+  return (
+    <Modal open={deletePopupOpen} onClose={onCancel} style={modalStyle}>
+      <div className="popup-main">
+        <div className="popup-header">Confirm deletion</div>
+
+        <div className="popup-text">{DeleteMsg}</div>
+
+        <div className="popup-buttons">
+          <div className="popup-button-style" onClick={onCancel}>
+            CANCEL
+          </div>
+          <div className="popup-button-style" onClick={onDelete}>
+            DELETE
+          </div>
+        </div>
+      </div>
+    </Modal>
+  );
+}
+
+export default DeletePopup;
diff --git a/src/utils/globalFilter.tsx b/src/utils/globalFilter.tsx
new file mode 100644
index 00000000..28ccf0e0
--- /dev/null
+++ b/src/utils/globalFilter.tsx
@@ -0,0 +1,52 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React from 'react';
+
+function GlobalFilter({
+  globalFilter,
+  setGlobalFilter,
+  setPollingDisable
+}: any) {
+  const [value, setValue] = React.useState(globalFilter);
+  const onChange = (value: any) => {
+    setGlobalFilter(value || undefined);
+  };
+  if (value !== undefined) {
+    if (value.length !== 0) {
+      setPollingDisable(true);
+    } else {
+      setPollingDisable(false);
+    }
+  }
+
+  return (
+    <span>
+      <input
+        value={value || ''}
+        onChange={e => {
+          setValue(e.target.value);
+          onChange(e.target.value);
+        }}
+        placeholder={'Filter Table'}
+        className="filter-section-part"
+      />
+    </span>
+  );
+}
+
+export default GlobalFilter;
diff --git a/src/utils/jobServices.tsx b/src/utils/jobServices.tsx
new file mode 100644
index 00000000..a8fb2428
--- /dev/null
+++ b/src/utils/jobServices.tsx
@@ -0,0 +1,76 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { toast } from 'react-toastify';
+import { BASE_URL, API_HEADER_CONTENT_TYPE, API_HEADER_BEARER } from './const';
+import { authApi } from './utils';
+
+export const stopJobApi = async (jobId: string) => {
+  const credentials = await authApi();
+  if (credentials) {
+    fetch(
+      `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/jobs/${jobId}:cancel`,
+      {
+        method: 'POST',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      }
+    )
+      .then((response: Response) => {
+        response
+          .json()
+          .then((responseResult: Response) => {
+            console.log(responseResult);
+          })
+          .catch((e: Error) => console.log(e));
+      })
+      .catch((err: Error) => {
+        console.error('Error Stoping jobs', err);
+        toast.error('Failed to Stop job');
+      });
+  }
+};
+export const deleteJobApi = async (jobId: string) => {
+  const credentials = await authApi();
+  if (credentials) {
+    fetch(
+      `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/jobs/${jobId}`,
+      {
+        method: 'DELETE',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      }
+    )
+      .then((response: Response) => {
+        response
+          .json()
+          .then((responseResult: Response) => {
+            console.log(responseResult);
+            // listJobsAPI();
+          })
+          .catch((e: Error) => console.log(e));
+      })
+      .catch((err: Error) => {
+        console.error('Error Deleting Job', err);
+        toast.error('Failed to Delete the job');
+      });
+  }
+};
diff --git a/src/utils/sessionService.tsx b/src/utils/sessionService.tsx
new file mode 100644
index 00000000..9e23492f
--- /dev/null
+++ b/src/utils/sessionService.tsx
@@ -0,0 +1,83 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import {
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  API_HEADER_BEARER
+} from '../utils/const';
+import { authApi } from '../utils/utils';
+import { toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+
+export const deleteSessionAPI = async (selectedSession: string) => {
+  const credentials = await authApi();
+  if (credentials) {
+    fetch(
+      `${BASE_URL}/projects/${credentials.project_id}/locations/${credentials.region_id}/sessions/${selectedSession}`,
+      {
+        method: 'DELETE',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      }
+    )
+      .then((response: Response) => {
+        response
+          .json()
+          .then((responseResult: Response) => {
+            console.log(responseResult);
+          })
+          .catch((e: Error) => console.log(e));
+      })
+      .catch((err: Error) => {
+        console.error('Error deleting session', err);
+
+        toast.error('Failed to delete the session');
+
+
+      });
+  }
+};
+export const terminateSessionAPI = async (selectedSession: string) => {
+  const credentials = await authApi();
+  if (credentials) {
+    fetch(
+      `${BASE_URL}/projects/${credentials.project_id}/locations/${credentials.region_id}/sessions/${selectedSession}:terminate`,
+      {
+        method: 'POST',
+        headers: {
+          'Content-Type': API_HEADER_CONTENT_TYPE,
+          Authorization: API_HEADER_BEARER + credentials.access_token
+        }
+      }
+    )
+      .then((response: Response) => {
+        response
+          .json()
+          .then((responseResult: Response) => {
+            console.log(responseResult);
+          })
+          .catch((e: Error) => console.log(e));
+      })
+      .catch((err: Error) => {
+        console.error('Error terminating session', err);
+        toast.error('Failed to terminate session');
+      });
+  }
+};
diff --git a/src/utils/statusDisplay.tsx b/src/utils/statusDisplay.tsx
new file mode 100644
index 00000000..34684836
--- /dev/null
+++ b/src/utils/statusDisplay.tsx
@@ -0,0 +1,79 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { ClipLoader } from 'react-spinners';
+import {
+  STATUS_CREATING,
+  STATUS_DELETING,
+  STATUS_FAIL,
+  STATUS_PROVISIONING,
+  STATUS_RUNNING,
+  STATUS_STARTING,
+  STATUS_STOPPING,
+  STATUS_CANCELLED,
+  STATUS_PENDING,
+  STATUS_SUCCESS
+} from '../utils/const';
+import { LabIcon } from '@jupyterlab/ui-components';
+import stopIcon from '../../style/icons/stop_icon.svg';
+import clusterRunningIcon from '../../style/icons/cluster_running_icon.svg';
+import clusterErrorIcon from '../../style/icons/cluster_error_icon.svg';
+import SucceededIcon from '../../style/icons/succeeded_icon.svg';
+
+export const statusDisplay = (statusMsg: string) => {
+  const iconClusterRunning = new LabIcon({
+    name: 'launcher:cluster-running-icon',
+    svgstr: clusterRunningIcon
+  });
+  const iconClusterError = new LabIcon({
+    name: 'launcher:cluster-error-icon',
+    svgstr: clusterErrorIcon
+  });
+  const iconSucceeded = new LabIcon({
+    name: 'launcher:succeeded-icon',
+    svgstr: SucceededIcon
+  });
+  const iconStop = new LabIcon({
+    name: 'launcher:stop-icon',
+    svgstr: stopIcon
+  });
+
+  return (
+    <div className="cluster-detail-status-parent">
+      {statusMsg === STATUS_CANCELLED && <iconStop.react tag="div" />}
+      {statusMsg === STATUS_RUNNING && <iconClusterRunning.react tag="div" />}
+      {statusMsg === STATUS_SUCCESS && <iconSucceeded.react tag="div" />}
+      {statusMsg === STATUS_FAIL && <iconClusterError.react tag="div" />}
+      {(statusMsg === STATUS_PROVISIONING ||
+        statusMsg === STATUS_CREATING ||
+        statusMsg === STATUS_STARTING ||
+        statusMsg === STATUS_STOPPING ||
+        statusMsg === STATUS_PENDING ||
+        statusMsg === STATUS_DELETING) && (
+        <ClipLoader
+          color="#8A8A8A"
+          loading={true}
+          size={15}
+          aria-label="Loading Spinner"
+          data-testid="loader"
+        />
+      )}
+      <div className="cluster-status">{statusMsg.toLowerCase()}</div>
+    </div>
+  );
+};
diff --git a/src/utils/tableData.tsx b/src/utils/tableData.tsx
new file mode 100644
index 00000000..750b52b4
--- /dev/null
+++ b/src/utils/tableData.tsx
@@ -0,0 +1,79 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { ClipLoader } from 'react-spinners';
+
+function TableData({
+  getTableProps,
+  headerGroups,
+  getTableBodyProps,
+  isLoading,
+  rows,
+  prepareRow,
+  tableDataCondition,
+  fromPage
+}: any) {
+  return (
+    <table {...getTableProps()} className="clusters-list-table">
+      <thead>
+        {headerGroups.map((headerGroup: any) => (
+          <tr
+            {...headerGroup.getHeaderGroupProps()}
+            className="cluster-list-table-header"
+          >
+            {headerGroup.headers.map((column: any) => (
+              <th
+                {...column.getHeaderProps()}
+                className="clusters-table-header"
+              >
+                {column.render('Header')}
+              </th>
+            ))}
+          </tr>
+        ))}
+      </thead>
+      <tbody {...getTableBodyProps()} className="clusters-table-body">
+        {isLoading ? (
+          <div className="spin-loader">
+            <ClipLoader
+              color="#8A8A8A"
+              loading={true}
+              size={18}
+              aria-label="Loading Spinner"
+              data-testid="loader"
+            />
+            Loading {fromPage}
+          </div>
+        ) : (
+          rows.map((row: any) => {
+            prepareRow(row);
+            return (
+              <tr {...row.getRowProps()} className="cluster-list-data-parent">
+                {row.cells.map((cell: any) => {
+                  return tableDataCondition(cell);
+                })}
+              </tr>
+            );
+          })
+        )}
+      </tbody>
+    </table>
+  );
+}
+
+export default TableData;
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
new file mode 100644
index 00000000..c5683980
--- /dev/null
+++ b/src/utils/utils.ts
@@ -0,0 +1,253 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { requestAPI } from '../handler/handler';
+import {
+  DCU_HOURS,
+  GB_MONTHS,
+  PYSPARK,
+  SPARK,
+  SPARKR,
+  SPARKSQL,
+  STATUS_CREATING,
+  STATUS_DONE,
+  STATUS_ERROR,
+  STATUS_FAIL,
+  STATUS_PROVISIONING,
+  STATUS_SETUP_DONE,
+  STATUS_STARTING,
+  STATUS_SUCCESS
+} from './const';
+import pysparkLogo from '../../style/icons/pyspark_logo.svg';
+import pythonLogo from '../../style/icons/python_logo.svg';
+import sparkrLogo from '../../style/icons/sparkr_logo.svg';
+import scalaLogo from '../../style/icons/scala_logo.svg';
+import { LabIcon } from '@jupyterlab/ui-components';
+interface IAuthCredentials {
+  access_token?: string;
+  project_id?: string;
+  region_id?: string;
+  config_error?: number;
+  login_error?: number;
+}
+
+export const authApi = async (): Promise<IAuthCredentials | undefined> => {
+  try {
+    const data = await requestAPI('credentials');
+    if (typeof data === 'object' && data !== null) {
+      const credentials: IAuthCredentials = {
+        access_token: (data as { access_token: string }).access_token,
+        project_id: (data as { project_id: string }).project_id,
+        region_id: (data as { region_id: string }).region_id,
+        config_error: (data as { config_error: number }).config_error,
+        login_error: (data as { login_error: number }).login_error
+      };
+      return credentials;
+    } else {
+      console.error('Invalid data format.');
+    }
+  } catch (reason) {
+    console.error(`Error on GET credentials.\n${reason}`);
+  }
+};
+
+export const jobTimeFormat = (startTime: string) => {
+  const date = new Date(startTime);
+
+  const formattedDate = date.toLocaleString('en-US', {
+    month: 'long',
+    day: 'numeric',
+    year: 'numeric',
+    hour: 'numeric',
+    minute: 'numeric',
+    second: 'numeric',
+    hour12: true
+  });
+
+  return formattedDate;
+};
+interface IJobData {
+  [key: string]: any;
+}
+
+export const jobTypeValue = (data: IJobData): string | undefined => {
+  const result = Object.keys(data).filter(key => key.endsWith('Job'));
+  const jobTypeName = result[0].split('Job')[0];
+  switch (jobTypeName) {
+    case 'spark':
+      return SPARK;
+    case 'sparkR':
+      return SPARKR;
+    case 'sparkSql':
+      return SPARKSQL;
+    case 'pyspark':
+      return PYSPARK;
+  }
+};
+export const jobTypeValueArguments = (data: IJobData): string => {
+  const result = Object.keys(data).filter(key => key.endsWith('Job'));
+  return result[0].split('Job')[0];
+};
+
+export const BatchTypeValue = (data: IJobData): string | undefined => {
+  const result = Object.keys(data).filter(key => key.endsWith('Batch'));
+  const batchTypeName = result[0].split('Batch')[0];
+  switch (batchTypeName) {
+    case 'spark':
+      return SPARK;
+    case 'sparkR':
+      return SPARKR;
+    case 'sparkSql':
+      return SPARKSQL;
+    case 'pyspark':
+      return PYSPARK;
+  }
+};
+
+export const jobTypeDisplay = (data: string | undefined) => {
+  switch (data) {
+    case 'spark':
+      return 'Spark';
+    case 'sparkR':
+      return 'SparkR';
+    case 'sparkSql':
+      return 'Spark SQL';
+    case 'pyspark':
+      return 'PySpark';
+    case 'hive':
+      return 'Hive';
+    default:
+      return data;
+  }
+};
+
+export const elapsedTime = (endTime: string, jobStartTime: Date): string => {
+  const jobEndTime = new Date(endTime);
+  const elapsedMilliseconds = jobEndTime.getTime() - jobStartTime.getTime();
+  const elapsedSeconds = Math.round(elapsedMilliseconds / 1000) % 60;
+  const elapsedMinutes = Math.floor(elapsedMilliseconds / (1000 * 60)) % 60;
+  const elapsedHours = Math.floor(elapsedMilliseconds / (1000 * 60 * 60));
+  let elapsedTimeString = '';
+  if (elapsedHours > 0) {
+    elapsedTimeString += `${elapsedHours} hr `;
+  }
+
+  if (elapsedMinutes > 0) {
+    elapsedTimeString += `${elapsedMinutes} min `;
+  }
+  if (elapsedSeconds > 0) {
+    elapsedTimeString += `${elapsedSeconds} sec `;
+  }
+  return elapsedTimeString;
+};
+
+export const statusMessage = (data: any) => {
+  if (data.status.state === STATUS_DONE) {
+    return STATUS_SUCCESS;
+  } else if (data.status.state === STATUS_ERROR) {
+    return STATUS_FAIL;
+  } else if (data.status.state === STATUS_SETUP_DONE) {
+    return STATUS_STARTING;
+  } else {
+    return data.status.state;
+  }
+};
+
+export const statusValue = (data: any) => {
+  if (data.status.state === STATUS_CREATING) {
+    return STATUS_PROVISIONING;
+  } else {
+    return data.status.state;
+  }
+};
+
+export const checkConfig = async (
+  setLoginState: React.Dispatch<React.SetStateAction<boolean>>,
+  setConfigError: React.Dispatch<React.SetStateAction<boolean>>,
+  setLoginError: React.Dispatch<React.SetStateAction<boolean>>
+): Promise<void> => {
+  const credentials = await authApi();
+  if (credentials) {
+    if (credentials.access_token === '') {
+      localStorage.clear();
+      if (credentials.config_error === 1) {
+        localStorage.clear();
+        setConfigError(true);
+      }
+      if (credentials.login_error === 1) {
+        localStorage.clear();
+        setLoginError(true);
+      }
+    } else {
+      setLoginState(true);
+    }
+  }
+};
+export const statusMessageBatch = (data: any) => {
+  if (data.state === STATUS_DONE) {
+    return STATUS_SUCCESS;
+  } else if (data.state === STATUS_ERROR) {
+    return STATUS_FAIL;
+  } else if (data.state === STATUS_SETUP_DONE) {
+    return STATUS_STARTING;
+  } else {
+    return data.state;
+  }
+};
+
+export const convertToDCUHours = (milliDcu: string) => {
+  return `~ ${(Number(milliDcu) / DCU_HOURS).toFixed(3)} DCU-hours`;
+};
+
+export const convertToGBMonths = (milliDcu: string) => {
+  return `~ ${(Number(milliDcu) / GB_MONTHS).toFixed(3)} GB-months`;
+};
+
+const iconPysparkLogo = new LabIcon({
+  name: 'launcher:pyspark-logo-icon',
+  svgstr: pysparkLogo
+});
+const iconPythonLogo = new LabIcon({
+  name: 'launcher:python-logo-icon',
+  svgstr: pythonLogo
+});
+const iconSparkRLogo = new LabIcon({
+  name: 'launcher:sparkr-logo-icon',
+  svgstr: sparkrLogo
+});
+const iconScalaLogo = new LabIcon({
+  name: 'launcher:scala-logo-icon',
+  svgstr: scalaLogo
+});
+
+export const iconDisplay = (kernelType: any) => {
+  if (
+    kernelType?.name.includes('spylon') ||
+    kernelType?.name.includes('apache')
+  ) {
+    return iconScalaLogo;
+  } else if (kernelType?.name.includes('ir')) {
+    return iconSparkRLogo;
+  } else if (
+    kernelType?.name.includes('pyspark') ||
+    kernelType?.resources.endpointParentResource.includes('/sessions')
+  ) {
+    return iconPysparkLogo;
+  } else {
+    return iconPythonLogo;
+  }
+};
diff --git a/src/utils/viewLogs.tsx b/src/utils/viewLogs.tsx
new file mode 100644
index 00000000..c653c463
--- /dev/null
+++ b/src/utils/viewLogs.tsx
@@ -0,0 +1,155 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { LabIcon } from '@jupyterlab/ui-components';
+import ViewLogsIcon from '../../style/icons/view_logs_icon.svg';
+import {
+  API_HEADER_BEARER,
+  API_HEADER_CONTENT_TYPE,
+  BASE_URL,
+  VIEW_LOGS_BATCH_URL,
+  VIEW_LOGS_CLUSTER_URL,
+  VIEW_LOGS_SESSION_URL
+} from './const';
+import { authApi } from './utils';
+
+const iconViewLogs = new LabIcon({
+  name: 'launcher:view-logs-icon',
+  svgstr: ViewLogsIcon
+});
+
+function ViewLogs({
+  clusterInfo,
+  projectName,
+  clusterName,
+  setErrorView,
+  batchInfoResponse,
+  sessionInfo
+}: any) {
+  const handleJobDetailsViewLogs = async (clusterName: string) => {
+    const credentials = await authApi();
+    if (credentials) {
+      fetch(
+        `${BASE_URL}/projects/${credentials.project_id}/regions/${credentials.region_id}/clusters/${clusterName}`,
+        {
+          method: 'GET',
+          headers: {
+            'Content-Type': API_HEADER_CONTENT_TYPE,
+            Authorization: API_HEADER_BEARER + credentials.access_token
+          }
+        }
+      )
+        .then((response: Response) => {
+          response
+            .json()
+            .then((responseResult: any) => {
+              if (responseResult.error && responseResult.error.code === 404) {
+                setErrorView(true);
+              } else {
+                window.open(
+                  responseResult.config.endpointConfig.httpPorts[
+                    'Spark History Server'
+                  ]
+                );
+              }
+            })
+            .catch((e: Error) => {
+              console.error(e);
+            });
+        })
+        .catch((err: Error) => {
+          console.error('Error listing clusters Details', err);
+        });
+    }
+  };
+
+  return (
+    <>
+      <div
+        className="action-cluster-section"
+        onClick={() => {
+          if (clusterInfo) {
+            window.open(
+              `${VIEW_LOGS_CLUSTER_URL}"${clusterInfo.clusterName}" resource.labels.cluster_uuid="${clusterInfo.clusterUuid}"?project=${projectName}`,
+              '_blank'
+            );
+          } else if (batchInfoResponse) {
+            window.open(
+              batchInfoResponse.runtimeInfo.endpoints['Spark History Server'],
+              '_blank'
+            );
+          } else if (sessionInfo) {
+            window.open(
+              sessionInfo.runtimeInfo.endpoints['Spark History Server'],
+              '_blank'
+            );
+          } else {
+            handleJobDetailsViewLogs(clusterName);
+          }
+        }}
+      >
+        <div className="action-cluster-icon">
+          <iconViewLogs.react tag="div" />
+        </div>
+        {clusterInfo ? (
+          <div className="action-cluster-text">VIEW CLOUD LOGS</div>
+        ) : (
+          <div className="action-cluster-text">VIEW SPARK LOGS</div>
+        )}
+      </div>
+      <div
+        className="action-cluster-section"
+        onClick={() => {
+          if (sessionInfo) {
+            window.open(
+              `${VIEW_LOGS_SESSION_URL} resource.labels.project_id="${
+                sessionInfo.name.split('/')[1]
+              }"
+              resource.labels.location="us-central1"
+              resource.labels.session_id="${
+                sessionInfo.name.split('/')[5]
+              }"?project=${sessionInfo.name.split('/')[1]}`,
+              '_blank'
+            );
+          } else {
+            window.open(
+              `${VIEW_LOGS_BATCH_URL} resource.labels.project_id="${
+                batchInfoResponse.name.split('/')[1]
+              }"
+              resource.labels.location="us-central1"
+              resource.labels.session_id="${
+                batchInfoResponse.name.split('/')[5]
+              }"?project=${batchInfoResponse.name.split('/')[1]}`,
+              '_blank'
+            );
+          }
+        }}
+      >
+        {!clusterInfo && !clusterName && (
+          <>
+            <div className="action-cluster-icon">
+              <iconViewLogs.react tag="div" />
+            </div>
+            <div className="action-cluster-text">VIEW CLOUD LOGS</div>
+          </>
+        )}
+      </div>
+    </>
+  );
+}
+export default ViewLogs;
diff --git a/style/authLogin.css b/style/authLogin.css
new file mode 100644
index 00000000..d6e0ccb5
--- /dev/null
+++ b/style/authLogin.css
@@ -0,0 +1,257 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto);
+
+.signin-google-icon {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  margin-top: -20px;
+  cursor: pointer;
+}
+.signin-google-icon:disabled {
+  opacity: 0.5; 
+}
+.settings-overlay {
+  width: 100%;
+  height: 80px;
+  display: flex;
+  flex-direction: row;
+  align-items: flex-end;
+  padding-bottom: 10px;
+}
+
+.settings-text {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 18px;
+  line-height: 22px;
+  color: #000000;
+  padding-left: 10px;
+  padding-bottom: 5px;
+}
+
+.settings-icon {
+  padding-left: 70px;
+}
+.settings-seperator {
+  border: 1px solid #e0e0e0;
+  width: 100%;
+  height: 0px;
+  margin-left: 70px;
+}
+.configForm {
+  display: flex;
+  flex-direction: column;
+  padding-left: 70px;
+  padding-top: 60px;
+  width: 100%;
+  width: 630px;
+}
+.project-overlay {
+  position: relative;
+}
+.region-overlay {
+  position: relative;
+  margin-top: 50px;
+}
+.project-text {
+  width: 60px;
+  height: 13px;
+  font-style: normal;
+  font-weight: 1000;
+  font-size: 12px;
+  line-height: 13px;
+  display: flex;
+  align-items: center;
+  color: #000000;
+  position: absolute;
+  background-color: #fff;
+  bottom: 32px;
+  padding-left: 2px;
+  left: 7px;
+}
+
+.project-select {
+  box-sizing: border-box;
+  background: #ffffff !important;
+  border: 1px solid rgba(0, 0, 0, 0.38) !important;
+  display: block !important;
+  height: 36px;
+  width: 100%;
+  border-radius: 4px !important;
+  appearance: none !important;
+  -webkit-appearance: none !important;
+  -moz-appearance: none;
+  position: unset !important;
+  transform: none !important;
+}
+
+.region-text {
+  height: 13px;
+  font-style: normal;
+  font-weight: 1000;
+  font-size: 12px;
+  line-height: 13px;
+  display: flex;
+  align-items: center;
+  color: #000000;
+  position: absolute;
+  background-color: #fff;
+  padding-right: 4px;
+  bottom: 34px;
+  padding-left: 2px;
+  left: 7px;
+}
+
+.region-select {
+  box-sizing: border-box;
+  background: #ffffff !important;
+  border: 1px solid rgba(0, 0, 0, 0.38) !important;
+  display: block !important;
+  height: 40px;
+  width: 100%;
+  border-radius: 4px !important;
+  appearance: none !important;
+  -webkit-appearance: none !important;
+  -moz-appearance: none;
+  transform: none !important;
+  position: unset !important;
+}
+.region-loader {
+  margin-left: 1px;
+  margin-top: 160px;
+}
+.save-overlay {
+  display: flex;
+  flex-direction: row;
+  align-items: flex-start;
+  padding: 32px 0px 0px;
+  gap: 12px;
+  height: 64px;
+}
+.save-button {
+  height: 32px;
+  background: #3367d6;
+  box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.12), 0px 2px 2px rgba(0, 0, 0, 0.2),
+    0px 1px 1px rgba(0, 0, 0, 0.14);
+  border-radius: 4px;
+  flex: none;
+  width: 65px;
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 32px;
+  align-items: center;
+  text-align: center;
+  text-transform: uppercase;
+  color: #ffffff;
+  border: none;
+  cursor: pointer;
+}
+.save-button:disabled{
+  background: lightgrey;
+  color:  black;
+}
+.save-loader {
+  margin-top: 5px;
+}
+.user-info-card {
+  box-sizing: border-box;
+  width: 344px;
+  height: fit-content;
+  background: #ffffff;
+  border: 1px solid #b0b0b0;
+  border-radius: 27px;
+  margin-left: 21%;
+  margin-top: 30px;
+  display: flex;
+  flex-direction: column;
+  margin-right: 25px;
+}
+.user-image-overlay {
+  width: 63px;
+  height: 62px;
+  border-radius: 33.5px;
+}
+.user-image {
+  height: 62px;
+  border-radius: 33.5px;
+}
+.config-overlay {
+  display: flex;
+  flex-direction: row;
+}
+.google-header {
+  align-items: center;
+  justify-content: center;
+  padding-top: 8px;
+  padding-bottom: 8px;
+  display: flex;
+  font-size: 12px;
+}
+.seperator {
+  width: 344px;
+  height: 0px;
+  left: 1316.5px;
+  top: 217px;
+  border: 1px solid #e1e3e1;
+}
+.user-overlay {
+  display: flex;
+  padding: 30px 30px;
+}
+
+.google-footer {
+  width: 344px;
+  height: 0px;
+  border: 1px solid #e1e3e1;
+  text-align: center;
+}
+.user-details {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 12px;
+  line-height: 17px;
+  color: #000000;
+  padding-left: 30px;
+  padding-top: 20px;
+  justify-content: center;
+  align-items: center;
+  overflow-wrap: anywhere;
+}
+.add-account {
+  display: flex;
+  justify-content: center;
+  height: 32px;
+}
+.account-icon {
+  padding-left: 20px;
+  margin-top: 5px;
+}
+.account-dropdown {
+  width: 250px;
+  height: 32px;
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 32px;
+  display: flex;
+  align-items: center;
+  text-align: center;
+  text-transform: uppercase;
+  color: #1f1f1f;
+  border: none;
+}
+.footer-text {
+  padding-top: 15px;
+}
+
+.privacy-terms{
+  margin: 0 5px;
+}
+.ui.selection.active.dropdown .menu {
+  border: none !important;
+}
+.ui.dropdown .menu .selected.item, .ui.dropdown.selected {
+  background: rgb(230,236,250) !important;
+  color: rgb(28,58,169) !important;
+}
\ No newline at end of file
diff --git a/style/base.css b/style/base.css
new file mode 100644
index 00000000..90e4d283
--- /dev/null
+++ b/style/base.css
@@ -0,0 +1,81 @@
+/*
+    See the JupyterLab Developer Guide for useful CSS Patterns:
+
+    https://jupyterlab.readthedocs.io/en/stable/developer/css.html
+*/
+
+/*Hiding Notebook section cards with name Remote for Remote kernels*/
+.jp-LauncherCard[data-category='Notebook'][title~='Dataproc'], .jp-LauncherCard[data-category='Notebook'][title~='dataproc-serverless-interactive'] {
+  display: none;
+}
+
+/*Hiding Console section cards with name Remote for Remote kernels*/
+.jp-LauncherCard[data-category='Console'][title~='Dataproc'], .jp-LauncherCard[data-category='Console'][title~='dataproc-serverless-interactive'] {
+  display: none;
+}
+
+/*Changing Icon for category with image pyspark*/
+.f1hgkb35 svg[data-icon="launcher:pyspark-logo-icon"] {
+  content-visibility: hidden;
+  display: block;
+  height: 32px;
+  margin: 0 auto;
+  width: 32px;
+  background-image: url('./icons/dataproc_icon.svg');
+}
+
+/*Changing Icon for category with image cluster*/
+.f1hgkb35 svg[data-icon="launcher:clusters-icon"] {
+  content-visibility: hidden;
+  display: block;
+  height: 32px;
+  margin: 0 auto;
+  width: 32px;
+  background-image: url('./icons/dataproc_icon.svg');
+}
+
+/*Changing Icon for category with image python*/
+.f1hgkb35 svg[data-icon="launcher:python-logo-icon"] {
+  content-visibility: hidden;
+  display: block;
+  height: 32px;
+  margin: 0 auto;
+  width: 32px;
+  background-image: url('./icons/dataproc_icon.svg');
+}
+
+/*Changing Icon for category with image sparkR*/
+.f1hgkb35 svg[data-icon="launcher:sparkr-logo-icon"] {
+  content-visibility: hidden;
+  display: block;
+  height: 32px;
+  margin: 0 auto;
+  width: 32px;
+  background-image: url('./icons/dataproc_icon.svg');
+}
+
+/*Changing Icon for category with image scala*/
+.f1hgkb35 svg[data-icon="launcher:scala-logo-icon"] {
+  content-visibility: hidden;
+  display: block;
+  height: 32px;
+  margin: 0 auto;
+  width: 32px;
+  background-image: url('./icons/dataproc_icon.svg');
+}
+
+h2 {
+  display: flex;
+  align-items: center;
+  margin: 15px 0px !important;
+}
+
+.ui.dropdown:not(.button)>.default.text {
+  color: rgba(0, 0, 0, 0.66) !important;
+}
+
+/* Highlight select dropdown field when focus */
+.ui.selection.active.dropdown:focus {
+  border: 3px solid #3367D6 !important;
+}
+
diff --git a/style/batches.css b/style/batches.css
new file mode 100644
index 00000000..fb339de9
--- /dev/null
+++ b/style/batches.css
@@ -0,0 +1,72 @@
+.batch-header-part {
+  display: flex;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.create-batch-overlay {
+  height: 48px;
+  padding-left: 15px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+}
+
+.batch-details-label-level-two {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 18px;
+  width: 65%;
+  padding-left: 30px;
+  color: #000000;
+  overflow-wrap: anywhere;
+}
+
+.batch-details-container {
+  width: 80%;
+  padding-left: 15px;
+}
+.batch-details-container-top {
+  width: 50%;
+  padding-left: 15px;
+}
+.details-label {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 18px;
+  width: 65%;
+  color: #000000;
+}
+.batch-details-label-level-one {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 18px;
+  width: 65%;
+  padding-left: 15px;
+  color: #000000;
+  overflow-wrap: anywhere;
+}
+.details-value {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 20px;
+  width: 35%;
+  color: rgba(0, 0, 0, 0.66);
+}
+.batch-details-row-label {
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+  display: flex;
+  justify-content: flex-start;
+  align-items: flex-start;
+  min-height: 18px;
+  margin-bottom: 6px;
+}
+.scroll-comp-batch{
+ 
+    overflow-y: auto;
+    max-height: 85vh;
+  
+}
\ No newline at end of file
diff --git a/style/cluster.css b/style/cluster.css
new file mode 100644
index 00000000..c81983e9
--- /dev/null
+++ b/style/cluster.css
@@ -0,0 +1,281 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto);
+
+.clusters-list-overlay {
+  height: 37px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0px;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.selected-header {
+  cursor: pointer;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 8px 24px;
+
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 20px;
+  
+  text-transform: uppercase;
+
+  color: #3367D6;
+  box-shadow: inset 0px -2px 0px #3367D6;
+}
+
+.unselected-header {
+  cursor: pointer;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 8px 24px;
+
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 20px;
+  
+  text-transform: uppercase;
+
+  color: rgba(0, 0, 0, 0.66);
+}
+
+.create-cluster-overlay {
+  height: 48px;
+  padding-left: 15px;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+
+  display: flex;
+  align-items: center;
+}
+.create-cluster-sub-overlay{
+  width:20%;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+}
+
+.cluster-status {
+  padding-left: 5px;
+  text-transform: capitalize;
+}
+
+ .actions-icon {
+  display: flex;
+}
+.cluster-status-parent{
+  display: flex;
+  align-items: center;
+  height: 29px;
+  color: rgba(0, 0, 0, 0.66);
+}
+
+.cluster-detail-status-parent{
+  display: flex;
+  align-items: center;
+  height: 29px;
+  color: rgba(0, 0, 0, 0.66);
+}
+
+.icon-buttons-style {
+  cursor: pointer;
+  padding-right: 5px;
+}
+
+.icon-buttons-style-disable{
+  /* cursor: not-allowed; */
+  padding-right: 5px;
+  color:rgb(245,245,245);
+  opacity: 0.5;
+}
+
+.spin-loader{
+  display: flex;
+  margin-top: 10px;
+  margin-left: 10px;
+}
+.spin-loaderMain{
+  display: flex;
+  margin-top: 20%;
+  margin-left: 50%;
+}
+
+.filter-cluster-overlay {
+  height: 42px;
+  padding-left: 15px;
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.filter-section-part {
+  border: 0px;
+  width: 100%;
+}
+
+.create-cluster-text {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 32px;
+  
+  display: flex;
+  align-items: center;
+  text-align: center;
+  text-transform: uppercase;
+
+  color: #3367D6;
+}
+
+.filter-cluster-text {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 20px;
+  
+  display: flex;
+  align-items: center;
+  text-align: center;
+}
+
+.create-cluster-icon {
+  display: flex;
+  height: 18px;
+  width: 18px;
+  color: #3367D6;
+}
+
+.filter-cluster-icon {
+  display: flex;
+  height: 18px;
+  width: 18px;
+}
+
+.filter-cluster-section {
+  width: 100%;
+  padding-left: 8px;
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 20px;
+  
+  color: #757575;
+}
+
+.clusters-list-table-parent {
+  height: 62vh;
+  overflow: auto;
+}
+
+.clusters-list-table {
+  width: 100%;
+  border-spacing: 0;
+}
+
+.cluster-list-table-header {
+  background: rgba(0, 0, 0, 0.04);
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  height: 29px;
+  line-height: 18px;
+  color: #000000;
+}
+
+.cluster-list-data-parent {
+  background: #ffffff;
+  height: 29px;
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 18px;
+  
+  color: rgba(0, 0, 0, 0.66);
+}
+
+.clusters-table-header {
+  text-align: left;
+  padding-left: 15px;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.clusters-table-body {
+  overflow-y: auto;
+  max-height: 55vh;
+}
+
+.clusters-table-data {
+  padding-left: 15px;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.clusters-table-data:nth-child(1) {
+  color: #3367D6;
+  text-decoration: underline;
+  cursor: pointer;
+}
+
+.cluster-name {
+  color: #3367D6;
+  text-decoration: underline;
+  cursor: pointer;
+  height: 29px;
+  padding-left: 15px;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+  align-items: center;
+}
+
+.clusters-table-data:nth-child(2) {
+  text-transform: capitalize;
+}
+
+.clusters-list-start-icons {
+  display: flex;
+  justify-content: center;
+}
+
+.clusters-list-start-buttons-padding {
+  padding-left: 3px;
+  padding-right: 3px;
+}
+
+.component-level{
+  max-height: 85vh;
+}
+.scroll-comp{
+  overflow-y: auto;
+  max-height: 79vh;
+}
+.login-error{
+  align-items: center;
+  justify-content: center;
+  display: flex;
+  font-size: 20px;
+  font-weight: 600;
+  padding: 50px;
+}
+.no-data-style {
+  margin: 10px 15px
+}
+
+.error-view-parent{
+  display: flex;
+  align-items: center;
+  padding: 0px 30px;
+  height: 50px;
+  background-color: rgb(251 233 231);
+  color: #000000;
+}
+
+.error-view-message-parent{
+  display: flex;
+  margin-left: 35%;
+}
+
+.error-view-message{
+  margin-left: 30px;
+}
diff --git a/style/clusterDetails.css b/style/clusterDetails.css
new file mode 100644
index 00000000..1eec9235
--- /dev/null
+++ b/style/clusterDetails.css
@@ -0,0 +1,119 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto);
+
+.cluster-details-header {
+  height: 48px;
+  display: flex;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+.scrollable-content {
+  overflow: auto;
+  max-height: calc(100vh - 48px);
+}
+
+.cluster-details-title {
+  padding: 0px 15px;
+  height: 48px;
+  font-style: normal;
+  font-weight: 400;
+  font-size: 18px;
+  line-height: 24px;
+  
+  display: flex;
+  align-items: center;
+  color: #000000;
+}
+
+.action-cluster-text {
+  padding-left: 5px;
+}
+
+.action-cluster-section.disabled {
+  color: rgba(0, 0, 0, 0.54);
+}
+
+.loader-full-style {
+  display: flex;
+  align-items: center;
+  text-align: center;
+  margin-top: 20%;
+  margin-left: 50%;
+}
+
+.back-arrow-icon {
+  padding-left: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.action-cluster-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.action-disabled {
+  opacity: 0.5;
+  /* cursor: not-allowed; */
+}
+.action-cluster-section {
+  cursor: pointer;
+  padding-left: 20px;
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 32px;
+  
+  display: flex;
+  align-items: center;
+  text-align: center;
+  color: #3367D6;
+}
+
+.cluster-details-container {
+  width: 50%;
+  padding-left: 15px;
+}
+
+.cluster-details-label {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 18px;
+  min-width: 35%;
+  color: #000000;
+  overflow-wrap: anywhere;
+}
+
+.cluster-details-value {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 18px;
+  width: 65%;
+  color: rgba(0, 0, 0, 0.66);
+}
+
+.row-details {
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  min-height: 18px;
+  margin-bottom: 6px;
+}
+.cluster-details-value-job {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 18px;
+  width: 65%;
+  color: #3367D6;
+  text-decoration: underline;
+  cursor: pointer;
+  height: 20px;
+}
+
+.cluster-details-action-parent{
+  display: flex;
+}
\ No newline at end of file
diff --git a/style/icons/add_account_icon.svg b/style/icons/add_account_icon.svg
new file mode 100644
index 00000000..e672d664
--- /dev/null
+++ b/style/icons/add_account_icon.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8 8C6.9 8 5.95833 7.60833 5.175 6.825C4.39167 6.04167 4 5.1 4 4C4 2.9 4.39167 1.95833 5.175 1.175C5.95833 0.391666 6.9 -9.53674e-07 8 -9.53674e-07C9.1 -9.53674e-07 10.0417 0.391666 10.825 1.175C11.6083 1.95833 12 2.9 12 4C12 5.1 11.6083 6.04167 10.825 6.825C10.0417 7.60833 9.1 8 8 8ZM1.19209e-07 16V13.2C1.19209e-07 12.6333 0.141667 12.1167 0.425 11.65C0.725 11.1667 1.11667 10.8 1.6 10.55C2.63333 10.0333 3.68333 9.65 4.75 9.4C5.81667 9.13333 6.9 9 8 9C9.1 9 10.1833 9.13333 11.25 9.4C12.3167 9.65 13.3667 10.0333 14.4 10.55C14.8833 10.8 15.2667 11.1667 15.55 11.65C15.85 12.1167 16 12.6333 16 13.2V16H1.19209e-07ZM2 14H14V13.2C14 13.0167 13.95 12.85 13.85 12.7C13.7667 12.55 13.65 12.4333 13.5 12.35C12.6 11.9 11.6917 11.5667 10.775 11.35C9.85833 11.1167 8.93333 11 8 11C7.06667 11 6.14167 11.1167 5.225 11.35C4.30833 11.5667 3.4 11.9 2.5 12.35C2.35 12.4333 2.225 12.55 2.125 12.7C2.04167 12.85 2 13.0167 2 13.2V14ZM8 6C8.55 6 9.01667 5.80833 9.4 5.425C9.8 5.025 10 4.55 10 4C10 3.45 9.8 2.98333 9.4 2.6C9.01667 2.2 8.55 2 8 2C7.45 2 6.975 2.2 6.575 2.6C6.19167 2.98333 6 3.45 6 4C6 4.55 6.19167 5.025 6.575 5.425C6.975 5.80833 7.45 6 8 6Z" fill="#444746"/>
+</svg>
diff --git a/style/icons/clone_icon.svg b/style/icons/clone_icon.svg
new file mode 100644
index 00000000..89fb7a2d
--- /dev/null
+++ b/style/icons/clone_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2582)">
+<path d="M7.5 12C7.08333 12 6.72917 11.8542 6.4375 11.5625C6.14583 11.2708 6 10.9167 6 10.5V4.5C6 4.08333 6.14583 3.72917 6.4375 3.4375C6.72917 3.14583 7.08333 3 7.5 3H13.5C13.9167 3 14.2708 3.14583 14.5625 3.4375C14.8542 3.72917 15 4.08333 15 4.5V10.5C15 10.9167 14.8542 11.2708 14.5625 11.5625C14.2708 11.8542 13.9167 12 13.5 12H7.5ZM7.5 4.5V10.5H13.5V4.5H7.5ZM4.5 15C4.08333 15 3.72917 14.8542 3.4375 14.5625C3.14583 14.2708 3 13.9167 3 13.5V7H4.5V13.5H11V15H4.5ZM7.5 4.5V10.5V4.5Z" fill="#444746"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2582">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/clone_icon_disable.svg b/style/icons/clone_icon_disable.svg
new file mode 100644
index 00000000..529aeacf
--- /dev/null
+++ b/style/icons/clone_icon_disable.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2582)">
+<path d="M7.5 12C7.08333 12 6.72917 11.8542 6.4375 11.5625C6.14583 11.2708 6 10.9167 6 10.5V4.5C6 4.08333 6.14583 3.72917 6.4375 3.4375C6.72917 3.14583 7.08333 3 7.5 3H13.5C13.9167 3 14.2708 3.14583 14.5625 3.4375C14.8542 3.72917 15 4.08333 15 4.5V10.5C15 10.9167 14.8542 11.2708 14.5625 11.5625C14.2708 11.8542 13.9167 12 13.5 12H7.5ZM7.5 4.5V10.5H13.5V4.5H7.5ZM4.5 15C4.08333 15 3.72917 14.8542 3.4375 14.5625C3.14583 14.2708 3 13.9167 3 13.5V7H4.5V13.5H11V15H4.5ZM7.5 4.5V10.5V4.5Z" fill="#8A8A8A"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2582">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/clone_job_icon.svg b/style/icons/clone_job_icon.svg
new file mode 100644
index 00000000..6fa78131
--- /dev/null
+++ b/style/icons/clone_job_icon.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0 1C0 0.447715 0.447714 0 0.999999 0H8.93233V2H1.98496V10H0V1ZM4 3C3.44772 3 3 3.44772 3 4V13C3 13.5523 3.44772 14 4 14H11C11.5523 14 12 13.5523 12 13V4C12 3.44772 11.5523 3 11 3H4ZM5 12V5H10V12H5Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/clone_job_icon_disable.svg b/style/icons/clone_job_icon_disable.svg
new file mode 100644
index 00000000..a003ac8c
--- /dev/null
+++ b/style/icons/clone_job_icon_disable.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0 1C0 0.447715 0.447714 0 0.999999 0H8.93233V2H1.98496V10H0V1ZM4 3C3.44772 3 3 3.44772 3 4V13C3 13.5523 3.44772 14 4 14H11C11.5523 14 12 13.5523 12 13V4C12 3.44772 11.5523 3 11 3H4ZM5 12V5H10V12H5Z" fill="#8A8A8A"/>
+</svg>
diff --git a/style/icons/cluster_error_icon.svg b/style/icons/cluster_error_icon.svg
new file mode 100644
index 00000000..3435bc39
--- /dev/null
+++ b/style/icons/cluster_error_icon.svg
@@ -0,0 +1,3 @@
+<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.49219 0.5C11.3582 0.5 14.4922 3.63401 14.4922 7.5C14.4922 11.366 11.3582 14.5 7.49219 14.5C3.62619 14.5 0.492188 11.366 0.492188 7.5C0.492188 3.63401 3.62619 0.5 7.49219 0.5ZM8.49219 8.5V3.5H6.49219V8.5H8.49219ZM8.49219 9.5V11.5H6.49219V9.5H8.49219Z" fill="#D50000"/>
+</svg>
diff --git a/style/icons/cluster_icon.svg b/style/icons/cluster_icon.svg
new file mode 100644
index 00000000..43e77fca
--- /dev/null
+++ b/style/icons/cluster_icon.svg
@@ -0,0 +1,4 @@
+<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="52" height="52" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M30.2222 15.4444C30.2222 17.7763 28.3318 19.6666 25.9999 19.6666C23.6681 19.6666 21.7777 17.7763 21.7777 15.4444C21.7777 13.1125 23.6681 11.2222 25.9999 11.2222C28.3318 11.2222 30.2222 13.1125 30.2222 15.4444ZM15.4444 30.2222C17.7763 30.2222 19.6666 28.3318 19.6666 25.9999C19.6666 23.6681 17.7763 21.7777 15.4444 21.7777C13.1125 21.7777 11.2222 23.6681 11.2222 25.9999C11.2222 28.3318 13.1125 30.2222 15.4444 30.2222ZM36.5555 30.2222C38.8874 30.2222 40.7777 28.3318 40.7777 25.9999C40.7777 23.6681 38.8874 21.7777 36.5555 21.7777C34.2236 21.7777 32.3333 23.6681 32.3333 25.9999C32.3333 28.3318 34.2236 30.2222 36.5555 30.2222ZM25.9999 40.7777C28.3318 40.7777 30.2222 38.8874 30.2222 36.5555C30.2222 34.2236 28.3318 32.3333 25.9999 32.3333C23.6681 32.3333 21.7777 34.2236 21.7777 36.5555C21.7777 38.8874 23.6681 40.7777 25.9999 40.7777ZM29.1666 25.9999C29.1666 27.7488 27.7488 29.1666 25.9999 29.1666C24.251 29.1666 22.8333 27.7488 22.8333 25.9999C22.8333 24.251 24.251 22.8333 25.9999 22.8333C27.7488 22.8333 29.1666 24.251 29.1666 25.9999ZM17.5555 19.6666C18.7214 19.6666 19.6666 18.7214 19.6666 17.5555C19.6666 16.3896 18.7214 15.4444 17.5555 15.4444C16.3896 15.4444 15.4444 16.3896 15.4444 17.5555C15.4444 18.7214 16.3896 19.6666 17.5555 19.6666ZM19.6666 34.4444C19.6666 35.6103 18.7214 36.5555 17.5555 36.5555C16.3896 36.5555 15.4444 35.6103 15.4444 34.4444C15.4444 33.2785 16.3896 32.3333 17.5555 32.3333C18.7214 32.3333 19.6666 33.2785 19.6666 34.4444ZM34.4444 19.6666C35.6103 19.6666 36.5555 18.7214 36.5555 17.5555C36.5555 16.3896 35.6103 15.4444 34.4444 15.4444C33.2785 15.4444 32.3333 16.3896 32.3333 17.5555C32.3333 18.7214 33.2785 19.6666 34.4444 19.6666ZM36.5555 34.4444C36.5555 35.6103 35.6103 36.5555 34.4444 36.5555C33.2785 36.5555 32.3333 35.6103 32.3333 34.4444C32.3333 33.2785 33.2785 32.3333 34.4444 32.3333C35.6103 32.3333 36.5555 33.2785 36.5555 34.4444Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/cluster_running_icon.svg b/style/icons/cluster_running_icon.svg
new file mode 100644
index 00000000..2530a9a0
--- /dev/null
+++ b/style/icons/cluster_running_icon.svg
@@ -0,0 +1,3 @@
+<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4922 7.5C14.4922 6.33973 14.2099 5.2454 13.7103 4.28193L12.1948 5.79737C12.3873 6.3288 12.4922 6.90215 12.4922 7.5C12.4922 10.2614 10.2536 12.5 7.49219 12.5C4.73076 12.5 2.49219 10.2614 2.49219 7.5C2.49219 4.73857 4.73076 2.5 7.49219 2.5V7.5L11.0277 3.96446L11.4922 3.5L12.4419 2.55025C11.1752 1.2835 9.42519 0.5 7.49219 0.5C3.62619 0.5 0.492188 3.634 0.492188 7.5C0.492188 11.366 3.62619 14.5 7.49219 14.5C11.3582 14.5 14.4922 11.366 14.4922 7.5Z" fill="#188038"/>
+</svg>
diff --git a/style/icons/create_cluster_icon.svg b/style/icons/create_cluster_icon.svg
new file mode 100644
index 00000000..c46cf96e
--- /dev/null
+++ b/style/icons/create_cluster_icon.svg
@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12.25H1.75V1.75H7V0.25H1.75C0.9175 0.25 0.25 0.925 0.25 1.75V12.25C0.25 13.075 0.9175 13.75 1.75 13.75H12.25C13.075 13.75 13.75 13.075 13.75 12.25V7H12.25V12.25ZM8.5 0.25V1.75H11.1925L3.82 9.1225L4.8775 10.18L12.25 2.8075V5.5H13.75V0.25H8.5Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/dataproc_icon.svg b/style/icons/dataproc_icon.svg
new file mode 100644
index 00000000..89f0df04
--- /dev/null
+++ b/style/icons/dataproc_icon.svg
@@ -0,0 +1,21 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_3409)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M25.3562 19.5131L9.06505 28.9287L7.5093 26.237L23.8005 16.8213L25.3562 19.5131Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00774 25.8615L9.00024 7.04547L11.9935 7.04407L12.0002 23.5971L9.00774 25.8615Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.103 12.3096L11.6797 8.52124L27.9792 17.9225L26.4259 20.6157L12.103 12.3096Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.8295 13.2219L11.6797 8.52124L12.103 12.3096L14.5156 13.6874C16.4101 14.3677 18.77 14.0119 19.8295 13.2219Z" fill="#3367D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.8071 10.0613L9.8069 10.061C9.2795 9.1476 9.0004 8.1049 9 7.0453C8.9994 5.4316 9.627 3.9137 10.768 2.7718C11.909 1.6299 13.4262 1.0007 15.0402 1C17.1939 0.999001 19.2013 2.158 20.2791 4.0245C20.8065 4.9379 21.0854 5.9808 21.086 7.0402C21.0871 10.3725 18.3772 13.0846 15.0453 13.0858C12.8919 13.0863 10.8846 11.9275 9.8071 10.0613ZM17.5866 5.579C17.0627 4.6716 16.0874 4.1083 15.0416 4.1088C14.258 4.1093 13.5214 4.4147 12.9675 4.9691C12.4136 5.5235 12.1088 6.2604 12.109 7.0441C12.1093 7.5589 12.2443 8.0647 12.4993 8.5064C13.0232 9.4138 13.998 9.977 15.0443 9.9766C16.662 9.9762 17.9775 8.6593 17.9769 7.0413C17.9768 6.5268 17.8418 6.021 17.5866 5.579Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00439 17.5057L9.00779 25.8615L12.0002 23.597L11.9995 20.9899C11.6524 19.282 10.2041 18.0406 9.00439 17.5057Z" fill="#3367D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.122 23.6935L25.3569 19.5124L21.8246 17.9619L19.8294 19.116C18.7904 20.2079 17.9676 22.3548 18.122 23.6935Z" fill="#3367D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0.809447 26.7189L0.808247 26.7168C-0.857153 23.8325 0.134247 20.1297 3.01805 18.4633C4.41535 17.6555 6.04385 17.4406 7.60325 17.8577C9.16265 18.2748 10.466 19.2741 11.2736 20.6716C12.9402 23.558 11.949 27.2606 9.06545 28.9273C6.18015 30.5947 2.47665 29.604 0.809447 26.7189ZM8.58255 22.2286C8.18975 21.5488 7.55685 21.0637 6.79975 20.861C6.04275 20.6586 5.25215 20.7631 4.57365 21.1552C3.17355 21.9643 2.69215 23.762 3.50085 25.1626L3.50135 25.1634C4.31085 26.5642 6.10905 27.0451 7.50985 26.2356C8.90975 25.4262 9.39115 23.6289 8.58255 22.2286Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7282 26.1829C19.728 26.1826 19.728 26.1826 19.728 26.1826C18.6514 24.3179 18.6503 22.0015 19.7253 20.1381C20.5318 18.7399 21.8347 17.7395 23.3937 17.321C24.9527 16.9026 26.5811 17.1162 27.9793 17.9226C28.8986 18.4527 29.6636 19.2167 30.1919 20.1316C31.2685 21.9963 31.2696 24.3127 30.1945 26.1765C29.388 27.5747 28.0851 28.5751 26.5264 28.9934C24.9674 29.4118 23.3388 29.198 21.9405 28.3913C21.0213 27.8615 20.2563 27.0976 19.7282 26.1829ZM27.4994 21.6862C27.2438 21.2434 26.8726 20.8733 26.426 20.6158C25.0246 19.8074 23.2267 20.2901 22.4186 21.6913C21.8965 22.5965 21.8973 23.7217 22.4206 24.628C22.6762 25.0708 23.0474 25.4409 23.494 25.6984C24.1732 26.0902 24.9637 26.194 25.7205 25.9909C26.4775 25.7877 27.1097 25.3021 27.5014 24.6233C28.0233 23.7182 28.0227 22.5925 27.4994 21.6862Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9864 22.6326C11.9864 22.6326 8.23897 25.814 7.50977 26.2356L12.0727 23.5987C12.0676 23.2586 12.0497 22.9713 11.9864 22.6326Z" fill="#5C85DE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0359 12.7301C13.0359 12.7301 12.1011 7.55887 12.1008 7.04407L12.103 12.3097C12.4015 12.4746 12.7132 12.6162 13.0359 12.7301Z" fill="#5C85DE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2841 18.3535C21.2841 18.3535 25.696 20.1948 26.426 20.6158L21.861 17.9828C21.567 18.1598 21.5438 18.1301 21.2841 18.3535Z" fill="#5C85DE"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_3409">
+<rect width="32" height="32" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/delete_cluster_icon.svg b/style/icons/delete_cluster_icon.svg
new file mode 100644
index 00000000..564d9a84
--- /dev/null
+++ b/style/icons/delete_cluster_icon.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.23244 1H12V3H0V1H3.76756C4.11337 0.402199 4.75972 0 5.5 0H6.5C7.24028 0 7.88663 0.402199 8.23244 1ZM1 4H11V12C11 13.1046 10.1046 14 9 14H3C1.89543 14 1 13.1046 1 12V4Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/delete_icon.svg b/style/icons/delete_icon.svg
new file mode 100644
index 00000000..30ed59de
--- /dev/null
+++ b/style/icons/delete_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2584)">
+<path d="M4.78333 16.975C4.30972 16.975 3.89722 16.8069 3.54583 16.4708C3.20972 16.1194 3.04167 15.7069 3.04167 15.2333V3.775H2.03333V2.03333H6.525V1.025H11.475V2.03333H15.9667V3.775H14.9583V15.2333C14.9583 15.7069 14.7826 16.1194 14.4313 16.4708C14.0951 16.8069 13.6903 16.975 13.2167 16.975H4.78333ZM13.2167 3.775H4.78333V15.2333H13.2167V3.775ZM6.525 13.4917H8.26667V5.51667H6.525V13.4917ZM9.73333 13.4917H11.475V5.51667H9.73333V13.4917ZM4.78333 3.775V15.2333V3.775Z" fill="#444746"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2584">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/edit_icon.svg b/style/icons/edit_icon.svg
new file mode 100644
index 00000000..f79d87fa
--- /dev/null
+++ b/style/icons/edit_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_3526_17578)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.7756 4.02011C16.0748 4.32014 16.0748 4.80351 15.7756 5.10266L14.3711 6.50715L11.4928 3.62886L12.8973 2.22436C13.1965 1.92521 13.6799 1.92521 13.9799 2.22436L15.7756 4.02011ZM2 16V13.1208L10.4901 4.63157L13.3684 7.50986L4.87917 16H2Z" fill="#3367D6"/>
+</g>
+<defs>
+<clipPath id="clip0_3526_17578">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/edit_icon_disable.svg b/style/icons/edit_icon_disable.svg
new file mode 100644
index 00000000..1271fd65
--- /dev/null
+++ b/style/icons/edit_icon_disable.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_3526_17578)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.7756 4.02011C16.0748 4.32014 16.0748 4.80351 15.7756 5.10266L14.3711 6.50715L11.4928 3.62886L12.8973 2.22436C13.1965 1.92521 13.6799 1.92521 13.9799 2.22436L15.7756 4.02011ZM2 16V13.1208L10.4901 4.63157L13.3684 7.50986L4.87917 16H2Z" fill="black"/>
+</g>
+<defs>
+<clipPath id="clip0_3526_17578">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
\ No newline at end of file
diff --git a/style/icons/error_icon.svg b/style/icons/error_icon.svg
new file mode 100644
index 00000000..55b52911
--- /dev/null
+++ b/style/icons/error_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2416_4781)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9 1.5C4.86 1.5 1.5 4.86 1.5 9C1.5 13.14 4.86 16.5 9 16.5C13.14 16.5 16.5 13.14 16.5 9C16.5 4.86 13.14 1.5 9 1.5ZM9.75 12.75H8.25V11.25H9.75V12.75ZM9.75 9.75H8.25V5.25H9.75V9.75Z" fill="#D50000"/>
+</g>
+<defs>
+<clipPath id="clip0_2416_4781">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/filter_icon.svg b/style/icons/filter_icon.svg
new file mode 100644
index 00000000..470d5abc
--- /dev/null
+++ b/style/icons/filter_icon.svg
@@ -0,0 +1,3 @@
+<svg width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H14V2H0V0ZM2 4H12V6H2V4ZM10 8H4V10H10V8Z" fill="black"/>
+</svg>
diff --git a/style/icons/left_arrow_icon.svg b/style/icons/left_arrow_icon.svg
new file mode 100644
index 00000000..9f0c6e45
--- /dev/null
+++ b/style/icons/left_arrow_icon.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M20 11H7.83L13.42 5.41L12 4L4 12L12 20L13.41 18.59L7.83 13H20V11Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/plus_icon.svg b/style/icons/plus_icon.svg
new file mode 100644
index 00000000..e8aaf233
--- /dev/null
+++ b/style/icons/plus_icon.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7 0H5V5H0V7H5V12H7V7H12V5H7V0Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/plus_icon_disable.svg b/style/icons/plus_icon_disable.svg
new file mode 100644
index 00000000..b6e3ddc6
--- /dev/null
+++ b/style/icons/plus_icon_disable.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7 0H5V5H0V7H5V12H7V7H12V5H7V0Z" fill="#8A8A8A"/>
+</svg>
\ No newline at end of file
diff --git a/style/icons/pyspark_logo.svg b/style/icons/pyspark_logo.svg
new file mode 100644
index 00000000..cf7c9aff
--- /dev/null
+++ b/style/icons/pyspark_logo.svg
@@ -0,0 +1,25 @@
+<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="52" height="52" fill="white"/>
+<path d="M1.95545 27.1482H2.59885C3.20021 27.1482 3.65017 27.0302 3.94874 26.7943C4.24732 26.5541 4.3966 26.2065 4.3966 25.7514C4.3966 25.2921 4.27045 24.9529 4.01813 24.7338C3.77002 24.5147 3.37893 24.4052 2.84486 24.4052H1.95545V27.1482ZM6.37098 25.6819C6.37098 26.6763 6.05979 27.4368 5.43741 27.9635C4.81923 28.4902 3.93823 28.7536 2.7944 28.7536H1.95545V32.0402H0V22.7998H2.94579C4.06439 22.7998 4.91385 23.0421 5.49418 23.5266C6.07871 24.007 6.37098 24.7254 6.37098 25.6819Z" fill="#E25A1C"/>
+<path d="M6.9513 24.974H9.05814L10.3891 28.9495C10.5026 29.295 10.5804 29.7038 10.6225 30.1757H10.6603C10.7066 29.7417 10.797 29.333 10.9316 28.9495L12.2373 24.974H14.3L11.3164 32.944C11.043 33.6814 10.6519 34.2334 10.1431 34.5999C9.63846 34.9665 9.04763 35.1498 8.37058 35.1498C8.03836 35.1498 7.71245 35.114 7.39285 35.0424V33.5128C7.62414 33.5676 7.87646 33.595 8.1498 33.595C8.49043 33.595 8.7869 33.4897 9.03922 33.279C9.29574 33.0725 9.49549 32.7586 9.63846 32.3372L9.75201 31.9896L6.9513 24.974Z" fill="#E25A1C"/>
+<g clip-path="url(#clip0_3526_17591)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M48.5824 24.9978C48.5487 24.9273 48.5336 24.8918 48.5154 24.858C48.028 23.9502 47.5419 23.0424 47.0495 22.1346C47.0002 22.0436 47.0064 21.9899 47.074 21.9114C47.8495 21.0245 48.615 20.1326 49.3918 19.2494C49.4193 19.2182 49.4437 19.1852 49.454 19.1297C49.2284 19.1872 49.0029 19.2441 48.7761 19.3026C47.8402 19.5455 46.8968 19.7872 45.9696 20.0338C45.8826 20.0567 45.8431 20.0318 45.7993 19.9609C45.268 19.0911 44.733 18.2189 44.1955 17.3602C44.1681 17.3152 44.1382 17.2715 44.0795 17.2339C44.0364 17.4657 43.9928 17.6964 43.9505 17.9282C43.8014 18.7464 43.6523 19.5598 43.5044 20.3817C43.4883 20.4699 43.4662 20.5583 43.4585 20.6466C43.4511 20.731 43.4066 20.7622 43.3282 20.7865C42.2244 21.1263 41.1231 21.4685 40.0205 21.8108C39.9721 21.8256 39.9247 21.8453 39.8727 21.8913C40.7748 22.2421 41.6768 22.593 42.5915 22.9499C42.5581 22.9758 42.5361 22.9959 42.5116 23.0113C41.9478 23.3682 41.3828 23.7252 40.8202 24.0834C40.7527 24.1266 40.6993 24.1327 40.6235 24.0992C39.9495 23.8024 39.2704 23.5104 38.5938 23.216C38.2906 23.0835 38.0188 22.9081 37.807 22.6529C37.3272 22.0788 37.4224 21.4262 38.0614 21.0214C38.2706 20.8901 38.5086 20.7932 38.7454 20.7172C39.8267 20.3725 40.9129 20.04 41.9904 19.7063C42.0814 19.678 42.1232 19.6383 42.1408 19.542C42.2861 18.7225 42.4352 17.8982 42.5868 17.0885C42.6675 16.6506 42.7101 16.2028 42.9276 15.8005C43.0109 15.6459 43.1105 15.495 43.2308 15.3662C43.6618 14.8976 44.2619 14.8804 44.7218 15.326C44.8759 15.4768 45.0074 15.6535 45.1214 15.8363C45.6226 16.6373 46.1162 17.4433 46.6124 18.2529C46.6706 18.3482 46.723 18.3674 46.8304 18.3397C48.0394 18.0219 49.2485 17.7091 50.4638 17.3963C50.7144 17.3317 50.9662 17.3082 51.2231 17.3552C51.7806 17.4573 52.0249 17.8729 51.8357 18.4016C51.7501 18.642 51.6014 18.8444 51.4348 19.037C50.5903 20.0098 49.7434 20.9875 48.9039 21.9566C48.8349 22.0361 48.8335 22.0928 48.8814 22.1824C49.3876 23.1159 49.8887 24.047 50.3974 24.9916C50.5178 25.2148 50.6104 25.4479 50.6129 25.7043C50.6188 26.2882 50.1819 26.7666 49.5893 26.8525C49.2573 26.9 48.949 26.8305 48.6371 26.7366C47.8791 26.5072 47.1211 26.2815 46.3568 26.057C46.2863 26.0364 46.2593 26.009 46.2467 25.9348C46.1595 25.411 46.0637 24.8884 45.971 24.3646C45.9686 24.3502 45.973 24.3349 45.9755 24.3028C46.8413 24.5371 47.7045 24.7677 48.5941 25.0094" fill="#E25A1C"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M47.0789 32.6646C46.3948 32.6636 45.7133 32.6603 45.0242 32.6632C44.9336 32.6632 44.8826 32.6378 44.8325 32.5625C44.0231 31.3554 43.2037 30.1459 42.4019 28.9437C42.3759 28.9054 42.3485 28.8685 42.3025 28.8038C42.1283 30.1042 41.9567 31.38 41.7851 32.6558H39.9934C40.0145 32.4852 40.0331 32.3196 40.0552 32.1552C40.2293 30.8427 40.406 29.5423 40.5814 28.2297C40.7493 26.9785 40.9159 25.7272 41.0863 24.476C41.0912 24.439 41.1166 24.3916 41.1475 24.3719C41.7639 23.9781 42.3828 23.588 43.0017 23.198C43.0106 23.1922 43.0233 23.1917 43.0551 23.1826C42.8684 24.5811 42.683 25.9673 42.4963 27.3535C42.5037 27.3583 42.511 27.3635 42.5184 27.3683C43.4894 26.3158 44.4604 25.2584 45.4502 24.1911C45.4786 24.353 45.5035 24.4917 45.528 24.6303C45.5975 25.0265 45.6645 25.4252 45.7384 25.8214C45.7536 25.9014 45.7316 25.9502 45.6773 26.0067C45.0496 26.6495 44.4244 27.2947 43.798 27.9449C43.7705 27.9732 43.7446 28.0024 43.7133 28.036C43.7333 28.0671 43.7505 28.0974 43.7705 28.1251C44.8493 29.6094 45.9255 31.0938 47.003 32.5781C47.0225 32.605 47.0509 32.6261 47.0749 32.65V32.6734" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M25.905 28.7394C25.8776 28.6032 25.858 28.402 25.7959 28.2143C25.4952 27.3078 24.5468 26.8159 23.5657 27.0355C22.487 27.2808 21.7114 28.1113 21.6112 29.1945C21.5285 29.9919 21.967 30.7647 22.7814 31.0469C23.4379 31.2787 24.0719 31.1818 24.6608 30.8371C25.4388 30.3807 25.8598 29.7024 25.9137 28.7394H25.905ZM21.1691 32.1129C21.1162 32.503 21.0653 32.8698 21.0162 33.2378C20.9507 33.726 20.8847 34.2155 20.822 34.7099C20.8148 34.7669 20.7971 34.7923 20.7344 34.7918C20.2421 34.79 19.7497 34.7905 19.256 34.7895C19.2447 34.7895 19.2335 34.7837 19.207 34.7761C19.2369 34.5455 19.2658 34.3136 19.2966 34.083C19.4052 33.2734 19.5134 32.4637 19.6236 31.6541C19.7502 30.7267 19.8491 29.8017 20.0108 28.8817C20.2964 27.2501 21.7147 25.8517 23.3685 25.4959C24.3295 25.2923 25.2479 25.3866 26.0873 25.9167C26.9243 26.4442 27.4029 27.217 27.5156 28.1739C27.671 29.5355 27.1586 30.6641 26.175 31.6087C25.526 32.2233 24.7467 32.6134 23.8572 32.7459C22.9338 32.882 22.0655 32.7315 21.2887 32.1987C21.2603 32.1791 21.2295 32.1619 21.1806 32.1318" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M20.3421 24.3846C19.7908 24.787 19.2583 25.1746 18.7259 25.5635C18.6398 25.431 18.563 25.2985 18.4728 25.1747C18.241 24.8569 17.9528 24.6202 17.5344 24.592C17.1861 24.5679 16.8879 24.68 16.6511 24.9318C16.4393 25.1562 16.4118 25.4764 16.6095 25.7304C16.8275 26.01 17.0655 26.275 17.3098 26.5339C17.7145 26.9632 18.1368 27.3754 18.5377 27.8097C18.9023 28.2022 19.1942 28.6414 19.2844 29.1836C19.3917 29.8215 19.2615 30.4226 18.9536 30.9869C18.3836 32.0235 17.4878 32.6307 16.2975 32.8147C15.7738 32.8975 15.2526 32.8813 14.7439 32.7341C14.0661 32.539 13.5937 32.1036 13.2905 31.4951C13.1834 31.278 13.1014 31.0486 13.0049 30.8155C13.5975 30.5051 14.1751 30.2021 14.7589 29.8967C14.779 29.9442 14.7936 29.9839 14.8123 30.0218C14.9122 30.2169 14.994 30.4242 15.1168 30.6045C15.4839 31.1381 16.0752 31.3001 16.6704 31.0363C16.8245 30.9682 16.9736 30.8732 17.1001 30.7627C17.486 30.4266 17.5587 29.958 17.273 29.536C17.1089 29.2931 16.9034 29.0748 16.7029 28.8576C16.2243 28.3375 15.7257 27.8333 15.2621 27.2997C14.9401 26.9341 14.7209 26.5097 14.6507 26.0239C14.5749 25.4891 14.684 24.9861 14.9677 24.5396C15.6731 23.422 16.6967 22.8467 18.0498 22.8835C18.8228 22.9109 19.4405 23.265 19.9166 23.8538C20.0582 24.028 20.191 24.2084 20.3351 24.3973" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M33.4224 30.3339C33.3323 31.0074 33.247 31.6465 33.1593 32.2844C33.1544 32.317 33.1285 32.3611 33.1005 32.3736C31.7599 32.9796 30.0059 32.8949 28.9159 31.6756C28.327 31.0205 28.0802 30.2403 28.1165 29.3816C28.2021 27.382 29.8956 25.6278 31.9253 25.3825C33.1093 25.2402 34.143 25.553 34.9448 26.4546C35.4911 27.068 35.7442 27.804 35.7066 28.6137C35.6821 29.1485 35.5964 29.6797 35.5287 30.2084C35.4322 30.9616 35.327 31.7173 35.2255 32.4656C35.2216 32.4918 35.2162 32.5187 35.2098 32.5528H33.6186C33.6397 32.3798 33.6592 32.2106 33.6818 32.0413C33.7973 31.1715 33.9248 30.2993 34.0263 29.4283C34.0895 28.8861 34.0497 28.3476 33.7958 27.8459C33.5264 27.3122 33.0754 27.0289 32.4802 26.9675C31.2499 26.84 30.0747 27.6766 29.8241 28.8567C29.6512 29.6393 29.924 30.3901 30.5608 30.8072C31.181 31.2108 31.8388 31.212 32.5153 30.9507C32.8586 30.8182 33.1493 30.6072 33.4274 30.3239" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M40.1881 25.5866C40.1152 26.1288 40.0428 26.6625 39.9701 27.2059C39.6318 27.2059 39.301 27.2041 38.9703 27.2064C38.7022 27.2083 38.4578 27.3781 38.3764 27.6235C38.3451 27.7193 38.3334 27.8222 38.3196 27.9228C38.1518 29.1741 37.9851 30.413 37.8185 31.652C37.7764 31.9661 37.7358 32.2801 37.6937 32.5991H36.0273C36.0582 32.3599 36.0875 32.128 36.1183 31.8962C36.2264 31.089 36.3351 30.2769 36.4441 29.4795C36.5385 28.7815 36.6232 28.081 36.7347 27.3818C36.8838 26.4495 37.831 25.6153 38.7895 25.5785C39.248 25.5589 39.7079 25.5752 40.1802 25.5752" fill="#3C3A3E"/>
+<path d="M49.0837 32.6649V31.978H49.0798L48.8041 32.6649H48.7166L48.4409 31.978H48.4365V32.6649H48.2987V31.8418H48.513L48.7635 32.4674L49.0104 31.8418H49.2234V32.6649H49.0837ZM47.8633 31.9522V32.6649H47.7255V31.9522H47.4624V31.8419H48.1277V31.9522H47.8646" fill="#3C3A3E"/>
+<path d="M22.5598 23.2315H22.972L22.8745 22.6206L22.5598 23.2315ZM23.0372 23.6265H22.3518L22.1338 24.0448H21.6477L22.7189 22.082H23.1875L23.5521 24.0448H23.1023L23.0377 23.6265" fill="#3C3A3E"/>
+<path d="M25.4916 22.4832H25.2473L25.1651 22.9358H25.4094C25.5573 22.9358 25.6738 22.841 25.6738 22.6623C25.6738 22.5439 25.6004 22.4832 25.4909 22.4832H25.4916ZM24.9003 22.0882H25.5417C25.8775 22.0882 26.1118 22.2844 26.1118 22.6206C26.1118 23.045 25.8061 23.3296 25.3701 23.3296H25.0932L24.9629 24.0472H24.5444L24.8978 22.0845" fill="#3C3A3E"/>
+<path d="M27.5337 23.2315H27.9459L27.8486 22.6206L27.5337 23.2315ZM28.0098 23.6265H27.3245L27.1065 24.0448H26.6204L27.6916 22.082H28.1602L28.5248 24.0448H28.075L28.0103 23.6265" fill="#3C3A3E"/>
+<path d="M30.6659 23.9794C30.5243 24.0427 30.3689 24.0805 30.2123 24.0805C29.6824 24.0805 29.3503 23.6916 29.3503 23.2046C29.3503 22.5814 29.8853 22.0576 30.5218 22.0576C30.6809 22.0576 30.825 22.095 30.9428 22.1582L30.884 22.628C30.7959 22.5327 30.6535 22.4661 30.4781 22.4661C30.1135 22.4661 29.789 22.7887 29.789 23.1629C29.789 23.4475 29.9719 23.6708 30.2601 23.6708C30.4367 23.6708 30.6071 23.6041 30.7224 23.5125L30.6666 23.9787" fill="#3C3A3E"/>
+<path d="M33.297 23.256H32.4262L32.2846 24.0448H31.8674L32.2208 22.082H32.6392L32.4989 22.8573H33.3697L33.5112 22.082H33.9297L33.5764 24.0448H33.1579L33.2982 23.256" fill="#3C3A3E"/>
+<path d="M34.8005 24.0409L35.1539 22.0781H36.2639L36.1934 22.4731H35.5018L35.4312 22.8534H36.0665L35.9959 23.2484H35.3607L35.2902 23.6434H35.9818L35.9112 24.0384H34.8012" fill="#3C3A3E"/>
+</g>
+<defs>
+<clipPath id="clip0_3526_17591">
+<rect width="39" height="19.8305" fill="white" transform="translate(13 15)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/python_logo.svg b/style/icons/python_logo.svg
new file mode 100644
index 00000000..764d7cc8
--- /dev/null
+++ b/style/icons/python_logo.svg
@@ -0,0 +1,14 @@
+<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21.0869 0C10.3025 0 10.9755 4.66616 10.9755 4.66616L10.9888 9.50052H21.2794V10.9508H6.89887C6.89887 10.9508 0 10.1694 0 21.0275C0 31.883 6.02274 31.4989 6.02274 31.4989H9.61753V26.4605C9.61753 26.4605 9.42372 20.4514 15.5447 20.4514H25.7503C25.7503 20.4514 31.485 20.5441 31.485 14.9216V5.62508C31.485 5.62508 32.3558 0 21.0869 0ZM15.4119 3.25028C16.4354 3.25028 17.2638 4.07676 17.2638 5.09794C17.2638 6.11912 16.4354 6.94559 15.4119 6.94559C15.1687 6.94594 14.9277 6.89839 14.7029 6.80566C14.478 6.71293 14.2738 6.57685 14.1017 6.40521C13.9297 6.23356 13.7933 6.02974 13.7004 5.80542C13.6074 5.58109 13.5598 5.34068 13.5601 5.09794C13.5601 4.07676 14.3885 3.25028 15.4119 3.25028Z" fill="url(#paint0_linear_3526_17687)"/>
+<path d="M20.9132 42.0006C31.6976 42.0006 31.0245 37.3342 31.0245 37.3342L31.0113 32.4996H20.7207V31.0493H35.0999C35.0999 31.0493 42.0001 31.8307 42.0001 20.9735C42.0001 10.1163 35.9773 10.5017 35.9773 10.5017H32.3825V15.5389C32.3825 15.5389 32.5764 21.5483 26.4554 21.5483H16.2498C16.2498 21.5483 10.5151 21.4556 10.5151 27.0783V36.3752C10.5151 36.3752 9.6443 42.0006 20.9132 42.0006ZM26.5881 38.7501C26.3449 38.7505 26.1039 38.7029 25.8791 38.6102C25.6542 38.5175 25.4499 38.3814 25.2779 38.2097C25.1059 38.0381 24.9695 37.8342 24.8766 37.6099C24.7836 37.3856 24.736 37.1452 24.7363 36.9024C24.7363 35.8825 25.5647 35.056 26.5881 35.056C27.6116 35.056 28.44 35.8812 28.44 36.9024C28.44 37.925 27.6116 38.7501 26.5881 38.7501Z" fill="url(#paint1_linear_3526_17687)"/>
+<defs>
+<linearGradient id="paint0_linear_3526_17687" x1="4.08197" y1="3.68471" x2="25.0391" y2="24.7594" gradientUnits="userSpaceOnUse">
+<stop stop-color="#387EB8"/>
+<stop offset="1" stop-color="#366994"/>
+</linearGradient>
+<linearGradient id="paint1_linear_3526_17687" x1="16.5246" y1="16.8992" x2="39.0359" y2="38.5138" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FFE052"/>
+<stop offset="1" stop-color="#FFC331"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/style/icons/refresh_cluster_icon.svg b/style/icons/refresh_cluster_icon.svg
new file mode 100644
index 00000000..464b74c9
--- /dev/null
+++ b/style/icons/refresh_cluster_icon.svg
@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7 14C10.5262 14 13.4438 11.3921 13.9291 8H11.9C11.4368 10.2824 9.41913 12 7 12C4.2384 12 2 9.7616 2 7C2 4.2384 4.2384 2 7 2C8.3808 2 9.6308 2.5596 10.5356 3.4644L8 6H14V0L11.9497 2.05034C10.6829 0.783563 8.93287 0 7 0C3.13425 0 0 3.13425 0 7C0 10.8658 3.13425 14 7 14Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/restart_icon.svg b/style/icons/restart_icon.svg
new file mode 100644
index 00000000..b842a010
--- /dev/null
+++ b/style/icons/restart_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2377)">
+<path d="M8.12917 16.9521C6.3875 16.7229 4.93611 15.959 3.775 14.6604C2.61389 13.3465 2.03333 11.8035 2.03333 10.0312C2.03333 9.05347 2.21667 8.1368 2.58333 7.28125C2.96528 6.41042 3.49236 5.65417 4.16458 5.0125L5.40208 6.25C4.88264 6.73889 4.47778 7.31181 4.1875 7.96875C3.9125 8.61042 3.775 9.29792 3.775 10.0312C3.775 11.3146 4.1875 12.4451 5.0125 13.4229C5.8375 14.3854 6.87639 14.9736 8.12917 15.1875V16.9521ZM9.87083 16.9521V15.1875C11.1236 14.9736 12.1625 14.3854 12.9875 13.4229C13.8125 12.4451 14.225 11.3146 14.225 10.0312C14.225 8.57986 13.7132 7.35 12.6896 6.34167C11.6813 5.31806 10.4514 4.80625 9 4.80625H8.88542L9.91667 5.8375L8.70208 7.05208L5.58542 3.93542L8.70208 0.818749L9.91667 2.03333L8.90833 3.06458H9C10.9403 3.06458 12.5826 3.74444 13.9271 5.10417C15.2868 6.44861 15.9667 8.09097 15.9667 10.0312C15.9667 11.8035 15.3861 13.3465 14.225 14.6604C13.0639 15.959 11.6125 16.7229 9.87083 16.9521Z" fill="#444746"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2377">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/restart_icon_disable.svg b/style/icons/restart_icon_disable.svg
new file mode 100644
index 00000000..11362805
--- /dev/null
+++ b/style/icons/restart_icon_disable.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2377)">
+<path d="M8.12917 16.9521C6.3875 16.7229 4.93611 15.959 3.775 14.6604C2.61389 13.3465 2.03333 11.8035 2.03333 10.0312C2.03333 9.05347 2.21667 8.1368 2.58333 7.28125C2.96528 6.41042 3.49236 5.65417 4.16458 5.0125L5.40208 6.25C4.88264 6.73889 4.47778 7.31181 4.1875 7.96875C3.9125 8.61042 3.775 9.29792 3.775 10.0312C3.775 11.3146 4.1875 12.4451 5.0125 13.4229C5.8375 14.3854 6.87639 14.9736 8.12917 15.1875V16.9521ZM9.87083 16.9521V15.1875C11.1236 14.9736 12.1625 14.3854 12.9875 13.4229C13.8125 12.4451 14.225 11.3146 14.225 10.0312C14.225 8.57986 13.7132 7.35 12.6896 6.34167C11.6813 5.31806 10.4514 4.80625 9 4.80625H8.88542L9.91667 5.8375L8.70208 7.05208L5.58542 3.93542L8.70208 0.818749L9.91667 2.03333L8.90833 3.06458H9C10.9403 3.06458 12.5826 3.74444 13.9271 5.10417C15.2868 6.44861 15.9667 8.09097 15.9667 10.0312C15.9667 11.8035 15.3861 13.3465 14.225 14.6604C13.0639 15.959 11.6125 16.7229 9.87083 16.9521Z" fill="#8A8A8A"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2377">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/scala_logo.svg b/style/icons/scala_logo.svg
new file mode 100644
index 00000000..9b266a81
--- /dev/null
+++ b/style/icons/scala_logo.svg
@@ -0,0 +1,36 @@
+<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_3526_17609)">
+<rect width="52" height="52" fill="white"/>
+<g clip-path="url(#clip1_3526_17609)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M43.3194 39.9948C43.2831 39.9173 43.2667 39.8783 43.2472 39.8411C42.7223 38.8428 42.1988 37.8445 41.6685 36.8462C41.6154 36.7461 41.6221 36.687 41.6948 36.6007C42.53 35.6253 43.3544 34.6446 44.191 33.6733C44.2205 33.639 44.2469 33.6027 44.2579 33.5416C44.0151 33.6049 43.7722 33.6675 43.528 33.7318C42.5201 33.9989 41.5041 34.2647 40.5056 34.5358C40.4118 34.5611 40.3693 34.5337 40.3221 34.4557C39.75 33.4992 39.1739 32.5401 38.595 31.5958C38.5655 31.5462 38.5332 31.4982 38.4701 31.4568C38.4237 31.7118 38.3767 31.9654 38.3311 32.2204C38.1705 33.1202 38.01 34.0146 37.8508 34.9184C37.8333 35.0154 37.8096 35.1127 37.8012 35.2098C37.7933 35.3026 37.7454 35.3369 37.6609 35.3636C36.4722 35.7373 35.2862 36.1137 34.0988 36.49C34.0467 36.5064 33.9956 36.528 33.9396 36.5785C34.9111 36.9644 35.8826 37.3502 36.8675 37.7428C36.8316 37.7712 36.8079 37.7934 36.7816 37.8102C36.1744 38.2028 35.5659 38.5954 34.9601 38.9893C34.8873 39.0368 34.8299 39.0435 34.7482 39.0067C34.0223 38.6802 33.291 38.3591 32.5624 38.0354C32.2359 37.8897 31.9431 37.6968 31.7151 37.4162C31.1983 36.7848 31.3008 36.0671 31.989 35.6219C32.2143 35.4776 32.4707 35.371 32.7257 35.2874C33.8901 34.9083 35.0599 34.5427 36.2203 34.1758C36.3182 34.1446 36.3633 34.1009 36.3822 33.995C36.5387 33.0939 36.6993 32.1873 36.8625 31.2969C36.9494 30.8153 36.9953 30.3229 37.2295 29.8805C37.3193 29.7105 37.4265 29.5445 37.5561 29.4029C38.0202 28.8876 38.6665 28.8687 39.1617 29.3587C39.3277 29.5246 39.4693 29.7188 39.5921 29.9199C40.1318 30.8008 40.6635 31.6871 41.1978 32.5774C41.2605 32.6823 41.3169 32.7033 41.4325 32.6728C42.7346 32.3234 44.0366 31.9794 45.3454 31.6354C45.6153 31.5643 45.8865 31.5386 46.1631 31.5902C46.7635 31.7025 47.0266 32.1595 46.8229 32.741C46.7307 33.0054 46.5706 33.228 46.3911 33.4397C45.4817 34.5095 44.5696 35.5847 43.6656 36.6504C43.5912 36.7379 43.5898 36.8002 43.6413 36.8987C44.1864 37.9253 44.7261 38.9492 45.2739 39.988C45.4036 40.2335 45.5033 40.4898 45.506 40.7717C45.5123 41.4139 45.0419 41.94 44.4036 42.0344C44.0461 42.0867 43.7142 42.0103 43.3782 41.907C42.5619 41.6547 41.7456 41.4065 40.9225 41.1596C40.8465 41.1369 40.8175 41.1068 40.8039 41.0252C40.71 40.4492 40.6069 39.8745 40.5071 39.2985C40.5045 39.2827 40.5092 39.2658 40.5119 39.2305C41.4443 39.4881 42.3739 39.7418 43.3319 40.0075" fill="#E25A1C"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M41.7004 48.4264C40.9637 48.4253 40.2297 48.4217 39.4876 48.4249C39.39 48.4249 39.3351 48.397 39.2811 48.3141C38.4095 46.9867 37.5271 45.6565 36.6635 44.3345C36.6356 44.2924 36.6061 44.2518 36.5565 44.1807C36.369 45.6107 36.1841 47.0137 35.9993 48.4167H34.0698C34.0925 48.2292 34.1126 48.047 34.1363 47.8663C34.3239 46.4228 34.5141 44.9928 34.703 43.5494C34.8838 42.1733 35.0633 40.7973 35.2468 39.4213C35.2521 39.3807 35.2794 39.3285 35.3126 39.3069C35.9765 38.8739 36.643 38.4449 37.3096 38.0159C37.3191 38.0095 37.3328 38.009 37.367 37.999C37.166 39.5369 36.9663 41.0613 36.7653 42.5857C36.7732 42.591 36.7811 42.5968 36.789 42.6021C37.8347 41.4446 38.8804 40.2817 39.9463 39.1081C39.977 39.2861 40.0038 39.4386 40.0301 39.591C40.105 40.0268 40.1772 40.4652 40.2568 40.9009C40.2731 40.9889 40.2494 41.0426 40.1909 41.1046C39.515 41.8115 38.8417 42.5211 38.167 43.2361C38.1375 43.2673 38.1096 43.2994 38.0758 43.3363C38.0974 43.3705 38.1159 43.4038 38.1375 43.4343C39.2992 45.0666 40.4582 46.6989 41.6186 48.3313C41.6397 48.3608 41.6702 48.384 41.6961 48.4103V48.4361" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8976 44.1093C18.868 43.9596 18.847 43.7383 18.78 43.5319C18.4562 42.535 17.4348 41.994 16.3783 42.2355C15.2166 42.5053 14.3814 43.4186 14.2735 44.6098C14.1844 45.4867 14.6566 46.3366 15.5337 46.6468C16.2407 46.9018 16.9234 46.7952 17.5576 46.4162C18.3955 45.9143 18.8488 45.1683 18.9069 44.1093H18.8976ZM13.7973 47.8192C13.7404 48.2481 13.6856 48.6515 13.6327 49.0562C13.5621 49.5931 13.491 50.1314 13.4235 50.6751C13.4157 50.7378 13.3967 50.7657 13.3292 50.7652C12.799 50.7632 12.2687 50.7637 11.7371 50.7626C11.7249 50.7626 11.7128 50.7563 11.6843 50.7479C11.7164 50.4943 11.7476 50.2393 11.7808 49.9857C11.8978 49.0953 12.0142 48.205 12.133 47.3146C12.2692 46.2947 12.3758 45.2776 12.5499 44.2658C12.8575 42.4716 14.3849 40.9337 16.1659 40.5425C17.2008 40.3185 18.1899 40.4223 19.0939 41.0052C19.9952 41.5853 20.5106 42.4352 20.632 43.4874C20.7994 44.9848 20.2475 46.2259 19.1883 47.2647C18.4894 47.9406 17.6501 48.3696 16.6922 48.5153C15.6977 48.665 14.7627 48.4995 13.9261 47.9136C13.8955 47.892 13.8623 47.8731 13.8097 47.8399" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9068 39.3201C12.3131 39.7626 11.7396 40.1889 11.1662 40.6165C11.0735 40.4708 10.9908 40.3252 10.8936 40.1889C10.644 39.8395 10.3337 39.5791 9.88303 39.5481C9.50793 39.5217 9.1868 39.645 8.93179 39.9218C8.70376 40.1687 8.67408 40.5208 8.88699 40.8C9.12177 41.1076 9.37813 41.399 9.64124 41.6836C10.0771 42.1558 10.5318 42.6091 10.9635 43.0866C11.3562 43.5183 11.6705 44.0013 11.7677 44.5975C11.8832 45.299 11.743 45.9601 11.4115 46.5806C10.7976 47.7206 9.83283 48.3883 8.55102 48.5907C7.98702 48.6817 7.42573 48.6639 6.87792 48.502C6.14796 48.2876 5.63929 47.8086 5.31276 47.1395C5.1974 46.9007 5.10902 46.6485 5.00513 46.3922C5.64333 46.0509 6.26535 45.7176 6.89411 45.3817C6.9157 45.4339 6.93149 45.4777 6.95159 45.5193C7.05913 45.7338 7.14724 45.9618 7.27946 46.1601C7.6748 46.747 8.31166 46.925 8.95257 46.635C9.11853 46.5601 9.27909 46.4556 9.41537 46.3342C9.83094 45.9645 9.9092 45.4492 9.60157 44.9851C9.42481 44.718 9.20353 44.4779 8.98765 44.2391C8.47222 43.6671 7.93521 43.1127 7.43598 42.5258C7.08922 42.1238 6.85309 41.6571 6.77753 41.1228C6.6959 40.5347 6.81343 39.9816 7.1189 39.4905C7.87854 38.2615 8.9809 37.6288 10.4381 37.6693C11.2706 37.6994 11.9358 38.0889 12.4485 38.7364C12.601 38.928 12.744 39.1263 12.8992 39.334" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M26.9932 45.8631C26.8962 46.6037 26.8043 47.3066 26.7098 48.0081C26.7046 48.0439 26.6766 48.0924 26.6466 48.1061C25.2028 48.7726 23.3138 48.6795 22.14 47.3385C21.5058 46.6181 21.24 45.7602 21.2791 44.8158C21.3713 42.6169 23.1951 40.6878 25.3809 40.418C26.656 40.2615 27.7692 40.6055 28.6327 41.597C29.221 42.2716 29.4935 43.081 29.4531 43.9713C29.4267 44.5595 29.3345 45.1437 29.2615 45.7251C29.1576 46.5534 29.0442 47.3844 28.9349 48.2073C28.9307 48.2362 28.9249 48.2657 28.9181 48.3032H27.2045C27.2272 48.113 27.2482 47.9268 27.2725 47.7407C27.3969 46.7842 27.5342 45.8251 27.6435 44.8672C27.7115 44.271 27.6688 43.6787 27.3953 43.127C27.1052 42.5402 26.6194 42.2285 25.9785 42.1611C24.6535 42.0208 23.3879 42.9408 23.1181 44.2386C22.9319 45.0993 23.2256 45.9249 23.9114 46.3836C24.5793 46.8274 25.2877 46.8287 26.0163 46.5414C26.386 46.3957 26.699 46.1637 26.9986 45.852" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M34.2794 40.6426C34.2009 41.2389 34.1229 41.8257 34.0446 42.4233C33.6803 42.4233 33.3241 42.4213 32.9679 42.4239C32.6792 42.426 32.416 42.6127 32.3283 42.8825C32.2946 42.9879 32.2821 43.1011 32.2672 43.2117C32.0864 44.5877 31.907 45.9502 31.7275 47.3128C31.6822 47.6581 31.6385 48.0035 31.5931 48.3542H29.7986C29.8318 48.0911 29.8633 47.8362 29.8965 47.5812C30.013 46.6935 30.13 45.8005 30.2474 44.9236C30.3491 44.156 30.4403 43.3857 30.5604 42.6168C30.7209 41.5915 31.741 40.6742 32.7732 40.6337C33.267 40.6121 33.7622 40.63 34.2709 40.63" fill="#3C3A3E"/>
+<path d="M43.8592 48.4262V47.6707H43.8551L43.5582 48.4262H43.4639L43.1671 47.6707H43.1623V48.4262H43.0139V47.521H43.2447L43.5145 48.209L43.7803 47.521H44.0097V48.4262H43.8592ZM42.5451 47.6424V48.4262H42.3966V47.6424H42.1133V47.5211H42.8297V47.6424H42.5464" fill="#3C3A3E"/>
+<path d="M15.295 38.0521H15.7389L15.634 37.3803L15.295 38.0521ZM15.8091 38.4865H15.071L14.8363 38.9465H14.3127L15.4664 36.7881H15.971L16.3636 38.9465H15.8793L15.8096 38.4865" fill="#3C3A3E"/>
+<path d="M18.4522 37.2295H18.1891L18.1006 37.7272H18.3637C18.5229 37.7272 18.6484 37.623 18.6484 37.4264C18.6484 37.2962 18.5693 37.2295 18.4514 37.2295H18.4522ZM17.8153 36.7951H18.5062C18.8678 36.7951 19.1201 37.0109 19.1201 37.3805C19.1201 37.8473 18.7908 38.1603 18.3213 38.1603H18.0231L17.8828 38.9495H17.4321L17.8126 36.791" fill="#3C3A3E"/>
+<path d="M20.6516 38.0521H21.0955L20.9907 37.3803L20.6516 38.0521ZM21.1643 38.4865H20.4263L20.1915 38.9465H19.668L20.8216 36.7881H21.3262L21.7189 38.9465H21.2345L21.1649 38.4865" fill="#3C3A3E"/>
+<path d="M24.0249 38.8751C23.8724 38.9447 23.7051 38.9863 23.5365 38.9863C22.9657 38.9863 22.6082 38.5586 22.6082 38.0231C22.6082 37.3378 23.1843 36.7617 23.8697 36.7617C24.0411 36.7617 24.1963 36.8029 24.3231 36.8723L24.2598 37.389C24.1649 37.2842 24.0115 37.2109 23.8226 37.2109C23.43 37.2109 23.0805 37.5657 23.0805 37.9772C23.0805 38.2902 23.2775 38.5357 23.5879 38.5357C23.7781 38.5357 23.9616 38.4624 24.0857 38.3617L24.0257 38.8743" fill="#3C3A3E"/>
+<path d="M26.8584 38.0791H25.9206L25.7682 38.9465H25.3188L25.6993 36.7881H26.15L25.9989 37.6407H26.9366L27.0891 36.7881H27.5398L27.1593 38.9465H26.7086L26.8597 38.0791" fill="#3C3A3E"/>
+<path d="M28.4773 38.9426L28.8578 36.7842H30.0532L29.9773 37.2186H29.2325L29.1565 37.6368H29.8406L29.7646 38.0712H29.0806L29.0046 38.5055H29.7494L29.6734 38.9399H28.478" fill="#3C3A3E"/>
+</g>
+<rect x="-26" y="-3" width="107" height="21" fill="#F4F6FB"/>
+<path d="M19.5541 10.6707C19.5541 11.428 19.2806 12.0247 18.7336 12.4607C18.1905 12.8967 17.4332 13.1147 16.4617 13.1147C15.5667 13.1147 14.7749 12.9465 14.0864 12.6099V10.9575C14.6525 11.21 15.1306 11.3878 15.5208 11.4911C15.9147 11.5944 16.2743 11.646 16.5994 11.646C16.9895 11.646 17.2878 11.5714 17.4944 11.4222C17.7048 11.2731 17.8099 11.0512 17.8099 10.7567C17.8099 10.5922 17.764 10.4469 17.6722 10.3207C17.5804 10.1906 17.4447 10.0663 17.2649 9.94775C17.0889 9.82918 16.7275 9.63985 16.1805 9.37976C15.668 9.13879 15.2836 8.90739 15.0273 8.68555C14.7711 8.4637 14.5664 8.20553 14.4135 7.91101C14.2605 7.6165 14.184 7.27226 14.184 6.8783C14.184 6.13627 14.4345 5.55298 14.9355 5.12842C15.4404 4.70386 16.1366 4.49158 17.0239 4.49158C17.46 4.49158 17.875 4.54321 18.2689 4.64648C18.6667 4.74976 19.0817 4.8951 19.5139 5.08252L18.9402 6.46521C18.4927 6.28162 18.1217 6.15348 17.8271 6.08081C17.5365 6.00814 17.2496 5.9718 16.9666 5.9718C16.63 5.9718 16.3718 6.05021 16.192 6.20703C16.0122 6.36385 15.9224 6.56848 15.9224 6.82092C15.9224 6.97774 15.9587 7.11544 16.0314 7.23401C16.104 7.34875 16.2188 7.46159 16.3756 7.57251C16.5363 7.67961 16.913 7.87467 17.5059 8.15771C18.29 8.53255 18.8274 8.9093 19.118 9.28796C19.4087 9.6628 19.5541 10.1237 19.5541 10.6707Z" fill="black"/>
+<path d="M23.189 13.1147C21.1924 13.1147 20.1941 12.0189 20.1941 9.82727C20.1941 8.73718 20.4657 7.90527 21.0088 7.33154C21.552 6.75399 22.3303 6.46521 23.3439 6.46521C24.0859 6.46521 24.7515 6.61055 25.3405 6.90125L24.8241 8.25525C24.5487 8.14433 24.2925 8.05444 24.0553 7.9856C23.8182 7.91292 23.5811 7.87659 23.3439 7.87659C22.4336 7.87659 21.9784 8.52299 21.9784 9.8158C21.9784 11.0704 22.4336 11.6976 23.3439 11.6976C23.6805 11.6976 23.9922 11.6536 24.2791 11.5657C24.566 11.4739 24.8528 11.3324 25.1397 11.1411V12.6385C24.8566 12.8183 24.5698 12.9426 24.2791 13.0115C23.9922 13.0803 23.6289 13.1147 23.189 13.1147Z" fill="black"/>
+<path d="M30.3524 13L30.0139 12.1279H29.968C29.6735 12.4989 29.3694 12.7571 29.0557 12.9025C28.7459 13.044 28.3405 13.1147 27.8394 13.1147C27.2236 13.1147 26.7379 12.9388 26.3822 12.5869C26.0303 12.235 25.8543 11.734 25.8543 11.0837C25.8543 10.4029 26.0915 9.90186 26.5658 9.58057C27.0439 9.25545 27.7629 9.07568 28.723 9.04126L29.836 9.00684V8.72571C29.836 8.07548 29.5033 7.75037 28.8377 7.75037C28.3252 7.75037 27.7228 7.90527 27.0305 8.21509L26.451 7.0332C27.1892 6.64689 28.0077 6.45374 28.9066 6.45374C29.7672 6.45374 30.427 6.64115 30.8859 7.01599C31.3449 7.39083 31.5744 7.96073 31.5744 8.72571V13H30.3524ZM29.836 10.0281L29.159 10.051C28.6503 10.0663 28.2716 10.1581 28.023 10.3264C27.7744 10.4947 27.6501 10.751 27.6501 11.0952C27.6501 11.5886 27.9331 11.8353 28.4992 11.8353C28.9047 11.8353 29.2279 11.7187 29.4688 11.4854C29.7136 11.252 29.836 10.9422 29.836 10.5559V10.0281Z" fill="black"/>
+<path d="M34.779 13H33.0292V4.07275H34.779V13Z" fill="black"/>
+<path d="M40.3417 13L40.0032 12.1279H39.9573C39.6628 12.4989 39.3587 12.7571 39.0451 12.9025C38.7353 13.044 38.3298 13.1147 37.8288 13.1147C37.213 13.1147 36.7272 12.9388 36.3715 12.5869C36.0196 12.235 35.8437 11.734 35.8437 11.0837C35.8437 10.4029 36.0808 9.90186 36.5551 9.58057C37.0332 9.25545 37.7523 9.07568 38.7123 9.04126L39.8254 9.00684V8.72571C39.8254 8.07548 39.4926 7.75037 38.8271 7.75037C38.3145 7.75037 37.7121 7.90527 37.0198 8.21509L36.4403 7.0332C37.1785 6.64689 37.9971 6.45374 38.8959 6.45374C39.7565 6.45374 40.4163 6.64115 40.8753 7.01599C41.3343 7.39083 41.5638 7.96073 41.5638 8.72571V13H40.3417ZM39.8254 10.0281L39.1483 10.051C38.6396 10.0663 38.261 10.1581 38.0124 10.3264C37.7637 10.4947 37.6394 10.751 37.6394 11.0952C37.6394 11.5886 37.9225 11.8353 38.4886 11.8353C38.894 11.8353 39.2172 11.7187 39.4582 11.4854C39.703 11.252 39.8254 10.9422 39.8254 10.5559V10.0281Z" fill="black"/>
+<path d="M25.065 25.4977C25.065 26.0682 25.0104 26.5721 24.901 27.0093C24.7948 27.4434 24.6338 27.8093 24.4182 28.107C24.2057 28.4016 23.9415 28.6248 23.6257 28.7767C23.3129 28.9256 22.95 29 22.5371 29C22.1271 29 21.7643 28.924 21.4484 28.7721C21.1326 28.6202 20.8669 28.3969 20.6513 28.1023C20.4357 27.8047 20.2733 27.4372 20.164 27C20.0547 26.5628 20 26.0589 20 25.4884C20 24.7318 20.0957 24.0946 20.287 23.5767C20.4813 23.0589 20.7667 22.6667 21.1433 22.4C21.5228 22.1333 21.9874 22 22.5371 22C23.0897 22 23.5528 22.1318 23.9263 22.3953C24.3028 22.6589 24.5867 23.0512 24.7781 23.5721C24.9694 24.0899 25.065 24.7318 25.065 25.4977ZM21.6489 25.4977C21.6489 25.9597 21.6823 26.3457 21.7491 26.6558C21.8159 26.9659 21.9146 27.1984 22.0451 27.3535C22.1787 27.5054 22.3427 27.5814 22.5371 27.5814C22.7375 27.5814 22.9014 27.507 23.029 27.3581C23.1596 27.2093 23.2552 26.9814 23.3159 26.6744C23.3797 26.3643 23.4116 25.9721 23.4116 25.4977C23.4116 24.7783 23.3387 24.2481 23.193 23.907C23.0502 23.5628 22.8316 23.3907 22.5371 23.3907C22.3427 23.3907 22.1787 23.4698 22.0451 23.6279C21.9146 23.7829 21.8159 24.0171 21.7491 24.3302C21.6823 24.6403 21.6489 25.0295 21.6489 25.4977Z" fill="#3C3A3E"/>
+<path d="M31 28.907H29.1507L27.4153 24.6791H27.3789C27.3971 24.9271 27.4108 25.1581 27.4199 25.3721C27.432 25.5829 27.4396 25.7752 27.4427 25.9488C27.4487 26.1194 27.4518 26.2698 27.4518 26.4V28.907H25.9805V22.107H27.8161L29.5424 26.2186H29.5789C29.5667 25.9891 29.5561 25.7752 29.547 25.5767C29.5379 25.3783 29.5303 25.1953 29.5242 25.0279C29.5182 24.8605 29.5151 24.7085 29.5151 24.5721V22.107H31V28.907Z" fill="#3C3A3E"/>
+</g>
+<defs>
+<clipPath id="clip0_3526_17609">
+<rect width="52" height="52" fill="white"/>
+</clipPath>
+<clipPath id="clip1_3526_17609">
+<rect width="42" height="21.8077" fill="white" transform="translate(5 29)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/serverless_icon.svg b/style/icons/serverless_icon.svg
new file mode 100644
index 00000000..e78642cd
--- /dev/null
+++ b/style/icons/serverless_icon.svg
@@ -0,0 +1,11 @@
+<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="52" height="52" fill="white"/>
+<g clip-path="url(#clip0_2788_15614)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M41.2396 23.229L36.8855 26.1318L41.2396 29.0346L43.4167 30.486L41.2396 31.9373L36.8855 34.8401L26 42.0971L15.1146 34.8401L10.7605 31.9373L8.58337 30.486L10.7605 29.0346L15.1049 26.1383L15.0894 26.115L10.7605 23.229L8.58337 21.7776L10.7605 20.3262L26 10.1665L41.2396 20.3262L43.4167 21.7776L41.2396 23.229ZM24.5487 14.0417V19.2892L16.8524 24.3875L12.9375 21.7776L24.5487 14.0417ZM27.4514 19.8473L34.7084 24.6804L39.0625 21.7776L27.4514 14.0417V19.8473ZM27.5834 38.1387V32.3332H30.75L26 25.9998L21.25 32.3332H24.4167V38.1387L12.9375 30.486L17.2917 27.5832L26 21.7786L34.7084 27.5832L39.0625 30.486L27.5834 38.1387Z" fill="#3367D6"/>
+</g>
+<defs>
+<clipPath id="clip0_2788_15614">
+<rect width="38" height="38" fill="white" transform="translate(7 7)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/settings_icon.svg b/style/icons/settings_icon.svg
new file mode 100644
index 00000000..e76ae1fe
--- /dev/null
+++ b/style/icons/settings_icon.svg
@@ -0,0 +1,14 @@
+<svg width="31" height="29" viewBox="0 0 31 29" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M25.3562 18.5132L9.06505 27.9289L7.5093 25.2371L23.8005 15.8214L25.3562 18.5132Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00774 24.8616L9.00024 6.04559L11.9935 6.04419L12.0002 22.5972L9.00774 24.8616Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.103 11.3096L11.6797 7.52124L27.9792 16.9225L26.4259 19.6157L12.103 11.3096Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.8295 12.2219L11.6797 7.52124L12.103 11.3096L14.5156 12.6874C16.4101 13.3677 18.77 13.0119 19.8295 12.2219Z" fill="#3367D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.8071 9.0613L9.8069 9.061C9.2795 8.1476 9.0004 7.1049 9 6.0453C8.9994 4.4316 9.627 2.9137 10.768 1.7718C11.909 0.629901 13.4262 0.000700647 15.0402 6.46588e-07C17.1939 -0.000999353 19.2013 1.158 20.2791 3.0245C20.8065 3.9379 21.0854 4.9808 21.086 6.0402C21.0871 9.3725 18.3772 12.0846 15.0453 12.0858C12.8919 12.0863 10.8846 10.9275 9.8071 9.0613ZM17.5866 4.579C17.0627 3.6716 16.0874 3.1083 15.0416 3.1088C14.258 3.1093 13.5214 3.4147 12.9675 3.9691C12.4136 4.5235 12.1088 5.2604 12.109 6.0441C12.1093 6.5589 12.2443 7.0647 12.4993 7.5064C13.0232 8.4138 13.998 8.977 15.0443 8.9766C16.662 8.9762 17.9775 7.6593 17.9769 6.0413C17.9768 5.5268 17.8418 5.021 17.5866 4.579Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00439 16.5056L9.00779 24.8614L12.0002 22.5969L11.9995 19.9898C11.6524 18.2819 10.2041 17.0405 9.00439 16.5056Z" fill="#3367D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.122 22.6935L25.3569 18.5124L21.8246 16.9619L19.8294 18.116C18.7904 19.2079 17.9676 21.3548 18.122 22.6935Z" fill="#3367D6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0.809447 25.719L0.808247 25.7169C-0.857153 22.8326 0.134247 19.1298 3.01805 17.4634C4.41535 16.6556 6.04385 16.4407 7.60325 16.8578C9.16265 17.2749 10.466 18.2742 11.2736 19.6717C12.9402 22.5581 11.949 26.2607 9.06545 27.9274C6.18015 29.5948 2.47665 28.6041 0.809447 25.719ZM8.58255 21.2287C8.18975 20.5489 7.55685 20.0638 6.79975 19.8611C6.04275 19.6587 5.25215 19.7632 4.57365 20.1553C3.17355 20.9644 2.69215 22.7621 3.50085 24.1627L3.50135 24.1635C4.31085 25.5643 6.10905 26.0452 7.50985 25.2357C8.90975 24.4263 9.39115 22.629 8.58255 21.2287Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7283 25.1829C19.7281 25.1826 19.7281 25.1826 19.7281 25.1826C18.6515 23.3179 18.6504 21.0015 19.7254 19.1381C20.5319 17.7399 21.8348 16.7395 23.3938 16.321C24.9528 15.9026 26.5812 16.1162 27.9794 16.9226C28.8987 17.4527 29.6637 18.2167 30.192 19.1316C31.2686 20.9963 31.2697 23.3127 30.1946 25.1765C29.3881 26.5747 28.0852 27.5751 26.5265 27.9934C24.9675 28.4118 23.3389 28.198 21.9406 27.3913C21.0214 26.8615 20.2564 26.0976 19.7283 25.1829ZM27.4995 20.6862C27.2439 20.2434 26.8727 19.8733 26.4261 19.6158C25.0247 18.8074 23.2268 19.2901 22.4187 20.6913C21.8966 21.5965 21.8974 22.7217 22.4207 23.628C22.6763 24.0708 23.0475 24.4409 23.4941 24.6984C24.1733 25.0902 24.9638 25.194 25.7206 24.9909C26.4776 24.7877 27.1098 24.3021 27.5015 23.6233C28.0234 22.7182 28.0228 21.5925 27.4995 20.6862Z" fill="#85A4E6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9864 21.6326C11.9864 21.6326 8.23897 24.814 7.50977 25.2356L12.0727 22.5987C12.0676 22.2586 12.0497 21.9713 11.9864 21.6326Z" fill="#5C85DE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0359 11.7302C13.0359 11.7302 12.1011 6.55899 12.1008 6.04419L12.103 11.3098C12.4015 11.4747 12.7132 11.6163 13.0359 11.7302Z" fill="#5C85DE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2842 17.3536C21.2842 17.3536 25.6961 19.1949 26.4261 19.6159L21.8611 16.9829C21.5671 17.1599 21.5439 17.1302 21.2842 17.3536Z" fill="#5C85DE"/>
+</svg>
diff --git a/style/icons/signin_google_icon.svg b/style/icons/signin_google_icon.svg
new file mode 100644
index 00000000..21a06ec4
--- /dev/null
+++ b/style/icons/signin_google_icon.svg
@@ -0,0 +1,9 @@
+<svg width="215" height="63" viewBox="0 0 215 63" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<rect width="215" height="63" fill="url(#pattern0)"/>
+<defs>
+<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
+<use xlink:href="#image0_2908_26611" transform="scale(0.00232558 0.00793651)"/>
+</pattern>
+<image id="image0_2908_26611" width="430" height="126" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAa4AAAB+CAYAAAB8vPUBAAAKsWlDQ1BJQ0MgUHJvZmlsZQAASImVlwdUE+kWgP+Z9JDQAhGQEnpHikAAKSG0AArSwUZIAgklxBQE7MriCq4FFREsK7pUBRtFbIgoFhbF3jfIIqCsiwUbKm+AQ9jdd957591z/rnfuXP/W+bMP+cOAGQttkiUDqsCkCGUiiMCfWlx8Qk0XD9AAzIgAFugyuZIRIzw8FCAyJT+u3y4B6Bxfdt2PNa/3/+vosblSTgAQOEIJ3ElnAyETyLrPUcklgKAqkLsxsukonHuRFhDjBSIsHycUyb5/TgnTTAaP+ETFcFEWBcAPInNFqcAQLJA7LQsTgoShxSEsL2QKxAinI2wV0ZGJhfhZoQtEB8RwuPx6Ul/iZPyt5hJiphsdoqCJ3uZELyfQCJKZ+f8n4/jf0tGumwqhxmySHxxUASikbqgB2mZIQoWJs0Lm2IBd8J/gvmyoOgp5kiYCVPMZfuFKPamzwud4mRBAEsRR8qKmmKexD9yisWZEYpcyWImY4rZ4um8srRohZ3PYyni5/KjYqc4SxAzb4olaZEh0z5MhV0si1DUzxMG+k7nDVD0niH5S78ClmKvlB8VpOidPV0/T8iYjimJU9TG5fn5T/tEK/xFUl9FLlF6uMKflx6osEuyIhV7pcgLOb03XPEMU9nB4VMMIoEUyAAXCEAmoAE/REuACKQDNsiR8rKl4w0xM0U5YkEKX0pjICeNR2MJOXY2NEd7RycAxs/t5GvxjjpxHiHqtWnb+moAPE+NjY2dnrYF3wTgWCIAxIZpm8UiAFT7AbhyhiMTZ03a0OMXDCACFaABtIE+MAYWyJfBEbgAD+AD/EEwCANRIB4sBhzABxlADJaBFWAtyAeFYCvYCUrBfnAQVIEj4DhoAmfABXAZXAc3wV3wGMhBH3gFhsEHMApBEA4iQxRIGzKATCFryBGiQ16QPxQKRUDxUCKUAgkhGbQCWg8VQkVQKXQAqoaOQaegC9BVqBt6CPVAg9Bb6AuMgkmwBqwHm8GzYDrMgEPgKHgRnAIvhXPhPHgzXAKXw4fhRvgCfB2+C8vhV/AICqCUUFSUIcoWRUcxUWGoBFQySoxahSpAFaPKUXWoFlQH6jZKjhpCfUZj0RQ0DW2L9kAHoaPRHPRS9Cr0JnQpugrdiG5H30b3oIfR3zFkjC7GGuOOYWHiMCmYZZh8TDGmAtOAuYS5i+nDfMBisVSsOdYVG4SNx6Zil2M3Yfdi67Gt2G5sL3YEh8Np46xxnrgwHBsnxeXjduMO487jbuH6cJ/wSngDvCM+AJ+AF+LX4YvxNfhz+Fv4fvwoQZVgSnAnhBG4hBzCFsIhQgvhBqGPMEpUI5oTPYlRxFTiWmIJsY54ifiE+E5JSclIyU1pvpJAaY1SidJRpStKPUqfSeokKxKTtJAkI20mVZJaSQ9J78hkshnZh5xAlpI3k6vJF8nPyJ+UKcp2yixlrvJq5TLlRuVbyq9VCCqmKgyVxSq5KsUqJ1RuqAypElTNVJmqbNVVqmWqp1Tvq46oUdQc1MLUMtQ2qdWoXVUbUMepm6n7q3PV89QPql9U76WgKMYUJoVDWU85RLlE6dPAaphrsDRSNQo1jmh0aQxrqmvO1ozRzNYs0zyrKaeiqGZUFjWduoV6nHqP+mWG3gzGDN6MjTPqZtya8VFrppaPFk+rQKte667WF22atr92mvY27SbtpzpoHSud+TrLdPbpXNIZmqkx02MmZ2bBzOMzH+nCula6EbrLdQ/qduqO6OnrBeqJ9HbrXdQb0qfq++in6u/QP6c/aEAx8DIQGOwwOG/wkqZJY9DSaSW0dtqwoa5hkKHM8IBhl+GokblRtNE6o3qjp8ZEY7pxsvEO4zbjYRMDk7kmK0xqTR6ZEkzppnzTXaYdph/NzM1izTaYNZkNmGuZs8xzzWvNn1iQLbwtllqUW9yxxFrSLdMs91retIKtnK34VmVWN6xhaxdrgfVe624bjI2bjdCm3Oa+LcmWYZtlW2vbY0e1C7VbZ9dk93qWyayEWdtmdcz6bu9sn25/yP6xg7pDsMM6hxaHt45WjhzHMsc7TmSnAKfVTs1Ob2Zbz+bN3jf7gTPFea7zBuc2528uri5ilzqXQVcT10TXPa736Rr0cPom+hU3jJuv22q3M26f3V3cpe7H3f/0sPVI86jxGJhjPoc359CcXk8jT7bnAU+5F80r0etnL7m3oTfbu9z7uY+xD9enwqefYclIZRxmvPa19xX7Nvh+ZLozVzJb/VB+gX4Ffl3+6v7R/qX+zwKMAlICagOGA50Dlwe2BmGCQoK2Bd1n6bE4rGrWcLBr8Mrg9hBSSGRIacjzUKtQcWjLXHhu8Nztc5/MM50nnNcUBsJYYdvDnoabhy8NPz0fOz98ftn8FxEOESsiOiIpkUsiayI/RPlGbYl6HG0RLYtui1GJWRhTHfMx1i+2KFYeNytuZdz1eJ14QXxzAi4hJqEiYWSB/4KdC/oWOi/MX3hvkfmi7EVXF+ssTl98donKEvaSE4mYxNjEmsSv7DB2OXskiZW0J2mYw+Ts4rzi+nB3cAd5nrwiXn+yZ3JR8kCKZ8r2lEG+N7+YPyRgCkoFb1KDUvenfkwLS6tMG0uPTa/PwGckZpwSqgvThO2Z+pnZmd0ia1G+SL7UfenOpcPiEHGFBJIskjRLNZABqVNmIftB1pPllVWW9WlZzLIT2WrZwuzOHKucjTn9uQG5vyxHL+csb1thuGLtip6VjJUHVkGrkla1rTZenbe6b03gmqq1xLVpa39dZ7+uaN379bHrW/L08tbk9f4Q+ENtvnK+OP/+Bo8N+39E/yj4sWuj08bdG78XcAuuFdoXFhd+3cTZdO0nh59KfhrbnLy5a4vLln1bsVuFW+9t895WVaRWlFvUu33u9sYdtB0FO97vXLLzavHs4v27iLtku+QloSXNu012b939tZRferfMt6x+j+6ejXs+7uXuvbXPZ1/dfr39hfu//Cz4+cGBwAON5WblxQexB7MOvjgUc6jjF/ov1RU6FYUV3yqFlfKqiKr2atfq6hrdmi21cK2sdvDwwsM3j/gdaa6zrTtQT60vPAqOyo6+PJZ47N7xkONtJ+gn6k6antzTQGkoaIQacxqHm/hN8ub45u5TwafaWjxaGk7bna48Y3im7Kzm2S3niOfyzo2dzz0/0ipqHbqQcqG3bUnb44txF++0z2/vuhRy6crlgMsXOxgd5694Xjlz1f3qqWv0a03XXa43djp3Nvzq/GtDl0tX4w3XG8033W62dM/pPnfL+9aF2363L99h3bl+d97d7nvR9x7cX3hf/oD7YOBh+sM3j7IejT5e8wTzpOCp6tPiZ7rPyn+z/K1e7iI/2+PX0/k88vnjXk7vq98lv3/ty3tBflHcb9BfPeA4cGYwYPDmywUv+16JXo0O5f+h9see1xavT/7p82fncNxw3xvxm7G3m95pv6t8P/t920j4yLMPGR9GPxZ80v5U9Zn+ueNL7Jf+0WVfcV9Lvll+a/ke8v3JWMbYmIgtZk+MAihkwcnJALytBIAcDwAFmSGICybn6gmBJv8FJgj8J56cvSfEBYA6RI2PR8xWAI4iy2wNACo+AIyPRlE+AHZyUqypGXhiXh8XLPLnUufO4L+LemZTDf4pk7P8X+r+pwaKqH/T/wJRFA0eTO16dQAAAIplWElmTU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAACQAAAAAQAAAJAAAAABAAOShgAHAAAAEgAAAHigAgAEAAAAAQAAAa6gAwAEAAAAAQAAAH4AAAAAQVNDSUkAAABTY3JlZW5zaG90KBfV5AAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAdZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MTI2PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjQzMDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlVzZXJDb21tZW50PlNjcmVlbnNob3Q8L2V4aWY6VXNlckNvbW1lbnQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgrXhkB8AAAAHGlET1QAAAACAAAAAAAAAD8AAAAoAAAAPwAAAD8AABEJPIGUpwAAENVJREFUeAHsnAd4FcUahv8ggdBL6B0ELCgdFLEgTa8PvUawXJoVAqFc6R1EkK7yUKSJilQpV4WLgL1RvVKUJr0EBELoJXe+ibt3c87Zwwk5SXaT73+enJ3dnd0z+86c+eaf+TchccqERgIkQAIkQAIuIRBC4XJJTbGYJEACJEACmgCFiw2BBEiABEjAVQQoXK6qLhaWBEiABEiAwsU2QAIkQAIk4CoCFC5XVRcLSwIkQAIkQOFiGyABEiABEnAVAQqXq6qLhSUBEiABEqBwsQ2QAAmQAAm4igCFy1XVxcKSAAmQAAlQuNgGSIAESIAEXEWAwuWq6mJhSYAESIAEKFxsAyRAAiRAAq4iQOFyVXWxsCRAAiRAAhQutgESIAESIAFXEaBwuaq6WFgSIAESIAEKF9sACZAACZCAqwhQuFxVXSwsCZAACZAAhYttgARIgARIwFUEKFyuqi4WlgRIgARIgMLFNkACJEACJOAqAhQuV1UXC0sCJEACJEDhYhsgARIgARJwFQHHCNeVK1cl5kKsXL58Ra5fvyFxrsLIwpIACZCAuwmEqOKHhmaULFnCJGeO7BIWltmxD+QI4ToVfUbOx8Q6FhILRgIkQALpjUCunNmlQP5wRz52qgvXseMn5eKlKxpOntw5JUf2bJIpU6iEhED/aSRAAiRAAilBIC4uTq5duy4XYi/K2XMx+iuzZQ2TIoULpsTXJ+o7UlW4DE8L7mnhgvklc+ZMiSo8M5MACZAACQSfwNWr1+T4yWi9bONEzyvVhAtrWoePntDESxQrTNEKftvjHUmABEjgjglAvA4dOa6vL160kKPWvFJNuAxvC9OD+cLz3DFcXkgCJEACJJA8BE6fOaunDZ3mdaWacB08dFSuqehBelvJ0+B4VxIgARJIKgHD68qklnNKliia1NsF7fpUE669+w7qkPeyZUowECNo1ckbkQAJkEDwCCBgY+/+Q4JQubJ3lwzejZN4p1QTrj1KuGDlHAQjiSx5OQmQAAmkOQJO7KspXGmumfGBSIAESCB4BChcFpZOhGEpHpMkQAIkQAKKgBP7anpcbJokQAIkQAK2BChcFjROhGEpHpMkQAIkQAKKgBP7anpcbJokQAIkQAK2BChcFjROhGEpHpMkQAIkQAKKgBP7anpcbJokQAIkQAK2BChcFjROhGEpHpMkQAIkQAKKgBP7anpcbJokQAIkQAK2BChcFjROhGEpHpMkQAIkQAKKgBP7anpcbJokQAIkQAK2BChcFjROhGEpHpMkQAIkQAKKgBP7anpcbJokQAIkQAK2BChcFjROhIHiXfptmy4ltpd3bI9P74g/lrVCZb2fpUIlvc0X8U+95QcJkAAJpFUCTuyr6XH93dogVGc+mSeX/hapQBshxAxCRhELlBjzkQAJuIkAhctSW06BcaeCZXkUnQxv86LeUsA8yXCfBEjAzQSc0ldbGaZrj+v0wrlyZtE8K48kpyFgFK8kY+QNSIAEHEKAwmWpiNSGkRyiZTwexcsgwS0JkIDbCaR2X+2LX7r0uA4Pikr0WpYveHbHig+fKFkfiA/ksMvD4yRAAiTgBgIULkstpRaM5PS08HipIVq3bt2S/QcOyM6du+SmShctUkTuvae85MyZ00LcO/nrf3/TBys++ID3SRccuXHjhvzw409Spkxp/cwuKHLARQxW3Vy9elXQPmCZM2eWDBkyBFyGpGS8cOGCbpPHT5yUM2fOSI7s2SU8PFweVG0tZ44cSbm1a69NrbpIKrDU6qv9lTtdeVyJES0jWtDqORmh8giT9xV9mNKidV113JMmT5F1X26Qy5cvJ6jnsLAwadOqpTz3XDsJUx2Wp02Z+q4sWbZcH27VorlEdnvdM4vj9zt2fln27tsnISEhMmLYEHn8sUcdX+ZAChjMuonq1Uc2b9mqv3bsmNHy8EM1AynCHefZoQZPn65YKRs2fiXXrl3zug+Es1KlihLZ9TW5u0wZr/Np+UBK10WwWFK4LCRTA8bvLZ60lMB3EoIV3vbF2071eYpgSovWxUuXZOCgIWan5PtpRKpVrSJvvTlKMmXKZGaJi4uTRk1bCEbFsBxqBLx6xTItAGYmhycgWBAuwx57tLaMGjHM2HXtNtC62bFjp5w9d04/Z4X775M8efL4fOaU6izh/U6bPlMWL1nqsxyeB0MzZpROnTpIRNs2kkENPNKDpVRdBJtlavTVt3uGdONxBbKudSdBFRAweGVWz+x20INxfsiwEXpUa9yrXLmygs4bntc3334nR44cNU5Ji+bNpEdkV3MfiaHDR8r6DRv1sbpP1pGhgwfqtFs+bt68KW0i2kv06dO6yL2iukvTJo3dUny/5Qykbnr16Su/bNqk7zNm9Eh5pNbDPu+ZEp0lBlG9er8hO3ftMssAz6pG9Wp6GjdP7txy+MgR2bNnr+zbf0CuX79u5oOXPHL4UHM/LSdSoi6Sgx+Fy0I1JWHcPLddYr6dK6dmx/8HDEsxzOSdiJZ5cQonMEXYqElzLVJ33XWXjB0zSnUS1ROUYv6CD2XW+3P0sfz58smHH8wVTB8aBoFbs3ad3n2qYX3JkiWLcco12+joaPns8zVStmwZqVWrVpoZuQdSN04SrgHK88dgyTCIUXc1UEK787Tz58/Lm2+Nk+9/+FFy5colkye8rcXNM19a3KdwBa9W04XHdWlbT7mhxCsk9G6J/SlOidjeBATdJFoo+KbNW6Rn73/pZ6is1gumTJqQ4HmwgwX5sW9PkMqVK0k95VGFhoZ65eEB9xJwinAtWrxU3nlvmgnyyTpPyOCB/QUDKn+2/NMVUqlixXQjWmBB4fLXIhJ3Ll0IV8zGegmo3PiroUQv+MI8ds+yDWbaDYlvv/te+g8crIuK9Y1p705NdLGPHj0qR48d19cVLVJYihYt6vMeWHP58aef5ZdfNsnJU6ekiIpYfPCBCloQER126NBhOXHypL4W05WYFjJs9+9/SExMjN6FwGKdDd+7ees22b79V71fpnQpqaM6O1+jc+M+dttdu3erdbpYffree+9JEK127Phxc7oUUYf5VETbuXPnZeu2bfovNvailCpVUmrWqKEjMO2+w/N4jFoX3L37d/MwpsMQHOJpWIPCFBqstPqe/Pnze2aRU6ei5c+DB/XxbFmzSoUK9+u0Xd1cvHhREPwAmzHzffljzx6d7tjhRbn/vvt02nofHLB2luPHjdGeOaaRtygOqANEGqJ8Tzz+uBQo4F1GfVObD3j+rVpHmGtt9evVlYH9+yZL5CK8UExt79m7T3E7JdmzZZPCqt3WeeJxVf5SNiVMePgW2rKKQt26bbucVG0W9VaoUEGpUa2aVKtW1Wc9JryDSFJ+D9a68Bcog6nUL9dvkF2qnaGN5MiRXUfN1q/3pO3v1LOcwdxPydmxQMud5oUL04QXlcflaTdjK+qpQ7d5W3gOdMoR7Z43HymqR6Q0b9rE3A8kMWv2HJn/wYc66wvPt5fOHTt4XQbRiVKeHdYmPC08PK8O+vjii7VmdCKCI7DOZpj1h7r4k49k/fqNagF/hnHa3GZXodJ9ekUJRuuJsa6RPcQIG584fpwORDGux7PhGWH9+70hBZVw9B0wyCv6Emsx7Z5tK106dQyo44JwNW3eSrDGBps1Y5qUL1dOp42PKyoEvbEKfkH4M6xJ40bSu2cP47S5fXv8RFm5+t96H+tzWKeD2dUN1pBeea2bzmP3cU/58jJz+nvmaWsdzJj2rho0bJXpM2aZ541ENiUE+H6IT6C27sv1MnzkaJ0dA5YlixcKgi6CbWv/s04mTppiDgQ871/7kUfUGu0ALcKe54z9ffv3S78Bg+XEiRPGoQTbkiVLCNYK8SqJnQXz92AnXBBVrHGePXvWqxgQ2mbqdx7V3X8b8LowiQcoXBaAKQXDmCa0fLWZjLtWUHI1/Mjcd1PCMzijSpXKOvy91sMPBTTitescDQYY4Xbv2TuBd2GcM7ZYMyterKgeBeOYP+Fq2KC+oAOyM3R4H8yfI0UKF7bL4nU8UOHCqBxrKr7Cs42bDhrQTxrUT+iZG+c8t1Yx6Kwi4154rn2CLFjvwbqPYXnz5pXlSz7xEsZWbZ/VI2rkmzDuLamuvDeYXd0kVbgQhGME5Ogv8viAiM95f0bAHsygIcPkq6+/0Xdp3y5CXu7S2eOOSd+F5zFi1Jvmu2h2d8Qa75jRI3xOicOrjezRU3vcdtfjeMECBWTqlIlSqGBBr2zB/j34Ei54k5Hdo0yBhlCFq7bz119/CbxFw9pFtJVXXu5i7Cb7NqX66sQ8SJr3uPwJV8bclSRrZe/1IV8AW4484+twoo4tHRieqPz+MmORe/DQ4Xraw5oPnWQDNWpu2aKZmgYpZD2VIG3XOSITQpv7vNHPDLVH4AY6Z0ynYEpv2/btsnLVaq/v9idcuC/eJ2vf7lntlYVlCdNiMnPWbNMLerphA+0dIW8gFqhwGfdq3OgZqV+3rhQvUVx27dot702brqZLj+nT6KwWLYz3QI38dtvl6j0leAAwTJu+O3Vygqyjx4yVL9asTXBs2jtTzKlAnDhw4E95sWN8R4/XEVYuX2KuC9nVDUb8P6spW9iCjz6W/SpCD4aQ8vJqmhaGez1Us4ZO48MqstjH2lNEm9ZqavAxyZ07l36BG9OOxrQmvJc3Rw1H1tta55deNacr8coFBk3BtK+/+VYwQDO825o1qkt9NbjAtPMJ9WLzps2bBWtsV65c0V+LyEpEKGa0eH2IZuzWvafu/JEJnlVT5QFXq1pVicEtvV786YpVegob5zFwgnhZp66T4/fgKVyYRXn19UjT08JA4Ln27QRTv7GxsbJFTa+PUx46fvew1197Rdq2bqXTyf1B4bIQTikYaVW4DJQYkaIDNsLCjeMYPWOE3V29WIzoLU+z6xyRb+NXX2tRRBrrUuPUi6vw6KwG76VP3/6yVf2gDPMnXBg9DhsySK9JGPmxXbX6M/WDjB88FC9WTEc/Ws/7SydGuHy9EoCReIdOL5kd4/Ili9R/d8jr7yv1udPqP0G0VGs7WO8A51WfLtWCgZMIimnSrKVgShFrgNjCPEfJHy9cZE6bPv1UQ+nfNz7YBnn91Q3Ow+4kOENf5+O1AbShYSNG6fuizKtXLtfp230gstV4vnmzZ0np0qV8XoIX3VesXOXznPVg3z69TXHHdGvzFq1NQdXrZ8or9nznC1Nr/1Lt0JiW9Zw2two31sKmTBrv9XtAfXaLjDIHMZiyRls1LDl+D57ChWArBF3B7F5PwXTnq2qqGGyyKkFbsWyx3+lRo/xJ3aZUX52YcqZ5j8szMMMKJ5vytu5SXlcgFgyPa/jzOaVCydBAvi5ReTCNsXjJMvlcjfKxsG81LLiPHjncax3GX+eIwA8EgMDs1r9wDsEOTVu00h049v0JV+1HaqmR/AhkS2AIOPhHo6b6GEbK69Z8FtBUJy4IVLg8PRprATp1edmc6nxn8kSpWPFB62nb9KtdIwUBGDB0csb6HIQcU6yw7t26yuy58/SL3p6ijDyG6MPDgadjmL+6MfLciXBVrVJFJk0YZ9zC3EJsGzz1jCDYAgbhCuTfMuEldiP4BlOMdv8JA69l4PWM25l1uhYeKzxXWInixWXenFmmR+p5H0QoTpw8VR/GeiPWHWFYz2qr1oIxwMDAafHCj2wDUA78qTzgDvEeMKatl6mpXWPAlxy/B6twIQCjTUQ7PR2IWYkF8+falhOzLBBSGH7Xj9b+f7vRB5Phw4nC9T8AAAD//2tnJ2UAABJJSURBVO2dB3wUxRfHXxRIaKGXgBA6CUERRbEgiihiAYFIE5IYQBARkSK9N0GqAaRKFQOYkFAUewf9C4oKCEiTUAKhh04o/3lzzjKce5tLzF0W7jd8PrezM7O7s98pb96bt8HvmgiUDWHHrr3yqZUrBnv06anf1Hd5f/9ykeRfLsplvp4RPvKYfpqp+PCIQAoLzpmpa925iJvy142/UVz8clq77kfjktKlStH8ubPJ39/fSJszdx4tXLRYnkdGtKEO7aKNvNZto+jAgQPyfPzbY+j++2oZec6RtlHRlJS0TyaPGjGMHqnzsFGke8836ZdfN8rzTh07UJvWrYw8PdLw2cZ07tw5mbRm9QrKmzevnu0y/trrb9AfmzbL/EkTxtG999Q0yvK78TtyqH3/fTRu7FtGnh7pN2CQwWr0yOFU5+GH9GyX8SVLl9G7M2bJ/IZPNaD+fXvLeMyUaRS3PIH8/Pxo+YdLaeas2fTJZ5/LvEXz51JwcFn5rs8934wuX75MuXPnplWJ8ZQrVy7jWVZtowr1fLMvrd+wQZ6OGT2SHnrwAZV1w1Fvg/bRL1FUZNsb8tVJ6zaRdODgQXka+/4CKl26tMpyeXy506u0/a+/ZP5bo0bQww89aFqWWcUvTzTNO3XqFF24eFHmTRg3hu6r5ehrkybHUMKKlTK96fONqfsbr5tez4ncV7nPcmDuX3y2hnLmyEFff/MtDRk2QqaXCw6mhfPfk3FXP+HNW9GRo0dl9sRxY6lWrXtl3BPj4e0xo+mB2vfL+3/z7Xc0eOhwGc+fPz+VL+d6Tkw5cpQOHToky776Skdq1bKFjHvyx1tzdUbewe9WF1znfutBl0/+bsrkVhNc+kt+9/0PNHT4SDk5cvprr3amFs3DjSJWk2ODhs8ak8nqlQkUKAaTqzDqrbH06T8Ts5Xgsppcn2nUhM6cOSMf4QnB1bJFc+rSuZPpKwwYNIS+/2GtzMuI4OJJnid7DoULF6aEuKVy0mzRqg0dOnyYwsKq0fSpMfTD2nXUf+BgWa5jh/bUtk1r+Tx+Lod6jz1Kw4YMknH1Y9U2qkxmBJeVcHkxIor273csVtwVXCwUWDhw4AmUJ9KMhvYdX6EdO3bKy+KXxVKxYsVkXF9QDBk0gOo/Xs/y1k3CW9Dx48dlmdjFC4kXa0s/jKNp786QaY0bPUe9erxheY9hI0bRl199Lcv06d2Lnn26oYx7Yjzogkuvp2UFnTJbt2pBnTtlnLnTbdI9heDSEHkLhpXgylGwBuW5e6JWK9fRrNC44gcWcf2ATOQcOXLEGOhml0+fOYtilyyTWU8+UZ8GDehnFLOaHPVJYN57s6hihQrGdc6Rbt170sbfHAsDK8GlD1Tne3hacL3YqiW90ull58fK88wKLr44un1H2rV7t7zPnFnTyU/844mYg1oNXxTaRKMm4XThwgUKCalKs6ZPo/ETJtHK1R/JckMHD6TH6z0m4+rHqm1UmcwILqs2yIzg0jWawMBAoWEuuUFzVHV1dTx27Dg1a96S2FKQN08eWvORQ8Pi8rpQ7Nm9Gz3fuJGr29CVK1eI+9D58+dlmXhRj2JFi0qNjTU3DsyYWVuFXr370s/rHVosjxUeMxw8PR6WJyTS5Jip8lnVw8Ko1r33yHh6PzVq3HWDlSG98pnN99ZcnZH63fIa18W/F9DFvxeaMtlTuBHVuMt6FaYuHLIoVUUtj5v3prnMzwrBxYOUV4UrVq6iLVv+pBnvTpUTotlDWesaOHiozOIB8e7Ud4xiVpNjx85daNu27bKs1aTBdXlamPmUqcfXBNe8BQtp3nxH3+rQPpoup12m+QsXSW661sJmIDYHsRkrTmgVnbt0pZSUI5QzZ05pJswjJm09WLWNKmcHwcXt37zli3T0mMOMzmbWYUMHSzOdqqfVceLkdyhxxSpZhE2drJWrMG36TFq67EN5+kT9x2nwwP4q619H7qvcZznkECZCNhXeJliv+/En6tvfIayKFGGt2LGI+9cNRMLVq1el8FMm66kxk+muO6vLop4eD2vXraN+AxxaOZsn2UxppwDBpbWGt2C4Elxrbq9Dift3U3j1pvTCnc20mmU+ysLNleCqLva2hok9rv8aeIC1bhtJyckOO3dYtVAhkGLkpOh878UfLKGZs+fI5LqP1KGRw4caRawmR558585bIMtWrVKFYiZPkHsxxsX/RBISV9Ckd6YYyb4muHbv3kMvtXdocndWDxN7V+elBlahQnma/95sg8sXX35Fw0eOludNnm9kTNa8J8TmO+dg1TaqrK4d6GYtla+O+h5XVmtc/AwWPCyAVHjwgdqyn7FQtgpsXmYzMwfej5on9mDLliljXLLhl1+pRy/HvmGBAgWINf+iRcwtFtwHuS9y4OePfWuUjJ8V+6ZNmzU3Flb6vpUsoP3o+0xsGk+IXyYXFlzE0+Mh9fRpaipMnWlpaVJj5b24UkFBWu2uR3lPNyDAn6pUrnw90cMxb83VGXmNW17jYhjODhqTzobRttQTBqfY1o5VspGQyYiVOTGrBBdXzVlgtIuOosiItnKVqarOHbxPvwF09uxZmcSmMjaZqWA1OR5OSaGIqHbSvMXla95dQ05GvHHM4aow7az55FMaN26CjMtE8eNrgovfW9+4VxxeiowgbhMVeAJtLJwx0oQzhh769XmTnm74lJ4k41ZtowqPeXs8fbzmE3n62KN1abjQdDiwFnT77bfLOP94WnDxM1gos3BWgbWG7t26Upk77lBJxvHEiRNC0MXQt999b6Q5OwdxBpsP20ZG0779+2W5smXL0JTJE6lQoULGdRzRnXD4nIUWCy8Vxk2YSKtWfyxP2RGGnY14kaEHFpKsmV26dEkmO+/XeWM8jBw9hj77/Av5/JCqVWQ92fyqh81btghh3keOywdq1xZaaD/Kly+fXsQjcQguDas3YSita1euarT6fBD9mbJVqwllida1RZgIB1uYE7PSo5AHWLcevaSpUL1I+fLl6J6ad9PVK1dp7Y8/SlOUyitYsADFLl4k9xFUWnqTIw/mPn37G5PtbbfdJlZ5laio2Dv4/Y9NdFqsEjlwOmuBHHxRcOn7iBKC+Hlv9kyqXKmiOpXH3oLlT//72Uhj4bIiIc7U8SW9tuGbfBgXT1OmTTfux16AVatUpoPJyXIfTWV4Q3Dx/l0fMfFvFB6temABUUHsjxYWwiZZ1Gv3nr/p7717DQHBZe+pWVMIm5E3eLyqe7ADTNfXuxumSNa87hNC8W6xt5MsPOt+EX1023aHVyNf83KHdhTR5kV1uTzyHiOzV/uw7L3JCzHeR7oi+i3389/FHq1aVEiNUXjHshaoB0+PB17ccFspEz0LrfCmTaiyGHPHhCl2/YZf5P4bs+YQHRVJ0S9F6lX0WNybc7W7L+ETGhcLrsR9u6Vp0BWY/2IyTE9o8TOzYn9LrztrUt2696K/duzQk/8Vr1SxIo0YPkR6WemZ7kyOvCrmTXIlmPTrOc5mL3ZIUG7Lvii4tvy5Ve5ZKTZBQSVp6Qfvq1PjuPqjj+nt8dcdgdh1n134zYI7bcMTWFS7DobJWN2nePFiFLc0Vp16RePih3EfmTt/AS16/wOpLRkVcBFhAdLp5fb0QngzUzO3uixp3z7q2q0HsaZmFdjNn939zQKzYtMqWyGsAmuKY8U+myszp6fHAy8GeUzv3LXLqprU/IVw6tqls2WZrMyE4NJoehvGiC9H/0vT0qojo5kRXu4IrZZ181CLurmdH/efz3k1mSi+dVkcu4ROnjx1w/3YLPJE/Xr0+mtdTFez7kyOfENeqcYuWSpXt7wq5fuyKYO9tNjTSzfFOO+huLvav1m9CpkPm7T4+x/loMCfHPCnB86B26dJsxcM02qPN7pJwe9cjs/dbRvWSCaL/R32hON6cKgWGiocdq7vO7rbBpnxKpQPdPrhfb/ElSuF2etL49s8vQg7SYRUDaFOHdsTf1vlTti7N4mmTZ9BP/+83uCnrmNB3Up87sAC0Cqw0wXf43NRL+VMpMpzn27Y4EnqLD6ZCNC+dVT5+tHT44H7yTtTpkpTKn/np4dCBQtSeHhTqVWyo4+3grfnanfeyyc0LgbB5kEWXu4EdwQY3y9+UwJdPV+J/vitvuVts1rbcn4YCzA2wfCHwLzyDalalcqIPQH2rMqqwPtaJ8Wqt6AYPGweVIFNlspENHfOTGIND8G7BFijOHgwmXgS98aehztvx8IhKSlJfCx7WGpLbMrkvsFm68wG9sRkbYSP+fLlpaCSJSk0NOSG/pjevdlSsXXbdjosvrUTqh6VLFGCQsVnCs6enendx9Pj4cTJk7RdmEFTxH5zQECAo57CEcvZhJlePbMiH4JLo5gdMOI2Laf4zQlaLVxHqxUPpdDiIbJAtRKhRsE/D2+lrSnbbtDeggNDaMdGx/c7RsF/Ip7Stpyfk9XnvI/Gbvc8gHSnDv05vDps3SaC2D7PA2rViuUZngD0+yEOAnYl4MvjITvm6vT6gc9oXAqEOyZDVTajxyJXn6HkPQ2My7LSk9C4qRci/JcMugtX5NRUx7dr+seY6vFsxmB3ZvWXBh6t+wiNGDZEZeMIArcMAV8fDxBcWlfOThieFF4h+RtJ0+HNKrS4idjcGN2hI+0RXmAcWJu6X/y9vwfFR6Ls4swmoBUrVxubyGz+iRGuyu7uWcib4gcEbhICvj4esnOudtVFfE7jUiAyYjZU17h7jAjrTcEFQjz6B3XdrUtmy/HGf0/xzQi7V1sF3jCePGk8lS9XzqoY8kDgpibgy+MBgkvrunaA4QnhxXtjg+q7/vM0GgLbR9kFea74k0affPoZsQOIc+Cv9wcO6AtNyxkMzm9JAr46HuwwVzt3KJ/VuBQIFl4c3HXaUNeZHd3xRjS7zu5p/F9P8Iee/N8p8J81Klq0iPwwEqZBu7cc6ucJAr42HiC4tF5kRxiZ0cCU92FW/b1DDRGiIAACIJDtBOw4V/u8xmXWK5QWxnm66zsLKRXC72wqo3qaysMRBEAABG4VAhBcWkvaEYZWPURBAARAAAQEATvO1dC40DVBAARAAARcEoDg0tDYEYZWPURBAARAAAQEATvO1dC40DVBAARAAARcEoDg0tDYEYZWPURBAARAAAQEATvO1dC40DVBAARAAARcEoDg0tDYEYZWPURBAARAAAQEATvO1dC40DVBAARAAARcEoDg0tDYEYZWPURBAARAAAQEATvO1dC40DVBAARAAARcEoDg0tDYEYZWPURBAARAAAQEATvO1dC40DVBAARAAARcEoDg0tDYEYZWPURBAARAAAQEATvO1dC40DVBAARAAARcEoDg0tDs3LWXronzShXKkp+fn5aDKAiAAAiAgB0IXLt2jXbuTiKeoStVDLZDlWQdsk3j2pt0gC6lXaaydwSRv38u2wBBRUAABEAABBwELl68REn7kylXzhwUXLa0bbBkm+BKOXKMTqWeoUIFA6lokUK2AYKKgAAIgAAIOAgcPXaCTpxMpQKB+ah4sSK2wZJtguvChYu078AhCQJal236AyoCAiAAApKA0rb4pEzpkhQQ4G8bMtkmuJiA0rpyCjU0qEQxmAxt0y1QERAAAV8mwEIr+fARShPbOXbTtrhdslVwcQUOJh+ms+cucFSaDfPny0u5cuWEw4Ykgh8QAAEQ8A4BdsS4dCmNTp85K82D/NS8eQKoVFAJ71QgA0/JdsHFdVWaVwbqjaIgAAIgAAIeJGBHTUu9ri0EF1eG97xST5+h8+cvSPWUXeURQAAEQAAEvEOAXd552yZ37gAKzJ/PVntazgRsI7icK4ZzEAABEAABEDAjAMFlRgVpIAACIAACtiUAwWXbpkHFQAAEQAAEzAhAcJlRQRoIgAAIgIBtCUBw2bZpUDEQAAEQAAEzAhBcZlSQBgIgAAIgYFsCEFy2bRpUDARAAARAwIwABJcZFaSBAAiAAAjYlgAEl22bBhUDARAAARAwIwDBZUYFaSAAAiAAArYlAMFl26ZBxUAABEAABMwIQHCZUUEaCIAACICAbQlAcNm2aVAxEAABEAABMwIQXGZUkAYCIAACIGBbAhBctm0aVAwEQAAEQMCMAASXGRWkgQAIgAAI2JYABJdtmwYVAwEQAAEQMCMAwWVGBWkgAAIgAAK2JQDBZdumQcVAAARAAATMCEBwmVFBGgiAAAiAgG0JQHDZtmlQMRAAARAAATMCEFxmVJAGAiAAAiBgWwIQXLZtGlQMBEAABEDAjMD/AQlGmZU+qyz9AAAAAElFTkSuQmCC"/>
+</defs>
+</svg>
diff --git a/style/icons/sparkr_logo.svg b/style/icons/sparkr_logo.svg
new file mode 100644
index 00000000..ade6f041
--- /dev/null
+++ b/style/icons/sparkr_logo.svg
@@ -0,0 +1,32 @@
+<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_3526_17649)">
+<rect width="52" height="52" fill="white"/>
+<g clip-path="url(#clip1_3526_17649)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M43.3194 39.9948C43.2831 39.9173 43.2667 39.8783 43.2472 39.8411C42.7223 38.8428 42.1988 37.8445 41.6685 36.8462C41.6154 36.7461 41.6221 36.687 41.6948 36.6007C42.53 35.6253 43.3544 34.6446 44.191 33.6733C44.2205 33.639 44.2469 33.6027 44.2579 33.5416C44.0151 33.6049 43.7722 33.6675 43.528 33.7318C42.5201 33.9989 41.5041 34.2647 40.5056 34.5358C40.4118 34.5611 40.3693 34.5337 40.3221 34.4557C39.75 33.4992 39.1739 32.5401 38.595 31.5958C38.5655 31.5462 38.5332 31.4982 38.4701 31.4568C38.4237 31.7118 38.3767 31.9654 38.3311 32.2204C38.1705 33.1202 38.01 34.0146 37.8508 34.9184C37.8333 35.0154 37.8096 35.1127 37.8012 35.2098C37.7933 35.3026 37.7454 35.3369 37.6609 35.3636C36.4722 35.7373 35.2862 36.1137 34.0988 36.4901C34.0467 36.5064 33.9956 36.528 33.9396 36.5785C34.9111 36.9644 35.8826 37.3502 36.8675 37.7428C36.8316 37.7712 36.8079 37.7934 36.7816 37.8102C36.1744 38.2028 35.5659 38.5954 34.9601 38.9893C34.8873 39.0368 34.8299 39.0435 34.7482 39.0067C34.0223 38.6802 33.291 38.3591 32.5624 38.0354C32.2359 37.8897 31.9431 37.6968 31.7151 37.4162C31.1983 36.7848 31.3008 36.0671 31.989 35.6219C32.2143 35.4776 32.4707 35.371 32.7257 35.2874C33.8901 34.9083 35.0599 34.5427 36.2203 34.1758C36.3182 34.1446 36.3633 34.1009 36.3822 33.995C36.5387 33.0939 36.6993 32.1873 36.8625 31.2969C36.9494 30.8153 36.9953 30.3229 37.2295 29.8805C37.3193 29.7105 37.4265 29.5445 37.5561 29.4029C38.0202 28.8876 38.6665 28.8687 39.1617 29.3587C39.3277 29.5246 39.4693 29.7188 39.5921 29.9199C40.1318 30.8008 40.6635 31.6871 41.1978 32.5774C41.2605 32.6823 41.3169 32.7033 41.4325 32.6728C42.7346 32.3234 44.0366 31.9794 45.3454 31.6354C45.6153 31.5643 45.8865 31.5386 46.1631 31.5902C46.7635 31.7025 47.0266 32.1595 46.8229 32.741C46.7307 33.0054 46.5706 33.228 46.3911 33.4398C45.4817 34.5095 44.5696 35.5847 43.6656 36.6505C43.5912 36.7379 43.5898 36.8002 43.6413 36.8987C44.1864 37.9253 44.7261 38.9492 45.2739 39.988C45.4036 40.2335 45.5033 40.4898 45.506 40.7717C45.5123 41.4139 45.0419 41.94 44.4036 42.0344C44.0461 42.0867 43.7142 42.0103 43.3782 41.907C42.5619 41.6547 41.7456 41.4065 40.9225 41.1596C40.8465 41.1369 40.8175 41.1068 40.8039 41.0252C40.71 40.4492 40.6069 39.8745 40.5071 39.2985C40.5045 39.2827 40.5092 39.2658 40.5119 39.2305C41.4443 39.4881 42.3739 39.7418 43.3319 40.0075" fill="#E25A1C"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M41.7003 48.4264C40.9636 48.4253 40.2295 48.4217 39.4874 48.4249C39.3899 48.4249 39.335 48.397 39.281 48.3141C38.4094 46.9867 37.5269 45.6565 36.6634 44.3345C36.6355 44.2924 36.6059 44.2518 36.5564 44.1807C36.3689 45.6107 36.184 47.0137 35.9992 48.4167H34.0697C34.0924 48.2292 34.1125 48.047 34.1362 47.8663C34.3238 46.4228 34.514 44.9928 34.7029 43.5494C34.8837 42.1733 35.0632 40.7973 35.2467 39.4213C35.2519 39.3807 35.2793 39.3285 35.3125 39.3069C35.9764 38.8739 36.6429 38.4449 37.3094 38.0159C37.319 38.0095 37.3327 38.009 37.3669 37.999C37.1659 39.5369 36.9662 41.0613 36.7651 42.5857C36.7731 42.591 36.7809 42.5968 36.7889 42.6021C37.8346 41.4446 38.8803 40.2817 39.9462 39.1081C39.9768 39.2861 40.0037 39.4386 40.03 39.591C40.1049 40.0268 40.1771 40.4652 40.2567 40.9009C40.273 40.9889 40.2492 41.0426 40.1908 41.1046C39.5148 41.8115 38.8415 42.5211 38.1669 43.2361C38.1374 43.2673 38.1094 43.2994 38.0757 43.3363C38.0973 43.3705 38.1158 43.4038 38.1374 43.4343C39.2991 45.0666 40.4581 46.6989 41.6185 48.3313C41.6395 48.3608 41.67 48.384 41.6959 48.4103V48.4361" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8976 44.1093C18.868 43.9596 18.847 43.7383 18.78 43.5319C18.4562 42.535 17.4348 41.994 16.3783 42.2355C15.2166 42.5053 14.3814 43.4186 14.2735 44.6098C14.1844 45.4867 14.6566 46.3366 15.5337 46.6468C16.2407 46.9018 16.9234 46.7952 17.5576 46.4162C18.3955 45.9143 18.8488 45.1683 18.9069 44.1093H18.8976ZM13.7973 47.8192C13.7404 48.2481 13.6856 48.6515 13.6327 49.0562C13.5621 49.5931 13.491 50.1314 13.4235 50.6751C13.4157 50.7378 13.3967 50.7657 13.3292 50.7652C12.799 50.7631 12.2687 50.7637 11.7371 50.7626C11.7249 50.7626 11.7128 50.7563 11.6843 50.7479C11.7164 50.4943 11.7476 50.2393 11.7808 49.9857C11.8978 49.0953 12.0142 48.205 12.133 47.3146C12.2692 46.2947 12.3758 45.2776 12.5499 44.2658C12.8575 42.4716 14.3849 40.9337 16.1659 40.5425C17.2008 40.3185 18.1899 40.4223 19.0939 41.0052C19.9952 41.5853 20.5106 42.4352 20.632 43.4874C20.7994 44.9848 20.2475 46.2259 19.1883 47.2647C18.4894 47.9406 17.6501 48.3696 16.6922 48.5153C15.6977 48.665 14.7627 48.4995 13.9261 47.9136C13.8955 47.892 13.8623 47.8731 13.8097 47.8399" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9065 39.3201C12.3128 39.7626 11.7394 40.1889 11.1659 40.6165C11.0732 40.4708 10.9905 40.3252 10.8934 40.1889C10.6438 39.8395 10.3334 39.5791 9.88278 39.5481C9.50768 39.5217 9.18656 39.645 8.93154 39.9218C8.70352 40.1687 8.67383 40.5208 8.88675 40.8C9.12152 41.1076 9.37788 41.399 9.64099 41.6836C10.0768 42.1558 10.5315 42.6091 10.9633 43.0866C11.3559 43.5183 11.6703 44.0013 11.7675 44.5975C11.8829 45.299 11.7428 45.9601 11.4112 46.5806C10.7973 47.7206 9.83259 48.3883 8.55078 48.5907C7.98678 48.6817 7.42548 48.6639 6.87768 48.502C6.14772 48.2876 5.63904 47.8086 5.31252 47.1395C5.19715 46.9007 5.10878 46.6485 5.00488 46.3922C5.64309 46.0509 6.26511 45.7176 6.89387 45.3817C6.91546 45.4339 6.93124 45.4777 6.95135 45.5193C7.05888 45.7338 7.14699 45.9618 7.27922 46.1601C7.67456 46.747 8.31142 46.925 8.95232 46.635C9.11828 46.5601 9.27885 46.4556 9.41512 46.3342C9.8307 45.9645 9.90896 45.4492 9.60132 44.9851C9.42457 44.718 9.20329 44.4779 8.9874 44.2391C8.47198 43.6671 7.93497 43.1127 7.43574 42.5258C7.08897 42.1238 6.85285 41.6571 6.77729 41.1228C6.69566 40.5347 6.81318 39.9816 7.11866 39.4905C7.8783 38.2615 8.98066 37.6288 10.4379 37.6693C11.2704 37.6994 11.9356 38.0889 12.4483 38.7364C12.6008 38.928 12.7438 39.1263 12.899 39.334" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M26.9932 45.8631C26.8962 46.6037 26.8043 47.3066 26.7098 48.0081C26.7046 48.0439 26.6766 48.0924 26.6466 48.1061C25.2028 48.7726 23.3138 48.6795 22.14 47.3385C21.5058 46.6181 21.24 45.7602 21.2791 44.8158C21.3713 42.6169 23.1951 40.6878 25.3809 40.418C26.656 40.2615 27.7692 40.6055 28.6327 41.597C29.221 42.2716 29.4935 43.081 29.4531 43.9713C29.4267 44.5595 29.3344 45.1437 29.2615 45.7251C29.1576 46.5534 29.0442 47.3844 28.9349 48.2073C28.9307 48.2362 28.9249 48.2657 28.9181 48.3032H27.2045C27.2271 48.113 27.2482 47.9268 27.2725 47.7407C27.3969 46.7842 27.5342 45.8251 27.6435 44.8672C27.7115 44.271 27.6688 43.6787 27.3953 43.127C27.1052 42.5402 26.6194 42.2285 25.9785 42.1611C24.6535 42.0208 23.3879 42.9408 23.1181 44.2386C22.9319 45.0993 23.2256 45.9249 23.9114 46.3836C24.5793 46.8274 25.2877 46.8287 26.0163 46.5414C26.386 46.3957 26.699 46.1637 26.9986 45.852" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M34.2793 40.6421C34.2007 41.2384 34.1228 41.8252 34.0445 42.4228C33.6802 42.4228 33.324 42.4208 32.9678 42.4234C32.679 42.4255 32.4159 42.6122 32.3282 42.882C32.2945 42.9874 32.2819 43.1006 32.2671 43.2112C32.0863 44.5872 31.9068 45.9497 31.7274 47.3123C31.6821 47.6576 31.6383 48.003 31.593 48.3537H29.7985C29.8317 48.0907 29.8632 47.8357 29.8964 47.5807C30.0129 46.6931 30.1298 45.8 30.2472 44.9231C30.349 44.1555 30.4402 43.3852 30.5603 42.6163C30.7208 41.591 31.7409 40.6737 32.7731 40.6332C33.2669 40.6116 33.7621 40.6296 34.2708 40.6296" fill="#3C3A3E"/>
+<path d="M43.8591 48.4262V47.6707H43.8549L43.5581 48.4262H43.4638L43.1669 47.6707H43.1622V48.4262H43.0138V47.521H43.2445L43.5144 48.209L43.7802 47.521H44.0096V48.4262H43.8591ZM42.5449 47.6424V48.4262H42.3965V47.6424H42.1132V47.5211H42.8296V47.6424H42.5463" fill="#3C3A3E"/>
+<path d="M15.295 38.0521H15.7389L15.634 37.3803L15.295 38.0521ZM15.8091 38.4865H15.071L14.8363 38.9465H14.3127L15.4664 36.7881H15.971L16.3636 38.9465H15.8793L15.8096 38.4865" fill="#3C3A3E"/>
+<path d="M18.4523 37.2295H18.1892L18.1007 37.7272H18.3638C18.523 37.7272 18.6485 37.623 18.6485 37.4264C18.6485 37.2962 18.5694 37.2295 18.4515 37.2295H18.4523ZM17.8154 36.7951H18.5063C18.8679 36.7951 19.1202 37.0109 19.1202 37.3805C19.1202 37.8473 18.791 38.1603 18.3214 38.1603H18.0232L17.8829 38.9495H17.4323L17.8127 36.791" fill="#3C3A3E"/>
+<path d="M20.6516 38.0521H21.0955L20.9907 37.3803L20.6516 38.0521ZM21.1643 38.4865H20.4263L20.1915 38.9465H19.668L20.8216 36.7881H21.3262L21.7189 38.9465H21.2345L21.1649 38.4865" fill="#3C3A3E"/>
+<path d="M24.0248 38.8751C23.8723 38.9447 23.705 38.9863 23.5363 38.9863C22.9656 38.9863 22.608 38.5586 22.608 38.0231C22.608 37.3378 23.1842 36.7617 23.8696 36.7617C24.041 36.7617 24.1961 36.8029 24.323 36.8723L24.2597 37.389C24.1648 37.2842 24.0114 37.2109 23.8225 37.2109C23.4299 37.2109 23.0804 37.5657 23.0804 37.9772C23.0804 38.2902 23.2774 38.5357 23.5877 38.5357C23.778 38.5357 23.9615 38.4624 24.0856 38.3617L24.0256 38.8743" fill="#3C3A3E"/>
+<path d="M26.8582 38.0791H25.9205L25.768 38.9465H25.3187L25.6992 36.7881H26.1499L25.9988 37.6407H26.9365L27.089 36.7881H27.5396L27.1591 38.9465H26.7085L26.8596 38.0791" fill="#3C3A3E"/>
+<path d="M28.4774 38.9426L28.8579 36.7842H30.0534L29.9774 37.2186H29.2326L29.1566 37.6368H29.8407L29.7648 38.0712H29.0807L29.0047 38.5055H29.7495L29.6736 38.9399H28.4781" fill="#3C3A3E"/>
+</g>
+<rect x="-28" y="-3" width="107" height="21" fill="#F4F6FB"/>
+<path d="M24.9558 8.33582H25.5295C26.0918 8.33582 26.5068 8.24211 26.7745 8.05469C27.0423 7.86727 27.1761 7.57275 27.1761 7.17114C27.1761 6.77336 27.0385 6.49032 26.7631 6.32202C26.4915 6.15373 26.0688 6.06958 25.4951 6.06958H24.9558V8.33582ZM24.9558 9.78162V13.0002H23.1772V4.6123H25.6213C26.7611 4.6123 27.6045 4.82076 28.1515 5.23767C28.6984 5.65076 28.9719 6.27995 28.9719 7.12524C28.9719 7.61865 28.8361 8.05851 28.5646 8.44482C28.293 8.82731 27.9086 9.12756 27.4114 9.34558C28.6736 11.2312 29.4959 12.4495 29.8784 13.0002H27.9048L25.9025 9.78162H24.9558Z" fill="black"/>
+<path d="M25.065 25.4977C25.065 26.0682 25.0104 26.5721 24.901 27.0093C24.7948 27.4434 24.6338 27.8093 24.4182 28.107C24.2057 28.4016 23.9415 28.6248 23.6257 28.7767C23.3129 28.9256 22.95 29 22.5371 29C22.1271 29 21.7643 28.924 21.4484 28.7721C21.1326 28.6202 20.8669 28.3969 20.6513 28.1023C20.4357 27.8047 20.2733 27.4372 20.164 27C20.0547 26.5628 20 26.0589 20 25.4884C20 24.7318 20.0957 24.0946 20.287 23.5767C20.4813 23.0589 20.7667 22.6667 21.1433 22.4C21.5228 22.1333 21.9874 22 22.5371 22C23.0897 22 23.5528 22.1318 23.9263 22.3953C24.3028 22.6589 24.5867 23.0512 24.7781 23.5721C24.9694 24.0899 25.065 24.7318 25.065 25.4977ZM21.6489 25.4977C21.6489 25.9597 21.6823 26.3457 21.7491 26.6558C21.8159 26.9659 21.9146 27.1984 22.0451 27.3535C22.1787 27.5054 22.3427 27.5814 22.5371 27.5814C22.7375 27.5814 22.9014 27.507 23.029 27.3581C23.1596 27.2093 23.2552 26.9814 23.3159 26.6744C23.3797 26.3643 23.4116 25.9721 23.4116 25.4977C23.4116 24.7783 23.3387 24.2481 23.193 23.907C23.0502 23.5628 22.8316 23.3907 22.5371 23.3907C22.3427 23.3907 22.1787 23.4698 22.0451 23.6279C21.9146 23.7829 21.8159 24.0171 21.7491 24.3302C21.6823 24.6403 21.6489 25.0295 21.6489 25.4977Z" fill="#3C3A3E"/>
+<path d="M31 28.907H29.1507L27.4153 24.6791H27.3789C27.3971 24.9271 27.4108 25.1581 27.4199 25.3721C27.432 25.5829 27.4396 25.7752 27.4427 25.9488C27.4487 26.1194 27.4518 26.2698 27.4518 26.4V28.907H25.9805V22.107H27.8161L29.5424 26.2186H29.5789C29.5667 25.9891 29.5561 25.7752 29.547 25.5767C29.5379 25.3783 29.5303 25.1953 29.5242 25.0279C29.5182 24.8605 29.5151 24.7085 29.5151 24.5721V22.107H31V28.907Z" fill="#3C3A3E"/>
+</g>
+<defs>
+<clipPath id="clip0_3526_17649">
+<rect width="52" height="52" fill="white"/>
+</clipPath>
+<clipPath id="clip1_3526_17649">
+<rect width="42" height="21.8077" fill="white" transform="translate(5 29)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/sparksql_logo.svg b/style/icons/sparksql_logo.svg
new file mode 100644
index 00000000..0a412d08
--- /dev/null
+++ b/style/icons/sparksql_logo.svg
@@ -0,0 +1,26 @@
+<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="52" height="52" fill="white"/>
+<g clip-path="url(#clip0_3526_17630)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M30.9412 25.9583C30.9119 25.897 30.8987 25.8661 30.8829 25.8367C30.4591 25.0473 30.0364 24.2579 29.6082 23.4686C29.5653 23.3894 29.5707 23.3427 29.6295 23.2744C30.3038 22.5032 30.9695 21.7277 31.645 20.9597C31.6688 20.9326 31.6901 20.9039 31.699 20.8556C31.5029 20.9056 31.3068 20.9551 31.1096 21.006C30.2958 21.2172 29.4754 21.4273 28.6692 21.6417C28.5935 21.6617 28.5592 21.64 28.521 21.5784C28.0591 20.8221 27.5939 20.0636 27.1265 19.3169C27.1026 19.2778 27.0766 19.2398 27.0256 19.207C26.9881 19.4087 26.9502 19.6092 26.9134 19.8108C26.7837 20.5223 26.6541 21.2295 26.5255 21.9442C26.5115 22.0209 26.4923 22.0978 26.4856 22.1747C26.4791 22.248 26.4404 22.2751 26.3722 22.2963C25.4124 22.5917 24.4548 22.8894 23.496 23.187C23.454 23.1999 23.4127 23.2169 23.3675 23.2569C24.1519 23.562 24.9363 23.8671 25.7316 24.1775C25.7027 24.2 25.6835 24.2175 25.6622 24.2309C25.172 24.5413 24.6806 24.8517 24.1914 25.1632C24.1327 25.2007 24.0863 25.206 24.0204 25.1769C23.4343 24.9188 22.8438 24.6649 22.2554 24.4089C21.9918 24.2937 21.7554 24.1411 21.5713 23.9193C21.154 23.42 21.2368 22.8525 21.7924 22.5005C21.9744 22.3864 22.1814 22.3021 22.3873 22.236C23.3275 21.9362 24.2721 21.6472 25.209 21.357C25.2881 21.3324 25.3245 21.2978 25.3398 21.2141C25.4661 20.5015 25.5958 19.7847 25.7276 19.0806C25.7978 18.6998 25.8348 18.3105 26.0239 17.9606C26.0964 17.8262 26.183 17.695 26.2876 17.583C26.6624 17.1755 27.1842 17.1606 27.5841 17.548C27.7181 17.6792 27.8325 17.8328 27.9316 17.9917C28.3674 18.6883 28.7967 19.3891 29.2281 20.0932C29.2788 20.1761 29.3243 20.1927 29.4177 20.1686C30.469 19.8923 31.5203 19.6203 32.5771 19.3483C32.795 19.2921 33.014 19.2717 33.2374 19.3125C33.7222 19.4013 33.9346 19.7627 33.7701 20.2225C33.6957 20.4315 33.5664 20.6075 33.4215 20.775C32.6872 21.6209 31.9507 22.4711 31.2207 23.3138C31.1607 23.3829 31.1595 23.4322 31.2011 23.5101C31.6413 24.3218 32.0771 25.1315 32.5194 25.9529C32.6241 26.147 32.7046 26.3497 32.7068 26.5726C32.7119 27.0804 32.332 27.4964 31.8167 27.5711C31.528 27.6123 31.26 27.552 30.9887 27.4703C30.3296 27.2708 29.6704 27.0745 29.0058 26.8793C28.9445 26.8614 28.9211 26.8376 28.9101 26.7731C28.8342 26.3176 28.751 25.8631 28.6704 25.4077C28.6683 25.3952 28.6721 25.3818 28.6743 25.3539C29.4271 25.5576 30.1778 25.7582 30.9513 25.9683" fill="#E25A1C"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M29.6337 32.6256C29.0389 32.6247 28.4462 32.6218 27.847 32.6244C27.7682 32.6244 27.7239 32.6023 27.6803 32.5368C26.9765 31.4872 26.264 30.4354 25.5667 29.39C25.5442 29.3567 25.5203 29.3246 25.4803 29.2684C25.3289 30.3991 25.1796 31.5085 25.0304 32.6179H23.4724C23.4907 32.4696 23.5069 32.3256 23.5261 32.1827C23.6776 31.0413 23.8312 29.9105 23.9837 28.7692C24.1297 27.6811 24.2746 26.5931 24.4228 25.505C24.427 25.4729 24.4491 25.4316 24.4759 25.4146C25.012 25.0721 25.5502 24.7329 26.0884 24.3937C26.0961 24.3887 26.1071 24.3883 26.1348 24.3804C25.9724 25.5964 25.8112 26.8018 25.6489 28.0072C25.6553 28.0114 25.6616 28.016 25.668 28.0201C26.5124 27.1049 27.3567 26.1854 28.2174 25.2573C28.2421 25.3981 28.2638 25.5187 28.2851 25.6392C28.3455 25.9838 28.4038 26.3304 28.4681 26.675C28.4813 26.7445 28.4621 26.787 28.4149 26.8361C27.8691 27.395 27.3255 27.9561 26.7807 28.5215C26.7569 28.5461 26.7343 28.5715 26.7071 28.6007C26.7245 28.6277 26.7394 28.6541 26.7569 28.6782C27.6949 29.9689 28.6308 31.2596 29.5677 32.5503C29.5847 32.5737 29.6093 32.5921 29.6302 32.6129V32.6332" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2218 29.212C11.1979 29.0936 11.1809 28.9187 11.1269 28.7555C10.8654 27.9672 10.0407 27.5394 9.18764 27.7304C8.2496 27.9437 7.57521 28.6659 7.48806 29.6078C7.41615 30.3012 7.79747 30.9732 8.50563 31.2185C9.07651 31.4201 9.62779 31.3359 10.1398 31.0361C10.8164 30.6393 11.1825 30.0494 11.2293 29.212H11.2218ZM7.10358 32.1455C7.0576 32.4847 7.01337 32.8037 6.97066 33.1237C6.91368 33.5482 6.85627 33.9739 6.8018 34.4038C6.79548 34.4534 6.78012 34.4754 6.72564 34.475C6.29748 34.4734 5.86931 34.4738 5.44006 34.473C5.43025 34.473 5.42045 34.468 5.39746 34.4614C5.42339 34.2608 5.44856 34.0592 5.47536 33.8587C5.56982 33.1546 5.66384 32.4506 5.75971 31.7466C5.86975 30.9401 5.95582 30.1358 6.09636 29.3358C6.34476 27.9171 7.57805 26.701 9.01616 26.3916C9.85178 26.2146 10.6504 26.2966 11.3803 26.7575C12.1081 27.2162 12.5243 27.8883 12.6223 28.7203C12.7574 29.9043 12.3118 30.8857 11.4566 31.7071C10.8922 32.2415 10.2146 32.5807 9.44105 32.6959C8.63811 32.8143 7.8831 32.6835 7.20763 32.2202C7.18289 32.2031 7.15609 32.1882 7.1136 32.1619" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.38434 25.4249C5.90497 25.7748 5.44194 26.1119 4.97892 26.4501C4.90407 26.3349 4.83729 26.2197 4.75884 26.1119C4.55729 25.8356 4.30671 25.6298 3.94283 25.6052C3.63995 25.5843 3.38066 25.6818 3.17474 25.9007C2.99062 26.0959 2.96665 26.3743 3.13857 26.5951C3.32814 26.8383 3.53514 27.0688 3.74759 27.2938C4.09949 27.6672 4.46665 28.0256 4.81528 28.4032C5.13232 28.7446 5.38616 29.1265 5.46461 29.5979C5.55787 30.1526 5.44467 30.6753 5.17698 31.166C4.68127 32.0674 3.9023 32.5954 2.86729 32.7554C2.41189 32.8274 1.95867 32.8133 1.51634 32.6853C0.926937 32.5157 0.516204 32.137 0.252551 31.608C0.159401 31.4191 0.0880401 31.2197 0.00415039 31.017C0.519473 30.7471 1.02172 30.4836 1.52942 30.218C1.54685 30.2593 1.5596 30.2939 1.57583 30.3268C1.66266 30.4964 1.7338 30.6767 1.84057 30.8335C2.15979 31.2975 2.67402 31.4383 3.19152 31.209C3.32553 31.1498 3.45518 31.0671 3.56521 30.9711C3.90077 30.6788 3.96396 30.2714 3.71556 29.9044C3.57284 29.6932 3.39416 29.5033 3.21985 29.3145C2.80367 28.8622 2.37006 28.4238 1.96695 27.9598C1.68696 27.6419 1.4963 27.2728 1.43529 26.8504C1.36937 26.3853 1.46427 25.948 1.71092 25.5597C2.3243 24.5879 3.2144 24.0876 4.39104 24.1196C5.06324 24.1434 5.60035 24.4514 6.01436 24.9634C6.13747 25.1149 6.25295 25.2717 6.37824 25.4359" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.7585 30.5983C17.6801 31.184 17.6059 31.7397 17.5297 32.2944C17.5254 32.3228 17.5029 32.3611 17.4786 32.372C16.3128 32.8989 14.7876 32.8253 13.8397 31.765C13.3277 31.1954 13.113 30.5169 13.1446 29.7702C13.219 28.0315 14.6917 26.5061 16.4566 26.2927C17.4862 26.169 18.385 26.441 19.0823 27.2251C19.5573 27.7584 19.7774 28.3984 19.7447 29.1025C19.7234 29.5676 19.6489 30.0295 19.59 30.4892C19.5061 31.1442 19.4146 31.8013 19.3263 32.452C19.3229 32.4748 19.3183 32.4982 19.3127 32.5278H17.9291C17.9474 32.3774 17.9644 32.2302 17.984 32.083C18.0844 31.3267 18.1953 30.5683 18.2836 29.8109C18.3385 29.3394 18.304 28.8711 18.0831 28.4348C17.8489 27.9708 17.4567 27.7244 16.9392 27.6711C15.8693 27.5601 14.8474 28.2876 14.6295 29.3138C14.4791 29.9944 14.7163 30.6472 15.2701 31.0099C15.8094 31.3608 16.3814 31.3619 16.9697 31.1347C17.2682 31.0195 17.5209 30.836 17.7628 30.5896" fill="#3C3A3E"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M23.6417 26.4705C23.5783 26.942 23.5153 27.406 23.4522 27.8786C23.158 27.8786 22.8704 27.877 22.5828 27.879C22.3496 27.8807 22.1372 28.0284 22.0663 28.2417C22.0391 28.325 22.029 28.4145 22.017 28.502C21.871 29.59 21.7261 30.6674 21.5812 31.7448C21.5446 32.0179 21.5093 32.291 21.4727 32.5683H20.0237C20.0505 32.3603 20.076 32.1587 20.1028 31.9571C20.1968 31.2552 20.2913 30.549 20.386 29.8556C20.4682 29.2487 20.5418 28.6396 20.6388 28.0316C20.7684 27.2209 21.5921 26.4955 22.4255 26.4635C22.8243 26.4464 23.2241 26.4606 23.6349 26.4606" fill="#3C3A3E"/>
+<path d="M31.3771 32.6254V32.0281H31.3737L31.134 32.6254H31.0579L30.8182 32.0281H30.8144V32.6254H30.6945V31.9097H30.8808L31.0987 32.4537L31.3133 31.9097H31.4985V32.6254H31.3771ZM30.3159 32.0057V32.6254H30.1961V32.0057H29.9673V31.9098H30.5458V32.0057H30.317" fill="#3C3A3E"/>
+<path d="M8.31291 24.4224H8.67135L8.58659 23.8911L8.31291 24.4224ZM8.72801 24.7658H8.13206L7.94249 25.1296H7.51978L8.45128 23.4229H8.85874L9.17578 25.1296H8.78466L8.72844 24.7658" fill="#3C3A3E"/>
+<path d="M10.8622 23.7715H10.6498L10.5783 24.1651H10.7907C10.9193 24.1651 11.0206 24.0826 11.0206 23.9272C11.0206 23.8243 10.9568 23.7715 10.8616 23.7715H10.8622ZM10.348 23.428H10.9058C11.1978 23.428 11.4015 23.5987 11.4015 23.891C11.4015 24.26 11.1357 24.5075 10.7565 24.5075H10.5158L10.4025 25.1316H10.0386L10.3458 23.4248" fill="#3C3A3E"/>
+<path d="M12.638 24.4224H12.9964L12.9118 23.8911L12.638 24.4224ZM13.052 24.7658H12.456L12.2665 25.1296H11.8438L12.7753 23.4229H13.1827L13.4998 25.1296H13.1086L13.0524 24.7658" fill="#3C3A3E"/>
+<path d="M15.3617 25.073C15.2386 25.128 15.1035 25.1609 14.9673 25.1609C14.5065 25.1609 14.2178 24.8227 14.2178 24.3992C14.2178 23.8573 14.683 23.4019 15.2364 23.4019C15.3748 23.4019 15.5001 23.4344 15.6025 23.4893L15.5514 23.8979C15.4748 23.815 15.3509 23.7571 15.1984 23.7571C14.8814 23.7571 14.5992 24.0376 14.5992 24.363C14.5992 24.6104 14.7583 24.8046 15.0088 24.8046C15.1625 24.8046 15.3106 24.7467 15.4109 24.667L15.3624 25.0723" fill="#3C3A3E"/>
+<path d="M17.6496 24.4437H16.8924L16.7693 25.1296H16.4065L16.7137 23.4229H17.0776L16.9556 24.097H17.7128L17.8359 23.4229H18.1998L17.8925 25.1296H17.5287L17.6507 24.4437" fill="#3C3A3E"/>
+<path d="M18.957 25.1262L19.2643 23.4194H20.2295L20.1682 23.7629H19.5668L19.5055 24.0936H20.0578L19.9965 24.4371H19.4441L19.3828 24.7806H19.9842L19.9229 25.124H18.9576" fill="#3C3A3E"/>
+</g>
+<path d="M38.3735 30.2685C38.3735 30.9604 38.1221 31.5056 37.6193 31.904C37.12 32.3024 36.4239 32.5016 35.5308 32.5016C34.7081 32.5016 33.9803 32.3478 33.3474 32.0403V30.5306C33.8678 30.7612 34.3073 30.9237 34.6659 31.0181C35.028 31.1125 35.3585 31.1596 35.6574 31.1596C36.016 31.1596 36.2903 31.0915 36.4801 30.9552C36.6735 30.8189 36.7702 30.6162 36.7702 30.3471C36.7702 30.1968 36.728 30.064 36.6436 29.9487C36.5592 29.8299 36.4344 29.7163 36.2692 29.608C36.1074 29.4996 35.7752 29.3267 35.2724 29.089C34.8013 28.8689 34.4479 28.6574 34.2123 28.4547C33.9768 28.252 33.7887 28.0161 33.648 27.7471C33.5074 27.478 33.4371 27.1634 33.4371 26.8035C33.4371 26.1255 33.6674 25.5926 34.128 25.2047C34.5921 24.8167 35.232 24.6228 36.0477 24.6228C36.4485 24.6228 36.83 24.67 37.1921 24.7643C37.5578 24.8587 37.9393 24.9915 38.3366 25.1627L37.8092 26.4261C37.3978 26.2583 37.0568 26.1412 36.786 26.0748C36.5188 26.0084 36.2551 25.9752 35.9949 25.9752C35.6855 25.9752 35.4482 26.0469 35.283 26.1902C35.1177 26.3334 35.0351 26.5204 35.0351 26.7511C35.0351 26.8943 35.0685 27.0202 35.1353 27.1285C35.2021 27.2333 35.3076 27.3364 35.4517 27.4378C35.5994 27.5356 35.9457 27.7139 36.4907 27.9725C37.2115 28.3149 37.7055 28.6592 37.9727 29.0051C38.2399 29.3476 38.3735 29.7687 38.3735 30.2685Z" fill="black"/>
+<path d="M46.2044 28.5543C46.2044 29.456 46.0427 30.2126 45.7192 30.8241C45.3992 31.4322 44.9281 31.8778 44.3058 32.1609L46.1622 34.221H44.0685L42.655 32.5016H42.5337C41.3524 32.5016 40.4452 32.1609 39.8124 31.4794C39.1795 30.7979 38.8631 29.8194 38.8631 28.5438C38.8631 27.2683 39.1795 26.295 39.8124 25.624C40.4488 24.9495 41.3594 24.6123 42.5443 24.6123C43.7292 24.6123 44.6345 24.9513 45.2604 25.6293C45.8897 26.3072 46.2044 27.2823 46.2044 28.5543ZM40.5771 28.5543C40.5771 29.4105 40.7406 30.0553 41.0676 30.4886C41.3946 30.922 41.8833 31.1387 42.5337 31.1387C43.8382 31.1387 44.4904 30.2772 44.4904 28.5543C44.4904 26.8279 43.8417 25.9648 42.5443 25.9648C41.8938 25.9648 41.4033 26.1832 41.0728 26.62C40.7423 27.0534 40.5771 27.6981 40.5771 28.5543Z" fill="black"/>
+<path d="M47.2372 32.3968V24.7329H48.8721V31.0548H51.9996V32.3968H47.2372Z" fill="black"/>
+<defs>
+<clipPath id="clip0_3526_17630">
+<rect width="33.913" height="17.2439" fill="white" transform="translate(0 17.2646)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/start_cluster_icon.svg b/style/icons/start_cluster_icon.svg
new file mode 100644
index 00000000..2714727e
--- /dev/null
+++ b/style/icons/start_cluster_icon.svg
@@ -0,0 +1,3 @@
+<svg width="11" height="12" viewBox="0 0 11 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0L11 6L0 12V0Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/start_cluster_icon_disable.svg b/style/icons/start_cluster_icon_disable.svg
new file mode 100644
index 00000000..31d184f7
--- /dev/null
+++ b/style/icons/start_cluster_icon_disable.svg
@@ -0,0 +1,4 @@
+<svg width="11" height="12" viewBox="0 0 11 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0L11 6L0 12V0Z" fill="#8A8A8A"/>
+</svg>
+
diff --git a/style/icons/start_icon.svg b/style/icons/start_icon.svg
new file mode 100644
index 00000000..7ba44086
--- /dev/null
+++ b/style/icons/start_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2375)">
+<path d="M7 12.5L12.5 9L7 5.5V12.5ZM9 17C7.90278 17 6.86806 16.7917 5.89583 16.375C4.92361 15.9583 4.06944 15.3889 3.33333 14.6667C2.61111 13.9306 2.04167 13.0764 1.625 12.1042C1.20833 11.1319 1 10.0972 1 9C1 7.88889 1.20833 6.85417 1.625 5.89583C2.04167 4.92361 2.61111 4.07639 3.33333 3.35417C4.06944 2.61805 4.92361 2.04167 5.89583 1.625C6.86806 1.20833 7.90278 0.999999 9 0.999999C10.1111 0.999999 11.1458 1.20833 12.1042 1.625C13.0764 2.04167 13.9236 2.61805 14.6458 3.35417C15.3819 4.07639 15.9583 4.92361 16.375 5.89583C16.7917 6.85417 17 7.88889 17 9C17 10.0972 16.7917 11.1319 16.375 12.1042C15.9583 13.0764 15.3819 13.9306 14.6458 14.6667C13.9236 15.3889 13.0764 15.9583 12.1042 16.375C11.1458 16.7917 10.1111 17 9 17ZM9 15.5C10.8056 15.5 12.3403 14.8681 13.6042 13.6042C14.8681 12.3403 15.5 10.8056 15.5 9C15.5 7.19444 14.8681 5.65972 13.6042 4.39583C12.3403 3.13194 10.8056 2.5 9 2.5C7.19444 2.5 5.65972 3.13194 4.39583 4.39583C3.13194 5.65972 2.5 7.19444 2.5 9C2.5 10.8056 3.13194 12.3403 4.39583 13.6042C5.65972 14.8681 7.19444 15.5 9 15.5Z" fill="#444746"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2375">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/start_icon_disable.svg b/style/icons/start_icon_disable.svg
new file mode 100644
index 00000000..2b649a6f
--- /dev/null
+++ b/style/icons/start_icon_disable.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2375)">
+<path d="M7 12.5L12.5 9L7 5.5V12.5ZM9 17C7.90278 17 6.86806 16.7917 5.89583 16.375C4.92361 15.9583 4.06944 15.3889 3.33333 14.6667C2.61111 13.9306 2.04167 13.0764 1.625 12.1042C1.20833 11.1319 1 10.0972 1 9C1 7.88889 1.20833 6.85417 1.625 5.89583C2.04167 4.92361 2.61111 4.07639 3.33333 3.35417C4.06944 2.61805 4.92361 2.04167 5.89583 1.625C6.86806 1.20833 7.90278 0.999999 9 0.999999C10.1111 0.999999 11.1458 1.20833 12.1042 1.625C13.0764 2.04167 13.9236 2.61805 14.6458 3.35417C15.3819 4.07639 15.9583 4.92361 16.375 5.89583C16.7917 6.85417 17 7.88889 17 9C17 10.0972 16.7917 11.1319 16.375 12.1042C15.9583 13.0764 15.3819 13.9306 14.6458 14.6667C13.9236 15.3889 13.0764 15.9583 12.1042 16.375C11.1458 16.7917 10.1111 17 9 17ZM9 15.5C10.8056 15.5 12.3403 14.8681 13.6042 13.6042C14.8681 12.3403 15.5 10.8056 15.5 9C15.5 7.19444 14.8681 5.65972 13.6042 4.39583C12.3403 3.13194 10.8056 2.5 9 2.5C7.19444 2.5 5.65972 3.13194 4.39583 4.39583C3.13194 5.65972 2.5 7.19444 2.5 9C2.5 10.8056 3.13194 12.3403 4.39583 13.6042C5.65972 14.8681 7.19444 15.5 9 15.5Z" fill="#8A8A8A"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2375">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/stop_cluster_disable_icon.svg b/style/icons/stop_cluster_disable_icon.svg
new file mode 100644
index 00000000..321dba6e
--- /dev/null
+++ b/style/icons/stop_cluster_disable_icon.svg
@@ -0,0 +1,3 @@
+<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="10" height="10" fill="#8A8A8A"/>
+</svg>
diff --git a/style/icons/stop_cluster_icon.svg b/style/icons/stop_cluster_icon.svg
new file mode 100644
index 00000000..e609a9d1
--- /dev/null
+++ b/style/icons/stop_cluster_icon.svg
@@ -0,0 +1,3 @@
+<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="10" height="10" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/stop_disable_icon.svg b/style/icons/stop_disable_icon.svg
new file mode 100644
index 00000000..7ca5f8cb
--- /dev/null
+++ b/style/icons/stop_disable_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2376)">
+<path d="M6 12H12V6H6V12ZM9 17C7.90278 17 6.86806 16.7917 5.89583 16.375C4.92361 15.9583 4.06944 15.3889 3.33333 14.6667C2.61111 13.9306 2.04167 13.0764 1.625 12.1042C1.20833 11.1319 1 10.0972 1 9C1 7.88889 1.20833 6.85417 1.625 5.89583C2.04167 4.92361 2.61111 4.07639 3.33333 3.35417C4.06944 2.61805 4.92361 2.04167 5.89583 1.625C6.86806 1.20833 7.90278 0.999999 9 0.999999C10.1111 0.999999 11.1458 1.20833 12.1042 1.625C13.0764 2.04167 13.9236 2.61805 14.6458 3.35417C15.3819 4.07639 15.9583 4.92361 16.375 5.89583C16.7917 6.85417 17 7.88889 17 9C17 10.0972 16.7917 11.1319 16.375 12.1042C15.9583 13.0764 15.3819 13.9306 14.6458 14.6667C13.9236 15.3889 13.0764 15.9583 12.1042 16.375C11.1458 16.7917 10.1111 17 9 17ZM9 15.5C10.8056 15.5 12.3403 14.8681 13.6042 13.6042C14.8681 12.3403 15.5 10.8056 15.5 9C15.5 7.19444 14.8681 5.65972 13.6042 4.39583C12.3403 3.13194 10.8056 2.5 9 2.5C7.19444 2.5 5.65972 3.13194 4.39583 4.39583C3.13194 5.65972 2.5 7.19444 2.5 9C2.5 10.8056 3.13194 12.3403 4.39583 13.6042C5.65972 14.8681 7.19444 15.5 9 15.5Z" fill="#8A8A8A"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2376">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/stop_icon.svg b/style/icons/stop_icon.svg
new file mode 100644
index 00000000..03acd479
--- /dev/null
+++ b/style/icons/stop_icon.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2376)">
+<path d="M6 12H12V6H6V12ZM9 17C7.90278 17 6.86806 16.7917 5.89583 16.375C4.92361 15.9583 4.06944 15.3889 3.33333 14.6667C2.61111 13.9306 2.04167 13.0764 1.625 12.1042C1.20833 11.1319 1 10.0972 1 9C1 7.88889 1.20833 6.85417 1.625 5.89583C2.04167 4.92361 2.61111 4.07639 3.33333 3.35417C4.06944 2.61805 4.92361 2.04167 5.89583 1.625C6.86806 1.20833 7.90278 0.999999 9 0.999999C10.1111 0.999999 11.1458 1.20833 12.1042 1.625C13.0764 2.04167 13.9236 2.61805 14.6458 3.35417C15.3819 4.07639 15.9583 4.92361 16.375 5.89583C16.7917 6.85417 17 7.88889 17 9C17 10.0972 16.7917 11.1319 16.375 12.1042C15.9583 13.0764 15.3819 13.9306 14.6458 14.6667C13.9236 15.3889 13.0764 15.9583 12.1042 16.375C11.1458 16.7917 10.1111 17 9 17ZM9 15.5C10.8056 15.5 12.3403 14.8681 13.6042 13.6042C14.8681 12.3403 15.5 10.8056 15.5 9C15.5 7.19444 14.8681 5.65972 13.6042 4.39583C12.3403 3.13194 10.8056 2.5 9 2.5C7.19444 2.5 5.65972 3.13194 4.39583 4.39583C3.13194 5.65972 2.5 7.19444 2.5 9C2.5 10.8056 3.13194 12.3403 4.39583 13.6042C5.65972 14.8681 7.19444 15.5 9 15.5Z" fill="#444746"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2376">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/stop_icon_disable.svg b/style/icons/stop_icon_disable.svg
new file mode 100644
index 00000000..7ca5f8cb
--- /dev/null
+++ b/style/icons/stop_icon_disable.svg
@@ -0,0 +1,10 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1227_2376)">
+<path d="M6 12H12V6H6V12ZM9 17C7.90278 17 6.86806 16.7917 5.89583 16.375C4.92361 15.9583 4.06944 15.3889 3.33333 14.6667C2.61111 13.9306 2.04167 13.0764 1.625 12.1042C1.20833 11.1319 1 10.0972 1 9C1 7.88889 1.20833 6.85417 1.625 5.89583C2.04167 4.92361 2.61111 4.07639 3.33333 3.35417C4.06944 2.61805 4.92361 2.04167 5.89583 1.625C6.86806 1.20833 7.90278 0.999999 9 0.999999C10.1111 0.999999 11.1458 1.20833 12.1042 1.625C13.0764 2.04167 13.9236 2.61805 14.6458 3.35417C15.3819 4.07639 15.9583 4.92361 16.375 5.89583C16.7917 6.85417 17 7.88889 17 9C17 10.0972 16.7917 11.1319 16.375 12.1042C15.9583 13.0764 15.3819 13.9306 14.6458 14.6667C13.9236 15.3889 13.0764 15.9583 12.1042 16.375C11.1458 16.7917 10.1111 17 9 17ZM9 15.5C10.8056 15.5 12.3403 14.8681 13.6042 13.6042C14.8681 12.3403 15.5 10.8056 15.5 9C15.5 7.19444 14.8681 5.65972 13.6042 4.39583C12.3403 3.13194 10.8056 2.5 9 2.5C7.19444 2.5 5.65972 3.13194 4.39583 4.39583C3.13194 5.65972 2.5 7.19444 2.5 9C2.5 10.8056 3.13194 12.3403 4.39583 13.6042C5.65972 14.8681 7.19444 15.5 9 15.5Z" fill="#8A8A8A"/>
+</g>
+<defs>
+<clipPath id="clip0_1227_2376">
+<rect width="18" height="18" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/submit_job_icon.svg b/style/icons/submit_job_icon.svg
new file mode 100644
index 00000000..edcf6988
--- /dev/null
+++ b/style/icons/submit_job_icon.svg
@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 0.25H1.75C0.9175 0.25 0.25 0.925 0.25 1.75V12.25C0.25 13.075 0.9175 13.75 1.75 13.75H12.25C13.075 13.75 13.75 13.075 13.75 12.25V1.75C13.75 0.925 13.075 0.25 12.25 0.25ZM10.75 7.75H7.75V10.75H6.25V7.75H3.25V6.25H6.25V3.25H7.75V6.25H10.75V7.75Z" fill="#3367D6"/>
+</svg>
diff --git a/style/icons/succeeded_icon.svg b/style/icons/succeeded_icon.svg
new file mode 100644
index 00000000..737b7f6f
--- /dev/null
+++ b/style/icons/succeeded_icon.svg
@@ -0,0 +1,3 @@
+<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7 0.5C3.136 0.5 0 3.636 0 7.5C0 11.364 3.136 14.5 7 14.5C10.864 14.5 14 11.364 14 7.5C14 3.636 10.864 0.5 7 0.5ZM5.6 11L2.1 7.5L3.087 6.513L5.6 9.019L10.913 3.706L11.9 4.7L5.6 11Z" fill="#188038"/>
+</svg>
diff --git a/style/icons/view_logs_icon.svg b/style/icons/view_logs_icon.svg
new file mode 100644
index 00000000..c46cf96e
--- /dev/null
+++ b/style/icons/view_logs_icon.svg
@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12.25H1.75V1.75H7V0.25H1.75C0.9175 0.25 0.25 0.925 0.25 1.75V12.25C0.25 13.075 0.9175 13.75 1.75 13.75H12.25C13.075 13.75 13.75 13.075 13.75 12.25V7H12.25V12.25ZM8.5 0.25V1.75H11.1925L3.82 9.1225L4.8775 10.18L12.25 2.8075V5.5H13.75V0.25H8.5Z" fill="#3367D6"/>
+</svg>
diff --git a/style/index.css b/style/index.css
new file mode 100644
index 00000000..79ebe613
--- /dev/null
+++ b/style/index.css
@@ -0,0 +1,10 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto);
+@import url('base.css');
+@import url('cluster.css');
+@import url('job.css');
+@import url('clusterDetails.css');
+@import url('submitJob.css');
+@import url('batches.css');
+@import url('authLogin.css');
+@import url('sessionDetails.css');
+@import url('popup.css');
diff --git a/style/index.js b/style/index.js
new file mode 100644
index 00000000..b41cb5bb
--- /dev/null
+++ b/style/index.js
@@ -0,0 +1,26 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import './base.css';
+import './cluster.css';
+import './job.css';
+import './clusterDetails.css';
+import './submitJob.css';
+import './batches.css';
+import './authLogin.css';
+import './sessionDetails.css';
+import './popup.css'
diff --git a/style/job.css b/style/job.css
new file mode 100644
index 00000000..8dec6163
--- /dev/null
+++ b/style/job.css
@@ -0,0 +1,250 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto);
+.cluster-details-value-job {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 18px;
+  width: 65%;
+  color: #3367D6;
+  text-decoration: underline;
+  cursor: pointer;
+  height: 29px;
+}
+
+.job-label-style {
+  background-color: rgba(0, 0, 0, 0.08);
+  padding: 0px 10px;
+  border-radius: 10px;
+  color: rgb(0, 0, 0);
+  font-weight: 400;
+  line-height: 20px;
+  margin: 3px 0px;
+  margin-right: 5px;
+}
+ 
+.job-argument-style {
+  padding: 2px;
+}
+
+.edit-input-style {
+  margin-right: 20px;
+  width: 90%;
+}
+
+.key-message-wrapper {
+  width: 90%;
+}
+
+.job-label-edit-parent {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 18px;
+  width: 50%;
+  color: rgba(0, 0, 0, 0.66);
+  padding: 10px;
+}
+
+.job-label-edit-row {
+  display: flex;
+  margin: 5px 0px;
+  padding-bottom: 18px;
+}
+
+.job-label-style-parent {
+  display: flex;
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 18px;
+  width: 65%;
+  flex-wrap: wrap;
+  color: rgba(0, 0, 0, 0.66);
+}
+
+.job-edit-header {
+  padding: 0px 15px;
+  height: 48px;
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+}
+
+.job-save-button-style {
+  margin-right: 10px;
+  background: #3367D6;
+  color: #ffffff;
+  height: 30px;
+  font-size: 13px;
+  width: 55px;
+  cursor: pointer;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 4px;
+}
+
+.job-button-style-parent {
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  min-height: 55px;
+}
+
+.job-cancel-button-style {
+  margin-right: 10px;
+  background: white;
+  color: #000000;
+  height: 30px;
+  width: 75px;
+  font-size: 13px;
+  cursor: pointer;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 4px;
+  font-weight: 600;
+}
+.job-cancel-button-style:hover {
+  background-color: #D3D3D3;
+}
+
+.job-add-label-button-disabled {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0px 12px;
+  gap: 4px;
+
+  width: 110px;
+  height: 32px;
+
+  background: #FFFFFF;
+  color:rgba(0, 0, 0, 0.54);
+}
+
+.job-add-label-button {
+  cursor: pointer;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0px 12px;
+  gap: 4px;
+
+  width: 110px;
+  height: 32px;
+
+  background: #FFFFFF;
+  box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.12), 0px 2px 2px rgba(0, 0, 0, 0.2), 0px 1px 1px rgba(0, 0, 0, 0.14);
+  border-radius: 4px;
+}
+
+.job-edit-button {
+  cursor: pointer;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0px 12px;
+  gap: 4px;
+
+  width: 75px;
+  height: 32px;
+
+  background: #FFFFFF;
+  box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.12), 0px 2px 2px rgba(0, 0, 0, 0.2), 0px 1px 1px rgba(0, 0, 0, 0.14);
+  border-radius: 4px;
+}
+
+.job-edit-button-disabled {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0px 12px;
+  gap: 4px;
+  color:rgba(0, 0, 0, 0.54);
+
+  width: 75px;
+  height: 32px;
+
+  background: #FFFFFF;
+  opacity: 0.5;
+}
+
+.job-edit-text-disabled {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 32px;
+  /* identical to box height, or 246% */
+
+  display: flex;
+  align-items: center;
+  text-align: center;
+  text-transform: uppercase;
+
+  /* Quick Selection/Blue 700 */
+
+  color:rgba(0, 0, 0, 0.54);
+}
+
+.color-icon {
+  color: #3367D6;
+  display: flex;
+}
+
+.color-icon-disabled {
+  color:rgba(0, 0, 0, 0.54);
+  display: flex;
+}
+
+.job-edit-text {
+  font-style: normal;
+  font-weight: 500;
+  font-size: 13px;
+  line-height: 32px;
+  /* identical to box height, or 246% */
+
+  display: flex;
+  align-items: center;
+  text-align: center;
+  text-transform: uppercase;
+
+  /* Quick Selection/Blue 700 */
+
+  color: #3367D6;
+}
+
+.job-delete-icon{
+  margin-top: 6px;
+  cursor: pointer;
+}
+
+.job-label-style-list {
+  background-color: rgba(0, 0, 0, 0.08);
+  padding: 0px 10px;
+  width: fit-content;
+  border-radius: 10px;
+  color: rgb(0, 0, 0);
+  font-weight: 400;
+  line-height: 20px;
+  margin: 3px 0px;
+}
+
+.jobs-list-table-parent {
+  height: 63vh;
+  overflow: auto;
+}
+
+.jobs-list-table-parent-small {
+  height: 49vh;
+  overflow: auto;
+}
+.job-details-label-row {
+  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
+  display: flex;
+  justify-content: flex-start;
+  align-items: flex-start;
+  min-height: 18px;
+  margin-bottom: 6px;
+}
\ No newline at end of file
diff --git a/style/popup.css b/style/popup.css
new file mode 100644
index 00000000..30724074
--- /dev/null
+++ b/style/popup.css
@@ -0,0 +1,34 @@
+.popup-main{
+    padding: 25px 25px;
+}
+.popup-header{
+    font-weight: bold;
+    font-size: 18px;
+    line-height: 30px;
+    padding-bottom: 15px;
+}
+.popup-text{
+    font-weight: 500;
+    font-size: 14px;
+    line-height: 30px;
+    padding-bottom: 15px;
+}
+.popup-button-style {
+    margin-right: 10px;
+    color: #3367d6;
+    height: 30px;
+    width: 75px;
+    font-size: 13px;
+    cursor: pointer;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-radius: 4px;
+    font-weight: 600;
+  }
+  .popup-buttons{
+    display: flex;
+    margin-left:auto;
+    margin-right: 0;
+    justify-content: right;
+  }
\ No newline at end of file
diff --git a/style/sessionDetails.css b/style/sessionDetails.css
new file mode 100644
index 00000000..59aab0ed
--- /dev/null
+++ b/style/sessionDetails.css
@@ -0,0 +1,68 @@
+.session-details-label {
+    font-style: normal;
+    font-weight: 500;
+    font-size: 13px;
+    line-height: 18px;
+    width: 35%;
+    color: #000000;
+    overflow-wrap: anywhere;
+    padding-left : 15px;
+}
+.session-env-details-label{
+    font-style: normal;
+    font-weight: 500;
+    font-size: 13px;
+    line-height: 18px;
+    width: 35%;
+    color: #000000;
+    overflow-wrap: anywhere;
+    padding-left: 35px;
+}
+
+.session-details-value {
+    font-style: normal;
+    font-weight: 400;
+    font-size: 13px;
+    line-height: 18px;
+    width: 65%;
+    color: rgba(0, 0, 0, 0.66);
+    padding-left: 20px;
+}
+
+.session-env-details-value {
+    font-style: normal;
+    font-weight: 400;
+    font-size: 13px;
+    line-height: 18px;
+    width: 65%;
+    color: rgba(0, 0, 0, 0.66);
+    padding-left: 20px;
+}
+.session-detail-status-parent{
+    display: flex;
+    align-items: center;
+    height: 29px;
+    color: rgba(0, 0, 0, 0.66);
+    padding-left: 20px;
+}
+.session-label-style-parent {
+    display: flex;
+    font-style: normal;
+    font-weight: 400;
+    font-size: 13px;
+    line-height: 18px;
+    width: 65%;
+    color: rgba(0, 0, 0, 0.66);
+    padding-left: 20px;
+    padding-top: 5px;
+    padding-bottom: 5px;
+}
+.session-loader{
+    display: flex;
+    align-items: end;
+}
+
+  .session-list-table-parent {
+    height: 70vh;
+    overflow: auto;
+  }
\ No newline at end of file
diff --git a/style/submitJob.css b/style/submitJob.css
new file mode 100644
index 00000000..6e973364
--- /dev/null
+++ b/style/submitJob.css
@@ -0,0 +1,213 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto);
+
+.submit-job-container {
+  margin-left: 15px;
+  margin-bottom: 15px;
+  display: flex;
+  flex-direction: column;
+}
+
+.submit-job-label-header {
+  margin-top: 15px;
+  font-style: normal;
+  font-weight: 500;
+  font-size: 20px;
+  line-height: 28px;
+  display: flex;
+  align-items: center;
+  color: #000000;
+}
+
+.job-add-property-button-disabled {
+  margin-top: 15px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0px 12px;
+  gap: 4px;
+
+  width: 150px;
+  height: 32px;
+
+  background: #ffffff;
+  color: rgba(0, 0, 0, 0.54);
+}
+
+.job-add-property-button {
+  margin-top: 15px;
+  cursor: pointer;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 0px 12px;
+  gap: 4px;
+
+  width: 150px;
+  height: 32px;
+
+  background: #ffffff;
+  box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.12), 0px 2px 2px rgba(0, 0, 0, 0.2),
+    0px 1px 1px rgba(0, 0, 0, 0.14);
+  border-radius: 4px;
+}
+
+.select-job-style {
+  background: #ffffff;
+  border: 1px solid black !important;
+  border-radius: 4px;
+  width: 50%;
+  margin-top: 10px;
+  min-height: 40px;
+}
+.react-tagsinput-input {
+  font-family: 'Roboto' !important;
+  color: black !important;
+  font-style: normal;
+  font-weight: 400;
+  background: transparent;
+  border: 0;
+  font-size: 13px;
+  margin-bottom: 10px !important;
+  margin-top: 10px !important;
+  outline: none !important;
+  padding: 0px !important;
+  width: 95% !important;
+  margin-left: 15px;
+}
+
+.react-tagsinput-tag {
+  border: 1px solid #d3d3d3 !important;
+  display: inline-block;
+  font-family: 'Roboto' !important;
+  font-size: 13px;
+  font-weight: 400;
+  margin-bottom: 5px;
+  margin-right: 5px;
+  padding: 5px;
+  background-color: lightgrey !important;
+  border-radius: 20px !important;
+  color: black !important;
+  margin-top: 5px !important;
+}
+.react-tagsinput-remove {
+  border-radius: 50% !important;
+  padding: 4px !important;
+  margin-left: 4px !important;
+  background-color: grey !important;
+  padding-right: 6px !important;
+}
+
+a {
+  color: black !important;
+}
+a:hover {
+  color: black !important;
+}
+.submit-job-cluster-message {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 13px;
+  line-height: 10px;
+  padding-top: 10px;
+  padding-left: 5px;
+  color: #000000;
+}
+.submit-job-learn-more {
+  color: #3367d6;
+  text-decoration: underline;
+  cursor: pointer;
+  padding-left: 3px;
+}
+
+.submit-job-message {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 10px;
+  line-height: 15px;
+  padding-left: 5px;
+  color: #575757;
+  width: 50%;
+  overflow: hidden;
+  word-wrap: break-word;
+}
+
+.submit-job-message-with-link {
+  font-style: normal;
+  font-weight: 400;
+  font-size: 10px;
+  line-height: 15px;
+  padding-left: 5px;
+  color: #575757;
+  width: 50%;
+  overflow: hidden;
+  display: flex;
+  word-wrap: break-word;
+}
+.jobType-select {
+  box-sizing: border-box;
+  background: #ffffff !important;
+  border: 1px solid rgba(0, 0, 0, 0.38) !important;
+  display: block !important;
+  height: 40px;
+  width: 100%;
+  border-radius: 4px !important;
+  appearance: none !important;
+  -webkit-appearance: none !important;
+  -moz-appearance: none;
+  transform: none !important;
+  position: unset !important;
+}
+.jobType-overlay {
+  position: relative;
+  margin-top: 20px;
+  width: 50%;
+}
+.submit-button-disable-style {
+  margin-right: 10px;
+  background: white;
+  color: #575757;
+  height: 30px;
+  font-size: 13px;
+  width: 70px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 4px;
+  font-weight: 600;
+}
+.submit-button-style {
+  margin-right: 10px;
+  background: #3367d6;
+  color: #ffffff;
+  height: 30px;
+  width: 70px;
+  font-size: 13px;
+  cursor: pointer;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 4px;
+}
+
+.error-key-parent {
+  padding-top: 5px;
+  display: flex;
+  align-items: center;
+  width: 90%;
+}
+
+.error-key-missing {
+  color: #d50000;
+  padding-left: 5px;
+  font-size: 10px;
+}
+
+/* Highlight input field when focus */
+.ui.input>input:focus {
+  border: 3px solid #3367D6 !important;
+}
+
+/* Highlight tags input field when focus */
+.react-tagsinput--focused{
+  border: 3px solid #3367D6 !important;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..8906c2ce
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,24 @@
+{
+  "compilerOptions": {
+    "allowSyntheticDefaultImports": true,
+    "composite": true,
+    "declaration": true,
+    "esModuleInterop": true,
+    "incremental": true,
+    "jsx": "react",
+    "module": "esnext",
+    "moduleResolution": "node",
+    "noEmitOnError": true,
+    "noImplicitAny": true,
+    "noUnusedLocals": true,
+    "preserveWatchOutput": true,
+    "resolveJsonModule": true,
+    "outDir": "lib",
+    "rootDir": "src",
+    "strict": true,
+    "strictNullChecks": true,
+    "target": "ES2018",
+    "types": ["jest"]
+  },
+  "include": ["src/*", "src/**/*"]
+}


Test patch:
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 00000000..c25f8e97
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,22 @@
+# Copyright 2023 Google LLC
+#
+# 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
+#
+# https://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.
+
+import pytest
+
+pytest_plugins = ("pytest_jupyter.jupyter_server", )
+
+
+@pytest.fixture
+def jp_server_config(jp_server_config):
+    return {"ServerApp": {"jpserver_extensions": {"dataproc_plugin": True}}}
diff --git a/dataproc_plugin/tests/__init__.py b/dataproc_plugin/tests/__init__.py
new file mode 100644
index 00000000..0c04cc36
--- /dev/null
+++ b/dataproc_plugin/tests/__init__.py
@@ -0,0 +1,15 @@
+# Copyright 2023 Google LLC
+#
+# 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
+#
+# https://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.
+
+"""Python unit tests for dataproc_plugin."""
diff --git a/dataproc_plugin/tests/test_handlers.py b/dataproc_plugin/tests/test_handlers.py
new file mode 100644
index 00000000..da92e477
--- /dev/null
+++ b/dataproc_plugin/tests/test_handlers.py
@@ -0,0 +1,27 @@
+# Copyright 2023 Google LLC
+#
+# 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
+#
+# https://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.
+
+import json
+
+
+async def test_get_example(jp_fetch):
+    # When
+    response = await jp_fetch("dataproc-plugin", "get-example")
+
+    # Then
+    assert response.code == 200
+    payload = json.loads(response.body)
+    assert payload == {
+        "data": "This is /dataproc-plugin/get-example endpoint!"
+    }
\ No newline at end of file
diff --git a/tsconfig.test.json b/tsconfig.test.json
new file mode 100644
index 00000000..1c66acf6
--- /dev/null
+++ b/tsconfig.test.json
@@ -0,0 +1,3 @@
+{
+  "extends": "./tsconfig"
+}
diff --git a/ui-tests/README.md b/ui-tests/README.md
new file mode 100644
index 00000000..dbe6e8aa
--- /dev/null
+++ b/ui-tests/README.md
@@ -0,0 +1,167 @@
+# Integration Testing
+
+This folder contains the integration tests of the extension.
+
+They are defined using [Playwright](https://playwright.dev/docs/intro) test runner
+and [Galata](https://github.com/jupyterlab/jupyterlab/tree/master/galata) helper.
+
+The Playwright configuration is defined in [playwright.config.js](./playwright.config.js).
+
+The JupyterLab server configuration to use for the integration test is defined
+in [jupyter_server_test_config.py](./jupyter_server_test_config.py).
+
+The default configuration will produce video for failing tests and an HTML report.
+
+> There is a new experimental UI mode that you may fall in love with; see [that video](https://www.youtube.com/watch?v=jF0yA-JLQW0).
+
+## Run the tests
+
+> All commands are assumed to be executed from the root directory
+
+To run the tests, you need to:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Execute the [Playwright](https://playwright.dev/docs/intro) tests:
+
+```sh
+cd ./ui-tests
+jlpm playwright test
+```
+
+Test results will be shown in the terminal. In case of any test failures, the test report
+will be opened in your browser at the end of the tests execution; see
+[Playwright documentation](https://playwright.dev/docs/test-reporters#html-reporter)
+for configuring that behavior.
+
+## Update the tests snapshots
+
+> All commands are assumed to be executed from the root directory
+
+If you are comparing snapshots to validate your tests, you may need to update
+the reference snapshots stored in the repository. To do that, you need to:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Execute the [Playwright](https://playwright.dev/docs/intro) command:
+
+```sh
+cd ./ui-tests
+jlpm playwright test -u
+```
+
+> Some discrepancy may occurs between the snapshots generated on your computer and
+> the one generated on the CI. To ease updating the snapshots on a PR, you can
+> type `please update playwright snapshots` to trigger the update by a bot on the CI.
+> Once the bot has computed new snapshots, it will commit them to the PR branch.
+
+## Create tests
+
+> All commands are assumed to be executed from the root directory
+
+To create tests, the easiest way is to use the code generator tool of playwright:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Start the server:
+
+```sh
+cd ./ui-tests
+jlpm start
+```
+
+4. Execute the [Playwright code generator](https://playwright.dev/docs/codegen) in **another terminal**:
+
+```sh
+cd ./ui-tests
+jlpm playwright codegen localhost:8888
+```
+
+## Debug tests
+
+> All commands are assumed to be executed from the root directory
+
+To debug tests, a good way is to use the inspector tool of playwright:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Execute the Playwright tests in [debug mode](https://playwright.dev/docs/debug):
+
+```sh
+cd ./ui-tests
+jlpm playwright test --debug
+```
+
+## Upgrade Playwright and the browsers
+
+To update the web browser versions, you must update the package `@playwright/test`:
+
+```sh
+cd ./ui-tests
+jlpm up "@playwright/test"
+jlpm playwright install
+```
diff --git a/ui-tests/jupyter_server_test_config.py b/ui-tests/jupyter_server_test_config.py
new file mode 100644
index 00000000..9dca5f84
--- /dev/null
+++ b/ui-tests/jupyter_server_test_config.py
@@ -0,0 +1,26 @@
+# Copyright 2023 Google LLC
+#
+# 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
+#
+# https://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.
+
+"""Server configuration for integration tests.
+
+!! Never use this configuration in production because it
+opens the server to the world and provide access to JupyterLab
+JavaScript objects through the global window variable.
+"""
+from jupyterlab.galata import configure_jupyter_server
+
+configure_jupyter_server(c)
+
+# Uncomment to set server log level to debug level
+# c.ServerApp.log_level = "DEBUG"
diff --git a/ui-tests/package.json b/ui-tests/package.json
new file mode 100644
index 00000000..2e32aa75
--- /dev/null
+++ b/ui-tests/package.json
@@ -0,0 +1,15 @@
+{
+  "name": "dataproc_plugin-ui-tests",
+  "version": "1.0.0",
+  "description": "JupyterLab dataproc_plugin Integration Tests",
+  "private": true,
+  "scripts": {
+    "start": "jupyter lab --config jupyter_server_test_config.py",
+    "test": "jlpm playwright test",
+    "test:update": "jlpm playwright test --update-snapshots"
+  },
+  "devDependencies": {
+    "@jupyterlab/galata": "^5.0.0",
+    "@playwright/test": "^1.32.0"
+  }
+}
diff --git a/ui-tests/playwright.config.js b/ui-tests/playwright.config.js
new file mode 100644
index 00000000..c3a3109d
--- /dev/null
+++ b/ui-tests/playwright.config.js
@@ -0,0 +1,31 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+/**
+ * Configuration for Playwright using default from @jupyterlab/galata
+ */
+const baseConfig = require('@jupyterlab/galata/lib/playwright-config');
+
+module.exports = {
+  ...baseConfig,
+  webServer: {
+    command: 'jlpm start',
+    url: 'http://localhost:8888/lab',
+    timeout: 120 * 1000,
+    reuseExistingServer: !process.env.CI
+  }
+};
diff --git a/ui-tests/tests/dataproc_plugin.spec.ts b/ui-tests/tests/dataproc_plugin.spec.ts
new file mode 100644
index 00000000..6c350c26
--- /dev/null
+++ b/ui-tests/tests/dataproc_plugin.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ *
+ * 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.
+ */
+
+import { expect, test } from '@jupyterlab/galata';
+
+/**
+ * Don't load JupyterLab webpage before running the tests.
+ * This is required to ensure we capture all log messages.
+ */
+test.use({ autoGoto: false });
+
+test('should emit an activation console message', async ({ page }) => {
+  const logs: string[] = [];
+
+  page.on('console', message => {
+    logs.push(message.text());
+  });
+
+  await page.goto();
+
+  expect(
+    logs.filter(s => s === 'JupyterLab extension dataproc_plugin is activated!')
+  ).toHaveLength(1);
+});
diff --git a/ui-tests/yarn.lock b/ui-tests/yarn.lock
new file mode 100644
index 00000000..e69de29b