[PATCH] [Kimchi] [RFC] Storave Volume management UI

From: Samuel Guimarães <sguimaraes943@gmail.com> This patch adds the UI for Storage Volume management action button, filter input, gallery view and gallery / list view toggle-switch button with multi-selection enabled. This is just the initial patch for the UI to get feedback, will add Storage Volume modal and confirm windows and map create, delete, resize, wipe and clone actions in the next commit. Samuel Guimarães (1): Storave Volume management UI ui/css/kimchi.css | 266 +++++++++++++++++++++++++++++------- ui/css/src/modules/_storage.scss | 287 +++++++++++++++++++++++++++++++++------ ui/js/src/kimchi.storage_main.js | 82 +++++------ ui/pages/tabs/storage.html.tmpl | 121 +++++++++++------ 4 files changed, 576 insertions(+), 180 deletions(-) -- 1.9.3

From: Samuel Guimarães <sguimaraes943@gmail.com> This commit adds the UI for Storage Volume management action button, filter input, gallery view and gallery / list view toggleswitch button with multi-selection enabled. Signed-off-by: Samuel Guimarães <sguimaraes943@gmail.com> --- ui/css/kimchi.css | 266 +++++++++++++++++++++++++++++------- ui/css/src/modules/_storage.scss | 287 +++++++++++++++++++++++++++++++++------ ui/js/src/kimchi.storage_main.js | 82 +++++------ ui/pages/tabs/storage.html.tmpl | 121 +++++++++++------ 4 files changed, 576 insertions(+), 180 deletions(-) diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css index 49ea39a..728f816 100644 --- a/ui/css/kimchi.css +++ b/ui/css/kimchi.css @@ -2183,6 +2183,8 @@ body.wok-gallery { } #storage-root-container .wok-datagrid-body .handle { + -webkit-user-select: none; + -moz-user-select: none; user-select: none; position: relative; } @@ -2226,69 +2228,38 @@ body.wok-gallery { } #storage-root-container .volumes .volumeslist { - padding: 11px; + padding: 22px; max-height: 285px; min-height: 136px; + max-height: 505px; overflow: auto; } -#storage-root-container .volumes .volume-box { - background: #fff; - padding: 4px 20px; - margin: 11px; - display: inline-block; - width: 409px; - height: 110px; +#storage-root-container .volumes .volumeslist .row { + font-size: 0; + margin-bottom: 22px; } -#storage-root-container .volumes .volume-title { - height: 46px; - width: 100%; - border-bottom: 1px solid #ccc; - position: relative; +#storage-root-container .volumes .volumeslist .filter { + width: 514px; } -#storage-root-container .volumes .volume-name { - font-size: 15pt; - font-weight: 300; - width: 274px; - line-height: 46px; +#storage-root-container .volumes .pool-action { display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -#storage-root-container .volumes .volume-utilization { - vertical-align: top; - text-align: right; - display: inline-block; - width: 90px; - height: 46px; - line-height: 46px; + margin-right: 20px; } #storage-root-container .volumes .volume-icon { display: inline-block; + vertical-align: middle; width: 27px; - height: 46px; - line-height: 46px; + height: 27px; + line-height: 27px; background-repeat: no-repeat; background-position: 50%; background-color: transparent; } -#storage-root-container .volumes .volume-usage { - vertical-align: top; - font-size: 15pt; - font-weight: 400; - display: inline-block; - text-align: right; - line-height: 46px; - padding-left: 0; - margin-left: 5px; -} - #storage-root-container .volumes .volume-icon.icon-high { background-image: url("/images/theme-default/high.png"); } @@ -2304,13 +2275,13 @@ body.wok-gallery { #storage-root-container .volumes .volume-progress { position: absolute; margin: 0; - width: 409px; - top: -4px; - left: -20px; + width: 407px; + top: 4px; + left: 4px; } #storage-root-container .volumes .volume-progress .progress-bar-outer { - background: transparent; + background: #ddd; height: 6px; overflow: hidden; width: 100%; @@ -2351,6 +2322,207 @@ body.wok-gallery { line-height: 136px; } +#storage-root-container .volumes .wok-gallery .volume-inline-progress { + display: none; +} + +#storage-root-container .volumes .wok-gallery.wok-datagrid { + background: transparent; + margin-top: -12px; +} + +#storage-root-container .volumes .wok-gallery.wok-datagrid > .wok-datagrid-header { + display: none; +} + +#storage-root-container .volumes .wok-gallery.wok-datagrid > .wok-datagrid-body { + margin-left: -3px; +} + +#storage-root-container .volumes .wok-gallery.wok-datagrid .wok-datagrid-body > .wok-datagrid-row { + position: relative; + cursor: pointer; + background: #fff !important; + border: 0 !important; + display: inline-block !important; + width: 415px !important; + margin: 12px 4px 0; +} + +#storage-root-container .volumes .wok-gallery.wok-datagrid .wok-datagrid-body > .wok-datagrid-row, #storage-root-container .volumes .wok-gallery.wok-datagrid .wok-datagrid-body > .wok-datagrid-row * { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +#storage-root-container .volumes .wok-gallery.wok-datagrid .wok-datagrid-body > .wok-datagrid-row.selected .volume-box-border { + border-color: #8cc63f; +} + +#storage-root-container .volumes .wok-gallery.wok-datagrid .wok-datagrid-body > .wok-datagrid-row.selected .volume-box-inner { + border-color: #000; +} + +#storage-root-container .volumes .wok-gallery .volume-box-outer { + border-radius: 3px; + overflow: hidden; +} + +#storage-root-container .volumes .wok-gallery .volume-box-border { + display: block; + border: 3px solid transparent; + transition: border-color .1s ease-in-out; +} + +#storage-root-container .volumes .wok-gallery .volume-box-inner { + padding: 0 16px; + width: 409px; + height: 110px; + display: flex; + flex-flow: row wrap; + justify-content: space-around; + border: 1px solid #fff; + background: #fff; + transition: border-color .1s ease-in-out; +} + +#storage-root-container .volumes .wok-gallery span.column-name, +#storage-root-container .volumes .wok-gallery span.column-used { + height: 52px; + line-height: 52px; + border-bottom: 1px solid #ccc; +} + +#storage-root-container .volumes .wok-gallery span.column-name { + width: 303px; + order: 1; +} + +#storage-root-container .volumes .wok-gallery span.column-used { + width: 72px; + order: 2; +} + +#storage-root-container .volumes .wok-gallery span.column-name label.volume-name { + width: 100%; + height: 52px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: block; + margin: 0; + padding-right: 6px; +} + +#storage-root-container .volumes .wok-gallery span.column-allocated, +#storage-root-container .volumes .wok-gallery span.column-capacity, +#storage-root-container .volumes .wok-gallery span.column-format, +#storage-root-container .volumes .wok-gallery span.column-type { + order: 3; + width: 72px; + margin-top: -5px; +} + +#storage-root-container .volumes .wok-gallery span.gallery-header { + font-weight: 600; + display: block; +} + +#storage-root-container .volumes .wok-list span.gallery-header { + display: none; +} + +#storage-root-container .volumes .wok-list .volume-inline-progress { + vertical-align: middle; + display: inline-block; +} + +#storage-root-container .volumes .wok-list .volume-inline-progress > span.wok-loading-icon { + margin-right: 0; +} + +#storage-root-container .volumes .wok-list .volume-progress { + width: 100%; + top: 0px; + left: 0px; +} + +#storage-root-container .volumes .wok-list .volume-progress .progress-bar-outer { + height: 3px; +} + +#storage-root-container .volumes .wok-list .volume-box-inner { + font-size: 0; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header > span.column-name { + padding-left: 41px; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row { + position: relative; + cursor: pointer; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row.selected { + background: #ddd !important; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div > span { + font-family: "Open Sans", Helvetica, Arial, "Lucida Grande", sans-serif; + line-height: 2.42857; + vertical-align: top; + font-size: 12.5pt; + font-weight: 400; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div > span.column-name { + padding-left: 15px; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header > span, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div > span { + display: inline-block; + vertical-align: middle; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header span.column-format, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div span.column-format { + width: 173px; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header span.column-used, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header span.column-type, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header span.column-capacity, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header span.column-allocated, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div span.column-used, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div span.column-type, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div span.column-capacity, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div span.column-allocated { + width: 165px; + text-transform: capitalize; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header > span.column-name, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div > span.column-name { + width: 444px; +} + +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-header > span.column-name label, +#storage-root-container .volumes .wok-list.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div > span.column-name label { + padding: 0 5px 0 0; + margin-right: 0; + margin-bottom: 0; + font-size: inherit; + font-weight: inherit; + width: 100%; + display: block; + height: auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + #storage-root-container .wok-datagrid > .wok-datagrid-body > .wok-datagrid-row[data-stat="inactive"] { color: #999 !important; } diff --git a/ui/css/src/modules/_storage.scss b/ui/css/src/modules/_storage.scss index 4a9f9e1..23fbb3c 100644 --- a/ui/css/src/modules/_storage.scss +++ b/ui/css/src/modules/_storage.scss @@ -25,6 +25,8 @@ position: relative; } .handle { + -webkit-user-select: none; + -moz-user-select: none; user-select: none; position: relative; } @@ -58,66 +60,44 @@ width: 100%; background: $navbar-default-toggle-hover-bg; display: none; + > .footer { z-index: 100; } + .volumeslist { - padding: 11px; + padding: 22px; max-height: 285px; min-height: 136px; + max-height: 505px; overflow: auto; } - .volume-box { - background: $navbar-inverse-toggle-icon-bar-bg; - padding: 4px 20px; - margin: 11px; - display: inline-block; - width: 409px; - height: 110px; - } - .volume-title { - height: 46px; - width: 100%; - border-bottom: 1px solid $input-border; - position: relative; + + .volumeslist .row { + font-size: 0; + margin-bottom: 22px; } - .volume-name { - font-size: 15pt; - font-weight: 300; - width: 274px; - line-height: 46px; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + + .volumeslist .filter { + width: 514px; } - .volume-utilization { - vertical-align: top; - text-align: right; + + .pool-action { display: inline-block; - width: 90px; - height: 46px; - line-height: 46px; + margin-right: 20px; } + .volume-icon { display: inline-block; + vertical-align: middle; width: 27px; - height: 46px; - line-height: 46px; + height: 27px; + line-height: 27px; background-repeat: no-repeat; background-position: 50%; background-color: transparent; } - .volume-usage { - vertical-align: top; - font-size: 15pt; - font-weight: 400; - display: inline-block; - text-align: right; - line-height: 46px; - padding-left: 0; - margin-left: 5px; - } + .volume-icon.icon-high { background-image: url('#{$wok-icon-path}/high.png'); } @@ -127,14 +107,16 @@ .volume-icon.icon-low { background-image: url('#{$wok-icon-path}/low.png'); } + .volume-progress { position: absolute; margin: 0; - width: 409px; - top: -4px; - left: -20px; + width: 407px; + top: 4px; + left: 4px; + .progress-bar-outer { - background: transparent; + background: $table-bg-hover; height: 6px; overflow: hidden; width: 100%; @@ -145,6 +127,7 @@ width: 100%; } } + .volume-data { margin: 0; padding: 0; @@ -168,6 +151,220 @@ text-align: center; line-height: 136px; } + + .wok-gallery { + + .volume-inline-progress { + display: none; + } + + &.wok-datagrid { + background: transparent; + margin-top: -12px; + } + + &.wok-datagrid > .wok-datagrid-header { + display: none; + } + + &.wok-datagrid > .wok-datagrid-body { + margin-left: -3px; + } + + &.wok-datagrid .wok-datagrid-body > .wok-datagrid-row { + position: relative; + cursor: pointer; + background: $body-bg !important; + border: 0 !important; + display: inline-block !important; + width: 415px !important; + margin: 12px 4px 0; + &, * { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + } + } + + &.wok-datagrid .wok-datagrid-body > .wok-datagrid-row.selected .volume-box-border { + border-color: $guests-color; + } + + &.wok-datagrid .wok-datagrid-body > .wok-datagrid-row.selected .volume-box-inner { + border-color: $gray-base; + } + + .volume-box-outer { + border-radius: 3px; + overflow: hidden; + } + + .volume-box-border { + display: block; + border: 3px solid transparent; + transition: border-color .1s ease-in-out + } + + .volume-box-inner { + padding: 0 16px; + width: 409px; + height: 110px; + display: flex; + flex-flow: row wrap; + justify-content: space-around; + border: 1px solid #fff; + background: #fff; + transition: border-color .1s ease-in-out; + } + + span.column-name, + span.column-used { + height: 52px; + line-height: 52px; + border-bottom: 1px solid $input-border; + } + + span.column-name { + width: 303px; + order: 1; + } + + span.column-used { + width: 72px; + order: 2; + } + + span.column-name label.volume-name { + width: 100%; + height: 52px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: block; + margin: 0; + padding-right: 6px; + } + + span.column-allocated, + span.column-capacity, + span.column-format, + span.column-type { + order: 3; + width: 72px; + margin-top: -5px; + } + + span.gallery-header { + font-weight: 600; + display: block; + } + + } + + .wok-list { + + span.gallery-header { + display: none; + } + + .volume-inline-progress { + vertical-align: middle; + display: inline-block; + } + + .volume-inline-progress > span.wok-loading-icon { + margin-right: 0; + } + + .volume-progress { + width: 100%; + top: 0px; + left: 0px; + + .progress-bar-outer { + height: 3px; + } + } + + .volume-box-inner { + font-size: 0; + } + + &.wok-datagrid > .wok-datagrid-header { + + > span.column-name { + padding-left: 41px; + } + + } + + &.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row { + position: relative; + cursor: pointer; + } + + &.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row.selected { + background: $table-bg-hover !important; + } + + &.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div { + + > span { + font-family: $font-family-sans-serif; + line-height: (1 + $line-height-base); + vertical-align: top; + font-size: 12.5pt; + font-weight: 400; + } + + > span.column-name { + padding-left: 15px; + } + + } + + &.wok-datagrid > .wok-datagrid-header, + &.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > div > div > div { + + >span { + display: inline-block; + vertical-align: middle; + } + + span.column-format { + width: 173px; + } + + span.column-used, + span.column-type, + span.column-capacity, + span.column-allocated { + width: 165px; + text-transform: capitalize; + } + + > span.column-name { + width: 444px; + + label { + padding: 0 5px 0 0; + margin-right: 0; + margin-bottom: 0; + font-size: inherit; + font-weight: inherit; + width: 100%; + display: block; + height: auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + + } + + } .wok-datagrid > .wok-datagrid-body > .wok-datagrid-row[data-stat="inactive"] { color: $gray-light !important; diff --git a/ui/js/src/kimchi.storage_main.js b/ui/js/src/kimchi.storage_main.js index 5312388..1d1781f 100644 --- a/ui/js/src/kimchi.storage_main.js +++ b/ui/js/src/kimchi.storage_main.js @@ -65,6 +65,34 @@ kimchi.doListStoragePools = function() { kimchi.storageBindClick = function() { + $('.volumes').on('click','.toggle-gallery',function(e){ + e.preventDefault(); + e.stopPropagation(); + var button = $(this); + var volumeBlock = $(this).parent().parent().parent(); + var text = $('span.text', button).text(); + $(".wok-list, .wok-gallery",volumeBlock).toggleClass("wok-list wok-gallery"); + $('span.text', button).text(text == i18n['KCHTMPL6005M'] ? i18n['KCHTMPL6004M'] : i18n['KCHTMPL6005M']); + }); + + $('.volumes').on('click','.wok-datagrid-row',function(e){ + if (!$(e.target).is("input[type='checkbox']") && !$(e.target).is("label")) { + var volumeBlock = $(this); + var checkbox = volumeBlock.find('[name="selected-volume[]"]'); + checkbox.trigger('click'); + } + }); + + $('.volumes').on('change','[name="selected-volume[]"]',function(e){ + var checkbox = $(this); + var volumeBlock = $(this).parent().parent().parent().parent().parent(); + if(checkbox.is(":checked")){ + volumeBlock.addClass('selected'); + }else { + volumeBlock.removeClass('selected'); + } + }); + $('.inactive').each(function(index) { if ('active' === $(this).data('state')) { $(this).hide(); @@ -229,7 +257,8 @@ kimchi._generateVolumeHTML = function(volume) { if(volume['type'] === 'kimchi-iso') { return ''; } - var volumeHtml = $('#volumeTmpl').html(); + var volumeHtml = $('#volumeTmpl2').html(); + volume.checkbox = volume.name.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi,'-'), volume.capacityLevel = Math.round(volume.allocation / volume.capacity * 100) || 0; if (volume.capacityLevel > 100) { volume.capacityIcon = 'icon-high'; @@ -273,7 +302,7 @@ kimchi.doListVolumes = function(poolObj) { return result; }; - var volumeDiv = $('#volume' + poolName); + var volumeDiv = $('#volume' + poolName + ' > .wok-datagrid > .wok-datagrid-body'); $(volumeDiv).empty(); var slide = $('.volumes', poolObj); var handleArrow = $('.arrow-down', poolObj); @@ -312,7 +341,7 @@ kimchi.doListVolumes = function(poolObj) { if (listHtml.length > 0) { volumeDiv.html(listHtml); } else { - volumeDiv.html("<div class='pool-empty'>" + i18n['KCHPOOL6002M'] + "</div>"); + volumeDiv.parent().parent().parent().html("<div class='pool-empty'>" + i18n['KCHPOOL6002M'] + "</div>"); } $.each(ongoingVolumesMap, function(volumeName, task) { @@ -329,53 +358,6 @@ kimchi.doListVolumes = function(poolObj) { kimchi.initLogicalPoolExtend = function() { - // $("#logicalPoolExtend").dialog({ - // autoOpen : false, - // modal : true, - // width : 600, - // resizable : false, - // closeText: "X", - // open : function(){ - // $('#loading-info', '#logicalPoolExtend').removeClass('hidden'); - // $(".ui-dialog-titlebar-close", $("#logicalPoolExtend").parent()).removeAttr("title"); - // kimchi.listHostPartitions(function(data) { - // $('#loading-info', '#logicalPoolExtend').addClass('hidden'); - // if (data.length > 0) { - // for(var i=0;i<data.length;i++){ - // if (data[i].type === 'part' || data[i].type === 'disk') { - // $('.host-partition', '#logicalPoolExtend').append(wok.substitute($('#logicalPoolExtendTmpl').html(), data[i])); - // } - // } - // } else { - // $('.host-partition').html(i18n['KCHPOOL6011M']); - // $('.host-partition').addClass('text-help'); - // } - // }, function(err) { - // $('#loading-info', '#logicalPoolExtend').addClass('hidden'); - // $('.host-partition').html(i18n['KCHPOOL6013M'] + '<br/>(' + err.responseJSON.reason + ')'); - // $('.host-partition').addClass('text-help'); - // }); - // }, - // beforeClose : function() { $('.host-partition', '#logicalPoolExtend').empty(); }, - // buttons : [{ - // class: "ui-button-primary", - // text: i18n.KCHAPI6007M, - // click: function(){ - // var devicePaths = []; - // $("input[type='checkbox']:checked", "#logicalPoolExtend").each(function(){ - // devicePaths.push($(this).prop('value')); - // }) - // kimchi.updateStoragePool($("#logicalPoolExtend").dialog("option", "poolName"),{disks: devicePaths},function(data){ - // var item = $("#"+$("#logicalPoolExtend").dialog("option", "poolName")); - // $(".usage", $(".storage-name", item)).text((Math.round(data.allocated/data.capacity*100)||0)+"%"); - // $(".storage-text", $(".storage-capacity", item)).text(wok.changetoProperUnit(data.capacity,1)); - // $(".storage-text", $(".storage-allocate", item)).text(wok.changetoProperUnit(data.allocated,1)); - // }); - // $(this).dialog("close"); - // } - // }] - // }); - $('#logicalPoolExtend').on('hidden.bs.modal', function () { $('.host-partition', '#logicalPoolExtend').empty(); }); diff --git a/ui/pages/tabs/storage.html.tmpl b/ui/pages/tabs/storage.html.tmpl index fa51e48..8302730 100644 --- a/ui/pages/tabs/storage.html.tmpl +++ b/ui/pages/tabs/storage.html.tmpl @@ -102,50 +102,67 @@ </div> </div> <script id="storageTmpl" type="html/text"> - <div id="{name}" class="storage-li in" data-name="{name}" data-stat="{state}"> - <span class='column-state' val="{state}"> - <span class='storage-state {state}'> - <i class="fa fa-power-off"></i> - </span> - </span><!-- - --><span class='column-name' title="{name}" val="{name}">{name}</span><!-- - --><span class='column-type' val="{type}">{type}</span><!-- - --><span class='column-location' val="{path}">{path}</span><!-- - --><span class='column-usage {state}' val="{usage}" ><span class='usage-icon {icon}'>{usage}</span>%</span><!-- - --><span class='column-allocated' val="{allocated}">{allocated}</span><!-- - --><span class='column-capacity' val="{capacity}">{capacity}</span><!-- - --><span class="column-disks {state}"> - <div class="handle arrow-down"></div> - </span><!-- - --><span class="column-action storage-button" style="display:none"> - <span class="pull-right"> +<div id="{name}" class="storage-li in" data-name="{name}" data-stat="{state}"> + <span class='column-state' val="{state}"><span class='storage-state {state}'><i class="fa fa-power-off"></i></span></span><!-- + --><span class='column-name' title="{name}" val="{name}">{name}</span><!-- + --><span class='column-type' val="{type}">{type}</span><!-- + --><span class='column-location' val="{path}">{path}</span><!-- + --><span class='column-usage {state}' val="{usage}" ><span class='usage-icon {icon}'>{usage}</span>%</span><!-- + --><span class='column-allocated' val="{allocated}">{allocated}</span><!-- + --><span class='column-capacity' val="{capacity}">{capacity}</span><!-- + --><span class="column-disks {state}"><div class="handle arrow-down"></div></span><!-- + --><span class="column-action storage-button" style="display:none"> + <span class="pull-right"> <div class="dropdown menu-flat storage-action" data-state="{state}" data-type="{type}" data-name="{name}"> <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false" aria-haspopup="true"><span class="edit-alt"></span>$_("Actions")<span class="caret"></span></button> <ul class="dropdown-menu actionsheet"> - <li role="presentation"> - <a href="#" class="pool-deactivate" data-inuse="{in_use}" data-stat="{state}" data-name="{name}" data-persistent="{persistent}" href="#"><i class="fa fa-minus-circle"></i>$_("Deactivate")</a> - </li> - <li role="presentation"> - <a href="#" class="pool-activate" data-stat="{state}" data-name="{name}"><i class="fa fa-power-off"></i>$_("Activate")</a> - </li> - <li role="presentation"> - <a href="#" class="pool-add-volume" data-stat="{state}" data-name="{name}" data-type="{type}"><i class="fa fa-plus-circle"></i>$_("Add Volume")</a> - </li> - <li role="presentation" class="{enableExt}"> - <a href="#" class="pool-extend" data-stat="{state}" data-name="{name}" data-toggle="modal" data-target="#logicalPoolExtend"><i class="fa fa-external-link-square"></i>$_("Extend")</a> - </li> - <li role="presentation" class="critical"> - <a href="#" class="pool-delete" data-inuse="{in_use}" data-stat="{state}" data-name="{name}"><i class="fa fa-ban"></i>$_("Undefine")</a> - </li> + <li><a href="#" class="pool-deactivate" data-inuse="{in_use}" data-stat="{state}" data-name="{name}" data-persistent="{persistent}"><i class="fa fa-minus-circle"></i>$_("Deactivate")</a></li> + <li><a href="#" class="pool-activate" data-stat="{state}" data-name="{name}"><i class="fa fa-power-off"></i>$_("Activate")</a></li> + <li><a href="#" class="pool-add-volume" data-stat="{state}" data-name="{name}" data-type="{type}"><i class="fa fa-plus-circle"></i>$_("Add Volume")</a></li> + <li class="{enableExt}"><a href="#" class="pool-extend" data-stat="{state}" data-name="{name}" data-toggle="modal" data-target="#logicalPoolExtend"><i class="fa fa-external-link-square"></i>$_("Extend")</a></li> + <li class="critical"><a href="#" class="pool-delete" data-inuse="{in_use}" data-stat="{state}" data-name="{name}"><i class="fa fa-ban"></i>$_("Undefine")</a></li> </ul> </div> - </span> </span> - <div class="volumes"> - <div id="volume{name}" class="volumeslist" data-name="{name}" ></div> - <div class="clear"></div> - </div> - </div> + </span> + <div class="volumes"> + <div id="volume{name}" class="volumeslist" data-name="{name}"> + <div class="row"> + <div class="pull-left"> + <div class="dropdown menu-flat pool-action"> + <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false" aria-haspopup="true"><span class="edit-alt"></span>$_("Actions")<span class="caret"></span></button> + <ul class="dropdown-menu actionsheet"> + <li><a href="#" class="volume-create"><i class="fa fa-plus-circle"></i> $_("Create")</a></li> + <li><a href="#" class="volume-resize"><i class="fa fa-external-link-square"></i> $_("Resize")</a></li> + <li><a href="#" class="volume-clone"><i class="fa fa-copy"></i> $_("Clone")</a></li> + <li><a href="#" class="volume-wipe"><i class="fa fa-eraser"></i> $_("Wipe")</a></li> + <li class="critical"><a href="#" class="volume-delete"><i class="fa fa-minus-circle"></i> $_("Delete")</a></li> + </ul> + </div> + <button type="button" class="btn btn-default toggle-gallery"><span class="text">$_('View Gallery')</span> <i class="fa fa-angle-right"></i><i class="fa fa-angle-right"></i><i class="fa fa-angle-right"></i></button> + </div> + <div class="pull-right"> + <label><span class="sr-only">$_('Filter:')</span> + <input type="text" class="filter form-control" placeholder="$_('Filter')"> + </label> + </div> + </div> + <div class="wok-datagrid wok-list"> + <div class="wok-datagrid-header"> + <span class="column-name">$_('Name')</span><!-- + --><span class="column-format">$_('Format')</span><!-- + --><span class="column-type">$_('Type')</span><!-- + --><span class="column-used">$_('Used')</span><!-- + --><span class="column-allocated">$_('Allocated')</span><!-- + --><span class="column-capacity">$_('Capacity')</span> + </div> + <ul class="wok-datagrid-body"> + </ul> + </div> + </div> + <div class="clear"></div> + </div> +</div> </script> <script id="volumeTmpl" type="html/text"> <div class="volume-box" data-volume-name="{name}"> @@ -187,6 +204,34 @@ </ul> </div> </script> +<script id="volumeTmpl2" type="html/text"> +<li class="wok-datagrid-row"> + <div class="volume-progress hidden"> + <div class="progress-bar-outer"> + <div class="progress-bar-inner"></div> + </div> + <div class="progress-label"> + <span class="progress-status"></span> + <span class="progress-transferred"></span> + </div> + </div> + <div class="volume-box-outer"> + <div class="volume-box-border"> + <div class="volume-box-inner" data-volume-name="{name}"> + <span class="column-name" title="{name}"> + <input type="checkbox" class="wok-checkbox" name="selected-volume[]" id="{checkbox}" value="{volume-id}"> + <label class="volume-name" for="{checkbox}"><span class="volume-inline-progress hidden"><span class="wok-loading-icon"></span></span> {name}</label><!-- + --></span><!-- + --><span class="column-format"><span class="gallery-header">$_('Format')</span>{format}</span><!-- + --><span class="column-type"><span class="gallery-header">$_('Type')</span>{type}</span><!-- + --><span class="column-used"><span role="presentation" class="volume-icon {capacityIcon}"></span> {capacityLevel}%</span><!-- + --><span class="column-allocated"><span class="gallery-header">$_('Allocation')</span>{allocation}</span><!-- + --><span class="column-capacity"><span class="gallery-header">$_('Capacity')</span>{capacity}</span> + </div> + </div> + </div> +</li> +</script> <script id="logicalPoolExtendTmpl" type="html/text"> <div> <input type="checkbox" class="wok-checkbox" id="{name}" value="{path}" name="devices"> -- 1.9.3

