public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH manager 0/7] backup edit window improvements
@ 2023-03-06 14:23 Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 1/7] ui: VMSelector: columns customizable Dominik Csapak
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

two bugfixes (the VMSelector changes are necessary for those)
and a refactor of the edit window (i put that patch at the end, because even
with simplifications, it's still ~30 lines more, and i was not sure if that's
worth it)

Dominik Csapak (7):
  ui: VMSelector: columns customizable
  ui: VMSelector: improve {set,get}Value handling with a loading store
  ui: VMSelector: change from filter to load parameters
  ui: VMSelector: correctly change invalid class on en/disable
  fix #4490: ui: add column filters in Backup Job edit window
  fix #4239: ui: show selected but non-existing vmids in backup edit
  ui: BackupEdit: refactor edit window into declarative style

 www/manager6/dc/Backup.js       | 788 +++++++++++++++-----------------
 www/manager6/form/VMSelector.js |  75 ++-
 2 files changed, 423 insertions(+), 440 deletions(-)

-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] [PATCH manager 1/7] ui: VMSelector: columns customizable
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
@ 2023-03-06 14:23 ` Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 2/7] ui: VMSelector: improve {set, get}Value handling with a loading store Dominik Csapak
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

we will reuse this component but don't want to show all columns

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/form/VMSelector.js | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/www/manager6/form/VMSelector.js b/www/manager6/form/VMSelector.js
index 78cd90134..d80a21ec0 100644
--- a/www/manager6/form/VMSelector.js
+++ b/www/manager6/form/VMSelector.js
@@ -22,7 +22,8 @@ Ext.define('PVE.form.VMSelector', {
 	    value: /lxc|qemu/,
 	}],
     },
-    columns: [
+
+    columnsDeclaration: [
 	{
 	    header: 'ID',
 	    dataIndex: 'vmid',
@@ -94,6 +95,9 @@ Ext.define('PVE.form.VMSelector', {
 	},
     ],
 
+    // should be a list of 'dataIndex' values, if 'undefined' all declared columns will be included
+    columnSelection: undefined,
+
     selModel: {
 	selType: 'checkboxmodel',
 	mode: 'SIMPLE',
@@ -155,6 +159,12 @@ Ext.define('PVE.form.VMSelector', {
     initComponent: function() {
 	let me = this;
 
+	let columns = me.columnsDeclaration.filter((column) =>
+	    me.columnSelection ? me.columnSelection.indexOf(column.dataIndex) !== -1 : true,
+	).map((x) => x);
+
+	me.columns = columns;
+
 	me.callParent();
 
 	if (me.nodename) {
-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] [PATCH manager 2/7] ui: VMSelector: improve {set, get}Value handling with a loading store
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 1/7] ui: VMSelector: columns customizable Dominik Csapak
@ 2023-03-06 14:23 ` Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 3/7] ui: VMSelector: change from filter to load parameters Dominik Csapak
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

