You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by dg...@apache.org on 2020/05/06 15:54:08 UTC

[incubator-dlab] 01/01: [DLAB-1773]: Fixed bugs, added possibility save empty folder, multi-file selecting/deleting

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

dgnatyshyn pushed a commit to branch DLAB-1773
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git

commit 64908c130805a66b82282b0140ef8fcd774cdc03
Author: Dmytro Gnatyshyn <di...@ukr.net>
AuthorDate: Wed May 6 18:53:38 2020 +0300

    [DLAB-1773]:  Fixed bugs, added possibility save empty folder, multi-file selecting/deleting
---
 .../app/core/services/bucket-browser.service.ts    |  8 +-
 .../bucket-browser/bucket-browser.component.html   |  9 +--
 .../bucket-browser/bucket-browser.component.scss   |  9 ++-
 .../bucket-browser/bucket-browser.component.ts     | 86 ++++++++++------------
 .../bucket-confirmation-dialog.component.html      | 66 +++++++++++++++++
 .../bucket-confirmation-dialog.component.scss      | 79 ++++++++++++++++++++
 .../bucket-confirmation-dialog.component.ts        | 46 ++++++++++++
 .../folder-tree/folder-tree.component.html         | 16 +++-
 .../folder-tree/folder-tree.component.ts           | 36 +++++++--
 .../webapp/src/app/resources/resources.module.ts   |  6 +-
 10 files changed, 288 insertions(+), 73 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
index c251ed9..54fc3a1 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
@@ -8,7 +8,7 @@ export class TodoItemNode {
   children: TodoItemNode[];
   item: string;
   id: string;
-  size: number;
+  object: any;
 }
 
 export class TodoItemFlatNode {
@@ -49,13 +49,11 @@ export class BucketBrowserService {
         catchError(ErrorUtils.handleServiceError));
   }
 
