Ext.define('pbs-slot-model', {
    extend: 'Ext.data.Model',
    fields: ['entry-id', 'label-text', 'is-labeled', ' model', 'name', 'vendor', 'serial', 'state', 'status', 'pool',
	{
	    name: 'is-blocked',
	    calculate: function(data) {
		return data.state !== undefined;
	    },
	},
	{
	    name: 'is-empty',
	    calculate: function(data) {
		return data['label-text'] === undefined;
	    },
	},
    ],
    idProperty: 'entry-id',
});

Ext.define('PBS.TapeManagement.FreeSlotSelector', {
    extend: 'Proxmox.form.ComboGrid',
    alias: 'widget.pbsFreeSlotSelector',

    valueField: 'id',
    displayField: 'id',

    listConfig: {
	columns: [
	    {
		dataIndex: 'id',
		text: gettext('ID'),
		flex: 1,
	    },
	    {
		dataIndex: 'type',
		text: gettext('Type'),
		flex: 1,
	    },
	],
    },
});

Ext.define('PBS.TapeManagement.ChangerStatus', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.pbsChangerStatus',

    tools: [PBS.Utils.get_help_tool("tape_backup")],

    controller: {
	xclass: 'Ext.app.ViewController',

	importTape: function(v, rI, cI, button, el, record) {
	    let me = this;
	    let view = me.getView();
	    let from = record.data['entry-id'];
	    let changer = encodeURIComponent(view.changer);
	    Ext.create('Proxmox.window.Edit', {
		title: gettext('Import'),
		isCreate: true,
		submitText: gettext('OK'),
		method: 'POST',
		url: `/api2/extjs/tape/changer/${changer}/transfer`,
		items: [
		    {
			xtype: 'displayfield',
			name: 'from',
			value: from,
			submitValue: true,
			fieldLabel: gettext('From Slot'),
		    },
		    {
			xtype: 'pbsFreeSlotSelector',
			name: 'to',
			fieldLabel: gettext('To Slot'),
			store: {
			    data: me.free_slots,
			},
		    },
		],
		listeners: {
		    destroy: function() {
			me.reload();
		    },
		},
	    }).show();
	},

	slotTransfer: function(v, rI, cI, button, el, record) {
	    let me = this;
	    let view = me.getView();
	    let from = record.data['entry-id'];
	    let changer = encodeURIComponent(view.changer);
	    Ext.create('Proxmox.window.Edit', {
		title: gettext('Transfer'),
		isCreate: true,
		submitText: gettext('OK'),
		method: 'POST',
		url: `/api2/extjs/tape/changer/${changer}/transfer`,
		items: [
		    {
			xtype: 'displayfield',
			name: 'from',
			value: from,
			submitValue: true,
			fieldLabel: gettext('From Slot'),
		    },
		    {
			xtype: 'pbsFreeSlotSelector',
			name: 'to',
			fieldLabel: gettext('To Slot'),
			store: {
			    data: me.free_slots.concat(me.free_ie_slots),
			},
		    },
		],
		listeners: {
		    destroy: function() {
			me.reload();
		    },
		},
	    }).show();
	},

	labelMedia: function(button, event, record) {
	    let me = this;
	    Ext.create('PBS.TapeManagement.LabelMediaWindow', {
		driveid: record.data.name,
		label: record.data["label-text"],
	    }).show();
	},

	catalog: function(button, event, record) {
	    let me = this;

	    let view = me.getView();
	    PBS.Utils.driveCommand(record.data.name, 'catalog', {
		waitMsgTarget: view,
		method: 'POST',
		success: function(response) {
		    Ext.create('Proxmox.window.TaskViewer', {
			upid: response.result.data,
		    }).show();
		},
	    });
	},

	'format-inserted': function(button, event, record) {
	    let me = this;

	    let view = me.getView();
	    PBS.Utils.driveCommand(record.data.name, 'format-media', {
		waitMsgTarget: view,
		method: 'POST',
		success: function(response) {
		    Ext.create('Proxmox.window.TaskProgress', {
			upid: response.result.data,
			taskDone: function() {
			    me.reload();
			},
		    }).show();
		},
	    });
	},

	format: function(v, rI, cI, button, el, record) {
	    let me = this;
	    let view = me.getView();
	    let label = record.data['label-text'];

	    let changer = encodeURIComponent(view.changer);
	    let singleDrive = me.drives.length === 1 ? me.drives[0] : undefined;
	    Ext.create('PBS.TapeManagement.EraseWindow', {
		label,
		changer,
		singleDrive,
		listeners: {
		    destroy: function() {
			me.reload();
		    },
		},
	    }).show();
	},

	load: function(v, rI, cI, button, el, record) {
	    let me = this;
	    let view = me.getView();
	    let label = record.data['label-text'];
	    let slot = record.data['entry-id'];

	    let changer = encodeURIComponent(view.changer);
	    let singleDrive = me.drives.length === 1 ? me.drives[0] : undefined;

	    let apiCall = label !== "" ? 'load-media' : 'load-slot';
	    let params = label !== "" ? { 'label-text': label } : { 'source-slot': slot };

	    if (singleDrive !== undefined) {
		Proxmox.Utils.API2Request({
		    method: 'POST',
		    params,
		    url: `/api2/extjs/tape/drive/${singleDrive}/${apiCall}`,
		    success: function(response, opt) {
			Ext.create('Proxmox.window.TaskProgress', {
			    upid: response.result.data,
			    taskDone: function(success) {
				me.reload();
			    },
			}).show();
		    },
		    failure: function(response, opt) {
			Ext.Msg.alert(gettext('Error'), response.htmlStatus);
		    },
		});
	    } else {
		Ext.create('Proxmox.window.Edit', {
		    isCreate: true,
		    autoShow: true,
		    submitText: gettext('OK'),
		    title: gettext('Load Media into Drive'),
		    url: `/api2/extjs/tape/drive`,
		    method: 'POST',
		    submitUrl: function(url, values) {
			let drive = values.drive;
			delete values.drive;
			return `${url}/${encodeURIComponent(drive)}/${apiCall}`;
		    },
		    items: [
			label !== "" ? {
			    xtype: 'displayfield',
			    name: 'label-text',
			    value: label,
			    submitValue: true,
			    fieldLabel: gettext('Media'),
			} : {
			    xtype: 'displayfield',
			    name: 'source-slot',
			    value: slot,
			    submitValue: true,
			    fieldLabel: gettext('Source Slot'),
			},
			{
			    xtype: 'pbsDriveSelector',
			    fieldLabel: gettext('Drive'),
			    changer: changer,
			    name: 'drive',
			},
		    ],
		    listeners: {
			destroy: function() {
			    me.reload();
			},
		    },
		});
	    }
	},

	unload: async function(v, rI, cI, button, el, record) {
	    let me = this;
	    let drive = record.data.name;
	    try {
		await Proxmox.Async.api2({
		    method: 'POST',
		    timeout: 5*60*1000,
		    url: `/api2/extjs/tape/drive/${encodeURIComponent(drive)}/unload`,
		});
	    } catch (response) {
		Ext.Msg.alert(gettext('Error'), response.result.message);
	    }
	    me.reload();
	},

	cartridgeMemory: function(view, rI, cI, button, el, record) {
	    let me = this;
	    let drive = record.data.name;
	    PBS.Utils.driveCommand(drive, 'cartridge-memory', {
		waitMsgTarget: me.getView(),
		success: PBS.Utils.showCartridgeMemoryWindow,
	    });
	},

	cleanDrive: function(button, event, record) {
	    let me = this;
	    PBS.Utils.driveCommand(record.data.name, 'clean', {
		waitMsgTarget: me.getView(),
		method: 'PUT',
		success: function(response) {
		    Ext.create('Proxmox.window.TaskProgress', {
			upid: response.result.data,
			taskDone: function() {
			    me.reload();
			},
		    }).show();
		},
	    });
	},

	volumeStatistics: function(view, rI, cI, button, el, record) {
	    let me = this;
	    let drive = record.data.name;
	    PBS.Utils.driveCommand(drive, 'volume-statistics', {
		waitMsgTarget: me.getView(),
		success: PBS.Utils.showVolumeStatisticsWindow,
	    });
	},

	readLabel: function(view, rI, cI, button, el, record) {
	    let me = this;
	    let drive = record.data.name;

	    PBS.Utils.driveCommand(drive, 'read-label', {
		waitMsgTarget: me.getView(),
		success: PBS.Utils.showMediaLabelWindow,
	    });
	},

	status: function(view, rI, cI, button, el, record) {
	    let me = this;
	    let drive = record.data.name;
	    PBS.Utils.driveCommand(drive, 'status', {
		waitMsgTarget: me.getView(),
		success: PBS.Utils.showDriveStatusWindow,
	    });
	},

	reloadList: function() {
	    let me = this;
	    me.lookup('changerselector').getStore().load();
	},

	barcodeLabel: function() {
	    let me = this;
	    let view = me.getView();
	    let changer = view.changer;
	    if (changer === '') {
		return;
	    }

	    let singleDrive = me.drives.length === 1 ? me.drives[0] : undefined;

	    Ext.create('Proxmox.window.Edit', {
		title: gettext('Barcode Label'),
		showTaskViewer: true,
		method: 'POST',
		url: '/api2/extjs/tape/drive',
		submitUrl: function(url, values) {
		    let drive = values.drive;
		    delete values.drive;
		    return `${url}/${encodeURIComponent(drive)}/barcode-label-media`;
		},

		items: [
		    {
			xtype: singleDrive === undefined ? 'pbsDriveSelector' : 'displayfield',
			fieldLabel: gettext('Drive'),
			submitValue: true,
			name: 'drive',
			value: singleDrive,
			changer: changer,
		    },
		    {
			xtype: 'pbsMediaPoolSelector',
			fieldLabel: gettext('Pool'),
			name: 'pool',
			skipEmptyText: true,
			allowBlank: true,
		    },
		],
	    }).show();
	},

	inventory: function() {
	    let me = this;
	    let view = me.getView();
	    let changer = view.changer;
	    if (changer === '') {
		return;
	    }

	    let singleDrive = me.drives.length === 1 ? me.drives[0] : undefined;

	    if (singleDrive !== undefined) {
		Proxmox.Utils.API2Request({
		    method: 'PUT',
		    url: `/api2/extjs/tape/drive/${singleDrive}/inventory`,
		    success: function(response, opt) {
			Ext.create('Proxmox.window.TaskViewer', {
			    upid: response.result.data,
			    taskDone: function(success) {
				me.reload();
			    },
			}).show();
		    },
		    failure: function(response, opt) {
			Ext.Msg.alert(gettext('Error'), response.htmlStatus);
		    },
		});
	    } else {
		Ext.create('Proxmox.window.Edit', {
		    title: gettext('Inventory'),
		    showTaskViewer: true,
		    method: 'PUT',
		    url: '/api2/extjs/tape/drive',
		    submitUrl: function(url, values) {
			let drive = values.drive;
			delete values.drive;
			return `${url}/${encodeURIComponent(drive)}/inventory`;
		    },

		    items: [
			{
			    xtype: 'pbsDriveSelector',
			    fieldLabel: gettext('Drive'),
			    name: 'drive',
			    changer: changer,
			},
		    ],
		}).show();
	    }
	},

	scheduleReload: function(time) {
	    let me = this;
	    if (me.reloadTimeout === undefined) {
		me.reloadTimeout = setTimeout(function() {
		    me.reload();
		}, time);
	    }
	},

	cancelReload: function() {
	    let me = this;
	    if (me.reloadTimeout !== undefined) {
		clearTimeout(me.reloadTimeout);
		me.reloadTimeout = undefined;
	    }
	},

	reload: function() {
	    let me = this;
	    me.cancelReload();
	    me.reload_full(true);
	},

	reload_no_cache: function() {
	    let me = this;
	    if (me.reloadTimeout !== undefined) {
		clearTimeout(me.reloadTimeout);
		me.reloadTimeout = undefined;
	    }
	    me.reload_full(false);
	},

	drives: [],

	updateDrives: function(drives) {
	    let me = this;
	    me.drives = drives;
	},

	free_slots: [],
	free_ie_slots: [],

	updateFreeSlots: function(free_slots, free_ie_slots) {
	    let me = this;
	    me.free_slots = free_slots;
	    me.free_ie_slots = free_ie_slots;
	},

	reload_full: async function(use_cache) {
	    let me = this;
	    let view = me.getView();
	    let changer = view.changer;
	    if (changer === '') {
		return;
	    }

	    try {
		if (!use_cache) {
		    Proxmox.Utils.setErrorMask(view, true);
		    Proxmox.Utils.setErrorMask(me.lookup('content'));
		}
		let status_fut = Proxmox.Async.api2({
		    timeout: 5*60*1000,
		    method: 'GET',
		    url: `/api2/extjs/tape/changer/${encodeURIComponent(changer)}/status`,
		    params: {
			cache: use_cache,
		    },
		});
		let drives_fut = Proxmox.Async.api2({
		    timeout: 5*60*1000,
		    url: `/api2/extjs/tape/drive?changer=${encodeURIComponent(changer)}`,
		});

		let tapes_fut = Proxmox.Async.api2({
		    timeout: 5*60*1000,
		    url: '/api2/extjs/tape/media/list',
		    method: 'GET',
		    params: {
			"update-status": false,
		    },
		});

		let [status, drives, tapes_list] = await Promise.all([status_fut, drives_fut, tapes_fut]);

		let data = {
		    slot: [],
		    'import-export': [],
		    drive: [],
		};

		let tapes = {};

		for (const tape of tapes_list.result.data) {
		    tapes[tape['label-text']] = {
			labeled: true,
			pool: tape.pool,
			status: tape.expired ? 'expired' : tape.status,
		    };
		}

		let drive_entries = {};

		for (const entry of drives.result.data) {
		    drive_entries[entry['changer-drivenum'] || 0] = entry;
		}

		let free_slots = [];
		let free_ie_slots = [];

		let valid_drives = [];

		for (let entry of status.result.data) {
		    let type = entry['entry-kind'];
		    let id = entry['entry-id'];

		    if (type === 'drive') {
			if (drive_entries[id] === undefined) {
			    continue;
			}

			entry = Ext.applyIf(entry, drive_entries[id]);
			valid_drives.push(drive_entries[id].name);
		    }

		    if (tapes[entry['label-text']] !== undefined) {
			entry['is-labeled'] = true;
			entry.pool = tapes[entry['label-text']].pool;
			entry.status = tapes[entry['label-text']].status;
		    } else {
			entry['is-labeled'] = false;
		    }

		    if (!entry['label-text'] && type !== 'drive') {
			if (type === 'slot') {
			    free_slots.push({
				id,
				type,
			    });
			} else {
			    free_ie_slots.push({
				id,
				type,
			    });
			}
		    }
		    data[type].push(entry);
		}

		// the stores are diffstores and are only refreshed
		// on a 'load' event, which does not trigger on 'setData'
		// so we have to fire them ourselves

		me.lookup('slots').getStore().rstore.setData(data.slot);
		me.lookup('slots').getStore().rstore.fireEvent('load', me, [], true);

		me.lookup('import_export').getStore().rstore.setData(data['import-export']);
		me.lookup('import_export').getStore().rstore.fireEvent('load', me, [], true);

		me.lookup('drives').getStore().rstore.setData(data.drive);
		me.lookup('drives').getStore().rstore.fireEvent('load', me, [], true);

		// manually fire selectionchange to update button status
		me.lookup('drives').getSelectionModel().fireEvent('selectionchange', me);

		me.updateFreeSlots(free_slots, free_ie_slots);
		me.updateDrives(valid_drives);

		if (!use_cache) {
		    Proxmox.Utils.setErrorMask(view);
		}
		Proxmox.Utils.setErrorMask(me.lookup('content'));
	    } catch (response) {
		if (!view || view.isDestroyed) {
		    return;
		}

		if (!use_cache) {
		    Proxmox.Utils.setErrorMask(view);
		}
		Proxmox.Utils.setErrorMask(me.lookup('content'), response.result.message.toString());
	    }

	    me.scheduleReload(5000);
	},

	renderLabel: function(value) {
	    if (value === undefined) {
		return '';
	    }

	    if (value === "") {
		return Ext.htmlEncode("<no-barcode>");
	    }

	    return value;
	},

	renderIsLabeled: function(value, mD, record) {
	    if (!record.data['label-text']) {
		return "";
	    }

	    if (record.data['label-text'].startsWith("CLN")) {
		return "";
	    }

	    if (!value) {
		return gettext('Not Labeled');
	    }

	    let status = record.data.status;
	    if (record.data.pool) {
		return `${status} (${record.data.pool})`;
	    }
	    return status;
	},

	control: {
	    'grid[reference=drives]': {
		cellclick: function(table, td, ci, rec, tr, ri, e) {
		    if (e.position.column.dataIndex !== 'state') {
			return;
		    }

		    let upid = rec.data.state;
		    if (!upid || !upid.startsWith("UPID")) {
			return;
		    }

		    Ext.create('Proxmox.window.TaskViewer', {
			autoShow: true,
			upid,
		    });
		},
	    },
	},

	init: function(view) {
	    let me = this;
	    if (!view.changer) {
		throw "no changer given";
	    }

	    view.title = `${gettext("Changer")}: ${view.changer}`;
	    me.reload();
	},
    },

    listeners: {
	deactivate: 'cancelReload',
	beforedestroy: 'cancelReload',
    },

    tbar: [
	{
	    text: gettext('Reload'),
	    xtype: 'proxmoxButton',
	    handler: 'reload_no_cache',
	    selModel: false,
	},
	'-',
	{
	    text: gettext('Barcode Label'),
	    xtype: 'proxmoxButton',
	    handler: 'barcodeLabel',
	    iconCls: 'fa fa-barcode',
	},
	{
	    text: gettext('Inventory'),
	    xtype: 'proxmoxButton',
	    handler: 'inventory',
	    iconCls: 'fa fa-book',
	},
    ],

    layout: 'fit',
    bodyPadding: 5,

    items: [
	{
	    xtype: 'container',
	    reference: 'content',
	    layout: {
		type: 'hbox',
		align: 'stretch',
	    },
	    items: [
		{
		    xtype: 'grid',
		    reference: 'slots',
		    title: gettext('Slots'),
		    padding: 5,
		    srollable: true,
		    flex: 1,
		    store: {
			type: 'diff',
			rstore: {
			    type: 'store',
			    model: 'pbs-slot-model',
			},
			data: [],
		    },
		    columns: [
			{
			    text: gettext('ID'),
			    dataIndex: 'entry-id',
			    width: 50,
			},
			{
			    text: gettext("Content"),
			    dataIndex: 'label-text',
			    flex: 1,
			    renderer: 'renderLabel',
			},
			{
			    text: gettext('Inventory'),
			    dataIndex: 'is-labeled',
			    renderer: 'renderIsLabeled',
			    flex: 1,
			},
			{
			    text: gettext('Actions'),
			    xtype: 'actioncolumn',
			    width: 100,
			    items: [
				{
				    iconCls: 'fa fa-rotate-90 fa-exchange',
				    handler: 'slotTransfer',
				    tooltip: gettext('Transfer'),
				    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'],
				},
				{
				    iconCls: 'fa fa-trash-o',
				    handler: 'format',
				    tooltip: gettext('Format'),
				    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'],
				},
				{
				    iconCls: 'fa fa-rotate-90 fa-upload',
				    handler: 'load',
				    tooltip: gettext('Load'),
				    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'],
				},
			    ],
			},
		    ],
		},
		{
		    xtype: 'container',
		    flex: 2,
		    defaults: {
			padding: 5,
		    },
		    layout: {
			type: 'vbox',
			align: 'stretch',
		    },
		    items: [
			{
			    xtype: 'grid',
			    reference: 'drives',
			    scrollable: true,
			    maxHeight: 350, // ~10 drives
			    title: gettext('Drives'),
			    store: {
				type: 'diff',
				rstore: {
				    type: 'store',
				    model: 'pbs-slot-model',
				},
				data: [],
			    },
			    tbar: [
				{
				    text: gettext('Label Media'),
				    xtype: 'proxmoxButton',
				    handler: 'labelMedia',
				    iconCls: 'fa fa-barcode',
				    disabled: true,
				    enableFn: (rec) => !rec.data["is-empty"],
				},
				{
				    text: gettext('Catalog'),
				    xtype: 'proxmoxButton',
				    handler: 'catalog',
				    iconCls: 'fa fa-book',
				    disabled: true,
				    enableFn: (rec) => !rec.data["is-empty"],
				},
				{
				    text: gettext('Format'),
				    xtype: 'proxmoxButton',
				    handler: 'format-inserted',
				    iconCls: 'fa fa-trash-o',
				    disabled: true,
				    enableFn: (rec) => !rec.data["is-empty"],
				    dangerous: true,
				    confirmMsg: gettext('Are you sure you want to format the inserted tape?'),
				},
				'-',
				{
				    text: gettext('Clean Drive'),
				    xtype: 'proxmoxButton',
				    handler: 'cleanDrive',
				    iconCls: 'fa fa-shower',
				    disabled: true,
				},
			    ],
			    columns: [
				{
				    text: gettext('ID'),
				    dataIndex: 'entry-id',
				    hidden: true,
				    width: 50,
				},
				{
				    text: gettext("Content"),
				    dataIndex: 'label-text',
				    flex: 1,
				    renderer: 'renderLabel',
				},
				{
				    text: gettext('Inventory'),
				    dataIndex: 'is-labeled',
				    renderer: 'renderIsLabeled',
				    flex: 1.5,
				},
				{
				    text: gettext("Name"),
				    sortable: true,
				    dataIndex: 'name',
				    flex: 1,
				    renderer: Ext.htmlEncode,
				},
				{
				    text: gettext('State'),
				    dataIndex: 'state',
				    flex: 3,
				    renderer: PBS.Utils.renderDriveState,
				},
				{
				    text: gettext("Vendor"),
				    sortable: true,
				    dataIndex: 'vendor',
				    hidden: true,
				    flex: 1,
				    renderer: Ext.htmlEncode,
				},
				{
				    text: gettext("Model"),
				    sortable: true,
				    dataIndex: 'model',
				    hidden: true,
				    flex: 1,
				    renderer: Ext.htmlEncode,
				},
				{
				    text: gettext("Serial"),
				    sortable: true,
				    dataIndex: 'serial',
				    hidden: true,
				    flex: 1,
				    renderer: Ext.htmlEncode,
				},
				{
				    xtype: 'actioncolumn',
				    text: gettext('Actions'),
				    width: 140,
				    items: [
					{
					    iconCls: 'fa fa-rotate-270 fa-upload',
					    handler: 'unload',
					    tooltip: gettext('Unload'),
					    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'] || rec.data['is-blocked'],
					},
					{
					    iconCls: 'fa fa-hdd-o',
					    handler: 'cartridgeMemory',
					    tooltip: gettext('Cartridge Memory'),
					    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'] || rec.data['is-blocked'],
					},
					{
					    iconCls: 'fa fa-line-chart',
					    handler: 'volumeStatistics',
					    tooltip: gettext('Volume Statistics'),
					    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'] || rec.data['is-blocked'],
					},
					{
					    iconCls: 'fa fa-tag',
					    handler: 'readLabel',
					    tooltip: gettext('Read Label'),
					    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'] || rec.data['is-blocked'],
					},
					{
					    iconCls: 'fa fa-info-circle',
					    tooltip: gettext('Status'),
					    handler: 'status',
					    isActionDisabled: (v, r, c, i, rec) => rec.data['is-blocked'],
					},
				    ],
				},
			    ],
			},
			{
			    xtype: 'grid',
			    reference: 'import_export',
			    flex: 1,
			    srollable: true,
			    store: {
				type: 'diff',
				rstore: {
				    type: 'store',
				    model: 'pbs-slot-model',
				},
				data: [],
			    },
			    title: gettext('Import-Export Slots'),
			    columns: [
				{
				    text: gettext('ID'),
				    dataIndex: 'entry-id',
				    width: 50,
				},
				{
				    text: gettext("Content"),
				    dataIndex: 'label-text',
				    renderer: 'renderLabel',
				    flex: 1,
				},
				{
				    text: gettext('Inventory'),
				    dataIndex: 'is-labeled',
				    renderer: 'renderIsLabeled',
				    flex: 1,
				},
				{
				    text: gettext('Actions'),
				    xtype: 'actioncolumn',
				    items: [
					{
					    iconCls: 'fa fa-rotate-270 fa-upload',
					    handler: 'importTape',
					    tooltip: gettext('Import'),
					    isActionDisabled: (v, r, c, i, rec) => rec.data['is-empty'],
					},
				    ],
				    width: 80,
				},
			    ],
			},
		    ],
		},
	    ],
	},
    ],
});