diff --git a/www/Makefile b/www/Makefile
index 1f45bc14..b24783ca 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -26,6 +26,7 @@ TAPE_UI_FILES= \
tape/ChangerStatus.js \
tape/DriveConfig.js \
tape/PoolConfig.js \
+ tape/TapeInventory.js \
tape/TapeManagement.js \
endif
diff --git a/www/tape/TapeInventory.js b/www/tape/TapeInventory.js
new file mode 100644
index 00000000..ba968752
--- /dev/null
+++ b/www/tape/TapeInventory.js
@@ -0,0 +1,104 @@
+Ext.define('pbs-model-tapes', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'catalog',
+ 'ctime',
+ 'expired',
+ 'label-text',
+ 'location',
+ 'media-set-ctime',
+ 'media-set-name',
+ 'media-set-uuid',
+ 'pool',
+ 'seq-nr',
+ 'status',
+ 'uuid',
+ ],
+ idProperty: 'label-text',
+ proxy: {
+ type: 'proxmox',
+ url: '/api2/json/tape/media/list',
+ },
+});
+
+Ext.define('PBS.TapeManagement.TapeInventory', {
+ extend: 'Ext.grid.Panel',
+ alias: 'widget.pbsTapeInventory',
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ reload: function() {
+ this.getView().getStore().rstore.load();
+ },
+
+ stopStore: function() {
+ this.getView().getStore().rstore.stopUpdate();
+ },
+
+ startStore: function() {
+ this.getView().getStore().rstore.startUpdate();
+ },
+ },
+
+ listeners: {
+ beforedestroy: 'stopStore',
+ deactivate: 'stopStore',
+ activate: 'startStore',
+ },
+
+ store: {
+ type: 'diff',
+ rstore: {
+ type: 'update',
+ storeid: 'proxmox-tape-tapes',
+ model: 'pbs-model-tapes',
+ },
+ sorters: 'label-text',
+ },
+
+ columns: [
+ {
+ text: gettext('Label'),
+ dataIndex: 'label-text',
+ flex: 1,
+ },
+ {
+ text: gettext('Pool'),
+ dataIndex: 'pool',
+ sorter: (a, b) => (a.data.pool || "").localeCompare(b.data.pool || ""),
+ flex: 1,
+ },
+ {
+ text: gettext('Media Set'),
+ dataIndex: 'media-set-name',
+ flex: 2,
+ sorter: function(a, b) {
+ return (a.data['media-set-ctime'] || 0) - (b.data['media-set-ctime'] || 0);
+ },
+ },
+ {
+ text: gettext('Location'),
+ dataIndex: 'location',
+ flex: 1,
+ renderer: function(value) {
+ if (value === 'offline') {
+ return ` ${gettext("Offline")}`;
+ } else if (value.startsWith('online-')) {
+ let location = value.substring(value.indexOf('-') + 1);
+ return ` ${gettext("Online")} - ${location}`;
+ } else if (value.startsWith('vault-')) {
+ let location = value.substring(value.indexOf('-') + 1);
+ return ` ${gettext("Vault")} - ${location}`;
+ } else {
+ return value;
+ }
+ },
+ },
+ {
+ text: gettext('Status'),
+ dataIndex: 'status',
+ flex: 1,
+ },
+ ],
+});
diff --git a/www/tape/TapeManagement.js b/www/tape/TapeManagement.js
index 8461b0b3..e558620a 100644
--- a/www/tape/TapeManagement.js
+++ b/www/tape/TapeManagement.js
@@ -16,6 +16,11 @@ Ext.define('PBS.TapeManagement', {
itemId: 'backup',
xtype: 'pbsBackupOverview',
},
+ {
+ title: gettext('Tape Inventory'),
+ itemId: 'inventory',
+ xtype: 'pbsTapeInventory',
+ },
{
title: gettext('Library'),
itemId: 'library',