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

Issue:
Bump webpack-dev-middleware from 6.1.1 to 6.1.3
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 6.1.1 to 6.1.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/webpack/webpack-dev-middleware/releases">webpack-dev-middleware's releases</a>.</em></p>
<blockquote>
<h2>v6.1.3</h2>
<h3><a href="https://github.com/webpack/webpack-dev-middleware/compare/v6.1.2...v6.1.3">6.1.3</a> (2024-03-29)</h3>
<h3>Bug Fixes</h3>
<ul>
<li>security problem (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1799">#1799</a>) (<a href="https://github.com/webpack/webpack-dev-middleware/commit/5a4d9e8d6ab587b4137fdec885c36b79b10b29f7">5a4d9e8</a>)</li>
</ul>
<h2>v6.1.2</h2>
<h3><a href="https://github.com/webpack/webpack-dev-middleware/compare/v6.1.1...v6.1.2">6.1.2</a> (2024-03-20)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><strong>security:</strong> do not allow to read files above (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1778">#1778</a>) (<a href="https://github.com/webpack/webpack-dev-middleware/commit/9670b3495da518fe667ff3428c5e4cb9f2f3d353">9670b34</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/webpack/webpack-dev-middleware/blob/v6.1.3/CHANGELOG.md">webpack-dev-middleware's changelog</a>.</em></p>
<blockquote>
<h3><a href="https://github.com/webpack/webpack-dev-middleware/compare/v6.1.2...v6.1.3">6.1.3</a> (2024-03-29)</h3>
<h3>Bug Fixes</h3>
<ul>
<li>security problem (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1799">#1799</a>) (<a href="https://github.com/webpack/webpack-dev-middleware/commit/5a4d9e8d6ab587b4137fdec885c36b79b10b29f7">5a4d9e8</a>)</li>
</ul>
<h3><a href="https://github.com/webpack/webpack-dev-middleware/compare/v6.1.1...v6.1.2">6.1.2</a> (2024-03-20)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><strong>security:</strong> do not allow to read files above (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1778">#1778</a>) (<a href="https://github.com/webpack/webpack-dev-middleware/commit/9670b3495da518fe667ff3428c5e4cb9f2f3d353">9670b34</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/webpack/webpack-dev-middleware/commit/4537522597563c5fbe613ee17f91c7e02070c102"><code>4537522</code></a> chore(release): 6.1.3</li>
<li><a href="https://github.com/webpack/webpack-dev-middleware/commit/5a4d9e8d6ab587b4137fdec885c36b79b10b29f7"><code>5a4d9e8</code></a> fix: security problem (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1799">#1799</a>)</li>
<li><a href="https://github.com/webpack/webpack-dev-middleware/commit/54e4a9604905898066979a3a918d3d545f761447"><code>54e4a96</code></a> chore(release): 6.1.2</li>
<li><a href="https://github.com/webpack/webpack-dev-middleware/commit/9670b3495da518fe667ff3428c5e4cb9f2f3d353"><code>9670b34</code></a> fix(security): do not allow to read files above (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1778">#1778</a>)</li>
<li>See full diff in <a href="https://github.com/webpack/webpack-dev-middleware/compare/v6.1.1...v6.1.3">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=webpack-dev-middleware&package-manager=npm_and_yarn&previous-version=6.1.1&new-version=6.1.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/GoogleCloudDataproc/dataproc-jupyter-plugin/network/alerts).

</details>


Discussion / hints:

Fix patch:
diff --git a/dataproc_jupyter_plugin/contollers/bigqueryController.py b/dataproc_jupyter_plugin/contollers/bigqueryController.py
new file mode 100644
index 00000000..21b45118
--- /dev/null
+++ b/dataproc_jupyter_plugin/contollers/bigqueryController.py
@@ -0,0 +1,124 @@
+# Copyright 2024 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
+import tornado
+from dataproc_jupyter_plugin import handlers
+from dataproc_jupyter_plugin.services.bigqueryService import (
+    BigQueryDatasetInfoService,
+    BigQueryDatasetListService,
+    BigQueryPreviewService,
+    BigQueryProjectService,
+    BigQueryTableInfoService,
+    BigQueryTableListService,
+)
+
+
+class BigqueryDatasetController(APIHandler):
+    @tornado.web.authenticated
+    def get(self):
+        try:
+            page_token = self.get_argument("pageToken")
+            bigquery_dataset = BigQueryDatasetListService()
+            credentials = handlers.get_cached_credentials(self.log)
+            dataset_list = bigquery_dataset.list_datasets(
+                credentials, page_token, self.log
+            )
+            self.finish(json.dumps(dataset_list))
+        except Exception as e:
+            self.log.exception(f"Error fetching datasets")
+            self.finish({"error": str(e)})
+
+
+class BigqueryTableController(APIHandler):
+    @tornado.web.authenticated
+    def get(self):
+        try:
+            page_token = self.get_argument("pageToken")
+            dataset_id = self.get_argument("dataset_id")
+            bigquery_dataset = BigQueryTableListService()
+            credentials = handlers.get_cached_credentials(self.log)
+            table_list = bigquery_dataset.list_table(
+                credentials, dataset_id, page_token, self.log
+            )
+            self.finish(json.dumps(table_list))
+        except Exception as e:
+            self.log.exception(f"Error fetching datasets")
+            self.finish({"error": str(e)})
+
+
+class BigqueryDatasetInfoController(APIHandler):
+    @tornado.web.authenticated
+    def get(self):
+        try:
+            dataset_id = self.get_argument("dataset_id")
+            bq_dataset = BigQueryDatasetInfoService()
+            credentials = handlers.get_cached_credentials(self.log)
+            dataset_info = bq_dataset.list_dataset_info(
+                credentials, dataset_id, self.log
+            )
+            self.finish(json.dumps(dataset_info))
+        except Exception as e:
+            self.log.exception(f"Error fetching dataset information")
+            self.finish({"error": str(e)})
+
+
+class BigqueryTableInfoController(APIHandler):
+    @tornado.web.authenticated
+    def get(self):
+        try:
+            dataset_id = self.get_argument("dataset_id")
+            table_id = self.get_argument("table_id")
+            bq_table = BigQueryTableInfoService()
+            credentials = handlers.get_cached_credentials(self.log)
+            table_info = bq_table.list_table_info(
+                credentials, dataset_id, table_id, self.log
+            )
+            self.finish(json.dumps(table_info))
+        except Exception as e:
+            self.log.exception(f"Error fetching table information")
+            self.finish({"error": str(e)})
+
+
+class BigqueryPreviewController(APIHandler):
+    @tornado.web.authenticated
+    def get(self):
+        try:
+            dataset_id = self.get_argument("dataset_id")
+            table_id = self.get_argument("table_id")
+            page_token = self.get_argument("pageToken")
+            bq_preview = BigQueryPreviewService()
+            credentials = handlers.get_cached_credentials(self.log)
+            preview_data = bq_preview.bigquery_preview_data(
+                credentials, dataset_id, table_id, page_token, self.log
+            )
+            self.finish(json.dumps(preview_data))
+        except Exception as e:
+            self.log.exception(f"Error fetching preview data")
+            self.finish({"error": str(e)})
+
+
+class BigqueryProjectsController(APIHandler):
+    @tornado.web.authenticated
+    def get(self):
+        try:
+            bq_preview = BigQueryProjectService()
+            credentials = handlers.get_cached_credentials(self.log)
+            preview_data = bq_preview.bigquery_preview_data(credentials, self.log)
+            self.finish(json.dumps(preview_data))
+        except Exception as e:
+            self.log.exception(f"Error fetching projects")
+            self.finish({"error": str(e)})
diff --git a/dataproc_jupyter_plugin/handlers.py b/dataproc_jupyter_plugin/handlers.py
index 5b46e04e..a6ff8914 100644
--- a/dataproc_jupyter_plugin/handlers.py
+++ b/dataproc_jupyter_plugin/handlers.py
@@ -21,6 +21,7 @@
 import time
 
 from cachetools import TTLCache
+from dataproc_jupyter_plugin.contollers.bigqueryController import BigqueryDatasetController, BigqueryDatasetInfoController, BigqueryPreviewController, BigqueryProjectsController, BigqueryTableController, BigqueryTableInfoController
 from jupyter_server.base.handlers import APIHandler
 from jupyter_server.serverapp import ServerApp
 from jupyter_server.utils import url_path_join
@@ -365,6 +366,12 @@ def full_path(name):
         "importErrorsList": ImportErrorController,
         "triggerDag": TriggerDagController,
         "downloadOutput": downloadOutputController,
+        "bigQueryDataset": BigqueryDatasetController,
+        "bigQueryTable": BigqueryTableController,
+        "bigQueryDatasetInfo": BigqueryDatasetInfoController,
+        "bigQueryTableInfo": BigqueryTableInfoController,
+        "bigQueryPreview": BigqueryPreviewController,
+        "ProjectsList": BigqueryProjectsController
     }
     handlers = [(full_path(name), handler) for name, handler in handlersMap.items()]
     web_app.add_handlers(host_pattern, handlers)
diff --git a/dataproc_jupyter_plugin/services/bigqueryService.py b/dataproc_jupyter_plugin/services/bigqueryService.py
new file mode 100644
index 00000000..53f1578b
--- /dev/null
+++ b/dataproc_jupyter_plugin/services/bigqueryService.py
@@ -0,0 +1,189 @@
+# Copyright 2024 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 requests
+from dataproc_jupyter_plugin.utils.constants import CONTENT_TYPE, dataplex_url
+
+
+class ApiHeaders:
+    @staticmethod
+    def create_headers(access_token):
+        return {
+            "Content-Type": CONTENT_TYPE,
+            "Authorization": f"Bearer {access_token}",
+        }
+
+
+class BigQueryDatasetListService:
+    def list_datasets(self, credentials, page_token, log):
+        try:
+            if (
+                ("access_token" in credentials)
+                and ("project_id" in credentials)
+                and ("region_id" in credentials)
+            ):
+                access_token = credentials["access_token"]
+                project_id = credentials["project_id"]
+                api_endpoint = f"https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/datasets?pageToken={page_token}"
+                headers = ApiHeaders.create_headers(access_token)
+                response = requests.get(api_endpoint, headers=headers)
+                if response.status_code == 200:
+                    resp = response.json()
+                    return resp
+                else:
+                    log.exception(f"Missing required credentials")
+                    raise ValueError("Missing required credentials")
+            else:
+                log.exception(f"Missing required credentials")
+                raise ValueError("Missing required credentials")
+        except Exception as e:
+            log.exception(f"Error fetching cluster list")
+            return {"error": str(e)}
+
+
+class BigQueryTableListService:
+    def list_table(self, credentials, dataset_id, page_token, log):
+        try:
+            if (
+                ("access_token" in credentials)
+                and ("project_id" in credentials)
+                and ("region_id" in credentials)
+            ):
+                access_token = credentials["access_token"]
+                project_id = credentials["project_id"]
+                api_endpoint = f"https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/datasets/{dataset_id}/tables?pageToken={page_token}"
+                headers = ApiHeaders.create_headers(access_token)
+                response = requests.get(api_endpoint, headers=headers)
+                if response.status_code == 200:
+                    resp = response.json()
+                    return resp
+                else:
+                    log.exception(f"Missing required credentials")
+                    raise ValueError("Missing required credentials")
+            else:
+                log.exception(f"Missing required credentials")
+                raise ValueError("Missing required credentials")
+        except Exception as e:
+            log.exception(f"Error fetching schema")
+            return {"error": str(e)}
+
+
+class BigQueryDatasetInfoService:
+    def list_dataset_info(self, credentials, dataset_id, log):
+        try:
+            if (
+                ("access_token" in credentials)
+                and ("project_id" in credentials)
+                and ("region_id" in credentials)
+            ):
+                access_token = credentials["access_token"]
+                project_id = credentials["project_id"]
+                api_endpoint = f"https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/datasets/{dataset_id}"
+                headers = ApiHeaders.create_headers(access_token)
+                response = requests.get(api_endpoint, headers=headers)
+                if response.status_code == 200:
+                    resp = response.json()
+                    return resp
+                else:
+                    log.exception(f"Missing required credentials")
+                    raise ValueError("Missing required credentials")
+            else:
+                log.exception(f"Missing required credentials")
+                raise ValueError("Missing required credentials")
+        except Exception as e:
+            log.exception(f"Error fetching dataset information")
+            return {"error": str(e)}
+
+
+class BigQueryTableInfoService:
+    def list_table_info(self, credentials, dataset_id, table_id, log):
+        try:
+            if (
+                ("access_token" in credentials)
+                and ("project_id" in credentials)
+                and ("region_id" in credentials)
+            ):
+                access_token = credentials["access_token"]
+                project_id = credentials["project_id"]
+                api_endpoint = f"https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/datasets/{dataset_id}/tables/{table_id}"
+                headers = ApiHeaders.create_headers(access_token)
+                response = requests.get(api_endpoint, headers=headers)
+                if response.status_code == 200:
+                    resp = response.json()
+                    return resp
+                else:
+                    log.exception(f"Missing required credentials")
+                    raise ValueError("Missing required credentials")
+            else:
+                log.exception(f"Missing required credentials")
+                raise ValueError("Missing required credentials")
+        except Exception as e:
+            log.exception(f"Error fetching table information")
+            return {"error": str(e)}
+
+
+class BigQueryPreviewService:
+    def bigquery_preview_data(self, credentials, dataset_id, table_id, page_token, log):
+        try:
+            if (
+                ("access_token" in credentials)
+                and ("project_id" in credentials)
+                and ("region_id" in credentials)
+            ):
+                access_token = credentials["access_token"]
+                project_id = credentials["project_id"]
+                api_endpoint = f"https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/datasets/{dataset_id}/tables/{table_id}/data?pageToken={page_token}"
+                headers = ApiHeaders.create_headers(access_token)
+                response = requests.get(api_endpoint, headers=headers)
+                if response.status_code == 200:
+                    resp = response.json()
+                    return resp
+                else:
+                    log.exception(f"Missing required credentials")
+                    raise ValueError("Missing required credentials")
+            else:
+                log.exception(f"Missing required credentials")
+                raise ValueError("Missing required credentials")
+        except Exception as e:
+            log.exception(f"Error fetching preview data")
+            return {"error": str(e)}
+
+
+class BigQueryProjectService:
+    def bigquery_projects(self, credentials, dataset_id, table_id, log):
+        try:
+            if (
+                ("access_token" in credentials)
+                and ("project_id" in credentials)
+                and ("region_id" in credentials)
+            ):
+                access_token = credentials["access_token"]
+                api_endpoint = (
+                    f"https://cloudresourcemanager.googleapis.com/v1/projects"
+                )
+                headers = ApiHeaders.create_headers(access_token)
+                response = requests.get(api_endpoint, headers=headers)
+                if response.status_code == 200:
+                    resp = response.json()
+                    return resp
+                else:
+                    log.exception(f"Missing required credentials")
+                    raise ValueError("Missing required credentials")
+            else:
+                log.exception(f"Missing required credentials")
+                raise ValueError("Missing required credentials")
+        except Exception as e:
+            log.exception(f"Error fetching projects")
+            return {"error": str(e)}
diff --git a/dataproc_jupyter_plugin/utils/constants.py b/dataproc_jupyter_plugin/utils/constants.py
index 1a8fcc55..3e6ac6bc 100644
--- a/dataproc_jupyter_plugin/utils/constants.py
+++ b/dataproc_jupyter_plugin/utils/constants.py
@@ -33,3 +33,4 @@
 storage_url = gcp_service_url(
     "storage", default_url="https://storage.googleapis.com/storage/v1/"
 )
+dataplex_url = gcp_service_url("dataplex", default_url="https://dataplex.googleapis.com/v1")
diff --git a/package.json b/package.json
index 297a3d12..d3b4f01f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "dataproc_jupyter_plugin",
-    "version": "0.1.75",
+    "version": "0.1.76",
     "description": "It is a plugin to work with dataproc services in Jupyterlab",
     "keywords": [
         "jupyter",
diff --git a/pyproject.toml b/pyproject.toml
index 1ac07b15..dd0ca6f3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,7 +26,8 @@ dependencies = [
     "cachetools>=4.2.4",
     "kernels-mixer>=0.0.4",
     "pendulum>=3.0.0",
-    "pydantic~=1.10.0"
+    "pydantic~=1.10.0",
+    "bigframes~=0.22.0"
 ]
 dynamic = ["version", "description", "authors", "urls", "keywords"]
 
diff --git a/schema/plugin.json b/schema/plugin.json
index ac963f10..c4e2eff1 100644
--- a/schema/plugin.json
+++ b/schema/plugin.json
@@ -26,6 +26,12 @@
       "title": "Enable Preview Features",
       "description": "Whether or not preview features such as the DPMS explorer and GCS browser are enabled.",
       "default": true
+    },
+    "bqRegion": {
+      "type": "string",
+      "title": "Set the BQ Region",
+      "description": "This value will be used to configure the BQ region for the plugin",
+      "default": "US"
     }
   },
   "additionalProperties": false
diff --git a/src/controls/BigQueryRegionDropdown.tsx b/src/controls/BigQueryRegionDropdown.tsx
new file mode 100644
index 00000000..0321659c
--- /dev/null
+++ b/src/controls/BigQueryRegionDropdown.tsx
@@ -0,0 +1,103 @@
+/**
+ * @license
+ * Copyright 2024 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, { useMemo } from 'react';
+import TextField from '@mui/material/TextField';
+import Autocomplete from '@mui/material/Autocomplete';
+import { useRegion } from '../utils/regionService';
+import { Paper, PaperProps } from '@mui/material';
+
+type Props = {
+  /** The currently selected project ID */
+  projectId: string;
+  /** The currently selected Region */
+  region: string;
+  /** Callback function for when the project ID is changed by the dropdown */
+  onRegionChange: (projectId: string) => void;
+};
+
+/**
+ * Component to render a region selector dropdown.
+ */
+export function BigQueryRegionDropdown(props: Props) {
+  const { projectId, region, onRegionChange } = props;
+  const regions = useRegion(projectId);
+
+  let regionStrList = useMemo(
+    () => regions.map(region => region.name),
+    [regions]
+  );
+
+  let bigQueryRegionOptions: any = [];
+
+  if (regionStrList.length > 0) {
+    let exceptRegions = ['us-central2', 'us-east7', 'us-west8'];
+    let bqRegionsList: string[] = [];
+    bqRegionsList = regionStrList.filter(
+      element => !exceptRegions.includes(element)
+    );
+
+    let multiRegionList = ['US', 'EU'];
+    let omniRegionList = [
+      'aws-us-east-1',
+      'aws-us-west-2',
+      'aws-ap-northeast-2',
+      'aws-eu-west-1',
+      'azure-eastus2'
+    ];
+
+    bqRegionsList = multiRegionList
+      .concat(omniRegionList)
+      .concat(bqRegionsList);
+    regionStrList = bqRegionsList;
+
+    bigQueryRegionOptions = regionStrList.map((option: string) => {
+      const categoryType = 'Regions';
+      let object = { title: option };
+      return {
+        categoryType: multiRegionList.includes(option)
+          ? 'Multi Regions'
+          : omniRegionList.includes(option)
+          ? 'Omni Locations'
+          : categoryType,
+        ...object
+      };
+    });
+  }
+
+  const handleRegionChange = (value: any) => {
+    if (value.title) {
+      onRegionChange(value.title);
+    }
+  };
+
+  return (
+    <Autocomplete
+      value={{ title: region }}
+      options={bigQueryRegionOptions}
+      groupBy={(option: any) => option.categoryType}
+      getOptionLabel={(option: any) => {
+        return option.title ? option.title : option;
+      }}
+      onChange={(_, value) => handleRegionChange(value ?? '')}
+      PaperComponent={(props: PaperProps) => <Paper elevation={8} {...props} />}
+      renderInput={params => (
+        <TextField {...params} label={'BigQuery Region*'} />
+      )}
+    />
+  );
+}
diff --git a/src/controls/RegionDropdown.tsx b/src/controls/RegionDropdown.tsx
index 0a0417c4..3f84731b 100644
--- a/src/controls/RegionDropdown.tsx
+++ b/src/controls/RegionDropdown.tsx
@@ -48,7 +48,7 @@ export function RegionDropdown(props: Props) {
       options={regionStrList}
       onChange={(_, value) => onRegionChange(value ?? '')}
       PaperComponent={(props: PaperProps) => <Paper elevation={8} {...props} />}
-      renderInput={params => <TextField {...params} label="Region" />}
+      renderInput={params => <TextField {...params} label={'Region*'} />}
     />
   );
 }
diff --git a/src/dpms/bigQueryDatasetInfo.tsx b/src/dpms/bigQueryDatasetInfo.tsx
new file mode 100644
index 00000000..6f490c95
--- /dev/null
+++ b/src/dpms/bigQueryDatasetInfo.tsx
@@ -0,0 +1,69 @@
+/**
+ * @license
+ * Copyright 2024 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 { BigQueryService } from './bigQueryService';
+import { ClipLoader } from 'react-spinners';
+
+const BigQueryDatasetInfo = ({ dataset }: { dataset: string }) => {
+  const [datasetInfo, setDatasetInfo] = useState<any>({});
+  const [isLoading, setIsLoading] = useState(true);
+
+  useEffect(() => {
+    BigQueryService.getBigQueryDatasetDetailsAPIService(
+      dataset,
+      setDatasetInfo,
+      setIsLoading
+    );
+  }, []);
+
+  return (
+    <>
+      {isLoading ? (
+        <div className="database-loader">
+          <div>
+            <ClipLoader
+              color="#3367d6"
+              loading={true}
+              size={20}
+              aria-label="Loading Spinner"
+              data-testid="loader"
+            />
+          </div>
+          Loading dataset details
+        </div>
+      ) : (
+        <>
+          <div className="table-container">
+            <table className="db-table">
+              <tbody>
+                {Object.keys(datasetInfo).map((tableData, index) => (
+                  <tr key={index} className="tr-row">
+                    <td className="bold-column">{tableData}</td>
+                    <td>{datasetInfo[tableData]}</td>
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          </div>
+        </>
+      )}
+    </>
+  );
+};
+
+export default BigQueryDatasetInfo;
diff --git a/src/dpms/bigQueryDatasetInfoWrapper.tsx b/src/dpms/bigQueryDatasetInfoWrapper.tsx
new file mode 100644
index 00000000..7e40b367
--- /dev/null
+++ b/src/dpms/bigQueryDatasetInfoWrapper.tsx
@@ -0,0 +1,65 @@
+/**
+ * @license
+ * Copyright 2024 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 { IThemeManager } from '@jupyterlab/apputils';
+import { DataprocWidget } from '../controls/DataprocWidget';
+import BigQueryDatasetInfo from './bigQueryDatasetInfo';
+
+interface IDatabaseProps {
+  title: string;
+}
+
+const BigQueryDatasetInfoWrapper = ({
+  title,
+}: IDatabaseProps): React.ReactElement => {
+  const renderTable = () => {
+    return (
+      <>
+          <BigQueryDatasetInfo dataset={title} />
+      </>
+    );
+  };
+
+  return (
+    <div>
+      <div className="dpms-Wrapper">
+        <div className="title-overlay">{title}</div>
+          <div className="db-title">Dataset info</div>
+        {renderTable()}
+      </div>
+    </div>
+  );
+};
+
+export class BigQueryDatasetWrapper extends DataprocWidget {
+  constructor(
+    title: string,
+    themeManager: IThemeManager
+  ) {
+    super(themeManager);
+    this.title.label = title;
+  }
+
+  renderInternal(): React.ReactElement {
+    return (
+      <BigQueryDatasetInfoWrapper
+        title={this.title.label}
+      />
+    );
+  }
+}
diff --git a/src/dpms/bigQuerySchema.tsx b/src/dpms/bigQuerySchema.tsx
new file mode 100644
index 00000000..ed612946
--- /dev/null
+++ b/src/dpms/bigQuerySchema.tsx
@@ -0,0 +1,129 @@
+/**
+ * @license
+ * Copyright 2024 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 } from 'react-table';
+
+interface IColumn {
+  name: string;
+  type: string;
+  mode: string;
+  key?: string;
+  collation?: string;
+  defaultValue?: string;
+  policyTags?: string;
+  dataPolicies?: string;
+  description?: string;
+}
+
+const BigQuerySchemaInfo = ({ column }: { column: IColumn[] }) => {
+  const columns = [
+    {
+      Header: 'Field name',
+      accessor: 'name'
+    },
+    {
+      Header: 'Type',
+      accessor: 'type'
+    },
+    {
+      Header: 'Mode',
+      accessor: 'mode'
+    },
+    {
+      Header: 'Key',
+      accessor: 'key'
+    },
+    {
+      Header: 'Collation',
+      accessor: 'collation'
+    },
+    {
+      Header: 'Default Value',
+      accessor: 'defaultValue'
+    },
+    {
+      Header: 'Policy Tags',
+      accessor: 'policyTags '
+    },
+    {
+      Header: 'Data Policies',
+      accessor: 'dataPolicies'
+    },
+    {
+      Header: 'Description',
+      accessor: 'description'
+    }
+  ];
+
+  const data = column.map((column: IColumn) => ({
+    name: column.name,
+    type: column.type || '',
+    mode: column.mode || '',
+    key: column.key || '',
+    collation: column.collation || '',
+    defaultValue: column.defaultValue || '',
+    policyTags: column.policyTags || '',
+    dataPolicies: column.dataPolicies || '',
+    description: column.description
+  }));
+
+  const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
+    // @ts-ignore  react-table 'columns' which is declared here on type 'TableOptions<IColumns>'
+    useTable({ columns, data });
+
+  return (
+    <div className="dpms-Wrapper">
+      <div className="table-container">
+        <table className="big-query-schema-table" {...getTableProps()}>
+          <thead>
+            {headerGroups.map(headerGroup => (
+              <tr {...headerGroup.getHeaderGroupProps()}>
+                {headerGroup.headers.map(column => (
+                  <th {...column.getHeaderProps()}>
+                    {column.render('Header')}
+                  </th>
+                ))}
+              </tr>
+            ))}
+          </thead>
+          <tbody {...getTableBodyProps()}>
+            {rows.map(row => {
+              prepareRow(row);
+              return (
+                <tr {...row.getRowProps()}>
+                  {row.cells.map((cell, index) => {
+                    return (
+                      <td
+                        className={index === 0 ? 'bold-column' : ''}
+                        {...cell.getCellProps()}
+                      >
+                        {cell.render('Cell')}
+                      </td>
+                    );
+                  })}
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>
+    </div>
+  );
+};
+
+export default BigQuerySchemaInfo;
diff --git a/src/dpms/bigQueryService.tsx b/src/dpms/bigQueryService.tsx
new file mode 100644
index 00000000..821a6d70
--- /dev/null
+++ b/src/dpms/bigQueryService.tsx
@@ -0,0 +1,357 @@
+/**
+ * @license
+ * Copyright 2024 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-toastify/dist/ReactToastify.css';
+import { requestAPI } from '../handler/handler';
+import { toast } from 'react-toastify';
+import { toastifyCustomStyle } from '../utils/utils';
+import { ISettingRegistry } from '@jupyterlab/settingregistry';
+import { PLUGIN_ID } from '../utils/const';
+
+interface IPreviewColumn {
+  Header: string;
+  accessor: string;
+}
+
+export class BigQueryService {
+  static bigQueryPreviewAPIService = async (
+    columns: IPreviewColumn[],
+    tableId: string,
+    dataSetId: string,
+    setIsLoading: (value: boolean) => void,
+    setPreviewDataList: any,
+    nextPageToken?: string,
+    previousDatasetList?: object
+  ) => {
+    try {
+      const pageToken = nextPageToken ?? '';
+      const data: any = await requestAPI(
+        `bigQueryPreview?dataset_id=${dataSetId}&table_id=${tableId}&pageToken=${pageToken}`
+      );
+
+      if (data.error) {
+        toast.error(data.error, toastifyCustomStyle);
+        setIsLoading(false);
+      } else if (data.totalRows == 0) {
+        setIsLoading(false);
+      } else {
+        const existingDatasetList = previousDatasetList ?? [];
+        //setStateAction never type issue
+        const allDatasetList: any = [
+          ...(existingDatasetList as []),
+          ...data.rows
+        ];
+
+        if (data.pageToken) {
+          this.bigQueryPreviewAPIService(
+            columns,
+            tableId,
+            dataSetId,
+            setIsLoading,
+            setPreviewDataList,
+            data.pageToken,
+            allDatasetList
+          );
+        } else {
+          let transformRowInfoList: any = [];
+          allDatasetList.forEach((rowInfo: any) => {
+            let transformRowInfo: any = {};
+            rowInfo['f'].forEach((fieldInfo: any, index: number) => {
+              transformRowInfo[columns[index].Header] = fieldInfo['v'];
+            });
+            transformRowInfoList.push(transformRowInfo);
+          });
+          setPreviewDataList(transformRowInfoList);
+          setIsLoading(false);
+        }
+      }
+    } catch (reason) {
+      console.error(`Error on GET credentials.\n${reason}`);
+    }
+  };
+
+  static getBigQueryColumnDetailsAPIService = async (
+    datasetId: string,
+    tableId: string,
+    setSchemaResponse: any
+  ) => {
+    try {
+      const data: any = await requestAPI(
+        `bigQueryTableInfo?dataset_id=${datasetId}&table_id=${tableId}`
+      );
+      setSchemaResponse(data);
+    } catch (reason) {
+      console.error(`Error in fetching big query schema.\n${reason}`);
+      toast.error(`Failed to fetch big query schema`, toastifyCustomStyle);
+    }
+  };
+
+  static getBigQuerySchemaInfoAPIService = async (
+    datasetId: string,
+    tableId: string,
+    setSchemaInfoResponse: any
+  ) => {
+    try {
+      const data: any = await requestAPI(
+        `bigQueryTableInfo?dataset_id=${datasetId}&table_id=${tableId}`
+      );
+      if (data.schema.fields) {
+        setSchemaInfoResponse(data.schema.fields);
+      } else {
+        setSchemaInfoResponse([]);
+      }
+    } catch (reason) {
+      console.error(`Error in fetching big query schema.\n${reason}`);
+      toast.error(`Failed to fetch big query schema`, toastifyCustomStyle);
+    }
+  };
+
+  static getBigQueryDatasetsAPIService = async (
+    notebookValue: string,
+    settingRegistry: ISettingRegistry,
+    setDatabaseNames: (value: string[]) => void,
+    setDataSetResponse: any,
+    setSchemaError: (value: boolean) => void,
+    setIsLoading: (value: boolean) => void,
+    nextPageToken?: string,
+    previousDatasetList?: object
+  ) => {
+    if (notebookValue) {
+      const pageToken = nextPageToken ?? '';
+      try {
+        const data: any = await requestAPI(
+          `bigQueryDataset?pageToken=${pageToken}`
+        );
+
+        const existingDatasetList = previousDatasetList ?? [];
+        //setStateAction never type issue
+        const allDatasetList: any = [
+          ...(existingDatasetList as []),
+          ...data.datasets
+        ];
+
+        if (data.nextPageToken) {
+          this.getBigQueryDatasetsAPIService(
+            notebookValue,
+            settingRegistry,
+            setDatabaseNames,
+            setDataSetResponse,
+            setSchemaError,
+            setIsLoading,
+            data.nextPageToken,
+            allDatasetList
+          );
+        } else {
+          const settings = await settingRegistry.load(PLUGIN_ID);
+
+          let filterDatasetByLocation = allDatasetList;
+          filterDatasetByLocation = filterDatasetByLocation.filter(
+            (dataset: any) =>
+              dataset.location === settings.get('bqRegion')['composite']
+          );
+
+          if (filterDatasetByLocation.length > 0) {
+            const databaseNames: string[] = [];
+            const updatedDatabaseDetails: { [key: string]: string } = {};
+            filterDatasetByLocation.forEach(
+              (data: {
+                datasetReference: { description: string; datasetId: string };
+              }) => {
+                databaseNames.push(data.datasetReference.datasetId);
+                const description = data.datasetReference.description || 'None';
+                updatedDatabaseDetails[data.datasetReference.datasetId] =
+                  description;
+              }
+            );
+            setDataSetResponse(filterDatasetByLocation);
+            setDatabaseNames(databaseNames);
+            setSchemaError(false);
+          } else {
+            toast.error(
+              `No Dataset available in this region`,
+              toastifyCustomStyle
+            );
+            setIsLoading(false);
+          }
+        }
+      } catch (reason) {
+        console.error(`Error in fetching datasets.\n${reason}`);
+        toast.error(`Failed to fetch datasets`, toastifyCustomStyle);
+        setIsLoading(false);
+      }
+    }
+  };
+
+  static getBigQueryTableAPIService = async (
+    notebookValue: string,
+    datasetId: string,
+    setDatabaseNames: (value: string[]) => void,
+    setTableResponse: any,
+    setSchemaError: (value: boolean) => void,
+    nextPageToken?: string,
+    previousDatasetList?: object
+  ) => {
+    if (notebookValue) {
+      const pageToken = nextPageToken ?? '';
+      try {
+        const data: any = await requestAPI(
+          `bigQueryTable?dataset_id=${datasetId}&pageToken=${pageToken}`
+        );
+
+        if (data.tables) {
+          const existingDatasetList = previousDatasetList ?? [];
+          //setStateAction never type issue
+          const allDatasetList: any = [
+            ...(existingDatasetList as []),
+            ...data.tables
+          ];
+
+          if (data.nextPageToken) {
+            this.getBigQueryTableAPIService(
+              notebookValue,
+              datasetId,
+              setDatabaseNames,
+              setTableResponse,
+              setSchemaError,
+              data.nextPageToken,
+              allDatasetList
+            );
+          } else {
+            const tableNames: string[] = [];
+            const entryNames: string[] = [];
+            const updatedTableDetails: { [key: string]: string } = {};
+            const datasetTableMapping: { [key: string]: string } = {};
+            allDatasetList.forEach(
+              (entry: {
+                tableReference: {
+                  description: string;
+                  datasetId: string;
+                  tableId: string;
+                };
+              }) => {
+                tableNames.push(entry.tableReference.tableId);
+                entryNames.push(entry.tableReference.tableId);
+                const description = entry.tableReference.description || 'None';
+                updatedTableDetails[entry.tableReference.tableId] = description;
+                datasetTableMapping[entry.tableReference.tableId] =
+                  entry.tableReference.datasetId;
+              }
+            );
+            setTableResponse(allDatasetList);
+          }
+        } else {
+          setTableResponse(datasetId);
+        }
+      } catch (reason) {
+        console.error(`Error in fetching datasets.\n${reason}`);
+        toast.error(`Failed to fetch datasets`, toastifyCustomStyle);
+      }
+    }
+  };
+
+  static getBigQueryDatasetInfoAPIService = async (
+    dataset: string,
+    setDatasetInfo: any
+  ) => {
+    try {
+      const data: any = await requestAPI(
+        `bigQueryDatasetInfo?dataset_id=${dataset}`
+      );
+      let datasetInfoTemp: any = {};
+      datasetInfoTemp['Case insensitive'] = data.isCaseInsensitive;
+      setDatasetInfo(datasetInfoTemp);
+    } catch (reason) {
+      console.error(`Error on GET credentials.\n${reason}`);
+    }
+  };
+
+  static getBigQueryTableInfoAPIService = async (
+    title: string,
+    dataset: string,
+    setTableInfo: any,
+    datasetInfo: any,
+    setIsLoading: (value: boolean) => void
+  ) => {
+    try {
+      const data: any = await requestAPI(
+        `bigQueryTableInfo?dataset_id=${dataset}&table_id=${title}`
+      );
+
+      let tableInfoTemp: any = {};
+      tableInfoTemp['Table ID'] = data.id;
+      tableInfoTemp['Created'] = data.creationTime
+        ? new Date(Number(data.creationTime)).toString()
+        : '';
+      tableInfoTemp['Last modified'] = data.lastModifiedTime
+        ? new Date(Number(data.lastModifiedTime)).toString()
+        : '';
+      tableInfoTemp['Table expiration'] = data.expirationTime
+        ? new Date(Number(data.expirationTime)).toString()
+        : '';
+      tableInfoTemp['Data location'] = data.location;
+      tableInfoTemp['Default collation'] = data.defaultCollation;
+      tableInfoTemp['Default rounding mode'] = data.defaultRoundingMode;
+      tableInfoTemp['Description'] = data.description;
+      tableInfoTemp['Case insensitive'] = datasetInfo['Case insensitive']
+        ? datasetInfo['Case insensitive'].toString()
+        : '';
+      setTableInfo(tableInfoTemp);
+      setIsLoading(false);
+    } catch (reason) {
+      console.error(`Error on GET credentials.\n${reason}`);
+    }
+  };
+
+  static getBigQueryDatasetDetailsAPIService = async (
+    dataset: string,
+    setDatasetInfo: any,
+    setIsLoading: (value: boolean) => void
+  ) => {
+    try {
+      const data: any = await requestAPI(
+        `bigQueryDatasetInfo?dataset_id=${dataset}`
+      );
+      let datasetInfoTemp: any = {};
+      datasetInfoTemp['Dataset ID'] = data.id;
+      datasetInfoTemp['Created'] = data.creationTime
+        ? new Date(Number(data.creationTime)).toString()
+        : '';
+      datasetInfoTemp['Default table expiration'] =
+        data.defaultTableExpirationMs
+          ? data.defaultTableExpirationMs / (1000 * 60 * 60 * 24) + ' days'
+          : '';
+      datasetInfoTemp['Last modified'] = data.lastModifiedTime
+        ? new Date(Number(data.lastModifiedTime)).toString()
+        : '';
+      datasetInfoTemp['Data location'] = data.location;
+      datasetInfoTemp['Description'] = data.description;
+      datasetInfoTemp['Default collation'] = data.defaultCollation;
+      datasetInfoTemp['Default rounding mode'] = data.defaultRoundingMode;
+      datasetInfoTemp['Time travel window'] = data.maxTimeTravelHours
+        ? data.maxTimeTravelHours / 24 + ' days'
+        : '';
+      datasetInfoTemp['Storage billing model'] = data.storageBillingModel;
+      datasetInfoTemp['Case insensitive'] = data.isCaseInsensitive
+        ? data.isCaseInsensitive.toString()
+        : '';
+      setDatasetInfo(datasetInfoTemp);
+      setIsLoading(false);
+    } catch (reason) {
+      console.error(`Error on GET credentials.\n${reason}`);
+    }
+  };
+}
diff --git a/src/dpms/bigQueryTableInfo.tsx b/src/dpms/bigQueryTableInfo.tsx
new file mode 100644
index 00000000..f80fddde
--- /dev/null
+++ b/src/dpms/bigQueryTableInfo.tsx
@@ -0,0 +1,83 @@
+/**
+ * @license
+ * Copyright 2024 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 { BigQueryService } from './bigQueryService';
+import { ClipLoader } from 'react-spinners';
+
+const BigQueryTableInfo = ({
+  title,
+  dataset
+}: {
+  title: string;
+  dataset: string;
+}) => {
+  const [datasetInfo, setDatasetInfo] = useState<any>({});
+  const [tableInfo, setTableInfo] = useState<any>({});
+  const [isLoading, setIsLoading] = useState(true);
+
+  useEffect(() => {
+    BigQueryService.getBigQueryDatasetInfoAPIService(dataset, setDatasetInfo);
+  }, []);
+
+  useEffect(() => {
+    BigQueryService.getBigQueryTableInfoAPIService(
+      title,
+      dataset,
+      setTableInfo,
+      datasetInfo,
+      setIsLoading
+    );
+  }, [datasetInfo]);
+
+  return (
+    <>
+      {isLoading ? (
+        <div className="database-loader">
+          <div>
+            <ClipLoader
+              color="#3367d6"
+              loading={true}
+              size={20}
+              aria-label="Loading Spinner"
+              data-testid="loader"
+            />
+          </div>
+          Loading table details
+        </div>
+      ) : (
+        <>
+          <div className="db-title">Table info</div>
+          <div className="table-container">
+            <table className="db-table">
+              <tbody>
+                {Object.keys(tableInfo).map((tableData, index) => (
+                  <tr key={index} className="tr-row">
+                    <td className="bold-column">{tableData}</td>
+                    <td>{tableInfo[tableData]}</td>
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          </div>
+        </>
+      )}
+    </>
+  );
+};
+
+export default BigQueryTableInfo;
diff --git a/src/dpms/bigQueryTableInfoWrapper.tsx b/src/dpms/bigQueryTableInfoWrapper.tsx
new file mode 100644
index 00000000..bdfcc9c4
--- /dev/null
+++ b/src/dpms/bigQueryTableInfoWrapper.tsx
@@ -0,0 +1,126 @@
+/**
+ * @license
+ * Copyright 2024 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 { IThemeManager } from '@jupyterlab/apputils';
+import { DataprocWidget } from '../controls/DataprocWidget';
+import PreviewDataInfo from './previewDataInfo';
+import BigQueryTableInfo from './bigQueryTableInfo';
+import BigQuerySchemaInfo from './bigQuerySchema';
+import { BigQueryService } from './bigQueryService';
+
+interface IDatabaseProps {
+  title: string;
+  database: string;
+}
+const BigQueryTableInfoWrapper = ({
+  title,
+  database
+}: IDatabaseProps): React.JSX.Element => {
+  type Mode = 'Details' | 'Schema' | 'Preview';
+
+  const [selectedMode, setSelectedMode] = useState<Mode>('Details');
+  const [schemaInfoResponse, setSchemaInfoResponse] = useState<any>()
+
+  const selectedModeChange = (mode: Mode) => {
+    setSelectedMode(mode);
+  };
+
+  const toggleStyleSelection = (toggleItem: string) => {
+    if (selectedMode === toggleItem) {
+      return 'selected-header';
+    } else {
+      return 'unselected-header';
+    }
+  };
+
+  useEffect(()=>{
+    BigQueryService.getBigQuerySchemaInfoAPIService(
+      database,
+      title,
+      setSchemaInfoResponse
+    )
+  },[])
+
+  return (
+    <div className="dpms-Wrapper">
+      <div className="table-info-overlay">
+        <div className="title-overlay">{title}</div>
+        <div className="clusters-list-overlay" role="tab">
+          <div
+            role="tabpanel"
+            className={toggleStyleSelection('Details')}
+            onClick={() => selectedModeChange('Details')}
+          >
+            Details
+          </div>
+          <div
+            role="tabpanel"
+            className={toggleStyleSelection('Schema')}
+            onClick={() => selectedModeChange('Schema')}
+          >
+            Schema
+          </div>
+          <div
+            role="tabpanel"
+            className={toggleStyleSelection('Preview')}
+            onClick={() => selectedModeChange('Preview')}
+          >
+            Preview
+          </div>
+        </div>
+        {selectedMode === 'Details' && (
+          <BigQueryTableInfo title={title} dataset={database} />
+        )}
+        {selectedMode === 'Schema' && (
+          <>
+            <div className="db-title">Schema</div>
+            <BigQuerySchemaInfo column={schemaInfoResponse} />
+          </>
+        )}
+        {selectedMode === 'Preview' && (
+          <>
+            <PreviewDataInfo
+              column={schemaInfoResponse}
+              tableId={title}
+              dataSetId={database}
+            />
+          </>
+        )}
+      </div>
+    </div>
+  );
+};
+
+export class BigQueryTableWrapper extends DataprocWidget {
+  constructor(
+    title: string,
+    private database: string,
+    themeManager: IThemeManager
+  ) {
+    super(themeManager);
+  }
+
+  renderInternal(): React.JSX.Element {
+    return (
+      <BigQueryTableInfoWrapper
+        title={this.title.label}
+        database={this.database}
+      />
+    );
+  }
+}
diff --git a/src/dpms/bigQueryWidget.tsx b/src/dpms/bigQueryWidget.tsx
new file mode 100644
index 00000000..c9403251
--- /dev/null
+++ b/src/dpms/bigQueryWidget.tsx
@@ -0,0 +1,631 @@
+/**
+ * @license
+ * Copyright 2024 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 { JupyterLab } from '@jupyterlab/application';
+import React, { useEffect, useState } from 'react';
+import { Tree, NodeRendererProps, NodeApi } from 'react-arborist';
+import { LabIcon } from '@jupyterlab/ui-components';
+import bigQueryProjectIcon from '../../style/icons/bigquery_project_icon.svg';
+import datasetIcon from '../../style/icons/dataset_icon.svg';
+import tableIcon from '../../style/icons/table_icon.svg';
+import columnsIcon from '../../style/icons/columns_icon.svg';
+import databaseWidgetIcon from '../../style/icons/database_widget_icon.svg';
+import datasetsIcon from '../../style/icons/datasets_icon.svg';
+import searchIcon from '../../style/icons/search_icon.svg';
+import rightArrowIcon from '../../style/icons/right_arrow_icon.svg';
+import downArrowIcon from '../../style/icons/down_arrow_icon.svg';
+import searchClearIcon from '../../style/icons/search_clear_icon.svg';
+import { MainAreaWidget } from '@jupyterlab/apputils';
+import { v4 as uuidv4 } from 'uuid';
+import { auto } from '@popperjs/core';
+import { ClipLoader } from 'react-spinners';
+import { IThemeManager } from '@jupyterlab/apputils';
+import { IconButton, InputAdornment, TextField } from '@mui/material';
+import { TitleComponent } from '../controls/SidePanelTitleWidget';
+import { BigQueryService } from './bigQueryService';
+import { authApi } from '../utils/utils';
+import { ISettingRegistry } from '@jupyterlab/settingregistry';
+import { BigQueryDatasetWrapper } from './bigQueryDatasetInfoWrapper';
+import { BigQueryTableWrapper } from './bigQueryTableInfoWrapper';
+
+const iconDatasets = new LabIcon({
+  name: 'launcher:datasets-icon',
+  svgstr: datasetsIcon
+});
+const iconDatabaseWidget = new LabIcon({
+  name: 'launcher:databse-widget-icon',
+  svgstr: databaseWidgetIcon
+});
+const iconRightArrow = new LabIcon({
+  name: 'launcher:right-arrow-icon',
+  svgstr: rightArrowIcon
+});
+const iconDownArrow = new LabIcon({
+  name: 'launcher:down-arrow-icon',
+  svgstr: downArrowIcon
+});
+const calculateDepth = (node: NodeApi): number => {
+  let depth = 0;
+  let currentNode = node;
+  while (currentNode.parent) {
+    depth++;
+    currentNode = currentNode.parent;
+  }
+  return depth;
+};
+const BigQueryComponent = ({
+  app,
+  settingRegistry,
+  themeManager
+}: {
+  app: JupyterLab;
+  settingRegistry: ISettingRegistry;
+  themeManager: IThemeManager;
+}): JSX.Element => {
+  const iconSearchClear = new LabIcon({
+    name: 'launcher:search-clear-icon',
+    svgstr: searchClearIcon
+  });
+  const iconBigQueryProject = new LabIcon({
+    name: 'launcher:bigquery-project-icon',
+    svgstr: bigQueryProjectIcon
+  });
+  const iconDataset = new LabIcon({
+    name: 'launcher:dataset-icon',
+    svgstr: datasetIcon
+  });
+  const iconTable = new LabIcon({
+    name: 'launcher:table-icon',
+    svgstr: tableIcon
+  });
+
+  const iconColumns = new LabIcon({
+    name: 'launcher:columns-icon',
+    svgstr: columnsIcon
+  });
+  const iconSearch = new LabIcon({
+    name: 'launcher:search-icon',
+    svgstr: searchIcon
+  });
+
+  const [projectNameInfo, setProjectNameInfo] = useState<string>('');
+  const [searchTerm, setSearchTerm] = useState('');
+  const [notebookValue, setNotebookValue] = useState<string>('');
+  const [dataprocMetastoreServices, setDataprocMetastoreServices] =
+    useState('');
+  const [isLoading, setIsLoading] = useState(true);
+  const [noDpmsInstance, setNoDpmsInstance] = useState(false);
+  const [databaseNames, setDatabaseNames] = useState<string[]>([]);
+  const [schemaError, setSchemaError] = useState(false);
+
+  const [dataSetResponse, setDataSetResponse] = useState<any>();
+  const [tableResponse, setTableResponse] = useState<any>();
+  const [schemaResponse, setSchemaResponse] = useState<any>();
+
+  const [treeStructureData, setTreeStructureData] = useState<any>([]);
+
+  const [currentNode, setCurrentNode] = useState<any>();
+
+  const getBigQueryColumnDetails = async (
+    tableId: string,
+    datasetId: string
+  ) => {
+    await BigQueryService.getBigQueryColumnDetailsAPIService(
+      datasetId,
+      tableId,
+      setSchemaResponse
+    );
+  };
+
+  interface IDataEntry {
+    id: string;
+    name: string;
+    type: string;
+    description: string;
+    children: any;
+  }
+
+  const treeStructureforDatasets = () => {
+    const data = [
+      {
+        id: uuidv4(),
+        name: projectNameInfo,
+        children: databaseNames.map(datasetName => ({
+          id: uuidv4(),
+          name: datasetName,
+          children: []
+        }))
+      }
+    ];
+
+    data.sort((a, b) => a.name.localeCompare(b.name));
+
+    data.forEach(db => {
+      db.children.sort((a, b) => a.name.localeCompare(b.name));
+    });
+
+    setTreeStructureData(data);
+  };
+
+  const treeStructureforTables = () => {
+    let tempData = [...treeStructureData];
+
+    tempData[0].children.forEach((dataset: any) => {
+      if (tableResponse.length > 0 && tableResponse[0].tableReference) {
+        if (dataset.name === tableResponse[0].tableReference.datasetId) {
+          dataset['children'] = tableResponse.map((tableDetails: any) => ({
+            id: uuidv4(),
+            name: tableDetails.tableReference.tableId,
+            children: []
+          }));
+        }
+      } else {
+        if (dataset.name === tableResponse) {
+          dataset['children'] = false;
+        }
+      }
+    });
+    setTreeStructureData(tempData);
+  };
+
+  const treeStructureforSchema = () => {
+    let tempData = [...treeStructureData];
+
+    tempData[0].children.forEach((dataset: any) => {
+      if (dataset.name === schemaResponse.tableReference.datasetId) {
+        dataset.children.forEach((table: any) => {
+          if (table.name === schemaResponse.tableReference.tableId) {
+            if (schemaResponse.schema?.fields) {
+              table['children'] = schemaResponse.schema?.fields.map(
+                (column: any) => ({
+                  id: uuidv4(),
+                  name: column.name,
+                  type: column.type,
+                  mode: column.mode,
+                  key: column.key,
+                  collation: column.collation,
+                  defaultValue: column.defaultValue,
+                  policyTags: column.policyTags,
+                  dataPolicies: column.dataPolicies,
+                  tableDescription: column.tableDescription,
+                  description: column.description
+                })
+              );
+            } else {
+              table['children'] = false;
+            }
+          }
+        });
+      }
+    });
+    setTreeStructureData(tempData);
+  };
+
+  const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
+    setSearchTerm(event.target.value);
+  };
+  const searchMatch = (node: { data: { name: string } }, term: string) => {
+    return node.data.name.toLowerCase().includes(term.toLowerCase());
+  };
+  const openedWidgets: Record<string, boolean> = {};
+  const handleNodeClick = (node: NodeApi) => {
+    const depth = calculateDepth(node);
+    const widgetTitle = node.data.name;
+    if (!openedWidgets[widgetTitle]) {
+      if (depth === 2) {
+        const content = new BigQueryDatasetWrapper(node.data.name, themeManager);
+        const widget = new MainAreaWidget<BigQueryDatasetWrapper>({ content });
+        const widgetId = 'node-widget-db';
+        widget.id = widgetId;
+        widget.title.label = node.data.name;
+        widget.title.closable = true;
+        widget.title.icon = iconDatabaseWidget;
+        app.shell.add(widget, 'main');
+        widget.disposed.connect(() => {
+          const widgetTitle = widget.title.label;
+          delete openedWidgets[widgetTitle];
+        });
+      } else if (depth === 3 && node.parent) {
+        const database = node.parent.data.name;
+        
+        const content = new BigQueryTableWrapper(
+          node.data.name,
+          database,
+          themeManager
+        );
+        const widget = new MainAreaWidget<BigQueryTableWrapper>({ content });
+        const widgetId = `node-widget-${uuidv4()}`;
+        widget.id = widgetId;
+        widget.title.label = node.data.name;
+        widget.title.closable = true;
+        widget.title.icon = iconDatasets;
+        app.shell.add(widget, 'main');
+        widget.disposed.connect(() => {
+          const widgetTitle = widget.title.label;
+          delete openedWidgets[widgetTitle];
+        });
+      }
+      openedWidgets[widgetTitle] = node.data.name;
+    }
+  };
+  const handleSearchClear = () => {
+    setSearchTerm('');
+  };
+  type NodeProps = NodeRendererProps<IDataEntry> & {
+    onClick: (node: NodeRendererProps<IDataEntry>['node']) => void;
+  };
+  const Node = ({ node, style, onClick }: NodeProps) => {
+    const handleToggle = () => {
+      if (calculateDepth(node) === 2) {
+        setCurrentNode(node);
+        getBigQueryTables(node.data.name); 
+      } else if (calculateDepth(node) === 3 && node.parent) {
+        setCurrentNode(node);
+        getBigQueryColumnDetails(node.data.name, node.parent?.data?.name);   
+      } else {
+        node.toggle();
+      }
+    };
+    const handleIconClick = (event: React.MouseEvent) => {
+      if (event.currentTarget.classList.contains('caret-icon')) {
+        handleToggle();
+      }
+    };
+    const handleTextClick = (event: React.MouseEvent) => {
+      event.stopPropagation();
+      onClick(node);
+    };
+    const renderNodeIcon = () => {
+      const depth = calculateDepth(node);
+      const hasChildren =
+        (node.children && node.children.length > 0) ||
+        (depth !== 4 && node.children);
+      const arrowIcon = hasChildren ? (
+        node.isOpen ? (
+          <>
+            <div
+              role="treeitem"
+              className="caret-icon right"
+              onClick={handleIconClick}
+            >
+              <iconDownArrow.react
+                tag="div"
+                className="icon-white logo-alignment-style"
+              />
+            </div>
+          </>
+        ) : (
+          <div
+            role="treeitem"
+            className="caret-icon down"
+            onClick={handleIconClick}
+          >
+            <iconRightArrow.react
+              tag="div"
+              className="icon-white logo-alignment-style"
+            />
+          </div>
+        )
+      ) : (
+        <div style={{ width: '29px' }}></div>
+      );
+      if (searchTerm) {
+        const arrowIcon = hasChildren ? (
+          node.isOpen ? (
+            <>
+              <div
+                role="treeitem"
+                className="caret-icon right"
+                onClick={handleIconClick}
+              >
+                <iconDownArrow.react
+                  tag="div"
+                  className="icon-white logo-alignment-style"
+                />
+              </div>
+            </>
+          ) : (
+            <div
+              role="treeitem"
+              className="caret-icon down"
+              onClick={handleIconClick}
+            >
+              <iconRightArrow.react
+                tag="div"
+                className="icon-white logo-alignment-style"
+              />
+            </div>
+          )
+        ) : (
+          <div style={{ width: '29px' }}></div>
+        );
+        if (depth === 1) {
+          return (
+            <>
+              {arrowIcon}
+              <div role="img" className="db-icon" onClick={handleIconClick}>
+                <iconBigQueryProject.react
+                  tag="div"
+                  className="icon-white logo-alignment-style"
+                />
+              </div>
+            </>
+          );
+        } else if (depth === 2) {
+          return (
+            <>
+              {arrowIcon}
+              <div role="img" className="db-icon" onClick={handleIconClick}>
+                <iconDataset.react
+                  tag="div"
+                  className="icon-white logo-alignment-style"
+                />
+              </div>
+            </>
+          );
+        } else if (depth === 3) {
+          return (
+            <>
+              {arrowIcon}
+              <div role="img" className="table-icon" onClick={handleIconClick}>
+                <iconTable.react
+                  tag="div"
+                  className="icon-white logo-alignment-style"
+                />
+              </div>
+            </>
+          );
+        }
+
+        return (
+          <>
+            <iconColumns.react
+              tag="div"
+              className="icon-white logo-alignment-style"
+            />
+          </>
+        );
+      }
+      if (depth === 1) {
+        return (
+          <>
+            {arrowIcon}
+            <div role="img" className="db-icon" onClick={handleIconClick}>
+              <iconBigQueryProject.react
+                tag="div"
+                className="icon-white logo-alignment-style"
+              />
+            </div>
+          </>
+        );
+      } else if (depth === 2) {
+        return (
+          <>
+            {arrowIcon}
+            <div role="img" className="db-icon" onClick={handleIconClick}>
+              <iconDataset.react
+                tag="div"
+                className="icon-white logo-alignment-style"
+              />
+            </div>
+          </>
+        );
+      } else if (depth === 3) {
+        return (
+          <>
+            {arrowIcon}
+            <div role="img" className="table-icon" onClick={handleIconClick}>
+              <iconTable.react
+                tag="div"
+                className="icon-white logo-alignment-style"
+              />
+            </div>
+          </>
+        );
+      }
+
+      return (
+        <>
+          <iconColumns.react
+            tag="div"
+            className="icon-white logo-alignment-style"
+          />
+        </>
+      );
+    };
+
+    return (
+      <div style={style}>
+        {renderNodeIcon()}
+        <div
+          role="treeitem"
+          title={
+            node.data.children && node.data.children.length > 0
+              ? node.data.children[0]?.tableDescription
+              : ''
+          }
+          onClick={handleTextClick}
+        >
+          {node.data.name}
+        </div>
+        <div className="dpms-column-type-text">{node.data.type}</div>
+      </div>
+    );
+  };
+
+  const getBigQueryDatasets = async () => {
+    const credentials: any = await authApi();
+    if (credentials) {
+      setProjectNameInfo(credentials.project_id);
+    }
+    await BigQueryService.getBigQueryDatasetsAPIService(
+      notebookValue,
+      settingRegistry,
+      setDatabaseNames,
+      setDataSetResponse,
+      setSchemaError,
+      setIsLoading
+    );
+  };
+
+  const getBigQueryTables = async (datasetId: string) => {
+    await BigQueryService.getBigQueryTableAPIService(
+      notebookValue,
+      datasetId,
+      setDatabaseNames,
+      setTableResponse,
+      setSchemaError
+    );
+  };
+
+  const getActiveNotebook = () => {
+    const notebookVal = localStorage.getItem('notebookValue');
+    if (notebookVal?.includes('bigframes')) {
+      setNotebookValue('bigframes');
+      setDataprocMetastoreServices('bigframes');
+    } else {
+      setNoDpmsInstance(true);
+    }
+  };
+  useEffect(() => {
+    getActiveNotebook();
+    return () => {
+      setNotebookValue('');
+    };
+  }, [notebookValue]);
+
+  useEffect(() => {
+    getBigQueryDatasets();
+  }, [dataprocMetastoreServices]);
+
+  useEffect(() => {
+    treeStructureforDatasets();
+  }, [dataSetResponse]);
+
+  useEffect(() => {
+    if (tableResponse) {
+      treeStructureforTables();
+    }
+  }, [tableResponse]);
+
+  useEffect(() => {
+    if (schemaResponse) {
+      treeStructureforSchema();
+    }
+  }, [schemaResponse]);
+
+  useEffect(() => {
+    if (treeStructureData.length > 0 && treeStructureData[0].name !== '') {
+      setIsLoading(false);
+    }
+    if (currentNode) {
+      currentNode?.toggle();
+    }
+  }, [treeStructureData]);
+
+  return (
+    <div className="dpms-Wrapper">
+      <TitleComponent titleStr="Dataset Explorer" isPreview />
+      {!noDpmsInstance ? (
+        <>
+          <div>
+            {isLoading ? (
+              <div className="database-loader">
+                <div>
+                  <ClipLoader
+                    color="#3367d6"
+                    loading={true}
+                    size={20}
+                    aria-label="Loading Spinner"
+                    data-testid="loader"
+                  />
+                </div>
+                Loading datasets
+              </div>
+            ) : (
+              <>
+                <div className="search-field">
+                  <TextField
+                    placeholder="Search your DBs and tables"
+                    type="text"
+                    variant="outlined"
+                    fullWidth
+                    size="small"
+                    onChange={handleSearch}
+                    value={searchTerm}
+                    InputProps={{
+                      startAdornment: (
+                        <InputAdornment position="start">
+                          <iconSearch.react
+                            tag="div"
+                            className="icon-white logo-alignment-style"
+                          />
+                        </InputAdornment>
+                      ),
+                      endAdornment: searchTerm && (
+                        <IconButton
+                          aria-label="toggle password visibility"
+                          onClick={handleSearchClear}
+                        >
+                          <iconSearchClear.react
+                            tag="div"
+                            className="icon-white logo-alignment-style search-clear-icon"
+                          />
+                        </IconButton>
+                      )
+                    }}
+                  />
+                </div>
+                <div className="tree-container">
+                  {treeStructureData.length > 0 &&
+                    treeStructureData[0].name !== '' && (
+                      <Tree
+                        className="dataset-tree"
+                        data={treeStructureData}
+                        openByDefault={false}
+                        indent={24}
+                        width={auto}
+                        height={765}
+                        rowHeight={36}
+                        overscanCount={1}
+                        paddingTop={30}
+                        paddingBottom={10}
+                        padding={25}
+                        searchTerm={searchTerm}
+                        searchMatch={searchMatch}
+                        idAccessor={(node: any) => node.id}
+                      >
+                        {(props: NodeRendererProps<any>) => (
+                          <Node {...props} onClick={handleNodeClick} />
+                        )}
+                      </Tree>
+                    )}
+                </div>
+              </>
+            )}
+          </div>
+        </>
+      ) : schemaError ? (
+        <div className="dpms-error">No schema available</div>
+      ) : (
+        <div className="dpms-error">DPMS schema explorer not set up</div>
+      )}
+    </div>
+  );
+};
+
+export default BigQueryComponent;
diff --git a/src/dpms/databaseInfo.tsx b/src/dpms/databaseInfo.tsx
index a1c95642..4680850c 100644
--- a/src/dpms/databaseInfo.tsx
+++ b/src/dpms/databaseInfo.tsx
@@ -18,6 +18,7 @@
 import React from 'react';
 import { IThemeManager } from '@jupyterlab/apputils';
 import { DataprocWidget } from '../controls/DataprocWidget';
+import BigQueryDatasetInfo from './bigQueryDatasetInfo';
 
 interface IDatabaseProps {
   title: string;
@@ -37,21 +38,24 @@ const DatabaseInfo = ({
   };
   const renderTable = () => {
     return (
-      <div className="table-container">
-        <table className="db-table">
-          <tbody>
-            {Object.entries(database).map(([key, value], index) => (
-              <tr
-                key={key}
-                className={index % 2 === 0 ? 'tr-row-even' : 'tr-row-odd'}
-              >
-                <td className="bold-column">{key}</td>
-                <td>{value}</td>
-              </tr>
-            ))}
-          </tbody>
-        </table>
-      </div>
+      <>
+        {dataprocMetastoreServices === 'bigframes' ? (
+          <BigQueryDatasetInfo dataset={title} />
+        ) : (
+          <div className="table-container">
+            <table className="db-table">
+              <tbody>
+                {Object.entries(database).map(([key, value], index) => (
+                  <tr key={key} className="tr-row">
+                    <td className="bold-column">{key}</td>
+                    <td>{value}</td>
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          </div>
+        )}
+      </>
     );
   };
 
@@ -59,7 +63,11 @@ const DatabaseInfo = ({
     <div>
       <div className="dpms-Wrapper">
         <div className="title-overlay">{title}</div>
-        <div className="db-title">Database info</div>
+        {dataprocMetastoreServices === 'bigframes' ? (
+          <div className="db-title">Dataset info</div>
+        ) : (
+          <div className="db-title">Database info</div>
+        )}
         {renderTable()}
       </div>
     </div>
diff --git a/src/dpms/dpmsWidget.tsx b/src/dpms/dpmsWidget.tsx
index 4554c58c..35508e7c 100644
--- a/src/dpms/dpmsWidget.tsx
+++ b/src/dpms/dpmsWidget.tsx
@@ -48,6 +48,9 @@ import { IThemeManager } from '@jupyterlab/apputils';
 import { IconButton, InputAdornment, TextField } from '@mui/material';
 import { DataprocLoggingService, LOG_LEVEL } from '../utils/loggingService';
 import { TitleComponent } from '../controls/SidePanelTitleWidget';
+import BigQueryComponent from './bigQueryWidget';
+import { ISettingRegistry } from '@jupyterlab/settingregistry';
+
 const iconDatasets = new LabIcon({
   name: 'launcher:datasets-icon',
   svgstr: datasetsIcon
@@ -88,7 +91,6 @@ const DpmsComponent = ({
     name: 'launcher:database-icon',
     svgstr: databaseIcon
   });
-
   const iconTable = new LabIcon({
     name: 'launcher:table-icon',
     svgstr: tableIcon
@@ -164,6 +166,7 @@ const DpmsComponent = ({
         });
     }
   };
+
   interface ITableResponse {
     results: Array<{
       displayName: string;
@@ -253,15 +256,17 @@ const DpmsComponent = ({
   interface IDataEntry {
     id: string;
     name: string;
+    type: string;
     description: string;
-    children: Table[];
+    children: any;
   }
+
   const databases: { [dbName: string]: { [tableName: string]: IColumn[] } } =
     {};
 
   columnResponse.forEach((res: IColumn) => {
     /* fullyQualifiedName : dataproc_metastore:projectId.location.metastore_instance.database_name.table_name
-fetching database name from fully qualified name structure */
+      fetching database name from fully qualified name structure */
     const dbName = res.fullyQualifiedName.split('.').slice(-2, -1)[0];
     const tableName = res.displayName;
     const columns: IColumn[] = res.schema.columns.map(
@@ -271,13 +276,13 @@ fetching database name from fully qualified name structure */
         mode: string;
         description: string;
       }) => ({
-        name: column.column,
+        name: `${column.column}`,
         schema: res.schema, // Include the schema object
         fullyQualifiedName: res.fullyQualifiedName,
         displayName: res.displayName,
-        column: res.column,
-        type: res.type,
-        mode: res.mode,
+        column: res.column, //no response
+        type: column.type,
+        mode: column.mode,
         description: res.description
       })
     );
