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

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-ui.git

commit 2ab3c4af4b57927253f62914138c9cc74a8fccee
Author: Alex Heneveld <[email protected]>
AuthorDate: Fri Jun 21 10:31:39 2024 +0100

    merge iuliana's uploader enhancements, adding a couple things
    
    - updates catalog after save
    - shows like a modal rather than a full screen
    - show failure more clearly
---
 .../catalog/app/views/catalog/catalog.state.js     | 14 +++-
 .../utils/catalog-uploader/catalog-uploader.html   | 94 +++++++++++++--------
 .../utils/catalog-uploader/catalog-uploader.js     | 24 ++++--
 .../utils/catalog-uploader/catalog-uploader.less   | 95 +++++++++++++++-------
 4 files changed, 150 insertions(+), 77 deletions(-)

diff --git a/ui-modules/catalog/app/views/catalog/catalog.state.js 
b/ui-modules/catalog/app/views/catalog/catalog.state.js
index 64534312..1d931f0e 100644
--- a/ui-modules/catalog/app/views/catalog/catalog.state.js
+++ b/ui-modules/catalog/app/views/catalog/catalog.state.js
@@ -24,6 +24,7 @@ import brUtils from 'brooklyn-ui-utils/utils/general';
 import template from './catalog.template.html';
 import {analyzeDescription} from 'brooklyn-ui-utils/md-helper';
 import {HIDE_INTERSTITIAL_SPINNER_EVENT} from 
'brooklyn-ui-utils/interstitial-spinner/interstitial-spinner';
+import {CATALOG_UPLOAD_COMPLETED} from 
'brooklyn-ui-utils/catalog-uploader/catalog-uploader';
 
 const MODULE_NAME = 'catalog.state';
 
@@ -136,10 +137,15 @@ export function catalogController($scope, $rootScope, 
catalogApi, brUtilsGeneral
         $rootScope.$broadcast('open-catalog-uploader');
     };
 
