[pbs-devel] [PATCH proxmox-backup] ui: tape/BackupOverview: add 'restore partial' button

Thomas Lamprecht t.lamprecht at proxmox.com
Tue May 11 18:17:53 CEST 2021


On 11.05.21 14:42, Dominik Csapak wrote:
> this opens the restore window, but with a snapshot selector, so that
> the user can select the snapshots to restore
> 
> includes a textfield for basic filtering


why isn't that handled directly in the restore window, without extra buttons?
Could possibly be a radio group there.

I really want to avoid many buttons in places like top-bars, makes it crowded
and confusing..

Also, why doesn't this uses action buttons for those things like the content
tree? IMO we should try to be consistent with UX, cross-product this may be
hard and lots of work, but I do not see any excuse for intra-product consistency
(especially on rather simple UIs like PBS, compared to PVE).

> 
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
>  www/tape/BackupOverview.js     |  31 ++++++++
>  www/tape/window/TapeRestore.js | 125 +++++++++++++++++++++++++++++++++
>  2 files changed, 156 insertions(+)
> 
> diff --git a/www/tape/BackupOverview.js b/www/tape/BackupOverview.js
> index c028d58d..e039595d 100644
> --- a/www/tape/BackupOverview.js
> +++ b/www/tape/BackupOverview.js
> @@ -48,6 +48,29 @@ Ext.define('PBS.TapeManagement.BackupOverview', {
>  	    }).show();
>  	},
>  
> +	restoreList: function(button, record) {
> +	    let me = this;
> +	    let view = me.getView();
> +	    let selection = view.getSelection();
> +	    if (!selection || selection.length < 1) {
> +		return;
> +	    }
> +
> +	    let node = selection[0];
> +	    let mediaset = node.data.text;
> +	    let uuid = node.data['media-set-uuid'];
> +	    Ext.create('PBS.TapeManagement.TapeRestoreWindow', {
> +		mediaset,
> +		uuid,
> +		list_snapshots: true,
> +		listeners: {
> +		    destroy: function() {
> +			me.reload();
> +		    },
> +		},
> +	    }).show();
> +	},
> +
>  	restore: function(button, record) {
>  	    let me = this;
>  	    let view = me.getView();
> @@ -295,6 +318,14 @@ Ext.define('PBS.TapeManagement.BackupOverview', {
>  	    parentXType: 'treepanel',
>  	    enableFn: (rec) => !!rec.data['media-set-uuid'],
>  	},
> +	{
> +	    xtype: 'proxmoxButton',
> +	    disabled: true,
> +	    text: gettext('Restore partial Media Set'),

really long button text's should be avoided...

> +	    handler: 'restoreList',
> +	    parentXType: 'treepanel',
> +	    enableFn: (rec) => !!rec.data['media-set-uuid'],
> +	},
>  	{
>  	    xtype: 'proxmoxButton',
>  	    disabled: true,
> diff --git a/www/tape/window/TapeRestore.js b/www/tape/window/TapeRestore.js
> index 7e4f5cae..560d4812 100644
> --- a/www/tape/window/TapeRestore.js
> +++ b/www/tape/window/TapeRestore.js
> @@ -49,6 +49,10 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
>  		    values.snapshots = me.up('window').list;
>  		}
>  
> +		if (Ext.isString(values.snapshots)) {
> +		    values.snapshots = values.snapshots.split(',');
> +		}
> +
>  		values.store = datastores.join(',');
>  
>  		return values;
> @@ -138,6 +142,23 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
>  		    defaultBindProperty: 'value',
>  		    hidden: true,
>  		},
> +		{
> +		    fieldLabel: gettext('Snapshot Selection'),
> +		    labelWidth: 200,
> +		    cbind: {
> +			hidden: '{!list_snapshots}',
> +		    },
> +		    reference: 'snapshotLabel',
> +		    xtype: 'displayfield',
> +		},
> +		{
> +		    xtype: 'pbsTapeSnapshotGrid',
> +		    reference: 'snapshotGrid',
> +		    name: 'snapshots',
> +		    cbind: {
> +			hidden: '{!list_snapshots}',
> +		    },
> +		},
>  	    ],
>  	},
>      ],
> @@ -186,6 +207,9 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
>  			    datastores[content.store] = true;
>  			}
>  			me.setDataStores(Object.keys(datastores));
> +			let store = me.lookup('snapshotGrid').getStore();
> +			store.setData(response.result.data);
> +			store.sort('snapshot');
>  		    },
>  		    failure: function() {
>  			// ignore failing api call, maybe catalog is missing
> @@ -308,3 +332,104 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
>  	},
>      ],
>  });
> +
> +Ext.define('PBS.TapeManagement.SnapshotGrid', {
> +    extend: 'Ext.grid.Panel',
> +    alias: 'widget.pbsTapeSnapshotGrid',
> +    mixins: ['Ext.form.field.Field'],
> +
> +    getValue: function() {
> +	let me = this;
> +	let snapshots = [];
> +
> +	me.getStore().each((rec) => {
> +	    if (rec.data.include) {
> +		let store = rec.data.store;
> +		let snap = rec.data.snapshot;
> +		snapshots.push(`${store}:${snap}`);
> +	    }
> +	});
> +
> +	return snapshots;
> +    },
> +
> +    setValue: function(value) {
> +	let me = this;
> +	// not implemented
> +	return me;
> +    },
> +
> +    getErrors: function(value) {
> +	let me = this;
> +	let firstSelected = me.getStore().findBy((rec) => !!rec.data.include);
> +
> +	if (firstSelected === -1) {
> +	    me.addCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
> +	    let errorMsg = gettext("Need at least one snapshot");
> +	    me.getActionEl().dom.setAttribute('data-errorqtip', errorMsg);
> +
> +	    return [errorMsg];
> +	}
> +	me.removeCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
> +	me.getActionEl().dom.setAttribute('data-errorqtip', "");
> +	return [];
> +    },
> +
> +    scrollable: true,
> +    height: 200,
> +
> +    viewConfig: {
> +	emptyText: gettext('No Snapshots'),
> +	markDirty: false,
> +    },
> +
> +    tbar: [
> +	{
> +	    fieldLabel: gettext('Filter'),
> +	    xtype: 'textfield',
> +	    isFormField: false,
> +	    listeners: {
> +		change: function(field, value) {
> +		    let me = this;
> +		    let grid = me.up('grid');
> +		    let store = grid.getStore();
> +		    store.clearFilter();
> +		    store.filter({
> +			property: 'snapshot',
> +			anyMatch: true,
> +			caseSensitive: true,
> +			exactMatch: false,
> +			value,
> +		    });
> +		    grid.checkChange();
> +		},
> +	    },
> +	},
> +    ],
> +
> +    store: { data: [] },
> +
> +    columns: [
> +	{
> +	    xtype: 'checkcolumn',
> +	    text: gettext('Include'),
> +	    dataIndex: 'include',
> +	    listeners: {
> +		checkchange: function(cb, value) {
> +		    let grid = this.up('grid');
> +		    grid.checkChange();
> +		},
> +	    },
> +	},
> +	{
> +	    text: gettext('Source Datastore'),
> +	    dataIndex: 'store',
> +	    flex: 1,
> +	},
> +	{
> +	    text: gettext('Snapshot'),
> +	    dataIndex: 'snapshot',
> +	    flex: 4,
> +	},
> +    ],
> +});
> 






More information about the pbs-devel mailing list