[PATCH v2] [Kimchi] [RFC] Storage 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. v1: - HTML and CSS v2: - Delete and Wipe with multi-selection - Confirm messages with list of selected volumes when wiping or deleting volumes (requires SCSS/CSS patch sent to Wok) - Filter working - Removed "Add Volume" link from Storage Pool action button - Added "Add Volume" to Volume box action button To do: - Clone function - Fix progress bar (gallery view) and spinner (list view) when adding and cloning a new volume - Add a temporary volume when cloning process is still in progress Known issues: - List doesn't refresh when the only volume available is deleted from Storage Pool. Please let me know if you find any bug or if you have any suggestion. Samuel Guimarães (1): Storage Volume management UI ui/css/kimchi.css | 273 +++++++++++++++---- ui/css/src/modules/_storage.scss | 295 +++++++++++++++++---- ui/js/src/kimchi.api.js | 50 ++++ ui/js/src/kimchi.storage_main.js | 256 ++++++++++++++---- ui/js/src/kimchi.storagepool_add_volume_main.js | 2 +- ui/js/src/kimchi.storagepool_resize_volume_main.js | 58 ++++ ui/pages/i18n.json.tmpl | 5 + ui/pages/storagepool-resize-volume.html.tmpl | 51 ++++ ui/pages/tabs/storage.html.tmpl | 120 ++++++--- 9 files changed, 925 insertions(+), 185 deletions(-) create mode 100644 ui/js/src/kimchi.storagepool_resize_volume_main.js create mode 100644 ui/pages/storagepool-resize-volume.html.tmpl -- 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 | 273 +++++++++++++++---- ui/css/src/modules/_storage.scss | 295 +++++++++++++++++---- ui/js/src/kimchi.api.js | 50 ++++ ui/js/src/kimchi.storage_main.js | 256 ++++++++++++++---- ui/js/src/kimchi.storagepool_add_volume_main.js | 2 +- ui/js/src/kimchi.storagepool_resize_volume_main.js | 58 ++++ ui/pages/i18n.json.tmpl | 5 + ui/pages/storagepool-resize-volume.html.tmpl | 51 ++++ ui/pages/tabs/storage.html.tmpl | 120 ++++++--- 9 files changed, 925 insertions(+), 185 deletions(-) create mode 100644 ui/js/src/kimchi.storagepool_resize_volume_main.js create mode 100644 ui/pages/storagepool-resize-volume.html.tmpl diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css index 49ea39a..0e2f420 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%; @@ -2347,8 +2318,216 @@ body.wok-gallery { } #storage-root-container .volumes .pool-empty { + width: 100%; + cursor: default !important; +} + +#storage-root-container .volumes .pool-empty > span { + width: 100%; text-align: center; line-height: 136px; + vertical-align: middle !important; +} + +#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"] { diff --git a/ui/css/src/modules/_storage.scss b/ui/css/src/modules/_storage.scss index 4a9f9e1..5b7f44a 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; @@ -164,10 +147,230 @@ } } } - .pool-empty { + .pool-empty{ + width: 100%; + cursor: default !important; + > span { + width: 100%; text-align: center; line-height: 136px; + vertical-align: middle !important; + } } + + .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.api.js b/ui/js/src/kimchi.api.js index 83d1cc7..321bcb0 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -725,6 +725,56 @@ var kimchi = { }); }, + cloneStoragePoolVolume: function(poolName, volumeName, suc, err) { + var url = 'plugins/kimchi/storagepools/' + encodeURIComponent(poolName) + '/storagevolumes/' + encodeURIComponent(volumeName) + '/clone'; + wok.requestJSON({ + url : url, + type : 'POST', + data : JSON.stringify(data), + contentType : 'application/json', + dataType : 'json', + success : suc, + error : err + }); + }, + + resizeStoragePoolVolume: function(poolName, volumeName, data, suc, err) { + var url = 'plugins/kimchi/storagepools/' + encodeURIComponent(poolName) + '/storagevolumes/' + encodeURIComponent(volumeName) + '/resize'; + wok.requestJSON({ + url : url, + type : 'POST', + data : JSON.stringify(data), + contentType : 'application/json', + dataType : 'json', + success : suc, + error : err + }); + }, + + wipeStoragePoolVolume: function(poolName, volumeName, suc, err) { + var url = 'plugins/kimchi/storagepools/' + encodeURIComponent(poolName) + '/storagevolumes/' + encodeURIComponent(volumeName) + '/wipe'; + wok.requestJSON({ + url : url, + type : 'POST', + contentType : 'application/json', + dataType : 'json', + success : suc, + error : err + }); + }, + + deleteStoragePoolVolume: function(poolName, volumeName, suc, err) { + var url = 'plugins/kimchi/storagepools/' + encodeURIComponent(poolName) + '/storagevolumes/' + encodeURIComponent(volumeName); + wok.requestJSON({ + url : url, + type : 'DELETE', + contentType : 'application/json', + dataType : 'json', + success : suc, + error : err + }); + }, + getHostVgs: function(suc, err) { var url = 'plugins/kimchi/host/vgs/'; wok.requestJSON({ diff --git a/ui/js/src/kimchi.storage_main.js b/ui/js/src/kimchi.storage_main.js index 5312388..cf0e2e1 100644 --- a/ui/js/src/kimchi.storage_main.js +++ b/ui/js/src/kimchi.storage_main.js @@ -65,6 +65,166 @@ 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']); + }); + + if(wok.tabMode['storage'] === 'admin') { + + $('.volumes').on('click','.volume-delete',function(e){ + e.preventDefault(); + e.stopPropagation(); + var button = $(this); + kimchi.selectedSP = $(this).data('name'); + var volumeBlock = $(this).data('name'); + var volumes = $('[data-name="'+kimchi.selectedSP+'"] input:checkbox:checked').map(function(){ + return this.value; + }).get(); + kimchi.selectedVolumes = volumes.slice(); + // console.log('selected volumes'); + // console.log(kimchi.selectedVolumes); + // console.log('selected volumes clones'); + // console.log(volumes); + var formatedVolumes = ''; + if(kimchi.selectedVolumes.length && !button.parent().is('disabled')){ + for (i = 0; i < kimchi.selectedVolumes.length; i++) { + formatedVolumes += "<li>" + kimchi.selectedVolumes[i] + "</li>"; + } + var confirmMessage = i18n['KCHPOOL6010M'].replace('%1','<ul>'+formatedVolumes+'</ul>'+i18n['KCHPOOL6009M']); + var settings = { + title : i18n['KCHAPI6001M'], + content : confirmMessage, + confirm : i18n['KCHAPI6002M'], + cancel : i18n['KCHAPI6003M'] + }; + wok.confirm(settings, function() { + $.each(kimchi.selectedVolumes, function(i,j) { + console.log(i + ": " + j); + volumes = jQuery.grep(volumes, function(value) { + return value != j; + }); + kimchi.deleteStoragePoolVolume(kimchi.selectedSP,j,function(){ + wok.message.success(i18n['KCHPOOL6017M'].replace('%1','<strong>'+j+'</strong>')); + },function(err){ + wok.message.error(err.responseJSON.reason); + }); + if(volumes.length === 0){ + wok.topic('kimchi/storageVolumeDeleted').publish(); + kimchi.selectedVolumes = ''; + volumes = ''; + } + }); + }); + }else { + return false; + } + }); + + $('.volumes').on('click','.volume-wipe',function(e){ + e.preventDefault(); + e.stopPropagation(); + var button = $(this); + kimchi.selectedSP = $(this).data('name'); + var volumeBlock = $(this).data('name'); + var volumes = $('[data-name="'+kimchi.selectedSP+'"] input:checkbox:checked').map(function(){ + return this.value; + }).get(); + kimchi.selectedVolumes = volumes.slice(); + var formatedVolumes = ''; + if(kimchi.selectedVolumes.length && !button.parent().is('disabled')){ + for (i = 0; i < kimchi.selectedVolumes.length; i++) { + formatedVolumes += "<li>" + kimchi.selectedVolumes[i] + "</li>"; + } + var confirmMessage = i18n['KCHPOOL6018M'].replace('%1','<ul>'+formatedVolumes+'</ul>'+i18n['KCHPOOL6009M']); + var settings = { + title : i18n['KCHPOOL6019M'], + content : confirmMessage, + confirm : i18n['KCHAPI6002M'], + cancel : i18n['KCHAPI6003M'] + }; + wok.confirm(settings, function() { + $.each(kimchi.selectedVolumes, function(i,j) { + volumes = jQuery.grep(volumes, function(value) { + return value != j; + }); + kimchi.wipeStoragePoolVolume(kimchi.selectedSP,j,function(){ + wok.message.success(i18n['KCHPOOL6017M'].replace('%1','<strong>'+j+'</strong>')); + },function(err){ + wok.message.error(err.responseJSON.reason); + }); + if(volumes.length === 0){ + wok.topic('kimchi/storageVolumeWiped').publish(); + kimchi.selectedVolumes = ''; + volumes = ''; + } + }); + }); + }else { + return false; + } + }); + + $('.volumes').on('click','.volume-resize',function(e){ + e.preventDefault(); + e.stopPropagation(); + var button = $(this); + kimchi.selectedSP = $(this).data('name'); + var volumes = $('[data-name="'+kimchi.selectedSP+'"] input:checkbox:checked').map(function(){ + return this.value; + }).get(); + kimchi.selectedVolumes = volumes.slice(0,1); + if(kimchi.selectedVolumes.length && !button.parent().is('disabled')){ + wok.window.open('plugins/kimchi/storagepool-resize-volume.html'); + }else { + return false; + } + }); + + $('.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).closest('.wok-datagrid-row'); + var volumesBlock = $(this).closest('.volumeslist'); + var disabled = []; + if($('[name="selected-volume[]"]:checked',volumesBlock).length > 1) { + disabled = ['volume-clone','volume-wipe','volume-delete']; + $('.volume-resize',volumesBlock).parent().addClass('disabled'); + for (i = 0; i < disabled.length; i++) { + $('.'+disabled[i],volumesBlock).parent().removeClass('disabled'); + } + }else if($('[name="selected-volume[]"]:checked',volumesBlock).length === 1){ + disabled = ['volume-resize','volume-clone','volume-wipe','volume-delete']; + for (i = 0; i < disabled.length; i++) { + $('.'+disabled[i],volumesBlock).parent().removeClass('disabled'); + } + }else { + disabled = ['volume-resize','volume-clone','volume-wipe','volume-delete']; + for (i = 0; i < disabled.length; i++) { + $('.'+disabled[i],volumesBlock).parent().addClass('disabled'); + } + } + if(checkbox.is(":checked")){ + volumeBlock.addClass('selected'); + }else { + volumeBlock.removeClass('selected'); + } + }); + + } + $('.inactive').each(function(index) { if ('active' === $(this).data('state')) { $(this).hide(); @@ -229,7 +389,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,11 +434,12 @@ kimchi.doListVolumes = function(poolObj) { return result; }; - var volumeDiv = $('#volume' + poolName); - $(volumeDiv).empty(); + var volumeDiv = $('#volume-' + poolName); + var volumeDatatable = $('.wok-datagrid > .wok-datagrid-body',volumeDiv); + $(volumeDatatable).empty(); var slide = $('.volumes', poolObj); var handleArrow = $('.arrow-down', poolObj); - + console.log('arrow down'); kimchi.listStorageVolumes(poolName, function(result) { var listHtml = ''; var ongoingVolumes = []; @@ -310,9 +472,14 @@ kimchi.doListVolumes = function(poolObj) { }); if (listHtml.length > 0) { - volumeDiv.html(listHtml); + $('.filter',volumeDiv).prop('disabled',false); + $('.toggle-gallery',volumeDiv).prop('disabled',false); + $(volumeDatatable).html(listHtml); + } else { - volumeDiv.html("<div class='pool-empty'>" + i18n['KCHPOOL6002M'] + "</div>"); + $('.filter',volumeDiv).prop('disabled',true); + $('.toggle-gallery',volumeDiv).prop('disabled',true); + $(volumeDatatable).html("<div class='pool-empty wok-datagrid-row'><span class='volume-empty'>" + i18n['KCHPOOL6002M'] + "</span></div>"); } $.each(ongoingVolumesMap, function(volumeName, task) { @@ -322,6 +489,18 @@ kimchi.doListVolumes = function(poolObj) { poolObj.removeClass('in'); kimchi.changeArrow(handleArrow); slide.slideDown('slow'); + + volumeDivId = volumeDiv.attr('id'); + + volumeOptions = { + valueNames: ['volume-name-filter', 'volume-format-filter', 'volume-type-filter'] + }; + volumeFilterList = new List(volumeDivId, volumeOptions); + + volumeFilterList.sort('volume-name-filter', { + order: "asc" + }); + }, function(err) { wok.message.error(err.responseJSON.reason); }, false); @@ -329,53 +508,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(); }); @@ -442,6 +574,24 @@ kimchi.storage_main = function() { kimchi.doListVolumes(poolNode); }); + wok.topic('kimchi/storageVolumeDeleted').subscribe(function() { + pool = kimchi.selectedSP; + var poolNode = $('.storage-li[data-name="' + pool + '"]'); + kimchi.doListVolumes(poolNode); + }); + + wok.topic('kimchi/storageVolumeWiped').subscribe(function() { + pool = kimchi.selectedSP; + var poolNode = $('.storage-li[data-name="' + pool + '"]'); + kimchi.doListVolumes(poolNode); + }); + + wok.topic('kimchi/storageVolumeResized').subscribe(function() { + pool = kimchi.selectedSP; + var poolNode = $('.storage-li[data-name="' + pool + '"]'); + kimchi.doListVolumes(poolNode); + }); + wok.topic('kimchi/volumeTransferProgress').subscribe(function(result) { var extractProgressData = function(data) { var sizeArray = /(\d+)\/(\d+)/g.exec(data) || [0, 0, 0]; diff --git a/ui/js/src/kimchi.storagepool_add_volume_main.js b/ui/js/src/kimchi.storagepool_add_volume_main.js index c398369..e167a20 100644 --- a/ui/js/src/kimchi.storagepool_add_volume_main.js +++ b/ui/js/src/kimchi.storagepool_add_volume_main.js @@ -180,6 +180,6 @@ kimchi.sp_add_volume_main = function() { uploadFile(); } event.preventDefault(); - $('#modalWindow').modal.close(); + wok.window.close(); }); }; diff --git a/ui/js/src/kimchi.storagepool_resize_volume_main.js b/ui/js/src/kimchi.storagepool_resize_volume_main.js new file mode 100644 index 0000000..0b350bd --- /dev/null +++ b/ui/js/src/kimchi.storagepool_resize_volume_main.js @@ -0,0 +1,58 @@ +/* + * Project Kimchi + * + * Copyright IBM Corp, 2016 + * + * Licensed 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. + */ + +kimchi.sp_resize_volume_main = function() { + + var addButton = $('#sp-resize-volume-button'); + var size = $('#volume-size'); + var form = $('form#form-sp-resize-volume'); + + $(addButton).prop('disabled',true); + $(size).on('keyup', function(){ + if($(this).val().length !==0) { + addButton.prop('disabled', false); + } else{ + addButton.prop('disabled',true); + } + }); + $(addButton).on('click',function(e){ + e.preventDefault(); + e.stopPropagation(); + $(form).submit(); + }); + $(form).on('submit',function(e){ + e.preventDefault(); + e.stopPropagation(); + var newsize = parseInt($(size).val()); + var data = {}; + data = { + size: newsize + }; + kimchi.resizeStoragePoolVolume(kimchi.selectedSP, kimchi.selectedVolumes[0], data, function() { + $(size).prop('disabled', true); + $(addButton).prop('disabled',true); + wok.topic('kimchi/storageVolumeResized').publish(); + wok.window.close(); + }, function(err) { + wok.message.error(err.responseJSON.reason, '#alert-modal-container'); + $(size).prop('disabled', false); + $(addButton).prop('disabled',false); + $(size).focus(); + }); + }); +} diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl index e152989..2b786a5 100644 --- a/ui/pages/i18n.json.tmpl +++ b/ui/pages/i18n.json.tmpl @@ -98,6 +98,8 @@ "KCHPOOL6006M": "$_("Loading iSCSI targets...")", "KCHPOOL6007M": "$_("No iSCSI found. Please input one.")", "KCHPOOL6008M": "$_("Failed to load iSCSI targets.")", + "KCHPOOL6009M": "$_("Would you like to continue?")", + "KCHPOOL6010M": "$_("This will permanently delete the following storage volumes: %1")", "KCHPOOL6005E": "$_("Invalid NFS mount path.")", "KCHPOOL6006E": "$_("No logical device selected.")", @@ -108,6 +110,9 @@ "KCHPOOL6014M": "$_("In progress...")", "KCHPOOL6015M": "$_("Failed!")", "KCHPOOL6016M": "$_("No LVM found in the system.")", + "KCHPOOL6017M": "$_("Volume %1 was successfully removed.")", + "KCHPOOL6018M": "$_("This will permanently wipe the following storage volumes: %1")", + "KCHPOOL6019M": "$_("Wipe Confirmation")", "KCHVMSTOR0001E": "$_("CDROM path needs to be a valid local/remote path and cannot be blank.")", "KCHVMSTOR0002E": "$_("Disk pool or volume cannot be blank.")" diff --git a/ui/pages/storagepool-resize-volume.html.tmpl b/ui/pages/storagepool-resize-volume.html.tmpl new file mode 100644 index 0000000..fd9d798 --- /dev/null +++ b/ui/pages/storagepool-resize-volume.html.tmpl @@ -0,0 +1,51 @@ +#* + * Project Kimchi + * + * Copyright IBM Corp, 2016 + * + * Licensed 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. + *# +#unicode UTF-8 +#import gettext +#from wok.cachebust import href +#silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang, fallback=True) +#silent _ = t.gettext +#silent _t = t.gettext +<html> +<body> +<div id="sp-add-volume-window" class="window modal-content"> + <form id="form-sp-resize-volume"> + <div class="modal-header"> + <h4 class="modal-title">$_("Resize Volume")</h4> + </div> + <div class="modal-body"> + <span id="alert-modal-container"></span> + <div class="form-group"> + <label for="volume-size">$_("Size")</label> + <input type="number" id="volume-size" class="form-control" name="size" /> + <p class="help-block"> + <i class="fa fa-info-circle"></i> $_("The total space which can be used to store data. The unit is bytes.") + </p> + </div> + </div> + <div class="modal-footer"> + <button type="submit" id="sp-resize-volume-button" class="btn btn-default" disabled="disabled">$_("Ok")</button> + <button type="button" class="btn btn-default" data-dismiss="modal">$_("Cancel")</button> + </div> + </form> +</div> +<script type="text/javascript"> + kimchi.sp_resize_volume_main(); +</script> +</body> +</html> diff --git a/ui/pages/tabs/storage.html.tmpl b/ui/pages/tabs/storage.html.tmpl index fa51e48..46f81c2 100644 --- a/ui/pages/tabs/storage.html.tmpl +++ b/ui/pages/tabs/storage.html.tmpl @@ -102,50 +102,66 @@ </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 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="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="disabled"><a href="#" class="volume-resize" data-name="{name}"><i class="fa fa-external-link-square"></i> $_("Resize")</a></li> + <li class="disabled"><a href="#" class="volume-clone" data-name="{name}"><i class="fa fa-copy"></i> $_("Clone")</a></li> + <li class="disabled"><a href="#" class="volume-wipe" data-name="{name}"><i class="fa fa-eraser"></i> $_("Wipe")</a></li> + <li class="disabled critical"><a href="#" class="volume-delete" data-name="{name}"><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 search" 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 list" id="volume-{name}-table"> + </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 +203,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="{name}"> + <label class="volume-name volume-name-filter" for="{checkbox}"><span class="volume-inline-progress hidden"><span class="wok-loading-icon"></span></span> {name}</label><!-- + --></span><!-- + --><span class="column-format volume-format-filter"><span class="gallery-header">$_('Format')</span>{format}</span><!-- + --><span class="column-type volume-type-filter"><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, Some comments, 1. Use MB to resize the volume. Bytes is to difficult to deal with. =) 2. When you do an action in a volume, the volume area gets smaller and all volumes disappears for a second and then the volume area is rebuild with the new data to get back to the original size. Is there a way to do not change the volume size in that process? Maybe add a loading icon to the affected volume, get the response from server and only update this entry? I think that would be better from a user perspective. What do you think about that? Regards, Aline Manera On 05/19/2016 06:43 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.
v1: - HTML and CSS
v2: - Delete and Wipe with multi-selection - Confirm messages with list of selected volumes when wiping or deleting volumes (requires SCSS/CSS patch sent to Wok) - Filter working - Removed "Add Volume" link from Storage Pool action button - Added "Add Volume" to Volume box action button
To do: - Clone function - Fix progress bar (gallery view) and spinner (list view) when adding and cloning a new volume - Add a temporary volume when cloning process is still in progress
Known issues: - List doesn't refresh when the only volume available is deleted from Storage Pool.
Please let me know if you find any bug or if you have any suggestion.
Samuel Guimarães (1): Storage Volume management UI
ui/css/kimchi.css | 273 +++++++++++++++---- ui/css/src/modules/_storage.scss | 295 +++++++++++++++++---- ui/js/src/kimchi.api.js | 50 ++++ ui/js/src/kimchi.storage_main.js | 256 ++++++++++++++---- ui/js/src/kimchi.storagepool_add_volume_main.js | 2 +- ui/js/src/kimchi.storagepool_resize_volume_main.js | 58 ++++ ui/pages/i18n.json.tmpl | 5 + ui/pages/storagepool-resize-volume.html.tmpl | 51 ++++ ui/pages/tabs/storage.html.tmpl | 120 ++++++--- 9 files changed, 925 insertions(+), 185 deletions(-) create mode 100644 ui/js/src/kimchi.storagepool_resize_volume_main.js create mode 100644 ui/pages/storagepool-resize-volume.html.tmpl
participants (2)
-
Aline Manera
-
sguimaraes943@gmail.com