-  public deleteFile(data) {
-    const url = JSON.stringify(data);
+  public deleteFile(data, url) {
     return this.applicationServiceFacade
-      .buildDeleteFileFromBucket(url)
+      .buildDeleteFileFromBucket(data, url)
       .pipe(
         map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
-
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
index 841b7c6..3d034f6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
@@ -60,14 +60,12 @@
          type="button" class="butt"
          mat-raised-button
          (click)="fileAction('download')"
-         [disabled]="!selected?.length || !this.bucketStatus.download || allDisable"
+         [disabled]=" !selected?.length || this.selectedItems?.length > 1 || !this.bucketStatus.download || allDisable"
        >
          Download
        </button>
       </span>
 
-
-<!--      <button type="button" class="butt" mat-raised-button [disabled]="this.addedFiles.length === 0" (click)="uploadNewFile()">Upload</button>-->
     </div>
     <p class="path"><span>Bucket path:</span><span class="url ellipsis"> {{path}}</span></p>
     <div class="backet-wrapper" [ngClass]="{'added-upload': addedFiles.length}" id="scrolling">
@@ -76,6 +74,7 @@
           (showFolderContent)=onFolderClick($event)
           (disableAll) = dissableAll($event)
           [folders] = folders
+          [endpoint] = endpoint
         > </dlab-folder-tree>
       </div>
       <div class="directory">
@@ -108,8 +107,8 @@
                   </span>
                 <span class="item-name name-wrap" [ngClass]="{'removed-checkbox': !this.bucketStatus.download && !this.bucketStatus.delete}">{{file.item}}</span>
               </div>
-              <div class="size">{{file.size?.size}}</div>
-              <div class="date" *ngIf="!file.isDownloading">{{file.size?.lastModifiedDate }}</div>
+              <div class="size">{{file.object?.size}}</div>
+              <div class="date" *ngIf="!file.isDownloading">{{file.object?.lastModifiedDate }}</div>
               <div class="progress-wrapper" *ngIf="file.isDownloading">
                 <mat-progress-bar mode="indeterminate"></mat-progress-bar>
               </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
index bb7029d..ad605bd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
@@ -82,12 +82,14 @@
   border: 2px solid rgba(0,0,0,.12);
   border-radius: 5px;
   display: flex;
+  width: 100%;
 
   &.added-upload{
     height: 37vh;
   }
 
   .navigation{
+    width: 33.3%;
     flex: 1;
     border-right: 2px solid rgba(0,0,0,.12);
     height: 100%;
@@ -106,6 +108,7 @@
   }
 
   .directory{
+    width: 66.7%;
     max-height: 100%;
     flex: 2;
     font-size: 14px;
@@ -313,7 +316,7 @@ input[type='file'] {
   background-color: white;
   min-height: 100px;
   color: black;
-  
+
   .upload-header{
     padding-left: 8px;
     background: #f6fafe;
@@ -331,7 +334,7 @@ input[type='file'] {
       font-size: 14px;
       background: #f6fafe;
   }
-    
+
     .close{
       position: absolute;
       top: 0;
@@ -419,8 +422,6 @@ input[type='file'] {
   }
 }
 
-
-
 @media only screen and (max-height: 840px) {
   .backet-wrapper {
     height: 45vh;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
index 22f70c2..7f1c8d5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
@@ -27,6 +27,8 @@ import {FolderTreeComponent} from './folder-tree/folder-tree.component';
 import {BucketBrowserService, TodoItemNode} from '../../core/services/bucket-browser.service';
 import {FileUtils} from '../../core/util';
 import {BucketDataService} from './bucket-data.service';
+import {ConfirmationDialogComponent, ConfirmationDialogType} from '../../shared/modal-dialog/confirmation-dialog';
+import {BucketConfirmationDialogComponent} from './bucket-confirmation-dialog/bucket-confirmation-dialog.component';
 
 @Component({
   selector: 'dlab-bucket-browser',
@@ -46,10 +48,10 @@ export class BucketBrowserComponent implements OnInit {
   public selectedFolder: any;
   public selectedFolderForAction: any;
   public selected: any[];
-  private uploadForm: FormGroup;
   public bucketStatus;
   public allDisable: boolean;
   public folders: any[];
+  private selectedItems;
 
 
   constructor(
@@ -61,7 +63,6 @@ export class BucketBrowserComponent implements OnInit {
     private _fb: FormBuilder,
     private bucketBrowserService: BucketBrowserService,
     private bucketDataService: BucketDataService,
-    private formBuilder: FormBuilder
   ) {
 
   }
@@ -70,9 +71,6 @@ export class BucketBrowserComponent implements OnInit {
     this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
     this.endpoint = this.data.endpoint;
     this.bucketStatus = this.data.bucketStatus;
-    this.uploadForm = this.formBuilder.group({
-      file: ['']
-    });
   }
 
   public showItem(item) {
@@ -82,13 +80,11 @@ export class BucketBrowserComponent implements OnInit {
 
   public handleFileInput(event) {
     if (event.target.files.length > 0) {
-      const file = event.target.files[0];
-      this.uploadForm.get('file').setValue(file);
-      const newAddedFiles = Object['values'](event.target.files).map(v => (
-        {name: v['name'], file: file, 'size': (v['size'] / 1048576).toFixed(2), path: this.path, isUploading: false, uploaded: false, errorUploading: false}));
+      const newAddedFiles = Object['values'](event.target.files).map(file => (
+        {name: file['name'], file: file, 'size': (file['size'] / 1048576).toFixed(2), path: this.path, isUploading: false, uploaded: false, errorUploading: false}));
       this.addedFiles = [...this.addedFiles, ...newAddedFiles];
     }
-    // event.target.value = null;
+    event.target.value = '';
   }
 
   public closeUploadWindow() {
@@ -99,25 +95,10 @@ export class BucketBrowserComponent implements OnInit {
 
 
   public toggleSelectedFile(file, type) {
-   // remove if when will be possible download several files
-    if (!file.isSelected || !file.isFolderSelected) {
-      this.folderItems.forEach(item => item.isSelected = false);
-      this.folderItems.forEach(item => item.isFolderSelected = false);
-    }
-    if (type === 'file') {
-      file.isSelected = !file.isSelected;
-      this.selected = this.folderItems.filter(item => item.isSelected);
-      this.selectedFolderForAction = this.folderItems.filter(item => item.isFolderSelected);
-    }
-
-    if (type === 'folder') {
-      file.isFolderSelected = !file.isFolderSelected;
-      this.selected = this.folderItems.filter(item => item.isSelected);
-      this.selectedFolderForAction = this.folderItems.filter(item => item.isFolderSelected);
-    }
-
-    console.log(this.selectedFolderForAction, this.selected );
-
+  type === 'file' ?  file.isSelected = !file.isSelected : file.isFolderSelected = !file.isFolderSelected;
+  this.selected = this.folderItems.filter(item => item.isSelected);
+  this.selectedFolderForAction = this.folderItems.filter(item => item.isFolderSelected);
+  this.selectedItems = [...this.selected, ...this.selectedFolderForAction];
   }
 
   filesPicked(files) {
@@ -186,34 +167,47 @@ export class BucketBrowserComponent implements OnInit {
 
   public fileAction(action) {
     // this.selected = this.folderItems.filter(item => item.isSelected);
-    const selected = this.folderItems.filter(item => item.isSelected)[0];
-    const folderSelected = this.folderItems.filter(item => item.isFolderSelected)[0];
-    if (!selected) {
-      this.toastr.error('Folder deleting not working yet!', 'Oops!');
-      return;
-    }
-    const path = encodeURIComponent(`${this.pathInsideBucket}${this.selected[0].item}`);
+    const selected = this.folderItems.filter(item => item.isSelected);
+    const folderSelected = this.folderItems.filter(item => item.isFolderSelected);
+
     if (action === 'download') {
-      selected['isDownloading'] = true;
+      const path = encodeURIComponent(`${this.pathInsideBucket}${this.selected[0].item}`);
+      selected[0]['isDownloading'] = true;
       this.bucketBrowserService.downloadFile(`/${this.bucketName}/object/${path}/endpoint/${this.endpoint}/download`)
         .subscribe(data =>  {
-        FileUtils.downloadBigFiles(data, selected.item);
-        selected['isDownloading'] = false;
+        FileUtils.downloadBigFiles(data, selected[0].item);
+        selected[0]['isDownloading'] = false;
         this.folderItems.forEach(item => item.isSelected = false);
         }, error => {
             this.toastr.error(error.message || 'File downloading error!', 'Oops!');
-            selected['isDownloading'] = false;
+            selected[0]['isDownloading'] = false;
           }
         );
     }
 
     if (action === 'delete') {
-      this.bucketBrowserService.deleteFile(`/${this.bucketName}/object/${path}/endpoint/${this.endpoint}`).subscribe(() => {
-        this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
-          this.toastr.success('File successfully deleted!', 'Success!');
-        this.folderItems.forEach(item => item.isSelected = false);
-        }, error => this.toastr.error(error.message || 'File deleting error!', 'Oops!')
-      );
+      const itemsForDeleting = [...folderSelected, ...selected];
+      const objects = itemsForDeleting.map(obj => obj.object.object);
+      const dataForServer = [];
+      objects.forEach(object => {
+        dataForServer.push(...this.bucketDataService.serverData.map(v => v.object).filter(v => v.indexOf(object) === 0));
+      });
+      const url = `/${this.bucketName}/endpoint/${this.endpoint}/delete`;
+      console.log(dataForServer);
+      this.dialog.open(BucketConfirmationDialogComponent, {data: {items: itemsForDeleting} , width: '550px'})
+        .afterClosed().subscribe((res) => {
+        !res && this.clearSelection();
+        res && this.bucketBrowserService.deleteFile(dataForServer, url).subscribe(() => {
+            this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
+            this.toastr.success('File successfully deleted!', 'Success!');
+            this.clearSelection();
+          }, error => {
+          this.toastr.error(error.message || 'File deleting error!', 'Oops!');
+          this.clearSelection();
+        });
+      });
+
+
     }
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html
new file mode 100644
index 0000000..aaa707e
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html
@@ -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.
+  -->
+
+<div id="dialog-box" class="confirmation-dialog">
+  <header class="dialog-header">
+    <h4 class="modal-title">
+      <span>
+        <span>Delete objects</span>
+      </span>
+    </h4>
+    <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
+  </header>
+  <div class="dialog-content">
+    <div class="content-box">
+<!--      <p *ngIf="data.type !== 4" class="info text-center">{{ model.title }}</p>-->
+<!--      <div *ngIf="data.type === 4" class="text-center m-bot-20">-->
+<!--        <h3 class="strong">Group data will be updated.</h3>-->
+<!--      </div>-->
+<!--      <p *ngIf="data.type === 4 && data.notebook.length" class="text-center delete-user">User<span *ngIf="data.notebook.length>1">s</span>  <span class="strong"> {{data.notebook.join(', ')}} </span>will be deleted from this group.</p>-->
+<!--&lt;!&ndash;        All <span *ngIf="data.notebook.length===1">his</span><span *ngIf="data.notebook.length>1">their</span> resources authorized within this group will be terminated.&ndash;&gt;-->
+      <mat-list class="resources">
+        <mat-list-item class="list-header">
+          <div class="object">Objects</div>
+          <div class="size">Size</div>
+        </mat-list-item>
+        <div class="scrolling-content" id="scrolling">
+          <mat-list-item *ngFor="let object of data.items">
+            <div class="object">
+              <span *ngIf="object['children']"><i class="material-icons folder-icon" >folder</i></span>
+              <div>{{object['item']}}</div>
+            </div>
+            <div  class="size">{{object['children'] ? '-' : object['object'].size}}</div>
+          </mat-list-item>
+        </div>
+      </mat-list>
+      <div mat-dialog-content class="bottom-message" >
+        <span class="confirm-message" *ngIf="isFolders">All affected objects will be deleted.</span>
+        <span class="confirm-message" *ngIf="!isFolders">These objects will be deleted.</span>
+      </div>
+
+      <div class="text-center m-top-20">
+        <p class="strong">Do you want to proceed?</p>
+      </div>
+      <div class="text-center m-top-20">
+        <button  mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">No</button>
+        <button mat-raised-button type="button" class="butt butt-success action" (click)="dialogRef.close(true)">Yes</button>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss
new file mode 100644
index 0000000..75eb0fe
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss
@@ -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.
+ */
+
+.confirmation-dialog {
+
+  .folder-icon{
+    color: rgb(232, 232, 232);
+    margin-right: 5px;
+  }
+  h3{
+    margin-bottom: 20px;
+  }
+  color: #718ba6;
+  p {
+    font-size: 14px;
+    font-weight: 600;
+    margin: 0;
+    margin-bottom: 10px;
+    &.info {
+      font-weight: 500;
+    }
+  }
+  .resources {
+    .object {
+      width: 70%;
+      display: flex;
+      align-items: center;
+    }
+
+    .size {
+      width: 30%;
+    }
+    .scrolling-content {
+      max-height: 200px;
+      overflow-y: auto;
+    }
+  }
+  .empty-list {
+    display: flex;
+    width: 100%;
+    justify-content: center;
+    color: #35afd5;
+    padding: 15px;
+  }
+
+  .list-header {
+    border-top: 1px solid #edf1f5;
+    border-bottom: 1px solid #edf1f5;
+    color: #577289;
+    width: 100%;
+  }
+
+  .bottom-message{
+    padding-top: 15px;
+    text-align: center;
+    .confirm-message{
+      color: #ef5c4b;
+      font-size: 13px;
+      min-height: 18px;
+      text-align: center;
+      padding-top: 20px}
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts
new file mode 100644
index 0000000..9d9c80c
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts
@@ -0,0 +1,46 @@
+/*
+ * 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 { Component, OnInit, Inject, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
+import { ToastrService } from 'ngx-toastr';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+  selector: 'bucket-confirmation-dialog',
+  templateUrl: 'bucket-confirmation-dialog.component.html',
+  styleUrls: ['./bucket-confirmation-dialog.component.scss'],
+  encapsulation: ViewEncapsulation.None
+})
+
+export class BucketConfirmationDialogComponent implements OnInit {
+  isFolders: boolean = false
+  constructor(
+    @Inject(MAT_DIALOG_DATA) public data: any,
+    public dialogRef: MatDialogRef<BucketConfirmationDialogComponent>,
+    public toastr: ToastrService
+  ) {
+
+  }
+
+  ngOnInit() {
+    this.isFolders = !!this.data.items.filter(v => v.children).length;
+  }
+
+
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
index 7d79751..5aec2e8 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
@@ -1,5 +1,5 @@
 <mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
-  <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding [ngStyle]="{'display': 'none'}">
+  <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding matTreeNodePaddingIndent="20" [ngStyle]="{'display': 'none'}">
   <button mat-icon-button disabled></button>
   <!--    <mat-checkbox class="checklist-leaf-node"-->
   <!--                  [checked]="checklistSelection.isSelected(node)"-->
@@ -7,7 +7,7 @@
   {{node.item}}
 </mat-tree-node>
 
-  <mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding class="input-node">
+  <mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding matTreeNodePaddingIndent="20" class="input-node">
     <form class="add-folder-form">
       <mat-form-field>
         <mat-label>New folder</mat-label>
@@ -23,12 +23,20 @@
 
         </mat-error>
       </mat-form-field>
-      <button (click)="saveNode(node, itemValue.value)" [ngClass]="{'check': folderFormControl.valid && folderFormControl.dirty}" mat-icon-button class="btn action-btn" [disabled]="!folderFormControl.valid || !folderFormControl.dirty"><span><i class="material-icons ">check</i></span></button>
+      <button (click)="saveNode(node, itemValue.value)"
+              [ngClass]="{'check': folderFormControl.valid && folderFormControl.dirty && !folderCreating}"
+              mat-icon-button class="btn action-btn"
+              [disabled]="!folderFormControl.valid || !folderFormControl.dirty"
+              matTooltip="Please wait! Folder is creating."
+              [matTooltipDisabled]="!folderCreating"
+              matTooltipPosition="above"
+      >
+        <span><i class="material-icons ">check</i></span></button>
       <button (click)="removeItem(node)" mat-icon-button class="btn close action-btn"><span ><i class="material-icons ">close</i></span></button>
     </form>
   </mat-tree-node>
 
-  <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
+  <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding  matTreeNodePaddingIndent="20">
     <button mat-icon-button matTreeNodeToggle
             [attr.aria-label]="'toggle ' + node.filename">
       <mat-icon class="mat-icon-rtl-mirror" [ngClass]="{'active-item': selectedFolder === node}">
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
index 4aeab2b..ff1ae69 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
@@ -8,6 +8,7 @@ import {Subscription} from 'rxjs';
 import {FormControl, FormGroupDirective, NgForm, Validators} from '@angular/forms';
 import {ErrorStateMatcher} from '@angular/material/core';
 import {PATTERNS} from '../../../core/util';
+import {ToastrService} from 'ngx-toastr';
 
 export class MyErrorStateMatcher implements ErrorStateMatcher {
   isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
@@ -27,6 +28,7 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
   @Output() showFolderContent: EventEmitter<any> = new EventEmitter();
   @Output() disableAll: EventEmitter<any> = new EventEmitter();
   @Input() folders;
+  @Input() endpoint;
 
   private folderTreeSubs;
   private path = [];
@@ -34,7 +36,7 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
   private flatNodeMap = new Map<TodoItemFlatNode, TodoItemNode>();
   private nestedNodeMap = new Map<TodoItemNode, TodoItemFlatNode>();
   private selectedParent: TodoItemFlatNode | null = null;
-  private newItemName = '';
+  private folderCreating = false;
   private subscriptions: Subscription = new Subscription();
   public treeControl: FlatTreeControl<TodoItemFlatNode>;
   private treeFlattener: MatTreeFlattener<TodoItemNode, TodoItemFlatNode>;
@@ -44,6 +46,7 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
   private checklistSelection = new SelectionModel<TodoItemFlatNode>(true /* multiple */);
 
   constructor(
+    public toastr: ToastrService,
     private bucketBrowserService: BucketBrowserService,
     private bucketDataService: BucketDataService,
     ) {
@@ -226,6 +229,8 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
 
   private addNewItem(node: TodoItemFlatNode, file, isFile, path) {
     const parentNode = this.flatNodeMap.get(node);
+
+    console.log(parentNode);
     this.bucketDataService.insertItem(parentNode!, file, isFile);
     this.treeControl.expand(node);
   }
@@ -237,14 +242,31 @@ export class FolderTreeComponent implements OnInit, OnDestroy {
   }
 
   private saveNode(node: TodoItemFlatNode, itemValue: string) {
-    const nestedNode = this.flatNodeMap.get(node);
-    this.bucketDataService.updateItem(nestedNode!, itemValue);
-    this.resetForm();
-    this.folderFormControl.updateValueAndValidity();
-    this.folderFormControl.markAsPristine();
+    this.folderCreating = true;
+    const parent = this.getParentNode(node);
+    const flatParent = this.flatNodeMap.get(parent);
+    const path = `${flatParent.object.object}${itemValue}/`;
+    const formData = new FormData();
+    formData.append('file', '');
+    formData.append('object', path);
+    formData.append('bucket', flatParent.object.bucket);
+    formData.append('endpoint', this.endpoint);
+    this.bucketBrowserService.uploadFile(formData)
+      .subscribe(() => {
+          this.bucketDataService.refreshBucketdata(flatParent.object.bucket, this.endpoint);
+          this.toastr.success('Folder successfully created!', 'Success!');
+          this.resetForm();
+          this.folderFormControl.updateValueAndValidity();
+          this.folderFormControl.markAsPristine();
+          this.folderCreating = false;
+        }, error => {
+          this.toastr.error(error.message || 'Folder creation error!', 'Oops!');
+          this.folderCreating = false;
+        }
+      );
   }
 
-  private resetForm(){
+  private resetForm() {
     this.folderFormControl.setValue('');
     this.folderFormControl.updateValueAndValidity();
     this.folderFormControl.markAsPristine();
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
index c87d12c..0da7bd4 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
@@ -31,6 +31,7 @@ import {BucketBrowserComponent} from './bucket-browser/bucket-browser.component'
 import {FolderTreeComponent} from './bucket-browser/folder-tree/folder-tree.component';
 import {MatTreeModule} from '@angular/material/tree';
 import {BucketDataService} from './bucket-browser/bucket-data.service';
+import {BucketConfirmationDialogComponent} from './bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component';
 
 
 @NgModule({
@@ -48,9 +49,10 @@ import {BucketDataService} from './bucket-browser/bucket-data.service';
     ManageUngitComponent,
     ConfirmDeleteAccountDialog,
     BucketBrowserComponent,
-    FolderTreeComponent
+    FolderTreeComponent,
+    BucketConfirmationDialogComponent
   ],
-  entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialog, BucketBrowserComponent, FolderTreeComponent],
+  entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialog, BucketBrowserComponent, FolderTreeComponent, BucketConfirmationDialogComponent],
   providers: [BucketDataService],
   exports: [ResourcesComponent]
 })


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org