From 6afcbacd7cd0776c0064da06c6a28cfa2fda3618 Mon Sep 17 00:00:00 2001 From: stein Date: Thu, 30 Oct 2025 16:41:59 +0100 Subject: [PATCH 1/4] Custom time for search index schedules --- .../assets/locale/messages.admin-core.de.xlf | 32 ++++++----- .../assets/locale/messages.admin-core.en.xlf | 21 ++++---- .../assets/locale/messages.admin-core.nl.xlf | 32 ++++++----- .../search-index-scheduling.component.css | 21 ++++++-- .../search-index-scheduling.component.html | 37 ++++++++----- .../search-index-scheduling.component.ts | 53 +++++++++++++++---- .../lib/search-index/search-index.module.ts | 4 ++ 7 files changed, 135 insertions(+), 65 deletions(-) diff --git a/projects/admin-core/assets/locale/messages.admin-core.de.xlf b/projects/admin-core/assets/locale/messages.admin-core.de.xlf index b4110b3d9..9c7460de4 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.de.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.de.xlf @@ -10,6 +10,14 @@ Single-layer filter Einzel-Ebenen-Filter + + Created by at + Erstellt von + + + Last updated by at + Zuletzt aktualisiert von + Add application Anwendung hinzufügen @@ -2054,6 +2062,10 @@ There is no schedule for this search index yet. Für diesen Suchindex gibt es noch keinen Zeitplan. + + Pick a time + Wähle eine Uhrzeit + Priority Priorität @@ -2075,20 +2087,20 @@ Zeitplan - Every day at 18:00 - Jeden Tag um 18:00 Uhr + Every day + Jeden Tag Every hour Stündlich - Every first day of the month at 18:00 - Jeden ersten Tag im Monat um 18:00 Uhr + Every first day of the month + Jeden ersten Tag im Monat - Every week Monday at 18:00 - Jede Woche Montag um 18:00 Uhr + Every week on monday + Jede Woche im Montag No schedule @@ -2413,14 +2425,6 @@ Valid until Gültig bis - - Created by - Erstellt von - - - Last updated by - Zuletzt aktualisiert von - diff --git a/projects/admin-core/assets/locale/messages.admin-core.en.xlf b/projects/admin-core/assets/locale/messages.admin-core.en.xlf index b070b01e1..fe2bbaa12 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.en.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.en.xlf @@ -8,6 +8,12 @@ Single-layer filter + + Created by at + + + Last updated by at + Add application @@ -1505,6 +1511,9 @@ There is no schedule for this search index yet. + + Pick a time + Priority @@ -1521,16 +1530,16 @@ Schedule - Every day at 18:00 + Every day Every hour - Every first day of the month at 18:00 + Every first day of the month - Every week Monday at 18:00 + Every week on monday No schedule @@ -1775,12 +1784,6 @@ Valid until - - Created by - - - Last updated by - diff --git a/projects/admin-core/assets/locale/messages.admin-core.nl.xlf b/projects/admin-core/assets/locale/messages.admin-core.nl.xlf index b002d37cb..72d63b389 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.nl.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.nl.xlf @@ -10,6 +10,14 @@ Single-layer filter Enkele laag filter + + Created by at + Aangemaakt door + + + Last updated by at + Laatst bijgewerkt door + Add application Applicatie toevoegen @@ -2037,6 +2045,10 @@ There is no schedule for this search index yet. Er is nog geen planning voor deze zoekindex. + + Pick a time + Kies een tijd + Priority Prioriteit @@ -2058,20 +2070,20 @@ Planning - Every day at 18:00 - Elke dag om 18:00 + Every day + Elke dag Every hour Elk uur - Every first day of the month at 18:00 - Elke eerste dag van de maand om 18:00 + Every first day of the month + Elke eerste dag van de maand - Every week Monday at 18:00 - Elke week maandag om 18:00 + Every week on monday + Elke week op maandag No schedule @@ -2397,14 +2409,6 @@ Valid until Geldig tot - - Created by - Aangemaakt door - - - Last updated by - Laatst bijgewerkt door - diff --git a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.css b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.css index 546afdbb4..eb82d40a3 100644 --- a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.css +++ b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.css @@ -1,7 +1,3 @@ -.form-control { - margin-top: 16px; -} - .schedule-title { margin-bottom: 16px; } @@ -37,3 +33,20 @@ margin-top: 16px; margin-bottom: 3px; } + +.schedule-frequency-and-time { + display: flex; + gap: 16px; + flex-direction: row; + margin-bottom: 16px; +} + +.schedule-frequency-field { + flex: 1; +} + +.time-picker-field { + margin-top: 0; + flex: 0 0 150px; +} + diff --git a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html index 275c3fe60..95740905b 100644 --- a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html +++ b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html @@ -14,19 +14,30 @@

