public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH docs/wt/manager] implement tagview
@ 2023-08-02 10:53 Dominik Csapak
  2023-08-02 10:53 ` [pve-devel] [PATCH docs 1/1] gui: add anchor for tags chapter Dominik Csapak
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-08-02 10:53 UTC (permalink / raw)
  To: pve-devel

this adds a 'tagview' to the web ui, organizing guests by their tags
(for details see the pve-manager patch)

pve-docs:

Dominik Csapak (1):
  gui: add anchor for tags chapter

 pve-gui.adoc | 1 +
 1 file changed, 1 insertion(+)

proxmox-widget-toolkit:

Dominik Csapak (1):
  css: add some conditions to the tag classes for the tag view

 src/css/ext6-pmx.css | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

pve-manager:

Dominik Csapak (1):
  ui: implement 'Tag View' for the resource tree

 www/manager6/Makefile             |  1 +
 www/manager6/Workspace.js         |  1 +
 www/manager6/form/ViewSelector.js | 32 +++++++++++++++++++
 www/manager6/grid/ResourceGrid.js |  2 +-
 www/manager6/panel/TagConfig.js   |  6 ++++
 www/manager6/tree/ResourceTree.js | 53 ++++++++++++++++++++++++++++---
 6 files changed, 89 insertions(+), 6 deletions(-)
 create mode 100644 www/manager6/panel/TagConfig.js

-- 
2.30.2





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

* [pve-devel] [PATCH docs 1/1] gui: add anchor for tags chapter
  2023-08-02 10:53 [pve-devel] [PATCH docs/wt/manager] implement tagview Dominik Csapak
@ 2023-08-02 10:53 ` Dominik Csapak
  2023-08-02 10:53 ` [pve-devel] [PATCH widget-toolkit 1/1] css: add some conditions to the tag classes for the tag view Dominik Csapak
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-08-02 10:53 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 pve-gui.adoc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pve-gui.adoc b/pve-gui.adoc
index e0fc873..0dd6646 100644
--- a/pve-gui.adoc
+++ b/pve-gui.adoc
@@ -379,6 +379,7 @@ and the corresponding interfaces for each menu item on the right.
 * *Permissions:* manage the permissions for the pool.
 
 
+[[gui_tags]]
 Tags
 ----
 
-- 
2.30.2





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

* [pve-devel] [PATCH widget-toolkit 1/1] css: add some conditions to the tag classes for the tag view
  2023-08-02 10:53 [pve-devel] [PATCH docs/wt/manager] implement tagview Dominik Csapak
  2023-08-02 10:53 ` [pve-devel] [PATCH docs 1/1] gui: add anchor for tags chapter Dominik Csapak
