public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Hanreich <s.hanreich@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-manager v6 19/24] ui: sdn: prefix list: adapt to changed api structure
Date: Fri,  8 May 2026 18:31:28 +0200	[thread overview]
Message-ID: <20260508163134.481912-20-s.hanreich@proxmox.com> (raw)
In-Reply-To: <20260508163134.481912-1-s.hanreich@proxmox.com>

From: Dominik Csapak <d.csapak@proxmox.com>

instead of having to parse out the prefix list entries from the overall
prefix list GET api call, the entries are now queried below:

/sdn/cluster/prefix-list/{id}/entries

so the entries grid can be simplified to use it's own store + url.
This makes it possible to move the stores out of the viewmodel and into
the respective grid views.

Also use proxmoxStdRemoveButtons here to simplify the removal code.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
[SH: fixed wrong url in edit window, made sequence number optional and
added empty text]
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 www/manager6/sdn/PrefixListPanel.js | 279 ++++++++++------------------
 1 file changed, 96 insertions(+), 183 deletions(-)

diff --git a/www/manager6/sdn/PrefixListPanel.js b/www/manager6/sdn/PrefixListPanel.js
index cdb860f4f..0e5cc11b8 100644
--- a/www/manager6/sdn/PrefixListPanel.js
+++ b/www/manager6/sdn/PrefixListPanel.js
@@ -1,16 +1,11 @@
 Ext.define('PVE.sdn.PrefixList', {
     extend: 'Ext.data.Model',
-    fields: ['id', 'entries', 'pending'],
-
-    getId: function () {
-        let me = this;
-        return me.data.pending?.[me.idProperty] ?? me.data[me.idProperty];
-    },
+    fields: ['id', 'state'],
 });
 
 Ext.define('PVE.sdn.PrefixListEntry', {
     extend: 'Ext.data.Model',
-    fields: ['id', 'action', 'prefix', 'le', 'ge', 'pending'],
+    fields: ['id', 'action', 'seq', 'prefix', 'le', 'ge', 'pending'],
 });
 
 Ext.define('PVE.sdn.EditPrefixListWindow', {
@@ -45,10 +40,11 @@ Ext.define('PVE.sdn.EditPrefixListWindow', {
 
 Ext.define('PVE.sdn.EditPrefixListEntryWindow', {
     extend: 'Proxmox.window.Edit',
+    mixins: ['Proxmox.Mixin.CBind'],
 
     subject: gettext('Prefix List Entry'),
 
-    url: '/cluster/sdn/prefix-lists',
+    baseUrl: '/cluster/sdn/prefix-lists',
 
     config: {
         entry: null,
@@ -57,6 +53,15 @@ Ext.define('PVE.sdn.EditPrefixListEntryWindow', {
     isCreate: false,
 
     items: [
+        {
+            xtype: 'proxmoxintegerfield',
+            name: 'seq',
+            fieldLabel: gettext('Sequence Nr.'),
+            emptyText: gettext('autogenerated'),
+            cbind: {
+                deleteEmpty: '{!isCreate}',
+            }
+        },
         {
             xtype: 'proxmoxKVComboBox',
             fieldLabel: gettext('Action'),
@@ -88,6 +93,17 @@ Ext.define('PVE.sdn.EditPrefixListEntryWindow', {
     initComponent: function () {
         let me = this;
         me.method = me.isCreate ? 'POST' : 'PUT';
+
+        if (!me.prefixList) {
+            throw new 'no prefixList given'();
+        }
+
+        if (me.entry) {
+            me.url = `${me.baseUrl}/${me.prefixList}/entries/${me.entry.seq}`;
+        } else {
+            me.url = `${me.baseUrl}/${me.prefixList}/entries`;
+        }
+
         me.callParent();
 
         me.setValues(me.getEntry());
@@ -108,19 +124,28 @@ Ext.define('PVE.sdn.PrefixListView', {
         },
         {
             text: gettext('Remove'),
-            xtype: 'button',
-            handler: 'removePrefixList',
-            bind: {
-                disabled: '{!prefixListGrid.selection}',
-            },
+            xtype: 'proxmoxStdRemoveButton',
+            baseurl: '/cluster/sdn/prefix-lists/',
+            dangerous: true,
+            callback: 'reloadPrefixList',
         },
+        '->',
         {
             text: gettext('Reload'),
             xtype: 'button',
-            handler: 'reload',
+            handler: 'reloadPrefixList',
         },
     ],
 
+    store: {
+        autoLoad: true,
+        model: 'PVE.sdn.PrefixList',
+        proxy: {
+            type: 'proxmox',
+            url: '/api2/json/cluster/sdn/prefix-lists?pending=1',
+        },
+    },
+
     columns: [
         {
             text: gettext('Name'),
@@ -139,6 +164,12 @@ Ext.define('PVE.sdn.PrefixListView', {
             },
         },
     ],
+
+    initComponent: function () {
+        let me = this;
+        me.callParent();
+        Proxmox.Utils.monStoreErrors(me, me.getStore());
+    },
 });
 
 Ext.define('PVE.sdn.PrefixListEntriesView', {
@@ -151,30 +182,22 @@ Ext.define('PVE.sdn.PrefixListEntriesView', {
         prefixList: null,
     },
 
-    viewConfig: {
-        plugins: [
-            {
-                ptype: 'gridviewdragdrop',
-            },
-        ],
-    },
-
     listeners: {
-        drop: 'saveEntries',
         itemdblclick: 'editPrefixListEntry',
     },
 
+    store: {
+        model: 'PVE.sdn.PrefixListEntry',
+        proxy: {
+            type: 'proxmox',
+        },
+    },
+
     columns: [
         {
-            width: 40,
-            resizable: false,
-            sortable: false,
-            hideable: false,
-            menuDisabled: true,
-            renderer: function (value, metaData, record, rowIdx, colIdx) {
-                metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
-                return "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>";
-            },
+            text: gettext('Sequence Nr.'),
+            dataIndex: 'seq',
+            flex: 1,
         },
         {
             text: gettext('Action'),
@@ -209,21 +232,32 @@ Ext.define('PVE.sdn.PrefixListEntriesView', {
         },
         {
             text: gettext('Edit'),
-            xtype: 'button',
+            xtype: 'proxmoxButton',
+            disabled: true,
             handler: 'editPrefixListEntry',
-            bind: {
-                disabled: '{!prefixListEntriesGrid.selection}',
-            },
         },
         {
             text: gettext('Remove'),
-            xtype: 'button',
-            handler: 'removePrefixListEntry',
-            bind: {
-                disabled: '{!prefixListEntriesGrid.selection}',
+            xtype: 'proxmoxStdRemoveButton',
+            customConfirmationMessage: gettext(
+                'Are you sure you want to remove entry with sequence {0}',
+            ),
+            getRecordName: (rec) => rec.data.seq,
+            getUrl: function (rec) {
+                let grid = this.up('grid');
+                let prefixList = grid.prefixList;
+                let id = prefixList.getId();
+                let seq = rec.data.seq;
+                return `/cluster/sdn/prefix-lists/${id}/entries/${seq}`;
             },
         },
     ],
+
+    initComponent: function () {
+        let me = this;
+        me.callParent();
+        Proxmox.Utils.monStoreErrors(me, me.getStore());
+    },
 });
 
 Ext.define('PVE.sdn.PrefixListPanel', {
@@ -233,30 +267,6 @@ Ext.define('PVE.sdn.PrefixListPanel', {
     emptyText: gettext('No prefix list configured'),
 
     viewModel: {
-        stores: {
-            prefixLists: {
-                autoLoad: true,
-                model: 'PVE.sdn.PrefixList',
-                proxy: {
-                    type: 'proxmox',
-                    url: '/api2/json/cluster/sdn/prefix-lists?pending=1',
-                },
-            },
-            prefixListEntries: {
-                model: 'PVE.sdn.PrefixListEntry',
-                proxy: {
-                    type: 'proxmox',
-                    reader: {
-                        transform: {
-                            fn: function (response) {
-                                let entries = response.data.entries ?? [];
-                                return entries.map(PVE.Parser.parsePropertyString);
-                            },
-                        },
-                    },
-                },
-            },
-        },
         formulas: {
             entryGridEmptyText: function (get) {
                 let selection = get('prefixListGrid.selection');
@@ -269,64 +279,24 @@ Ext.define('PVE.sdn.PrefixListPanel', {
     },
 
     controller: {
-        reload: function () {
+        reloadPrefixList: function () {
             let me = this;
-
-            let prefixList = me.getViewModel().get('prefixListGrid.selection');
-
-            me.getViewModel()
-                .getStore('prefixLists')
-                .load((records, _operation, success) => {
-                    if (!success || !prefixList) {
-                        return;
-                    }
-
-                    let newPrefixList = records.find((record) => {
-                        return record.getId() === prefixList.getId();
-                    });
-
-                    me.lookupReference('prefixListGrid').setSelection(newPrefixList);
-                });
+            me.lookup('prefixListGrid').getStore().load();
+            // reset entries grid
+            let entriesGrid = me.lookup('prefixListEntriesGrid');
+            entriesGrid.getStore().setData([]);
+            Proxmox.Utils.setErrorMask(entriesGrid, false);
         },
-        saveEntries: function () {
+        reloadPrefixEntries: function () {
             let me = this;
-
-            let prefixList = me.getViewModel().get('prefixListGrid.selection');
-
-            let entries = me
-                .getViewModel()
-                .getStore('prefixListEntries')
-                .getData()
-                .items.map((item) => {
-                    let data = item.data;
-                    delete data.id;
-
-                    return PVE.Parser.printPropertyString(data);
-                });
-
-            let params = {};
-
-            if (entries.length > 0) {
-                params.entries = entries;
-            } else {
-                params = { delete: ['entries'] };
-            }
-
-            Proxmox.Async.api2({
-                url: `/api2/extjs/cluster/sdn/prefix-lists/${prefixList.getId()}`,
-                params,
-                method: 'PUT',
-            })
-                .catch(Proxmox.Utils.alertResponseFailure)
-                .finally(() => {
-                    me.reload(prefixList);
-                });
+            me.lookup('prefixListEntriesGrid').getStore().load();
         },
         selectPrefixList: function (gridPanel, record, index, options) {
             let me = this;
 
-            let url = `/api2/extjs/cluster/sdn/prefix-lists/${record.getId()}`;
-            let entryStore = me.getViewModel().getStore('prefixListEntries');
+            let prefixEntriesGrid = me.lookup('prefixListEntriesGrid');
+            let entryStore = prefixEntriesGrid.getStore();
+            let url = `/api2/json/cluster/sdn/prefix-lists/${record.getId()}/entries`;
 
             entryStore.getProxy().setUrl(url);
             entryStore.load();
@@ -338,56 +308,28 @@ Ext.define('PVE.sdn.PrefixListPanel', {
                 autoShow: true,
                 isCreate: true,
                 listeners: {
-                    close: () => me.reload(),
+                    close: () => me.reloadPrefixList(),
                 },
             });
         },
-        removePrefixList: function () {
+        addPrefixListEntry: function () {
             let me = this;
             let prefixList = me.getViewModel().get('prefixListGrid.selection');
 
-            Ext.Msg.show({
-                title: gettext('Confirm'),
-                icon: Ext.Msg.WARNING,
-                message: Ext.String.format(gettext('Remove prefix list "{0}"?'), prefixList.getId()),
-                buttons: Ext.Msg.YESNO,
-                defaultFocus: 'no',
-                callback: function (btn) {
-                    if (btn !== 'yes') {
-                        return;
-                    }
-
-                    Proxmox.Async.api2({
-                        url: `/api2/extjs/cluster/sdn/prefix-lists/${prefixList.getId()}`,
-                        method: 'DELETE',
-                    })
-                        .catch(Proxmox.Utils.alertResponseFailure)
-                        .finally(() => {
-                            me.reload(prefixList);
-                        });
-                },
-            });
-        },
-        addPrefixListEntry: function () {
-            let panel = this;
-
             Ext.create('PVE.sdn.EditPrefixListEntryWindow', {
                 autoShow: true,
                 isCreate: true,
-                submit: function () {
-                    let me = this;
-
-                    panel.getViewModel().getStore('prefixListEntries').add(me.getValues());
-                    panel.saveEntries();
-
-                    me.close();
+                prefixList: prefixList.getId(),
+                listeners: {
+                    close: () => me.reloadPrefixEntries(),
                 },
             });
         },
         editPrefixListEntry: function () {
-            let panel = this;
+            let me = this;
 
-            let entry = panel.getViewModel().get('prefixListEntriesGrid.selection');
+            let entry = me.getViewModel().get('prefixListEntriesGrid.selection');
+            let prefixList = me.getViewModel().get('prefixListGrid.selection');
 
             if (!entry) {
                 console.warn('no prefix list entry selected!');
@@ -397,35 +339,10 @@ Ext.define('PVE.sdn.PrefixListPanel', {
             Ext.create('PVE.sdn.EditPrefixListEntryWindow', {
                 autoShow: true,
                 isCreate: false,
+                prefixList: prefixList.getId(),
                 entry: entry.data,
-                submit: function () {
-                    let me = this;
-                    entry.set(me.getValues());
-
-                    panel.saveEntries();
-
-                    me.close();
-                },
-            });
-        },
-        removePrefixListEntry: function () {
-            let me = this;
-
-            let entry = me.getViewModel().get('prefixListEntriesGrid.selection');
-
-            Ext.Msg.show({
-                title: gettext('Confirm'),
-                icon: Ext.Msg.WARNING,
-                message: gettext('Remove prefix list entry?'),
-                buttons: Ext.Msg.YESNO,
-                defaultFocus: 'no',
-                callback: function (btn) {
-                    if (btn !== 'yes') {
-                        return;
-                    }
-
-                    me.getViewModel().getStore('prefixListEntries').remove(entry);
-                    me.saveEntries();
+                listeners: {
+                    close: () => me.reloadPrefixEntries(),
                 },
             });
         },
@@ -441,9 +358,6 @@ Ext.define('PVE.sdn.PrefixListPanel', {
             border: false,
             split: true,
             reference: 'prefixListGrid',
-            bind: {
-                store: '{prefixLists}',
-            },
             listeners: {
                 select: 'selectPrefixList',
             },
@@ -454,7 +368,6 @@ Ext.define('PVE.sdn.PrefixListPanel', {
             border: false,
             bind: {
                 prefixList: '{prefixListGrid.selection}',
-                store: '{prefixListEntries}',
                 emptyText: '{entryGridEmptyText}',
             },
             reference: 'prefixListEntriesGrid',
-- 
2.47.3





  parent reply	other threads:[~2026-05-08 16:33 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-08 16:31 [PATCH manager/network/proxmox{-ve-rs,-perl-rs} v6 00/24] Add support for route maps / prefix lists to SDN Stefan Hanreich
2026-05-08 16:31 ` [PATCH proxmox-ve-rs v6 01/24] sdn: prefix lists: refactor section config and api format Stefan Hanreich
2026-05-08 16:31 ` [PATCH proxmox-ve-rs v6 02/24] prefix lists: implement validation for prefix lists Stefan Hanreich
2026-05-08 16:31 ` [PATCH proxmox-perl-rs v6 03/24] sdn: prefix lists: refactor existing API endpoint Stefan Hanreich
2026-05-08 16:31 ` [PATCH proxmox-perl-rs v6 04/24] sdn: prefix lists: add crud methods for prefix list entries Stefan Hanreich
2026-05-08 16:31 ` [PATCH proxmox-perl-rs v6 05/24] sdn: prefix lists: validate prefix lists Stefan Hanreich
2026-05-08 16:31 ` [PATCH proxmox-perl-rs v6 06/24] sdn: route maps: add route map list method Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-network v6 07/24] api: refactor route map api structure Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-network v6 08/24] api: refactor prefix list " Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 09/24] ui: sdn: add route map selector Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 10/24] ui: sdn: add prefix list selector Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 11/24] ui: sdn: add panel for managing prefix lists Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 12/24] ui: sdn: add panel for managing route map entries Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 13/24] ui: sdn: bgp controller: allow configuring route maps Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 14/24] ui: sdn: evpn " Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 15/24] ui: sdn: openfabric: add route filter Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 16/24] ui: sdn: ospf: add route filter setting Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 17/24] ui: sdn: prefix list: add missing subjects Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 18/24] sdn: do not fail rendering record data if pending property is missing Stefan Hanreich
2026-05-08 16:31 ` Stefan Hanreich [this message]
2026-05-08 16:31 ` [PATCH pve-manager v6 20/24] ui: sdn: route maps: adapt to new route map api structure Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 21/24] ui: sdn: prefix lists: route maps: use integerfields for numbers Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 22/24] ui: sdn: prefix list panel: reload data on deleting prefix list entry Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 23/24] ui: prefix list panel: delete empty le and get properties Stefan Hanreich
2026-05-08 16:31 ` [PATCH pve-manager v6 24/24] ui: prefix list entry panel: make prefix required Stefan Hanreich

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=20260508163134.481912-20-s.hanreich@proxmox.com \
    --to=s.hanreich@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