public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Dominik Csapak <d.csapak@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH manager v3 04/13] ui: add pending grid
Date: Fri, 15 May 2026 10:44:22 +0200	[thread overview]
Message-ID: <20260515085349.1123127-5-d.csapak@proxmox.com> (raw)
In-Reply-To: <20260515085349.1123127-1-d.csapak@proxmox.com>

A new grid type that gets its data from a PendingObjectGrid and displays
a parsed property string in various columns. Each column shows its own
pending changes so that this can be seen more easily.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/manager6/Makefile            |   1 +
 www/manager6/grid/PendingGrid.js | 134 +++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+)
 create mode 100644 www/manager6/grid/PendingGrid.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 280a9ca6..c2c1dffb 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -100,6 +100,7 @@ JSSRC= 							\
 	grid/FirewallAliases.js				\
 	grid/FirewallOptions.js				\
 	grid/FirewallRules.js				\
+	grid/PendingGrid.js				\
 	grid/PoolMembers.js				\
 	grid/Replication.js				\
 	grid/ResourceGrid.js				\
diff --git a/www/manager6/grid/PendingGrid.js b/www/manager6/grid/PendingGrid.js
new file mode 100644
index 00000000..e2d7b6c3
--- /dev/null
+++ b/www/manager6/grid/PendingGrid.js
@@ -0,0 +1,134 @@
+/* It's main use is to be a 'sub grid' for a 'PendingObjectGrid, for qemu/HardwareView
+ * and lxc/Resources.
+ */
+Ext.define('PVE.grid.PendingGrid', {
+    extend: 'Ext.grid.Panel',
+    alias: 'widget.pvePendingGrid',
+
+    /// the original store of the PendingObjectGrid, used for retrieving data
+    rstore: undefined,
+
+    /// the filter function to filter only relevant data from rstore
+    storeFilter: function (_rec) {
+        return true;
+    },
+
+    /// the parser for the data, should return an object
+    recordParser: function (key, value) {
+        let parsed = PVE.Parser.parsePropertyString(value);
+        return {
+            key,
+            ...parsed,
+        };
+    },
+
+    pendingRenderer: function (originalRenderer) {
+        originalRenderer ??= Ext.htmlEncode;
+        return function (val, mD, rec, rowIdx, colIdx, store, view) {
+            // add a ''pending parameter'
+            let txt = originalRenderer(val, mD, rec, rowIdx, colIdx, store, view, false);
+            let pendingTxt;
+
+            let pending = rec.get('pending');
+            if (pending) {
+                let dataIndex = view.up().getColumns()[colIdx].dataIndex;
+                let value = pending[dataIndex];
+                pendingTxt = originalRenderer(value, mD, rec, rowIdx, colIdx, store, view, true);
+            }
+
+            if (pendingTxt && txt && pendingTxt !== txt) {
+                txt += '<br />';
+                txt += `<span style="color: darkorange">${pendingTxt}</span>`;
+            }
+            if (pending && pendingTxt && (!txt || !rec.data.raw)) {
+                txt = `<span style="color: orange">${pendingTxt}</span>`;
+            }
+            if (pending && !pendingTxt && txt) {
+                txt = `<div style="color: darkorange; text-decoration: line-through;">${txt}</div>`;
+            }
+            if (rec.data.deleted && txt) {
+                txt = `<div style="color: darkorange; text-decoration: line-through;">${txt}</div>`;
+            }
+
+            return txt;
+        };
+    },
+
+    updateData: function () {
+        let me = this;
+        let records = [];
+        let data = me.rstore.getData();
+        (data.getSource() ?? data).each(function (rec) {
+            if (!me.storeFilter(rec)) {
+                return;
+            }
+
+            let key = rec.get('key');
+            let value = rec.get('value');
+            let deleted = rec.get('delete');
+
+            let parsed = me.recordParser(key, value);
+            if (parsed) {
+                parsed.raw = value;
+            }
+
+            let pending = rec.get('pending');
+            let parsedPending;
+            if (pending) {
+                parsedPending = me.recordParser(key, pending);
+                parsedPending.id = key;
+                parsedPending.raw = pending;
+            }
+
+            records.push({
+                ...parsed,
+                id: key,
+                key,
+                pending: parsedPending,
+                deleted,
+            });
+        });
+
+        me.getStore().setData(records);
+    },
+
+    store: {},
+
+    columns: [],
+
+    initComponent: function () {
+        let me = this;
+
+        if (!me.rstore) {
+            throw new 'no rstore defined'();
+        }
+
+        let columns = [];
+
+        // overwrite renderer to be pending
+        //
+        // CAUTION: don't modify the original column objects, rather copy them
+        for (const column of me.columns ?? []) {
+            let originalRenderer = column.renderer;
+            // might be a string (named scope)
+            if (Ext.isString(originalRenderer)) {
+                let renderString = originalRenderer.toString();
+                originalRenderer = function () {
+                    return Ext.callback(renderString, me.scope, arguments, 0, me);
+                };
+            }
+
+            let renderer = me.pendingRenderer(originalRenderer);
+            columns.push({
+                ...column,
+                renderer,
+            });
+        }
+
+        me.columns = columns;
+
+        me.callParent();
+
+        me.mon(me.rstore, 'refresh', me.updateData, me);
+    },
+});
-- 
2.47.3





  parent reply	other threads:[~2026-05-15  8:54 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-15  8:44 [PATCH manager v3 00/13] ui: split out disks and nics into grids Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 01/13] ui: utils: factor out 'media=cdrom' check Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 02/13] ui: factor out the guest key nic regex check Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 03/13] ui: parser: qemu drive: allow '-' in key names Dominik Csapak
2026-05-15  8:44 ` Dominik Csapak [this message]
2026-05-15  8:44 ` [PATCH manager v3 05/13] ui: revert button: add parentXType and reloadCallback Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 06/13] ui: button: add config remove button Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 07/13] ui: qemu: hardware: wrap in container Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 08/13] ui: qemu: introduce hardware disk grid Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 09/13] ui: qemu: introduce hardware net grid Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 10/13] ui: qemu: hardware view: separate disks into own grid Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 11/13] ui: qemu: hardware view: separate nics " Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 12/13] ui: qemu: hardware view: inline edit/remove/revert button in general grid Dominik Csapak
2026-05-15  8:44 ` [PATCH manager v3 13/13] ui: qemu: hardware view: inline 'add efi' menuitem Dominik Csapak
2026-05-15  9:18 ` [PATCH manager v3 00/13] ui: split out disks and nics into grids Dominik Csapak
2026-05-15  9:20   ` Dominik Csapak

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260515085349.1123127-5-d.csapak@proxmox.com \
    --to=d.csapak@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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