@ 2023-08-02 10:53 ` Dominik Csapak
  2023-08-02 10:53 ` [pve-devel] [PATCH manager 1/1] ui: implement 'Tag View' for the resource tree Dominik Csapak
  2023-08-23  8:16 ` [pve-devel] [PATCH docs/wt/manager] implement tagview Lukas Wagner
  3 siblings, 0 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-08-02 10:53 UTC (permalink / raw)
  To: pve-devel

in the tag view, we have a custom 'full' style in a place where we
can have another tagstyle class above. to compensate for that, we have
to add another condition to those styles, namely that there is not the
'proxmox-tags-full' in between.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/css/ext6-pmx.css | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/css/ext6-pmx.css b/src/css/ext6-pmx.css
index 2ffd2a8..e088ae1 100644
--- a/src/css/ext6-pmx.css
+++ b/src/css/ext6-pmx.css
@@ -15,7 +15,9 @@
 }
 
 .proxmox-tags-full .x-grid-cell-inner-treecolumn .proxmox-tag-light,
-.proxmox-tags-full .x-grid-cell-inner-treecolumn .proxmox-tag-dark {
+.proxmox-tags-full .x-grid-cell-inner-treecolumn .proxmox-tag-dark,
+.x-grid-cell-inner-treecolumn .proxmox-tags-full .proxmox-tag-light,
+.x-grid-cell-inner-treecolumn .proxmox-tags-full .proxmox-tag-dark {
     display: inherit;
 }
 
@@ -25,8 +27,10 @@
 }
 
 
-.proxmox-tags-circle .proxmox-tag-light,
-.proxmox-tags-circle .proxmox-tag-dark {
+.proxmox-tags-circle :not(span.proxmox-tags-full) > .proxmox-tag-light,
+.proxmox-tags-circle :not(span.proxmox-tags-full) > .proxmox-tag-dark,
+.proxmox-tags-circle > .proxmox-tag-light,
+.proxmox-tags-circle > .proxmox-tag-dark {
     margin: 0px 1px;
     position: relative;
     top: 2px;
@@ -38,13 +42,17 @@
     overflow: hidden;
 }
 
-.proxmox-tags-none .proxmox-tag-light,
-.proxmox-tags-none .proxmox-tag-dark {
+.proxmox-tags-none :not(span.proxmox-tags-full) > .proxmox-tag-light,
+.proxmox-tags-none :not(span.proxmox-tags-full) > .proxmox-tag-dark,
+.proxmox-tags-none > .proxmox-tag-light,
+.proxmox-tags-none > .proxmox-tag-dark {
     display: none;
 }
 
-.proxmox-tags-dense .proxmox-tag-light,
-.proxmox-tags-dense .proxmox-tag-dark {
+.proxmox-tags-dense :not(span.proxmox-tags-full) > .proxmox-tag-light,
+.proxmox-tags-dense :not(span.proxmox-tags-full) > .proxmox-tag-dark,
+.proxmox-tags-dense > .proxmox-tag-light,
+.proxmox-tags-dense > .proxmox-tag-dark {
     width: 6px;
     margin-right: 1px;
     display: inline-block;
-- 
2.30.2





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

* [pve-devel] [PATCH manager 1/1] ui: implement 'Tag View' for the resource tree
  2023-08-02 10:53 [pve-devel] [PATCH docs/wt/manager] implement tagview Dominik Csapak
  2023-08-02 10:53 ` [pve-devel] [PATCH docs 1/1] gui: add anchor for tags chapter Dominik Csapak
  2023-08-02 10:53 ` [pve-devel] [PATCH widget-toolkit 1/1] css: add some conditions to the tag classes for the tag view Dominik Csapak
@ 2023-08-02 10:53 ` Dominik Csapak
  2023-08-23  8:16 ` [pve-devel] [PATCH docs/wt/manager] implement tagview Lukas Wagner
  3 siblings, 0 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-08-02 10:53 UTC (permalink / raw)
  To: pve-devel

and keep the functionality in ResourceTree as generic as possible.

We achieve this by having an 'itemMap' function that can split one item
from the store into multiple to add to the tree.

for the updates, we have to have an 'idMapFn' (to get the original id
back)

we also have to modify how the move checks work a bit, since we only
want to move the items when the tags changed only in the tagview case

in the ResourceGrid we have to get the id a bit differently since we now
have 'virtual' ids for the entries tag contain the tag (which can't be
found in the resource store)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
this depends on the docs (onlineHelp ref) and widget toolkit (css)
patches

there is probably some optimization potential in how we handle the
overrides, but i tried to keep it as generic as possible so that
we can extend it easily if we want to.

 www/manager6/Makefile             |  1 +
 www/manager6/Workspace.js         |  1 +
 www/manager6/form/ViewSelector.js | 32 +++++++++++++++++++
 www/manager6/grid/ResourceGrid.js |  2 +-
 www/manager6/panel/TagConfig.js   |  6 ++++
 www/manager6/tree/ResourceTree.js | 53 ++++++++++++++++++++++++++++---
 6 files changed, 89 insertions(+), 6 deletions(-)
 create mode 100644 www/manager6/panel/TagConfig.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 7ec9d7a5..fdb38905 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -102,6 +102,7 @@ JSSRC= 							\
 	panel/GuestSummary.js				\
 	panel/TemplateStatusView.js			\
 	panel/MultiDiskEdit.js				\
+	panel/TagConfig.js				\
 	tree/ResourceTree.js				\
 	tree/SnapshotTree.js				\
 	tree/ResourceMapTree.js				\
diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js
index 18d574b7..a46fa75d 100644
--- a/www/manager6/Workspace.js
+++ b/www/manager6/Workspace.js
@@ -247,6 +247,7 @@ Ext.define('PVE.StdWorkspace', {
 			    storage: 'PVE.storage.Browser',
 			    sdn: 'PVE.sdn.Browser',
 			    pool: 'pvePoolConfig',
+			    tag: 'pveTagConfig',
 			};
 			PVE.curSelectedNode = treeNode;
 			me.setContent({
diff --git a/www/manager6/form/ViewSelector.js b/www/manager6/form/ViewSelector.js
index e25547c4..647399ef 100644
--- a/www/manager6/form/ViewSelector.js
+++ b/www/manager6/form/ViewSelector.js
@@ -32,6 +32,38 @@ Ext.define('PVE.form.ViewSelector', {
 		// Pool View only lists VMs and Containers
 		filterfn: ({ data }) => data.type === 'qemu' || data.type === 'lxc' || data.type === 'pool',
 	    },
+	    tags: {
+		text: gettext('Tag View'),
+		groups: ['tag'],
+		filterfn: ({ data }) => data.type === 'qemu' || data.type === 'lxc',
+		groupRenderer: function(info) {
+		    let tag = PVE.Utils.renderTags(info.tag, PVE.UIOptions.tagOverrides);
+		    return `<span class="proxmox-tags-full">${tag}</span>`;
+		},
+		idMapFn: function(id) {
+		    let [realId, _tag] = id.split('-');
+		    return realId;
+		},
+		itemMap: function(item) {
+		    let tags = (item.data.tags ?? '').split(/[;, ]/);
+		    if (tags.length === 1 && tags[0] === '') {
+			return item;
+		    }
+		    let items = [];
+		    for (const tag of tags) {
+			let id = `${item.data.id}-${tag}`;
+			let info = Ext.apply({ leaf: true }, item.data);
+			info.tag = tag;
+			info.realId = info.id;
+			info.id = id;
+			items.push(Ext.create('Ext.data.TreeModel', info));
+		    }
+		    return items;
+		},
+		attrMoveChecks: {
+		    tag: (newitem, olditem) => newitem.data.tags !== olditem.data.tags,
+		},
+	    },
 	};
 	let groupdef = Object.entries(default_views).map(([name, config]) => [name, config.text]);
 
diff --git a/www/manager6/grid/ResourceGrid.js b/www/manager6/grid/ResourceGrid.js
index 9376bcc2..b212e9e9 100644
--- a/www/manager6/grid/ResourceGrid.js
+++ b/www/manager6/grid/ResourceGrid.js
@@ -44,7 +44,7 @@ Ext.define('PVE.grid.ResourceGrid', {
 		    return;
 		}
 		for (let child of node.childNodes) {
-		    let orgNode = rstore.data.get(child.data.id);
+		    let orgNode = rstore.data.get(child.data.realId ?? child.data.id);
 		    if (orgNode) {
 			if ((!filterfn || filterfn(child)) && (!textfilter || textfilterMatch(child))) {
 			    nodeidx[child.data.id] = orgNode;
diff --git a/www/manager6/panel/TagConfig.js b/www/manager6/panel/TagConfig.js
new file mode 100644
index 00000000..203c47c2
--- /dev/null
+++ b/www/manager6/panel/TagConfig.js
@@ -0,0 +1,6 @@
+Ext.define('PVE.panel.TagConfig', {
+    extend: 'PVE.panel.Config',
+    alias: 'widget.pveTagConfig',
+
+    onlineHelp: 'gui_tags',
+});
diff --git a/www/manager6/tree/ResourceTree.js b/www/manager6/tree/ResourceTree.js
index 54c6403d..b88e9027 100644
--- a/www/manager6/tree/ResourceTree.js
+++ b/www/manager6/tree/ResourceTree.js
@@ -37,6 +37,9 @@ Ext.define('PVE.tree.ResourceTree', {
 	    template: {
 		iconCls: 'fa fa-file-o',
 	    },
+	    tag: {
+		iconCls: 'fa fa-tag',
+	    },
 	},
     },
 
@@ -161,12 +164,15 @@ Ext.define('PVE.tree.ResourceTree', {
 	me.setToolTip(info);
 
 	if (info.groupbyid) {
-	    info.text = info.groupbyid;
-	    if (info.type === 'type') {
+	    if (me.viewFilter.groupRenderer) {
+		info.text = me.viewFilter.groupRenderer(info);
+	    } else if (info.type === 'type') {
 		let defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid];
 		if (defaults && defaults.text) {
 		    info.text = defaults.text;
 		}
+	    } else {
+		info.text = info.groupbyid;
 	    }
 	}
 	let child = Ext.create('PVETree', info);
@@ -278,6 +284,9 @@ Ext.define('PVE.tree.ResourceTree', {
 
 	    let groups = me.viewFilter.groups || [];
 	    // explicitly check for node/template, as those are not always grouping attributes
+	    let attrMoveChecks = me.viewFilter.attrMoveChecks ?? {};
+	    let idMapFn = me.viewFilter.idMapFn ?? Ext.identityFn;
+
 	    // also check for name for when the tree is sorted by name
 	    let moveCheckAttrs = groups.concat(['node', 'template', 'name']);
 	    let filterfn = me.viewFilter.filterfn;
@@ -287,13 +296,20 @@ Ext.define('PVE.tree.ResourceTree', {
 	    // remove vanished or moved items and update changed items in-place
 	    for (const [key, olditem] of Object.entries(index)) {
 		// getById() use find(), which is slow (ExtJS4 DP5)
-		let item = rstore.data.get(olditem.data.id);
+		let oldid = olditem.data.id;
+		let id = idMapFn(olditem.data.id);
+		let item = rstore.data.get(id);
 
 		let changed = sorting_changed, moved = sorting_changed;
 		if (item) {
 		    // test if any grouping attributes changed, catches migrated tree-nodes in server view too
 		    for (const attr of moveCheckAttrs) {
-			if (item.data[attr] !== olditem.data[attr]) {
+			if (attrMoveChecks[attr]) {
+			    if (attrMoveChecks[attr](olditem, item)) {
+				moved = true;
+				break;
+			    }
+			} else if (item.data[attr] !== olditem.data[attr]) {
 			    moved = true;
 			    break;
 			}
@@ -313,6 +329,9 @@ Ext.define('PVE.tree.ResourceTree', {
 		    olditem.beginEdit();
 		    let info = olditem.data;
 		    Ext.apply(info, item.data);
+		    if (info.id !== oldid) {
+			info.id = oldid;
+		    }
 		    me.setIconCls(info);
 		    me.setText(info);
 		    me.setToolTip(info);
@@ -330,10 +349,15 @@ Ext.define('PVE.tree.ResourceTree', {
 		    // store events are suspended, so remove the item manually
 		    store.remove(olditem);
 		    parentNode.removeChild(olditem, true);
+		    if (parentNode.childNodes.length < 1 && parentNode.parentNode) {
+			let grandParent = parentNode.parentNode;
+			grandParent.removeChild(parentNode, true);
+		    }
 		}
 	    }
 
-	    rstore.each(function(item) { // add new items
+	    let items = rstore.getData().items.flatMap(me.viewFilter.itemMap ?? Ext.identityFn);
+	    items.forEach(function(item) { // add new items
 		let olditem = index[item.data.id];
 		if (olditem) {
 		    return;
@@ -469,6 +493,25 @@ Ext.define('PVE.tree.ResourceTree', {
 
 	rstore.on("load", updateTree);
 	rstore.startUpdate();
+
+	if (me.viewFilter.groupRenderer) {
+	    me.mon(Ext.GlobalEvents, 'loadedUiOptions', () => {
+		me.store.getRootNode().cascadeBy({
+		    before: function(node) {
+			if (node.data.groupbyid) {
+			    node.beginEdit();
+			    let info = node.data;
+			    me.setIconCls(info);
+			    me.setText(info);
+			    me.setToolTip(info);
+			    info.text = me.viewFilter.groupRenderer(info);
+			    node.commit();
+			}
+			return true;
+		    },
+		});
+	    });
+	}
     },
 
 });
-- 
2.30.2





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

* Re: [pve-devel] [PATCH docs/wt/manager] implement tagview
  2023-08-02 10:53 [pve-devel] [PATCH docs/wt/manager] implement tagview Dominik Csapak
                   ` (2 preceding siblings ...)
  2023-08-02 10:53 ` [pve-devel] [PATCH manager 1/1] ui: implement 'Tag View' for the resource tree Dominik Csapak
@ 2023-08-23  8:16 ` Lukas Wagner
  3 siblings, 0 replies; 5+ messages in thread
From: Lukas Wagner @ 2023-08-23  8:16 UTC (permalink / raw)
  To: Proxmox VE development discussion, Dominik Csapak

On 8/2/23 12:53, Dominik Csapak wrote:
> this adds a 'tagview' to the web ui, organizing guests by their tags
> (for details see the pve-manager patch)

Gave this a quick test on the respective latest master branches. Seems 
to work as advertised. Consider this:

Tested-by: Lukas Wagner <l.wagner@proxmox.com>

-- 
- Lukas




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

end of thread, other threads:[~2023-08-23  8:16 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-02 10:53 [pve-devel] [PATCH docs/wt/manager] implement tagview Dominik Csapak
2023-08-02 10:53 ` [pve-devel] [PATCH docs 1/1] gui: add anchor for tags chapter Dominik Csapak
2023-08-02 10:53 ` [pve-devel] [PATCH widget-toolkit 1/1] css: add some conditions to the tag classes for the tag view Dominik Csapak
2023-08-02 10:53 ` [pve-devel] [PATCH manager 1/1] ui: implement 'Tag View' for the resource tree Dominik Csapak
2023-08-23  8:16 ` [pve-devel] [PATCH docs/wt/manager] implement tagview Lukas Wagner

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