Hi Samuel, Overall comments: 1. As you added the "Create" options under storage volume (which I think it is best to be done), please, remove the "Add volume" option from storage pool level. 2. When no storage volume is selected, only "Create" option should be enabled. 3. When one storage volume is selected, disable "Create" option and enable "Wipe", "Resize", "Clone" and "Delete". 4. When more than on storage volume are selected, disable "Create", "Resize", "Clone" and enable "Wipe" and "Delete". 5. Filter is not working (maybe it is intentional as it is an RFC) 6. "Wipe" and "Delete" should display a confirmation box. 7. "Resize" should open a new dialog to get the new storage volume size. 8. I don't think we need anything for Clone. The operation should be atomic. No input is needed. It will return a Task to be tracked. We need to do something similar to what we have for VM cloning. Display a fake storage volume entry with a loading icon and "Cloning" label. 9. The create dialog should be what we have today for "Add volume" in the storage pool level with one more option "Capacity" to create a new empty storage volume given a size. It will return a Task to be tracked as well. And we should follow the same behavior while cloning. I will review the patch soon and provide any feedback inline there. Regards, Aline Manera On 05/13/2016 06:39 PM, sguimaraes943@gmail.com wrote:
From: Samuel Guimarães <sguimaraes943@gmail.com>
This patch adds the UI for Storage Volume management action button, filter input, gallery view and gallery / list view toggle-switch button with multi-selection enabled.
This is just the initial patch for the UI to get feedback, will add Storage Volume modal and confirm windows and map create, delete, resize, wipe and clone actions in the next commit.
Samuel Guimarães (1): Storave Volume management UI
ui/css/kimchi.css | 266 +++++++++++++++++++++++++++++------- ui/css/src/modules/_storage.scss | 287 +++++++++++++++++++++++++++++++++------ ui/js/src/kimchi.storage_main.js | 82 +++++------ ui/pages/tabs/storage.html.tmpl | 121 +++++++++++------ 4 files changed, 576 insertions(+), 180 deletions(-)

