@masterteam/system-settings 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/assets/i18n/ar.json +1 -0
- package/assets/i18n/en.json +1 -0
- package/assets/system-settings.css +1 -1
- package/fesm2022/{masterteam-system-settings-email-delivery-shell-Bf6EXLD2.mjs → masterteam-system-settings-email-delivery-shell-CBPKuYXr.mjs} +2 -2
- package/fesm2022/{masterteam-system-settings-email-delivery-shell-Bf6EXLD2.mjs.map → masterteam-system-settings-email-delivery-shell-CBPKuYXr.mjs.map} +1 -1
- package/fesm2022/{masterteam-system-settings-masterteam-system-settings-BjftcBn5.mjs → masterteam-system-settings-masterteam-system-settings-DbsjQ7c7.mjs} +221 -63
- package/fesm2022/masterteam-system-settings-masterteam-system-settings-DbsjQ7c7.mjs.map +1 -0
- package/fesm2022/masterteam-system-settings.mjs +1 -1
- package/package.json +3 -3
- package/types/masterteam-system-settings.d.ts +64 -6
- package/fesm2022/masterteam-system-settings-masterteam-system-settings-BjftcBn5.mjs.map +0 -1
|
@@ -205,8 +205,15 @@ class SystemSettingsTimeSheetSection {
|
|
|
205
205
|
dayOptions = input([], ...(ngDevMode ? [{ debugName: "dayOptions" }] : /* istanbul ignore next */ []));
|
|
206
206
|
frequencyOptions = input([], ...(ngDevMode ? [{ debugName: "frequencyOptions" }] : /* istanbul ignore next */ []));
|
|
207
207
|
maxDailyBelowDaily = input(false, ...(ngDevMode ? [{ debugName: "maxDailyBelowDaily" }] : /* istanbul ignore next */ []));
|
|
208
|
+
/**
|
|
209
|
+
* Hides the Timesheet Policy card only. Global Working Hours always
|
|
210
|
+
* renders - it is host-relevant on its own (working days/hours feed
|
|
211
|
+
* scheduling and calendar logic beyond timesheets), so a host without a
|
|
212
|
+
* timesheet feature can still configure it.
|
|
213
|
+
*/
|
|
214
|
+
hidePolicy = input(false, ...(ngDevMode ? [{ debugName: "hidePolicy" }] : /* istanbul ignore next */ []));
|
|
208
215
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsTimeSheetSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
209
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
216
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: SystemSettingsTimeSheetSection, isStandalone: true, selector: "mt-system-settings-timesheet-section", inputs: { policyForm: { classPropertyName: "policyForm", publicName: "policyForm", isSignal: true, isRequired: true, transformFunction: null }, globalWorkingHoursForm: { classPropertyName: "globalWorkingHoursForm", publicName: "globalWorkingHoursForm", isSignal: true, isRequired: true, transformFunction: null }, dayOptions: { classPropertyName: "dayOptions", publicName: "dayOptions", isSignal: true, isRequired: false, transformFunction: null }, frequencyOptions: { classPropertyName: "frequencyOptions", publicName: "frequencyOptions", isSignal: true, isRequired: false, transformFunction: null }, maxDailyBelowDaily: { classPropertyName: "maxDailyBelowDaily", publicName: "maxDailyBelowDaily", isSignal: true, isRequired: false, transformFunction: null }, hidePolicy: { classPropertyName: "hidePolicy", publicName: "hidePolicy", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <div\r\n [class]=\"\r\n hidePolicy() ? 'grid gap-6 mb-6' : 'grid gap-6 xl:grid-cols-2 mb-6'\r\n \"\r\n >\r\n @if (!hidePolicy()) {\r\n <mt-card class=\"block\">\r\n <div [formGroup]=\"policyForm()\" class=\"space-y-5\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"general-card-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"general-card-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-select-field\r\n formControlName=\"frequency\"\r\n [options]=\"frequencyOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('frequency')\"\r\n [placeholder]=\"t('frequency-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-number-field\r\n formControlName=\"maxDailyHours\"\r\n [label]=\"t('max-daily-hours')\"\r\n [placeholder]=\"t('max-daily-hours-placeholder')\"\r\n [min]=\"1\"\r\n [max]=\"24\"\r\n [required]=\"true\"\r\n [hint]=\"maxDailyBelowDaily() ? t('max-daily-below-daily') : ''\"\r\n />\r\n\r\n <mt-number-field\r\n formControlName=\"submissionDeadlineDays\"\r\n [label]=\"t('submission-deadline-days')\"\r\n [placeholder]=\"t('submission-deadline-days-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"90\"\r\n [required]=\"true\"\r\n [hint]=\"t('submission-deadline-hint')\"\r\n />\r\n </div>\r\n\r\n <div class=\"grid gap-3 md:grid-cols-2\">\r\n <mt-toggle-field\r\n formControlName=\"requireDescription\"\r\n [label]=\"t('require-description')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowFutureEntry\"\r\n [label]=\"t('allow-future-entry')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowBackdatedEntry\"\r\n [label]=\"t('allow-backdated-entry')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"autoLockOnApprove\"\r\n [label]=\"t('auto-lock-on-approve')\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n\r\n <mt-card class=\"block\">\r\n <div [formGroup]=\"globalWorkingHoursForm()\" class=\"space-y-5\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"working-hours-card-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"working-hours-card-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-multi-select-field\r\n class=\"md:col-span-2\"\r\n formControlName=\"workingDays\"\r\n [options]=\"dayOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'dayNumber'\"\r\n [label]=\"t('working-days')\"\r\n [placeholder]=\"t('working-days-placeholder')\"\r\n [display]=\"'chip'\"\r\n [required]=\"true\"\r\n [showClear]=\"true\"\r\n />\r\n\r\n <mt-number-field\r\n class=\"md:col-span-2\"\r\n formControlName=\"hoursPerDay\"\r\n [label]=\"t('daily-working-hours')\"\r\n [placeholder]=\"t('daily-working-hours-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"24\"\r\n [required]=\"true\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: MultiSelectField, selector: "mt-multi-select-field", inputs: ["field", "label", "placeholder", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "display", "required", "maxSelectedLabels", "group", "virtualScroll", "virtualScrollItemSize", "optionGroupLabel", "optionGroupChildren", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
210
217
|
}
|
|
211
218
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsTimeSheetSection, decorators: [{
|
|
212
219
|
type: Component,
|
|
@@ -218,8 +225,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
218
225
|
NumberField,
|
|
219
226
|
SelectField,
|
|
220
227
|
ToggleField,
|
|
221
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <div
|
|
222
|
-
}], propDecorators: { policyForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "policyForm", required: true }] }], globalWorkingHoursForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalWorkingHoursForm", required: true }] }], dayOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "dayOptions", required: false }] }], frequencyOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "frequencyOptions", required: false }] }], maxDailyBelowDaily: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDailyBelowDaily", required: false }] }] } });
|
|
228
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <div\r\n [class]=\"\r\n hidePolicy() ? 'grid gap-6 mb-6' : 'grid gap-6 xl:grid-cols-2 mb-6'\r\n \"\r\n >\r\n @if (!hidePolicy()) {\r\n <mt-card class=\"block\">\r\n <div [formGroup]=\"policyForm()\" class=\"space-y-5\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"general-card-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"general-card-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-select-field\r\n formControlName=\"frequency\"\r\n [options]=\"frequencyOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('frequency')\"\r\n [placeholder]=\"t('frequency-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-number-field\r\n formControlName=\"maxDailyHours\"\r\n [label]=\"t('max-daily-hours')\"\r\n [placeholder]=\"t('max-daily-hours-placeholder')\"\r\n [min]=\"1\"\r\n [max]=\"24\"\r\n [required]=\"true\"\r\n [hint]=\"maxDailyBelowDaily() ? t('max-daily-below-daily') : ''\"\r\n />\r\n\r\n <mt-number-field\r\n formControlName=\"submissionDeadlineDays\"\r\n [label]=\"t('submission-deadline-days')\"\r\n [placeholder]=\"t('submission-deadline-days-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"90\"\r\n [required]=\"true\"\r\n [hint]=\"t('submission-deadline-hint')\"\r\n />\r\n </div>\r\n\r\n <div class=\"grid gap-3 md:grid-cols-2\">\r\n <mt-toggle-field\r\n formControlName=\"requireDescription\"\r\n [label]=\"t('require-description')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowFutureEntry\"\r\n [label]=\"t('allow-future-entry')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowBackdatedEntry\"\r\n [label]=\"t('allow-backdated-entry')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"autoLockOnApprove\"\r\n [label]=\"t('auto-lock-on-approve')\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n\r\n <mt-card class=\"block\">\r\n <div [formGroup]=\"globalWorkingHoursForm()\" class=\"space-y-5\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"working-hours-card-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"working-hours-card-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-multi-select-field\r\n class=\"md:col-span-2\"\r\n formControlName=\"workingDays\"\r\n [options]=\"dayOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'dayNumber'\"\r\n [label]=\"t('working-days')\"\r\n [placeholder]=\"t('working-days-placeholder')\"\r\n [display]=\"'chip'\"\r\n [required]=\"true\"\r\n [showClear]=\"true\"\r\n />\r\n\r\n <mt-number-field\r\n class=\"md:col-span-2\"\r\n formControlName=\"hoursPerDay\"\r\n [label]=\"t('daily-working-hours')\"\r\n [placeholder]=\"t('daily-working-hours-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"24\"\r\n [required]=\"true\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n </div>\r\n</ng-container>\r\n" }]
|
|
229
|
+
}], propDecorators: { policyForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "policyForm", required: true }] }], globalWorkingHoursForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalWorkingHoursForm", required: true }] }], dayOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "dayOptions", required: false }] }], frequencyOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "frequencyOptions", required: false }] }], maxDailyBelowDaily: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDailyBelowDaily", required: false }] }], hidePolicy: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidePolicy", required: false }] }] } });
|
|
223
230
|
|
|
224
231
|
/** The settings record General Configuration fields live in. */
|
|
225
232
|
const GENERAL_CONFIGURATION_SETTING_KEY = 'Identity';
|
|
@@ -267,6 +274,11 @@ const EXPLORE_DEFAULT_VIEW_OPTIONS = [
|
|
|
267
274
|
translationKey: 'explore-default-view-timeline',
|
|
268
275
|
icon: 'time.clock',
|
|
269
276
|
},
|
|
277
|
+
{
|
|
278
|
+
id: 'table',
|
|
279
|
+
translationKey: 'explore-default-view-table',
|
|
280
|
+
icon: 'layout.table',
|
|
281
|
+
},
|
|
270
282
|
];
|
|
271
283
|
const DEFAULT_GENERAL_CONFIGURATION = {
|
|
272
284
|
defaultExploreView: 'cards',
|
|
@@ -385,23 +397,61 @@ class SystemSettings {
|
|
|
385
397
|
transloco = inject(TranslocoService);
|
|
386
398
|
modal = inject(ModalService);
|
|
387
399
|
toast = inject(ToastService);
|
|
388
|
-
|
|
389
|
-
|
|
400
|
+
/**
|
|
401
|
+
* Base path for the settings API. Safe to omit from a route's `data`
|
|
402
|
+
* even when the host uses `withComponentInputBinding()` - see
|
|
403
|
+
* `defaultWhenUndefined`.
|
|
404
|
+
*/
|
|
405
|
+
apiBasePath = input('settings', { ...(ngDevMode ? { debugName: "apiBasePath" } : /* istanbul ignore next */ {}), transform: defaultWhenUndefined('settings') });
|
|
406
|
+
/**
|
|
407
|
+
* Route the page's back button navigates to; falsy (including an
|
|
408
|
+
* explicit `null`) disables it - see `goBack()`. Safe to omit from a
|
|
409
|
+
* route's `data` even when the host uses `withComponentInputBinding()`
|
|
410
|
+
* - see `defaultWhenUndefined`.
|
|
411
|
+
*/
|
|
412
|
+
backRoute = input('/control-panel/product-settings', { ...(ngDevMode ? { debugName: "backRoute" } : /* istanbul ignore next */ {}), transform: defaultWhenUndefined('/control-panel/product-settings') });
|
|
390
413
|
formatSchema = input(null, ...(ngDevMode ? [{ debugName: "formatSchema" }] : /* istanbul ignore next */ []));
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
];
|
|
414
|
+
/**
|
|
415
|
+
* Breadcrumb "home" link. `null` renders the crumb as non-navigable.
|
|
416
|
+
* Safe to omit from a route's `data` even when the host uses
|
|
417
|
+
* `withComponentInputBinding()` - see `defaultWhenUndefined`.
|
|
418
|
+
*/
|
|
419
|
+
homeRoute = input('/control-panel/workspaces', { ...(ngDevMode ? { debugName: "homeRoute" } : /* istanbul ignore next */ {}), transform: defaultWhenUndefined('/control-panel/workspaces') });
|
|
420
|
+
/**
|
|
421
|
+
* Hides the Timesheet Policy card only - Global Working Hours stays
|
|
422
|
+
* visible and required, since it's host-relevant on its own (working
|
|
423
|
+
* days/hours feed scheduling and calendar logic beyond timesheets).
|
|
424
|
+
* TimeSheetPolicy's fields lose their validators and are excluded from
|
|
425
|
+
* save diffing while hidden; GlobalWorkingHours is unaffected.
|
|
426
|
+
*/
|
|
427
|
+
hideTimeSheet = input(false, ...(ngDevMode ? [{ debugName: "hideTimeSheet" }] : /* istanbul ignore next */ []));
|
|
428
|
+
/**
|
|
429
|
+
* Restricts which Explore-default-view options are offered. `null` (the
|
|
430
|
+
* default) preserves pplus5's original two options (`'cards'`,
|
|
431
|
+
* `'timeline'`) - NOT the full catalog, since the catalog now also
|
|
432
|
+
* contains `'table'` (added for splus4) and an unset input must not
|
|
433
|
+
* silently surface it to hosts that never opted in. Pass the ids
|
|
434
|
+
* explicitly (e.g. `['cards', 'table']`, or all three) to get anything
|
|
435
|
+
* else.
|
|
436
|
+
*/
|
|
437
|
+
exploreViewOptions = input(null, ...(ngDevMode ? [{ debugName: "exploreViewOptions" }] : /* istanbul ignore next */ []));
|
|
438
|
+
breadcrumbItems = computed(() => {
|
|
439
|
+
const homeRoute = this.homeRoute();
|
|
440
|
+
return [
|
|
441
|
+
{
|
|
442
|
+
label: '',
|
|
443
|
+
icon: 'general.home-line',
|
|
444
|
+
...(homeRoute ? { routerLink: homeRoute } : {}),
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
label: this.transloco.translate('product-settings.product-settings'),
|
|
448
|
+
routerLink: '/control-panel/product-settings',
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
label: this.transloco.translate('system-settings.page-title'),
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
}, ...(ngDevMode ? [{ debugName: "breadcrumbItems" }] : /* istanbul ignore next */ []));
|
|
405
455
|
form = new FormGroup({
|
|
406
456
|
generalConfiguration: new FormGroup({
|
|
407
457
|
defaultExploreView: new FormControl('cards', {
|
|
@@ -425,36 +475,24 @@ class SystemSettings {
|
|
|
425
475
|
days: new FormControl([], { nonNullable: true }),
|
|
426
476
|
}),
|
|
427
477
|
timeSheetPolicy: new FormGroup({
|
|
428
|
-
frequency: new FormControl('Weekly',
|
|
429
|
-
maxDailyHours: new FormControl(24,
|
|
430
|
-
Validators.required,
|
|
431
|
-
Validators.min(1),
|
|
432
|
-
Validators.max(24),
|
|
433
|
-
]),
|
|
478
|
+
frequency: new FormControl('Weekly', TIME_SHEET_FREQUENCY_VALIDATORS),
|
|
479
|
+
maxDailyHours: new FormControl(24, TIME_SHEET_MAX_DAILY_HOURS_VALIDATORS),
|
|
434
480
|
requireDescription: new FormControl(false, { nonNullable: true }),
|
|
435
481
|
allowFutureEntry: new FormControl(false, { nonNullable: true }),
|
|
436
482
|
allowBackdatedEntry: new FormControl(true, { nonNullable: true }),
|
|
437
483
|
autoLockOnApprove: new FormControl(true, { nonNullable: true }),
|
|
438
|
-
submissionDeadlineDays: new FormControl(3,
|
|
439
|
-
Validators.required,
|
|
440
|
-
Validators.min(0),
|
|
441
|
-
Validators.max(90),
|
|
442
|
-
]),
|
|
484
|
+
submissionDeadlineDays: new FormControl(3, TIME_SHEET_SUBMISSION_DEADLINE_VALIDATORS),
|
|
443
485
|
}),
|
|
444
486
|
globalWorkingHours: new FormGroup({
|
|
445
|
-
timeZoneId: new FormControl(null,
|
|
487
|
+
timeZoneId: new FormControl(null, GLOBAL_WORKING_HOURS_TIMEZONE_VALIDATORS),
|
|
446
488
|
workingDays: new FormControl([0, 1, 2, 3, 4], {
|
|
447
489
|
nonNullable: true,
|
|
448
|
-
validators:
|
|
490
|
+
validators: GLOBAL_WORKING_HOURS_WORKING_DAYS_VALIDATORS,
|
|
449
491
|
}),
|
|
450
|
-
hoursPerDay: new FormControl(8,
|
|
451
|
-
Validators.required,
|
|
452
|
-
Validators.min(0),
|
|
453
|
-
Validators.max(24),
|
|
454
|
-
]),
|
|
492
|
+
hoursPerDay: new FormControl(8, GLOBAL_WORKING_HOURS_HOURS_PER_DAY_VALIDATORS),
|
|
455
493
|
holidaysCsv: new FormControl('', { nonNullable: true }),
|
|
456
494
|
}),
|
|
457
|
-
}, maxDailyHoursGteHoursPerDay);
|
|
495
|
+
}, maxDailyHoursGteHoursPerDay(() => this.hideTimeSheet()));
|
|
458
496
|
settings = signal(null, ...(ngDevMode ? [{ debugName: "settings" }] : /* istanbul ignore next */ []));
|
|
459
497
|
settingsLoading = signal(false, ...(ngDevMode ? [{ debugName: "settingsLoading" }] : /* istanbul ignore next */ []));
|
|
460
498
|
settingsError = signal(null, ...(ngDevMode ? [{ debugName: "settingsError" }] : /* istanbul ignore next */ []));
|
|
@@ -525,9 +563,25 @@ class SystemSettings {
|
|
|
525
563
|
name: this.transloco.translate(`system-settings.${day.translationKey}`),
|
|
526
564
|
}));
|
|
527
565
|
}, ...(ngDevMode ? [{ debugName: "dotnetDayOptions" }] : /* istanbul ignore next */ []));
|
|
566
|
+
/**
|
|
567
|
+
* The Explore-default-view ids actually offered, in the fixed catalog's
|
|
568
|
+
* order: `exploreViewOptions()` when the host sets it, else the legacy
|
|
569
|
+
* pplus5 pair (`DEFAULT_EXPLORE_VIEW_OPTIONS`) - deliberately NOT the full
|
|
570
|
+
* `EXPLORE_DEFAULT_VIEW_OPTIONS` catalog, since that now includes `'table'`
|
|
571
|
+
* and an unconfigured host must keep seeing exactly what it saw before.
|
|
572
|
+
* Filtered against the catalog either way, so an invalid/unknown id in a
|
|
573
|
+
* host's list is silently dropped rather than offered. Always non-empty
|
|
574
|
+
* unless a host passes an empty array.
|
|
575
|
+
*/
|
|
576
|
+
allowedExploreDefaultViews = computed(() => {
|
|
577
|
+
const configured = this.exploreViewOptions() ?? DEFAULT_EXPLORE_VIEW_OPTIONS;
|
|
578
|
+
const configuredSet = new Set(configured);
|
|
579
|
+
return EXPLORE_DEFAULT_VIEW_OPTIONS.map((option) => option.id).filter((id) => configuredSet.has(id));
|
|
580
|
+
}, ...(ngDevMode ? [{ debugName: "allowedExploreDefaultViews" }] : /* istanbul ignore next */ []));
|
|
528
581
|
exploreDefaultViewOptions = computed(() => {
|
|
529
582
|
this.activeLang();
|
|
530
|
-
|
|
583
|
+
const allowed = new Set(this.allowedExploreDefaultViews());
|
|
584
|
+
return EXPLORE_DEFAULT_VIEW_OPTIONS.filter((option) => allowed.has(option.id)).map((option) => ({
|
|
531
585
|
id: option.id,
|
|
532
586
|
name: this.transloco.translate(`system-settings.${option.translationKey}`),
|
|
533
587
|
icon: option.icon,
|
|
@@ -558,7 +612,12 @@ class SystemSettings {
|
|
|
558
612
|
!this.areEqual(initial.timezone, current.timezone) ||
|
|
559
613
|
!this.areEqual(initial.currency, current.currency) ||
|
|
560
614
|
!this.areEqual(initial.weekend, current.weekend) ||
|
|
561
|
-
|
|
615
|
+
// Mirrors onSave()'s exclusion below: while Timesheet Policy is
|
|
616
|
+
// hidden nothing in the UI can change these fields (the card isn't
|
|
617
|
+
// rendered), but keeping this guard symmetric with onSave() means
|
|
618
|
+
// hasChanges() and the actual save payload can never disagree.
|
|
619
|
+
(!this.hideTimeSheet() &&
|
|
620
|
+
!this.areEqual(initial.timeSheetPolicy, current.timeSheetPolicy)) ||
|
|
562
621
|
!this.areEqual(initial.globalWorkingHours, current.globalWorkingHours));
|
|
563
622
|
}, ...(ngDevMode ? [{ debugName: "hasChanges" }] : /* istanbul ignore next */ []));
|
|
564
623
|
canSaveSettings = computed(() => !this.isLoading() &&
|
|
@@ -573,6 +632,30 @@ class SystemSettings {
|
|
|
573
632
|
to: formatDateOnly(holiday.to),
|
|
574
633
|
})), ...(ngDevMode ? [{ debugName: "holidayTableRows" }] : /* istanbul ignore next */ []));
|
|
575
634
|
constructor() {
|
|
635
|
+
// Only Timesheet Policy is conditionally hidden - Global Working Hours
|
|
636
|
+
// keeps its validators unconditionally (set once at construction, see
|
|
637
|
+
// the form below), since that card is always visible.
|
|
638
|
+
effect(() => {
|
|
639
|
+
const hidden = this.hideTimeSheet();
|
|
640
|
+
const timeSheetPolicy = this.form.controls.timeSheetPolicy.controls;
|
|
641
|
+
if (hidden) {
|
|
642
|
+
timeSheetPolicy.frequency.clearValidators();
|
|
643
|
+
timeSheetPolicy.maxDailyHours.clearValidators();
|
|
644
|
+
timeSheetPolicy.submissionDeadlineDays.clearValidators();
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
timeSheetPolicy.frequency.setValidators(TIME_SHEET_FREQUENCY_VALIDATORS);
|
|
648
|
+
timeSheetPolicy.maxDailyHours.setValidators(TIME_SHEET_MAX_DAILY_HOURS_VALIDATORS);
|
|
649
|
+
timeSheetPolicy.submissionDeadlineDays.setValidators(TIME_SHEET_SUBMISSION_DEADLINE_VALIDATORS);
|
|
650
|
+
}
|
|
651
|
+
for (const control of [
|
|
652
|
+
timeSheetPolicy.frequency,
|
|
653
|
+
timeSheetPolicy.maxDailyHours,
|
|
654
|
+
timeSheetPolicy.submissionDeadlineDays,
|
|
655
|
+
]) {
|
|
656
|
+
control.updateValueAndValidity({ emitEvent: false });
|
|
657
|
+
}
|
|
658
|
+
});
|
|
576
659
|
effect(() => {
|
|
577
660
|
const version = [
|
|
578
661
|
this.settingsLastUpdated() ?? 'none',
|
|
@@ -694,7 +777,8 @@ class SystemSettings {
|
|
|
694
777
|
value: nextSnapshot.weekend,
|
|
695
778
|
});
|
|
696
779
|
}
|
|
697
|
-
if (!this.
|
|
780
|
+
if (!this.hideTimeSheet() &&
|
|
781
|
+
!this.areEqual(initialSnapshot.timeSheetPolicy, nextSnapshot.timeSheetPolicy)) {
|
|
698
782
|
values.push({
|
|
699
783
|
key: 'TimeSheetPolicy',
|
|
700
784
|
value: nextSnapshot.timeSheetPolicy,
|
|
@@ -890,7 +974,7 @@ class SystemSettings {
|
|
|
890
974
|
}
|
|
891
975
|
buildGeneralConfigurationValue() {
|
|
892
976
|
return {
|
|
893
|
-
defaultExploreView: normalizeExploreDefaultView(this.form.controls.generalConfiguration.controls.defaultExploreView
|
|
977
|
+
defaultExploreView: this.normalizeExploreDefaultView(this.form.controls.generalConfiguration.controls.defaultExploreView
|
|
894
978
|
.value),
|
|
895
979
|
showDesignSwitcher: this.form.controls.generalConfiguration.controls.showDesignSwitcher
|
|
896
980
|
.value === true,
|
|
@@ -932,7 +1016,7 @@ class SystemSettings {
|
|
|
932
1016
|
return { ...DEFAULT_GENERAL_CONFIGURATION };
|
|
933
1017
|
}
|
|
934
1018
|
return {
|
|
935
|
-
defaultExploreView: normalizeExploreDefaultView(setting.value['defaultExploreView'] ??
|
|
1019
|
+
defaultExploreView: this.normalizeExploreDefaultView(setting.value['defaultExploreView'] ??
|
|
936
1020
|
setting.value['DefaultExploreView']),
|
|
937
1021
|
showDesignSwitcher: normalizeShowDesignSwitcher(setting.value['showDesignSwitcher'] ??
|
|
938
1022
|
setting.value['ShowDesignSwitcher']),
|
|
@@ -1086,10 +1170,24 @@ class SystemSettings {
|
|
|
1086
1170
|
}
|
|
1087
1171
|
normalizeGeneralConfigurationSnapshot(value) {
|
|
1088
1172
|
return {
|
|
1089
|
-
defaultExploreView: normalizeExploreDefaultView(value.defaultExploreView),
|
|
1173
|
+
defaultExploreView: this.normalizeExploreDefaultView(value.defaultExploreView),
|
|
1090
1174
|
showDesignSwitcher: normalizeShowDesignSwitcher(value.showDesignSwitcher),
|
|
1091
1175
|
};
|
|
1092
1176
|
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Resolves a stored/incoming value against the currently allowed Explore
|
|
1179
|
+
* views (`allowedExploreDefaultViews()`), not the full static catalog, so a
|
|
1180
|
+
* host-restricted `exploreViewOptions` is enforced on both hydration and
|
|
1181
|
+
* save. Falls back to the first allowed option - not a hardcoded 'cards' -
|
|
1182
|
+
* since a host may configure a subset that excludes it.
|
|
1183
|
+
*/
|
|
1184
|
+
normalizeExploreDefaultView(value) {
|
|
1185
|
+
const allowed = this.allowedExploreDefaultViews();
|
|
1186
|
+
if (allowed.some((id) => id === value)) {
|
|
1187
|
+
return value;
|
|
1188
|
+
}
|
|
1189
|
+
return allowed[0] ?? DEFAULT_GENERAL_CONFIGURATION.defaultExploreView;
|
|
1190
|
+
}
|
|
1093
1191
|
normalizeTimezoneSnapshot(value) {
|
|
1094
1192
|
const defaults = this.getDefaultTimezoneValue();
|
|
1095
1193
|
return {
|
|
@@ -1337,7 +1435,7 @@ class SystemSettings {
|
|
|
1337
1435
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
1338
1436
|
}
|
|
1339
1437
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1340
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: SystemSettings, isStandalone: true, selector: "mt-system-settings", inputs: { apiBasePath: { classPropertyName: "apiBasePath", publicName: "apiBasePath", isSignal: true, isRequired: false, transformFunction: null }, backRoute: { classPropertyName: "backRoute", publicName: "backRoute", isSignal: true, isRequired: false, transformFunction: null }, formatSchema: { classPropertyName: "formatSchema", publicName: "formatSchema", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <mt-page\r\n [title]=\"t('page-title')\"\r\n [avatarIcon]=\"'general.settings-02'\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-emerald-100)',\r\n '--p-avatar-color': 'var(--p-emerald-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n >\r\n <div class=\"flex flex-col gap-3\">\r\n <div\r\n class=\"flex flex-col gap-4 md:flex-row md:items-end md:justify-between\"\r\n >\r\n <div>\r\n <h3 class=\"text-lg font-bold text-neutral-900\">\r\n {{ t(\"page-title\") }}\r\n </h3>\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n />\r\n </div>\r\n\r\n <mt-button\r\n [label]=\"t('save')\"\r\n icon=\"general.save-01\"\r\n [tooltip]=\"t('save-tooltip')\"\r\n [badge]=\"hasChanges() ? '!' : ''\"\r\n badgeSeverity=\"warn\"\r\n [loading]=\"updating()\"\r\n [disabled]=\"!canSaveSettings()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n\r\n @if (isLoading()) {\r\n <div class=\"space-y-4\">\r\n <p-skeleton width=\"100%\" height=\"12rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"18rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"14rem\" borderRadius=\"0.75rem\" />\r\n </div>\r\n } @else {\r\n <!--\r\n BE/HTTP errors (timezonesError, settingsError, updateError) are already\r\n surfaced as toasts by the HTTP interceptor, so they are intentionally\r\n not rendered as top-of-page banners here to avoid duplicate messaging.\r\n -->\r\n <div class=\"space-y-6\">\r\n <mt-system-settings-general-section\r\n [generalConfigurationForm]=\"form.controls.generalConfiguration\"\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [exploreDefaultViewOptions]=\"exploreDefaultViewOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n />\r\n\r\n <mt-system-settings-timesheet-section\r\n [policyForm]=\"form.controls.timeSheetPolicy\"\r\n [globalWorkingHoursForm]=\"form.controls.globalWorkingHours\"\r\n [dayOptions]=\"dotnetDayOptions()\"\r\n [frequencyOptions]=\"frequencyOptions()\"\r\n [maxDailyBelowDaily]=\"form.errors?.['maxDailyBelowDaily'] ?? false\"\r\n />\r\n\r\n <mt-system-settings-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: SystemSettingsHolidaysSection, selector: "mt-system-settings-holidays-section", inputs: ["holidays"], outputs: ["addHoliday", "editHoliday", "deleteHoliday"] }, { kind: "component", type: SystemSettingsGeneralSection, selector: "mt-system-settings-general-section", inputs: ["generalConfigurationForm", "timezoneForm", "currencyForm", "exploreDefaultViewOptions", "dateFormatOptions", "timeFormatOptions", "currencyOptions", "currencyFormatOptions"] }, { kind: "component", type: SystemSettingsTimeSheetSection, selector: "mt-system-settings-timesheet-section", inputs: ["policyForm", "globalWorkingHoursForm", "dayOptions", "frequencyOptions", "maxDailyBelowDaily"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1438
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: SystemSettings, isStandalone: true, selector: "mt-system-settings", inputs: { apiBasePath: { classPropertyName: "apiBasePath", publicName: "apiBasePath", isSignal: true, isRequired: false, transformFunction: null }, backRoute: { classPropertyName: "backRoute", publicName: "backRoute", isSignal: true, isRequired: false, transformFunction: null }, formatSchema: { classPropertyName: "formatSchema", publicName: "formatSchema", isSignal: true, isRequired: false, transformFunction: null }, homeRoute: { classPropertyName: "homeRoute", publicName: "homeRoute", isSignal: true, isRequired: false, transformFunction: null }, hideTimeSheet: { classPropertyName: "hideTimeSheet", publicName: "hideTimeSheet", isSignal: true, isRequired: false, transformFunction: null }, exploreViewOptions: { classPropertyName: "exploreViewOptions", publicName: "exploreViewOptions", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <mt-page\r\n [title]=\"t('page-title')\"\r\n [avatarIcon]=\"'general.settings-02'\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-emerald-100)',\r\n '--p-avatar-color': 'var(--p-emerald-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n >\r\n <div class=\"flex flex-col gap-3\">\r\n <div\r\n class=\"flex flex-col gap-4 md:flex-row md:items-end md:justify-between\"\r\n >\r\n <div>\r\n <h3 class=\"text-lg font-bold text-neutral-900\">\r\n {{ t(\"page-title\") }}\r\n </h3>\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n />\r\n </div>\r\n\r\n <mt-button\r\n [label]=\"t('save')\"\r\n icon=\"general.save-01\"\r\n [tooltip]=\"t('save-tooltip')\"\r\n [badge]=\"hasChanges() ? '!' : ''\"\r\n badgeSeverity=\"warn\"\r\n [loading]=\"updating()\"\r\n [disabled]=\"!canSaveSettings()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n\r\n @if (isLoading()) {\r\n <div class=\"space-y-4\">\r\n <p-skeleton width=\"100%\" height=\"12rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"18rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"14rem\" borderRadius=\"0.75rem\" />\r\n </div>\r\n } @else {\r\n <!--\r\n BE/HTTP errors (timezonesError, settingsError, updateError) are already\r\n surfaced as toasts by the HTTP interceptor, so they are intentionally\r\n not rendered as top-of-page banners here to avoid duplicate messaging.\r\n -->\r\n <div class=\"space-y-6\">\r\n <mt-system-settings-general-section\r\n [generalConfigurationForm]=\"form.controls.generalConfiguration\"\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [exploreDefaultViewOptions]=\"exploreDefaultViewOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n />\r\n\r\n <mt-system-settings-timesheet-section\r\n [policyForm]=\"form.controls.timeSheetPolicy\"\r\n [globalWorkingHoursForm]=\"form.controls.globalWorkingHours\"\r\n [dayOptions]=\"dotnetDayOptions()\"\r\n [frequencyOptions]=\"frequencyOptions()\"\r\n [maxDailyBelowDaily]=\"form.errors?.['maxDailyBelowDaily'] ?? false\"\r\n [hidePolicy]=\"hideTimeSheet()\"\r\n />\r\n\r\n <mt-system-settings-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: SystemSettingsHolidaysSection, selector: "mt-system-settings-holidays-section", inputs: ["holidays"], outputs: ["addHoliday", "editHoliday", "deleteHoliday"] }, { kind: "component", type: SystemSettingsGeneralSection, selector: "mt-system-settings-general-section", inputs: ["generalConfigurationForm", "timezoneForm", "currencyForm", "exploreDefaultViewOptions", "dateFormatOptions", "timeFormatOptions", "currencyOptions", "currencyFormatOptions"] }, { kind: "component", type: SystemSettingsTimeSheetSection, selector: "mt-system-settings-timesheet-section", inputs: ["policyForm", "globalWorkingHoursForm", "dayOptions", "frequencyOptions", "maxDailyBelowDaily", "hidePolicy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1341
1439
|
}
|
|
1342
1440
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettings, decorators: [{
|
|
1343
1441
|
type: Component,
|
|
@@ -1352,8 +1450,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1352
1450
|
SystemSettingsHolidaysSection,
|
|
1353
1451
|
SystemSettingsGeneralSection,
|
|
1354
1452
|
SystemSettingsTimeSheetSection,
|
|
1355
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <mt-page\r\n [title]=\"t('page-title')\"\r\n [avatarIcon]=\"'general.settings-02'\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-emerald-100)',\r\n '--p-avatar-color': 'var(--p-emerald-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n >\r\n <div class=\"flex flex-col gap-3\">\r\n <div\r\n class=\"flex flex-col gap-4 md:flex-row md:items-end md:justify-between\"\r\n >\r\n <div>\r\n <h3 class=\"text-lg font-bold text-neutral-900\">\r\n {{ t(\"page-title\") }}\r\n </h3>\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n />\r\n </div>\r\n\r\n <mt-button\r\n [label]=\"t('save')\"\r\n icon=\"general.save-01\"\r\n [tooltip]=\"t('save-tooltip')\"\r\n [badge]=\"hasChanges() ? '!' : ''\"\r\n badgeSeverity=\"warn\"\r\n [loading]=\"updating()\"\r\n [disabled]=\"!canSaveSettings()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n\r\n @if (isLoading()) {\r\n <div class=\"space-y-4\">\r\n <p-skeleton width=\"100%\" height=\"12rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"18rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"14rem\" borderRadius=\"0.75rem\" />\r\n </div>\r\n } @else {\r\n <!--\r\n BE/HTTP errors (timezonesError, settingsError, updateError) are already\r\n surfaced as toasts by the HTTP interceptor, so they are intentionally\r\n not rendered as top-of-page banners here to avoid duplicate messaging.\r\n -->\r\n <div class=\"space-y-6\">\r\n <mt-system-settings-general-section\r\n [generalConfigurationForm]=\"form.controls.generalConfiguration\"\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [exploreDefaultViewOptions]=\"exploreDefaultViewOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n />\r\n\r\n <mt-system-settings-timesheet-section\r\n [policyForm]=\"form.controls.timeSheetPolicy\"\r\n [globalWorkingHoursForm]=\"form.controls.globalWorkingHours\"\r\n [dayOptions]=\"dotnetDayOptions()\"\r\n [frequencyOptions]=\"frequencyOptions()\"\r\n [maxDailyBelowDaily]=\"form.errors?.['maxDailyBelowDaily'] ?? false\"\r\n />\r\n\r\n <mt-system-settings-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block}\n"] }]
|
|
1356
|
-
}], ctorParameters: () => [], propDecorators: { apiBasePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "apiBasePath", required: false }] }], backRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "backRoute", required: false }] }], formatSchema: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatSchema", required: false }] }] } });
|
|
1453
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <mt-page\r\n [title]=\"t('page-title')\"\r\n [avatarIcon]=\"'general.settings-02'\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-emerald-100)',\r\n '--p-avatar-color': 'var(--p-emerald-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n >\r\n <div class=\"flex flex-col gap-3\">\r\n <div\r\n class=\"flex flex-col gap-4 md:flex-row md:items-end md:justify-between\"\r\n >\r\n <div>\r\n <h3 class=\"text-lg font-bold text-neutral-900\">\r\n {{ t(\"page-title\") }}\r\n </h3>\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n />\r\n </div>\r\n\r\n <mt-button\r\n [label]=\"t('save')\"\r\n icon=\"general.save-01\"\r\n [tooltip]=\"t('save-tooltip')\"\r\n [badge]=\"hasChanges() ? '!' : ''\"\r\n badgeSeverity=\"warn\"\r\n [loading]=\"updating()\"\r\n [disabled]=\"!canSaveSettings()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n\r\n @if (isLoading()) {\r\n <div class=\"space-y-4\">\r\n <p-skeleton width=\"100%\" height=\"12rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"18rem\" borderRadius=\"0.75rem\" />\r\n <p-skeleton width=\"100%\" height=\"14rem\" borderRadius=\"0.75rem\" />\r\n </div>\r\n } @else {\r\n <!--\r\n BE/HTTP errors (timezonesError, settingsError, updateError) are already\r\n surfaced as toasts by the HTTP interceptor, so they are intentionally\r\n not rendered as top-of-page banners here to avoid duplicate messaging.\r\n -->\r\n <div class=\"space-y-6\">\r\n <mt-system-settings-general-section\r\n [generalConfigurationForm]=\"form.controls.generalConfiguration\"\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [exploreDefaultViewOptions]=\"exploreDefaultViewOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n />\r\n\r\n <mt-system-settings-timesheet-section\r\n [policyForm]=\"form.controls.timeSheetPolicy\"\r\n [globalWorkingHoursForm]=\"form.controls.globalWorkingHours\"\r\n [dayOptions]=\"dotnetDayOptions()\"\r\n [frequencyOptions]=\"frequencyOptions()\"\r\n [maxDailyBelowDaily]=\"form.errors?.['maxDailyBelowDaily'] ?? false\"\r\n [hidePolicy]=\"hideTimeSheet()\"\r\n />\r\n\r\n <mt-system-settings-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block}\n"] }]
|
|
1454
|
+
}], ctorParameters: () => [], propDecorators: { apiBasePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "apiBasePath", required: false }] }], backRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "backRoute", required: false }] }], formatSchema: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatSchema", required: false }] }], homeRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "homeRoute", required: false }] }], hideTimeSheet: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideTimeSheet", required: false }] }], exploreViewOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "exploreViewOptions", required: false }] }] } });
|
|
1357
1455
|
function isRecord(value) {
|
|
1358
1456
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1359
1457
|
}
|
|
@@ -1421,11 +1519,6 @@ function getNumberArray(value) {
|
|
|
1421
1519
|
function normalizeShowDesignSwitcher(value) {
|
|
1422
1520
|
return value === true || value === 'true' || value === 'True';
|
|
1423
1521
|
}
|
|
1424
|
-
function normalizeExploreDefaultView(value) {
|
|
1425
|
-
return EXPLORE_DEFAULT_VIEW_OPTIONS.some((option) => option.id === value)
|
|
1426
|
-
? value
|
|
1427
|
-
: DEFAULT_GENERAL_CONFIGURATION.defaultExploreView;
|
|
1428
|
-
}
|
|
1429
1522
|
function normalizeWeekendDays(value) {
|
|
1430
1523
|
if (!Array.isArray(value)) {
|
|
1431
1524
|
return [];
|
|
@@ -1461,15 +1554,80 @@ function normalizeFrequency(value) {
|
|
|
1461
1554
|
? value
|
|
1462
1555
|
: 'Weekly';
|
|
1463
1556
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1557
|
+
/**
|
|
1558
|
+
* Restores an input's own default when the router's `withComponentInputBinding()`
|
|
1559
|
+
* wipes it. That feature calls `setInput(name, data[name])` for every
|
|
1560
|
+
* declared input on every route activation - including inputs a route's
|
|
1561
|
+
* `data` doesn't mention at all, which writes `undefined` and permanently
|
|
1562
|
+
* overwrites the `input()`'s own default the moment the route activates
|
|
1563
|
+
* (see `RoutedComponentInputBinder` in `@angular/router`). This substitutes
|
|
1564
|
+
* the fallback only for that specific "the router didn't supply it" case.
|
|
1565
|
+
*
|
|
1566
|
+
* Deliberately narrower than `delegations-list.ts`'s `defaultRouteInput`,
|
|
1567
|
+
* which also collapses an explicit `null` into the fallback: `homeRoute`'s
|
|
1568
|
+
* `null` is a meaningful, tested value ("non-navigable crumb"), not merely
|
|
1569
|
+
* "unset", so this only special-cases `undefined` and passes every other
|
|
1570
|
+
* value - including `null` - through untouched.
|
|
1571
|
+
*/
|
|
1572
|
+
function defaultWhenUndefined(fallback) {
|
|
1573
|
+
return (value) => (value === undefined ? fallback : value);
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* pplus5's original Explore-default-view offering, before `'table'` was
|
|
1577
|
+
* added to the catalog for splus4. This - not the full catalog - is what an
|
|
1578
|
+
* unset `exploreViewOptions` input resolves to, so existing hosts keep
|
|
1579
|
+
* seeing exactly the two options they always have.
|
|
1580
|
+
*/
|
|
1581
|
+
const DEFAULT_EXPLORE_VIEW_OPTIONS = [
|
|
1582
|
+
'cards',
|
|
1583
|
+
'timeline',
|
|
1584
|
+
];
|
|
1585
|
+
/**
|
|
1586
|
+
* Named validator sets for the six Timesheet/GlobalWorkingHours fields, kept
|
|
1587
|
+
* as constants so `hideTimeSheet` can restore the exact original validators
|
|
1588
|
+
* on toggle-back rather than re-declaring them inline in two places.
|
|
1589
|
+
*/
|
|
1590
|
+
const TIME_SHEET_FREQUENCY_VALIDATORS = [Validators.required];
|
|
1591
|
+
const TIME_SHEET_MAX_DAILY_HOURS_VALIDATORS = [
|
|
1592
|
+
Validators.required,
|
|
1593
|
+
Validators.min(1),
|
|
1594
|
+
Validators.max(24),
|
|
1595
|
+
];
|
|
1596
|
+
const TIME_SHEET_SUBMISSION_DEADLINE_VALIDATORS = [
|
|
1597
|
+
Validators.required,
|
|
1598
|
+
Validators.min(0),
|
|
1599
|
+
Validators.max(90),
|
|
1600
|
+
];
|
|
1601
|
+
const GLOBAL_WORKING_HOURS_TIMEZONE_VALIDATORS = [Validators.required];
|
|
1602
|
+
const GLOBAL_WORKING_HOURS_WORKING_DAYS_VALIDATORS = [Validators.required];
|
|
1603
|
+
const GLOBAL_WORKING_HOURS_HOURS_PER_DAY_VALIDATORS = [
|
|
1604
|
+
Validators.required,
|
|
1605
|
+
Validators.min(0),
|
|
1606
|
+
Validators.max(24),
|
|
1607
|
+
];
|
|
1608
|
+
/**
|
|
1609
|
+
* Cross-field validator comparing `timeSheetPolicy.maxDailyHours` against
|
|
1610
|
+
* `globalWorkingHours.hoursPerDay`. Takes the `hideTimeSheet` input as a
|
|
1611
|
+
* thunk (rather than reading it directly) so it stays a plain module-level
|
|
1612
|
+
* function while still skipping the comparison when Timesheet Policy is
|
|
1613
|
+
* hidden - Global Working Hours stays visible/editable on its own even
|
|
1614
|
+
* then, and a visible `hoursPerDay` must never be blocked by a comparison
|
|
1615
|
+
* against a `maxDailyHours` the user can no longer see or change.
|
|
1616
|
+
*/
|
|
1617
|
+
function maxDailyHoursGteHoursPerDay(isTimeSheetHidden) {
|
|
1618
|
+
return (group) => {
|
|
1619
|
+
if (isTimeSheetHidden()) {
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
const maxDailyHours = group.get('timeSheetPolicy.maxDailyHours')?.value;
|
|
1623
|
+
const hoursPerDay = group.get('globalWorkingHours.hoursPerDay')?.value;
|
|
1624
|
+
if (typeof maxDailyHours === 'number' &&
|
|
1625
|
+
typeof hoursPerDay === 'number' &&
|
|
1626
|
+
maxDailyHours < hoursPerDay) {
|
|
1627
|
+
return { maxDailyBelowDaily: true };
|
|
1628
|
+
}
|
|
1629
|
+
return null;
|
|
1630
|
+
};
|
|
1473
1631
|
}
|
|
1474
1632
|
function parseCsv(value) {
|
|
1475
1633
|
return value
|
|
@@ -2677,7 +2835,7 @@ function isTestRequestConflict(error) {
|
|
|
2677
2835
|
const EMAIL_DELIVERY_ROUTES = [
|
|
2678
2836
|
{
|
|
2679
2837
|
path: '',
|
|
2680
|
-
loadComponent: () => import('./masterteam-system-settings-email-delivery-shell-
|
|
2838
|
+
loadComponent: () => import('./masterteam-system-settings-email-delivery-shell-CBPKuYXr.mjs').then((module) => module.EmailDeliveryShell),
|
|
2681
2839
|
},
|
|
2682
2840
|
{ path: '**', redirectTo: '' },
|
|
2683
2841
|
];
|
|
@@ -2687,4 +2845,4 @@ const EMAIL_DELIVERY_ROUTES = [
|
|
|
2687
2845
|
*/
|
|
2688
2846
|
|
|
2689
2847
|
export { DEFAULT_GENERAL_CONFIGURATION as D, EmailDeliveryFacade as E, FIXED_CURRENCIES as F, GENERAL_CONFIGURATION_SETTING_KEY as G, RATE_SOURCE_OPTIONS as R, SystemSettings as S, TIMESHEET_FREQUENCY_OPTIONS as T, WEEKEND_DAY_DEFINITIONS as W, EmailDeliveryStatus as a, EMAIL_DELIVERY_ATTEMPT_STATUSES as b, createEmailTestRequestId as c, areEmailTestRecipientsValid as d, isEmailTestRequestPending as e, EMAIL_DELIVERY_PERMISSIONS as f, getEmailDeliveryStatusMap as g, EXPLORE_DEFAULT_VIEW_OPTIONS as h, isConnectionTestAttempt as i, DEFAULT_DATE_FORMAT_OPTIONS as j, DEFAULT_TIME_FORMAT_OPTIONS as k, DOTNET_DAY_DEFINITIONS as l, SCHEDULE_ITEM_TYPE_OPTIONS as m, SystemSettingsHolidayEditor as n, DEFAULT_EMAIL_DELIVERY_OPTIONS as o, parseEmailTestRecipients as p, EMAIL_DELIVERY_OPTIONS as q, provideEmailDelivery as r, EmailDeliveryAccessService as s, collectEmailTemplatePlaceholders as t, buildEmailTemplatePlaceholderFields as u, EmailUserTestDialog as v, EMAIL_USER_INITIATED_TEST_OPERATION_KIND as w, EMAIL_DELIVERY_ROUTES as x };
|
|
2690
|
-
//# sourceMappingURL=masterteam-system-settings-masterteam-system-settings-
|
|
2848
|
+
//# sourceMappingURL=masterteam-system-settings-masterteam-system-settings-DbsjQ7c7.mjs.map
|