From: Hannes Laimer <h.laimer@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [PATCH proxmox-backup v5 1/9] ui: show empty groups
Date: Thu, 19 Mar 2026 17:13:17 +0100 [thread overview]
Message-ID: <20260319161325.206846-2-h.laimer@proxmox.com> (raw)
In-Reply-To: <20260319161325.206846-1-h.laimer@proxmox.com>
Displays groups that do not contain any snapshots, currently deleting
the last snapshot will also remove the parent group. So things like
notes would be lost.
This is needed for cleaning up partially failed moves in a datastore
with a s3 backend as it allows for easy local and s3 cleanup. Without
such a group showing up in the UI the only way to trigger a cleanup in
the s3 storage would be trough our API.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/datastore/Content.js | 62 ++++++++++++++++++++++++++++++----------
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/www/datastore/Content.js b/www/datastore/Content.js
index a2aa1949..981acdc7 100644
--- a/www/datastore/Content.js
+++ b/www/datastore/Content.js
@@ -170,7 +170,8 @@ Ext.define('PBS.DataStoreContent', {
return groups;
},
- updateGroupNotes: async function (view) {
+ loadGroups: async function () {
+ let view = this.getView();
try {
let url = `/api2/extjs/admin/datastore/${view.datastore}/groups`;
if (view.namespace && view.namespace !== '') {
@@ -179,19 +180,24 @@ Ext.define('PBS.DataStoreContent', {
let {
result: { data: groups },
} = await Proxmox.Async.api2({ url });
- let map = {};
- for (const group of groups) {
- map[`${group['backup-type']}/${group['backup-id']}`] = group.comment;
- }
- view.getRootNode().cascade((node) => {
- if (node.data.ty === 'group') {
- let group = `${node.data.backup_type}/${node.data.backup_id}`;
- node.set('comment', map[group], { dirty: false });
- }
- });
+ return groups;
} catch (err) {
console.debug(err);
}
+ return [];
+ },
+
+ updateGroupNotes: function (view, groupList) {
+ let map = {};
+ for (const group of groupList) {
+ map[`${group['backup-type']}/${group['backup-id']}`] = group.comment;
+ }
+ view.getRootNode().cascade((node) => {
+ if (node.data.ty === 'group') {
+ let group = `${node.data.backup_type}/${node.data.backup_id}`;
+ node.set('comment', map[group], { dirty: false });
+ }
+ });
},
loadNamespaceFromSameLevel: async function () {
@@ -215,7 +221,10 @@ Ext.define('PBS.DataStoreContent', {
let me = this;
let view = this.getView();
- let namespaces = await me.loadNamespaceFromSameLevel();
+ let [namespaces, groupList] = await Promise.all([
+ me.loadNamespaceFromSameLevel(),
+ me.loadGroups(),
+ ]);
if (!success) {
// TODO also check error code for != 403 ?
@@ -232,6 +241,29 @@ Ext.define('PBS.DataStoreContent', {
let groups = this.getRecordGroups(records);
+ for (const item of groupList) {
+ let btype = item['backup-type'];
+ let group = btype + '/' + item['backup-id'];
+ if (groups[group] !== undefined) {
+ continue;
+ }
+ let cls = PBS.Utils.get_type_icon_cls(btype);
+ if (cls === '') {
+ continue;
+ }
+ groups[group] = {
+ text: group,
+ leaf: true,
+ iconCls: 'fa ' + cls,
+ expanded: false,
+ backup_type: btype,
+ backup_id: item['backup-id'],
+ comment: item.comment,
+ owner: item.owner,
+ children: [],
+ };
+ }
+
let selected;
let expanded = {};
@@ -399,7 +431,7 @@ Ext.define('PBS.DataStoreContent', {
);
}
- this.updateGroupNotes(view);
+ this.updateGroupNotes(view, groupList);
if (selected !== undefined) {
let selection = view.getRootNode().findChildBy(
@@ -985,7 +1017,7 @@ Ext.define('PBS.DataStoreContent', {
flex: 1,
renderer: (v, meta, record) => {
let data = record.data;
- if (!data || data.leaf || data.root) {
+ if (!data || (data.leaf && data.ty !== 'group') || data.root) {
return '';
}
@@ -1029,7 +1061,7 @@ Ext.define('PBS.DataStoreContent', {
},
dblclick: function (tree, el, row, col, ev, rec) {
let data = rec.data || {};
- if (data.leaf || data.root) {
+ if ((data.leaf && data.ty !== 'group') || data.root) {
return;
}
let view = tree.up();
--
2.47.3
next prev parent reply other threads:[~2026-03-19 16:13 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-19 16:13 [PATCH proxmox-backup v5 0/9] fixes #6195: add support for moving groups and namespaces Hannes Laimer
2026-03-19 16:13 ` Hannes Laimer [this message]
2026-03-19 16:13 ` [PATCH proxmox-backup v5 2/9] datastore: add namespace-level locking Hannes Laimer
2026-03-19 16:13 ` [PATCH proxmox-backup v5 3/9] datastore: add move_group Hannes Laimer
2026-03-19 16:13 ` [PATCH proxmox-backup v5 4/9] datastore: add move_namespace Hannes Laimer
2026-03-19 16:13 ` [PATCH proxmox-backup v5 5/9] api: add PUT endpoint for move_group Hannes Laimer
2026-03-19 16:13 ` [PATCH proxmox-backup v5 6/9] api: add PUT endpoint for move_namespace Hannes Laimer
2026-03-19 16:13 ` [PATCH proxmox-backup v5 7/9] ui: add move group action Hannes Laimer
2026-03-19 16:13 ` [PATCH proxmox-backup v5 8/9] ui: add move namespace action Hannes Laimer
2026-03-19 16:13 ` [PATCH proxmox-backup v5 9/9] cli: add move-namespace and move-group commands Hannes Laimer
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=20260319161325.206846-2-h.laimer@proxmox.com \
--to=h.laimer@proxmox.com \
--cc=pbs-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