This is an automated email from the ASF dual-hosted git repository.

yuzelin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git


The following commit(s) were added to refs/heads/main by this push:
     new cf48e27  [UI] Optimized style (#31)
cf48e27 is described below

commit cf48e277c24a9b6356612e81a7b35772543f4bcb
Author: s7monk <[email protected]>
AuthorDate: Wed Aug 23 10:38:56 2023 +0800

    [UI] Optimized style (#31)
---
 .../apache/paimon/web/api/table/TableManager.java  |   8 +-
 .../apache/paimon/web/api/table/TableMetadata.java |  21 +-
 .../web/server/controller/CatalogController.java   |  13 +
 .../web/server/controller/DatabaseController.java  | 132 ++++++++
 .../web/server/controller/TableController.java     | 255 +++++++++++++++
 .../paimon/web/server/data/model/DatabaseInfo.java |  38 +++
 .../paimon/web/server/data/model/TableColumn.java  |  42 +++
 .../paimon/web/server/data/model/TableInfo.java    |  49 +++
 .../web/server/data/result/enums/Status.java       |   8 +
 .../paimon/web/server/mapper/DatabaseMapper.java   |  33 ++
 .../paimon/web/server/service/DatabaseService.java |  45 +++
 .../server/service/impl/DatabaseServiceImpl.java   |  47 +++
 .../paimon/web/server/util/CatalogUtils.java       |  44 +++
 .../web/server/util/DataTypeConvertUtils.java      |  91 ++++++
 paimon-web-server/src/main/resources/db/ddl-h2.sql |  13 +-
 paimon-web-server/src/main/resources/db/dml-h2.sql |  10 +-
 .../src/main/resources/i18n/messages.properties    |   4 +
 .../main/resources/i18n/messages_en_US.properties  |   4 +
 .../src/main/resources/mapper/DatabaseMapper.xml   |  38 +++
 paimon-web-ui/src/api/api.ts                       |  40 ++-
 paimon-web-ui/src/api/data.d.ts                    |   7 +
 paimon-web-ui/src/api/endpoints.ts                 |   8 +
 .../components/{ => Btn}/ChangeI18nBtn/index.tsx   |   0
 .../src/components/Btn/GithubLogoButton/index.tsx  |  49 +++
 .../src/components/Btn/ThemeSwitcherBtn/index.tsx  |  51 +++
 .../CatalogIconMoreDropdown/index.tsx}             |  43 ++-
 .../DatabaseIconMoreDropdown/index.tsx}            |  43 ++-
 .../TableIconMoreDropdown/index.tsx                |  41 +++
 paimon-web-ui/src/locales/en/translation.json      |  36 ++-
 paimon-web-ui/src/locales/zh-CN/translation.json   |  52 ++-
 .../Header/header.module.less}                     |  14 +-
 paimon-web-ui/src/pages/Layout/Header/index.tsx    |  52 ++-
 .../CatalogModalForm/CatalogForm/index.tsx         |  33 +-
 .../components/CatalogModalForm/index.tsx          |  13 +-
 .../CatalogTree/catalog-tree.module.less           |  62 +++-
 .../LeftContent/components/CatalogTree/index.tsx   | 355 +++++++++++++++++++--
 .../DatabaseModalForm/DatabaseForm/index.tsx       |  74 +++++
 .../index.tsx                                      |  24 +-
 .../components/TableModalForm/TableForm/index.tsx  | 244 ++++++++++++++
 .../{CatalogModalForm => TableModalForm}/index.tsx |  33 +-
 .../Metadata/components/LeftContent/index.tsx      |   4 +
 paimon-web-ui/src/router/index.tsx                 |   6 +-
 paimon-web-ui/src/store/databaseStore.ts           |  66 ++++
 paimon-web-ui/src/store/tableStore.ts              |  79 +++++
 .../Database/data.d.ts}                            |   8 +-
 paimon-web-ui/src/types/Public/data.d.ts           |   3 -
 .../{api/endpoints.ts => types/Table/data.d.ts}    |  27 +-
 47 files changed, 2173 insertions(+), 189 deletions(-)

diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableManager.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableManager.java
index d09baa4..3e18828 100644
--- 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableManager.java
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableManager.java
@@ -81,14 +81,14 @@ public class TableManager {
 
         Schema.Builder schemaBuilder =
                 Schema.newBuilder()
-                        .partitionKeys(
-                                tableMetadata.primaryKeys() == null
-                                        ? ImmutableList.of()
-                                        : 
ImmutableList.copyOf(tableMetadata.primaryKeys()))
                         .partitionKeys(
                                 tableMetadata.partitionKeys() == null
                                         ? ImmutableList.of()
                                         : 
ImmutableList.copyOf(tableMetadata.partitionKeys()))
+                        .primaryKey(
+                                tableMetadata.primaryKeys() == null
+                                        ? ImmutableList.of()
+                                        : 
ImmutableList.copyOf(tableMetadata.primaryKeys()))
                         .comment(tableMetadata.comment() == null ? "" : 
tableMetadata.comment())
                         .options(handleOptions(tableMetadata.options()));
 
diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableMetadata.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableMetadata.java
index 90ea33d..89d02f4 100644
--- 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableMetadata.java
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableMetadata.java
@@ -144,6 +144,23 @@ public class TableMetadata {
                 .collect(Collectors.toSet());
     }
 
+    @Override
+    public String toString() {
+        return "TableMetadata{"
+                + "columns="
+                + columns
+                + ", partitionKeys="
+                + partitionKeys
+                + ", primaryKeys="
+                + primaryKeys
+                + ", options="
+                + options
+                + ", comment='"
+                + comment
+                + '\''
+                + '}';
+    }
+
     public static TableMetadata.Builder builder() {
         return new Builder();
     }
@@ -156,7 +173,7 @@ public class TableMetadata {
 
         private List<String> primaryKeys = new ArrayList<>();
 
-        private Map<String, String> options = new HashMap<>();
+        @Nullable private Map<String, String> options = new HashMap<>();
 
         @Nullable private String comment;
 
@@ -171,7 +188,7 @@ public class TableMetadata {
         }
 
         public Builder primaryKeys(List<String> primaryKeys) {
-            this.partitionKeys = new ArrayList<>(primaryKeys);
+            this.primaryKeys = new ArrayList<>(primaryKeys);
             return this;
         }
 
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
index ef954b4..cdc0bf1 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
@@ -26,7 +26,9 @@ import org.apache.paimon.web.server.service.CatalogService;
 
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -97,4 +99,15 @@ public class CatalogController {
         List<CatalogInfo> catalogs = catalogService.list();
         return R.succeed(catalogs);
     }
+
+    /**
+     * Removes a catalog by its ID.
+     *
+     * @param catalogId The ID of the catalog to be removed.
+     * @return A response indicating the success or failure of the removal 
operation.
+     */
+    @DeleteMapping("/{catalogId}")
+    public R<Void> remove(@PathVariable Integer catalogId) {
+        return catalogService.removeById(catalogId) ? R.succeed() : R.failed();
+    }
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/DatabaseController.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/DatabaseController.java
new file mode 100644
index 0000000..83d0584
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/DatabaseController.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.controller;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.web.api.database.DatabaseManager;
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+import org.apache.paimon.web.server.data.model.DatabaseInfo;
+import org.apache.paimon.web.server.data.result.R;
+import org.apache.paimon.web.server.data.result.enums.Status;
+import org.apache.paimon.web.server.service.CatalogService;
+import org.apache.paimon.web.server.util.CatalogUtils;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Database api controller. */
+@Slf4j
+@RestController
+@RequestMapping("/api/database")
+public class DatabaseController {
+
+    @Autowired private CatalogService catalogService;
+
+    /**
+     * Creates a new database based on the provided DatabaseInfo.
+     *
+     * @param databaseInfo The DatabaseInfo object containing the details of 
the new database.
+     * @return R<Void/> indicating the result of the operation.
+     */
+    @PostMapping("/createDatabase")
+    public R<Void> createDatabase(@RequestBody DatabaseInfo databaseInfo) {
+        try {
+            CatalogInfo catalogInfo = getCatalogInfo(databaseInfo);
+            Catalog catalog = CatalogUtils.getCatalog(catalogInfo);
+            if (DatabaseManager.databaseExists(catalog, 
databaseInfo.getDatabaseName())) {
+                return R.failed(Status.DATABASE_NAME_IS_EXIST, 
databaseInfo.getDatabaseName());
+            }
+            DatabaseManager.createDatabase(catalog, 
databaseInfo.getDatabaseName());
+            return R.succeed();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.failed(Status.DATABASE_CREATE_ERROR);
+        }
+    }
+
+    /**
+     * /** Get all database information.
+     *
+     * @return The list of all databases.
+     */
+    @GetMapping("/getAllDatabases")
+    public R<List<DatabaseInfo>> getAllDatabases() {
+        List<DatabaseInfo> databaseInfoList = new ArrayList<>();
+        List<CatalogInfo> catalogInfoList = catalogService.list();
+        if (catalogInfoList.size() > 0) {
+            catalogInfoList.forEach(
+                    item -> {
+                        Catalog catalog = CatalogUtils.getCatalog(item);
+                        List<String> list = 
DatabaseManager.listDatabase(catalog);
+                        list.forEach(
+                                databaseName -> {
+                                    DatabaseInfo info =
+                                            DatabaseInfo.builder()
+                                                    .databaseName(databaseName)
+                                                    .catalogId(item.getId())
+                                                    .description("")
+                                                    .build();
+                                    databaseInfoList.add(info);
+                                });
+                    });
+        }
+        return R.succeed(databaseInfoList);
+    }
+
+    /**
+     * Retrieves the associated CatalogInfo object based on the given 
DatabaseInfo object.
+     *
+     * @param databaseInfo The DatabaseInfo object for which to retrieve the 
associated CatalogInfo.
+     * @return The associated CatalogInfo object, or null if it doesn't exist.
+     */
+    private CatalogInfo getCatalogInfo(DatabaseInfo databaseInfo) {
+        LambdaQueryWrapper<CatalogInfo> queryWrapper = new 
LambdaQueryWrapper<>();
+        queryWrapper.eq(CatalogInfo::getId, databaseInfo.getCatalogId());
+        return catalogService.getOne(queryWrapper);
+    }
+
+    /**
+     * Removes a database by its name.
+     *
+     * @param databaseInfo The information of the database to be removed.
+     * @return A response indicating the success or failure of the removal 
operation.
+     * @throws RuntimeException if the database is not found or it is not 
empty.
+     */
+    @DeleteMapping("/delete")
+    public R<Void> remove(@RequestBody DatabaseInfo databaseInfo) {
+        try {
+            CatalogInfo catalogInfo = getCatalogInfo(databaseInfo);
+            Catalog catalog = CatalogUtils.getCatalog(catalogInfo);
+            DatabaseManager.dropDatabase(catalog, 
databaseInfo.getDatabaseName());
+            return R.succeed();
+        } catch (Catalog.DatabaseNotEmptyException | 
Catalog.DatabaseNotExistException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/TableController.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/TableController.java
new file mode 100644
index 0000000..33eaa1c
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/TableController.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.controller;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.types.DataField;
+import org.apache.paimon.web.api.database.DatabaseManager;
+import org.apache.paimon.web.api.table.ColumnMetadata;
+import org.apache.paimon.web.api.table.TableManager;
+import org.apache.paimon.web.api.table.TableMetadata;
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+import org.apache.paimon.web.server.data.model.TableColumn;
+import org.apache.paimon.web.server.data.model.TableInfo;
+import org.apache.paimon.web.server.data.result.R;
+import org.apache.paimon.web.server.data.result.enums.Status;
+import org.apache.paimon.web.server.service.CatalogService;
+import org.apache.paimon.web.server.util.CatalogUtils;
+import org.apache.paimon.web.server.util.DataTypeConvertUtils;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/** Table api controller. */
+@Slf4j
+@RestController
+@RequestMapping("/api/table")
+public class TableController {
+
+    @Autowired private CatalogService catalogService;
+
+    /**
+     * Creates a table in the database based on the provided TableInfo.
+     *
+     * @param tableInfo The TableInfo object containing information about the 
table.
+     * @return R<Void/> indicating the success or failure of the operation.
+     */
+    @PostMapping("/createTable")
+    public R<Void> createTable(@RequestBody TableInfo tableInfo) {
+        try {
+            Catalog catalog = 
CatalogUtils.getCatalog(getCatalogInfo(tableInfo.getCatalogName()));
+            List<String> partitionKeys = tableInfo.getPartitionKey();
+            Map<String, String> tableOptions = tableInfo.getTableOptions();
+            TableMetadata tableMetadata =
+                    TableMetadata.builder()
+                            .columns(buildColumns(tableInfo))
+                            .partitionKeys(partitionKeys)
+                            .primaryKeys(buildPrimaryKeys(tableInfo))
+                            .options(tableOptions)
+                            .comment(tableInfo.getDescription())
+                            .build();
+            if (TableManager.tableExists(
+                    catalog, tableInfo.getDatabaseName(), 
tableInfo.getTableName())) {
+                return R.failed(Status.TABLE_NAME_IS_EXIST, 
tableInfo.getTableName());
+            }
+            TableManager.createTable(
+                    catalog, tableInfo.getDatabaseName(), 
tableInfo.getTableName(), tableMetadata);
+            return R.succeed();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.failed(Status.TABLE_CREATE_ERROR);
+        }
+    }
+
+    /**
+     * Handler method for the "/getAllTables" endpoint. Retrieves information 
about all tables and
+     * returns a response containing the table details.
+     *
+     * @return Response object containing a list of {@link TableInfo} 
representing the tables.
+     */
+    @GetMapping("/getAllTables")
+    public R<List<TableInfo>> getAllTables() {
+        List<TableInfo> tableInfoList = new ArrayList<>();
+        List<CatalogInfo> catalogInfoList = catalogService.list();
+        if (catalogInfoList.size() > 0) {
+            catalogInfoList.forEach(
+                    item -> {
+                        Catalog catalog = CatalogUtils.getCatalog(item);
+                        List<String> databaseList = 
DatabaseManager.listDatabase(catalog);
+                        if (databaseList.size() > 0) {
+                            databaseList.forEach(
+                                    db -> {
+                                        try {
+                                            List<String> tables =
+                                                    
TableManager.listTables(catalog, db);
+                                            if (tables.size() > 0) {
+                                                tables.forEach(
+                                                        t -> {
+                                                            try {
+                                                                Table table =
+                                                                        
TableManager.getTable(
+                                                                               
 catalog, db, t);
+                                                                if (table != 
null) {
+                                                                    
List<String> primaryKeys =
+                                                                            
table.primaryKeys();
+                                                                    
List<DataField> fields =
+                                                                            
table.rowType()
+                                                                               
     .getFields();
+                                                                    
List<TableColumn> tableColumns =
+                                                                            
new ArrayList<>();
+                                                                    if 
(fields.size() > 0) {
+                                                                        
fields.forEach(
+                                                                               
 field -> {
+                                                                               
     TableColumn
+                                                                               
                     .TableColumnBuilder
+                                                                               
             builder =
+                                                                               
                     TableColumn
+                                                                               
                             .builder()
+                                                                               
                             .field(
+                                                                               
                                     field
+                                                                               
                                             .name())
+                                                                               
                             .dataType(
+                                                                               
                                     DataTypeConvertUtils
+                                                                               
                                             .fromPaimonType(
+                                                                               
                                                     field
+                                                                               
                                                             .type()))
+                                                                               
                             .comment(
+                                                                               
                                     field
+                                                                               
                                             .description());
+                                                                               
     if (primaryKeys
+                                                                               
                             .size()
+                                                                               
                     > 0
+                                                                               
             && primaryKeys
+                                                                               
                     .contains(
+                                                                               
                             field
+                                                                               
                                     .name())) {
+                                                                               
         builder
+                                                                               
                 .isPK(
+                                                                               
                         true);
+                                                                               
     }
+                                                                               
     tableColumns
+                                                                               
             .add(
+                                                                               
                     builder
+                                                                               
                             .build());
+                                                                               
 });
+                                                                    }
+                                                                    TableInfo 
tableInfo =
+                                                                            
TableInfo.builder()
+                                                                               
     .catalogName(
+                                                                               
             item
+                                                                               
                     .getCatalogName())
+                                                                               
     .databaseName(
+                                                                               
             db)
+                                                                               
     .tableName(
+                                                                               
             table
+                                                                               
                     .name())
+                                                                               
     .partitionKey(
+                                                                               
             table
+                                                                               
                     .partitionKeys())
+                                                                               
     .tableOptions(
+                                                                               
             table
+                                                                               
                     .options())
+                                                                               
     .tableColumns(
+                                                                               
             tableColumns)
+                                                                               
     .build();
+                                                                    
tableInfoList.add(tableInfo);
+                                                                }
+                                                            } catch (
+                                                                    
Catalog.TableNotExistException
+                                                                            e) 
{
+                                                                throw new 
RuntimeException(e);
+                                                            }
+                                                        });
+                                            }
+                                        } catch 
(Catalog.DatabaseNotExistException e) {
+                                            throw new RuntimeException(e);
+                                        }
+                                    });
+                        }
+                    });
+        }
+        return R.succeed(tableInfoList);
+    }
+
+    /**
+     * Builds a list of primary keys for the given table.
+     *
+     * @param tableInfo The TableInfo object representing the table.
+     * @return A list of primary keys as strings.
+     */
+    private List<String> buildPrimaryKeys(TableInfo tableInfo) {
+        List<String> primaryKeys = new ArrayList<>();
+        List<TableColumn> tableColumns = tableInfo.getTableColumns();
+        if (tableColumns != null && tableColumns.size() > 0) {
+            tableColumns.forEach(
+                    item -> {
+                        if (item.isPK()) {
+                            primaryKeys.add(item.getField());
+                        }
+                    });
+        }
+        return primaryKeys;
+    }
+
+    /**
+     * Builds a list of ColumnMetadata objects for the given table.
+     *
+     * @param tableInfo The TableInfo object representing the table.
+     * @return A list of ColumnMetadata objects.
+     */
+    private List<ColumnMetadata> buildColumns(TableInfo tableInfo) {
+        List<ColumnMetadata> columns = new ArrayList<>();
+        List<TableColumn> tableColumns = tableInfo.getTableColumns();
+        if (tableColumns != null && tableColumns.size() > 0) {
+            tableColumns.forEach(
+                    item -> {
+                        ColumnMetadata columnMetadata =
+                                new ColumnMetadata(
+                                        item.getField(),
+                                        
DataTypeConvertUtils.convert(item.getDataType()),
+                                        item.getComment() != null ? 
item.getComment() : null);
+                        columns.add(columnMetadata);
+                    });
+        }
+        return columns;
+    }
+
+    /**
+     * Retrieves the associated CatalogInfo object based on the given catalog 
name.
+     *
+     * @param catalogName The name of the catalog for which to retrieve the 
associated CatalogInfo.
+     * @return The associated CatalogInfo object, or null if it doesn't exist.
+     */
+    private CatalogInfo getCatalogInfo(String catalogName) {
+        LambdaQueryWrapper<CatalogInfo> queryWrapper = new 
LambdaQueryWrapper<>();
+        queryWrapper.eq(CatalogInfo::getCatalogName, catalogName);
+        return catalogService.getOne(queryWrapper);
+    }
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
new file mode 100644
index 0000000..0f93b78
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.data.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/** Database table model. */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class DatabaseInfo {
+
+    private String databaseName;
+
+    private Integer catalogId;
+
+    private String description;
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableColumn.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableColumn.java
new file mode 100644
index 0000000..0593dea
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableColumn.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.data.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/** TableColumn model. */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TableColumn {
+
+    private String field;
+
+    private String dataType;
+
+    private String comment;
+
+    private boolean isPK;
+
+    private String defaultValue;
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableInfo.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableInfo.java
new file mode 100644
index 0000000..7420063
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableInfo.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.data.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+/** Table model. */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TableInfo {
+
+    private String catalogName;
+
+    private String databaseName;
+
+    private String tableName;
+
+    private String description;
+
+    private List<TableColumn> tableColumns;
+
+    private List<String> partitionKey;
+
+    private Map<String, String> tableOptions;
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
index 9f1209f..c6e5788 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
@@ -56,6 +56,14 @@ public enum Status {
     /** ------------catalog-----------------. */
     CATALOG_NAME_IS_EXIST(10301, "catalog.name.exist"),
     CATALOG_CREATE_ERROR(10302, "catalog.create.error"),
+
+    /** ------------database-----------------. */
+    DATABASE_NAME_IS_EXIST(10401, "database.name.exist"),
+    DATABASE_CREATE_ERROR(10402, "database.create.error"),
+
+    /** ------------table-----------------. */
+    TABLE_NAME_IS_EXIST(10501, "table.name.exist"),
+    TABLE_CREATE_ERROR(10502, "table.create.error"),
     ;
 
     private final int code;
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/DatabaseMapper.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/DatabaseMapper.java
new file mode 100644
index 0000000..cf07ce4
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/DatabaseMapper.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.mapper;
+
+import org.apache.paimon.web.server.data.model.DatabaseInfo;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/** Database table mapper. */
+@Mapper
+public interface DatabaseMapper extends BaseMapper<DatabaseInfo> {
+
+    List<DatabaseInfo> selectByCatalogId(Integer catalogId);
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/DatabaseService.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/DatabaseService.java
new file mode 100644
index 0000000..8b38e42
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/DatabaseService.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.service;
+
+import org.apache.paimon.web.server.data.model.DatabaseInfo;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+/** Database Service. */
+public interface DatabaseService extends IService<DatabaseInfo> {
+
+    /**
+     * Verify if the database name is unique.
+     *
+     * @param databaseInfo database info
+     * @return result
+     */
+    boolean checkCatalogNameUnique(DatabaseInfo databaseInfo);
+
+    /**
+     * Retrieve a list of databases by catalog ID.
+     *
+     * @param catalogId the ID of the catalog
+     * @return a list of DatabaseInfo objects
+     */
+    List<DatabaseInfo> selectByCatalogId(Integer catalogId);
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/DatabaseServiceImpl.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/DatabaseServiceImpl.java
new file mode 100644
index 0000000..6d25fe0
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/DatabaseServiceImpl.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.service.impl;
+
+import org.apache.paimon.web.server.data.model.DatabaseInfo;
+import org.apache.paimon.web.server.mapper.DatabaseMapper;
+import org.apache.paimon.web.server.service.DatabaseService;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/** DatabaseServiceImpl. */
+@Service
+public class DatabaseServiceImpl extends ServiceImpl<DatabaseMapper, 
DatabaseInfo>
+        implements DatabaseService {
+
+    @Autowired private DatabaseMapper databaseMapper;
+
+    @Override
+    public boolean checkCatalogNameUnique(DatabaseInfo databaseInfo) {
+        return false;
+    }
+
+    @Override
+    public List<DatabaseInfo> selectByCatalogId(Integer catalogId) {
+        return databaseMapper.selectByCatalogId(catalogId);
+    }
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/CatalogUtils.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/CatalogUtils.java
new file mode 100644
index 0000000..c19668e
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/CatalogUtils.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.util;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.web.api.catalog.CatalogCreator;
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+
+/** catalog util. */
+public class CatalogUtils {
+
+    /**
+     * Get a Catalog based on the provided CatalogInfo.
+     *
+     * @param catalogInfo The CatalogInfo object containing the catalog 
details.
+     * @return The created Catalog object.
+     */
+    public static Catalog getCatalog(CatalogInfo catalogInfo) {
+        if ("filesystem".equals(catalogInfo.getCatalogType())) {
+            return 
CatalogCreator.createFilesystemCatalog(catalogInfo.getWarehouse());
+        } else {
+            return CatalogCreator.createHiveCatalog(
+                    catalogInfo.getWarehouse(),
+                    catalogInfo.getHiveUri(),
+                    catalogInfo.getHiveConfDir());
+        }
+    }
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/DataTypeConvertUtils.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/DataTypeConvertUtils.java
new file mode 100644
index 0000000..aa4f9f9
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/DataTypeConvertUtils.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.paimon.web.server.util;
+
+import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypes;
+
+/** data type convert util. */
+public class DataTypeConvertUtils {
+
+    public static DataType convert(String type) {
+        switch (type) {
+            case "INT":
+                return DataTypes.INT();
+            case "TINYINT":
+                return DataTypes.TINYINT();
+            case "SMALLINT":
+                return DataTypes.SMALLINT();
+            case "BIGINT":
+                return DataTypes.BIGINT();
+            case "STRING":
+                return DataTypes.STRING();
+            case "DOUBLE":
+                return DataTypes.DOUBLE();
+            case "BOOLEAN":
+                return DataTypes.BOOLEAN();
+            case "DATE":
+                return DataTypes.DATE();
+            case "TIME":
+                return DataTypes.TIME();
+            case "TIMESTAMP":
+                return DataTypes.TIMESTAMP();
+            case "BYTES":
+                return DataTypes.BYTES();
+            case "FLOAT":
+                return DataTypes.FLOAT();
+            case "DECIMAL":
+                return DataTypes.DECIMAL(38, 0);
+            default:
+                throw new RuntimeException("Invalid type: " + type);
+        }
+    }
+
+    public static String fromPaimonType(DataType dataType) {
+        if (dataType.equals(DataTypes.INT())) {
+            return "INT";
+        } else if (dataType.equals(DataTypes.TINYINT())) {
+            return "TINYINT";
+        } else if (dataType.equals(DataTypes.SMALLINT())) {
+            return "SMALLINT";
+        } else if (dataType.equals(DataTypes.BIGINT())) {
+            return "BIGINT";
+        } else if (dataType.equals(DataTypes.STRING())) {
+            return "STRING";
+        } else if (dataType.equals(DataTypes.DOUBLE())) {
+            return "DOUBLE";
+        } else if (dataType.equals(DataTypes.BOOLEAN())) {
+            return "BOOLEAN";
+        } else if (dataType.equals(DataTypes.DATE())) {
+            return "DATE";
+        } else if (dataType.equals(DataTypes.TIME())) {
+            return "TIME";
+        } else if (dataType.equals(DataTypes.TIMESTAMP())) {
+            return "TIMESTAMP";
+        } else if (dataType.equals(DataTypes.BYTES())) {
+            return "BYTES";
+        } else if (dataType.equals(DataTypes.FLOAT())) {
+            return "FLOAT";
+        } else if (dataType.equals(DataTypes.DECIMAL(38, 0))) {
+            return "DECIMAL";
+        } else {
+            return "UNKNOWN";
+        }
+    }
+}
diff --git a/paimon-web-server/src/main/resources/db/ddl-h2.sql 
b/paimon-web-server/src/main/resources/db/ddl-h2.sql
index fc6936f..ab24591 100644
--- a/paimon-web-server/src/main/resources/db/ddl-h2.sql
+++ b/paimon-web-server/src/main/resources/db/ddl-h2.sql
@@ -114,4 +114,15 @@ CREATE TABLE if not exists `catalog`
     `is_delete`   tinyint(1)   NOT NULL DEFAULT 0 COMMENT 'is delete',
     `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create 
time',
     `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update 
time'
-) engine = innodb;
\ No newline at end of file
+) engine = innodb;
+
+CREATE TABLE if not exists `databases`
+(
+    `id`          int(11)     not null auto_increment primary key comment 'id',
+    `database_name`     varchar(50)  not null comment 'database name',
+    `catalog_id`     int(11)     not null comment 'catalog id',
+    `description`     varchar(200)  comment 'description',
+    `is_delete`   tinyint(1)   NOT NULL DEFAULT 0 COMMENT 'is delete',
+    `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create 
time',
+    `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update 
time'
+) engine = innodb;
diff --git a/paimon-web-server/src/main/resources/db/dml-h2.sql 
b/paimon-web-server/src/main/resources/db/dml-h2.sql
index 6b0cfa6..77d10ae 100644
--- a/paimon-web-server/src/main/resources/db/dml-h2.sql
+++ b/paimon-web-server/src/main/resources/db/dml-h2.sql
@@ -61,7 +61,13 @@ values (1, 1),
        (1, 1003),
        (1, 1004);
 
-insert into `catalog` (catalog_type, catalog_name, warehouse)
+/*insert into `catalog` (catalog_type, catalog_name, warehouse)
 values ('filesystem', 'paimon', 'file:///D:/path/'),
        ('filesystem', 'fts', 'file:///D:/path/'),
-       ('filesystem', 'streaming_warehouse', 'file:///D:/path/')
+       ('filesystem', 'streaming_warehouse', 'file:///D:/path/');
+
+insert into `databases` (database_name, catalog_id, description)
+values ('ods', 1, 'description'),
+       ('ods', 2, 'description'),
+       ('ods', 3, 'description')*/
+
diff --git a/paimon-web-server/src/main/resources/i18n/messages.properties 
b/paimon-web-server/src/main/resources/i18n/messages.properties
index 7c16a94..86a4bc8 100644
--- a/paimon-web-server/src/main/resources/i18n/messages.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages.properties
@@ -35,3 +35,7 @@ menu.name.exist=This menu name is exist:{0}
 menu.path.invalid=This menu path is invalid:{0}
 catalog.name.exist=This catalog name {0} is exist
 catalog.create.error=An exception is returned when calling the Paimon API to 
create a Catalog.
+database.name.exist=This database name {0} is exist.
+database.create.error=An exception is returned when calling the Paimon API to 
create a Database.
+table.name.exist=This table name {0} is exist.
+table.create.error=An exception is returned when calling the Paimon API to 
create a Table.
diff --git 
a/paimon-web-server/src/main/resources/i18n/messages_en_US.properties 
b/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
index f48cf18..ca74509 100644
--- a/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
@@ -33,3 +33,7 @@ role.key.exist=This role key {0} is exist
 menu.in.used=This menu is in used
 menu.name.exist=This menu name is exist:{0}
 menu.path.invalid=This menu path is invalid:{0}
+catalog.name.exist=This catalog name {0} is exist
+catalog.create.error=An exception is returned when calling the Paimon API to 
create a Catalog.
+database.name.exist=This database name {0} is exist.
+database.create.error=An exception is returned when calling the Paimon API to 
create a Database.
diff --git a/paimon-web-server/src/main/resources/mapper/DatabaseMapper.xml 
b/paimon-web-server/src/main/resources/mapper/DatabaseMapper.xml
new file mode 100644
index 0000000..8f1aa28
--- /dev/null
+++ b/paimon-web-server/src/main/resources/mapper/DatabaseMapper.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+-->
+
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd";>
+<mapper namespace="org.apache.paimon.web.server.mapper.DatabaseMapper">
+
+    <resultMap type="org.apache.paimon.web.server.data.model.DatabaseInfo" 
id="BaseResultMap">
+        <id property="id" column="id" />
+        <result property="databaseName" column="database_name" />
+        <result property="catalogId" column="catalog_id" />
+    </resultMap>
+
+    <select id="selectByCatalogId" parameterType="java.lang.Integer" 
resultMap="BaseResultMap">
+        SELECT *
+        FROM database_info
+        WHERE catalog_id = #{catalogId}
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/paimon-web-ui/src/api/api.ts b/paimon-web-ui/src/api/api.ts
index 679728c..1d80977 100644
--- a/paimon-web-ui/src/api/api.ts
+++ b/paimon-web-ui/src/api/api.ts
@@ -19,6 +19,8 @@ import http from '@api/http'
 import {API_ENDPOINTS} from '@api/endpoints';
 import Result = API.Result;
 import {CatalogItemList} from "@src/types/Catalog/data";
+import {DatabaseItem} from "@src/types/Database/data";
+import {TableItem} from "@src/types/Table/data";
 
 export const createFileSystemCatalog = async (catalogProp: Prop.CatalogProp) 
=> {
     try {
@@ -44,10 +46,46 @@ export const getAllCatalogs = async () => {
     }
 }
 
+export const createDatabase = async (databaseProp: Prop.DatabaseProp) => {
+    try {
+        return await http.httpPost<Result<any>, 
Prop.DatabaseProp>(API_ENDPOINTS.CREATE_DATABASE, databaseProp);
+    } catch (error) {
+        console.error('Failed to create database:', error);
+    }
+};
+
+export const getAllDatabases = async () => {
+    try {
+        return await http.httpGet<Result<DatabaseItem[]>, 
null>(API_ENDPOINTS.GET_ALL_DATABASES)
+    } catch (error: any) {
+        console.error('Failed to get database:', error);
+    }
+}
+
+export const createTable = async (databaseProp: TableItem) => {
+    try {
+        return await http.httpPost<Result<any>, 
TableItem>(API_ENDPOINTS.CREATE_TABLE, databaseProp);
+    } catch (error) {
+        console.error('Failed to create table:', error);
+    }
+};
+
+export const getAllTables = async () => {
+    try {
+        return await http.httpGet<Result<TableItem[]>, 
null>(API_ENDPOINTS.GET_ALL_TABLES)
+    } catch (error: any) {
+        console.error('Failed to get tables:', error);
+    }
+}
+
 const Api = {
     createFileSystemCatalog,
     createHiveCatalog,
-    getAllCatalogs
+    getAllCatalogs,
+    createDatabase,
+    getAllDatabases,
+    createTable,
+    getAllTables,
 }
 
 export default Api;
diff --git a/paimon-web-ui/src/api/data.d.ts b/paimon-web-ui/src/api/data.d.ts
index 2381a78..886c716 100644
--- a/paimon-web-ui/src/api/data.d.ts
+++ b/paimon-web-ui/src/api/data.d.ts
@@ -42,4 +42,11 @@ declare namespace Prop {
         hiveConfDir: string,
         isDelete: boolean
     }
+
+    // Database
+    export interface DatabaseProp {
+        databaseName: string,
+        catalogId: number,
+        description: string
+    }
 }
\ No newline at end of file
diff --git a/paimon-web-ui/src/api/endpoints.ts 
b/paimon-web-ui/src/api/endpoints.ts
index 1567581..8cfbfce 100644
--- a/paimon-web-ui/src/api/endpoints.ts
+++ b/paimon-web-ui/src/api/endpoints.ts
@@ -26,4 +26,12 @@ export const API_ENDPOINTS = {
     CREATE_HIVE_CATALOG: '/catalog/createHiveCatalog',
     GET_ALL_CATALOGS: '/catalog/getAllCatalogs',
 
+    // database
+    CREATE_DATABASE: '/database/createDatabase',
+    GET_ALL_DATABASES: '/database/getAllDatabases',
+
+    // table
+    CREATE_TABLE: '/table/createTable',
+    GET_ALL_TABLES: '/table/getAllTables',
+
 };
\ No newline at end of file
diff --git a/paimon-web-ui/src/components/ChangeI18nBtn/index.tsx 
b/paimon-web-ui/src/components/Btn/ChangeI18nBtn/index.tsx
similarity index 100%
rename from paimon-web-ui/src/components/ChangeI18nBtn/index.tsx
rename to paimon-web-ui/src/components/Btn/ChangeI18nBtn/index.tsx
diff --git a/paimon-web-ui/src/components/Btn/GithubLogoButton/index.tsx 
b/paimon-web-ui/src/components/Btn/GithubLogoButton/index.tsx
new file mode 100644
index 0000000..2b34ab2
--- /dev/null
+++ b/paimon-web-ui/src/components/Btn/GithubLogoButton/index.tsx
@@ -0,0 +1,49 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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 {Button, Tooltip} from "@douyinfe/semi-ui";
+import {IconGithubLogo} from "@douyinfe/semi-icons";
+import i18n from 'i18next';
+import {useMemo} from "react";
+import { useTranslation } from 'react-i18next';
+
+const GithubLogoButton = () => {
+    const { t } = useTranslation();
+    const handleClick = () => {
+        window.open('https://github.com/apache/incubator-paimon-webui', 
'_blank');
+    }
+
+    const tooltipContent = useMemo(() => {
+        return t('component.btn-github-icon');
+    }, [i18n.language]);
+
+    return (
+        <Tooltip content={tooltipContent} position='bottom'>
+            <Button
+                theme="borderless"
+                icon={<IconGithubLogo size="extra-large"/>}
+                style={{
+                    color: 'var(--semi-color-text-2)',
+                    marginRight: '12px',
+                }}
+                onClick={handleClick}
+            />
+        </Tooltip>
+    )
+}
+
+export default GithubLogoButton;
\ No newline at end of file
diff --git a/paimon-web-ui/src/components/Btn/ThemeSwitcherBtn/index.tsx 
b/paimon-web-ui/src/components/Btn/ThemeSwitcherBtn/index.tsx
new file mode 100644
index 0000000..a298b6e
--- /dev/null
+++ b/paimon-web-ui/src/components/Btn/ThemeSwitcherBtn/index.tsx
@@ -0,0 +1,51 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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 { useMemo } from "react";
+import {Button, Tooltip} from "@douyinfe/semi-ui";
+import {IconMoon, IconSun} from "@douyinfe/semi-icons";
+import useThemeSwitcher from '@utils/mode.ts';
+import i18n from 'i18next';
+import { useTranslation } from 'react-i18next';
+
+const ThemeSwitcherBtn = () => {
+    const { t } = useTranslation();
+    const {dark, switchMode } = useThemeSwitcher();
+
+    const [tooltipContent, icon] = useMemo(() => {
+        const tooltipContent = dark ? t('component.btn-switch-to-light-mode') 
: t('component.btn-switch-to-dark-mode');
+        const icon = dark ? <IconSun size={"extra-large"}/> : <IconMoon 
size={"extra-large"}/>;
+        return [tooltipContent, icon];
+    }, [dark, i18n.language]);
+
+    return (
+        <Tooltip content={tooltipContent} position='bottom'>
+            <Button
+                theme="borderless"
+                icon={icon}
+                style={{
+                    color: 'var(--semi-color-text-2)',
+                    marginRight: '12px',
+                }}
+                aria-label={tooltipContent}
+                onClick={switchMode}
+            />
+        </Tooltip>
+    )
+}
+
+export default ThemeSwitcherBtn;
\ No newline at end of file
diff --git a/paimon-web-ui/src/api/data.d.ts 
b/paimon-web-ui/src/components/IconMoreDropdown/CatalogIconMoreDropdown/index.tsx
similarity index 53%
copy from paimon-web-ui/src/api/data.d.ts
copy to 
paimon-web-ui/src/components/IconMoreDropdown/CatalogIconMoreDropdown/index.tsx
index 2381a78..bf3c504 100644
--- a/paimon-web-ui/src/api/data.d.ts
+++ 
b/paimon-web-ui/src/components/IconMoreDropdown/CatalogIconMoreDropdown/index.tsx
@@ -15,31 +15,26 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-declare namespace API {
-    export interface Result<T> {
-        code: number;
-        msg: string;
-        data: T;
-    }
+import { IconMore, IconDelete } from "@douyinfe/semi-icons";
+import { Dropdown } from '@douyinfe/semi-ui';
+import { useTranslation } from 'react-i18next';
 
-    export interface PageData<T> {
-        records: T[];
-        page: number;
-        size: number;
-        total: number;
-    }
+const CatalogIconMoreDropdown = () => {
+    const { t } = useTranslation();
 
-    export type PageResult<T> = Result<PageData<T>>
+    return(
+        <Dropdown
+            trigger={'click'}
+            position={'bottomLeft'}
+            render={
+                <Dropdown.Menu>
+                    <Dropdown.Item><IconDelete/> 
{t('component.icon-more-dropdown-delete')}</Dropdown.Item>
+                </Dropdown.Menu>
+            }
+        >
+            <IconMore onClick={(e) => e.stopPropagation()}/>
+        </Dropdown>
+    );
 }
 
-declare namespace Prop {
-    // Catalog
-    export interface CatalogProp {
-        catalogName: string,
-        catalogType: string,
-        warehouse: string,
-        hiveUri: string,
-        hiveConfDir: string,
-        isDelete: boolean
-    }
-}
\ No newline at end of file
+export default CatalogIconMoreDropdown;
\ No newline at end of file
diff --git a/paimon-web-ui/src/api/data.d.ts 
b/paimon-web-ui/src/components/IconMoreDropdown/DatabaseIconMoreDropdown/index.tsx
similarity index 53%
copy from paimon-web-ui/src/api/data.d.ts
copy to 
paimon-web-ui/src/components/IconMoreDropdown/DatabaseIconMoreDropdown/index.tsx
index 2381a78..bf3c504 100644
--- a/paimon-web-ui/src/api/data.d.ts
+++ 
b/paimon-web-ui/src/components/IconMoreDropdown/DatabaseIconMoreDropdown/index.tsx
@@ -15,31 +15,26 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-declare namespace API {
-    export interface Result<T> {
-        code: number;
-        msg: string;
-        data: T;
-    }
+import { IconMore, IconDelete } from "@douyinfe/semi-icons";
+import { Dropdown } from '@douyinfe/semi-ui';
+import { useTranslation } from 'react-i18next';
 
-    export interface PageData<T> {
-        records: T[];
-        page: number;
-        size: number;
-        total: number;
-    }
+const CatalogIconMoreDropdown = () => {
+    const { t } = useTranslation();
 
-    export type PageResult<T> = Result<PageData<T>>
+    return(
+        <Dropdown
+            trigger={'click'}
+            position={'bottomLeft'}
+            render={
+                <Dropdown.Menu>
+                    <Dropdown.Item><IconDelete/> 
{t('component.icon-more-dropdown-delete')}</Dropdown.Item>
+                </Dropdown.Menu>
+            }
+        >
+            <IconMore onClick={(e) => e.stopPropagation()}/>
+        </Dropdown>
+    );
 }
 
-declare namespace Prop {
-    // Catalog
-    export interface CatalogProp {
-        catalogName: string,
-        catalogType: string,
-        warehouse: string,
-        hiveUri: string,
-        hiveConfDir: string,
-        isDelete: boolean
-    }
-}
\ No newline at end of file
+export default CatalogIconMoreDropdown;
\ No newline at end of file
diff --git 
a/paimon-web-ui/src/components/IconMoreDropdown/TableIconMoreDropdown/index.tsx 
b/paimon-web-ui/src/components/IconMoreDropdown/TableIconMoreDropdown/index.tsx
new file mode 100644
index 0000000..81b504f
--- /dev/null
+++ 
b/paimon-web-ui/src/components/IconMoreDropdown/TableIconMoreDropdown/index.tsx
@@ -0,0 +1,41 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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 { IconMore, IconDelete, IconEdit } from "@douyinfe/semi-icons";
+import { Dropdown } from '@douyinfe/semi-ui';
+import { useTranslation } from 'react-i18next';
+
+const TableIconMoreDropdown = () => {
+    const { t } = useTranslation();
+
+    return(
+        <Dropdown
+            trigger={'click'}
+            position={'bottomLeft'}
+            render={
+                <Dropdown.Menu>
+                    <Dropdown.Item><IconEdit/> 
{t('component.icon-more-dropdown-rename')}</Dropdown.Item>
+                    <Dropdown.Item><IconDelete/> 
{t('component.icon-more-dropdown-delete')}</Dropdown.Item>
+                </Dropdown.Menu>
+            }
+        >
+            <IconMore onClick={(e) => e.stopPropagation()}/>
+        </Dropdown>
+    );
+}
+
+export default TableIconMoreDropdown;
\ No newline at end of file
diff --git a/paimon-web-ui/src/locales/en/translation.json 
b/paimon-web-ui/src/locales/en/translation.json
index e4277d5..1ac2217 100644
--- a/paimon-web-ui/src/locales/en/translation.json
+++ b/paimon-web-ui/src/locales/en/translation.json
@@ -15,6 +15,40 @@
     "catalog": "Catalog",
     "tableInfo": "TableInfo",
     "details": "Details",
-    "files": "Files"
+    "files": "Files",
+    "add-database": "Add Database",
+    "add-table": "Add Table",
+    "create-database": "Create Database",
+    "create-table": "Create Table",
+    "submit": "Submit",
+    "cancel": "Cancel",
+    "message": "This item is required.",
+    "database-name": "Database Name",
+    "table-name": "Table Name",
+    "description": "Description",
+    "text-area-description": "Please Enter",
+    "create-catalog": "Create Catalog",
+    "create-database-success": "Database created successfully!",
+    "create-database-failed": "Failed to create database:",
+    "create-table-success": "Table created successfully!",
+    "create-table-failed": "Failed to create table:",
+    "form-group-basic-information": "Basic Information",
+    "form-group-add-column-ordinary": "Common Column",
+    "form-group-add-column-partition": "Partition Column",
+    "add-column-field": "Field",
+    "add-column-default-value": "Default Value",
+    "add-column-type": "Type",
+    "add-column-primary-key": "Primary Key",
+    "add-column-comment": "Comment",
+    "form-group-add-config": "Add Options",
+    "add-config-key": "Key",
+    "add-config-value": "Value"
+  },
+  "component": {
+    "icon-more-dropdown-rename": "Rename",
+    "icon-more-dropdown-delete": "Delete",
+    "btn-switch-to-light-mode": "Switch to Light Mode",
+    "btn-switch-to-dark-mode": "Switch to Dark Mode",
+    "btn-github-icon": "Visit GitHub repository"
   }
 }
diff --git a/paimon-web-ui/src/locales/zh-CN/translation.json 
b/paimon-web-ui/src/locales/zh-CN/translation.json
index a144468..8d5ecc3 100644
--- a/paimon-web-ui/src/locales/zh-CN/translation.json
+++ b/paimon-web-ui/src/locales/zh-CN/translation.json
@@ -1,20 +1,54 @@
 {
   "common": {
-    "filter": "过滤"
+    "filter": "搜索"
   },
   "header": {
-    "playground": "运行信息",
-    "metadata": "元数据",
-    "cdcingestion": "CDC集成",
-    "system": "系统"
+    "playground": "查询控制台",
+    "metadata": "元数据管理",
+    "cdcingestion": "CDC 集成",
+    "system": "系统管理"
   },
   "playground": {
     "selectCatalog": "选择Catalog"
   },
   "metadata": {
-    "catalog": "元数据",
-    "tableInfo": "表信息",
-    "details": "详细",
-    "files": "文件"
+    "catalog": "Catalog目录",
+    "tableInfo": "基本信息",
+    "details": "详细信息",
+    "files": "数据文件",
+    "add-database": "添加数据库",
+    "add-table": "添加数据表",
+    "create-database": "创建 Database",
+    "create-table": "创建 Table",
+    "submit": "确定",
+    "cancel": "取消",
+    "message": "该项为必填项!",
+    "database-name": "名称",
+    "table-name": "名称",
+    "description": "描述",
+    "text-area-description": "请输入",
+    "create-catalog": "创建Catalog",
+    "create-database-success": "创建 Database 成功!",
+    "create-database-failed": "创建 Database 失败:",
+    "create-table-success": "创建 Table 成功!",
+    "create-table-failed": "创建 Table 失败:",
+    "form-group-basic-information": "基本信息",
+    "form-group-add-column-ordinary": "普通列",
+    "form-group-add-column-partition": "分区列",
+    "add-column-field": "字段",
+    "add-column-default-value": "默认值",
+    "add-column-type": "类型",
+    "add-column-primary-key": "主键",
+    "add-column-comment": "备注",
+    "form-group-add-config": "添加选项",
+    "add-config-key": "键",
+    "add-config-value": "值"
+  },
+  "component": {
+    "icon-more-dropdown-rename": "重命名",
+    "icon-more-dropdown-delete": "删除",
+    "btn-switch-to-light-mode": "切换到亮色模式",
+    "btn-switch-to-dark-mode": "切换到暗色模式",
+    "btn-github-icon": "访问 GitHub 仓库"
   }
 }
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
 b/paimon-web-ui/src/pages/Layout/Header/header.module.less
similarity index 80%
copy from 
paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
copy to paimon-web-ui/src/pages/Layout/Header/header.module.less
index 4f3f847..c3449e3 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
+++ b/paimon-web-ui/src/pages/Layout/Header/header.module.less
@@ -15,8 +15,14 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-.catalog-tree-input {
-  //background-color: var(--semi-color-bg-0);
-  width: 347px;
-  margin-left: -12px;
+.selected-nav-item {
+  color: rgba(var(--semi-blue-5), 1) !important;
+}
+
+.header {
+  width: 96px;
+  height: 36px;
+  font-size: 36px;
+  margin-left: -45px;
+  margin-right: -20px;
 }
\ No newline at end of file
diff --git a/paimon-web-ui/src/pages/Layout/Header/index.tsx 
b/paimon-web-ui/src/pages/Layout/Header/index.tsx
index 05b5440..8b9aceb 100644
--- a/paimon-web-ui/src/pages/Layout/Header/index.tsx
+++ b/paimon-web-ui/src/pages/Layout/Header/index.tsx
@@ -15,26 +15,33 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-import {Avatar, Button, Layout, Nav} from '@douyinfe/semi-ui';
-import { IconMoon, IconGithubLogo, IconSun } from '@douyinfe/semi-icons';
-import useThemeSwitcher from '@src/utils/mode'
-import paimonLogo from '@src/assets/logo/favicon_blue.svg'
-import paimonWhiteLogo from '@src/assets/logo/favicon_white.svg'
-import {useNavigate} from "react-router";
+import {Avatar, Layout, Nav} from '@douyinfe/semi-ui';
+import paimonLogo from '@src/assets/logo/favicon_blue.svg';
+import {useLocation, useNavigate} from "react-router";
 import {useMemo, useState} from "react";
 import menuList from "@config/menu.tsx";
-import ChangeI18nBtn from "@components/ChangeI18nBtn";
+import ChangeI18nBtn from "@components/Btn/ChangeI18nBtn";
+import ThemeSwitcherBtn from "@components/Btn/ThemeSwitcherBtn";
 import {useTranslation} from "react-i18next";
 import {Link} from "react-router-dom";
+import GithubLogoButton from "@components/Btn/GithubLogoButton";
+import styles from "./header.module.less";
 
 const { Header } = Layout
 
 const HeaderRoot = ()=> {
-    const {dark, switchMode } = useThemeSwitcher();
     const navigate = useNavigate()
 
     const [openKeys, setOpenKeys] = useState<string[]>([])
-    const [selectedKeys, setSelectedKeys] = useState<string[]>([])
+    const location = useLocation();
+
+    const initialKey = useMemo(() => {
+        const path = location.pathname;
+        const matchedItem = menuList.find(item => item.path === path);
+        return matchedItem ? matchedItem.name : menuList[0]?.name;
+    }, [location, menuList]);
+
+    const [selectedKeys, setSelectedKeys] = useState<string[]>([initialKey]);
 
     const navList = useMemo(() => {
         return menuList.map((e) => {
@@ -67,7 +74,7 @@ const HeaderRoot = ()=> {
             <div>
                  <Nav
                     header={{
-                        logo: <img src={dark ? paimonWhiteLogo : paimonLogo} 
alt="Apache Paimon"
+                        logo: <img src={ paimonLogo } alt="Apache Paimon"
                                    style={{ width: '96px', height: '36px', 
fontSize: 36, marginLeft: '-45px', marginRight: '-20px'}}/>,
                         text: 'Apache Paimon'
                     }}
@@ -80,23 +87,8 @@ const HeaderRoot = ()=> {
                     // items={navList}
                     footer={
                         <div>
-                            <Button
-                                theme="borderless"
-                                icon={!dark ? <IconMoon size={"extra-large"}/> 
: <IconSun size={"extra-large"}/>}
-                                style={{
-                                    color: 'var(--semi-color-text-2)',
-                                    marginRight: '12px',
-                                }}
-                                onClick={switchMode}
-                            />
-                            <Button
-                                theme="borderless"
-                                icon={<IconGithubLogo size="extra-large"/>}
-                                style={{
-                                    color: 'var(--semi-color-text-2)',
-                                    marginRight: '12px',
-                                }}
-                            />
+                            <ThemeSwitcherBtn />
+                            <GithubLogoButton />
                             <ChangeI18nBtn />
                             <Avatar
                                 color="orange"
@@ -115,7 +107,11 @@ const HeaderRoot = ()=> {
                                  style={{ textDecoration: "none" }}
                                  to={nav.path}
                              >
-                                <Nav.Item itemKey={nav.name} text={t('header.' 
+ nav.name)} />
+                                <Nav.Item
+                                    itemKey={nav.name}
+                                    text={t('header.' + nav.name)}
+                                    className={selectedKeys.includes(nav.name) 
? styles['selected-nav-item'] : ''}
+                                />
                              </Link>
                          )
                         })
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm/index.tsx
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm/index.tsx
index 55cd745..cb0723e 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm/index.tsx
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm/index.tsx
@@ -16,10 +16,11 @@ specific language governing permissions and limitations
 under the License. */
 
 import { Form } from '@douyinfe/semi-ui';
+import {useTranslation} from "react-i18next";
 
 // @ts-ignore
 const CatalogForm = ({ getFormApi }) => {
-    let message = 'This item is required.';
+    const { t } = useTranslation();
     return(
        <>
            <Form
@@ -37,18 +38,20 @@ const CatalogForm = ({ getFormApi }) => {
                                label='Catalog Name'
                                trigger='blur'
                                rules={[
-                                   { required: true, message },
+                                   { required: true, message: 
t('metadata.message') },
                                ]}
-                               style={{ width: "100%" }}/>
+                               style={{ width: "100%" }}
+                               showClear/>
 
                            <Form.Select
                                field="catalogType"
                                label='Catalog Type'
                                placeholder={"please select catalog"}
                                rules={[
-                                   { required: true, message },
+                                   { required: true, message: 
t('metadata.message') },
                                ]}
-                               style={{ width: "100%" }}>
+                               style={{ width: "100%" }}
+                               showClear>
                                <Form.Select.Option 
value="filesystem">FileSystem</Form.Select.Option>
                                <Form.Select.Option 
value="hive">Hive</Form.Select.Option>
                            </Form.Select>
@@ -59,9 +62,10 @@ const CatalogForm = ({ getFormApi }) => {
                                    label='Warehouse'
                                    trigger='blur'
                                    rules={[
-                                       { required: true, message },
+                                       { required: true, message: 
t('metadata.message') },
                                    ]}
-                                   style={{ width: "100%" }}/>
+                                   style={{ width: "100%" }}
+                                   showClear/>
                            )
                                :
                                <>
@@ -70,27 +74,30 @@ const CatalogForm = ({ getFormApi }) => {
                                        label='Warehouse'
                                        trigger='blur'
                                        rules={[
-                                           { required: true, message },
+                                           { required: true, message: 
t('metadata.message') },
                                        ]}
-                                       style={{ width: "100%" }}/>
+                                       style={{ width: "100%" }}
+                                       showClear/>
 
                                    <Form.Input
                                        field="uri"
                                        label='Hive Uri'
                                        trigger='blur'
                                        rules={[
-                                           { required: true, message },
+                                           { required: true, message: 
t('metadata.message') },
                                        ]}
-                                       style={{ width: "100%" }}/>
+                                       style={{ width: "100%" }}
+                                       showClear/>
 
                                    <Form.Input
                                        field="hiveConfDir"
                                        label='Hive Conf Dir'
                                        trigger='blur'
                                        rules={[
-                                           { required: true, message },
+                                           { required: true, message: 
t('metadata.message') },
                                        ]}
-                                       style={{ width: "100%" }}/>
+                                       style={{ width: "100%" }}
+                                       showClear/>
                                </>
                            }
                        </>
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
index 05780b1..11c546d 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
@@ -18,6 +18,8 @@ under the License. */
 import React, {useState} from "react";
 import { Modal } from '@douyinfe/semi-ui';
 import CatalogForm from 
"@pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm";
+import {useTranslation} from "react-i18next";
+import {auto} from "@popperjs/core";
 
 type CatalogModalFormProps = {
     visible: boolean;
@@ -27,6 +29,7 @@ type CatalogModalFormProps = {
 
 
 const CatalogModalForm: React.FC<CatalogModalFormProps> = ({ visible , 
onClose, onOk }) => {
+    const { t } = useTranslation();
     const [formApi, setFormApi] = useState(null);
 
     const getFormApi = (api: any) => {
@@ -40,14 +43,14 @@ const CatalogModalForm: React.FC<CatalogModalFormProps> = 
({ visible , onClose,
 
     return(
         <Modal
-            title="Create Catalog"
+            title={t('metadata.create-catalog')}
             visible = {visible}
             onCancel= {onClose}
             maskClosable={false}
-            okText={'Submit'}
-            cancelText={'Cancel'}
-            width={'750px'}
-            height={'550px'}
+            okText={t('metadata.submit')}
+            cancelText={t('metadata.cancel')}
+            width={'690px'}
+            height={auto}
             onOk={() => handleOkClick()}
         >
             <CatalogForm getFormApi={getFormApi}/>
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
index 4f3f847..d9be919 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
@@ -19,4 +19,64 @@ under the License. */
   //background-color: var(--semi-color-bg-0);
   width: 347px;
   margin-left: -12px;
-}
\ No newline at end of file
+}
+
+.liClass {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.iconPlus {
+  position: absolute;
+  right: 40px;
+  padding: 2px;
+  transition: all 0.2s ease-in-out; /* Add transition effect */
+  border: none;
+  color: #000;
+  font-size: 12px; /* Small size for the "+" icon in the initial state */
+  visibility: hidden; /* Hide the "+" icon in the initial state */
+  border-radius: 50%; /* Add border radius */
+  background: transparent; /* Transparent background in the initial state */
+}
+
+.liClass:hover .iconPlus{
+  visibility: visible; /* Show the "+" icon when hovering over the entire node 
*/
+  border: none;
+  color: #000;
+  font-size: 12px;
+  background: transparent;
+}
+
+.liClass .iconPlus:hover{
+  border: 1px solid #d3d3d3; /* Gray border when hovering over the "+" icon */
+  color: #d3d3d3; /* Gray color when hovering over the "+" icon */
+  font-size: 16px; /* Increase the size of the "+" icon when hovering */
+  background: #fff; /* White background when hovering over the "+" icon */
+  box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); /* Add shadow effect */
+}
+
+.iconMore {
+  display: flex;
+  align-items: center;
+  transition: all 0.2s ease-in-out;
+  visibility: hidden;
+  margin-right: 12px;
+  color: #000;
+}
+
+.liClass:hover .iconMore {
+  visibility: visible;
+}
+
+.semi-tree-node-selected {
+  color: rgba(var(--semi-blue-5), 1) !important;
+}
+
+.icon {
+  margin-right: 8px;
+  color: var(--semi-color-text-2);
+}
+
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
index 61746f1..ae8322f 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
@@ -16,26 +16,108 @@ specific language governing permissions and limitations
 under the License. */
 
 import {Input, Tree} from '@douyinfe/semi-ui';
-import { IconSearch, IconFile } from "@douyinfe/semi-icons";
+import { IconSearch, IconFile, IconPlus, IconFolder, IconFolderOpen } from 
"@douyinfe/semi-icons";
+import {Tooltip} from "@douyinfe/semi-ui";
 import {useEffect, useState} from "react";
-import styles from "./catalog-tree.module.less"
 import { useTranslation } from 'react-i18next';
+import { useDatabaseStore } from "@src/store/databaseStore.ts";
 import { useCatalogStore } from "@src/store/catalogStore.ts";
+import {useTableStore} from "@src/store/tableStore.ts";
+import DatabaseModalForm from 
"@pages/Metadata/components/LeftContent/components/DatabaseModalForm";
+import TableModalForm from 
"@pages/Metadata/components/LeftContent/components/TableModalForm";
+import {TableItem, TableColumn} from "@src/types/Table/data";
+import CatalogIconMoreDropdown from 
"@components/IconMoreDropdown/CatalogIconMoreDropdown";
+import DatabaseIconMoreDropdown from 
"@components/IconMoreDropdown/DatabaseIconMoreDropdown";
+import TableIconMoreDropdown from 
"@components/IconMoreDropdown/TableIconMoreDropdown";
+import styles from "./catalog-tree.module.less"
+
+type TreeDataItem = {
+    label: string;
+    value: string;
+    key: string;
+    type: "catalog" | "database" | "table";
+    catalogId: number;
+    parentId?: number;
+    children?: TreeDataItem[];
+};
 
 const CatalogTree = () => {
     const { t } = useTranslation()
+    const [hoveredNode, setHoveredNode] = useState(null);
+    const [showModal, setShowModal] = useState(false);
+    const [createType, setCreateType] =
+        useState<"catalog" | "database" | "table" | null>(null);
 
-    type TreeDataItem = {
-        label: string;
-        value: string;
-        key: string;
-        children?: TreeDataItem[];
-    };
+    // Database
+    const createDatabase = useDatabaseStore(state => state.createDatabase);
+    const fetchDatabases= useDatabaseStore(state => state.fetchDatabases);
+    const databaseItemList = useDatabaseStore(state => state.databaseItemList);
 
+    // Catalog
     const [treeData, setTreeData] = useState<TreeDataItem[]>([]);
     const fetchCatalogData = useCatalogStore(state => state.fetchCatalogData);
     const catalogItemList = useCatalogStore(state => state.catalogItemList);
 
+    // Table
+    const { inputs, configs, setInputs, setConfigs} = useTableStore();
+    const createTable = useTableStore(state => state.createTable);
+    const fetchTables= useTableStore(state => state.fetchTables);
+    const tableItemList = useTableStore(state => state.tableItemList);
+
+    const [selectedCatalogName, setSelectedCatalogName] = useState<string | 
null>(null);
+    const [selectedDatabaseName, setSelectedDatabaseName] = useState<string | 
null>(null);
+
+    const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
+    const [selectedKey, setSelectedKey] = useState(null);
+
+    useEffect(() => {
+        // Fetch the catalog data when the component mounts
+        fetchTables();
+    }, [fetchTables]);
+
+    useEffect(() => {
+        // Fetch the catalog data when the component mounts
+        fetchDatabases();
+    }, [fetchDatabases]);
+
+    const handleMouseEnter = (key: any) => {
+        setHoveredNode(key);
+    };
+
+    const handleMouseLeave = () => {
+        setHoveredNode(null);
+    };
+
+    const handleOpenModal = (type: "catalog" | "database" | "table",  name: 
string, parentId?: number) => {
+        if (type === "catalog" || type === "database") {
+            if (type === "catalog") {
+                setSelectedCatalogName(name);
+            } else if (type === "database") {
+                const databaseItem =
+                    databaseItemList.find(item => item.databaseName === name 
&& item.catalogId === parentId);
+                if (databaseItem) {
+                    const catalogItem = catalogItemList.find(item => item.id 
=== databaseItem.catalogId);
+                    if (catalogItem) {
+                        setSelectedCatalogName(catalogItem.catalogName);
+                    }
+                }
+                setSelectedDatabaseName(name);
+            }
+            setCreateType(type);
+            setShowModal(true);
+        }
+    };
+
+    const handleCloseDatabaseModal = () => {
+        setShowModal(false);
+    };
+
+    const handleCloseTableModal = () => {
+        setInputs([{}]);
+        setConfigs([]);
+        setShowModal(false);
+    };
+
     useEffect(() => {
         // Fetch the catalog data when the component mounts
         fetchCatalogData();
@@ -46,42 +128,259 @@ const CatalogTree = () => {
         const transformedData = catalogItemList.map(item => ({
             label: item.catalogName,
             value: item.catalogName,
+            type: "catalog" as "catalog",
+            catalogId: item.id,
             key: item.id.toString(),
+            children: databaseItemList
+                .filter(dbItem => dbItem.catalogId === item.id)
+                .map(dbItem => {
+                    const databaseKey = item.id.toString() + "-" + 
dbItem.databaseName;
+                    const tableChildren = tableItemList
+                        .filter(tableItem => tableItem.catalogName === 
item.catalogName && tableItem.databaseName === dbItem.databaseName)
+                        .map(tableItem => ({
+                            label: tableItem.tableName,
+                            value: tableItem.tableName,
+                            type: "table" as "table",
+                            catalogId: item.id,
+                            key: databaseKey + "-" + tableItem.tableName
+                        }));
+                    return {
+                        label: dbItem.databaseName,
+                        value: dbItem.databaseName,
+                        type: "database" as "database",
+                        catalogId: item.id,
+                        parentId: item.id,
+                        key: databaseKey,
+                        children: tableChildren
+                    };
+                }),
         }));
         setTreeData(transformedData);
-    }, [catalogItemList]);
+    }, [catalogItemList, databaseItemList, tableItemList]);
+
+    const handleCreateDatabaseOk = (formApi: any) => {
+        return new Promise<void>((resolve, reject) => {
+            formApi
+                .validate()
+                .then((values: any) => {
+                    const formData = values;
+                    const databaseProp: Prop.DatabaseProp = {
+                        databaseName: formData.databaseName,
+                        catalogId: formData.catalogId,
+                        description: formData.description
+                    };
+                    createDatabase(databaseProp)
+                        .then(() => {
+                            fetchDatabases();
+                            resolve();
+                        })
+                        .catch((error: any) => {
+                            console.log(error);
+                            reject(error);
+                        });
+                })
+                .catch((errors: any) => {
+                    console.log(errors);
+                    reject(errors);
+                });
+        });
+    }
+
+    const handleCreateTableOk = (formApi: any) => {
+        return new Promise<void>((resolve, reject) => {
+            formApi
+                .validate()
+                .then(() => {
+                    const values = formApi.getValues();
+                    const catalogName = formApi.catalogName;
+                    const databaseName = formApi.databaseName;
+
+                    let tableColumns: TableColumn[] = [];
+                    let tableOptions: Map<string, string> =new Map<string, 
string>();
+                    if (inputs.length > 0) {
+                        inputs.map((_, index) => {
+                            const field = values[`field${index}`];
+                            const dataType = values[`type${index}`];
+                            const comment = values[`comment${index}`];
+                            const primaryKey = values[`primaryKey${index}`];
+                            const defaultValue = 
values[`defaultValue${index}`];
+                            if (field !== undefined && dataType !== undefined) 
{
+                                const tableColumn: TableColumn = {
+                                    field: field,
+                                    dataType: dataType,
+                                    comment: comment === undefined ? null : 
comment,
+                                    isPK: primaryKey !== undefined,
+                                    defaultValue: defaultValue === undefined ? 
null : defaultValue,
+                                }
+                                tableColumns.push(tableColumn);
+                            }
+                        });
+                    }
+
+                    if (configs.length > 0) {
+                        configs.map((_, index) => {
+                            const key = values[`configKey${index}`];
+                            const value = values[`configValue${index}`];
+                            tableOptions.set(key, value);
+                        });
+                    }
+
+                    const tableProp: TableItem = {
+                        catalogName: catalogName,
+                        databaseName: databaseName,
+                        tableName: values.tableName,
+                        description: values.description === undefined ? null : 
values.description,
+                        tableColumns: tableColumns,
+                        partitionKey: values.partitionKey === undefined ? [] : 
values.partitionKey,
+                        tableOptions: tableOptions,
+                    }
+                    createTable(tableProp)
+                        .then(() => {
+                            fetchTables();
+                            setInputs([{}]);
+                            setConfigs([]);
+                            resolve();
+                        })
+                        .catch((error: any) => {
+                            console.log(error);
+                            setInputs([{}]);
+                            setConfigs([]);
+                            reject(error);
+                        });
+                    console.log(tableProp)
+                })
+                .catch((errors: any) => {
+                    console.log(errors);
+                    setInputs([{}]);
+                    setConfigs([]);
+                    reject(errors);
+                });
+        });
+    }
+
+    const findNodeByKey = (key: string, nodes?: TreeDataItem[]): TreeDataItem 
| null => {
+        if (!nodes) {
+            return null;
+        }
+
+        for (let i = 0; i < nodes.length; i++) {
+            if (nodes[i].key === key) {
+                return nodes[i];
+            } else if (nodes[i].children) {
+                const foundNode = findNodeByKey(key, nodes[i].children);
+                if (foundNode) {
+                    return foundNode;
+                }
+            }
+        }
+        return null;
+    };
+
+    const onExpand = (_: string[], info: any) => {
+        const key = info.node.key;
+        const expanded = info.expanded;
+
+        if (expanded) {
+            setExpandedKeys(prevKeys => [...prevKeys, key]);
+        } else {
+            const nodeToCollapse = findNodeByKey(key, treeData);
+            let keysToCollapse = [key];
+            if (nodeToCollapse && nodeToCollapse.children) {
+                keysToCollapse = [...keysToCollapse, 
...nodeToCollapse.children.map(child => child.key)];
+            }
+            setExpandedKeys(prevKeys => prevKeys.filter(k => 
!keysToCollapse.includes(k)));
+        }
+    };
 
     const renderLabel = (x: any) => {
-        const className = x.className;
-        const onExpand = x.onExpand;
-        const onClick = x.onClick;
-        const data = x.data;
-        const expandIcon = x.expandIcon;
-        const { label } = data;
+        const { className, onExpand, onClick, data, expandIcon } = x;
+        const { label, key, type } = data;
         const isLeaf = !(data.children && data.children.length);
+        const hasChildren = data.children && data.children.length > 0;
+        const isExpanded = expandedKeys.includes(key);
+        const isSelected = selectedKey === key;
+
+        let icon;
+        if (type === 'table') {
+            icon = <IconFile size={"small"} className={styles.icon}/>;
+        } else if (hasChildren) {
+            icon = isExpanded ? <IconFolderOpen size={"small"} 
className={styles.icon}/>
+                : <IconFolder size={"small"} className={styles.icon}/>
+        } else {
+            icon = <IconFolder size={"small"} className={styles.icon}/>;
+        }
+
+        let iconMore;
+        if (type === 'catalog') {
+            iconMore = <CatalogIconMoreDropdown/>;
+        } else if (type === 'database') {
+            iconMore = <DatabaseIconMoreDropdown/>;
+        } else {
+            iconMore = <TableIconMoreDropdown/>;
+        }
+
         return (
             <li
-                className={className}
+                className={`${className} ${styles.liClass} ${isSelected ? 
styles['semi-tree-node-selected'] : ''}`}
                 role="treeitem"
-                onClick={isLeaf ? onClick : onExpand}
+                onClick={(e) => {
+                    if (isLeaf) {
+                        onClick(e);
+                    } else {
+                        onExpand(e);
+                    }
+                    setSelectedKey(key);
+                }}
+                onMouseEnter={() => handleMouseEnter(key)}
+                onMouseLeave={handleMouseLeave}
+                tabIndex={0}
             >
-                {isLeaf ? <IconFile style={{marginRight: "8px", color: 
"var(--semi-color-text-2)"}}/> : expandIcon}
-                <span>{label}</span>
+                {expandIcon}
+                {icon}
+                <div style={{ flex: 1 }}>{label}</div>
+                { key === hoveredNode && (type === "catalog" || type === 
"database") && (
+                    <Tooltip content={type === "catalog" ? 
t('metadata.add-database') : t('metadata.add-table')}>
+                        <IconPlus
+                            className={styles.iconPlus}
+                            onClick={(e) => {
+                                e.stopPropagation();
+                                handleOpenModal(type, label, data.parentId)
+                            }}
+                        />
+                    </Tooltip>
+                )}
+                <div className={styles.iconMore}>{iconMore}</div>
             </li>
         );
     };
 
     return(
-        <Tree
-            filterTreeNode
-            treeData={treeData}
-            searchPlaceholder={t('common.filter')}
-            searchRender={({ prefix, ...restProps }) => (
-                <Input suffix={<IconSearch 
className={styles['catalog-tree-input-icon']}/>} {...restProps} 
className={styles['catalog-tree-input']}></Input>
+        <>
+            <Tree
+                filterTreeNode
+                treeData={treeData}
+                expandedKeys={expandedKeys}
+                onExpand={onExpand}
+                selectedKey={selectedKey}
+                searchPlaceholder={t('common.filter')}
+                searchRender={({ prefix, ...restProps }) => (
+                    <Input suffix={<IconSearch 
className={styles['catalog-tree-input-icon']}/>} {...restProps} 
className={styles['catalog-tree-input']}></Input>
+                )}
+                renderFullLabel={renderLabel}
+            />
+            {showModal && createType  === "catalog" && (
+                <DatabaseModalForm visible={showModal} 
onClose={handleCloseDatabaseModal} onOk={handleCreateDatabaseOk}/>
+            )}
+            {showModal && createType === "database" && (
+                <TableModalForm
+                    visible={showModal}
+                    onClose={handleCloseTableModal}
+                    onOk={handleCreateTableOk}
+                    catalogName={selectedCatalogName}
+                    databaseName={selectedDatabaseName}/>
             )}
-            renderFullLabel={renderLabel}
-        />
-    )
+        </>
+    );
 }
 
 export default CatalogTree;
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/DatabaseModalForm/DatabaseForm/index.tsx
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/DatabaseModalForm/DatabaseForm/index.tsx
new file mode 100644
index 0000000..f1f200a
--- /dev/null
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/DatabaseModalForm/DatabaseForm/index.tsx
@@ -0,0 +1,74 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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 { Form } from '@douyinfe/semi-ui';
+import {useTranslation} from "react-i18next";
+import {useCatalogStore} from "@src/store/catalogStore.ts";
+
+// @ts-ignore
+const DatabaseForm = ({ getFormApi }) => {
+    const { t } = useTranslation();
+    const catalogItemList = useCatalogStore(state => state.catalogItemList);
+
+    return(
+        <>
+            <Form
+                getFormApi={getFormApi}
+            >
+                {
+                    ({}) => (
+                        <>
+                            <Form.Input
+                                field="databaseName"
+                                label={t('metadata.database-name')}
+                                trigger='blur'
+                                rules={[
+                                    { required: true, message: 
t('metadata.message') },
+                                ]}
+                                style={{ width: "100%" }}
+                                showClear/>
+
+                            <Form.Select
+                                field="catalogId"
+                                label='Catalog'
+                                rules={[
+                                    { required: true, message: 
t('metadata.message') },
+                                ]}
+                                style={{ width: "100%" }}
+                                showClear>
+                                {catalogItemList.map(item => (
+                                    <Form.Select.Option key={item.id} 
value={item.id}>
+                                        {item.catalogName}
+                                    </Form.Select.Option>
+                                ))}
+                            </Form.Select>
+
+                            <Form.TextArea
+                                field="description"
+                                label={t('metadata.description')}
+                                style={{ width: "100%" }}
+                                
placeholder={t('metadata.text-area-description')}
+                                showClear/>
+                        </>
+                    )
+                }
+            </Form>
+        </>
+    );
+}
+
+export default DatabaseForm;
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/DatabaseModalForm/index.tsx
similarity index 69%
copy from 
paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
copy to 
paimon-web-ui/src/pages/Metadata/components/LeftContent/components/DatabaseModalForm/index.tsx
index 05780b1..d5722a3 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/DatabaseModalForm/index.tsx
@@ -17,17 +17,18 @@ under the License. */
 
 import React, {useState} from "react";
 import { Modal } from '@douyinfe/semi-ui';
-import CatalogForm from 
"@pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm";
+import DatabaseForm from 
"@pages/Metadata/components/LeftContent/components/DatabaseModalForm/DatabaseForm";
+import {useTranslation} from "react-i18next";
 
-type CatalogModalFormProps = {
+type DatabaseModalFormProps = {
     visible: boolean;
     onClose: () => void;
     onOk: (formApi: any) => void;
 };
 
-
-const CatalogModalForm: React.FC<CatalogModalFormProps> = ({ visible , 
onClose, onOk }) => {
+const DatabaseModalForm: React.FC<DatabaseModalFormProps> = ({ visible , 
onClose, onOk }) => {
     const [formApi, setFormApi] = useState(null);
+    const { t } = useTranslation();
 
     const getFormApi = (api: any) => {
         setFormApi(api);
@@ -40,19 +41,20 @@ const CatalogModalForm: React.FC<CatalogModalFormProps> = 
({ visible , onClose,
 
     return(
         <Modal
-            title="Create Catalog"
+            title = {t('metadata.create-database')}
             visible = {visible}
             onCancel= {onClose}
             maskClosable={false}
-            okText={'Submit'}
-            cancelText={'Cancel'}
-            width={'750px'}
-            height={'550px'}
+            okText={t('metadata.submit')}
+            cancelText={t('metadata.cancel')}
+            width={'650px'}
+            height={'490px'}
             onOk={() => handleOkClick()}
         >
-            <CatalogForm getFormApi={getFormApi}/>
+            <DatabaseForm getFormApi={getFormApi}/>
         </Modal>
     );
 }
 
-export default CatalogModalForm;
\ No newline at end of file
+export default DatabaseModalForm;
+
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/TableModalForm/TableForm/index.tsx
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/TableModalForm/TableForm/index.tsx
new file mode 100644
index 0000000..3c7ef8e
--- /dev/null
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/TableModalForm/TableForm/index.tsx
@@ -0,0 +1,244 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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 {Button, Form} from '@douyinfe/semi-ui';
+import {useTranslation} from "react-i18next";
+import {IconPlus, IconMinus} from "@douyinfe/semi-icons";
+import {useRef, useState} from "react";
+import {useTableStore} from "@src/store/tableStore.ts";
+
+// @ts-ignore
+const TableForm = ({ getFormApi }) => {
+    const formApiRef = useRef<any>(null);
+    const { t } = useTranslation();
+    const { inputs, configs, setInputs, setConfigs } = useTableStore();
+    const [fieldOptions, setFieldOptions] = useState<string[]>([]);
+
+    const handleAddInput = () => {
+        const newInputs = inputs.concat({});
+        setInputs(newInputs);
+    }
+
+    const handleRemoveInput = (index: any) => {
+        if (formApiRef.current) {
+            const newInputs = [...inputs];
+            formApiRef.current.setValue(`field${index}`, null);
+            formApiRef.current.setValue(`comment${index}`, null);
+            formApiRef.current.setValue(`type${index}`, null);
+            newInputs.splice(index, 1);
+            setInputs(newInputs);
+            const newFieldOptions = newInputs.map((_, index) => 
formApiRef.current.getValue(`field${index}`));
+            setFieldOptions(newFieldOptions);
+        }
+    }
+
+    const updateFieldOptions = (index: any) => {
+        if (formApiRef.current) {
+            const fieldValue = formApiRef.current.getValue(`field${index}`);
+            if (fieldValue !== undefined) {
+                const newFieldOptions = [...fieldOptions];
+                newFieldOptions[index] = fieldValue;
+                setFieldOptions(newFieldOptions);
+            }
+        }
+    };
+
+    const handleAddConfig = () => {
+        setConfigs(configs.concat({}));
+    }
+
+    const handleRemoveConfig = (index: any) => {
+        if (formApiRef.current) {
+            const newConfigs = [...configs];
+            formApiRef.current.setValue(`configKey${index}`, null);
+            formApiRef.current.setValue(`configValue${index}`, null);
+            newConfigs.splice(index, 1);
+            setConfigs(newConfigs);
+        }
+    }
+
+    const options = [
+        { value: 'INT', label: 'INT' },
+        { value: 'TINYINT', label: 'TINYINT' },
+        { value: 'SMALLINT', label: 'SMALLINT' },
+        { value: 'BIGINT', label: 'BIGINT' },
+        { value: 'STRING', label: 'STRING' },
+        { value: 'DOUBLE', label: 'DOUBLE' },
+        { value: 'BOOLEAN', label: 'BOOLEAN' },
+        { value: 'DATE', label: 'DATE' },
+        { value: 'TIME', label: 'TIME' },
+        { value: 'TIMESTAMP', label: 'TIMESTAMP' },
+        { value: 'BYTES', label: 'BYTES' },
+        { value: 'FLOAT', label: 'FLOAT' },
+        { value: 'DECIMAL', label: 'DECIMAL' },
+    ];
+
+    return(
+        <>
+            <Form
+                getFormApi={api => {
+                    formApiRef.current = api;
+                    if (getFormApi) {
+                        getFormApi(api);
+                    }
+                }}
+            >
+                {
+                    ({}) => (
+                        <>
+                            <Form.Section 
text={t('metadata.form-group-basic-information')}>
+                                <Form.Input
+                                    field="tableName"
+                                    label={t('metadata.table-name')}
+                                    trigger='blur'
+                                    rules={[
+                                        { required: true, message: 
t('metadata.message') },
+                                    ]}
+                                    style={{ width: "100%" }}
+                                    showClear/>
+                                <Form.TextArea
+                                    field="description"
+                                    label={t('metadata.description')}
+                                    style={{ width: "100%" }}
+                                    
placeholder={t('metadata.text-area-description')}
+                                    autosize rows={2}
+                                    showClear/>
+                            </Form.Section>
+
+                            <Form.Section
+                                text={
+                                    <div
+                                        style={{ display: 'flex', 
justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer',
+                                            color: inputs.length > 0 ? 'black' 
: 'lightgray',}}
+                                        onClick={handleAddInput}
+                                    >
+                                        
<span>{t('metadata.form-group-add-column-ordinary')}</span>
+                                        <IconPlus />
+                                    </div>
+                                }
+                            >
+                                {inputs.map((_, index) => (
+                                    <div style={{ display: 'flex', alignItems: 
'center', marginBottom: '10px'}} key={index}>
+                                        <Form.Input
+                                            noLabel={true}
+                                            field={`field${index}`}
+                                            
placeholder={t('metadata.add-column-field')}
+                                            style={{ width: "160px" }}
+                                            onBlur={() => 
updateFieldOptions(index)}
+                                            showClear />
+                                        <Form.Select
+                                            noLabel={true}
+                                            field={`type${index}`}
+                                            
placeholder={t('metadata.add-column-type')}
+                                            style={{ width: "130px", 
marginLeft: '10px' }}
+                                            allowCreate={true}
+                                            filter={true}
+                                            showClear>
+                                            {options.map((option, i) => (
+                                                <Form.Select.Option key={i} 
value={option.value}>
+                                                    {option.label}
+                                                </Form.Select.Option>
+                                            ))}
+                                        </Form.Select>
+                                        <Form.Input
+                                            noLabel={true}
+                                            field={`comment${index}`}
+                                            
placeholder={t('metadata.add-column-comment')}
+                                            style={{ width: "160px", 
marginLeft: '10px' }}
+                                            showClear />
+                                        <Form.Checkbox
+                                            field={`primaryKey${index}`}
+                                            noLabel={true}
+                                            style={{marginLeft: '10px'}}>
+                                                
{t('metadata.add-column-primary-key')}
+                                        </Form.Checkbox>
+                                        <Form.Input
+                                            noLabel={true}
+                                            field={`defaultValue${index}`}
+                                            
placeholder={t('metadata.add-column-default-value')}
+                                            style={{ width: "126px", 
marginLeft: '10px'}}
+                                            showClear />
+                                        <Button
+                                            onClick={() => 
handleRemoveInput(index)}
+                                            icon={<IconMinus />}
+                                            style={{ marginLeft: '10px', 
borderRadius: '50%' }}
+                                        />
+                                    </div>
+                                ))}
+                            </Form.Section>
+
+                            <Form.Section 
text={t('metadata.form-group-add-column-partition')}>
+                                <Form.Select
+                                    noLabel={true}
+                                    field="partitionKey"
+                                    style={{ width: "100%" }}
+                                    filter={true}
+                                    showClear
+                                    multiple
+                                >
+                                    {fieldOptions.map((field, index) => (
+                                        <Form.Select.Option key={index} 
value={field}>{field}</Form.Select.Option>
+                                    ))}
+                                </Form.Select>
+                            </Form.Section>
+
+                            <Form.Section
+                                text={
+                                    <div
+                                        style={{
+                                            display: 'flex', justifyContent: 
'space-between', alignItems: 'center',
+                                            cursor: 'pointer',
+                                            color: configs.length > 0 ? 
'black' : 'lightgray',
+                                            }}
+                                        onClick={handleAddConfig}
+                                    >
+                                        
<span>{t('metadata.form-group-add-config')}</span>
+                                        <IconPlus />
+                                    </div>
+                                }
+                            >
+                                {configs.map((_, index) => (
+                                    <div style={{ display: 'flex', alignItems: 
'center', marginBottom: '10px' }} key={index}>
+                                        <Form.Input
+                                            noLabel={true}
+                                            field={`configKey${index}`}
+                                            
placeholder={t('metadata.add-config-key')}
+                                            style={{ width: "329px"}}
+                                            showClear />
+                                        <Form.Input
+                                            noLabel={true}
+                                            field={`configValue${index}`}
+                                            
placeholder={t('metadata.add-config-value')}
+                                            style={{ width: "329px", 
marginLeft: '10px' }}
+                                            showClear />
+                                        <Button
+                                            onClick={() => 
handleRemoveConfig(index)}
+                                            icon={<IconMinus />}
+                                            style={{ marginLeft: '10px', 
borderRadius: '50%'}}
+                                        />
+                                    </div>
+                                ))}
+                            </Form.Section>
+                        </>
+                    )
+                }
+            </Form>
+        </>
+    );
+}
+
+export default TableForm;
\ No newline at end of file
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/TableModalForm/index.tsx
similarity index 61%
copy from 
paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
copy to 
paimon-web-ui/src/pages/Metadata/components/LeftContent/components/TableModalForm/index.tsx
index 05780b1..53938c5 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
+++ 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/TableModalForm/index.tsx
@@ -17,20 +17,29 @@ under the License. */
 
 import React, {useState} from "react";
 import { Modal } from '@douyinfe/semi-ui';
-import CatalogForm from 
"@pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm";
+import {useTranslation} from "react-i18next";
+import {auto} from "@popperjs/core";
+import TableForm from 
"@pages/Metadata/components/LeftContent/components/TableModalForm/TableForm";
 
-type CatalogModalFormProps = {
+type TableModalFormProps = {
     visible: boolean;
     onClose: () => void;
     onOk: (formApi: any) => void;
+    catalogName: string | null;
+    databaseName: string | null;
 };
 
-
-const CatalogModalForm: React.FC<CatalogModalFormProps> = ({ visible , 
onClose, onOk }) => {
+const TableModalForm: React.FC<TableModalFormProps> = ({ visible , onClose, 
onOk, catalogName, databaseName }) => {
     const [formApi, setFormApi] = useState(null);
+    const { t } = useTranslation();
 
     const getFormApi = (api: any) => {
-        setFormApi(api);
+        const formData = {
+            ... api,
+            catalogName,
+            databaseName,
+        }
+        setFormApi(formData);
     };
 
     const handleOkClick = async () => {
@@ -40,19 +49,19 @@ const CatalogModalForm: React.FC<CatalogModalFormProps> = 
({ visible , onClose,
 
     return(
         <Modal
-            title="Create Catalog"
+            title = {t('metadata.create-table')}
             visible = {visible}
             onCancel= {onClose}
             maskClosable={false}
-            okText={'Submit'}
-            cancelText={'Cancel'}
-            width={'750px'}
-            height={'550px'}
+            okText={t('metadata.submit')}
+            cancelText={t('metadata.cancel')}
+            width={'760px'}
+            height={auto}
             onOk={() => handleOkClick()}
         >
-            <CatalogForm getFormApi={getFormApi}/>
+            <TableForm getFormApi={getFormApi}/>
         </Modal>
     );
 }
 
-export default CatalogModalForm;
\ No newline at end of file
+export default TableModalForm;
\ No newline at end of file
diff --git a/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx 
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
index 1bada6f..c7979da 100644
--- a/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
+++ b/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
@@ -22,6 +22,7 @@ import CatalogModalForm from 
"@pages/Metadata/components/LeftContent/components/
 import {useCatalogStore} from "@src/store/catalogStore.ts";
 import styles from "./left-content.module.less";
 import { useTranslation } from 'react-i18next';
+import {useDatabaseStore} from "@src/store/databaseStore.ts";
 
 
 const MetadataSidebar = () => {
@@ -30,6 +31,7 @@ const MetadataSidebar = () => {
     const createFilesystemCatalog = useCatalogStore(state => 
state.createFileSystemCatalog);
     const createHiveCatalog = useCatalogStore(state => 
state.createHiveCatalog);
     const fetchCatalogData = useCatalogStore(state => state.fetchCatalogData);
+    const fetchAllDatabases = useDatabaseStore(state => state.fetchDatabases);
 
     const handleOpenModal = () => {
         setShowModal(true);
@@ -60,12 +62,14 @@ const MetadataSidebar = () => {
                         createFilesystemCatalog(catalogProp)
                             .then(() => {
                                 fetchCatalogData();
+                                fetchAllDatabases();
                                 resolve();
                             })
                     } else {
                         createHiveCatalog(catalogProp)
                             .then(() => {
                                 fetchCatalogData();
+                                fetchAllDatabases();
                                 resolve();
                             })
                     }
diff --git a/paimon-web-ui/src/router/index.tsx 
b/paimon-web-ui/src/router/index.tsx
index 3a9af47..8285513 100644
--- a/paimon-web-ui/src/router/index.tsx
+++ b/paimon-web-ui/src/router/index.tsx
@@ -16,7 +16,7 @@ specific language governing permissions and limitations
 under the License. */
 
 /*import { lazy } from 'react'*/
-import { RouteObject } from 'react-router';
+import {Navigate, RouteObject} from 'react-router';
 import { useRoutes } from 'react-router-dom';
 import LayoutPage from '@src/pages/Layout';
 import PlaygroundPage from '@src/pages/Playground';
@@ -36,6 +36,10 @@ const routeList: RouteObject[] = [
         path: '/layout',
         element: <LayoutPage/>,
         children: [
+            {
+                path: '/',
+                element: <Navigate to="playground" />
+            },
             {
                 path: 'playground',
                 element: <PlaygroundPage/>
diff --git a/paimon-web-ui/src/store/databaseStore.ts 
b/paimon-web-ui/src/store/databaseStore.ts
new file mode 100644
index 0000000..b597b89
--- /dev/null
+++ b/paimon-web-ui/src/store/databaseStore.ts
@@ -0,0 +1,66 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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 {create} from 'zustand';
+import Api from "@api/api.ts";
+import {Toast} from "@douyinfe/semi-ui";
+import {DatabaseItem} from "@src/types/Database/data";
+import i18n from 'i18next';
+
+type Store = {
+    databaseItemList: DatabaseItem[];
+    createDatabase: (databaseProp: Prop.DatabaseProp) => Promise<void>;
+    fetchDatabases: () => Promise<void>;
+};
+
+export const useDatabaseStore = create<Store>((set) => ({
+    databaseItemList: [],
+    createDatabase: async (databaseProp) => {
+        try {
+            const response = await Api.createDatabase(databaseProp);
+            if (!response) {
+                throw new Error('No response from createDatabase');
+            }
+            if (response.code === 200) {
+                Toast.success(i18n.t('metadata.create-database-success'));
+            } else {
+                console.error('Failed to create database:', response.msg);
+                Toast.error(i18n.t('metadata.create-database-failed') +  
response.msg);
+            }
+        } catch (error) {
+            console.error('Failed to create database:', error);
+            Toast.error(i18n.t('metadata.create-database-failed') + error);
+        }
+    },
+    fetchDatabases: async () => {
+        try {
+            const result = await Api.getAllDatabases();
+            if (result && result.data) {
+                const newDatabaseItemList = result.data.map((item) => {
+                    return {
+                        databaseName: item.databaseName,
+                        catalogId: item.catalogId,
+                        description: item.description,
+                    };
+                });
+                set((state) => ({ ...state, databaseItemList: 
newDatabaseItemList }));
+            }
+        } catch (error) {
+            console.error('Failed to get databases:', error);
+        }
+    },
+}));
\ No newline at end of file
diff --git a/paimon-web-ui/src/store/tableStore.ts 
b/paimon-web-ui/src/store/tableStore.ts
new file mode 100644
index 0000000..1c07932
--- /dev/null
+++ b/paimon-web-ui/src/store/tableStore.ts
@@ -0,0 +1,79 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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 {create} from 'zustand';
+import Api from "@api/api.ts";
+import {Toast} from "@douyinfe/semi-ui";
+import i18n from 'i18next';
+import {TableItem} from "@src/types/Table/data";
+
+type Store = {
+    inputs: Array<{}>;
+    configs: Array<{}>;
+    tableItemList: TableItem[];
+    setInputs: (newInputs: Array<{}>) => void;
+    setConfigs: (newConfigs: Array<{}>) => void;
+    createTable: (tableProp: TableItem) => Promise<void>;
+    fetchTables: () => Promise<void>;
+};
+
+export const useTableStore = create<Store>((set) => ({
+    inputs: [{}],
+    configs: [],
+    tableItemList: [],
+    setInputs: (newInputs) => set(() => ({ inputs: newInputs })),
+    setConfigs: (newConfigs) => set(() => ({ configs: newConfigs })),
+    createTable: async (tableProp) => {
+        try {
+            const response = await Api.createTable(tableProp);
+            if (!response) {
+                throw new Error('No response from createTable');
+            }
+            if (response.code === 200) {
+                Toast.success(i18n.t('metadata.create-table-success'));
+            } else {
+                console.error('Failed to create table:', response.msg);
+                Toast.error(i18n.t('metadata.create-table-failed') +  
response.msg);
+            }
+        } catch (error) {
+            console.error('Failed to create table:', error);
+            Toast.error(i18n.t('metadata.create-table-failed') + error);
+        }
+    },
+    fetchTables: async () => {
+        try {
+            const result = await Api.getAllTables();
+            if (result && result.data) {
+                const newTableItemList = result.data.map((item) => {
+                    return {
+                        catalogName: item.catalogName,
+                        databaseName: item.databaseName,
+                        tableName: item.tableName,
+                        description: item.description,
+                        tableColumns: item.tableColumns,
+                        partitionKey: item.partitionKey,
+                        tableOptions: item.tableOptions,
+                    };
+                });
+                set((state) => ({ ...state, tableItemList: newTableItemList 
}));
+            }
+        } catch (error) {
+            console.error('Failed to get tables:', error);
+        }
+    },
+}));
+
diff --git 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
 b/paimon-web-ui/src/types/Database/data.d.ts
similarity index 87%
copy from 
paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
copy to paimon-web-ui/src/types/Database/data.d.ts
index 4f3f847..792be79 100644
--- 
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
+++ b/paimon-web-ui/src/types/Database/data.d.ts
@@ -15,8 +15,8 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-.catalog-tree-input {
-  //background-color: var(--semi-color-bg-0);
-  width: 347px;
-  margin-left: -12px;
+export type DatabaseItem = {
+    databaseName: string,
+    catalogId: number,
+    description: string,
 }
\ No newline at end of file
diff --git a/paimon-web-ui/src/types/Public/data.d.ts 
b/paimon-web-ui/src/types/Public/data.d.ts
index accfa53..cb0a630 100644
--- a/paimon-web-ui/src/types/Public/data.d.ts
+++ b/paimon-web-ui/src/types/Public/data.d.ts
@@ -33,9 +33,6 @@ export interface PageData<T> {
 // with page
 export type PageResult<T> = Result<PageData<T>>
 
-
-
-
 export interface BaseBeanColumns extends ExcludeNameAndEnableColumns {
     name: string,
     enabled: boolean,
diff --git a/paimon-web-ui/src/api/endpoints.ts 
b/paimon-web-ui/src/types/Table/data.d.ts
similarity index 66%
copy from paimon-web-ui/src/api/endpoints.ts
copy to paimon-web-ui/src/types/Table/data.d.ts
index 1567581..80c7896 100644
--- a/paimon-web-ui/src/api/endpoints.ts
+++ b/paimon-web-ui/src/types/Table/data.d.ts
@@ -15,15 +15,20 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export const API_ENDPOINTS = {
+export type TableColumn = {
+    field: string,
+    dataType: string,
+    comment: string | null,
+    isPK: boolean,
+    defaultValue: string | null,
+}
 
-    // auth && login
-    GET_LDAP_ENABLE: '/ldap/enable',
-    LOGIN: '/login',
-
-    // catalog
-    CREATE_FILE_SYSTEM_CATALOG: '/catalog/createFilesystemCatalog',
-    CREATE_HIVE_CATALOG: '/catalog/createHiveCatalog',
-    GET_ALL_CATALOGS: '/catalog/getAllCatalogs',
-
-};
\ No newline at end of file
+export type TableItem = {
+    catalogName: string,
+    databaseName: string,
+    tableName: string,
+    description: string | null,
+    tableColumns: TableColumn[],
+    partitionKey: [],
+    tableOptions: Map<string, string>,
+}
\ No newline at end of file

Reply via email to