@@ -292,13 +297,14 @@ fetching database name from fully qualified name structure */
 
     databases[dbName][tableName].push(...columns);
   });
+
   const data = Object.entries(databases).map(([dbName, tables]) => ({
     id: uuidv4(),
     name: dbName,
     children: Object.entries(tables).map(([tableName, columns]) => ({
       id: uuidv4(),
       name: tableName,
-      desciption: '',
+      description: '',
       children: columns.map((column: IColumn) => ({
         id: uuidv4(),
         name: column.name,
@@ -308,6 +314,7 @@ fetching database name from fully qualified name structure */
       }))
     }))
   }));
+
   data.sort((a, b) => a.name.localeCompare(b.name));
 
   data.forEach(db => {
@@ -349,6 +356,7 @@ fetching database name from fully qualified name structure */
       } else if (depth === 2 && node.parent) {
         const database = node.parent.data.name;
         const column = node.data.children;
+
         const content = new Table(
           node.data.name,
           dataprocMetastoreServices,
@@ -422,8 +430,8 @@ fetching database name from fully qualified name structure */
         )
       ) : null;
       if (searchTerm) {
-        const arrowIcon =
-          hasChildren && node.isOpen ? (
+        const arrowIcon = hasChildren ? (
+          node.isOpen ? (
             <>
               <div
                 role="treeitem"
@@ -447,7 +455,8 @@ fetching database name from fully qualified name structure */
                 className="icon-white logo-alignment-style"
               />
             </div>
-          );
+          )
+        ) : null;
         if (depth === 1) {
           return (
             <>
@@ -522,9 +531,18 @@ fetching database name from fully qualified name structure */
     return (
       <div style={style}>
         {renderNodeIcon()}
-        <div role="treeitem" onClick={handleTextClick}>
+        <div
+          role="treeitem"
+          title={
+            node.data.children && node.data.children.length > 0
+              ? node.data.children[0]?.description
+              : ''
+          }
+          onClick={handleTextClick}
+        >
           {node.data.name}
         </div>
+        <div className="dpms-column-type-text">{node.data.type}</div>
       </div>
     );
   };
@@ -606,6 +624,7 @@ fetching database name from fully qualified name structure */
         });
     }
   };