Schedule
- - Schedule - - @for (option of scheduleOptions; track $index) { - - {{ option.viewValue }} - - } - - - @if (scheduleForm.value.cronExpression) { +
+ + Schedule + + @for (option of scheduleOptions; track $index) { + + {{ option.viewValue }} + + } + + + @if (!(scheduleForm.get('partialCronExpression')?.value === HOURLY_CRON_EXPRESSION + || scheduleForm.get('partialCronExpression')?.value === '')) { + + Pick a time + + + + + } +
+ @if (scheduleForm.value.partialCronExpression) { Description >(DateAdapter); public taskSchedule: TaskSchedule | undefined = undefined; + public HOURLY_CRON_EXPRESSION = '0 0 0/1 1/1 * ? *'; @Input({ required: true }) public set searchIndex(form: SearchIndexModel | null) { @@ -32,14 +36,15 @@ export class SearchIndexSchedulingComponent implements OnInit { public scheduleOptions = [ { cronExpression: '', viewValue: $localize `:@@admin-core.search-index.schedule.no-schedule:No schedule` }, - { cronExpression: '0 0 0/1 1/1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-hour:Every hour` }, - { cronExpression: '0 0 18 1/1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-day:Every day at 18:00` }, - { cronExpression: '0 0 18 ? * MON *', viewValue: $localize `:@@admin-core.search-index.schedule.every-week:Every week Monday at 18:00` }, - { cronExpression: '0 0 18 1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-month:Every first day of the month at 18:00` }, + { cronExpression: this.HOURLY_CRON_EXPRESSION, viewValue: $localize `:@@admin-core.search-index.schedule.every-hour:Every hour` }, + { cronExpression: '1/1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-day:Every day` }, + { cronExpression: '? * MON *', viewValue: $localize `:@@admin-core.search-index.schedule.every-week:Every week on monday` }, + { cronExpression: '1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-month:Every first day of the month` }, ]; public scheduleForm = new FormGroup({ - cronExpression: new FormControl('', { nonNullable: true }), + partialCronExpression: new FormControl('', { nonNullable: true }), + time: new FormControl(null, { nonNullable: true }), description: new FormControl('', { nonNullable: true, validators: [Validators.required], @@ -48,6 +53,7 @@ export class SearchIndexSchedulingComponent implements OnInit { }); public ngOnInit(): void { + this._adapter.setLocale(this.locale); const scheduleFormChanges$ = this.scheduleForm.valueChanges.pipe( takeUntilDestroyed(this.destroyRef), debounceTime(250), @@ -66,10 +72,14 @@ export class SearchIndexSchedulingComponent implements OnInit { ) .subscribe(value => { let schedule: TaskSchedule | undefined = undefined; - if (value.cronExpression) { + if (value.partialCronExpression) { + const timePart = value.time ? this.timeToPartialCronExpression(value.time) : '0 0 18 '; + const cronExpression = value.partialCronExpression === this.HOURLY_CRON_EXPRESSION + ? value.partialCronExpression + : timePart + value.partialCronExpression; schedule = { ...this.taskSchedule, - cronExpression: value.cronExpression, + cronExpression: cronExpression, description: value.description, priority: value.priority, }; @@ -84,13 +94,15 @@ export class SearchIndexSchedulingComponent implements OnInit { private initForm(schedule: TaskSchedule | undefined, searchIndexName?: string) { const preFillDescription: string = $localize `:@@admin-core.search-index.schedule.prefill-description:Update ${searchIndexName}`; if (!schedule) { - this.scheduleForm.patchValue({ cronExpression: '', description: preFillDescription, priority: undefined }, { emitEvent: false }); + this.scheduleForm.patchValue({ partialCronExpression: '', description: preFillDescription, priority: undefined }, { emitEvent: false }); } else { - if (!this.scheduleOptions.some(option => option.cronExpression === schedule.cronExpression)) { + const { time, partialCronExpression } = this.splitCronExpression(schedule.cronExpression); + if (!this.scheduleOptions.some(option => option.cronExpression === partialCronExpression)) { this.scheduleOptions.push({ cronExpression: schedule.cronExpression, viewValue: schedule.cronExpression }); } this.scheduleForm.patchValue({ - cronExpression: schedule.cronExpression, + partialCronExpression: partialCronExpression, + time: time, description: schedule.description || preFillDescription, priority: schedule.priority, }, { emitEvent: false }); @@ -104,4 +116,23 @@ export class SearchIndexSchedulingComponent implements OnInit { && this.scheduleForm.valid; } + private timeToPartialCronExpression(time: Date): string { + const minutes = isNaN(time.getMinutes()) ? 0 : time.getMinutes(); + const hours = isNaN(time.getHours()) ? 6 : time.getHours(); + return `0 ${minutes} ${hours} `; + } + + private splitCronExpression(cronExpression: string): { time: Date | null; partialCronExpression: string } { + const parts = cronExpression.split(' '); + if (cronExpression === this.HOURLY_CRON_EXPRESSION) { + return { time: null, partialCronExpression: cronExpression }; + } + const hours = parseInt(parts[2], 10); + const minutes = parseInt(parts[1], 10); + const time = new Date(); + time.setHours(hours, minutes, 0); + const partialCronExpression = parts.slice(3).join(' '); + return { time, partialCronExpression }; + } + } diff --git a/projects/admin-core/src/lib/search-index/search-index.module.ts b/projects/admin-core/src/lib/search-index/search-index.module.ts index c4ca711be..ec93d31f5 100644 --- a/projects/admin-core/src/lib/search-index/search-index.module.ts +++ b/projects/admin-core/src/lib/search-index/search-index.module.ts @@ -17,6 +17,7 @@ import { FormModule } from '../form/form.module'; import { SearchIndexService } from './services/search-index.service'; import { SearchIndexAttributeListComponent } from './search-index-attribute-list/search-index-attribute-list.component'; import { SearchIndexSchedulingComponent } from './search-index-scheduling/search-index-scheduling.component'; +import { MatTimepicker, MatTimepickerInput, MatTimepickerToggle } from '@angular/material/timepicker'; @NgModule({ declarations: [ @@ -36,6 +37,9 @@ import { SearchIndexSchedulingComponent } from './search-index-scheduling/search SharedAdminComponentsModule, CatalogModule, FormModule, + MatTimepickerToggle, + MatTimepicker, + MatTimepickerInput, ], exports: [ SearchIndexListComponent, From b91fe35dda5d27d36e2f69dbbcad5be8ed07c0d0 Mon Sep 17 00:00:00 2001 From: stein Date: Thu, 30 Oct 2025 16:51:02 +0100 Subject: [PATCH 2/4] Fill time field with 06:00 when choosing a schedule --- .../search-index-scheduling.component.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts index ebc6bf9b7..2d8d2f6ee 100644 --- a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts +++ b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts @@ -94,7 +94,14 @@ export class SearchIndexSchedulingComponent implements OnInit { private initForm(schedule: TaskSchedule | undefined, searchIndexName?: string) { const preFillDescription: string = $localize `:@@admin-core.search-index.schedule.prefill-description:Update ${searchIndexName}`; if (!schedule) { - this.scheduleForm.patchValue({ partialCronExpression: '', description: preFillDescription, priority: undefined }, { emitEvent: false }); + const time = new Date(); + time.setHours(6, 0, 0); + this.scheduleForm.patchValue({ + partialCronExpression: '', + time: time, + description: preFillDescription, + priority: undefined, + }, { emitEvent: false }); } else { const { time, partialCronExpression } = this.splitCronExpression(schedule.cronExpression); if (!this.scheduleOptions.some(option => option.cronExpression === partialCronExpression)) { From 039b3b03e78a697930b052fa9320e423d210e9cb Mon Sep 17 00:00:00 2001 From: stein Date: Fri, 31 Oct 2025 13:47:30 +0100 Subject: [PATCH 3/4] Show schedule on tasks page as readable text --- .../assets/locale/messages.admin-core.de.xlf | 12 +++-- .../assets/locale/messages.admin-core.en.xlf | 9 ++-- .../assets/locale/messages.admin-core.nl.xlf | 12 +++-- .../search-index-scheduling.component.html | 2 +- .../search-index-scheduling.component.ts | 35 ++++----------- .../tasks/helpers/cron-expression.helper.ts | 44 +++++++++++++++++++ .../task-details-row.component.html | 5 ++- .../task-details-row.component.ts | 11 ++++- 8 files changed, 90 insertions(+), 40 deletions(-) create mode 100644 projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts diff --git a/projects/admin-core/assets/locale/messages.admin-core.de.xlf b/projects/admin-core/assets/locale/messages.admin-core.de.xlf index 9c7460de4..417826718 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.de.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.de.xlf @@ -2197,6 +2197,10 @@ Select settings from the left side Wählen Sie Einstellungen auf der linken Seite aus + + at + um + Delete task '' Aufgabe '' löschen @@ -2225,10 +2229,6 @@ last execution succesful letzte Ausführung erfolgreich - - Cron expression - Planung (Cron expression) - Description Beschreibung @@ -2261,6 +2261,10 @@ Progress Fortschritt + + Schedule + Zeitplan + Scheduling state Planungsstatus diff --git a/projects/admin-core/assets/locale/messages.admin-core.en.xlf b/projects/admin-core/assets/locale/messages.admin-core.en.xlf index fe2bbaa12..0d54d3995 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.en.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.en.xlf @@ -1613,6 +1613,9 @@ Select settings from the left side + + at + Delete task '' @@ -1634,9 +1637,6 @@ last execution succesful - - Cron expression - Description @@ -1661,6 +1661,9 @@ Progress + + Schedule + Scheduling state diff --git a/projects/admin-core/assets/locale/messages.admin-core.nl.xlf b/projects/admin-core/assets/locale/messages.admin-core.nl.xlf index 72d63b389..bc4ce5b9c 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.nl.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.nl.xlf @@ -2181,6 +2181,10 @@ Select settings from the left side Selecteer een instelling aan de linker kant + + at + om + Delete task '' Verwijder taak @@ -2209,10 +2213,6 @@ last execution succesful laatste uitvoering succesvol - - Cron expression - Planning (Cron expression) - Description Beschrijving @@ -2245,6 +2245,10 @@ Progress Voortgang + + Schedule + Planning + Scheduling state Planning staat diff --git a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html index 95740905b..9d594b96a 100644 --- a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html +++ b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.html @@ -27,7 +27,7 @@

Schedule - @if (!(scheduleForm.get('partialCronExpression')?.value === HOURLY_CRON_EXPRESSION + @if (!(scheduleForm.get('partialCronExpression')?.value === hourlyCronExpression || scheduleForm.get('partialCronExpression')?.value === '')) { Pick a time diff --git a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts index 2d8d2f6ee..1e0f9438f 100644 --- a/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts +++ b/projects/admin-core/src/lib/search-index/search-index-scheduling/search-index-scheduling.component.ts @@ -5,6 +5,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs'; import { FormHelper } from '../../helpers/form.helper'; import { DateAdapter } from '@angular/material/core'; +import { CronExpressionHelper } from '../../tasks/helpers/cron-expression.helper'; @Component({ selector: 'tm-admin-search-index-scheduling', @@ -20,7 +21,6 @@ export class SearchIndexSchedulingComponent implements OnInit { private readonly _adapter = inject>(DateAdapter); public taskSchedule: TaskSchedule | undefined = undefined; - public HOURLY_CRON_EXPRESSION = '0 0 0/1 1/1 * ? *'; @Input({ required: true }) public set searchIndex(form: SearchIndexModel | null) { @@ -34,13 +34,8 @@ export class SearchIndexSchedulingComponent implements OnInit { @Output() public formChanged = new EventEmitter(); - public scheduleOptions = [ - { cronExpression: '', viewValue: $localize `:@@admin-core.search-index.schedule.no-schedule:No schedule` }, - { cronExpression: this.HOURLY_CRON_EXPRESSION, viewValue: $localize `:@@admin-core.search-index.schedule.every-hour:Every hour` }, - { cronExpression: '1/1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-day:Every day` }, - { cronExpression: '? * MON *', viewValue: $localize `:@@admin-core.search-index.schedule.every-week:Every week on monday` }, - { cronExpression: '1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-month:Every first day of the month` }, - ]; + public hourlyCronExpression = CronExpressionHelper.HOURLY_CRON_EXPRESSION; + public scheduleOptions = CronExpressionHelper.SCHEDULE_OPTIONS; public scheduleForm = new FormGroup({ partialCronExpression: new FormControl('', { nonNullable: true }), @@ -73,8 +68,8 @@ export class SearchIndexSchedulingComponent implements OnInit { .subscribe(value => { let schedule: TaskSchedule | undefined = undefined; if (value.partialCronExpression) { - const timePart = value.time ? this.timeToPartialCronExpression(value.time) : '0 0 18 '; - const cronExpression = value.partialCronExpression === this.HOURLY_CRON_EXPRESSION + const timePart = value.time ? this.timeToPartialCronExpression(value.time) : '0 0 6 '; + const cronExpression = value.partialCronExpression === CronExpressionHelper.HOURLY_CRON_EXPRESSION ? value.partialCronExpression : timePart + value.partialCronExpression; schedule = { @@ -103,7 +98,7 @@ export class SearchIndexSchedulingComponent implements OnInit { priority: undefined, }, { emitEvent: false }); } else { - const { time, partialCronExpression } = this.splitCronExpression(schedule.cronExpression); + const { time, partialCronExpression } = CronExpressionHelper.splitCronExpression(schedule.cronExpression); if (!this.scheduleOptions.some(option => option.cronExpression === partialCronExpression)) { this.scheduleOptions.push({ cronExpression: schedule.cronExpression, viewValue: schedule.cronExpression }); } @@ -124,22 +119,10 @@ export class SearchIndexSchedulingComponent implements OnInit { } private timeToPartialCronExpression(time: Date): string { - const minutes = isNaN(time.getMinutes()) ? 0 : time.getMinutes(); - const hours = isNaN(time.getHours()) ? 6 : time.getHours(); + const timeDateObject = new Date(time); + const minutes = isNaN(timeDateObject.getMinutes()) ? 0 : timeDateObject.getMinutes(); + const hours = isNaN(timeDateObject.getHours()) ? 6 : timeDateObject.getHours(); return `0 ${minutes} ${hours} `; } - private splitCronExpression(cronExpression: string): { time: Date | null; partialCronExpression: string } { - const parts = cronExpression.split(' '); - if (cronExpression === this.HOURLY_CRON_EXPRESSION) { - return { time: null, partialCronExpression: cronExpression }; - } - const hours = parseInt(parts[2], 10); - const minutes = parseInt(parts[1], 10); - const time = new Date(); - time.setHours(hours, minutes, 0); - const partialCronExpression = parts.slice(3).join(' '); - return { time, partialCronExpression }; - } - } diff --git a/projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts b/projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts new file mode 100644 index 000000000..2d84e6167 --- /dev/null +++ b/projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts @@ -0,0 +1,44 @@ + + +export class CronExpressionHelper { + public static HOURLY_CRON_EXPRESSION = '0 0 0/1 1/1 * ? *'; + + public static SCHEDULE_OPTIONS = [ + { cronExpression: '', viewValue: $localize `:@@admin-core.search-index.schedule.no-schedule:No schedule` }, + { cronExpression: CronExpressionHelper.HOURLY_CRON_EXPRESSION, viewValue: $localize `:@@admin-core.search-index.schedule.every-hour:Every hour` }, + { cronExpression: '1/1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-day:Every day` }, + { cronExpression: '? * MON *', viewValue: $localize `:@@admin-core.search-index.schedule.every-week:Every week on monday` }, + { cronExpression: '1 * ? *', viewValue: $localize `:@@admin-core.search-index.schedule.every-month:Every first day of the month` }, + ]; + + public static splitCronExpression(cronExpression: string): { time: Date | null; partialCronExpression: string } { + const parts = cronExpression.split(' '); + if (cronExpression === CronExpressionHelper.HOURLY_CRON_EXPRESSION) { + return { time: null, partialCronExpression: cronExpression }; + } + const hours = parseInt(parts[2], 10); + const minutes = parseInt(parts[1], 10); + const time = new Date(); + time.setHours(hours, minutes, 0); + const partialCronExpression = parts.slice(3).join(' '); + return { time, partialCronExpression }; + } + + public static cronExpressionToReadableText(cronExpression: string): string { + if (cronExpression === CronExpressionHelper.HOURLY_CRON_EXPRESSION) { + return CronExpressionHelper.SCHEDULE_OPTIONS + .find(option => option.cronExpression === CronExpressionHelper.HOURLY_CRON_EXPRESSION)?.viewValue || cronExpression; + } + const { time, partialCronExpression } = CronExpressionHelper.splitCronExpression(cronExpression); + const timeString = time + ? `${time.getHours().toString().padStart(2, '0')}:${time.getMinutes().toString().padStart(2, '0')}` + : ''; + const schedule = CronExpressionHelper.SCHEDULE_OPTIONS + .find(option => option.cronExpression === partialCronExpression); + if (schedule) { + return timeString ? schedule.viewValue + $localize `:@@admin-core.tasks.at: at ` + timeString : schedule.viewValue; + } + return cronExpression; + } + +} diff --git a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html index c2c1abb00..bd5414621 100644 --- a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html +++ b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html @@ -4,7 +4,10 @@
@if (canConvertToDate(infoValue)) { {{ infoValue | date: 'medium' }} - } @else { + } @else if (isCronExpression()) { + {{ cronExpressionToReadableText(infoValue) }} + } + @else { {{ infoValue }} }
diff --git a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts index 675542a7e..506c2d019 100644 --- a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts +++ b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts @@ -1,4 +1,5 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { CronExpressionHelper } from '../helpers/cron-expression.helper'; @Component({ selector: 'tm-admin-task-details-row', @@ -23,7 +24,7 @@ export class TaskDetailsRowComponent { type: $localize `:@@admin-core.tasks.task-details.type:Type`, description: $localize `:@@admin-core.tasks.task-details.description:Description`, uuid: $localize `:@@admin-core.tasks.task-details.uuid:Uuid`, - cronExpression: $localize `:@@admin-core.tasks.task-details.cron-expression:Cron expression`, + cronExpression: $localize `:@@admin-core.tasks.task-details.schedule:Schedule`, timezone: $localize `:@@admin-core.tasks.task-details.timezone:Timezone`, startTime: $localize `:@@admin-core.tasks.task-details.start-time:Start time`, lastTime: $localize `:@@admin-core.tasks.task-details.last-time:Last time the task was started`, @@ -45,4 +46,12 @@ export class TaskDetailsRowComponent { return TaskDetailsRowComponent.DATE_VALIDATOR_PATTERN.test(original.toString().substring(0, 10)); } + public isCronExpression(): boolean { + return this.infoType === 'cronExpression'; + } + + public cronExpressionToReadableText(cronExpression: string): string { + return CronExpressionHelper.cronExpressionToReadableText(cronExpression); + } + } From cbd686efa93076c00e2c7e15a3d61ef619b0886d Mon Sep 17 00:00:00 2001 From: stein Date: Fri, 31 Oct 2025 14:09:39 +0100 Subject: [PATCH 4/4] Use locale to show time in tasks schedule --- .../admin-core/assets/locale/messages.admin-core.de.xlf | 8 ++++---- .../admin-core/assets/locale/messages.admin-core.en.xlf | 6 +++--- .../admin-core/assets/locale/messages.admin-core.nl.xlf | 8 ++++---- .../src/lib/tasks/helpers/cron-expression.helper.ts | 6 +++--- .../task-details-row/task-details-row.component.html | 2 +- .../tasks/task-details-row/task-details-row.component.ts | 5 +++-- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/projects/admin-core/assets/locale/messages.admin-core.de.xlf b/projects/admin-core/assets/locale/messages.admin-core.de.xlf index 417826718..9c9f30b4e 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.de.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.de.xlf @@ -2197,10 +2197,6 @@ Select settings from the left side Wählen Sie Einstellungen auf der linken Seite aus - - at - um - Delete task '' Aufgabe '' löschen @@ -2329,6 +2325,10 @@ Tasks Aufgaben + + at + um Uhr + Delete file? Datei löschen? diff --git a/projects/admin-core/assets/locale/messages.admin-core.en.xlf b/projects/admin-core/assets/locale/messages.admin-core.en.xlf index 0d54d3995..9cf4298ce 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.en.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.en.xlf @@ -1613,9 +1613,6 @@ Select settings from the left side - - at - Delete task '' @@ -1712,6 +1709,9 @@ Tasks + + at + Delete file? diff --git a/projects/admin-core/assets/locale/messages.admin-core.nl.xlf b/projects/admin-core/assets/locale/messages.admin-core.nl.xlf index bc4ce5b9c..4dc985e6c 100644 --- a/projects/admin-core/assets/locale/messages.admin-core.nl.xlf +++ b/projects/admin-core/assets/locale/messages.admin-core.nl.xlf @@ -2181,10 +2181,6 @@ Select settings from the left side Selecteer een instelling aan de linker kant - - at - om - Delete task '' Verwijder taak @@ -2313,6 +2309,10 @@ Tasks Taken + + at + om uur + Delete file? Bestand verwijderen? diff --git a/projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts b/projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts index 2d84e6167..968efa549 100644 --- a/projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts +++ b/projects/admin-core/src/lib/tasks/helpers/cron-expression.helper.ts @@ -24,19 +24,19 @@ export class CronExpressionHelper { return { time, partialCronExpression }; } - public static cronExpressionToReadableText(cronExpression: string): string { + public static cronExpressionToReadableText(cronExpression: string, locale: string): string { if (cronExpression === CronExpressionHelper.HOURLY_CRON_EXPRESSION) { return CronExpressionHelper.SCHEDULE_OPTIONS .find(option => option.cronExpression === CronExpressionHelper.HOURLY_CRON_EXPRESSION)?.viewValue || cronExpression; } const { time, partialCronExpression } = CronExpressionHelper.splitCronExpression(cronExpression); const timeString = time - ? `${time.getHours().toString().padStart(2, '0')}:${time.getMinutes().toString().padStart(2, '0')}` + ? time.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit' }) : ''; const schedule = CronExpressionHelper.SCHEDULE_OPTIONS .find(option => option.cronExpression === partialCronExpression); if (schedule) { - return timeString ? schedule.viewValue + $localize `:@@admin-core.tasks.at: at ` + timeString : schedule.viewValue; + return timeString ? $localize `:@@admin-core.tasks.time:${schedule.viewValue} at ${timeString}` : schedule.viewValue; } return cronExpression; } diff --git a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html index bd5414621..76ef4fed8 100644 --- a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html +++ b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.html @@ -3,7 +3,7 @@ @if (infoValue) {
@if (canConvertToDate(infoValue)) { - {{ infoValue | date: 'medium' }} + {{ infoValue | date: 'medium':'':locale }} } @else if (isCronExpression()) { {{ cronExpressionToReadableText(infoValue) }} } diff --git a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts index 506c2d019..217ff0afe 100644 --- a/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts +++ b/projects/admin-core/src/lib/tasks/task-details-row/task-details-row.component.ts @@ -1,4 +1,4 @@ -import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { Component, ChangeDetectionStrategy, Input, inject, LOCALE_ID } from '@angular/core'; import { CronExpressionHelper } from '../helpers/cron-expression.helper'; @Component({ @@ -9,6 +9,7 @@ import { CronExpressionHelper } from '../helpers/cron-expression.helper'; standalone: false, }) export class TaskDetailsRowComponent { + public locale = inject(LOCALE_ID); private static DATE_VALIDATOR_PATTERN = /^\d{4}-\d{2}-\d{2}$/; @@ -51,7 +52,7 @@ export class TaskDetailsRowComponent { } public cronExpressionToReadableText(cronExpression: string): string { - return CronExpressionHelper.cronExpressionToReadableText(cronExpression); + return CronExpressionHelper.cronExpressionToReadableText(cronExpression, this.locale); } }