proxmox-backup/www/window/TrafficControlEdit.js
Thomas Lamprecht ff344655e2 ui: traffic-control edit: make network edit a single text field
here's to note that the radio-group was my idea, Dominik just
executed it, nicely that is.

But, the panel looks a bit glitchy layout wise as with that and the
bandwidth fields (maybe we should render their unit inline) the
vertical alignments were all over the place.

So for now make it a simple text field and throw in a tooltip for
good measurement

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-11-20 22:04:59 +01:00

450 lines
9.3 KiB
JavaScript

Ext.define('PBS.window.TrafficControlEdit', {
extend: 'Proxmox.window.Edit',
alias: 'widget.pbsTrafficControlEdit',
mixins: ['Proxmox.Mixin.CBind'],
onlineHelp: 'sysadmin_traffic_control',
width: 800,
isAdd: true,
subject: gettext('Traffic Control Rule'),
fieldDefaults: { labelWidth: 120 },
cbindData: function(initialConfig) {
let me = this;
let baseurl = '/api2/extjs/config/traffic-control';
let name = initialConfig.name;
me.isCreate = !name;
me.url = name ? `${baseurl}/${name}` : baseurl;
me.method = name ? 'PUT' : 'POST';
return { };
},
controller: {
xclass: 'Ext.app.ViewController',
weekdays: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
dowChanged: function(field, value) {
let me = this;
let record = field.getWidgetRecord();
if (record === undefined) {
// this is sometimes called before a record/column is initialized
return;
}
let col = field.getWidgetColumn();
record.set(col.dataIndex, value);
record.commit();
me.updateTimeframeField();
},
timeChanged: function(field, value) {
let me = this;
if (value === null) {
return;
}
let record = field.getWidgetRecord();
if (record === undefined) {
// this is sometimes called before a record/column is initialized
return;
}
let col = field.getWidgetColumn();
let hours = value.getHours().toString().padStart(2, '0');
let minutes = value.getMinutes().toString().padStart(2, '0');
record.set(col.dataIndex, `${hours}:${minutes}`);
record.commit();
me.updateTimeframeField();
},
addTimeframe: function() {
let me = this;
me.lookup('timeframes').getStore().add({
start: "00:00",
end: "23:59",
mon: true,
tue: true,
wed: true,
thu: true,
fri: true,
sat: true,
sun: true,
});
me.updateTimeframeField();
},
updateTimeframeField: function() {
let me = this;
let timeframes = [];
me.lookup('timeframes').getStore().each((rec) => {
let timeframe = '';
let days = me.weekdays.filter(day => rec.data[day]);
if (days.length < 7 && days.length > 0) {
timeframe += days.join(',') + ' ';
}
let { start, end } = rec.data;
timeframe += `${start}-${end}`;
timeframes.push(timeframe);
});
let field = me.lookup('timeframe');
field.suspendEvent('change');
field.setValue(timeframes.join(';'));
field.resumeEvent('change');
},
removeTimeFrame: function(field) {
let me = this;
let record = field.getWidgetRecord();
if (record === undefined) {
// this is sometimes called before a record/column is initialized
return;
}
me.lookup('timeframes').getStore().remove(record);
me.updateTimeframeField();
},
parseTimeframe: function(timeframe) {
let me = this;
let [, days, start, end] = /^(?:(\S*)\s+)?([0-9:]+)-([0-9:]+)$/.exec(timeframe) || [];
if (start === '0') {
start = "00:00";
}
let record = {
start,
end,
};
if (!days) {
days = 'mon..sun';
}
days = days.split(',');
days.forEach((day) => {
if (record[day]) {
return;
}
if (me.weekdays.indexOf(day) !== -1) {
record[day] = true;
} else {
// we have a range 'xxx..yyy'
let [startDay, endDay] = day.split('..');
let startIdx = me.weekdays.indexOf(startDay);
let endIdx = me.weekdays.indexOf(endDay);
if (endIdx < startIdx) {
endIdx += me.weekdays.length;
}
for (let dayIdx = startIdx; dayIdx <= endIdx; dayIdx++) {
let curDay = me.weekdays[dayIdx%me.weekdays.length];
if (!record[curDay]) {
record[curDay] = true;
}
}
}
});
return record;
},
setGridData: function(field, value) {
let me = this;
if (!value) {
return;
}
value = value.split(';');
let records = value.map((timeframe) => me.parseTimeframe(timeframe));
me.lookup('timeframes').getStore().setData(records);
},
control: {
'grid checkbox': {
change: 'dowChanged',
},
'grid timefield': {
change: 'timeChanged',
},
'grid button': {
click: 'removeTimeFrame',
},
'field[name=timeframe]': {
change: 'setGridData',
},
},
},
items: {
xtype: 'inputpanel',
onGetValues: function(values) {
let me = this;
let isCreate = me.up('window').isCreate;
if (!values['network-select']) {
values.network = '0.0.0.0/0';
} else if (values.network) {
values.network = values.network.split(/\s*,\s*/);
}
if ('timeframe' in values && !values.timeframe) {
delete values.timeframe;
}
if (values.timeframe && !Ext.isArray(values.timeframe)) {
let timeframe = [], seen = {};
values.timeframe.split(';').forEach(tf => {
if (!seen[tf]) {
timeframe.push(tf);
seen[tf] = true;
}
});
values.timeframe = timeframe;
}
delete values['network-select'];
if (!isCreate) {
PBS.Utils.delete_if_default(values, 'timeframe');
PBS.Utils.delete_if_default(values, 'rate-in');
PBS.Utils.delete_if_default(values, 'rate-out');
PBS.Utils.delete_if_default(values, 'burst-in');
PBS.Utils.delete_if_default(values, 'burst-out');
if (typeof values.delete === 'string') {
values.delete = values.delete.split(',');
}
}
return values;
},
column1: [
{
xtype: 'pmxDisplayEditField',
name: 'name',
fieldLabel: gettext('Name'),
renderer: Ext.htmlEncode,
allowBlank: false,
minLength: 4,
cbind: {
editable: '{isCreate}',
},
},
{
xtype: 'pmxBandwidthField',
fieldLabel: gettext('Rate In'),
name: 'rate-in',
},
{
xtype: 'pmxBandwidthField',
fieldLabel: gettext('Rate Out'),
name: 'rate-out',
},
],
column2: [
{
xtype: 'proxmoxtextfield',
name: 'comment',
cbind: {
deleteEmpty: '{!isCreate}',
},
fieldLabel: gettext('Comment'),
},
{
xtype: 'pmxBandwidthField',
fieldLabel: gettext('Burst In'),
name: 'burst-in',
},
{
xtype: 'pmxBandwidthField',
fieldLabel: gettext('Burst Out'),
name: 'burst-out',
},
],
columnB: [
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Network(s)'),
name: 'network',
emptyText: gettext('0.0.0.0/0 (Apply on all Networks)'),
autoEl: {
tag: 'div',
'data-qtip': gettext('A comma-separated list of networks to apply the (shared) limit.'),
},
},
{
xtype: 'displayfield',
fieldLabel: gettext('Timeframes'),
},
{
xtype: 'fieldcontainer',
items: [
{
xtype: 'grid',
height: 150,
scrollable: true,
reference: 'timeframes',
store: {
fields: ['start', 'end', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
data: [],
},
columns: [
{
text: gettext('Time Start'),
xtype: 'widgetcolumn',
dataIndex: 'start',
widget: {
xtype: 'timefield',
isFormField: false,
format: 'H:i',
formatText: 'HH:MM',
},
flex: 1,
},
{
text: gettext('Time End'),
xtype: 'widgetcolumn',
dataIndex: 'end',
widget: {
xtype: 'timefield',
isFormField: false,
format: 'H:i',
formatText: 'HH:MM',
maxValue: '23:59',
},
flex: 1,
},
{
text: gettext('Mon'),
xtype: 'widgetcolumn',
dataIndex: 'mon',
width: 60,
widget: {
xtype: 'checkbox',
isFormField: false,
},
},
{
text: gettext('Tue'),
xtype: 'widgetcolumn',
dataIndex: 'tue',
width: 60,
widget: {
xtype: 'checkbox',
isFormField: false,
},
},
{
text: gettext('Wed'),
xtype: 'widgetcolumn',
dataIndex: 'wed',
width: 60,
widget: {
xtype: 'checkbox',
isFormField: false,
},
},
{
text: gettext('Thu'),
xtype: 'widgetcolumn',
dataIndex: 'thu',
width: 60,
widget: {
xtype: 'checkbox',
isFormField: false,
},
},
{
text: gettext('Fri'),
xtype: 'widgetcolumn',
dataIndex: 'fri',
width: 60,
widget: {
xtype: 'checkbox',
isFormField: false,
},
},
{
text: gettext('Sat'),
xtype: 'widgetcolumn',
dataIndex: 'sat',
width: 60,
widget: {
xtype: 'checkbox',
isFormField: false,
},
},
{
text: gettext('Sun'),
xtype: 'widgetcolumn',
dataIndex: 'sun',
width: 60,
widget: {
xtype: 'checkbox',
isFormField: false,
},
},
{
xtype: 'widgetcolumn',
width: 40,
widget: {
xtype: 'button',
iconCls: 'fa fa-trash-o',
},
},
],
},
],
},
{
xtype: 'button',
text: gettext('Add'),
iconCls: 'fa fa-plus-circle',
handler: 'addTimeframe',
},
{
xtype: 'hidden',
reference: 'timeframe',
name: 'timeframe',
},
],
},
initComponent: function() {
let me = this;
me.callParent();
if (!me.isCreate) {
me.load({
success: function(response) {
let data = response.result.data;
if (data.network?.length === 1 && data.network[0] === '0.0.0.0/0') {
data['network-select'] = 'all';
delete data.network;
} else {
data['network-select'] = 'limit';
}
if (Ext.isArray(data.timeframe)) {
data.timeframe = data.timeframe.join(';');
}
me.setValues(data);
},
});
}
},
});