You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by li...@apache.org on 2020/03/24 01:14:16 UTC

[submarine] branch master updated: SUBMARINE-431. [WEB] Implement data page with Angular

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

liuxun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new a9594e4  SUBMARINE-431. [WEB] Implement data page with Angular
a9594e4 is described below

commit a9594e476e47520e4e4bdb498d2e7bdbf5cca603
Author: jasoonn <b0...@ntu.edu.tw>
AuthorDate: Mon Mar 23 15:24:35 2020 +0800

    SUBMARINE-431. [WEB] Implement data page with Angular
    
    ### What is this PR for?
    Implement frontend of data page in workbench with Angular
    
    ### What type of PR is it?
     Feature
    
    ### Todos
    
    ### What is the Jira issue?
    https://issues.apache.org/jira/browse/SUBMARINE-431
    
    ### How should this be tested?
    [Travis](https://github.com/apache/submarine/pull/244/checks)
    
    ### Screenshots (if appropriate)
    ![](https://i.imgur.com/McgdjCc.gif)
    
    ### Questions:
    * Does the licenses files need update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Author: jasoonn <b0...@ntu.edu.tw>
    
    Closes #244 from jasoonn/SUBMARINE-431 and squashes the following commits:
    
    f3dc3e8 [jasoonn] update
---
 .../org/apache/submarine/integration/dataIT.java   |  69 +++++
 .../app/pages/workbench/data/data.component.html   | 281 ++++++++++++++++++++-
 .../app/pages/workbench/data/data.component.scss   |  39 +++
 .../src/app/pages/workbench/data/data.component.ts | 147 +++++++++++
 .../src/app/pages/workbench/job/job.component.html |  11 +-
 5 files changed, 541 insertions(+), 6 deletions(-)

diff --git a/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/dataIT.java b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/dataIT.java
new file mode 100644
index 0000000..f574c67
--- /dev/null
+++ b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/dataIT.java
@@ -0,0 +1,69 @@
+/*
+ * 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.submarine.integration;
+
+import org.apache.submarine.AbstractSubmarineIT;
+import org.apache.submarine.WebDriverManager;
+import org.openqa.selenium.By;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+public class dataIT extends AbstractSubmarineIT {
+
+  public final static Logger LOG = LoggerFactory.getLogger(dataIT.class);
+
+  @BeforeClass
+  public static void startUp(){
+    LOG.info("[Testcase]: dataIT");
+    driver =  WebDriverManager.getWebDriver();
+  }
+
+  @AfterClass
+  public static void tearDown(){
+    driver.quit();
+  }
+
+  @Test
+  public void dataNavigation() throws Exception {
+    // Login
+    LOG.info("Login");
+    pollingWait(By.cssSelector("input[ng-reflect-name='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin");
+    pollingWait(By.cssSelector("input[ng-reflect-name='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin");
+    clickAndWait(By.cssSelector("button[class='login-form-button ant-btn ant-btn-primary']"));
+    pollingWait(By.cssSelector("a[routerlink='/workbench/dashboard']"), MAX_BROWSER_TIMEOUT_SEC);
+
+    // Routing to data page
+    pollingWait(By.xpath("//span[contains(text(), \"Data\")]"), MAX_BROWSER_TIMEOUT_SEC).click();
+    Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/workbench/data");
+
+    // Test create new Table
+    pollingWait(By.xpath("//button[@id='createBtn']"), MAX_BROWSER_TIMEOUT_SEC).click();
+    Assert.assertEquals(pollingWait(By.xpath("//form"), MAX_BROWSER_TIMEOUT_SEC).isDisplayed(), true);
+
+    pollingWait(By.xpath("//button[@id='firstNextBtn']"), MAX_BROWSER_TIMEOUT_SEC).click();
+    pollingWait(By.xpath("//input[@id='tableName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("e2e test Table");
+    pollingWait(By.xpath("//button[@id='secondNextBtn']"), MAX_BROWSER_TIMEOUT_SEC).click();
+
+    pollingWait(By.xpath("//button[@id='submit']"), MAX_BROWSER_TIMEOUT_SEC).click();
+    Assert.assertEquals(pollingWait(By.xpath("//thead"), MAX_BROWSER_TIMEOUT_SEC).isDisplayed(), true);
+  }
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.html
index 54de2cd..efb9101 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.html
@@ -17,4 +17,283 @@
   ~ under the License.
   -->
 
-<p>data works!</p>
+  <nz-layout style="margin: -24px -24px 16px;">
+    <nz-layout class="inner-layout">
+      <div id="dataOuter">
+        <nz-breadcrumb>
+          <nz-breadcrumb-item>
+            <a [routerLink]="['/', 'workbench', 'home']">Home</a>
+          </nz-breadcrumb-item>
+          <nz-breadcrumb-item>Data</nz-breadcrumb-item>
+        </nz-breadcrumb>
+        <br/>
+        <h2>Data</h2>
+        <nz-content>A Submarine database is a collection of tables. A Submarine table is a collection of structured data. </nz-content>
+        <br>
+      </div>
+      
+      <div *ngIf="newTable==false">
+        <div class="trainingDiv">
+          <h1 style="margin-top: 15px;margin-left: 25px;">
+            Database
+            <button nz-button id="createBtn" [nzSize]='large' nzType="primary" style="margin-top: 8px;margin-right: 50px;float: right;" (click)="newTable=true;"><i nz-icon nzType="plus"></i>Create Table</button>
+          </h1>
+        </div>
+        <div class="trainingDiv" style="margin-top: 2px;">
+          <div nz-row>
+            <div nz-col [nzSpan]="6">
+              <h3 style="margin-left: 20px;margin-top: 20px;">Database & Table List</h3>
+              <nz-select
+                [nzMaxTagCount]="3"
+                [nzMaxTagPlaceholder]="tagPlaceHolder"
+                nzMode="multiple"
+                nzPlaceHolder="Please select"
+                [(ngModel)]="listOfDatabaseValue"
+                style="width: 80%;margin-left: 20px;margin-top: 20px;"
+              >
+                <nz-option *ngFor="let database of listofDatabase" [nzLabel]="database" [nzValue]="database"></nz-option>
+              </nz-select>
+              <nz-input-group nzSearch style="width:80%;margin-top: 10px;margin-left: 20px;" [nzAddOnAfter]="suffixIconButton">
+                <input type="text" nz-input placeholder="input search text" [(ngModel)]="searchValue"  />
+              </nz-input-group>
+              <ng-template #suffixIconButton>
+                <button nz-button nzSearch (click)="listSort()"><i nz-icon nzType="search"></i></button>
+              </ng-template>
+            </div>
+            <div nz-col [nzSpan]="18">
+              <nz-tabset>
+                <nz-tab nzTitle="Schema">
+                  <nz-table #basicTable nzBordered [nzFrontPagination]="false" [nzData]="listData" [nzNoResult]="'No data'" [nzTitle]="addColButton">
+                    <thead>
+                      <tr>
+                        <th>Column Name</th>
+                        <th>Column Type</th>
+                        <th>Comment</th>
+                        <th>Action</th>
+                      </tr>
+                    </thead>
+                    <tbody>
+                      <tr *ngFor="let data of basicTable.data">
+                        <td>
+                          <ng-container *ngIf="!data.edit; else nameInputTpl">
+                            {{ data.name }}
+                          </ng-container>
+                          <ng-template #nameInputTpl>
+                            <input type="text" nz-input [(ngModel)]="data.nameTmp" />
+                          </ng-template>
+                        </td>
+                        <td>
+                          <ng-container *ngIf="!data.edit; else typeInputTpl">
+                            {{ data.type }}
+                          </ng-container>
+                          <ng-template #typeInputTpl>
+                            <input type="text" nz-input [(ngModel)]="data.typeTmp" />
+                          </ng-template>
+                        </td>
+                        <td>
+                          <ng-container *ngIf="!data.edit; else cmtInputTpl">
+                            {{ data.comment }}
+                          </ng-container>
+                          <ng-template #cmtInputTpl>
+                            <input type="text" nz-input [(ngModel)]="data.commentTmp" />
+                          </ng-template>
+                        </td>
+                        <td>
+                          <ng-container *ngIf="!data.edit; else actionInputTpl">
+                            <button nz-button nzType="link" (click)="data.edit=true;" style="padding-left: 2px;padding-right: 5px;">Edit</button>|<button nz-button nzType="link" (click)="removeCol(data.name)" style="padding-left: 2px;padding-right: 5px;">Delete</button>
+                          </ng-container>
+                          <ng-template #actionInputTpl>
+                            <button nz-button nzType="link" (click)="saveEdit(data);" style="padding-left: 2px;padding-right: 5px;">Save</button>|<button nz-button nzType="link" (click)="cancelEdit(data)" style="padding-left: 2px;padding-right: 5px;">Cancel</button>
+                          </ng-template>
+                        </td>
+                      </tr>
+                    </tbody>
+                    <ng-template #addColButton>
+                      <button nz-button nzSearch (click)="addCol()"><i nz-icon nzType="plus"></i>Add New Column</button>
+                    </ng-template>
+                  </nz-table>
+                </nz-tab>
+                <nz-tab nzTitle="Sample Data">
+                  <nz-spin [nzSpinning]="true" style="height: 50px;">
+                  </nz-spin>
+                </nz-tab>
+              </nz-tabset>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div *ngIf="newTable">
+        <div class="trainingDiv">
+          <h2 style="margin-top: 15px;margin-left: 25px;">
+            Create a Table in Three Steps
+            <button nz-button nzType="primary" nzShape="circle" style="float:right;margin-right: 15px;" (click)="newTable=false;"><i nz-icon
+              nzType="close" nzTheme="outline"></i></button>
+          </h2>
+        </div>
+        <div class="trainingDiv" style="margin-top: 2px;">
+          <div style="padding-left:25%;padding-right:25%;margin-top: 30px;">
+            <nz-steps [nzCurrent]="newTablePage">
+              <nz-step nzTitle="Specify Data Source"></nz-step>
+              <nz-step nzTitle="Specify Table Attributes"></nz-step>
+              <nz-step nzTitle="Review Table Data"></nz-step>
+            </nz-steps>
+          </div>
+          <div [ngSwitch]="newTablePage">
+            <form [formGroup]="createTable">
+              <div *ngSwitchCase="0">
+                <div>
+                  <label class="form-label" style="margin-top: 62px;">Data soruce:</label>
+                  <nz-radio-group class="form-control" style="margin-top: 55px;" formControlName="dataSource">
+                    <label nz-radio-button nzValue="upload">Upload File</label>
+                    <label nz-radio-button nzValue="hdfs">HDFS</label>
+                    <label nz-radio-button nzValue="s3" nzDisabled>S3</label>
+                  </nz-radio-group>
+                  <div>
+                    <label class="form-label">Path:</label>
+                    <nz-input-group nzAddOnBefore="file://" style="width: 400px;margin-top: 25px;">
+                      <input type="text" nz-input class="form-control" formControlName="path" />
+                    </nz-input-group>
+                  </div>
+                  <div style="margin-left: 38%;" *ngIf="createTable.get('dataSource').value=='upload'">
+                    <label style="float:left;width:200px;text-align: right;padding-right: 12px;clear: left;margin-top: 32px;color: black;margin-left: -21%;">Upload Files:</label>
+                    <nz-upload nzAction="https://jsonplaceholder.typicode.com/posts/">
+                      <button nz-button style="margin-top: 25px;"><i nz-icon nzType="upload"></i><span>Click to Upload</span></button>
+                    </nz-upload>
+                  </div>
+                  <div>
+                    <label class="form-label">File Type:</label>
+                    <nz-select style="width: 200px;margin-top: 25px;" nzShowSearch nzAllowClear nzPlaceHolder="Select file type" class="form-control" formControlName="fileType">
+                      <nz-option nzLabel="csv" nzValue="csv"></nz-option>
+                      <nz-option nzLabel="csv" nzValue="csv"></nz-option>
+                      <nz-option nzLabel="csv" nzValue="csv"></nz-option>
+                    </nz-select>
+                  </div>
+                  <div>
+                    <label class="form-label"><span class="red-star">* </span> Column Delimiter:</label>
+                      <input type="text" style="width: 50px;margin-top: 25px;" nz-input class="form-control" formControlName="columnDelimeter" />
+                      <label nz-checkbox style="margin-left: 20px;" class="form-control" formControlName="header">First row is header</label>
+                  </div>
+                </div>
+                <div style="margin-top: 32px;text-align: center;">
+                  <button id="firstNextBtn" nz-button nzType="primary" [disabled]="!createTable.get('columnDelimeter').valid" (click)="newTablePage = newTablePage + 1">Create Table with UI </button>
+                  <button style="margin-left: 5px;" nz-button nzType="default" [disabled]="true" (click)="openNotebook()">Create Table in Notebook </button>
+                </div>
+              </div>
+              <div *ngSwitchCase="1">
+                <div>
+                  <label class="form-label">Database Name:</label>
+                  <nz-select style="width: 200px;margin-top: 25px;" nzShowSearch nzAllowClear nzPlaceHolder="Select database" class="form-control" formControlName="dataBaseName">
+                    <nz-option nzLabel="default" nzValue="default"></nz-option>
+                    <nz-option nzLabel="db1" nzValue="db1"></nz-option>
+                    <nz-option nzLabel="db2" nzValue="db2"></nz-option>
+                  </nz-select>
+                </div>
+                <div>
+                  <label class="form-label"><span class="red-star">* </span> Table Name:</label>
+                    <input id="tableName" type="text" style="width: 200px;margin-top: 25px;" nz-input class="form-control" formControlName="tableName" />
+                </div>
+                <div>
+                  <label class="form-label">Parse Columns:</label>
+                  <button nz-button style="margin-top: 25px;" nzType="default" (click)="parseColumn()"><i nz-icon nzType="sync" nzTheme="outline"></i>Parse </button>
+                </div>
+                <nz-table #createTTable style="margin-left:30%;margin-right:30%;margin-top: 25px;" nzBordered [nzFrontPagination]="false" [nzData]="listCreateData" [nzNoResult]="'No data'" [nzTitle]="addCreateColButton">
+                  <thead>
+                    <tr>
+                      <th>Column Name</th>
+                      <th>Column Type</th>
+                      <th>Comment</th>
+                      <th>Action</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <tr *ngFor="let data of createTTable.data">
+                      <td>
+                        <ng-container *ngIf="!data.edit; else nameCreateInputTpl">
+                          {{ data.name }}
+                        </ng-container>
+                        <ng-template #nameCreateInputTpl>
+                          <input type="text" style="width: 120px;" nz-input [ngModelOptions]="{standalone: true}" [(ngModel)]="data.nameTmp" />
+                        </ng-template>
+                      </td>
+                      <td>
+                        <ng-container *ngIf="!data.edit; else typeCreateInputTpl">
+                          {{ data.type }}
+                        </ng-container>
+                        <ng-template #typeCreateInputTpl>
+                          <input style="width: 120px;" type="text" nz-input [(ngModel)]="data.typeTmp" [ngModelOptions]="{standalone: true}" />
+                        </ng-template>
+                      </td>
+                      <td>
+                        <ng-container *ngIf="!data.edit; else cmtCreateInputTpl">
+                          {{ data.comment }}
+                        </ng-container>
+                        <ng-template #cmtCreateInputTpl>
+                          <input type="text" style="width: 120px;" nz-input [(ngModel)]="data.commentTmp" [ngModelOptions]="{standalone: true}" />
+                        </ng-template>
+                      </td>
+                      <td>
+                        <ng-container *ngIf="!data.edit">
+                          <button nz-button nzType="link" (click)="data.edit=true;" style="padding-left: 2px;padding-right: 5px;">Edit</button>|<button nz-button nzType="link" (click)="removeCreateCol(data.name)" style="padding-left: 2px;padding-right: 5px;">Delete</button>
+                        </ng-container>
+                        <ng-container *ngIf="data.edit">
+                          <button nz-button nzType="link" (click)="saveCreateEdit(data);" style="padding-left: 2px;padding-right: 5px;">Save</button>|<button nz-button nzType="link" (click)="cancelCreateEdit(data)" style="padding-left: 2px;padding-right: 5px;">Cancel</button>
+                        </ng-container>
+                      </td>
+                    </tr>
+                  </tbody>
+                  <ng-template #addCreateColButton>
+                    <button nz-button nzSearch (click)="addCreateCol()"><i nz-icon nzType="plus"></i>Add New Column</button>
+                  </ng-template>
+                </nz-table>
+                <div style="margin-top: 32px;text-align: center;">
+                  <button nz-button nzType="default" (click)="newTablePage = newTablePage - 1">Previous Step </button>
+                  <button id="secondNextBtn" style="margin-left: 5px;" nz-button nzType="primary" [disabled]="!createTable.get('tableName').valid" (click)="newTablePage = newTablePage + 1">Next Step </button>
+                </div>
+              </div>
+              <div *ngSwitchCase="2">
+                <nz-spin [nzSpinning]="true">
+                  <h3 style="margin-top: 50px;">Sample Data</h3>
+                </nz-spin>
+                <div style="margin-top: 32px;text-align: center;">
+                  <button nz-button nzType="default" (click)="newTablePage = newTablePage - 1">Previous Step </button>
+                  <button id="submit" style="margin-left: 5px;" nz-button nzType="primary" type="button" (click)="submit()">Submit</button>
+                </div>
+              </div>
+            </form>
+          </div>
+        </div>
+        <div class="trainingDiv" style="margin-top: 2px;padding-left: 100px;">
+          <div [ngSwitch]="newTablePage">
+            <div *ngSwitchCase="0">
+              <h3 style="color: gray;"><strong>Description</strong></h3>
+              <h4 style="color: gray;margin-top: 20px;"><strong>Data source</strong></h4>
+              <h4 style="color: gray;">Upload one or more local files to the store and create table structures and data based on the file to content.</h4>
+              <h4 style="color: gray;margin-top: 15px;"><strong>Path</strong></h4>
+              <h4 style="color: gray;">Specify the storage path of the file.</h4>
+              <h4 style="color: gray;margin-top: 15px;"><strong>Upload Files</strong></h4>
+              <h4 style="color: gray;">When using the `upload file` mode, add the local file to the upload list by clicking the `Select File` button. Click the `Start Upload` button to upload the file to the specified storage directory.</h4>
+              <h4 style="color: gray;margin-top: 15px;"><strong>File type</strong></h4>
+              <h4 style="color: gray;">Select the type of file to upload, the system will parse the file according to the file type you choose.</h4>
+              <h4 style="color: gray;margin-top: 15px;"><strong>Column Delimiter</strong></h4>
+              <h4 style="color: gray;">Sets the separator for each column in the record, and the system splits the field based on the separator.</h4>
+              <h4 style="color: gray;margin-top: 15px;"><strong>Create Table with UI</strong></h4>
+              <h4 style="color: gray;">Use the UI operation interface to set the table schema, preview the data, and create the table step by step.</h4>
+              <h4 style="color: gray;margin-top: 15px;"><strong>Create Table in Notebook</strong></h4>
+              <h4 style="color: gray;">Create a table by handwriting the code through the notebook.</h4>
+            </div>
+            <div *ngSwitchCase="1">
+              <h3 style="color: gray;"><strong>Description</strong></h3>
+              <h4 style="color: gray;margin-top: 20px;"><strong>Database Name</strong></h4>
+              <h4 style="color: gray;">Select which database belongs to which table you want to create.</h4>
+              <h4 style="color: gray;margin-top: 20px;"><strong>Table Name</strong></h4>
+              <h4 style="color: gray;">Set the name of the table and automatically check if the table name conflicts.</h4>
+              <h4 style="color: gray;margin-top: 20px;"><strong>Parse Columns</strong></h4>
+              <h4 style="color: gray;">Click the Parse button to analyze the schema field and type of the table from the uploaded file.</h4>
+              <h4 style="color: gray;margin-top: 20px;"><strong>Columns Attributes Table</strong></h4>
+              <h4 style="color: gray;">You can modify the field name and type for the analyzed schema.</h4>
+            </div>
+          </div>
+        </div>
+      </div>
+  </nz-layout>
+</nz-layout>
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
index 3d56d22..6d4337c 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
@@ -16,3 +16,42 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+ #dataOuter{
+    background-color: white;
+    padding-left: 30px;
+    padding-top: 20px;
+ }
+
+ .trainingDiv{
+    margin-top: 25px;
+    margin-left: 20px;
+    margin-right: 20px;
+    padding: 10px;
+    background-color:white;
+  }
+  
+  .horizontal_dotted_line{
+   border-top: 2px dotted whitesmoke;
+   color:transparent;
+ } 
+
+ .red-star {
+   margin-top: 20px;
+   color: red;
+}
+
+.form-label {
+   float:left;
+   width:200px;
+   text-align: right;
+   padding-right: 12px;
+   margin-top: 32px;
+   margin-left: 25%;
+   clear: left;
+   color: black;
+}
+
+input.ng-invalid.ng-touched {
+   border: 1px solid red;
+ }
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.ts
index 9fa6250..bf05996 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.ts
@@ -18,6 +18,7 @@
  */
 
 import { Component, OnInit } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 @Component({
   selector: 'submarine-data',
@@ -26,9 +27,155 @@ import { Component, OnInit } from '@angular/core';
 })
 export class DataComponent implements OnInit {
 
+  listOfDatabaseValue = ['default'];
+  listofDatabase = ['db1', 'db2', 'default'];
+  searchValue = "";
+
+  listData= [
+    {
+      name: 'col-0',
+      type: 'string',
+      comment: 'comment...',
+      nameTmp: 'col-0',
+      typeTmp: 'string',
+      commentTmp: 'comment...',
+      edit: false
+    },
+    {
+      name: 'col-1',
+      type: 'string',
+      comment: 'comment...',
+      nameTmp: 'col-1',
+      typeTmp: 'string',
+      commentTmp: 'comment...',
+      edit: false
+    }
+  ];
+  listCount = 2;
+
+  //Create table part
+  newTable = false;
+  newTablePage = 0;
+  createTable: FormGroup;
+  listCreateData = [];
+
   constructor() { }
 
   ngOnInit() {
+    this.createTable =  new FormGroup({
+      'dataSource': new FormControl("upload"),
+      'path': new FormControl(null),
+      'uploadFile': new FormControl(null),
+      'fileType': new FormControl(null),
+      'columnDelimeter': new FormControl('.', [Validators.required]),
+      'header': new FormControl('false'),
+      
+      'dataBaseName': new FormControl('db1'),
+      'tableName': new FormControl(null, [Validators.required])
+    });
+  }
+
+  //TODO(jasoonn): Perform sorting
+  listSort(){
+    console.log('sort list according to ' + this.searchValue);
+  }
+
+  cancelEdit(data){
+    data.nameTmp = data.name;
+    data.typeTmp = data.type;
+    data.commentTmp = data.comment;
+    data.edit = false;
+  }
+
+  //TODO(jasoonn): Update remote database
+  saveEdit(data){
+    data.name = data.nameTmp;
+    data.type = data.typeTmp;
+    data.comment = data.commentTmp;
+    data.edit = false;
+  }
+
+  addCol(){
+    this.listData.push(
+      {
+        name: 'col-' + this.listCount,
+        type: 'string',
+        comment: 'comment...',
+        nameTmp: 'col-' + this.listCount,
+        typeTmp: 'string',
+        commentTmp: 'comment...',
+        edit: false
+      }
+    );
+    this.listData=[...this.listData];
+    this.listCount ++;
+  }
+
+  removeCol(name){
+    this.listData = this.listData.filter(d => d.name !== name);
   }
 
+  //TODO(jasoonn): Create Table in Notebook
+  openNotebook(){
+    this.newTable = false;
+    this.newTablePage = 0;
+  }
+
+  //TODO(jasoonn): Parse column while creating Table
+  parseColumn(){
+    ;
+  }
+
+  addCreateCol(){
+    this.listCreateData.push(
+      {
+        name: 'col_' + this.listCreateData.length,
+        type: 'string',
+        comment: 'comment...',
+        nameTmp: 'col_' + this.listCreateData.length,
+        typeTmp: 'string',
+        commentTmp: 'comment...',
+        edit: false
+      }
+    );
+    this.listCreateData=[...this.listCreateData];
+  }
+
+  removeCreateCol(name){
+    this.listCreateData = this.listCreateData.filter(d => d.name !== name);
+  }
+
+  cancelCreateEdit(data){
+    data.nameTmp = data.name;
+    data.typeTmp = data.type;
+    data.commentTmp = data.comment;
+    data.edit = false;
+  }
+
+  //TODO(jasoonn): Update remote database
+  saveCreateEdit(data){
+    data.name = data.nameTmp;
+    data.type = data.typeTmp;
+    data.comment = data.commentTmp;
+    data.edit = false;
+  }
+
+  //TODO(jasoonn): Create table
+  submit(){
+    this.newTable = false;
+    this.newTablePage = 0;
+    console.log(this.createTable);
+    this.createTable =  new FormGroup({
+      'dataSource': new FormControl("upload"),
+      'path': new FormControl(null),
+      'uploadFile': new FormControl(null),
+      'fileType': new FormControl(null),
+      'columnDelimeter': new FormControl('.', [Validators.required]),
+      'header': new FormControl('false'),
+      
+      'dataBaseName': new FormControl('db1'),
+      'tableName': new FormControl(null, [Validators.required])
+    });
+    this.listCreateData = [];
+  }
 }
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html
index f46c164..cf3594f 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html
@@ -39,13 +39,14 @@
         <label nz-radio-button nzValue="Own">Owned By Me</label>
         <label nz-radio-button nzValue="Access">Accessable By Me</label>
       </nz-radio-group>
-      <nz-input-group  style="width:300px;margin-top: 15px;margin-left:10px;margin-right:5px;" [nzSuffix]="suffixIconSearch">
-        <input type="text" nz-input [(ngModel)]="searchText" (keydown)="filter($event)" placeholder="input search text" />
+      <nz-input-group nzSearch style="width:300px;margin-top: 15px;margin-left:10px;margin-right:5px;" [nzAddOnAfter]="suffixIconButton">
+        <input type="text" nz-input placeholder="input search text" />
       </nz-input-group>
-      <ng-template #suffixIconSearch>
-          <i nz-icon nzType="search"></i>
+      <ng-template #suffixIconButton>
+        <button nz-button nzType="primary" nzSearch><i nz-icon nzType="search"></i></button>
       </ng-template>
-      <button nz-button id="openJob" nzType="primary" style="margin-right: 30px;margin-bottom: 15px;" (click)="isVisible=true;"><i nz-icon nzType="plus"></i>New Job</button>
+        
+      <button nz-button id="openJob" nzType="primary" style="margin-right: 30px;margin-bottom: 15px;margin-top: 15px;" (click)="isVisible=true;"><i nz-icon nzType="plus"></i>New Job</button>
     </div>  
     <nz-table id="releaseTable" nzBordered #basicTable [nzData]="joblist" [nzNoResult]="'No data'">
       <thead>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org