-    catalogApi.getBundles({params: {detail: true}}).then(data => {
-        $scope.$emit(HIDE_INTERSTITIAL_SPINNER_EVENT);
-
-        processBundles(data);
+    function getBundles(initialLoad) {
+        catalogApi.getBundles({params: {detail: true}}).then(data => {
+            if (initialLoad) $scope.$emit(HIDE_INTERSTITIAL_SPINNER_EVENT);
+            processBundles(data);
+        });
+    }
+    getBundles(true);
+    $scope.$on(CATALOG_UPLOAD_COMPLETED, ()=>{
+        getBundles(false);
     });
 
     function processBundles(bundles) {
diff --git a/ui-modules/utils/catalog-uploader/catalog-uploader.html 
b/ui-modules/utils/catalog-uploader/catalog-uploader.html
index bc6b9d4f..0cb633e1 100644
--- a/ui-modules/utils/catalog-uploader/catalog-uploader.html
+++ b/ui-modules/utils/catalog-uploader/catalog-uploader.html
@@ -17,46 +17,70 @@
   under the License.
 -->
 <div class="brooklyn-catalog-uploader">
-    <i class="fa fa-times target-close pull-right" ng-click="close()"></i>
-    <div class="container">
-        <div class="row">
-            <div class="col-sm-8 text-center">
-                <p><i class="fa fa-3x fa-upload"></i></p>
-                <p>
-                    <input type="file" name="files" id="files" multiple 
custom-on-change="filesChanged" />
-                    <label for="files" ng-click="choose()"><strong>Choose 
files</strong><span class="drag-upload"> or drag & drop them 
here</span>.</label>
-                </p>
-            </div>
+  <div class="brooklyn-catalog-uploader-screen">
+  </div>
+  <div class="brooklyn-catalog-uploader-inner">
+    <div class="pull-right" style="position: absolute; right: 10px; top: 8px;">
+        <i class="fa fa-times target-close" ng-click="close()"></i>
+    </div>
+    <div style="display: flex; flex-direction: column; height: 100%; width: 
100%;">
+
+        <div style="flex: 0 1 auto; height: 60px;"></div>
+
+        <div class="text-center">
+            <p><i class="fa fa-3x fa-upload"></i></p>
+            <p>
+                <input type="file" name="files" id="files" multiple 
custom-on-change="filesChanged" />
+                <label for="files" ng-click="choose()"><strong>Choose 
files</strong><span class="drag-upload"> or drag & drop them 
here</span>.</label>
+            </p>
         </div>
 
-        <div class="row">
-            <div class="col-sm-8 text-muted upload-item" 
ng-repeat="selectedFile in selectedFiles" ng-init="isDetailsCollapsed = true">
-                <div class="progress">
-                    <div class="progress-bar progress-bar-striped" 
role="progressbar" ng-class="{'active': !selectedFile.error && 
!selectedFile.result, 'progress-bar-danger': selectedFile.error, 
'progress-bar-success': selectedFile.result}" style="width: 100%">
-                        <span ng-if="!selectedFile.result && 
!selectedFile.error">Importing</span> {{selectedFile.name}}
-                    </div>
-                </div>
-                <p ng-click="isDetailsCollapsed = !isDetailsCollapsed" 
class="upload-item-details-link"><i class="fa fa-info-circle"></i> 
{{isDetailsCollapsed ? 'View' : 'hide'}} details</p>
-                <div uib-collapse="isDetailsCollapsed" 
class="upload-item-details">
-                    <p ng-if="selectedFile.result">Successfully imported the 
following items:
-                        <ul class="fa-ul">
-                            <li ng-repeat="(key, item) in selectedFile.result 
track by key">
-                                <i class="fa-li fa fa-check-square"></i>
-                                <a ng-href="{{getCatalogItemUrl(item)}}" 
target="_blank">{{item.itemType}} {{item.symbolicName}}:{{item.version}}</a>
-                            </li>
-                        </ul>
-                    </p>
-                    <p ng-if="selectedFile.error">{{selectedFile.error}}</p>
+        <div style="flex: 0 1 auto; height: 15px;"></div>
+
+        <div class="upload-item" ng-repeat="selectedFile in selectedFiles" 
ng-init="isDetailsCollapsed = true">
+            <div class="progress">
+                <div class="progress-bar" role="progressbar" ng-class="{
+                        'active': !selectedFile.error && !selectedFile.result,
+                        'progress-bar-striped': !selectedFile.error && 
!selectedFile.result,
+                        'progress-bar-danger': selectedFile.error, 
'progress-bar-success': selectedFile.result}" style="width: 100%">
+                    <span ng-if="!selectedFile.result && 
!selectedFile.error">Importing</span> {{selectedFile.name}}
                 </div>
             </div>
-        </div>
-        <div class="row">
-            <div class="col-sm-8 text-left">
-                <button class="btn btn-success" ng-click="close()">
-                    <i class="fa fa-fw fa-arrow-circle-left"></i>
-                    Return to catalog
-                </button>
+
+            <span class="upload-item-details-link" 
ng-click="isDetailsCollapsed = !isDetailsCollapsed">
+                <span ng-if="selectedFile.error" style="color: darkred;">
+                    <i class="fa fa-times"></i> Upload failed.
+                </span>
+                <span ng-if="!selectedFile.error && selectedFile.result">
+                    <i class="fa fa-check"></i> <b>Completed.</b>
+                </span>
+                <span ng-if="selectedFile.error || selectedFile.result">
+                      Click {{isDetailsCollapsed ? 'for' : 'to hide'}} 
{{selectedFile.error ? ' error ' : ''}} details.
+                </span>
+            </span>
+
+            <div uib-collapse="isDetailsCollapsed" class="upload-item-details">
+                <p ng-if="selectedFile.result">Successfully imported the 
following items:
+                    <ul class="fa-ul">
+                        <li ng-repeat="(key, item) in selectedFile.result 
track by key">
+                            <i class="fa-li fa fa-check-square"></i>
+                            <a ng-href="{{getCatalogItemUrl(item)}}" 
target="_blank">{{item.itemType}} {{item.symbolicName}}:{{item.version}}</a>
+                        </li>
+                    </ul>
+                </p>
+                <p ng-if="selectedFile.error">{{selectedFile.error}}</p>
             </div>
         </div>
+
+        <div style="flex: 1 1 auto; height: 15px;"></div>
+
+        <div class="text-right" style="flex: 0 0 auto;">
+            <button class="btn btn-success" ng-click="close()">
+                <i class="fa fa-fw fa-arrow-circle-left"></i>
+                Return to catalog
+            </button>
+        </div>
+        <div style="flex: 0 0 auto; height: 10px;"></div>
     </div>
+  </div>
 </div>
diff --git a/ui-modules/utils/catalog-uploader/catalog-uploader.js 
b/ui-modules/utils/catalog-uploader/catalog-uploader.js
index d16f7716..0beed85b 100644
--- a/ui-modules/utils/catalog-uploader/catalog-uploader.js
+++ b/ui-modules/utils/catalog-uploader/catalog-uploader.js
@@ -34,10 +34,12 @@ const MODULE_NAME = 'brooklyn.components.catalog-uploader';
 angular.module(MODULE_NAME, [catalogApi])
     .service('brooklynCatalogUploader', ['$q', 'catalogApi', 
catalogUploaderService])
     .directive('customOnChange', customOnChangeDirective)
-    .directive('brooklynCatalogUploader', ['$compile', 
'brooklynCatalogUploader', catalogUploaderDirective]);
+    .directive('brooklynCatalogUploader', ['$compile', '$rootScope', 
'brooklynCatalogUploader', catalogUploaderDirective]);
 
 export default MODULE_NAME;
 
+export const CATALOG_UPLOAD_COMPLETED = "brooklyn-catalog-upload-completed";
+
 /**
  * @ngdoc directive
  * @name brooklynCatalogUploader
@@ -52,7 +54,7 @@ export default MODULE_NAME;
  * @param {string} brooklynCatalogUploader The value can be empty. Otherwise, 
the directive will listen for any event broadcasted
  * with this name and will trigger the overlay upon reception.
  */
-export function catalogUploaderDirective($compile, brooklynCatalogUploader) {
+export function catalogUploaderDirective($compile, $rootScope, 
brooklynCatalogUploader) {
     return {
         restrict: 'A',
         link: link
@@ -67,27 +69,32 @@ export function catalogUploaderDirective($compile, 
brooklynCatalogUploader) {
         element.append($compile(template)(scope));
 
         let counter = 0;
+        let requireManualClose = false;
         element.bind('drag dragstart dragend dragover dragenter dragleave 
drop', (event)=> {
             event.preventDefault();
             event.stopPropagation();
         }).bind('drag dragstart dragover dragenter', (event)=> {
             event.dataTransfer.dropEffect = 'copy';
             element.addClass('br-drag-active');
+            element.addClass('br-drag-active-2');
         }).bind('dragenter', ()=> {
             counter++;
         }).bind('dragleave', (event)=> {
             counter--;
-            if (counter === 0) {
-                element.removeClass('br-drag-active');
-            }
+            element.removeClass('br-drag-active-2');
+            if (!requireManualClose && counter === 0) 
element.removeClass('br-drag-active'); // close if we were triggered by a drag
         }).bind('drop', (event)=> {
             scope.upload(event.dataTransfer.files);
+            counter--;
+            element.removeClass('br-drag-active-2');
+            requireManualClose = true;
+            if (!requireManualClose && counter === 0) 
element.removeClass('br-drag-active'); // close if we were triggered by a drag
         });
 
         let field = attrs.brooklynCatalogUploader;
         if (angular.isDefined(field)) {
             scope.$on(field, ()=> {
-                counter++;
+                requireManualClose = true;
                 element.addClass('br-drag-active');
             });
         }
@@ -95,7 +102,8 @@ export function catalogUploaderDirective($compile, 
brooklynCatalogUploader) {
         scope.selectedFiles = [];
 
         scope.close = ()=> {
-            counter--;
+            requireManualClose = false;
+            counter = 0;
             scope.selectedFiles = []; // clean up the imported file list on 
returning to catalog, still needs a manual refresh to show the imported bundle
             element.removeClass('br-drag-active');
         };
@@ -110,7 +118,9 @@ export function catalogUploaderDirective($compile, 
brooklynCatalogUploader) {
 
                 brooklynCatalogUploader.upload(file).then((data)=> {
                     file.result = data;
+                    $rootScope.$broadcast(CATALOG_UPLOAD_COMPLETED);
                 }).catch((error)=> {
+                    console.warn("ERROR uploading "+file, error);
                     file.error = error;
                 }).finally(()=> {
                     scope.$applyAsync();
diff --git a/ui-modules/utils/catalog-uploader/catalog-uploader.less 
b/ui-modules/utils/catalog-uploader/catalog-uploader.less
index b2e174c2..7cc106d5 100644
--- a/ui-modules/utils/catalog-uploader/catalog-uploader.less
+++ b/ui-modules/utils/catalog-uploader/catalog-uploader.less
@@ -17,50 +17,77 @@
  * under the License.
  */
 .brooklyn-catalog-uploader {
-  padding: 50px;
-  z-index: 100;
   visibility: hidden;
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background-color: white;
-  overflow-y: scroll;
-  transition: all 0.3s ease;
   opacity: 0;
 
-  .upload-box {
-    background: @gray-lighter;
-    border: 5px @gray-light dashed;
-  }
+  z-index: 100;
+  width: 100%;
+  height: 100%;
 
-  .target-close {
-    cursor: pointer;
+  &, .brooklyn-catalog-uploader-screen, .brooklyn-catalog-uploader-inner {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
   }
 
-  input[type=file] {
-    display: none;
+  .brooklyn-catalog-uploader-screen {
+    width: 100%;
+    height: 100%;
+    background-color: black;
+    opacity: 60%;
+    z-index: 50;
   }
+  .brooklyn-catalog-uploader-inner {
+    padding: 50px;
+    z-index: 100;
+    width: 70%;
+    height: 60%;
+    margin: auto;
+    margin-top: 10%;
 
-  label > strong {
-    text-decoration: underline;
-    cursor: pointer;
-    color: @brand-primary;
-  }
+    border: 3px @brand-primary solid;
+    border-radius: 6px;
+
+    background-color: white;
+    overflow-y: scroll;
+    opacity: 1;
 
-  .upload-item {
-    margin-bottom: 1em;
+    .upload-box {
+      background: @gray-lighter;
+      border: 5px @gray-light dashed;
+    }
 
-    .progress {
-      margin-bottom: 0;
+    .target-close {
+      cursor: pointer;
+    }
 
-      .progress-bar {
-        transition: background-color 0.3s ease;
-      }
+    input[type=file] {
+      display: none;
     }
-    .upload-item-details-link {
+
+    label > strong {
+      text-decoration: underline;
       cursor: pointer;
+      color: @brand-primary;
+    }
+
+    .upload-item {
+      margin-bottom: 1em;
+      color: @gray-light;
+
+      .progress {
+        margin-bottom: 0;
+
+        .progress-bar {
+          transition: background-color 0.3s ease;
+        }
+      }
+
+      .upload-item-details-link {
+        cursor: pointer;
+      }
     }
   }
 }
@@ -68,5 +95,11 @@
 .br-drag-active .brooklyn-catalog-uploader {
   visibility: visible;
   opacity: 1;
+  transition: 0.3s ease;
 }
 
+.br-drag-active-2 .brooklyn-catalog-uploader {
+  .brooklyn-catalog-uploader-inner {
+    border: 3px @brand-primary dashed !important;
+  }
+}

Reply via email to