public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
To: Lukas Wagner <l.wagner@proxmox.com>
Cc: pbs-devel@lists.proxmox.com, pve-devel@lists.proxmox.com
Subject: Re: [PATCH proxmox-widget-toolkit 18/29] notification: matcher: add better calendar editor
Date: Fri, 24 Jul 2026 08:46:33 +0200	[thread overview]
Message-ID: <xljzz65dighwikxg6rffxtejtnmwfbdrq6aquhcrj6mhpekddw@dzd6xl4hoeyt> (raw)
In-Reply-To: <20260709115716.299836-19-l.wagner@proxmox.com>

On Thu, Jul 09, 2026 at 01:57:05PM +0200, Lukas Wagner wrote:
> This new editor allows one to enter the start time, end time and tick
> the matched week-days, instead of having to enter the appropriate string
> representation of the time range (e.g. 'mon..tue 08:00-12:00')
> 
> The general approach was copied from PBS's traffic rule edit panel, but
> it is too different to generalize this into a new, reusable component.
> 
one comment inline
> Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
> ---
>  .../NotificationMatchExpressionEditPanel.js   |   2 +-
>  src/window/NotificationMatcherEdit.js         | 300 +++++++++++++++++-
>  2 files changed, 290 insertions(+), 12 deletions(-)
> 
> diff --git a/src/panel/NotificationMatchExpressionEditPanel.js b/src/panel/NotificationMatchExpressionEditPanel.js
> index 6d1f93e..e117ea5 100644
> --- a/src/panel/NotificationMatchExpressionEditPanel.js
> +++ b/src/panel/NotificationMatchExpressionEditPanel.js
> @@ -87,7 +87,7 @@ Ext.define('Proxmox.panel.NotificationMatchExpressionEditPanel', {
>                              break;
>                          case 'match-calendar':
>                              data = {
> -                                value: '',
> +                                value: '00:00-23:59',
>                              };
>                              leaf = true;
>                              break;
> diff --git a/src/window/NotificationMatcherEdit.js b/src/window/NotificationMatcherEdit.js
> index 893c3e3..433f86c 100644
> --- a/src/window/NotificationMatcherEdit.js
> +++ b/src/window/NotificationMatcherEdit.js
> @@ -361,7 +361,7 @@ Ext.define('Proxmox.panel.NotificationRulesEditPanel', {
>                              break;
>                          case 'match-calendar':
>                              data = {
> -                                value: '',
> +                                value: '00:00-23:59',
>                              };
>                              break;
>                      }
> @@ -977,6 +977,11 @@ Ext.define('Proxmox.panel.MatchCalendarSettings', {
>                  },
>                  set: function (value) {
>                      let me = this;
> +
> +                    if (!me.get('typeIsMatchCalendar')) {
> +                        return;
> +                    }
> +
>                      let record = me.get('selectedRecord');
>                      let currentData = record.get('data');
>                      record.set({
> @@ -992,23 +997,296 @@ Ext.define('Proxmox.panel.MatchCalendarSettings', {
>              },
>          },
>      },
> +    controller: {
> +        xclass: 'Ext.app.ViewController',
> +        control: {
> +            'grid checkbox': {
> +                change: 'dowChanged',
> +            },
> +            timefield: {
> +                change: 'timeChanged',
> +            },
> +            'field[reference=timeframe]': {
> +                change: 'setGridData',
> +            },
> +        },
> +
> +        weekdays: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
> +
> +        setGridData: function (field, value) {
> +            let me = this;
> +
> +            let record = me.parseTimeframe(value);
> +
> +            me.lookup('weekdayGrid').getStore().setData([record]);
> +            me.lookup('timeStart').setValue(record.start);
> +            me.lookup('timeEnd').setValue(record.end);
> +        },
> +
> +        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;
> +        },
> +
> +        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();
> +
> +            let startField = me.lookup('timeStart');
> +            let endField = me.lookup('timeEnd');
> +
> +            me.updateTimeframeField(startField, endField);
> +        },
> +
> +        timeChanged: function (field, value) {
> +            let me = this;
> +
> +            let startField = me.lookup('timeStart');
> +            let endField = me.lookup('timeEnd');
> +
> +            let start = startField.getValue();
> +            let end = endField.getValue();
> +
> +            let valid = !(start && end && start >= end);
> +
> +            if (!valid) {
> +                startField.markInvalid(gettext('Start time must be before end time'));
> +                endField.markInvalid(gettext('End time must be after start time'));
> +            } else {
> +                startField.clearInvalid();
> +                endField.clearInvalid();
> +            }
> +
> +            me.updateTimeframeField(startField, endField);
> +        },
> +
> +        updateTimeframeField: function (startField, endField) {
> +            let me = this;
> +
> +            let data = me.lookup('weekdayGrid').getStore().getData().getAt(0);
> +
> +            let timeframe = me.formatSelectedDays(data.data);
> +
> +            let start = me.formatTime(startField);
> +            let end = me.formatTime(endField);
> +
> +            timeframe += ` ${start}-${end}`;
when no days are selected, this creates a timeframe with a leading
whitespace (` 8:00-12:00`), which the backend fails to parse with the
following error:

invalid matcher config: 'expression' is not valid: could not parse schedule: unable to parse daily duration at ' 08:00-12:00' 

i think a good approach could be blocking creation of calendar matchers 
without days selected in the UI. the semantics of the day-less calendar 
expression '8-12' are "every day from 8-12", which the UI already 
represents as "8-12 + all days selected", so not sure what not 
selecting any day would/should mean except "never match"? 

in the backend, we might wanna trim the input to be more robust against
whitespace errors as well [0] (no very strong opinion on that though,
mostly got confused by the error message).

[0] https://lore.proxmox.com/pve-devel/20260709115716.299836-1-l.wagner@proxmox.com/T/#mf1dabe82fe5ecce4fadd6860689167c6734ee0d4
> +
> +            let field = me.lookup('timeframe');
> +            field.suspendEvent('change');
> +            field.setValue(timeframe);
> +
> +            me.getViewModel().set('matchCalendarValue', timeframe);
> +
> +            field.resumeEvent('change');
> +        },
> +
[...]




  reply	other threads:[~2026-07-24  6:46 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-09 11:56 [PATCH many 00/29] notifications: add nested match expressions Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 01/29] add new proxmox-match-expression crate Lukas Wagner
2026-07-24  6:46   ` Arthur Bied-Charreton
2026-07-09 11:56 ` [PATCH proxmox 02/29] notify: promote matcher to dir-style module Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 03/29] notify: fix doc comment Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 04/29] notify: matcher: break out severity matcher into submodule Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 05/29] notify: matcher: break out field " Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 06/29] notify: matcher: break out calendar " Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 07/29] notify: matcher: calendar: add basic unit test Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 08/29] notify: matcher: add InlineSeverityMatcher Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 09/29] notify: matcher: add InlineFieldMatcher Lukas Wagner
2026-07-09 11:56 ` [PATCH proxmox 10/29] notify: matcher: add InlineCalendarMatcher Lukas Wagner
2026-07-24  6:47   ` Arthur Bied-Charreton
2026-07-24  9:43     ` Wolfgang Bumiller
2026-07-09 11:56 ` [PATCH proxmox 11/29] notify: matcher: add expression support Lukas Wagner
2026-07-24  6:59   ` Arthur Bied-Charreton
2026-07-09 11:56 ` [PATCH proxmox 12/29] notify: api: support new expression parameter Lukas Wagner
2026-07-24  6:46   ` Arthur Bied-Charreton
2026-07-24 10:01     ` Wolfgang Bumiller
2026-07-24 11:38       ` Arthur Bied-Charreton
2026-07-09 11:57 ` [PATCH proxmox 13/29] notify: api: add `get_matcher_as_expression` Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox 14/29] notify: migrate PBS's and PVE's default matcher to expression syntax Lukas Wagner
2026-07-24  6:47   ` Arthur Bied-Charreton
2026-07-09 11:57 ` [PATCH proxmox 15/29] notify: move legacy matcher keys behind feature flag Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox-widget-toolkit 16/29] notification: increase matcher window width Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox-widget-toolkit 17/29] notifications: matcher: add support for match expressions Lukas Wagner
2026-07-24  6:45   ` Arthur Bied-Charreton
2026-07-09 11:57 ` [PATCH proxmox-widget-toolkit 18/29] notification: matcher: add better calendar editor Lukas Wagner
2026-07-24  6:46   ` Arthur Bied-Charreton [this message]
2026-07-09 11:57 ` [PATCH proxmox-widget-toolkit 19/29] notifications: matcher: consistently use title case for UI elements Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox-backup 20/29] notification: opt into 'legacy-matchers' feature in proxmox-notify Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox-backup 21/29] api: notification: add 'migrate-to-expression' parameter to get_matcher Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox-backup 22/29] ui: notification: enable new matcher UI Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox-perl-rs 23/29] notify: matcher: pass matcher config / updater directly Lukas Wagner
2026-07-09 11:57 ` [PATCH proxmox-perl-rs 24/29] notify: opt into 'legacy-matchers' feature in proxmox-notify Lukas Wagner
2026-07-24  6:48   ` Arthur Bied-Charreton
2026-07-09 11:57 ` [PATCH proxmox-perl-rs 25/29] notify: add 'migrate_to_expression' parameter for get_matcher Lukas Wagner
2026-07-09 11:57 ` [PATCH manager 26/29] api: notification: pass config/updater directly to rust bindings Lukas Wagner
2026-07-09 11:57 ` [PATCH manager 27/29] api: notification: get_matcher: add 'migrate-to-expression' parameter Lukas Wagner
2026-07-09 11:57 ` [PATCH manager 28/29] api: notification: add 'expression' to matcher parameter schema Lukas Wagner
2026-07-09 11:57 ` [PATCH manager 29/29] ui: notification: enable new matcher UI Lukas Wagner
2026-07-24  6:44 ` [PATCH many 00/29] notifications: add nested match expressions Arthur Bied-Charreton
2026-07-24  6:53   ` Arthur Bied-Charreton

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=xljzz65dighwikxg6rffxtejtnmwfbdrq6aquhcrj6mhpekddw@dzd6xl4hoeyt \
    --to=a.bied-charreton@proxmox.com \
    --cc=l.wagner@proxmox.com \
    --cc=pbs-devel@lists.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