when we do {set,get}Value during a store load, the store might be empty
or incomplete, so defer the selection after the load and cache the value
for getValue invocations until the store is loaded

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/form/VMSelector.js | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/www/manager6/form/VMSelector.js b/www/manager6/form/VMSelector.js
index d80a21ec0..bb670f930 100644
--- a/www/manager6/form/VMSelector.js
+++ b/www/manager6/form/VMSelector.js
@@ -117,6 +117,9 @@ Ext.define('PVE.form.VMSelector', {
 
     getValue: function() {
 	var me = this;
+	if (me.savedValue !== undefined) {
+	    return me.savedValue;
+	}
 	var sm = me.getSelectionModel();
 	var selection = sm.getSelection();
 	var values = [];
@@ -130,6 +133,20 @@ Ext.define('PVE.form.VMSelector', {
 	return values;
     },
 
+    setValueSelection: function(value) {
+	let me = this;
+
+	let store = me.getStore();
+	let selection = value.map(item => store.findRecord('vmid', item, 0, false, true, true)).filter(r => r);
+
+	let sm = me.getSelectionModel();
+	if (selection.length) {
+	    sm.select(selection);
+	} else {
+	    sm.deselectAll();
+	}
+    },
+
     setValue: function(value) {
 	let me = this;
 	if (!Ext.isArray(value)) {
@@ -137,10 +154,15 @@ Ext.define('PVE.form.VMSelector', {
 	}
 
 	let store = me.getStore();
-	let selection = value.map(item => store.findRecord('vmid', item, 0, false, true, true)).filter(r => r);
-
-	me.getSelectionModel().select(selection);
-
+	if (!store.isLoaded()) {
+	    me.savedValue = value;
+	    store.on('load', function() {
+		me.setValueSelection(value);
+		delete me.savedValue;
+	    }, { single: true });
+	} else {
+	    me.setValueSelection(value);
+	}
 	return me.mixins.field.setValue.call(me, value);
     },
 
-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] [PATCH manager 3/7] ui: VMSelector: change from filter to load parameters
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 1/7] ui: VMSelector: columns customizable Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 2/7] ui: VMSelector: improve {set, get}Value handling with a loading store Dominik Csapak
@ 2023-03-06 14:23 ` Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 4/7] ui: VMSelector: correctly change invalid class on en/disable Dominik Csapak
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

so that we can modify the filters without having to consider filtering
for the type. Note that 'vm' for the 'type' parameter also returns
containers.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/form/VMSelector.js | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/www/manager6/form/VMSelector.js b/www/manager6/form/VMSelector.js
index bb670f930..fb54e4835 100644
--- a/www/manager6/form/VMSelector.js
+++ b/www/manager6/form/VMSelector.js
@@ -15,12 +15,7 @@ Ext.define('PVE.form.VMSelector', {
 
     store: {
 	model: 'PVEResources',
-	autoLoad: true,
 	sorters: 'vmid',
-	filters: [{
-	    property: 'type',
-	    value: /lxc|qemu/,
-	}],
     },
 
     columnsDeclaration: [
@@ -189,6 +184,8 @@ Ext.define('PVE.form.VMSelector', {
 
 	me.callParent();
 
+	me.getStore().load({ params: { type: 'vm' } });
+
 	if (me.nodename) {
 	    me.store.filters.add({
 		property: 'node',
-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] [PATCH manager 4/7] ui: VMSelector: correctly change invalid class on en/disable
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
                   ` (2 preceding siblings ...)
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 3/7] ui: VMSelector: change from filter to load parameters Dominik Csapak
@ 2023-03-06 14:23 ` Dominik Csapak
  2023-03-11 17:34   ` Thomas Lamprecht
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 5/7] fix #4490: ui: add column filters in Backup Job edit window Dominik Csapak
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

since we manually handle the invalid class, we have to manually trigger
that on setDisabled

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/form/VMSelector.js | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/www/manager6/form/VMSelector.js b/www/manager6/form/VMSelector.js
index fb54e4835..399c4e658 100644
--- a/www/manager6/form/VMSelector.js
+++ b/www/manager6/form/VMSelector.js
@@ -140,6 +140,8 @@ Ext.define('PVE.form.VMSelector', {
 	} else {
 	    sm.deselectAll();
 	}
+	// to correctly trigger invalid class
+	me.getErrors();
     },
 
     setValue: function(value) {
@@ -163,7 +165,7 @@ Ext.define('PVE.form.VMSelector', {
 
     getErrors: function(value) {
 	let me = this;
-	if (me.allowBlank === false &&
+	if (!me.isDisabled() && me.allowBlank === false &&
 	    me.getSelectionModel().getCount() === 0) {
 	    me.addBodyCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
 	    return [gettext('No VM selected')];
@@ -173,6 +175,13 @@ Ext.define('PVE.form.VMSelector', {
 	return [];
     },
 
+    setDisabled: function(disabled) {
+	let me = this;
+	let res = me.callParent([disabled]);
+	me.getErrors();
+	return res;
+    },
+
     initComponent: function() {
 	let me = this;
 
-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] [PATCH manager 5/7] fix #4490: ui: add column filters in Backup Job edit window
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
                   ` (3 preceding siblings ...)
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 4/7] ui: VMSelector: correctly change invalid class on en/disable Dominik Csapak
@ 2023-03-06 14:23 ` Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 6/7] fix #4239: ui: show selected but non-existing vmids in backup edit Dominik Csapak
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

by replacing the manual vm grid implementation and reusing the
VMSelector we already have. Since this is a full-fledged form field, we
can drop the complicated selection tracking / reselecting that we did
by saving into a hidden field.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/dc/Backup.js | 130 ++++----------------------------------
 1 file changed, 14 insertions(+), 116 deletions(-)

diff --git a/www/manager6/dc/Backup.js b/www/manager6/dc/Backup.js
index 9d3059841..d5e8bf20c 100644
--- a/www/manager6/dc/Backup.js
+++ b/www/manager6/dc/Backup.js
@@ -18,10 +18,6 @@ Ext.define('PVE.dc.BackupEdit', {
 	    method = 'PUT';
 	}
 
-	let vmidField = Ext.create('Ext.form.field.Hidden', {
-	    name: 'vmid',
-	});
-
 	// 'value' can be assigned a string or an array
 	let selModeField = Ext.create('Proxmox.form.KVComboBox', {
 	    xtype: 'proxmoxKVComboBox',
@@ -36,19 +32,6 @@ Ext.define('PVE.dc.BackupEdit', {
 	    value: '',
 	});
 
-	let sm = Ext.create('Ext.selection.CheckboxModel', {
-	    mode: 'SIMPLE',
-	    listeners: {
-		selectionchange: function(model, selected) {
-		    let sel = selected.map(record => record.data.vmid);
-		    // to avoid endless recursion suspend the vmidField change
-		    // event temporary as it calls us again
-		    vmidField.suspendEvent('change');
-		    vmidField.setValue(sel);
-		    vmidField.resumeEvent('change');
-		},
-	    },
-	});
 
 	let storagesel = Ext.create('PVE.form.StorageSelector', {
 	    fieldLabel: gettext('Storage'),
@@ -72,66 +55,27 @@ Ext.define('PVE.dc.BackupEdit', {
 	    },
 	});
 
-	let store = new Ext.data.Store({
-	    model: 'PVEResources',
-	    sorters: {
-		property: 'vmid',
-		direction: 'ASC',
-	    },
-	});
-
-	let vmgrid = Ext.createWidget('grid', {
-	    store: store,
-	    border: true,
+	let vmgrid = Ext.createWidget('vmselector', {
 	    height: 300,
-	    selModel: sm,
+	    name: 'vmid',
 	    disabled: true,
-	    columns: [
-		{
-		    header: 'ID',
-		    dataIndex: 'vmid',
-		    width: 60,
-		},
-		{
-		    header: gettext('Node'),
-		    dataIndex: 'node',
-		},
-		{
-		    header: gettext('Status'),
-		    dataIndex: 'uptime',
-		    renderer: function(value) {
-			if (value) {
-			    return Proxmox.Utils.runningText;
-			} else {
-			    return Proxmox.Utils.stoppedText;
-			}
-		    },
-		},
-		{
-		    header: gettext('Name'),
-		    dataIndex: 'name',
-		    flex: 1,
-		},
-		{
-		    header: gettext('Type'),
-		    dataIndex: 'type',
-		},
-	    ],
+	    allowBlank: false,
+	    columnSelection: ['vmid', 'node', 'status', 'name', 'type'],
 	});
 
 	let selectPoolMembers = function(poolid) {
 	    if (!poolid) {
 		return;
 	    }
-	    sm.deselectAll(true);
-	    store.filter([
+	    vmgrid.selModel.deselectAll(true);
+	    vmgrid.getStore().filter([
 		{
 		    id: 'poolFilter',
 		    property: 'pool',
 		    value: poolid,
 		},
 	    ]);
-	    sm.selectAll(true);
+	    vmgrid.selModel.selectAll(true);
 	};
 
 	let selPool = Ext.create('PVE.form.PoolSelector', {
@@ -157,12 +101,13 @@ Ext.define('PVE.dc.BackupEdit', {
 		change: function(f, value) {
 		    storagesel.setNodename(value);
 		    let mode = selModeField.getValue();
+		    let store = vmgrid.getStore();
 		    store.clearFilter();
 		    store.filterBy(function(rec) {
 			return !value || rec.get('node') === value;
 		    });
 		    if (mode === 'all') {
-			sm.selectAll(true);
+			vmgrid.selModel.selectAll(true);
 		    }
 		    if (mode === 'pool') {
 			selectPoolMembers(selPool.value);
@@ -218,7 +163,6 @@ Ext.define('PVE.dc.BackupEdit', {
 		defaultValue: 1,
 		checked: true,
 	    },
-	    vmidField,
 	];
 
 	let ipanel = Ext.create('Proxmox.panel.InputPanel', {
@@ -282,37 +226,17 @@ Ext.define('PVE.dc.BackupEdit', {
 	    },
 	});
 
-	let update_vmid_selection = function(list, mode) {
-	    if (mode !== 'all' && mode !== 'pool') {
-		sm.deselectAll(true);
-		if (list) {
-		    Ext.Array.each(list.split(','), function(vmid) {
-			var rec = store.findRecord('vmid', vmid, 0, false, true, true);
-			if (rec) {
-			    sm.select(rec, true);
-			}
-		    });
-		}
-	    }
-	};
-
-	vmidField.on('change', function(f, value) {
-	    let mode = selModeField.getValue();
-	    update_vmid_selection(value, mode);
-	});
-
 	selModeField.on('change', function(f, value, oldValue) {
 	    if (oldValue === 'pool') {
-		store.removeFilter('poolFilter');
+		vmgrid.getStore().removeFilter('poolFilter');
 	    }
 
-	    if (oldValue === 'all') {
-		sm.deselectAll(true);
-		vmidField.setValue('');
+	    if (oldValue === 'all' || oldValue === 'pool') {
+		vmgrid.selModel.deselectAll(true);
 	    }
 
 	    if (value === 'all') {
-		sm.selectAll(true);
+		vmgrid.selModel.selectAll(true);
 		vmgrid.setDisabled(true);
 	    } else {
 		vmgrid.setDisabled(false);
@@ -320,7 +244,7 @@ Ext.define('PVE.dc.BackupEdit', {
 
 	    if (value === 'pool') {
 		vmgrid.setDisabled(true);
-		vmidField.setValue('');
+		vmgrid.selModel.deselectAll(true);
 		selPool.setVisible(true);
 		selPool.setDisabled(false);
 		selPool.allowBlank = false;
@@ -330,32 +254,8 @@ Ext.define('PVE.dc.BackupEdit', {
 		selPool.setDisabled(true);
 		selPool.allowBlank = true;
 	    }
-	    let list = vmidField.getValue();
-	    update_vmid_selection(list, value);
 	});
 
-	let reload = function() {
-	    store.load({
-		params: {
-		    type: 'vm',
-		},
-		callback: function() {
-		    let node = nodesel.getValue();
-		    store.clearFilter();
-		    store.filterBy(rec => !node || node.length === 0 || rec.get('node') === node);
-		    let list = vmidField.getValue();
-		    let mode = selModeField.getValue();
-		    if (mode === 'all') {
-			sm.selectAll(true);
-		    } else if (mode === 'pool') {
-			selectPoolMembers(selPool.value);
-		    } else {
-			update_vmid_selection(list, mode);
-		    }
-		},
-	    });
-	};
-
 	Ext.applyIf(me, {
 	    subject: gettext("Backup Job"),
 	    url: url,
@@ -481,8 +381,6 @@ Ext.define('PVE.dc.BackupEdit', {
 		},
 	    });
 	}
-
-	reload();
     },
 });
 
-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] [PATCH manager 6/7] fix #4239: ui: show selected but non-existing vmids in backup edit
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
                   ` (4 preceding siblings ...)
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 5/7] fix #4490: ui: add column filters in Backup Job edit window Dominik Csapak
@ 2023-03-06 14:23 ` Dominik Csapak
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 7/7] ui: BackupEdit: refactor edit window into declarative style Dominik Csapak
  2023-03-14 10:37 ` [pve-devel] applied: [PATCH manager 0/7] backup edit window improvements Thomas Lamprecht
  7 siblings, 0 replies; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

by adding records manually when using 'setValue' on a vmselector.
It'll show up normally but have an 'unknown' nodename, and no type/status/etc.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/form/VMSelector.js | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/www/manager6/form/VMSelector.js b/www/manager6/form/VMSelector.js
index 399c4e658..e9eb5dbaa 100644
--- a/www/manager6/form/VMSelector.js
+++ b/www/manager6/form/VMSelector.js
@@ -132,7 +132,22 @@ Ext.define('PVE.form.VMSelector', {
 	let me = this;
 
 	let store = me.getStore();
-	let selection = value.map(item => store.findRecord('vmid', item, 0, false, true, true)).filter(r => r);
+	let notFound = [];
+	let selection = value.map(item => {
+	    let found = store.findRecord('vmid', item, 0, false, true, true);
+	    if (!found) {
+		notFound.push(item);
+	    }
+	    return found;
+	}).filter(r => r);
+
+	for (const vmid of notFound) {
+	    let rec = store.add({
+		vmid,
+		node: 'unknown',
+	    });
+	    selection.push(rec[0]);
+	}
 
 	let sm = me.getSelectionModel();
 	if (selection.length) {
-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] [PATCH manager 7/7] ui: BackupEdit: refactor edit window into declarative style
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
                   ` (5 preceding siblings ...)
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 6/7] fix #4239: ui: show selected but non-existing vmids in backup edit Dominik Csapak
@ 2023-03-06 14:23 ` Dominik Csapak
  2023-03-14 10:37 ` [pve-devel] applied: [PATCH manager 0/7] backup edit window improvements Thomas Lamprecht
  7 siblings, 0 replies; 10+ messages in thread
From: Dominik Csapak @ 2023-03-06 14:23 UTC (permalink / raw)
  To: pve-devel

simplifies some things, e.g. en/disabling the grid and pool selector
while refactoring, cleanup up some smaller things like nested if/else
paths, unnecessary splitting of the deprecated 'dow' parameter, etc.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/dc/Backup.js | 690 ++++++++++++++++++++------------------
 1 file changed, 361 insertions(+), 329 deletions(-)

diff --git a/www/manager6/dc/Backup.js b/www/manager6/dc/Backup.js
index d5e8bf20c..d0046177a 100644
--- a/www/manager6/dc/Backup.js
+++ b/www/manager6/dc/Backup.js
@@ -2,72 +2,107 @@ Ext.define('PVE.dc.BackupEdit', {
     extend: 'Proxmox.window.Edit',
     alias: ['widget.pveDcBackupEdit'],
 
+    mixins: ['Proxmox.Mixin.CBind'],
+
     defaultFocus: undefined,
 
-    initComponent: function() {
-	let me = this;
+    subject: gettext("Backup Job"),
+    bodyPadding: 0,
 
-	me.isCreate = !me.jobid;
+    url: '/api2/extjs/cluster/backup',
+    method: 'POST',
+    isCreate: true,
 
-	let url, method;
-	if (me.isCreate) {
-	    url = '/api2/extjs/cluster/backup';
-	    method = 'POST';
-	} else {
-	    url = '/api2/extjs/cluster/backup/' + me.jobid;
-	    method = 'PUT';
+    cbindData: function() {
+	let me = this;
+	if (me.jobid) {
+	    me.isCreate = false;
+	    me.method = 'PUT';
+	    me.url += `/${me.jobid}`;
 	}
+	return {};
+    },
 
-	// 'value' can be assigned a string or an array
-	let selModeField = Ext.create('Proxmox.form.KVComboBox', {
-	    xtype: 'proxmoxKVComboBox',
-	    comboItems: [
-		['include', gettext('Include selected VMs')],
-		['all', gettext('All')],
-		['exclude', gettext('Exclude selected VMs')],
-		['pool', gettext('Pool based')],
-	    ],
-	    fieldLabel: gettext('Selection mode'),
-	    name: 'selMode',
-	    value: '',
-	});
+    controller: {
+	xclass: 'Ext.app.ViewController',
 
+	onGetValues: function(values) {
+	    let me = this;
+	    let isCreate = me.getView().isCreate;
+	    if (!values.node) {
+		if (!isCreate) {
+		    Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' });
+		}
+		delete values.node;
+	    }
 
-	let storagesel = Ext.create('PVE.form.StorageSelector', {
-	    fieldLabel: gettext('Storage'),
-	    clusterView: true,
-	    storageContent: 'backup',
-	    allowBlank: false,
-	    name: 'storage',
-	    listeners: {
-		change: function(f, v) {
-		    let store = f.getStore();
-		    let rec = store.findRecord('storage', v, 0, false, true, true);
-		    let compressionSelector = me.down('pveCompressionSelector');
-
-		    if (rec && rec.data && rec.data.type === 'pbs') {
-			compressionSelector.setValue('zstd');
-			compressionSelector.setDisabled(true);
-		    } else if (!compressionSelector.getEditable()) {
-			compressionSelector.setDisabled(false);
-		    }
-		},
-	    },
-	});
+	    if (!values.id && isCreate) {
+		values.id = 'backup-' + Ext.data.identifier.Uuid.Global.generate().slice(0, 13);
+	    }
 
-	let vmgrid = Ext.createWidget('vmselector', {
-	    height: 300,
-	    name: 'vmid',
-	    disabled: true,
-	    allowBlank: false,
-	    columnSelection: ['vmid', 'node', 'status', 'name', 'type'],
-	});
+	    let selMode = values.selMode;
+	    delete values.selMode;
+
+	    if (selMode === 'all') {
+		values.all = 1;
+		values.exclude = '';
+		delete values.vmid;
+	    } else if (selMode === 'exclude') {
+		values.all = 1;
+		values.exclude = values.vmid;
+		delete values.vmid;
+	    } else if (selMode === 'pool') {
+		delete values.vmid;
+	    }
+
+	    if (selMode !== 'pool') {
+		delete values.pool;
+	    }
+	    return values;
+	},
+
+	nodeChange: function(f, value) {
+	    let me = this;
+	    me.lookup('storageSelector').setNodename(value);
+	    let vmgrid = me.lookup('vmgrid');
+	    let store = vmgrid.getStore();
+
+	    store.clearFilter();
+	    store.filterBy(function(rec) {
+		return !value || rec.get('node') === value;
+	    });
+
+	    let mode = me.lookup('modeSelector').getValue();
+	    if (mode === 'all') {
+		vmgrid.selModel.selectAll(true);
+	    }
+	    if (mode === 'pool') {
+		me.selectPoolMembers();
+	    }
+	},
+
+	storageChange: function(f, v) {
+	    let me = this;
+	    let rec = f.getStore().findRecord('storage', v, 0, false, true, true);
+	    let compressionSelector = me.lookup('compressionSelector');
+
+	    if (rec?.data?.type === 'pbs') {
+		compressionSelector.setValue('zstd');
+		compressionSelector.setDisabled(true);
+	    } else if (!compressionSelector.getEditable()) {
+		compressionSelector.setDisabled(false);
+	    }
+	},
+
+	selectPoolMembers: function() {
+	    let me = this;
+	    let vmgrid = me.lookup('vmgrid');
+	    let poolid = me.lookup('poolSelector').getValue();
 
-	let selectPoolMembers = function(poolid) {
+	    vmgrid.getSelectionModel().deselectAll(true);
 	    if (!poolid) {
 		return;
 	    }
-	    vmgrid.selModel.deselectAll(true);
 	    vmgrid.getStore().filter([
 		{
 		    id: 'poolFilter',
@@ -76,312 +111,309 @@ Ext.define('PVE.dc.BackupEdit', {
 		},
 	    ]);
 	    vmgrid.selModel.selectAll(true);
-	};
+	},
 
-	let selPool = Ext.create('PVE.form.PoolSelector', {
-	    fieldLabel: gettext('Pool to backup'),
-	    hidden: true,
-	    allowBlank: true,
-	    name: 'pool',
-	    listeners: {
-		change: function(selpool, newValue, oldValue) {
-		    selectPoolMembers(newValue);
-		},
-	    },
-	});
+	modeChange: function(f, value, oldValue) {
+	    let me = this;
+	    let vmgrid = me.lookup('vmgrid');
+	    vmgrid.getStore().removeFilter('poolFilter');
 
-	let nodesel = Ext.create('PVE.form.NodeSelector', {
-	    name: 'node',
-	    fieldLabel: gettext('Node'),
-	    allowBlank: true,
-	    editable: true,
-	    autoSelect: false,
-	    emptyText: '-- ' + gettext('All') + ' --',
-	    listeners: {
-		change: function(f, value) {
-		    storagesel.setNodename(value);
-		    let mode = selModeField.getValue();
-		    let store = vmgrid.getStore();
-		    store.clearFilter();
-		    store.filterBy(function(rec) {
-			return !value || rec.get('node') === value;
-		    });
-		    if (mode === 'all') {
-			vmgrid.selModel.selectAll(true);
-		    }
-		    if (mode === 'pool') {
-			selectPoolMembers(selPool.value);
-		    }
-		},
-	    },
-	});
+	    if (oldValue === 'all' && value !== 'all') {
+		vmgrid.getSelectionModel().deselectAll(true);
+	    }
 
-	let column1 = [
-	    nodesel,
-	    storagesel,
-	    {
-		xtype: 'pveCalendarEvent',
-		fieldLabel: gettext('Schedule'),
-		allowBlank: false,
-		name: 'schedule',
-	    },
-	    selModeField,
-	    selPool,
-	];
-
-	let column2 = [
-	    {
-		xtype: 'textfield',
-		fieldLabel: gettext('Send email to'),
-		name: 'mailto',
-	    },
-	    {
-		xtype: 'pveEmailNotificationSelector',
-		fieldLabel: gettext('Email'),
-		name: 'mailnotification',
-		deleteEmpty: !me.isCreate,
-		value: me.isCreate ? 'always' : '',
-	    },
-	    {
-		xtype: 'pveCompressionSelector',
-		fieldLabel: gettext('Compression'),
-		name: 'compress',
-		deleteEmpty: !me.isCreate,
-		value: 'zstd',
-	    },
-	    {
-		xtype: 'pveBackupModeSelector',
-		fieldLabel: gettext('Mode'),
-		value: 'snapshot',
-		name: 'mode',
-	    },
-	    {
-		xtype: 'proxmoxcheckbox',
-		fieldLabel: gettext('Enable'),
-		name: 'enabled',
-		uncheckedValue: 0,
-		defaultValue: 1,
-		checked: true,
-	    },
-	];
+	    if (value === 'all') {
+		vmgrid.getSelectionModel().selectAll(true);
+	    }
 
-	let ipanel = Ext.create('Proxmox.panel.InputPanel', {
-	    onlineHelp: 'chapter_vzdump',
-	    column1: column1,
-	    column2: column2,
-	    columnB: [
-		{
-		    xtype: 'proxmoxtextfield',
-		    name: 'comment',
-		    fieldLabel: gettext('Job Comment'),
-		    deleteEmpty: !me.isCreate,
-		    autoEl: {
-			tag: 'div',
-			'data-qtip': gettext('Description of the job'),
-		    },
-		},
-		vmgrid,
-	    ],
-	    advancedColumn1: [
-		{
-		    xtype: 'proxmoxcheckbox',
-		    fieldLabel: gettext('Repeat missed'),
-		    name: 'repeat-missed',
-		    uncheckedValue: 0,
-		    defaultValue: 0,
-		    deleteDefaultValue: !me.isCreate,
-		},
-	    ],
-	    onGetValues: function(values) {
-		if (!values.node) {
-		    if (!me.isCreate) {
-			Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' });
-		    }
-		    delete values.node;
-		}
+	    if (value === 'pool') {
+		me.selectPoolMembers();
+	    }
+	},
 
-		if (!values.id && me.isCreate) {
-		    values.id = 'backup-' + Ext.data.identifier.Uuid.Global.generate().slice(0, 13);
-		}
+	init: function(view) {
+	    let me = this;
+	    if (view.isCreate) {
+		me.lookup('modeSelector').setValue('include');
+	    } else {
+		view.load({
+		    success: function(response, _options) {
+			let data = response.result.data;
 
-		let selMode = values.selMode;
-		delete values.selMode;
-
-		if (selMode === 'all') {
-		    values.all = 1;
-		    values.exclude = '';
-		    delete values.vmid;
-		} else if (selMode === 'exclude') {
-		    values.all = 1;
-		    values.exclude = values.vmid;
-		    delete values.vmid;
-		} else if (selMode === 'pool') {
-		    delete values.vmid;
-		}
+			if (data.exclude) {
+			    data.vmid = data.exclude;
+			    data.selMode = 'exclude';
+			} else if (data.all) {
+			    data.vmid = '';
+			    data.selMode = 'all';
+			} else if (data.pool) {
+			    data.selMode = 'pool';
+			    data.selPool = data.pool;
+			} else {
+			    data.selMode = 'include';
+			}
 
-		if (selMode !== 'pool') {
-		    delete values.pool;
-		}
-		return values;
-	    },
-	});
+			me.getViewModel().set('selMode', data.selMode);
 
-	selModeField.on('change', function(f, value, oldValue) {
-	    if (oldValue === 'pool') {
-		vmgrid.getStore().removeFilter('poolFilter');
-	    }
+			if (data['prune-backups']) {
+			    Object.assign(data, data['prune-backups']);
+			    delete data['prune-backups'];
+			} else if (data.maxfiles !== undefined) {
+			    if (data.maxfiles > 0) {
+				data['keep-last'] = data.maxfiles;
+			    } else {
+				data['keep-all'] = 1;
+			    }
+			    delete data.maxfiles;
+			}
 
-	    if (oldValue === 'all' || oldValue === 'pool') {
-		vmgrid.selModel.deselectAll(true);
-	    }
+			if (data['notes-template']) {
+			    data['notes-template'] =
+				PVE.Utils.unEscapeNotesTemplate(data['notes-template']);
+			}
 
-	    if (value === 'all') {
-		vmgrid.selModel.selectAll(true);
-		vmgrid.setDisabled(true);
-	    } else {
-		vmgrid.setDisabled(false);
+			view.setValues(data);
+		    },
+		});
 	    }
+	},
+    },
 
-	    if (value === 'pool') {
-		vmgrid.setDisabled(true);
-		vmgrid.selModel.deselectAll(true);
-		selPool.setVisible(true);
-		selPool.setDisabled(false);
-		selPool.allowBlank = false;
-		selectPoolMembers(selPool.value);
-	    } else {
-		selPool.setVisible(false);
-		selPool.setDisabled(true);
-		selPool.allowBlank = true;
-	    }
-	});
+    viewModel: {
+	data: {
+	    selMode: 'include',
+	},
 
-	Ext.applyIf(me, {
-	    subject: gettext("Backup Job"),
-	    url: url,
-	    method: method,
-	    bodyPadding: 0,
+	formulas: {
+	    poolMode: (get) => get('selMode') === 'pool',
+	    disableVMSelection: (get) => get('selMode') !== 'include' && get('selMode') !== 'exclude',
+	},
+    },
+
+    items: [
+	{
+	    xtype: 'tabpanel',
+	    region: 'center',
+	    layout: 'fit',
+	    bodyPadding: 10,
 	    items: [
 		{
-		    xtype: 'tabpanel',
+		    xtype: 'container',
+		    title: gettext('General'),
 		    region: 'center',
-		    layout: 'fit',
-		    bodyPadding: 10,
+		    layout: {
+			type: 'vbox',
+			align: 'stretch',
+		    },
 		    items: [
-			{
-			    xtype: 'container',
-			    title: gettext('General'),
-			    region: 'center',
-			    layout: {
-				type: 'vbox',
-				align: 'stretch',
-			    },
-			    items: [
-				ipanel,
-			    ],
-			},
-			{
-			    xtype: 'pveBackupJobPrunePanel',
-			    title: gettext('Retention'),
-			    isCreate: me.isCreate,
-			    keepAllDefaultForCreate: false,
-			    showPBSHint: false,
-			    fallbackHintHtml: gettext('Without any keep option, the storage\'s configuration or node\'s vzdump.conf is used as fallback'),
-			},
 			{
 			    xtype: 'inputpanel',
-			    title: gettext('Note Template'),
-			    region: 'center',
-			    layout: {
-				type: 'vbox',
-				align: 'stretch',
-			    },
-			    onGetValues: function(values) {
-				if (values['notes-template']) {
-				    values['notes-template'] = PVE.Utils.escapeNotesTemplate(
-					values['notes-template']);
-				}
-				return values;
-			    },
-			    items: [
+			    onlineHelp: 'chapter_vzdump',
+			    column1: [
+				{
+				    xtype: 'pveNodeSelector',
+				    name: 'node',
+				    fieldLabel: gettext('Node'),
+				    allowBlank: true,
+				    editable: true,
+				    autoSelect: false,
+				    emptyText: '-- ' + gettext('All') + ' --',
+				    listeners: {
+					change: 'nodeChange',
+				    },
+				},
+				{
+				    xtype: 'pveStorageSelector',
+				    reference: 'storageSelector',
+				    fieldLabel: gettext('Storage'),
+				    clusterView: true,
+				    storageContent: 'backup',
+				    allowBlank: false,
+				    name: 'storage',
+				    listeners: {
+					change: 'storageChange',
+				    },
+				},
+				{
+				    xtype: 'pveCalendarEvent',
+				    fieldLabel: gettext('Schedule'),
+				    allowBlank: false,
+				    name: 'schedule',
+				},
+				{
+				    xtype: 'proxmoxKVComboBox',
+				    reference: 'modeSelector',
+				    comboItems: [
+					['include', gettext('Include selected VMs')],
+					['all', gettext('All')],
+					['exclude', gettext('Exclude selected VMs')],
+					['pool', gettext('Pool based')],
+				    ],
+				    fieldLabel: gettext('Selection mode'),
+				    name: 'selMode',
+				    value: '',
+				    bind: {
+					value: '{selMode}',
+				    },
+				    listeners: {
+					change: 'modeChange',
+				    },
+				},
+				{
+				    xtype: 'pvePoolSelector',
+				    reference: 'poolSelector',
+				    fieldLabel: gettext('Pool to backup'),
+				    hidden: true,
+				    allowBlank: false,
+				    name: 'pool',
+				    listeners: {
+					change: 'selectPoolMembers',
+				    },
+				    bind: {
+					hidden: '{!poolMode}',
+					disabled: '{!poolMode}',
+				    },
+				},
+			    ],
+			    column2: [
+				{
+				    xtype: 'textfield',
+				    fieldLabel: gettext('Send email to'),
+				    name: 'mailto',
+				},
+				{
+				    xtype: 'pveEmailNotificationSelector',
+				    fieldLabel: gettext('Email'),
+				    name: 'mailnotification',
+				    cbind: {
+					value: (get) => get('isCreate') ? 'always' : '',
+					deleteEmpty: '{!isCreate}',
+				    },
+				},
+				{
+				    xtype: 'pveCompressionSelector',
+				    reference: 'compressionSelector',
+				    fieldLabel: gettext('Compression'),
+				    name: 'compress',
+				    cbind: {
+					deleteEmpty: '{!isCreate}',
+				    },
+				    value: 'zstd',
+				},
+				{
+				    xtype: 'pveBackupModeSelector',
+				    fieldLabel: gettext('Mode'),
+				    value: 'snapshot',
+				    name: 'mode',
+				},
+				{
+				    xtype: 'proxmoxcheckbox',
+				    fieldLabel: gettext('Enable'),
+				    name: 'enabled',
+				    uncheckedValue: 0,
+				    defaultValue: 1,
+				    checked: true,
+				},
+			    ],
+			    columnB: [
+				{
+				    xtype: 'proxmoxtextfield',
+				    name: 'comment',
+				    fieldLabel: gettext('Job Comment'),
+				    cbind: {
+					deleteEmpty: '{!isCreate}',
+				    },
+				    autoEl: {
+					tag: 'div',
+					'data-qtip': gettext('Description of the job'),
+				    },
+				},
 				{
-				    xtype: 'textarea',
-				    name: 'notes-template',
-				    fieldLabel: gettext('Backup Notes'),
-                                    height: 100,
-				    maxLength: 512,
-				    deleteEmpty: !me.isCreate,
-				    value: me.isCreate ? '{{guestname}}' : undefined,
+				    xtype: 'vmselector',
+				    reference: 'vmgrid',
+				    height: 300,
+				    name: 'vmid',
+				    disabled: true,
+				    allowBlank: false,
+				    columnSelection: ['vmid', 'node', 'status', 'name', 'type'],
+				    bind: {
+					disabled: '{disableVMSelection}',
+				    },
 				},
+			    ],
+			    advancedColumn1: [
 				{
-				    xtype: 'box',
-				    style: {
-					margin: '8px 0px',
-					'line-height': '1.5em',
+				    xtype: 'proxmoxcheckbox',
+				    fieldLabel: gettext('Repeat missed'),
+				    name: 'repeat-missed',
+				    uncheckedValue: 0,
+				    defaultValue: 0,
+				    cbind: {
+					deleteDefaultValue: '{!isCreate}',
 				    },
-				    html: gettext('The notes are added to each backup created by this job.')
-				      + '<br>'
-				      + Ext.String.format(
-					gettext('Possible template variables are: {0}'),
-					PVE.Utils.notesTemplateVars.map(v => `<code>{{${v}}}</code>`).join(', '),
-				    ),
 				},
 			    ],
+			    onGetValues: function(values) {
+				return this.up('window').getController().onGetValues(values);
+			    },
 			},
 		    ],
 		},
-	    ],
-
-	});
-
-	me.callParent();
-
-	if (me.isCreate) {
-	    selModeField.setValue('include');
-	} else {
-            me.load({
-		success: function(response, options) {
-		    let data = response.result.data;
-
-		    data.dow = (data.dow || '').split(',');
-
-		    if (data.all || data.exclude) {
-			if (data.exclude) {
-			    data.vmid = data.exclude;
-			    data.selMode = 'exclude';
-			} else {
-			    data.vmid = '';
-			    data.selMode = 'all';
-			}
-		    } else if (data.pool) {
-			data.selMode = 'pool';
-			data.selPool = data.pool;
-		    } else {
-			data.selMode = 'include';
-		    }
-
-		    if (data['prune-backups']) {
-			Object.assign(data, data['prune-backups']);
-			delete data['prune-backups'];
-		    } else if (data.maxfiles !== undefined) {
-			if (data.maxfiles > 0) {
-			    data['keep-last'] = data.maxfiles;
-			} else {
-			    data['keep-all'] = 1;
+		{
+		    xtype: 'pveBackupJobPrunePanel',
+		    title: gettext('Retention'),
+		    cbind: {
+			isCreate: '{isCreate}',
+		    },
+		    keepAllDefaultForCreate: false,
+		    showPBSHint: false,
+		    fallbackHintHtml: gettext('Without any keep option, the storage\'s configuration or node\'s vzdump.conf is used as fallback'),
+		},
+		{
+		    xtype: 'inputpanel',
+		    title: gettext('Note Template'),
+		    region: 'center',
+		    layout: {
+			type: 'vbox',
+			align: 'stretch',
+		    },
+		    onGetValues: function(values) {
+			if (values['notes-template']) {
+			    values['notes-template'] =
+				PVE.Utils.escapeNotesTemplate(values['notes-template']);
 			}
-			delete data.maxfiles;
-		    }
-
-		    if (data['notes-template']) {
-			data['notes-template'] = PVE.Utils.unEscapeNotesTemplate(
-			    data['notes-template']);
-		    }
-
-		    me.setValues(data);
+			return values;
+		    },
+		    items: [
+			{
+			    xtype: 'textarea',
+			    name: 'notes-template',
+			    fieldLabel: gettext('Backup Notes'),
+			    height: 100,
+			    maxLength: 512,
+			    cbind: {
+				deleteEmpty: '{!isCreate}',
+				value: (get) => get('isCreate') ? '{{guestname}}' : undefined,
+			    },
+			},
+			{
+			    xtype: 'box',
+			    style: {
+				margin: '8px 0px',
+				'line-height': '1.5em',
+			    },
+			    html: gettext('The notes are added to each backup created by this job.')
+				+ '<br>'
+				+ Ext.String.format(
+				    gettext('Possible template variables are: {0}'),
+				    PVE.Utils.notesTemplateVars.map(v => `<code>{{${v}}}</code>`).join(', '),
+				),
+			},
+		    ],
 		},
-	    });
-	}
-    },
+	    ],
+	},
+    ],
 });
 
 Ext.define('PVE.dc.BackupView', {
-- 
2.30.2





^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [pve-devel] [PATCH manager 4/7] ui: VMSelector: correctly change invalid class on en/disable
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 4/7] ui: VMSelector: correctly change invalid class on en/disable Dominik Csapak
@ 2023-03-11 17:34   ` Thomas Lamprecht
  0 siblings, 0 replies; 10+ messages in thread
From: Thomas Lamprecht @ 2023-03-11 17:34 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak

Am 06/03/2023 um 15:23 schrieb Dominik Csapak:
> since we manually handle the invalid class, we have to manually trigger
> that on setDisabled
> 
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  www/manager6/form/VMSelector.js | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/www/manager6/form/VMSelector.js b/www/manager6/form/VMSelector.js
> index fb54e4835..399c4e658 100644
> --- a/www/manager6/form/VMSelector.js
> +++ b/www/manager6/form/VMSelector.js
> @@ -140,6 +140,8 @@ Ext.define('PVE.form.VMSelector', {
>  	} else {
>  	    sm.deselectAll();
>  	}
> +	// to correctly trigger invalid class
> +	me.getErrors();
>      },
>  
>      setValue: function(value) {
> @@ -163,7 +165,7 @@ Ext.define('PVE.form.VMSelector', {
>  
>      getErrors: function(value) {
>  	let me = this;
> -	if (me.allowBlank === false &&
> +	if (!me.isDisabled() && me.allowBlank === false &&

while at it you could have changed the odd `allowBlank === false` to just `!allowBlank` ;-)




^ permalink raw reply	[flat|nested] 10+ messages in thread

* [pve-devel] applied: [PATCH manager 0/7] backup edit window improvements
  2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
                   ` (6 preceding siblings ...)
  2023-03-06 14:23 ` [pve-devel] [PATCH manager 7/7] ui: BackupEdit: refactor edit window into declarative style Dominik Csapak
@ 2023-03-14 10:37 ` Thomas Lamprecht
  7 siblings, 0 replies; 10+ messages in thread
From: Thomas Lamprecht @ 2023-03-14 10:37 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak

Am 06/03/2023 um 15:23 schrieb Dominik Csapak:
> two bugfixes (the VMSelector changes are necessary for those)
> and a refactor of the edit window (i put that patch at the end, because even
> with simplifications, it's still ~30 lines more, and i was not sure if that's
> worth it)
> 
> Dominik Csapak (7):
>   ui: VMSelector: columns customizable
>   ui: VMSelector: improve {set,get}Value handling with a loading store
>   ui: VMSelector: change from filter to load parameters
>   ui: VMSelector: correctly change invalid class on en/disable
>   fix #4490: ui: add column filters in Backup Job edit window
>   fix #4239: ui: show selected but non-existing vmids in backup edit
>   ui: BackupEdit: refactor edit window into declarative style
> 
>  www/manager6/dc/Backup.js       | 788 +++++++++++++++-----------------
>  www/manager6/form/VMSelector.js |  75 ++-
>  2 files changed, 423 insertions(+), 440 deletions(-)
> 


applied series, albeit I wanted to look a bit more closely onto it but my finger
slipped, anyhow, the core idea is good and any regression we can fix, so thanks!




^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2023-03-14 10:37 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-06 14:23 [pve-devel] [PATCH manager 0/7] backup edit window improvements Dominik Csapak
2023-03-06 14:23 ` [pve-devel] [PATCH manager 1/7] ui: VMSelector: columns customizable Dominik Csapak
2023-03-06 14:23 ` [pve-devel] [PATCH manager 2/7] ui: VMSelector: improve {set, get}Value handling with a loading store Dominik Csapak
2023-03-06 14:23 ` [pve-devel] [PATCH manager 3/7] ui: VMSelector: change from filter to load parameters Dominik Csapak
2023-03-06 14:23 ` [pve-devel] [PATCH manager 4/7] ui: VMSelector: correctly change invalid class on en/disable Dominik Csapak
2023-03-11 17:34   ` Thomas Lamprecht
2023-03-06 14:23 ` [pve-devel] [PATCH manager 5/7] fix #4490: ui: add column filters in Backup Job edit window Dominik Csapak
2023-03-06 14:23 ` [pve-devel] [PATCH manager 6/7] fix #4239: ui: show selected but non-existing vmids in backup edit Dominik Csapak
2023-03-06 14:23 ` [pve-devel] [PATCH manager 7/7] ui: BackupEdit: refactor edit window into declarative style Dominik Csapak
2023-03-14 10:37 ` [pve-devel] applied: [PATCH manager 0/7] backup edit window improvements Thomas Lamprecht

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal