@masterteam/system-settings 0.0.18 → 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 +3 -2
- package/assets/i18n/en.json +3 -2
- package/assets/system-settings.css +1 -1
- package/fesm2022/{masterteam-system-settings-email-delivery-shell-Bjwtj5Ic.mjs → masterteam-system-settings-email-delivery-shell-CBPKuYXr.mjs} +2 -2
- package/fesm2022/{masterteam-system-settings-email-delivery-shell-Bjwtj5Ic.mjs.map → masterteam-system-settings-email-delivery-shell-CBPKuYXr.mjs.map} +1 -1
- package/fesm2022/{masterteam-system-settings-masterteam-system-settings-CO-XTJOi.mjs → masterteam-system-settings-masterteam-system-settings-DbsjQ7c7.mjs} +226 -85
- package/fesm2022/masterteam-system-settings-masterteam-system-settings-DbsjQ7c7.mjs.map +1 -0
- package/fesm2022/masterteam-system-settings.mjs +1 -1
- package/package.json +4 -4
- package/types/masterteam-system-settings.d.ts +64 -15
- package/fesm2022/masterteam-system-settings-masterteam-system-settings-CO-XTJOi.mjs.map +0 -1
|
@@ -15,7 +15,6 @@ import { ToastService } from '@masterteam/components/toast';
|
|
|
15
15
|
import * as i1$1 from 'primeng/skeleton';
|
|
16
16
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
17
17
|
import { Card } from '@masterteam/components/card';
|
|
18
|
-
import { MultiSelectField } from '@masterteam/components/multi-select-field';
|
|
19
18
|
import { SelectField } from '@masterteam/components/select-field';
|
|
20
19
|
import { RadioCardsField } from '@masterteam/components/radio-cards-field';
|
|
21
20
|
import { ToggleField } from '@masterteam/components/toggle-field';
|
|
@@ -23,9 +22,10 @@ import { ModalRef } from '@masterteam/components/dialog';
|
|
|
23
22
|
import { DateField } from '@masterteam/components/date-field';
|
|
24
23
|
import { TextField } from '@masterteam/components/text-field';
|
|
25
24
|
import { Table } from '@masterteam/components/table';
|
|
25
|
+
import { MultiSelectField } from '@masterteam/components/multi-select-field';
|
|
26
26
|
import { NumberField } from '@masterteam/components/number-field';
|
|
27
|
-
import { TextareaField } from '@masterteam/components/textarea-field';
|
|
28
27
|
import { of, map, tap, catchError, throwError, finalize, shareReplay, mergeMap, switchMap, forkJoin, timer } from 'rxjs';
|
|
28
|
+
import { TextareaField } from '@masterteam/components/textarea-field';
|
|
29
29
|
import { EntityStatus } from '@masterteam/components/entities';
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -54,15 +54,13 @@ class SystemSettingsGeneralSection {
|
|
|
54
54
|
generalConfigurationForm = input.required(...(ngDevMode ? [{ debugName: "generalConfigurationForm" }] : /* istanbul ignore next */ []));
|
|
55
55
|
timezoneForm = input.required(...(ngDevMode ? [{ debugName: "timezoneForm" }] : /* istanbul ignore next */ []));
|
|
56
56
|
currencyForm = input.required(...(ngDevMode ? [{ debugName: "currencyForm" }] : /* istanbul ignore next */ []));
|
|
57
|
-
weekendForm = input.required(...(ngDevMode ? [{ debugName: "weekendForm" }] : /* istanbul ignore next */ []));
|
|
58
57
|
exploreDefaultViewOptions = input([], ...(ngDevMode ? [{ debugName: "exploreDefaultViewOptions" }] : /* istanbul ignore next */ []));
|
|
59
58
|
dateFormatOptions = input([], ...(ngDevMode ? [{ debugName: "dateFormatOptions" }] : /* istanbul ignore next */ []));
|
|
60
59
|
timeFormatOptions = input([], ...(ngDevMode ? [{ debugName: "timeFormatOptions" }] : /* istanbul ignore next */ []));
|
|
61
60
|
currencyOptions = input([], ...(ngDevMode ? [{ debugName: "currencyOptions" }] : /* istanbul ignore next */ []));
|
|
62
61
|
currencyFormatOptions = input([], ...(ngDevMode ? [{ debugName: "currencyFormatOptions" }] : /* istanbul ignore next */ []));
|
|
63
|
-
weekendOptions = input([], ...(ngDevMode ? [{ debugName: "weekendOptions" }] : /* istanbul ignore next */ []));
|
|
64
62
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsGeneralSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
65
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: SystemSettingsGeneralSection, isStandalone: true, selector: "mt-system-settings-general-section", inputs: { generalConfigurationForm: { classPropertyName: "generalConfigurationForm", publicName: "generalConfigurationForm", isSignal: true, isRequired: true, transformFunction: null }, timezoneForm: { classPropertyName: "timezoneForm", publicName: "timezoneForm", isSignal: true, isRequired: true, transformFunction: null }, currencyForm: { classPropertyName: "currencyForm", publicName: "currencyForm", isSignal: true, isRequired: true, transformFunction: null },
|
|
63
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: SystemSettingsGeneralSection, isStandalone: true, selector: "mt-system-settings-general-section", inputs: { generalConfigurationForm: { classPropertyName: "generalConfigurationForm", publicName: "generalConfigurationForm", isSignal: true, isRequired: true, transformFunction: null }, timezoneForm: { classPropertyName: "timezoneForm", publicName: "timezoneForm", isSignal: true, isRequired: true, transformFunction: null }, currencyForm: { classPropertyName: "currencyForm", publicName: "currencyForm", isSignal: true, isRequired: true, transformFunction: null }, exploreDefaultViewOptions: { classPropertyName: "exploreDefaultViewOptions", publicName: "exploreDefaultViewOptions", isSignal: true, isRequired: false, transformFunction: null }, dateFormatOptions: { classPropertyName: "dateFormatOptions", publicName: "dateFormatOptions", isSignal: true, isRequired: false, transformFunction: null }, timeFormatOptions: { classPropertyName: "timeFormatOptions", publicName: "timeFormatOptions", isSignal: true, isRequired: false, transformFunction: null }, currencyOptions: { classPropertyName: "currencyOptions", publicName: "currencyOptions", isSignal: true, isRequired: false, transformFunction: null }, currencyFormatOptions: { classPropertyName: "currencyFormatOptions", publicName: "currencyFormatOptions", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <div class=\"grid gap-6 xl:grid-cols-3\">\r\n <mt-card class=\"block\">\r\n <div class=\"space-y-4\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"date-time-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"date-time-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div [formGroup]=\"timezoneForm()\" class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-select-field\r\n formControlName=\"dateFormat\"\r\n [options]=\"dateFormatOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('date-format')\"\r\n [placeholder]=\"t('date-format-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-select-field\r\n formControlName=\"timeFormat\"\r\n [options]=\"timeFormatOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('time-format')\"\r\n [placeholder]=\"t('time-format-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card class=\"block\">\r\n <div [formGroup]=\"currencyForm()\" class=\"space-y-4\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"currency-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"currency-description\") }}\r\n </p>\r\n </div>\r\n\r\n <mt-select-field\r\n formControlName=\"code\"\r\n [options]=\"currencyOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('currency')\"\r\n [placeholder]=\"t('currency-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-select-field\r\n formControlName=\"format\"\r\n [options]=\"currencyFormatOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('currency-format')\"\r\n [placeholder]=\"t('currency-format-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n </div>\r\n </mt-card>\r\n\r\n <mt-system-settings-general-config-section\r\n class=\"block\"\r\n [generalConfigurationForm]=\"generalConfigurationForm()\"\r\n [exploreDefaultViewOptions]=\"exploreDefaultViewOptions()\"\r\n />\r\n </div>\r\n</ng-container>\r\n", styles: [":host{display:block}\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: 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: SystemSettingsGeneralConfigSection, selector: "mt-system-settings-general-config-section", inputs: ["generalConfigurationForm", "exploreDefaultViewOptions"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
66
64
|
}
|
|
67
65
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsGeneralSection, decorators: [{
|
|
68
66
|
type: Component,
|
|
@@ -71,10 +69,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
71
69
|
TranslocoDirective,
|
|
72
70
|
Card,
|
|
73
71
|
SelectField,
|
|
74
|
-
MultiSelectField,
|
|
75
72
|
SystemSettingsGeneralConfigSection,
|
|
76
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\n <div class=\"grid gap-6 xl:grid-cols-3\">\n <mt-card class=\"block\">\n <div class=\"space-y-4\">\n <div class=\"space-y-1\">\n <h4 class=\"text-base font-semibold text-neutral-900\">\n {{ t(\"date-time-
|
|
77
|
-
}], propDecorators: { generalConfigurationForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "generalConfigurationForm", required: true }] }], timezoneForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "timezoneForm", required: true }] }], currencyForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencyForm", required: true }] }],
|
|
73
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <div class=\"grid gap-6 xl:grid-cols-3\">\r\n <mt-card class=\"block\">\r\n <div class=\"space-y-4\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"date-time-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"date-time-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div [formGroup]=\"timezoneForm()\" class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-select-field\r\n formControlName=\"dateFormat\"\r\n [options]=\"dateFormatOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('date-format')\"\r\n [placeholder]=\"t('date-format-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-select-field\r\n formControlName=\"timeFormat\"\r\n [options]=\"timeFormatOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('time-format')\"\r\n [placeholder]=\"t('time-format-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card class=\"block\">\r\n <div [formGroup]=\"currencyForm()\" class=\"space-y-4\">\r\n <div class=\"space-y-1\">\r\n <h4 class=\"text-base font-semibold text-neutral-900\">\r\n {{ t(\"currency-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"currency-description\") }}\r\n </p>\r\n </div>\r\n\r\n <mt-select-field\r\n formControlName=\"code\"\r\n [options]=\"currencyOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('currency')\"\r\n [placeholder]=\"t('currency-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-select-field\r\n formControlName=\"format\"\r\n [options]=\"currencyFormatOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('currency-format')\"\r\n [placeholder]=\"t('currency-format-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n </div>\r\n </mt-card>\r\n\r\n <mt-system-settings-general-config-section\r\n class=\"block\"\r\n [generalConfigurationForm]=\"generalConfigurationForm()\"\r\n [exploreDefaultViewOptions]=\"exploreDefaultViewOptions()\"\r\n />\r\n </div>\r\n</ng-container>\r\n", styles: [":host{display:block}\n"] }]
|
|
74
|
+
}], propDecorators: { generalConfigurationForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "generalConfigurationForm", required: true }] }], timezoneForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "timezoneForm", required: true }] }], currencyForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencyForm", required: true }] }], exploreDefaultViewOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "exploreDefaultViewOptions", required: false }] }], dateFormatOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormatOptions", required: false }] }], timeFormatOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormatOptions", required: false }] }], currencyOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencyOptions", required: false }] }], currencyFormatOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencyFormatOptions", required: false }] }] } });
|
|
78
75
|
|
|
79
76
|
class SystemSettingsHolidayEditor {
|
|
80
77
|
ref = inject(ModalRef);
|
|
@@ -207,10 +204,16 @@ class SystemSettingsTimeSheetSection {
|
|
|
207
204
|
globalWorkingHoursForm = input.required(...(ngDevMode ? [{ debugName: "globalWorkingHoursForm" }] : /* istanbul ignore next */ []));
|
|
208
205
|
dayOptions = input([], ...(ngDevMode ? [{ debugName: "dayOptions" }] : /* istanbul ignore next */ []));
|
|
209
206
|
frequencyOptions = input([], ...(ngDevMode ? [{ debugName: "frequencyOptions" }] : /* istanbul ignore next */ []));
|
|
210
|
-
timezoneOptions = input([], ...(ngDevMode ? [{ debugName: "timezoneOptions" }] : /* istanbul ignore next */ []));
|
|
211
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 */ []));
|
|
212
215
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsTimeSheetSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
213
|
-
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 });
|
|
214
217
|
}
|
|
215
218
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsTimeSheetSection, decorators: [{
|
|
216
219
|
type: Component,
|
|
@@ -221,10 +224,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
221
224
|
MultiSelectField,
|
|
222
225
|
NumberField,
|
|
223
226
|
SelectField,
|
|
224
|
-
TextareaField,
|
|
225
227
|
ToggleField,
|
|
226
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\n <div
|
|
227
|
-
}], 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 }] }],
|
|
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 }] }] } });
|
|
228
230
|
|
|
229
231
|
/** The settings record General Configuration fields live in. */
|
|
230
232
|
const GENERAL_CONFIGURATION_SETTING_KEY = 'Identity';
|
|
@@ -272,6 +274,11 @@ const EXPLORE_DEFAULT_VIEW_OPTIONS = [
|
|
|
272
274
|
translationKey: 'explore-default-view-timeline',
|
|
273
275
|
icon: 'time.clock',
|
|
274
276
|
},
|
|
277
|
+
{
|
|
278
|
+
id: 'table',
|
|
279
|
+
translationKey: 'explore-default-view-table',
|
|
280
|
+
icon: 'layout.table',
|
|
281
|
+
},
|
|
275
282
|
];
|
|
276
283
|
const DEFAULT_GENERAL_CONFIGURATION = {
|
|
277
284
|
defaultExploreView: 'cards',
|
|
@@ -390,23 +397,61 @@ class SystemSettings {
|
|
|
390
397
|
transloco = inject(TranslocoService);
|
|
391
398
|
modal = inject(ModalService);
|
|
392
399
|
toast = inject(ToastService);
|
|
393
|
-
|
|
394
|
-
|
|
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') });
|
|
395
413
|
formatSchema = input(null, ...(ngDevMode ? [{ debugName: "formatSchema" }] : /* istanbul ignore next */ []));
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
];
|
|
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 */ []));
|
|
410
455
|
form = new FormGroup({
|
|
411
456
|
generalConfiguration: new FormGroup({
|
|
412
457
|
defaultExploreView: new FormControl('cards', {
|
|
@@ -430,36 +475,24 @@ class SystemSettings {
|
|
|
430
475
|
days: new FormControl([], { nonNullable: true }),
|
|
431
476
|
}),
|
|
432
477
|
timeSheetPolicy: new FormGroup({
|
|
433
|
-
frequency: new FormControl('Weekly',
|
|
434
|
-
maxDailyHours: new FormControl(24,
|
|
435
|
-
Validators.required,
|
|
436
|
-
Validators.min(1),
|
|
437
|
-
Validators.max(24),
|
|
438
|
-
]),
|
|
478
|
+
frequency: new FormControl('Weekly', TIME_SHEET_FREQUENCY_VALIDATORS),
|
|
479
|
+
maxDailyHours: new FormControl(24, TIME_SHEET_MAX_DAILY_HOURS_VALIDATORS),
|
|
439
480
|
requireDescription: new FormControl(false, { nonNullable: true }),
|
|
440
481
|
allowFutureEntry: new FormControl(false, { nonNullable: true }),
|
|
441
482
|
allowBackdatedEntry: new FormControl(true, { nonNullable: true }),
|
|
442
483
|
autoLockOnApprove: new FormControl(true, { nonNullable: true }),
|
|
443
|
-
submissionDeadlineDays: new FormControl(3,
|
|
444
|
-
Validators.required,
|
|
445
|
-
Validators.min(0),
|
|
446
|
-
Validators.max(90),
|
|
447
|
-
]),
|
|
484
|
+
submissionDeadlineDays: new FormControl(3, TIME_SHEET_SUBMISSION_DEADLINE_VALIDATORS),
|
|
448
485
|
}),
|
|
449
486
|
globalWorkingHours: new FormGroup({
|
|
450
|
-
timeZoneId: new FormControl(null,
|
|
487
|
+
timeZoneId: new FormControl(null, GLOBAL_WORKING_HOURS_TIMEZONE_VALIDATORS),
|
|
451
488
|
workingDays: new FormControl([0, 1, 2, 3, 4], {
|
|
452
489
|
nonNullable: true,
|
|
453
|
-
validators:
|
|
490
|
+
validators: GLOBAL_WORKING_HOURS_WORKING_DAYS_VALIDATORS,
|
|
454
491
|
}),
|
|
455
|
-
hoursPerDay: new FormControl(8,
|
|
456
|
-
Validators.required,
|
|
457
|
-
Validators.min(0),
|
|
458
|
-
Validators.max(24),
|
|
459
|
-
]),
|
|
492
|
+
hoursPerDay: new FormControl(8, GLOBAL_WORKING_HOURS_HOURS_PER_DAY_VALIDATORS),
|
|
460
493
|
holidaysCsv: new FormControl('', { nonNullable: true }),
|
|
461
494
|
}),
|
|
462
|
-
}, maxDailyHoursGteHoursPerDay);
|
|
495
|
+
}, maxDailyHoursGteHoursPerDay(() => this.hideTimeSheet()));
|
|
463
496
|
settings = signal(null, ...(ngDevMode ? [{ debugName: "settings" }] : /* istanbul ignore next */ []));
|
|
464
497
|
settingsLoading = signal(false, ...(ngDevMode ? [{ debugName: "settingsLoading" }] : /* istanbul ignore next */ []));
|
|
465
498
|
settingsError = signal(null, ...(ngDevMode ? [{ debugName: "settingsError" }] : /* istanbul ignore next */ []));
|
|
@@ -509,10 +542,6 @@ class SystemSettings {
|
|
|
509
542
|
timeFormats: schema?.timeFormats ?? DEFAULT_TIME_FORMAT_OPTIONS,
|
|
510
543
|
};
|
|
511
544
|
}, ...(ngDevMode ? [{ debugName: "resolvedFormatSchema" }] : /* istanbul ignore next */ []));
|
|
512
|
-
timezoneOptions = computed(() => this.timezones().map((timezone) => ({
|
|
513
|
-
...timezone,
|
|
514
|
-
name: timezone.name ?? timezone.displayName,
|
|
515
|
-
})), ...(ngDevMode ? [{ debugName: "timezoneOptions" }] : /* istanbul ignore next */ []));
|
|
516
545
|
dateFormatOptions = computed(() => this.mapFormatOptions(this.resolvedFormatSchema().dateFormats, this.selectedDateFormat()), ...(ngDevMode ? [{ debugName: "dateFormatOptions" }] : /* istanbul ignore next */ []));
|
|
517
546
|
timeFormatOptions = computed(() => this.mapFormatOptions(this.resolvedFormatSchema().timeFormats, this.selectedTimeFormat()), ...(ngDevMode ? [{ debugName: "timeFormatOptions" }] : /* istanbul ignore next */ []));
|
|
518
547
|
currencyOptions = computed(() => {
|
|
@@ -526,14 +555,6 @@ class SystemSettings {
|
|
|
526
555
|
const currency = FIXED_CURRENCIES.find((item) => item.code === this.selectedCurrencyCode());
|
|
527
556
|
return this.buildCurrencyFormatOptions(currency);
|
|
528
557
|
}, ...(ngDevMode ? [{ debugName: "currencyFormatOptions" }] : /* istanbul ignore next */ []));
|
|
529
|
-
weekendOptions = computed(() => {
|
|
530
|
-
this.activeLang();
|
|
531
|
-
return WEEKEND_DAY_DEFINITIONS.map((day) => ({
|
|
532
|
-
id: `${day.dayNumber}`,
|
|
533
|
-
dayNumber: day.dayNumber,
|
|
534
|
-
name: this.transloco.translate(`system-settings.${day.translationKey}`),
|
|
535
|
-
}));
|
|
536
|
-
}, ...(ngDevMode ? [{ debugName: "weekendOptions" }] : /* istanbul ignore next */ []));
|
|
537
558
|
dotnetDayOptions = computed(() => {
|
|
538
559
|
this.activeLang();
|
|
539
560
|
return DOTNET_DAY_DEFINITIONS.map((day) => ({
|
|
@@ -542,9 +563,25 @@ class SystemSettings {
|
|
|
542
563
|
name: this.transloco.translate(`system-settings.${day.translationKey}`),
|
|
543
564
|
}));
|
|
544
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 */ []));
|
|
545
581
|
exploreDefaultViewOptions = computed(() => {
|
|
546
582
|
this.activeLang();
|
|
547
|
-
|
|
583
|
+
const allowed = new Set(this.allowedExploreDefaultViews());
|
|
584
|
+
return EXPLORE_DEFAULT_VIEW_OPTIONS.filter((option) => allowed.has(option.id)).map((option) => ({
|
|
548
585
|
id: option.id,
|
|
549
586
|
name: this.transloco.translate(`system-settings.${option.translationKey}`),
|
|
550
587
|
icon: option.icon,
|
|
@@ -575,7 +612,12 @@ class SystemSettings {
|
|
|
575
612
|
!this.areEqual(initial.timezone, current.timezone) ||
|
|
576
613
|
!this.areEqual(initial.currency, current.currency) ||
|
|
577
614
|
!this.areEqual(initial.weekend, current.weekend) ||
|
|
578
|
-
|
|
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)) ||
|
|
579
621
|
!this.areEqual(initial.globalWorkingHours, current.globalWorkingHours));
|
|
580
622
|
}, ...(ngDevMode ? [{ debugName: "hasChanges" }] : /* istanbul ignore next */ []));
|
|
581
623
|
canSaveSettings = computed(() => !this.isLoading() &&
|
|
@@ -590,6 +632,30 @@ class SystemSettings {
|
|
|
590
632
|
to: formatDateOnly(holiday.to),
|
|
591
633
|
})), ...(ngDevMode ? [{ debugName: "holidayTableRows" }] : /* istanbul ignore next */ []));
|
|
592
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
|
+
});
|
|
593
659
|
effect(() => {
|
|
594
660
|
const version = [
|
|
595
661
|
this.settingsLastUpdated() ?? 'none',
|
|
@@ -711,7 +777,8 @@ class SystemSettings {
|
|
|
711
777
|
value: nextSnapshot.weekend,
|
|
712
778
|
});
|
|
713
779
|
}
|
|
714
|
-
if (!this.
|
|
780
|
+
if (!this.hideTimeSheet() &&
|
|
781
|
+
!this.areEqual(initialSnapshot.timeSheetPolicy, nextSnapshot.timeSheetPolicy)) {
|
|
715
782
|
values.push({
|
|
716
783
|
key: 'TimeSheetPolicy',
|
|
717
784
|
value: nextSnapshot.timeSheetPolicy,
|
|
@@ -907,7 +974,7 @@ class SystemSettings {
|
|
|
907
974
|
}
|
|
908
975
|
buildGeneralConfigurationValue() {
|
|
909
976
|
return {
|
|
910
|
-
defaultExploreView: normalizeExploreDefaultView(this.form.controls.generalConfiguration.controls.defaultExploreView
|
|
977
|
+
defaultExploreView: this.normalizeExploreDefaultView(this.form.controls.generalConfiguration.controls.defaultExploreView
|
|
911
978
|
.value),
|
|
912
979
|
showDesignSwitcher: this.form.controls.generalConfiguration.controls.showDesignSwitcher
|
|
913
980
|
.value === true,
|
|
@@ -949,7 +1016,7 @@ class SystemSettings {
|
|
|
949
1016
|
return { ...DEFAULT_GENERAL_CONFIGURATION };
|
|
950
1017
|
}
|
|
951
1018
|
return {
|
|
952
|
-
defaultExploreView: normalizeExploreDefaultView(setting.value['defaultExploreView'] ??
|
|
1019
|
+
defaultExploreView: this.normalizeExploreDefaultView(setting.value['defaultExploreView'] ??
|
|
953
1020
|
setting.value['DefaultExploreView']),
|
|
954
1021
|
showDesignSwitcher: normalizeShowDesignSwitcher(setting.value['showDesignSwitcher'] ??
|
|
955
1022
|
setting.value['ShowDesignSwitcher']),
|
|
@@ -1103,10 +1170,24 @@ class SystemSettings {
|
|
|
1103
1170
|
}
|
|
1104
1171
|
normalizeGeneralConfigurationSnapshot(value) {
|
|
1105
1172
|
return {
|
|
1106
|
-
defaultExploreView: normalizeExploreDefaultView(value.defaultExploreView),
|
|
1173
|
+
defaultExploreView: this.normalizeExploreDefaultView(value.defaultExploreView),
|
|
1107
1174
|
showDesignSwitcher: normalizeShowDesignSwitcher(value.showDesignSwitcher),
|
|
1108
1175
|
};
|
|
1109
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
|
+
}
|
|
1110
1191
|
normalizeTimezoneSnapshot(value) {
|
|
1111
1192
|
const defaults = this.getDefaultTimezoneValue();
|
|
1112
1193
|
return {
|
|
@@ -1354,7 +1435,7 @@ class SystemSettings {
|
|
|
1354
1435
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
1355
1436
|
}
|
|
1356
1437
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1357
|
-
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 [
|
|
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 });
|
|
1358
1439
|
}
|
|
1359
1440
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettings, decorators: [{
|
|
1360
1441
|
type: Component,
|
|
@@ -1369,8 +1450,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1369
1450
|
SystemSettingsHolidaysSection,
|
|
1370
1451
|
SystemSettingsGeneralSection,
|
|
1371
1452
|
SystemSettingsTimeSheetSection,
|
|
1372
|
-
], 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 [
|
|
1373
|
-
}], 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 }] }] } });
|
|
1374
1455
|
function isRecord(value) {
|
|
1375
1456
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1376
1457
|
}
|
|
@@ -1438,11 +1519,6 @@ function getNumberArray(value) {
|
|
|
1438
1519
|
function normalizeShowDesignSwitcher(value) {
|
|
1439
1520
|
return value === true || value === 'true' || value === 'True';
|
|
1440
1521
|
}
|
|
1441
|
-
function normalizeExploreDefaultView(value) {
|
|
1442
|
-
return EXPLORE_DEFAULT_VIEW_OPTIONS.some((option) => option.id === value)
|
|
1443
|
-
? value
|
|
1444
|
-
: DEFAULT_GENERAL_CONFIGURATION.defaultExploreView;
|
|
1445
|
-
}
|
|
1446
1522
|
function normalizeWeekendDays(value) {
|
|
1447
1523
|
if (!Array.isArray(value)) {
|
|
1448
1524
|
return [];
|
|
@@ -1478,15 +1554,80 @@ function normalizeFrequency(value) {
|
|
|
1478
1554
|
? value
|
|
1479
1555
|
: 'Weekly';
|
|
1480
1556
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
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
|
+
};
|
|
1490
1631
|
}
|
|
1491
1632
|
function parseCsv(value) {
|
|
1492
1633
|
return value
|
|
@@ -2694,7 +2835,7 @@ function isTestRequestConflict(error) {
|
|
|
2694
2835
|
const EMAIL_DELIVERY_ROUTES = [
|
|
2695
2836
|
{
|
|
2696
2837
|
path: '',
|
|
2697
|
-
loadComponent: () => import('./masterteam-system-settings-email-delivery-shell-
|
|
2838
|
+
loadComponent: () => import('./masterteam-system-settings-email-delivery-shell-CBPKuYXr.mjs').then((module) => module.EmailDeliveryShell),
|
|
2698
2839
|
},
|
|
2699
2840
|
{ path: '**', redirectTo: '' },
|
|
2700
2841
|
];
|
|
@@ -2704,4 +2845,4 @@ const EMAIL_DELIVERY_ROUTES = [
|
|
|
2704
2845
|
*/
|
|
2705
2846
|
|
|
2706
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 };
|
|
2707
|
-
//# sourceMappingURL=masterteam-system-settings-masterteam-system-settings-
|
|
2848
|
+
//# sourceMappingURL=masterteam-system-settings-masterteam-system-settings-DbsjQ7c7.mjs.map
|