+
   interface IClusterDetailsResponse {
     error: {
       code: number;
@@ -786,7 +805,7 @@ fetching database name from fully qualified name structure */
 
   return (
     <div className="dpms-Wrapper">
-      <TitleComponent titleStr="Metadata Explorer" isPreview />
+      <TitleComponent titleStr="Dataset Explorer" isPreview />
       {!noDpmsInstance ? (
         <>
           <div>
@@ -842,7 +861,7 @@ fetching database name from fully qualified name structure */
                     ? data[totalDatabases - 1].children.length ===
                         totalTables && (
                         <Tree
-                          className="Tree"
+                          className="database-tree"
                           initialData={data}
                           openByDefault={false}
                           indent={24}
@@ -890,11 +909,28 @@ fetching database name from fully qualified name structure */
 };
 
 export class dpmsWidget extends DataprocWidget {
-  constructor(private app: JupyterLab, themeManager: IThemeManager) {
+  app: JupyterLab;
+  settingRegistry: ISettingRegistry;
+
+  constructor(
+    app: JupyterLab,
+    settingRegistry: ISettingRegistry,
+    themeManager: IThemeManager
+  ) {
     super(themeManager);
+    this.app = app;
+    this.settingRegistry = settingRegistry;
   }
 
   renderInternal(): JSX.Element {
-    return <DpmsComponent app={this.app} themeManager={this.themeManager} />;
+    return localStorage.getItem('notebookValue')?.includes('bigframes') ? (
+      <BigQueryComponent
+        app={this.app}
+        settingRegistry={this.settingRegistry}
+        themeManager={this.themeManager}
+      />
+    ) : (
+      <DpmsComponent app={this.app} themeManager={this.themeManager} />
+    );
   }
 }
diff --git a/src/dpms/previewDataInfo.tsx b/src/dpms/previewDataInfo.tsx
new file mode 100644
index 00000000..05bf1368
--- /dev/null
+++ b/src/dpms/previewDataInfo.tsx
@@ -0,0 +1,131 @@
+/**
+ * @license
+ * Copyright 2024 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 { ClipLoader } from 'react-spinners';
+import { useGlobalFilter, usePagination, useTable } from 'react-table';
+import { PaginationView } from '../utils/paginationView';
+import TableData from '../utils/tableData';
+import { BigQueryService } from './bigQueryService';
+import { ICellProps } from '../utils/utils';
+
+const PreviewDataInfo = ({ column, tableId, dataSetId }: any) => {
+  const [previewDataList, setPreviewDataList] = useState([]);
+  const [isLoading, setIsLoading] = useState(true);
+
+  const data = previewDataList;
+
+  const columns = React.useMemo(
+    () =>
+      column.map((column: any) => ({
+        Header: column.name.split(' ')[0],
+        accessor: column.name.split(' ')[0]
+      })),
+    []
+  );
+
+  const {
+    getTableProps,
+    getTableBodyProps,
+    headerGroups,
+    rows,
+    prepareRow,
+    page,
+    canPreviousPage,
+    canNextPage,
+    nextPage,
+    previousPage,
+    setPageSize,
+    state: { pageIndex, pageSize }
+  } = useTable(
+    { columns, data, autoResetPage: false, initialState: { pageSize: 50 } },
+    useGlobalFilter,
+    usePagination
+  );
+
+  useEffect(() => {
+    BigQueryService.bigQueryPreviewAPIService(
+      columns,
+      tableId,
+      dataSetId,
+      setIsLoading,
+      setPreviewDataList
+    );
+  }, []);
+
+  const tableDataCondition = (cell: ICellProps) => {
+    return (
+      <td {...cell.getCellProps()} className="preview-table-data">
+        {cell.value === null ? 'null' : cell.render('Cell')}
+      </td>
+    );
+  };
+
+  return (
+    <div>
+      {previewDataList.length > 0 ? (
+        <div>
+          <div className="preview-data-table-parent">
+            <TableData
+              getTableProps={getTableProps}
+              headerGroups={headerGroups}
+              getTableBodyProps={getTableBodyProps}
+              isLoading={isLoading}
+              rows={rows}
+              page={page}
+              prepareRow={prepareRow}
+              tableDataCondition={tableDataCondition}
+              fromPage="Preview"
+            />
+            {previewDataList.length > 50 && (
+              <PaginationView
+                pageSize={pageSize}
+                setPageSize={setPageSize}
+                pageIndex={pageIndex}
+                allData={previewDataList}
+                previousPage={previousPage}
+                nextPage={nextPage}
+                canPreviousPage={canPreviousPage}
+                canNextPage={canNextPage}
+              />
+            )}
+          </div>
+        </div>
+      ) : (
+        <div>
+          {isLoading && (
+            <div className="spin-loader-main">
+              <ClipLoader
+                color="#3367d6"
+                loading={true}
+                size={18}
+                aria-label="Loading Spinner"
+                data-testid="loader"
+              />
+              Loading Preview Data
+            </div>
+          )}
+          {!isLoading && (
+            <div className="no-data-style">No rows to display</div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default PreviewDataInfo;
diff --git a/src/dpms/schemaInfo.tsx b/src/dpms/schemaInfo.tsx
new file mode 100644
index 00000000..c42fdbd2
--- /dev/null
+++ b/src/dpms/schemaInfo.tsx
@@ -0,0 +1,99 @@
+/**
+ * @license
+ * Copyright 2024 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 } from 'react-table';
+
+interface IColumn {
+  name: string;
+  type: string;
+  mode: string;
+  description: string;
+}
+
+const SchemaInfo = ({ column }: any) => {
+  const columns = [
+    {
+      Header: 'Field name',
+      accessor: 'name'
+    },
+    {
+      Header: 'Type',
+      accessor: 'type'
+    },
+    {
+      Header: 'Mode',
+      accessor: 'mode'
+    },
+    {
+      Header: 'Description',
+      accessor: 'description'
+    }
+  ];
+
+  const data = column.map((column: IColumn) => ({
+    name: column.name,
+    type: column.type || '',
+    mode: column.mode || '',
+    description: column.description
+  }));
+
+  const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
+    // @ts-ignore  react-table 'columns' which is declared here on type 'TableOptions<IColumns>'
+    useTable({ columns, data });
+
+  return (
+    <div className="dpms-Wrapper">
+      <div className="table-container">
+        <table className="schema-table" {...getTableProps()}>
+          <thead>
+            {headerGroups.map(headerGroup => (
+              <tr {...headerGroup.getHeaderGroupProps()}>
+                {headerGroup.headers.map(column => (
+                  <th {...column.getHeaderProps()}>
+                    {column.render('Header')}
+                  </th>
+                ))}
+              </tr>
+            ))}
+          </thead>
+          <tbody {...getTableBodyProps()}>
+            {rows.map(row => {
+              prepareRow(row);
+              return (
+                <tr {...row.getRowProps()}>
+                  {row.cells.map((cell, index) => {
+                    return (
+                      <td
+                        className={index === 0 ? 'bold-column' : ''}
+                        {...cell.getCellProps()}
+                      >
+                        {cell.render('Cell')}
+                      </td>
+                    );
+                  })}
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>
+    </div>
+  );
+};
+
+export default SchemaInfo;
diff --git a/src/dpms/tableInfo.tsx b/src/dpms/tableInfo.tsx
index 0ff580a9..aeac4425 100644
--- a/src/dpms/tableInfo.tsx
+++ b/src/dpms/tableInfo.tsx
@@ -15,10 +15,11 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import { useTable } from 'react-table';
+import React, { useState } from 'react';
 import { IThemeManager } from '@jupyterlab/apputils';
-import { DataprocWidget} from '../controls/DataprocWidget';
+import { DataprocWidget } from '../controls/DataprocWidget';
+import SchemaInfo from './schemaInfo';
+
 interface IColumn {
   name: string;
   type: string;
@@ -47,107 +48,65 @@ const TableInfo = ({
     'Dataproc Metastore Instance': dataprocMetastoreServices
   };
 
-  const renderColumnTable = () => {
-    const columns = React.useMemo(
-      () => [
-        {
-          Header: 'Field name',
-          accessor: 'name'
-        },
-        {
-          Header: 'Type',
-          accessor: 'type'
-        },
-        {
-          Header: 'Mode',
-          accessor: 'mode'
-        },
-        {
-          Header: 'Description',
-          accessor: 'description'
-        }
-      ],
-      []
-    );
+  type Mode = 'Details' | 'Schema' 
 
-    const data = React.useMemo(() => {
-      return column.map((column: IColumn) => ({
-        name: column.name,
-        type: column.type || '',
-        mode: column.mode || '',
-        description: column.description
-      }));
-    }, [column]);
+  const [selectedMode, setSelectedMode] = useState<Mode>('Details');
 
-    const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
-      // @ts-ignore  react-table 'columns' which is declared here on type 'TableOptions<IColumns>'
-      useTable({ columns, data });
+  const selectedModeChange = (mode: Mode) => {
+    setSelectedMode(mode);
+  };
 
-    return (
-      <div className="dpms-Wrapper">
-        <div className="table-container">
-          <table
-            className= 'schema-table'
-            {...getTableProps()}
-          >
-            <thead>
-              {headerGroups.map(headerGroup => (
-                <tr {...headerGroup.getHeaderGroupProps()}>
-                  {headerGroup.headers.map(column => (
-                    <th {...column.getHeaderProps()}>
-                      {column.render('Header')}
-                    </th>
-                  ))}
-                </tr>
-              ))}
-            </thead>
-            <tbody {...getTableBodyProps()}>
-              {rows.map(row => {
-                prepareRow(row);
-                return (
-                  <tr {...row.getRowProps()}>
-                    {row.cells.map((cell, index) => {
-                      return (
-                        <td
-                          className={index === 0 ? 'bold-column' : ''}
-                          {...cell.getCellProps()}
-                        >
-                          {cell.render('Cell')}
-                        </td>
-                      );
-                    })}
-                  </tr>
-                );
-              })}
-            </tbody>
-          </table>
-        </div>
-      </div>
-    );
+  const toggleStyleSelection = (toggleItem: string) => {
+    if (selectedMode === toggleItem) {
+      return 'selected-header';
+    } else {
+      return 'unselected-header';
+    }
   };
 
   return (
     <div className="dpms-Wrapper">
       <div className="table-info-overlay">
         <div className="title-overlay">{title}</div>
-        <div className="db-title">Table info</div>
-        <div className="table-container">
-          <table className="db-table">
-            <tbody>
-              {Object.entries(table).map(([key, value], index) => (
-                <tr
-                  key={key}
-                  className={index % 2 === 0 ? 'tr-row-even' : 'tr-row-odd'}
-                >
-                  <td className="bold-column">{key}</td>
-                  <td>{value}</td>
-                </tr>
-              ))}
-            </tbody>
-          </table>
+        <div className="clusters-list-overlay" role="tab">
+          <div
+            role="tabpanel"
+            className={toggleStyleSelection('Details')}
+            onClick={() => selectedModeChange('Details')}
+          >
+            Details
+          </div>
+          <div
+            role="tabpanel"
+            className={toggleStyleSelection('Schema')}
+            onClick={() => selectedModeChange('Schema')}
+          >
+            Schema
+          </div>
         </div>
-        <div className="db-title">Schema</div>
-        {renderColumnTable()}
+        {selectedMode === 'Details' &&
+            <>
+              <div className="db-title">Table info</div>
+              <div className="table-container">
+                <table className="db-table">
+                  <tbody>
+                    {Object.entries(table).map(([key, value], index) => (
+                      <tr key={key} className="tr-row">
+                        <td className="bold-column">{key}</td>
+                        <td>{value}</td>
+                      </tr>
+                    ))}
+                  </tbody>
+                </table>
+              </div>
+            </>
+          }
+        {selectedMode === 'Schema' && (
+          <>
+            <div className="db-title">Schema</div>
+              <SchemaInfo column={column} />
+          </>
+        )}
       </div>
     </div>
   );
diff --git a/src/index.ts b/src/index.ts
index 05c92873..170f9e0f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -37,11 +37,11 @@ import storageIcon from '../style/icons/storage_icon.svg';
 import { Panel, Title, Widget } from '@lumino/widgets';
 import { AuthLogin } from './login/authLogin';
 import { KernelAPI, KernelSpecAPI } from '@jupyterlab/services';
-import { iconDisplay } from './utils/utils';
+import { authApi, iconDisplay } from './utils/utils';
 import { dpmsWidget } from './dpms/dpmsWidget';
 import dpmsIcon from '../style/icons/dpms_icon.svg';
 import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
-import { TITLE_LAUNCHER_CATEGORY } from './utils/const';
+import { PLUGIN_ID, TITLE_LAUNCHER_CATEGORY } from './utils/const';
 import { RuntimeTemplate } from './runtime/runtimeTemplate';
 import {
   IFileBrowserFactory,
@@ -56,13 +56,19 @@ import { GCSDrive } from './gcs/gcsDrive';
 import { GcsBrowserWidget } from './gcs/gcsBrowserWidget';
 import { DataprocLoggingService } from './utils/loggingService';
 import { NotebookScheduler } from './scheduler/notebookScheduler';
+import pythonLogo from '../third_party/icons/python_logo.svg';
+import NotebookTemplateService from './notebookTemplates/notebookTemplatesService';
+import * as path from 'path';
+import { requestAPI } from './handler/handler';
 
 const iconDpms = new LabIcon({
   name: 'launcher:dpms-icon',
   svgstr: dpmsIcon
 });
-
-const PLUGIN_ID = 'dataproc_jupyter_plugin:plugin';
+const iconPythonLogo = new LabIcon({
+  name: 'launcher:python-bigquery-logo-icon',
+  svgstr: pythonLogo
+});
 
 const extension: JupyterFrontEndPlugin<void> = {
   id: PLUGIN_ID,
@@ -128,7 +134,10 @@ const extension: JupyterFrontEndPlugin<void> = {
     window.addEventListener('beforeunload', () => {
       localStorage.removeItem('notebookValue');
     });
-
+    interface SettingsResponse {
+      enable_bigquery_integration?: boolean; 
+  }
+    let bqFeature:SettingsResponse = await requestAPI('settings')
     // START -- Enable Preview Features.
     const settings = await settingRegistry.load(PLUGIN_ID);
 
@@ -139,7 +148,6 @@ const extension: JupyterFrontEndPlugin<void> = {
     settings.changed.connect(() => {
       onPreviewEnabledChanged();
     });
-
     /**
      * Handler for when the Jupyter Lab theme changes.
      */
@@ -175,9 +183,16 @@ const extension: JupyterFrontEndPlugin<void> = {
         gcsDrive = undefined;
       } else {
         // Preview was enabled, (re)create DPMS and GCS.
+        if(!panelDpms && !panelGcs){
         panelDpms = new Panel();
         panelDpms.id = 'dpms-tab';
-        panelDpms.addWidget(new dpmsWidget(app as JupyterLab, themeManager));
+        panelDpms.addWidget(
+          new dpmsWidget(
+            app as JupyterLab,
+            settingRegistry as ISettingRegistry,
+            themeManager
+          )
+        );
         panelGcs = new Panel();
         panelGcs.id = 'GCS-bucket-tab';
         gcsDrive = new GCSDrive();
@@ -190,6 +205,7 @@ const extension: JupyterFrontEndPlugin<void> = {
         app.shell.add(panelGcs, 'left', { rank: 1001 });
         app.shell.add(panelDpms, 'left', { rank: 1000 });
       }
+      }
     };
 
     onPreviewEnabledChanged();
@@ -209,7 +225,11 @@ const extension: JupyterFrontEndPlugin<void> = {
           widget.dispose();
         }
       });
-      const newWidget = new dpmsWidget(app as JupyterLab, themeManager);
+      const newWidget = new dpmsWidget(
+        app as JupyterLab,
+        settingRegistry as ISettingRegistry,
+        themeManager
+      );
       panelDpms.addWidget(newWidget);
     };
 
@@ -269,6 +289,7 @@ const extension: JupyterFrontEndPlugin<void> = {
         document.title = title.label;
       }
     };
+
     labShell.currentChanged.connect(async (_, change) => {
       await KernelAPI.listRunning();
       const { oldValue, newValue } = change;
@@ -283,10 +304,15 @@ const extension: JupyterFrontEndPlugin<void> = {
       }
       if (newValue) {
         // Check if the new value is an instance of NotebookPanel
-
         if (newValue instanceof NotebookPanel) {
-          newValue.title.changed.connect(onTitleChanged);
-          newValue.toolbar.update();
+          if (newValue.title.label.includes('bigframes')) {
+            localStorage.setItem('notebookValue', 'bigframes');
+            localStorage.setItem('oldNotebookValue', 'bigframes');
+            loadDpmsWidget('');
+          } else {
+            newValue.title.changed.connect(onTitleChanged);
+            newValue.toolbar.update();
+          }
         } else if (
           (newValue.title.label === 'Launcher' ||
             newValue.title.label === 'Config Setup' ||
@@ -318,7 +344,9 @@ const extension: JupyterFrontEndPlugin<void> = {
             let oldNotebook = localStorage.getItem('oldNotebookValue');
             localStorage.setItem('notebookValue', oldNotebook || '');
             lastClusterName = localStorage.getItem('notebookValue') || '';
-            loadDpmsWidget(oldNotebook || '');
+            if (!oldNotebook?.includes('bigframes')) {
+              loadDpmsWidget(oldNotebook || '');
+            }
           }
         }
       }
@@ -327,6 +355,78 @@ const extension: JupyterFrontEndPlugin<void> = {
     const kernelSpecs = await KernelSpecAPI.getSpecs();
     const kernels = kernelSpecs.kernelspecs;
 
+    const downloadNotebook = async (
+      notebookContent: any,
+      notebookUrl: string
+    ) => {
+      const contentsManager = app.serviceManager.contents;
+      // Get the current file browser tracker
+      const { tracker } = factory;
+      // Get the current active widget in the file browser
+      const widget = tracker.currentWidget;
+      if (!widget) {
+        console.error('No active file browser widget found.');
+        return;
+      }
+
+      // Define the path to the 'bigQueryNotebookDownload' folder within the local application directory
+      const bigQueryNotebookDownloadFolderPath = widget.model.path.includes(
+        'gs:'
+      )
+        ? ''
+        : widget.model.path;
+
+      // Define the path to the 'bigQueryNotebookDownload' folder within the local application directory
+
+      const urlParts = notebookUrl.split('/');
+      const filePath = `${bigQueryNotebookDownloadFolderPath}${path.sep}${
+        urlParts[urlParts.length - 1]
+      }`;
+
+      const credentials = await authApi();
+      if (credentials) {
+        notebookContent.cells[2].source[1] = `PROJECT_ID = '${credentials.project_id}' \n`;
+        notebookContent.cells[2].source[2] = `REGION = '${
+          settings.get('bqRegion')['composite']
+        }'\n`;
+      }
+
+      // Save the file to the workspace
+      await contentsManager.save(filePath, {
+        type: 'file',
+        format: 'text',
+        content: JSON.stringify(notebookContent)
+      });
+
+      // Refresh the file fileBrowser to reflect the new file
+      app.shell.currentWidget?.update();
+
+      app.commands.execute('docmanager:open', {
+        path: filePath
+      });
+    };
+
+    const openBigQueryNotebook = async () => {
+      const template = {
+        url: 'https://raw.githubusercontent.com/GoogleCloudPlatform/dataproc-ml-quickstart-notebooks/main/public_datasets/bigframes/bigframes_quickstart.ipynb'
+      };
+      await NotebookTemplateService.handleClickService(
+        template,
+        downloadNotebook
+      );
+    };
+
+    const createBigQueryNotebookComponentCommand =
+      'create-bigquery-notebook-component';
+    commands.addCommand(createBigQueryNotebookComponentCommand, {
+      caption: 'BigQuery DataFrames',
+      label: 'BigQuery DataFrames',
+      icon: iconPythonLogo,
+      execute: async () => {
+        await openBigQueryNotebook();
+      }
+    });
+
     const createRuntimeTemplateComponentCommand =
       'create-runtime-template-component';
     commands.addCommand(createRuntimeTemplateComponentCommand, {
@@ -415,11 +515,12 @@ const extension: JupyterFrontEndPlugin<void> = {
 
     const createAuthLoginComponentCommand = 'cloud-dataproc-settings:configure';
     commands.addCommand(createAuthLoginComponentCommand, {
-      label: 'Cloud Dataproc Settings',
+      label: 'Google BigQuery Settings',
       execute: () => {
         const content = new AuthLogin(
           app as JupyterLab,
           launcher as ILauncher,
+          settingRegistry as ISettingRegistry,
           themeManager
         );
         const widget = new MainAreaWidget<AuthLogin>({ content });
@@ -432,6 +533,13 @@ const extension: JupyterFrontEndPlugin<void> = {
     let serverlessIndex = -1;
 
     if (launcher) {
+      if (bqFeature.enable_bigquery_integration){
+      launcher.add({
+        command: createBigQueryNotebookComponentCommand,
+        category: 'BigQuery Notebooks',
+        rank: 1
+      });
+    }
       Object.values(kernels).forEach((kernelsData, index) => {
         if (
           kernelsData?.resources.endpointParentResource &&
diff --git a/src/login/authLogin.tsx b/src/login/authLogin.tsx
index c94659f5..8187df7f 100644
--- a/src/login/authLogin.tsx
+++ b/src/login/authLogin.tsx
@@ -27,6 +27,7 @@ import { DataprocWidget } from '../controls/DataprocWidget';
 import { IThemeManager } from '@jupyterlab/apputils';
 import { ILauncher } from '@jupyterlab/launcher';
 import { JupyterLab } from '@jupyterlab/application';
+import { ISettingRegistry } from '@jupyterlab/settingregistry';
 
 // Create the LabIcon instance outside of the component
 const IconsigninGoogle = new LabIcon({
@@ -37,10 +38,12 @@ const IconsigninGoogle = new LabIcon({
 const AuthLoginComponent = ({
   app,
   launcher,
+  settingRegistry,
   themeManager
 }: {
   app: JupyterLab;
   launcher: ILauncher;
+  settingRegistry: ISettingRegistry,
   themeManager: IThemeManager;
 }): React.JSX.Element => {
   const [loginState, setLoginState] = useState(false);
@@ -94,6 +97,7 @@ const AuthLoginComponent = ({
           setConfigError={setConfigError}
           app={app}
           launcher={launcher}
+          settingRegistry={settingRegistry}
         />
       )}
       {loginError && (
@@ -129,14 +133,17 @@ const AuthLoginComponent = ({
 export class AuthLogin extends DataprocWidget {
   app: JupyterLab;
   launcher: ILauncher;
+  settingRegistry: ISettingRegistry;
 
   constructor(
     app: JupyterLab,
     launcher: ILauncher,
+    settingRegistry: ISettingRegistry,
     themeManager: IThemeManager
   ) {
     super(themeManager);
     this.app = app;
+    this.settingRegistry = settingRegistry,
     this.launcher = launcher;
   }
 
@@ -145,6 +152,7 @@ export class AuthLogin extends DataprocWidget {
       <AuthLoginComponent
         app={this.app}
         launcher={this.launcher}
+        settingRegistry={this.settingRegistry}
         themeManager={this.themeManager}
       />
     );
diff --git a/src/login/configSelection.tsx b/src/login/configSelection.tsx
index 56c6cfc0..1ea12f65 100644
--- a/src/login/configSelection.tsx
+++ b/src/login/configSelection.tsx
@@ -21,6 +21,7 @@ import settingsIcon from '../../style/icons/settings_icon.svg';
 import {
   API_HEADER_BEARER,
   API_HEADER_CONTENT_TYPE,
+  PLUGIN_ID,
   USER_INFO_URL,
   VERSION_DETAIL
 } from '../utils/const';
@@ -48,6 +49,9 @@ import { JupyterLab } from '@jupyterlab/application';
 import { ILauncher } from '@jupyterlab/launcher';
 import { DataprocLoggingService, LOG_LEVEL } from '../utils/loggingService';
 
+import { ISettingRegistry } from '@jupyterlab/settingregistry';
+import { BigQueryRegionDropdown } from '../controls/BigQueryRegionDropdown';
+
 const iconExpandLess = new LabIcon({
   name: 'launcher:expand-less-icon',
   svgstr: expandLessIcon
@@ -61,13 +65,15 @@ interface IConfigSelectionProps {
   setConfigError: (error: boolean) => void;
   app: JupyterLab;
   launcher: ILauncher;
+  settingRegistry: ISettingRegistry;
 }
 
 function ConfigSelection({
   configError,
   setConfigError,
   app,
-  launcher
+  launcher,
+  settingRegistry
 }: IConfigSelectionProps) {
   const Iconsettings = new LabIcon({
     name: 'launcher:settings_icon',
@@ -76,6 +82,7 @@ function ConfigSelection({
 
   const [projectId, setProjectId] = useState('');
   const [region, setRegion] = useState('');
+  const [bigQueryRegion, setBigQueryRegion] = useState<any>('');
   const [isSaving, setIsSaving] = useState(false);
   const [isLoadingUser, setIsLoadingUser] = useState(true);
   const [userInfo, setUserInfo] = useState({
@@ -102,6 +109,8 @@ function ConfigSelection({
           if (configStatus.includes('Failed')) {
             toast.error(configStatus, toastifyCustomStyle);
           } else {
+            const settings = await settingRegistry.load(PLUGIN_ID);
+            settings.set('bqRegion', bigQueryRegion);
             toast.success(
               `${configStatus} - You will need to restart Jupyter in order for the new project and region to fully take effect.`,
               toastifyCustomStyle
@@ -178,7 +187,14 @@ function ConfigSelection({
     setExpandRuntimeTemplate(runTimeMode);
   };
 
+  const handleSettingsRegistry = async () => {
+    const settings = await settingRegistry.load(PLUGIN_ID);
+    setBigQueryRegion(settings.get('bqRegion')['composite']);
+  };
+
   useEffect(() => {
+    handleSettingsRegistry();
+
     authApi().then(credentials => {
       displayUserInfo(credentials);
       setSelectedRuntimeClone(undefined);
@@ -222,7 +238,7 @@ function ConfigSelection({
             <div className="settings-text">Settings</div>
           </div>
           <div className="settings-separator"></div>
-          <div className="project-header">Project Info </div>
+          <div className="project-header">Google Cloud Project Settings </div>
           <div className="config-overlay">
             <div className="config-form">
               <div className="project-overlay">
@@ -230,7 +246,7 @@ function ConfigSelection({
                   value={projectId}
                   onChange={(_, projectId) => setProjectId(projectId ?? '')}
                   fetchFunc={projectListAPI}
-                  label="Project ID"
+                  label="Project ID*"
                   // Always show the clear indicator and hide the dropdown arrow
                   // make it very clear that this is an autocomplete.
                   sx={{
@@ -249,6 +265,16 @@ function ConfigSelection({
                   onRegionChange={region => setRegion(region)}
                 />
               </div>
+              <div className="bigquery-region-header">BigQuery Settings </div>
+              <div className="region-overlay">
+                <BigQueryRegionDropdown
+                  projectId={projectId}
+                  region={bigQueryRegion}
+                  onRegionChange={bigQueryRegion =>
+                    setBigQueryRegion(bigQueryRegion)
+                  }
+                />
+              </div>
               <div className="save-overlay">
                 <Button
                   variant="contained"
@@ -321,7 +347,7 @@ function ConfigSelection({
           <div>
             <div className="runtime-title-section">
               <div className="runtime-title-part">
-                Serverless Runtime Templates
+                Dataproc Serverless Runtime Templates
               </div>
               <div
                 className="expand-icon"
diff --git a/src/scheduler/createNotebookScheduler.tsx b/src/scheduler/createNotebookScheduler.tsx
index 71f90870..0861f368 100644
--- a/src/scheduler/createNotebookScheduler.tsx
+++ b/src/scheduler/createNotebookScheduler.tsx
@@ -119,6 +119,8 @@ const CreateNotebookScheduler = ({
   const [dagListCall, setDagListCall] = useState(false);
   const [isLoadingKernelDetail, setIsLoadingKernelDetail] = useState(false);
 
+  const [isBigQueryNotebook, setIsBigQueryNotebook] = useState(false);
+
   const listClustersAPI = async () => {
     await SchedulerService.listClustersAPIService(setClusterList);
   };
@@ -358,6 +360,10 @@ const CreateNotebookScheduler = ({
     listSessionTemplatesAPI();
     if (context !== '') {
       setInputFileSelected(context.path);
+      if (context.path.toLowerCase().startsWith('bigframes')) {
+        setIsBigQueryNotebook(true);
+        setSelectedMode('serverless')
+      }
     }
     setJobNameSelected('');
     if (!editMode) {
@@ -528,33 +534,37 @@ const CreateNotebookScheduler = ({
                 fromPage="scheduler"
               />
             </>
-            <div className="create-scheduler-form-element">
-              <FormControl>
-                <RadioGroup
-                  aria-labelledby="demo-controlled-radio-buttons-group"
-                  name="controlled-radio-buttons-group"
-                  value={selectedMode}
-                  onChange={handleSelectedModeChange}
-                  row={true}
-                >
-                  <FormControlLabel
-                    value="cluster"
-                    control={<Radio size="small" />}
-                    label={
-                      <Typography sx={{ fontSize: 13 }}>Cluster</Typography>
-                    }
-                  />
-                  <FormControlLabel
-                    value="serverless"
-                    className="create-scheduler-label-style"
-                    control={<Radio size="small" />}
-                    label={
-                      <Typography sx={{ fontSize: 13 }}>Serverless</Typography>
-                    }
-                  />
-                </RadioGroup>
-              </FormControl>
-            </div>
+            {!isBigQueryNotebook && (
+              <div className="create-scheduler-form-element">
+                <FormControl>
+                  <RadioGroup
+                    aria-labelledby="demo-controlled-radio-buttons-group"
+                    name="controlled-radio-buttons-group"
+                    value={selectedMode}
+                    onChange={handleSelectedModeChange}
+                    row={true}
+                  >
+                    <FormControlLabel
+                      value="cluster"
+                      control={<Radio size="small" />}
+                      label={
+                        <Typography sx={{ fontSize: 13 }}>Cluster</Typography>
+                      }
+                    />
+                    <FormControlLabel
+                      value="serverless"
+                      className="create-scheduler-label-style"
+                      control={<Radio size="small" />}
+                      label={
+                        <Typography sx={{ fontSize: 13 }}>
+                          Serverless
+                        </Typography>
+                      }
+                    />
+                  </RadioGroup>
+                </FormControl>
+              </div>
+            )}
             <div className="create-scheduler-form-element">
               {isLoadingKernelDetail && (
                 <ClipLoader
@@ -565,17 +575,19 @@ const CreateNotebookScheduler = ({
                   data-testid="loader"
                 />
               )}
-              {selectedMode === 'cluster' && !isLoadingKernelDetail && (
-                <Autocomplete
-                  className="create-scheduler-style"
-                  options={clusterList}
-                  value={clusterSelected}
-                  onChange={(_event, val) => handleClusterSelected(val)}
-                  renderInput={params => (
-                    <TextField {...params} label="Cluster*" />
-                  )}
-                />
-              )}
+              {!isBigQueryNotebook &&
+                selectedMode === 'cluster' &&
+                !isLoadingKernelDetail && (
+                  <Autocomplete
+                    className="create-scheduler-style"
+                    options={clusterList}
+                    value={clusterSelected}
+                    onChange={(_event, val) => handleClusterSelected(val)}
+                    renderInput={params => (
+                      <TextField {...params} label="Cluster*" />
+                    )}
+                  />
+                )}
               {selectedMode === 'serverless' && !isLoadingKernelDetail && (
                 <Autocomplete
                   className="create-scheduler-style"
@@ -588,7 +600,7 @@ const CreateNotebookScheduler = ({
                 />
               )}
             </div>
-            {selectedMode === 'cluster' && (
+            {!isBigQueryNotebook && selectedMode === 'cluster' && (
               <div className="create-scheduler-form-element">
                 <FormGroup row={true}>
                   <FormControlLabel
diff --git a/src/scheduler/executionHistory.tsx b/src/scheduler/executionHistory.tsx
index ec807ca3..b19cfe88 100644
--- a/src/scheduler/executionHistory.tsx
+++ b/src/scheduler/executionHistory.tsx
@@ -55,11 +55,6 @@ const ExecutionHistory = ({
   const [greenListDates, setGreenListDates] = useState<string[]>([]);
   const [darkGreenListDates, setDarkGreenListDates] = useState<string[]>([]);
 
-  const handleMonthChange = () => {
-    setDagRunId('');
-    setSelectedDate(null);
-  };
-
   const handleDateSelection = (selectedValue: any) => {
     setDagRunId('');
     setSelectedDate(selectedValue);
@@ -104,7 +99,7 @@ const ExecutionHistory = ({
       darkGreenListDates.length > 0 &&
       darkGreenListDates.includes(formattedTotalViewDate);
 
-    const isSelectedExecution = [selectedDate?.date()].includes(totalViewDates);
+    const isSelectedExecution = [selectedDate?.date()].includes(totalViewDates) && selectedDate?.month() === day?.month();
     const currentDataExecution =
       [dayjs(currentDate)?.date()].includes(totalViewDates) &&
       [dayjs(currentDate)?.month()].includes(day.month());
@@ -189,7 +184,6 @@ const ExecutionHistory = ({
                 slots={{
                   day: CustomDay
                 }}
-                onMonthChange={() => handleMonthChange()}
               />
             </LocalizationProvider>
             {startDate !== '' && endDate !== '' && (
diff --git a/src/scheduler/listDagRuns.tsx b/src/scheduler/listDagRuns.tsx
index ffe671fb..5cc431d4 100644
--- a/src/scheduler/listDagRuns.tsx
+++ b/src/scheduler/listDagRuns.tsx
@@ -276,7 +276,7 @@ const ListDagRuns = ({
       setDagRunsCurrentDateList([]);
       setDagRunId('');
     }
-  }, [selectedDate, dagRunsList]);
+  }, [selectedDate]);
 
   return (
     <div>
diff --git a/src/scheduler/schedulerServices.tsx b/src/scheduler/schedulerServices.tsx
index 27c0b11d..626697fb 100644
--- a/src/scheduler/schedulerServices.tsx
+++ b/src/scheduler/schedulerServices.tsx
@@ -550,12 +550,8 @@ export class SchedulerService {
           setDarkGreenListDates(darkGreenList);
 
           setDagRunsList(transformDagRunListData);
-          setDagRunId(
-            transformDagRunListData[transformDagRunListData.length - 1].dagRunId
-          );
         } else {
           setDagRunsList([]);
-          setDagRunId('');
           setBlueListDates([]);
           setGreyListDates([]);
           setOrangeListDates([]);
diff --git a/src/utils/const.ts b/src/utils/const.ts
index c8d70a38..a4a0449f 100644
--- a/src/utils/const.ts
+++ b/src/utils/const.ts
@@ -16,7 +16,7 @@
  */
 
 import { requestAPI } from '../handler/handler';
-export const VERSION_DETAIL = '0.1.75';
+export const VERSION_DETAIL = '0.1.76';
 export const CREATE_CLUSTER_URL =
   'https://console.cloud.google.com/dataproc/clusters';
 export const CREATE_BATCH_URL =
@@ -182,8 +182,9 @@ export enum HTTP_METHOD {
 export const NETWORK_TAG_MESSAGE =
   'Network tags are text attributes you can add to make firewall rules and routes applicable to specific VM instances.';
 export const LOGIN_ERROR_MESSAGE =
-  'Please navigate to Settings -> Cloud Dataproc Settings to login and continue';
+  'Please navigate to Settings -> Google BigQuery Settings to login and continue';
 export const NOTEBOOK_TEMPLATES_LIST_URL =
   'https://api.github.com/repos/GoogleCloudPlatform/dataproc-ml-quickstart-notebooks/contents/.ci/index.json';
 export type scheduleMode = 'runNow' | 'runSchedule';
 export const scheduleValueExpression = '30 17 * * 1-5'; //Expression for schedule Value in Scheduler Jobs
+export const PLUGIN_ID = 'dataproc_jupyter_plugin:plugin';
diff --git a/src/utils/regionService.tsx b/src/utils/regionService.tsx
index 9b25fa26..e18ba0a3 100644
--- a/src/utils/regionService.tsx
+++ b/src/utils/regionService.tsx
@@ -68,8 +68,6 @@ export function useRegion(projectId: string) {
           // so we should throw away these results.
           return;
         }
-
-        console.log(items);
         setRegions(items);
       })
       .catch((error) => {
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 97d9b82b..836e38ba 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -339,7 +339,11 @@ export const iconDisplay = (kernelType: KernelSpecAPI.ISpecModel) => {
     kernelType?.name.includes('pyspark') ||
     kernelType?.resources.endpointParentResource.includes('/sessions')
   ) {
-    return iconPysparkLogo;
+    if (kernelType?.language === 'scala') {
+      return iconScalaLogo;
+    } else {
+      return iconPysparkLogo;
+    }
   } else {
     return iconPythonLogo;
   }
diff --git a/style/authLogin.css b/style/authLogin.css
index 8ca4b0fb..a66a1914 100644
--- a/style/authLogin.css
+++ b/style/authLogin.css
@@ -290,7 +290,7 @@
   margin-right: 25px;
 }
 
-.project-header {
+.project-header, .bigquery-region-header {
   padding-top: 40px;
   padding-left: 5px;
   font-style: normal;
diff --git a/style/base.css b/style/base.css
index 8480cefd..5a5df9ef 100644
--- a/style/base.css
+++ b/style/base.css
@@ -14,6 +14,16 @@
   display: none;
 }
 
+/*Changing Icon for category with image add python bigquery*/
+.favyox6  svg[data-icon='launcher:python-bigquery-logo-icon'], .f1hgkb35 svg[data-icon='launcher:python-bigquery-logo-icon'] {
+  content-visibility: hidden;
+  display: block;
+  height: 32px;
+  margin: 0 auto;
+  width: 32px;
+  background-image: url('./icons/bigquery_logo.svg');
+}
+
 /*Changing Icon for category with image add runtime*/
 .favyox6  svg[data-icon='launcher:add-runtime-icon'], .f1hgkb35 svg[data-icon='launcher:add-runtime-icon'] {
   content-visibility: hidden;
@@ -183,7 +193,7 @@ body[data-jp-theme-name='JupyterLab Dark'] .color-icon path {
 body[data-jp-theme-name='JupyterLab Dark'] .filter-section-part {
   color: var(--jp-ui-font-color0);
 }
-body[data-jp-theme-name='JupyterLab Dark'] .schema-table th {
+body[data-jp-theme-name='JupyterLab Dark'] .schema-table th, body[data-jp-theme-name='JupyterLab Dark'] .big-query-schema-table th {
   background-color: var(--jp-layout-color2);
 }
 body[data-jp-theme-name='JupyterLab Dark'] .accordion-row-parent-header{
diff --git a/style/databaseInfo.css b/style/databaseInfo.css
index 78dd54a4..d73386c4 100644
--- a/style/databaseInfo.css
+++ b/style/databaseInfo.css
@@ -21,13 +21,14 @@
   font-weight: 600;
 }
 
-.tr-row-even {
+.tr-row:nth-child(odd) {
   background-color: var(--jp-layout-color2);
 }
 
-.tr-row-odd {
+.tr-row:nth-child(even) {
   background-color: var(--jp-layout-color1);
 }
+
 .db-table {
   width: 100%;
   border-collapse: collapse;
@@ -61,12 +62,17 @@
   border-bottom: 1px solid var(--jp-layout-color3);
 }
 
+.big-query-schema-table {
+  width: 95%;
+  border-collapse: collapse;
+}
+
 .schema-table {
   width: 50%;
   border-collapse: collapse;
 }
 
-.schema-table td {
+.schema-table td, .big-query-schema-table td {
   padding-left: 10px;
   height: 29px;
   font-size: 13px;
@@ -74,7 +80,7 @@
   border-bottom: 1px solid var(--jp-layout-color3);
 }
 
-.schema-table th {
+.schema-table th, .big-query-schema-table th {
   padding-left: 10px;
   text-align: left;
   background-color: #f5f5f5;
diff --git a/style/dpms.css b/style/dpms.css
index 4c6266f2..21d6c944 100644
--- a/style/dpms.css
+++ b/style/dpms.css
@@ -35,40 +35,64 @@
   margin-left: 20px;
 }
 
-.Tree div[role='treeitem'][aria-level='0'] > div {
+.dataset-tree div[role='treeitem'][aria-level='0'] > div {
   display: flex;
   align-items: center;
 }
 
-.Tree div[role='treeitem'][aria-level='0'] > div > div > div > svg {
+.dataset-tree div[role='treeitem'][aria-level='0'] > div > div > div > svg {
   margin-right: 5px;
   cursor: pointer;
 }
 
-.Tree div[role='treeitem'][aria-level='1'] > div {
+.dataset-tree div[role='treeitem'][aria-level='1'] > div,
+.database-tree div[role='treeitem'][aria-level='0'] > div {
   display: flex;
   align-items: center;
 }
 
-.Tree div[role='treeitem'][aria-level='1'] > div > div > div > svg {
+.dataset-tree div[role='treeitem'][aria-level='1'] > div > div > div > svg,
+.database-tree div[role='treeitem'][aria-level='0'] > div > div > div > svg {
+  margin-right: 5px;
+  cursor: pointer;
+}
+
+.dataset-tree div[role='treeitem'][aria-level='2'] > div,
+.database-tree div[role='treeitem'][aria-level='1'] > div {
+  display: flex;
+  align-items: center;
+}
+
+.dataset-tree div[role='treeitem'][aria-level='2'] > div > div > div > svg,
+.database-tree div[role='treeitem'][aria-level='1'] > div > div > div > svg {
   margin-right: 10px;
   cursor: pointer;
 }
 
-.Tree div[role='treeitem'][aria-level='2'] > div {
+.dataset-tree div[role='treeitem'][aria-level='3'] > div,
+.database-tree div[role='treeitem'][aria-level='2'] > div {
   display: flex;
   margin-left: 20px;
 }
 
-.Tree div[role='treeitem'][aria-level='2'] > div > div > svg {
+.dataset-tree div[role='treeitem'][aria-level='3'] > div > div > svg,
+.database-tree div[role='treeitem'][aria-level='2'] > div > div > svg {
   margin-right: 10px;
 }
 
-.Tree div[role='treeitem'] > div > div:last-child {
+.dataset-tree div[role='treeitem'] > div > div:nth-last-child(2),
+.database-tree div[role='treeitem'] > div > div:nth-last-child(2) {
   cursor: pointer;
 }
 
-.Tree div[role='treeitem'][aria-level='2'] > div > div:last-child {
+.dataset-tree
+  div[role='treeitem'][aria-level='3']
+  > div
+  > div:nth-last-child(2),
+.database-tree
+  div[role='treeitem'][aria-level='2']
+  > div
+  > div:nth-last-child(2) {
   cursor: context-menu !important;
 }
 
@@ -101,7 +125,8 @@
   margin-left: 10px;
 }
 
-.Tree {
+.dataset-tree,
+.database-tree {
   overflow: auto;
   height: 100% !important;
   width: auto !important;
@@ -120,3 +145,8 @@
 .css-1ua80n0-MuiInputBase-input-MuiOutlinedInput-input {
   font-size: 13px !important;
 }
+
+.dpms-column-type-text {
+  font-style: italic;
+  padding-left: 5px;
+}
diff --git a/style/icons/bigquery_logo.svg b/style/icons/bigquery_logo.svg
new file mode 100644
index 00000000..eb7ee398
--- /dev/null
+++ b/style/icons/bigquery_logo.svg
@@ -0,0 +1,16 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="bigquery/colored-32" clip-path="url(#clip0_390_8635)">
+<g id="Shape">
+<path id="Shape_2" fill-rule="evenodd" clip-rule="evenodd" d="M8.62677 14.3579V18.0489C9.20647 19.0456 10.0273 19.8818 11.0095 20.4829V14.3579H8.62677Z" fill="#809FE1"/>
+<path id="Shape_3" fill-rule="evenodd" clip-rule="evenodd" d="M13.0442 10.9722V21.5112C13.5374 21.5853 14.042 21.6308 14.5602 21.6308C15.0334 21.6308 15.4938 21.5899 15.9463 21.528V10.9722H13.0442Z" fill="#5982DB"/>
+<path id="Shape_4" fill-rule="evenodd" clip-rule="evenodd" d="M18.2943 15.809V20.4141C19.2909 19.7781 20.115 18.901 20.6776 17.8579V15.809H18.2943Z" fill="#809FE1"/>
+<path id="Shape_5" fill-rule="evenodd" clip-rule="evenodd" d="M24.0352 22.0425L22.0433 24.0354C21.8127 24.2653 21.8127 24.6408 22.0433 24.8714L27.0002 29.827C27.2301 30.0575 27.6063 30.0575 27.8362 29.827L29.8281 27.836C30.0573 27.6061 30.0573 27.23 29.8281 27.0004L24.8705 22.0425C24.6412 21.813 24.2645 21.813 24.0352 22.0425Z" fill="#3367D6"/>
+<path id="Shape_6" fill-rule="evenodd" clip-rule="evenodd" d="M14.6146 2C7.648 2 2 7.6479 2 14.6152C2 21.5819 7.648 27.2299 14.6146 27.2299C21.5812 27.2299 27.2292 21.5819 27.2292 14.6153C27.2293 7.6479 21.5813 2 14.6146 2ZM14.6147 23.9611C9.4529 23.9611 5.2688 19.777 5.2688 14.6152C5.2688 9.4534 9.4529 5.2684 14.6147 5.2684C19.7765 5.2684 23.9612 9.4535 23.9612 14.6152C23.9612 19.7769 19.7764 23.9611 14.6147 23.9611Z" fill="#5982DB"/>
+</g>
+</g>
+<defs>
+<clipPath id="clip0_390_8635">
+<rect width="32" height="32" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/style/icons/bigquery_project_icon.svg b/style/icons/bigquery_project_icon.svg
new file mode 100644
index 00000000..86368d0d
--- /dev/null
+++ b/style/icons/bigquery_project_icon.svg
@@ -0,0 +1,3 @@
+<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2.875 22L2.98023e-08 17.05L2.875 12H8.6L11.5 17.05L8.625 22H2.875ZM2.875 10L2.98023e-08 5.05L2.875 -9.53674e-07H8.6L11.5 5.05L8.6 10H2.875ZM4.025 20H7.45L9.175 17.05L7.45 14H4.025L2.3 17.05L4.025 20ZM4.025 8H7.45L9.175 5.05L7.45 2H4.025L2.3 5.05L4.025 8ZM13.375 16L10.475 11.05L13.35 6H19.1L22 11.05L19.125 16H13.375ZM14.5 14H17.95L19.675 11.05L17.95 8H14.525L12.8 11.05L14.5 14Z" fill="#444746"/>
+</svg>
diff --git a/style/icons/dataset_icon.svg b/style/icons/dataset_icon.svg
new file mode 100644
index 00000000..0a68dfa0
--- /dev/null
+++ b/style/icons/dataset_icon.svg
@@ -0,0 +1,3 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 14H8V10H4V14ZM10 14H14V10H10V14ZM4 8H8V4H4V8ZM10 8H14V4H10V8ZM2 18C1.45 18 0.975 17.8083 0.575 17.425C0.191667 17.025 0 16.55 0 16V2C0 1.45 0.191667 0.983333 0.575 0.599999C0.975 0.199999 1.45 -1.43051e-06 2 -1.43051e-06H16C16.55 -1.43051e-06 17.0167 0.199999 17.4 0.599999C17.8 0.983333 18 1.45 18 2V16C18 16.55 17.8 17.025 17.4 17.425C17.0167 17.8083 16.55 18 16 18H2ZM2 16H16V2H2V16ZM2 2V16V2Z" fill="#444746"/>
+</svg>
diff --git a/style/sessionDetails.css b/style/sessionDetails.css
index 25a052c0..1a462adf 100644
--- a/style/sessionDetails.css
+++ b/style/sessionDetails.css
@@ -66,3 +66,13 @@
   max-height: 100%;
   overflow: auto;
 }
+
+.preview-data-table-parent {
+  max-height: 81vh;
+  overflow: auto;
+}
+
+.preview-table-data {
+  padding-left: 15px;
+  border-bottom: 1px solid var(--jp-layout-color3);
+}
diff --git a/third_party/icons/python_logo.svg b/third_party/icons/python_logo.svg
index 764d7cc8..4d440816 100644
--- a/third_party/icons/python_logo.svg
+++ b/third_party/icons/python_logo.svg
@@ -1,4 +1,4 @@
-<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
+<!-- <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>
@@ -11,4 +11,6 @@
 <stop offset="1" stop-color="#FFC331"/>
 </linearGradient>
 </defs>
-</svg>
+</svg> -->
+
+<svg xmlns="http://www.w3.org/2000/svg" width="16" viewBox="-10 -10 131.16136169433594 132.38899993896484" data-icon="ui-components:python"><path xmlns="http://www.w3.org/2000/svg" class="jp-icon-selectable" fill="#306998" d="M 54.918785,9.1927421e-4 C 50.335132,0.02221727 45.957846,0.41313697 42.106285,1.0946693 30.760069,3.0991731 28.700036,7.2947714 28.700035,15.032169 v 10.21875 h 26.8125 v 3.40625 h -26.8125 -10.0625 c -7.792459,0 -14.6157588,4.683717 -16.7499998,13.59375 -2.46181998,10.212966 -2.57101508,16.586023 0,27.25 1.9059283,7.937852 6.4575432,13.593748 14.2499998,13.59375 h 9.21875 v -12.25 c 0,-8.849902 7.657144,-16.656248 16.75,-16.65625 h 26.78125 c 7.454951,0 13.406253,-6.138164 13.40625,-13.625 v -25.53125 c 0,-7.2663386 -6.12998,-12.7247771 -13.40625,-13.9374997 C 64.281548,0.32794397 59.502438,-0.02037903 54.918785,9.1927421e-4 Z m -14.5,8.21875012579 c 2.769547,0 5.03125,2.2986456 5.03125,5.1249996 -2e-6,2.816336 -2.261703,5.09375 -5.03125,5.09375 -2.779476,-1e-6 -5.03125,-2.277415 -5.03125,-5.09375 -10e-7,-2.826353 2.251774,-5.1249996 5.03125,-5.1249996 z"></path><path xmlns="http://www.w3.org/2000/svg" class="jp-icon-selectable" fill="#ffd43b" d="m 85.637535,28.657169 v 11.90625 c 0,9.230755 -7.825895,16.999999 -16.75,17 h -26.78125 c -7.335833,0 -13.406249,6.278483 -13.40625,13.625 v 25.531247 c 0,7.266344 6.318588,11.540324 13.40625,13.625004 8.487331,2.49561 16.626237,2.94663 26.78125,0 6.750155,-1.95439 13.406253,-5.88761 13.40625,-13.625004 V 86.500919 h -26.78125 v -3.40625 h 26.78125 13.406254 c 7.792461,0 10.696251,-5.435408 13.406241,-13.59375 2.79933,-8.398886 2.68022,-16.475776 0,-27.25 -1.92578,-7.757441 -5.60387,-13.59375 -13.406241,-13.59375 z m -15.0625,64.65625 c 2.779478,3e-6 5.03125,2.277417 5.03125,5.093747 -2e-6,2.826354 -2.251775,5.125004 -5.03125,5.125004 -2.76955,0 -5.03125,-2.29865 -5.03125,-5.125004 2e-6,-2.81633 2.261697,-5.093747 5.03125,-5.093747 z"></path></svg>


Test patch:
diff --git a/dataproc_jupyter_plugin/tests/test_bigqueryService.py b/dataproc_jupyter_plugin/tests/test_bigqueryService.py
new file mode 100644
index 00000000..c06753aa
--- /dev/null
+++ b/dataproc_jupyter_plugin/tests/test_bigqueryService.py
@@ -0,0 +1,134 @@
+# Copyright 2024 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 unittest.mock import Mock, patch
+from dataproc_jupyter_plugin.services.bigqueryService import (
+    BigQueryDatasetInfoService,
+    BigQueryDatasetService,
+    BigQueryPreviewService,
+    BigQuerySchemaService,
+    BigQueryTableInfoService,
+)
+
+
+def test_list_datasets_with_valid_credentials():
+    credentials = {
+        "access_token": "token",
+        "project_id": "project_id",
+        "region_id": "region_id",
+    }
+    log = Mock()
+    requests = Mock()
+    response = Mock()
+    response.status_code = 200
+    response.json.return_value = {"key": "value"}
+    requests.get.return_value = response
+    service = BigQueryDatasetService()
+    with patch("dataproc_jupyter_plugin.services.bigqueryService.requests", requests):
+        result = service.list_datasets(credentials, log)
+
+    assert result == {"key": "value"}
+
+
+def test_list_datasets_with_missing_credentials():
+    credentials = {}
+    log = Mock()
+    requests = Mock()
+    service = BigQueryDatasetService()
+    with patch("dataproc_jupyter_plugin.services.bigqueryService.requests", requests):
+        result = service.list_datasets(credentials, log)
+    assert result == {"error": "Missing required credentials"}
+
+
+def test_list_schema():
+    credentials = {
+        "access_token": "token",
+        "project_id": "project_id",
+        "region_id": "region_id",
+    }
+    log = Mock()
+    requests = Mock()
+    response = Mock()
+    entry_name = "entry_name"
+    response.status_code = 200
+    response.json.return_value = {"key": "value"}
+    requests.get.return_value = response
+    service = BigQuerySchemaService()
+    with patch("dataproc_jupyter_plugin.services.bigqueryService.requests", requests):
+        result = service.list_schema(credentials, entry_name, log)
+
+    assert result == {"key": "value"}
+
+
+def test_list_dataset_info():
+    credentials = {
+        "access_token": "token",
+        "project_id": "project_id",
+        "region_id": "region_id",
+    }
+    log = Mock()
+    requests = Mock()
+    response = Mock()
+    dataset_id = "dataset_id"
+    response.status_code = 200
+    response.json.return_value = {"key": "value"}
+    requests.get.return_value = response
+    service = BigQueryDatasetInfoService()
+    with patch("dataproc_jupyter_plugin.services.bigqueryService.requests", requests):
+        result = service.list_dataset_info(credentials, dataset_id, log)
+
+    assert result == {"key": "value"}
+
+
+def test_list_table_info():
+    credentials = {
+        "access_token": "token",
+        "project_id": "project_id",
+        "region_id": "region_id",
+    }
+    log = Mock()
+    requests = Mock()
+    response = Mock()
+    dataset_id = "dataset_id"
+    table_id = "table_id"
+    response.status_code = 200
+    response.json.return_value = {"key": "value"}
+    requests.get.return_value = response
+    service = BigQueryTableInfoService()
+    with patch("dataproc_jupyter_plugin.services.bigqueryService.requests", requests):
+        result = service.list_table_info(credentials, dataset_id, table_id, log)
+
+    assert result == {"key": "value"}
+
+
+def test_preview_data():
+    credentials = {
+        "access_token": "token",
+        "project_id": "project_id",
+        "region_id": "region_id",
+    }
+    log = Mock()
+    requests = Mock()
+    response = Mock()
+    dataset_id = "dataset_id"
+    table_id = "table_id"
+    response.status_code = 200
+    response.json.return_value = {"key": "value"}
+    requests.get.return_value = response
+    service = BigQueryPreviewService()
+    with patch("dataproc_jupyter_plugin.services.bigqueryService.requests", requests):
+        result = service.bigquery_preview_data(credentials, dataset_id, table_id, log)
+
+    assert result == {"key": "value"}
diff --git a/ui-tests-3.6.6/tests/settings_menu.spec.ts b/ui-tests-3.6.6/tests/settings_menu.spec.ts
index 18d270a1..cce98b56 100644
--- a/ui-tests-3.6.6/tests/settings_menu.spec.ts
+++ b/ui-tests-3.6.6/tests/settings_menu.spec.ts
@@ -23,7 +23,7 @@ test.describe('Settings Menu', () => {
       .getByLabel('main', { exact: true })
       .getByText('Settings')
       .click();
-    await page.getByText('Cloud Dataproc Settings').click();
+    await page.getByText('Google BigQuery Settings').click();
   });
 
   test('Can change project', async ({ page }) => {
@@ -31,7 +31,7 @@ test.describe('Settings Menu', () => {
       .getByLabel('main', { exact: true })
       .getByText('Settings')
       .click();
-    await page.getByText('Cloud Dataproc Settings').click();
+    await page.getByText('Google BigQuery Settings').click();
 
     // Assert clearing the Project ID disables the save button.
     await page.getByRole('combobox', { name: 'Project ID' }).click();
diff --git a/ui-tests/tests/settings_menu.spec.ts b/ui-tests/tests/settings_menu.spec.ts
index bbf4d7ab..10b98ece 100644
--- a/ui-tests/tests/settings_menu.spec.ts
+++ b/ui-tests/tests/settings_menu.spec.ts
@@ -23,7 +23,7 @@ test.describe('Settings Menu', () => {
       .getByLabel('main menu', { exact: true })
       .getByText('Settings')
       .click();
-    await page.getByText('Cloud Dataproc Settings').click();
+    await page.getByText('Google BigQuery Settings').click();
   });
 
   test('Can change project', async ({ page }) => {
@@ -31,7 +31,7 @@ test.describe('Settings Menu', () => {
       .getByLabel('main menu', { exact: true })
       .getByText('Settings')
       .click();
-    await page.getByText('Cloud Dataproc Settings').click();
+    await page.getByText('Google BigQuery Settings').click();
 
     // Assert clearing the Project ID disables the save button.
     await page.getByRole('combobox', { name: 'Project ID' }).click();