-----Original Message----- From: kimchi-devel-bounces@ovirt.org [mailto:kimchi-devel-bounces@ovirt.org] On Behalf Of Aline Manera Sent: segunda-feira, 16 de maio de 2016 15:06 To: sguimaraes943@gmail.com; Kimchi Devel <kimchi-devel@ovirt.org> Subject: Re: [Kimchi-devel] [PATCH] [Kimchi] [RFC] Storave Volume management UI Hi Samuel, Overall comments: 1. As you added the "Create" options under storage volume (which I think it is best to be done), please, remove the "Add volume" option from storage pool level. -I'll remove the Create from the Volume drop-down and keep the other one. 2. When no storage volume is selected, only "Create" option should be enabled. -Will disable the drop-down by default 3. When one storage volume is selected, disable "Create" option and enable "Wipe", "Resize", "Clone" and "Delete". -See above. 4. When more than on storage volume are selected, disable "Create", "Resize", "Clone" and enable "Wipe" and "Delete". From the API, I can see that Clone has some optional parameters. I'm still going to investigate the best approach to keep track of the cloning volumes but I think we could clone more than one item at once. It may get extremely slow due to several tasks running at the same time... 5. Filter is not working (maybe it is intentional as it is an RFC) Yes, the filter is currently not working. In fact I'm facing some issues with the filter using list.js once the volumes list gets refreshed. 6. "Wipe" and "Delete" should display a confirmation box. For now I'm displaying the array of selected volumes but sometimes it turns into a huge list. Should we change it to just a regular confirmation box saying that it will wipe/delete all the selected data? 7. "Resize" should open a new dialog to get the new storage volume size. Ok. 8. I don't think we need anything for Clone. The operation should be atomic. No input is needed. It will return a Task to be tracked. We need to do something similar to what we have for VM cloning. Display a fake storage volume entry with a loading icon and "Cloning" label. I'll see what I can do with the "fake" item but I think this is going to take more time than I was expecting. As for the parameters, the API.md has: * clone: Clone a Storage Volume. * pool: The name of the destination pool (optional). * name: The new storage volume name (optional). Shouldn't it have a modal window? 9. The create dialog should be what we have today for "Add volume" in the storage pool level with one more option "Capacity" to create a new empty storage volume given a size. It will return a Task to be tracked as well. And we should follow the same behavior while cloning. Ok. I'll move this as top priority once I finish wipe and filter (Delete is almost done but facing a minor issue when only one volume is available - The list won't update with empty results) I will review the patch soon and provide any feedback inline there. Regards, Aline Manera On 05/13/2016 06:39 PM, sguimaraes943@gmail.com wrote:
From: Samuel Guimarães <sguimaraes943@gmail.com>
This patch adds the UI for Storage Volume management action button, filter input, gallery view and gallery / list view toggle-switch button with multi-selection enabled.
This is just the initial patch for the UI to get feedback, will add Storage Volume modal and confirm windows and map create, delete, resize, wipe and clone actions in the next commit.
Samuel Guimarães (1): Storave Volume management UI
ui/css/kimchi.css | 266 +++++++++++++++++++++++++++++------- ui/css/src/modules/_storage.scss | 287 +++++++++++++++++++++++++++++++++------ ui/js/src/kimchi.storage_main.js | 82 +++++------ ui/pages/tabs/storage.html.tmpl | 121 +++++++++++------ 4 files changed, 576 insertions(+), 180 deletions(-)
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 05/16/2016 05:01 PM, Samuel Henrique De Oliveira Guimaraes wrote:
-----Original Message----- From: kimchi-devel-bounces@ovirt.org [mailto:kimchi-devel-bounces@ovirt.org] On Behalf Of Aline Manera Sent: segunda-feira, 16 de maio de 2016 15:06 To: sguimaraes943@gmail.com; Kimchi Devel <kimchi-devel@ovirt.org> Subject: Re: [Kimchi-devel] [PATCH] [Kimchi] [RFC] Storave Volume management UI
Hi Samuel,
Overall comments:
1. As you added the "Create" options under storage volume (which I think it is best to be done), please, remove the "Add volume" option from storage pool level. -I'll remove the Create from the Volume drop-down and keep the other one.
It is better to have all the storage volume centralized in one place. So as we are going to support storage volume actions, I'd rather to have the Create option listed there instead of on storage pool level (as it is today). Do you think it is possible to do that?
2. When no storage volume is selected, only "Create" option should be enabled. -Will disable the drop-down by default
3. When one storage volume is selected, disable "Create" option and enable "Wipe", "Resize", "Clone" and "Delete". -See above.
4. When more than on storage volume are selected, disable "Create", "Resize", "Clone" and enable "Wipe" and "Delete". From the API, I can see that Clone has some optional parameters. I'm still going to investigate the best approach to keep track of the cloning volumes but I think we could clone more than one item at once. It may get extremely slow due to several tasks running at the same time...
Yeap! We can have clone enabled for multiple selection as well.
5. Filter is not working (maybe it is intentional as it is an RFC) Yes, the filter is currently not working. In fact I'm facing some issues with the filter using list.js once the volumes list gets refreshed.
6. "Wipe" and "Delete" should display a confirmation box. For now I'm displaying the array of selected volumes but sometimes it turns into a huge list. Should we change it to just a regular confirmation box saying that it will wipe/delete all the selected data?
I prefer to keep showing the storage volume names instead of a generic message. It is already happened to me to delete a wrong VM because the confirmation box did not shown me which VM I was deleting.
7. "Resize" should open a new dialog to get the new storage volume size. Ok.
8. I don't think we need anything for Clone. The operation should be atomic. No input is needed. It will return a Task to be tracked. We need to do something similar to what we have for VM cloning. Display a fake storage volume entry with a loading icon and "Cloning" label. I'll see what I can do with the "fake" item but I think this is going to take more time than I was expecting. As for the parameters, the API.md has:
The 'fake' item is already created when using the "Add Volume" option. You just need to reuse it overall when needed. Because that, I also think it is better to move the "Add Volume" to the Create option inside the storage volume view.
* clone: Clone a Storage Volume. * pool: The name of the destination pool (optional). * name: The new storage volume name (optional).
Shouldn't it have a modal window?
No! No model window is needed. The 'pool' is the storage pool related to the storage volumes listed. When you access the Storage tab, you have the list of all storage pools. When accessing the details for a given storage pool, you get the storage volumes. When creating or cloning a storage volume, the storage pool linked to it must be used. So for the clone specific case, get the storage pool related to the storage volume section and keep the new name optional, that way the backend will automatically get one. For example, if I have a volume named "my-volume" in a "MyVols" pools and I want to clone it, the API will be: POST /storagepools/MyVols/my-volume/clone ie, POST /storagepools/<pool-name>/<storage-volume>/clone Does that make sense?
9. The create dialog should be what we have today for "Add volume" in the storage pool level with one more option "Capacity" to create a new empty storage volume given a size. It will return a Task to be tracked as well. And we should follow the same behavior while cloning. Ok. I'll move this as top priority once I finish wipe and filter (Delete is almost done but facing a minor issue when only one volume is available - The list won't update with empty results)
I will review the patch soon and provide any feedback inline there.
Regards, Aline Manera
On 05/13/2016 06:39 PM, sguimaraes943@gmail.com wrote:
From: Samuel Guimarães <sguimaraes943@gmail.com>
This patch adds the UI for Storage Volume management action button, filter input, gallery view and gallery / list view toggle-switch button with multi-selection enabled.
This is just the initial patch for the UI to get feedback, will add Storage Volume modal and confirm windows and map create, delete, resize, wipe and clone actions in the next commit.
Samuel Guimarães (1): Storave Volume management UI
ui/css/kimchi.css | 266 +++++++++++++++++++++++++++++------- ui/css/src/modules/_storage.scss | 287 +++++++++++++++++++++++++++++++++------ ui/js/src/kimchi.storage_main.js | 82 +++++------ ui/pages/tabs/storage.html.tmpl | 121 +++++++++++------ 4 files changed, 576 insertions(+), 180 deletions(-)
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
participants (3)
-
Aline Manera
-
Samuel Henrique De Oliveira Guimaraes
-
sguimaraes943@gmail.com