[pbs-devel] [PATCH proxmox-backup] ui: tape/BackupOverview: add 'restore partial' button
Dominik Csapak
d.csapak at proxmox.com
Wed May 12 08:22:19 CEST 2021
On 5/11/21 18:17, Thomas Lamprecht wrote:
> 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.
yes you're right, that'll much better, though i would not do a radio
button, but maybe only a checkbox '[] Select snapshots' which
enables the selection grid?
we could then open that window with a preselected(and filtered?) list
when the user clicks on the 'restore single snapshot' button?
does that make sense?
>
> 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).
mhmm sounds good, i'd do an action column where each media set and
snapshot gets a restore button, and for single snapshots i preselect
like i mentioned above.. does that make sense?
(we could probably do a restore button on every level in the tree,
and preselect accordingly, though i am unsure if that does not
get too confusing and if its even helpful...)
>
>>
>> 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