@masterteam/system-settings 0.0.9 → 0.0.10

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.
@@ -189,13 +189,11 @@ class SystemSettingsTimeSheetSection {
189
189
  policyForm = input.required(...(ngDevMode ? [{ debugName: "policyForm" }] : /* istanbul ignore next */ []));
190
190
  globalWorkingHoursForm = input.required(...(ngDevMode ? [{ debugName: "globalWorkingHoursForm" }] : /* istanbul ignore next */ []));
191
191
  dayOptions = input([], ...(ngDevMode ? [{ debugName: "dayOptions" }] : /* istanbul ignore next */ []));
192
- scheduleItemTypeOptions = input([], ...(ngDevMode ? [{ debugName: "scheduleItemTypeOptions" }] : /* istanbul ignore next */ []));
193
- rateSourceOptions = input([], ...(ngDevMode ? [{ debugName: "rateSourceOptions" }] : /* istanbul ignore next */ []));
192
+ frequencyOptions = input([], ...(ngDevMode ? [{ debugName: "frequencyOptions" }] : /* istanbul ignore next */ []));
194
193
  timezoneOptions = input([], ...(ngDevMode ? [{ debugName: "timezoneOptions" }] : /* istanbul ignore next */ []));
195
- allowOvertimeControl = computed(() => this.policyForm().get('allowOvertime'), ...(ngDevMode ? [{ debugName: "allowOvertimeControl" }] : /* istanbul ignore next */ []));
196
- costEnabledControl = computed(() => this.policyForm().get('costEnabled'), ...(ngDevMode ? [{ debugName: "costEnabledControl" }] : /* istanbul ignore next */ []));
194
+ maxDailyBelowDaily = input(false, ...(ngDevMode ? [{ debugName: "maxDailyBelowDaily" }] : /* istanbul ignore next */ []));
197
195
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsTimeSheetSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
198
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.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 }, scheduleItemTypeOptions: { classPropertyName: "scheduleItemTypeOptions", publicName: "scheduleItemTypeOptions", isSignal: true, isRequired: false, transformFunction: null }, rateSourceOptions: { classPropertyName: "rateSourceOptions", publicName: "rateSourceOptions", isSignal: true, isRequired: false, transformFunction: null }, timezoneOptions: { classPropertyName: "timezoneOptions", publicName: "timezoneOptions", 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-2 mb-6\">\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(\"timesheet-policy-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"timesheet-policy-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div\r\n class=\"rounded-lg border border-sky-200 bg-sky-50 px-4 py-3 text-sm text-sky-800\"\r\n >\r\n {{ t(\"timesheet-policy-workflow-note\") }}\r\n </div>\r\n\r\n <div class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-select-field\r\n formControlName=\"weekStartDay\"\r\n [options]=\"dayOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'dayNumber'\"\r\n [label]=\"t('week-start-day')\"\r\n [placeholder]=\"t('week-start-day-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-multi-select-field\r\n formControlName=\"allowedScheduleItemTypes\"\r\n [options]=\"scheduleItemTypeOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'itemType'\"\r\n [label]=\"t('allowed-schedule-item-types')\"\r\n [placeholder]=\"t('allowed-schedule-item-types-placeholder')\"\r\n [display]=\"'chip'\"\r\n [required]=\"true\"\r\n [showClear]=\"true\"\r\n />\r\n\r\n <mt-number-field\r\n formControlName=\"regularDailyHours\"\r\n [label]=\"t('regular-daily-hours')\"\r\n [placeholder]=\"t('regular-daily-hours-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"24\"\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 />\r\n\r\n <mt-number-field\r\n formControlName=\"pastEntryLockDays\"\r\n [label]=\"t('past-entry-lock-days')\"\r\n [placeholder]=\"t('past-entry-lock-days-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"3650\"\r\n />\r\n\r\n <mt-select-field\r\n formControlName=\"rateSource\"\r\n [options]=\"rateSourceOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('rate-source')\"\r\n [placeholder]=\"t('rate-source-placeholder')\"\r\n [required]=\"true\"\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=\"generationEnabled\"\r\n [label]=\"t('generation-enabled')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"manualGenerationEnabled\"\r\n [label]=\"t('manual-generation-enabled')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowOvertime\"\r\n [label]=\"t('allow-overtime')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"requireOvertimeReason\"\r\n [label]=\"t('require-overtime-reason')\"\r\n />\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=\"assignedItemsOnly\"\r\n [label]=\"t('assigned-items-only')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowZeroHourSubmit\"\r\n [label]=\"t('allow-zero-hour-submit')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"costEnabled\"\r\n [label]=\"t('cost-enabled')\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\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(\"global-working-hours-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"global-working-hours-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 class=\"md:col-span-2\"\r\n formControlName=\"timeZoneId\"\r\n [options]=\"timezoneOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('global-timezone')\"\r\n [placeholder]=\"t('global-timezone-placeholder')\"\r\n [filter]=\"true\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-text-field\r\n formControlName=\"workDayStart\"\r\n type=\"time\"\r\n [label]=\"t('work-day-start')\"\r\n [placeholder]=\"t('work-day-start-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-text-field\r\n formControlName=\"workDayEnd\"\r\n type=\"time\"\r\n [label]=\"t('work-day-end')\"\r\n [placeholder]=\"t('work-day-end-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\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-textarea-field\r\n class=\"md:col-span-2\"\r\n formControlName=\"holidaysCsv\"\r\n [label]=\"t('global-holidays')\"\r\n [placeholder]=\"t('global-holidays-placeholder')\"\r\n rows=\"3\"\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: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { 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 });
196
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.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 }, timezoneOptions: { classPropertyName: "timezoneOptions", publicName: "timezoneOptions", isSignal: true, isRequired: false, transformFunction: null }, maxDailyBelowDaily: { classPropertyName: "maxDailyBelowDaily", publicName: "maxDailyBelowDaily", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\n <div class=\"grid gap-6 xl:grid-cols-2 mb-6\">\n <mt-card class=\"block\">\n <div [formGroup]=\"policyForm()\" class=\"space-y-5\">\n <div class=\"space-y-1\">\n <h4 class=\"text-base font-semibold text-neutral-900\">\n {{ t(\"general-card-title\") }}\n </h4>\n <p class=\"text-sm text-neutral-600\">\n {{ t(\"general-card-description\") }}\n </p>\n </div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mt-select-field\n formControlName=\"frequency\"\n [options]=\"frequencyOptions()\"\n [optionLabel]=\"'name'\"\n [optionValue]=\"'id'\"\n [label]=\"t('frequency')\"\n [placeholder]=\"t('frequency-placeholder')\"\n [required]=\"true\"\n />\n\n <mt-number-field\n formControlName=\"maxDailyHours\"\n [label]=\"t('max-daily-hours')\"\n [placeholder]=\"t('max-daily-hours-placeholder')\"\n [min]=\"1\"\n [max]=\"24\"\n [required]=\"true\"\n [hint]=\"maxDailyBelowDaily() ? t('max-daily-below-daily') : ''\"\n />\n\n <mt-number-field\n formControlName=\"submissionDeadlineDays\"\n [label]=\"t('submission-deadline-days')\"\n [placeholder]=\"t('submission-deadline-days-placeholder')\"\n [min]=\"0\"\n [max]=\"90\"\n [required]=\"true\"\n [hint]=\"t('submission-deadline-hint')\"\n />\n </div>\n\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-toggle-field\n formControlName=\"requireDescription\"\n [label]=\"t('require-description')\"\n />\n <mt-toggle-field\n formControlName=\"allowFutureEntry\"\n [label]=\"t('allow-future-entry')\"\n />\n <mt-toggle-field\n formControlName=\"allowBackdatedEntry\"\n [label]=\"t('allow-backdated-entry')\"\n />\n <mt-toggle-field\n formControlName=\"autoLockOnApprove\"\n [label]=\"t('auto-lock-on-approve')\"\n />\n </div>\n </div>\n </mt-card>\n\n <mt-card class=\"block\">\n <div [formGroup]=\"globalWorkingHoursForm()\" class=\"space-y-5\">\n <div class=\"space-y-1\">\n <h4 class=\"text-base font-semibold text-neutral-900\">\n {{ t(\"working-hours-card-title\") }}\n </h4>\n <p class=\"text-sm text-neutral-600\">\n {{ t(\"working-hours-card-description\") }}\n </p>\n </div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mt-select-field\n class=\"md:col-span-2\"\n formControlName=\"timeZoneId\"\n [options]=\"timezoneOptions()\"\n [optionLabel]=\"'name'\"\n [optionValue]=\"'id'\"\n [label]=\"t('global-timezone')\"\n [placeholder]=\"t('global-timezone-placeholder')\"\n [filter]=\"true\"\n [required]=\"true\"\n />\n\n <mt-multi-select-field\n class=\"md:col-span-2\"\n formControlName=\"workingDays\"\n [options]=\"dayOptions()\"\n [optionLabel]=\"'name'\"\n [optionValue]=\"'dayNumber'\"\n [label]=\"t('working-days')\"\n [placeholder]=\"t('working-days-placeholder')\"\n [display]=\"'chip'\"\n [required]=\"true\"\n [showClear]=\"true\"\n />\n\n <mt-number-field\n class=\"md:col-span-2\"\n formControlName=\"hoursPerDay\"\n [label]=\"t('daily-working-hours')\"\n [placeholder]=\"t('daily-working-hours-placeholder')\"\n [min]=\"0\"\n [max]=\"24\"\n [required]=\"true\"\n />\n\n <mt-textarea-field\n class=\"md:col-span-2\"\n formControlName=\"holidaysCsv\"\n [label]=\"t('global-holidays')\"\n [placeholder]=\"t('global-holidays-placeholder')\"\n rows=\"3\"\n />\n </div>\n </div>\n </mt-card>\n </div>\n</ng-container>\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: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { 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 });
199
197
  }
200
198
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettingsTimeSheetSection, decorators: [{
201
199
  type: Component,
@@ -207,10 +205,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
207
205
  NumberField,
208
206
  SelectField,
209
207
  TextareaField,
210
- TextField,
211
208
  ToggleField,
212
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\r\n <div class=\"grid gap-6 xl:grid-cols-2 mb-6\">\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(\"timesheet-policy-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"timesheet-policy-description\") }}\r\n </p>\r\n </div>\r\n\r\n <div\r\n class=\"rounded-lg border border-sky-200 bg-sky-50 px-4 py-3 text-sm text-sky-800\"\r\n >\r\n {{ t(\"timesheet-policy-workflow-note\") }}\r\n </div>\r\n\r\n <div class=\"grid gap-4 md:grid-cols-2\">\r\n <mt-select-field\r\n formControlName=\"weekStartDay\"\r\n [options]=\"dayOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'dayNumber'\"\r\n [label]=\"t('week-start-day')\"\r\n [placeholder]=\"t('week-start-day-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-multi-select-field\r\n formControlName=\"allowedScheduleItemTypes\"\r\n [options]=\"scheduleItemTypeOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'itemType'\"\r\n [label]=\"t('allowed-schedule-item-types')\"\r\n [placeholder]=\"t('allowed-schedule-item-types-placeholder')\"\r\n [display]=\"'chip'\"\r\n [required]=\"true\"\r\n [showClear]=\"true\"\r\n />\r\n\r\n <mt-number-field\r\n formControlName=\"regularDailyHours\"\r\n [label]=\"t('regular-daily-hours')\"\r\n [placeholder]=\"t('regular-daily-hours-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"24\"\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 />\r\n\r\n <mt-number-field\r\n formControlName=\"pastEntryLockDays\"\r\n [label]=\"t('past-entry-lock-days')\"\r\n [placeholder]=\"t('past-entry-lock-days-placeholder')\"\r\n [min]=\"0\"\r\n [max]=\"3650\"\r\n />\r\n\r\n <mt-select-field\r\n formControlName=\"rateSource\"\r\n [options]=\"rateSourceOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('rate-source')\"\r\n [placeholder]=\"t('rate-source-placeholder')\"\r\n [required]=\"true\"\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=\"generationEnabled\"\r\n [label]=\"t('generation-enabled')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"manualGenerationEnabled\"\r\n [label]=\"t('manual-generation-enabled')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowOvertime\"\r\n [label]=\"t('allow-overtime')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"requireOvertimeReason\"\r\n [label]=\"t('require-overtime-reason')\"\r\n />\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=\"assignedItemsOnly\"\r\n [label]=\"t('assigned-items-only')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"allowZeroHourSubmit\"\r\n [label]=\"t('allow-zero-hour-submit')\"\r\n />\r\n <mt-toggle-field\r\n formControlName=\"costEnabled\"\r\n [label]=\"t('cost-enabled')\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\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(\"global-working-hours-title\") }}\r\n </h4>\r\n <p class=\"text-sm text-neutral-600\">\r\n {{ t(\"global-working-hours-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 class=\"md:col-span-2\"\r\n formControlName=\"timeZoneId\"\r\n [options]=\"timezoneOptions()\"\r\n [optionLabel]=\"'name'\"\r\n [optionValue]=\"'id'\"\r\n [label]=\"t('global-timezone')\"\r\n [placeholder]=\"t('global-timezone-placeholder')\"\r\n [filter]=\"true\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-text-field\r\n formControlName=\"workDayStart\"\r\n type=\"time\"\r\n [label]=\"t('work-day-start')\"\r\n [placeholder]=\"t('work-day-start-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\r\n <mt-text-field\r\n formControlName=\"workDayEnd\"\r\n type=\"time\"\r\n [label]=\"t('work-day-end')\"\r\n [placeholder]=\"t('work-day-end-placeholder')\"\r\n [required]=\"true\"\r\n />\r\n\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-textarea-field\r\n class=\"md:col-span-2\"\r\n formControlName=\"holidaysCsv\"\r\n [label]=\"t('global-holidays')\"\r\n [placeholder]=\"t('global-holidays-placeholder')\"\r\n rows=\"3\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n </div>\r\n</ng-container>\r\n" }]
213
- }], 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 }] }], scheduleItemTypeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "scheduleItemTypeOptions", required: false }] }], rateSourceOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rateSourceOptions", required: false }] }], timezoneOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "timezoneOptions", required: false }] }] } });
209
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'system-settings'\">\n <div class=\"grid gap-6 xl:grid-cols-2 mb-6\">\n <mt-card class=\"block\">\n <div [formGroup]=\"policyForm()\" class=\"space-y-5\">\n <div class=\"space-y-1\">\n <h4 class=\"text-base font-semibold text-neutral-900\">\n {{ t(\"general-card-title\") }}\n </h4>\n <p class=\"text-sm text-neutral-600\">\n {{ t(\"general-card-description\") }}\n </p>\n </div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mt-select-field\n formControlName=\"frequency\"\n [options]=\"frequencyOptions()\"\n [optionLabel]=\"'name'\"\n [optionValue]=\"'id'\"\n [label]=\"t('frequency')\"\n [placeholder]=\"t('frequency-placeholder')\"\n [required]=\"true\"\n />\n\n <mt-number-field\n formControlName=\"maxDailyHours\"\n [label]=\"t('max-daily-hours')\"\n [placeholder]=\"t('max-daily-hours-placeholder')\"\n [min]=\"1\"\n [max]=\"24\"\n [required]=\"true\"\n [hint]=\"maxDailyBelowDaily() ? t('max-daily-below-daily') : ''\"\n />\n\n <mt-number-field\n formControlName=\"submissionDeadlineDays\"\n [label]=\"t('submission-deadline-days')\"\n [placeholder]=\"t('submission-deadline-days-placeholder')\"\n [min]=\"0\"\n [max]=\"90\"\n [required]=\"true\"\n [hint]=\"t('submission-deadline-hint')\"\n />\n </div>\n\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-toggle-field\n formControlName=\"requireDescription\"\n [label]=\"t('require-description')\"\n />\n <mt-toggle-field\n formControlName=\"allowFutureEntry\"\n [label]=\"t('allow-future-entry')\"\n />\n <mt-toggle-field\n formControlName=\"allowBackdatedEntry\"\n [label]=\"t('allow-backdated-entry')\"\n />\n <mt-toggle-field\n formControlName=\"autoLockOnApprove\"\n [label]=\"t('auto-lock-on-approve')\"\n />\n </div>\n </div>\n </mt-card>\n\n <mt-card class=\"block\">\n <div [formGroup]=\"globalWorkingHoursForm()\" class=\"space-y-5\">\n <div class=\"space-y-1\">\n <h4 class=\"text-base font-semibold text-neutral-900\">\n {{ t(\"working-hours-card-title\") }}\n </h4>\n <p class=\"text-sm text-neutral-600\">\n {{ t(\"working-hours-card-description\") }}\n </p>\n </div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mt-select-field\n class=\"md:col-span-2\"\n formControlName=\"timeZoneId\"\n [options]=\"timezoneOptions()\"\n [optionLabel]=\"'name'\"\n [optionValue]=\"'id'\"\n [label]=\"t('global-timezone')\"\n [placeholder]=\"t('global-timezone-placeholder')\"\n [filter]=\"true\"\n [required]=\"true\"\n />\n\n <mt-multi-select-field\n class=\"md:col-span-2\"\n formControlName=\"workingDays\"\n [options]=\"dayOptions()\"\n [optionLabel]=\"'name'\"\n [optionValue]=\"'dayNumber'\"\n [label]=\"t('working-days')\"\n [placeholder]=\"t('working-days-placeholder')\"\n [display]=\"'chip'\"\n [required]=\"true\"\n [showClear]=\"true\"\n />\n\n <mt-number-field\n class=\"md:col-span-2\"\n formControlName=\"hoursPerDay\"\n [label]=\"t('daily-working-hours')\"\n [placeholder]=\"t('daily-working-hours-placeholder')\"\n [min]=\"0\"\n [max]=\"24\"\n [required]=\"true\"\n />\n\n <mt-textarea-field\n class=\"md:col-span-2\"\n formControlName=\"holidaysCsv\"\n [label]=\"t('global-holidays')\"\n [placeholder]=\"t('global-holidays-placeholder')\"\n rows=\"3\"\n />\n </div>\n </div>\n </mt-card>\n </div>\n</ng-container>\n" }]
210
+ }], 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 }] }], timezoneOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "timezoneOptions", required: false }] }], maxDailyBelowDaily: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDailyBelowDaily", required: false }] }] } });
214
211
 
215
212
  const FIXED_CURRENCIES = [
216
213
  {
@@ -303,6 +300,12 @@ const DOTNET_DAY_DEFINITIONS = [
303
300
  { dayNumber: 5, translationKey: 'friday' },
304
301
  { dayNumber: 6, translationKey: 'saturday' },
305
302
  ];
303
+ const TIMESHEET_FREQUENCY_OPTIONS = [
304
+ { id: 'Daily', translationKey: 'frequency-daily' },
305
+ { id: 'Weekly', translationKey: 'frequency-weekly' },
306
+ { id: 'BiWeekly', translationKey: 'frequency-biweekly' },
307
+ { id: 'Monthly', translationKey: 'frequency-monthly' },
308
+ ];
306
309
  const SCHEDULE_ITEM_TYPE_OPTIONS = [
307
310
  { itemType: 'Task', translationKey: 'schedule-item-task' },
308
311
  { itemType: 'Milestone', translationKey: 'schedule-item-milestone' },
@@ -386,53 +389,36 @@ class SystemSettings {
386
389
  port: new FormControl('', { nonNullable: true }),
387
390
  }),
388
391
  timeSheetPolicy: new FormGroup({
389
- weekStartDay: new FormControl(null, Validators.required),
390
- generationEnabled: new FormControl(true, { nonNullable: true }),
391
- manualGenerationEnabled: new FormControl(true, { nonNullable: true }),
392
- regularDailyHours: new FormControl(8, [
393
- Validators.required,
394
- Validators.min(0),
395
- Validators.max(24),
396
- ]),
392
+ frequency: new FormControl('Weekly', Validators.required),
397
393
  maxDailyHours: new FormControl(24, [
398
394
  Validators.required,
399
395
  Validators.min(1),
400
396
  Validators.max(24),
401
397
  ]),
402
- allowOvertime: new FormControl(true, { nonNullable: true }),
403
- requireOvertimeReason: new FormControl(false, { nonNullable: true }),
404
398
  requireDescription: new FormControl(false, { nonNullable: true }),
405
- allowFutureEntry: new FormControl(true, { nonNullable: true }),
406
- pastEntryLockDays: new FormControl(null, [
399
+ allowFutureEntry: new FormControl(false, { nonNullable: true }),
400
+ allowBackdatedEntry: new FormControl(true, { nonNullable: true }),
401
+ autoLockOnApprove: new FormControl(true, { nonNullable: true }),
402
+ submissionDeadlineDays: new FormControl(3, [
403
+ Validators.required,
407
404
  Validators.min(0),
408
- Validators.max(3650),
405
+ Validators.max(90),
409
406
  ]),
410
- allowedScheduleItemTypes: new FormControl(['Deliverable'], {
411
- nonNullable: true,
412
- validators: [Validators.required],
413
- }),
414
- assignedItemsOnly: new FormControl(false, { nonNullable: true }),
415
- allowZeroHourSubmit: new FormControl(true, { nonNullable: true }),
416
- costEnabled: new FormControl(true, { nonNullable: true }),
417
- rateSource: new FormControl('UserHourlyRate', Validators.required),
418
407
  }),
419
408
  globalWorkingHours: new FormGroup({
420
409
  timeZoneId: new FormControl(null, Validators.required),
421
- workDayStart: new FormControl('08:00', {
422
- nonNullable: true,
423
- validators: [Validators.required],
424
- }),
425
- workDayEnd: new FormControl('16:00', {
426
- nonNullable: true,
427
- validators: [Validators.required],
428
- }),
429
410
  workingDays: new FormControl([0, 1, 2, 3, 4], {
430
411
  nonNullable: true,
431
412
  validators: [Validators.required],
432
413
  }),
414
+ hoursPerDay: new FormControl(8, [
415
+ Validators.required,
416
+ Validators.min(0),
417
+ Validators.max(24),
418
+ ]),
433
419
  holidaysCsv: new FormControl('', { nonNullable: true }),
434
420
  }),
435
- });
421
+ }, maxDailyHoursGteHoursPerDay);
436
422
  settings = signal(null, ...(ngDevMode ? [{ debugName: "settings" }] : /* istanbul ignore next */ []));
437
423
  settingsLoading = signal(false, ...(ngDevMode ? [{ debugName: "settingsLoading" }] : /* istanbul ignore next */ []));
438
424
  settingsError = signal(null, ...(ngDevMode ? [{ debugName: "settingsError" }] : /* istanbul ignore next */ []));
@@ -510,21 +496,13 @@ class SystemSettings {
510
496
  name: this.transloco.translate(`system-settings.${day.translationKey}`),
511
497
  }));
512
498
  }, ...(ngDevMode ? [{ debugName: "dotnetDayOptions" }] : /* istanbul ignore next */ []));
513
- scheduleItemTypeOptions = computed(() => {
514
- this.activeLang();
515
- return SCHEDULE_ITEM_TYPE_OPTIONS.map((item) => ({
516
- id: item.itemType,
517
- itemType: item.itemType,
518
- name: this.transloco.translate(`system-settings.${item.translationKey}`),
519
- }));
520
- }, ...(ngDevMode ? [{ debugName: "scheduleItemTypeOptions" }] : /* istanbul ignore next */ []));
521
- rateSourceOptions = computed(() => {
499
+ frequencyOptions = computed(() => {
522
500
  this.activeLang();
523
- return RATE_SOURCE_OPTIONS.map((item) => ({
501
+ return TIMESHEET_FREQUENCY_OPTIONS.map((item) => ({
524
502
  id: item.id,
525
503
  name: this.transloco.translate(`system-settings.${item.translationKey}`),
526
504
  }));
527
- }, ...(ngDevMode ? [{ debugName: "rateSourceOptions" }] : /* istanbul ignore next */ []));
505
+ }, ...(ngDevMode ? [{ debugName: "frequencyOptions" }] : /* istanbul ignore next */ []));
528
506
  isReady = computed(() => this.hydrationVersion() !== null && this.initialSnapshot() !== null, ...(ngDevMode ? [{ debugName: "isReady" }] : /* istanbul ignore next */ []));
529
507
  isLoading = computed(() => this.settingsLoading() || this.timezonesLoading() || !this.isReady(), ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
530
508
  hasChanges = computed(() => {
@@ -754,8 +732,8 @@ class SystemSettings {
754
732
  const currency = this.normalizeCurrencySnapshot(this.extractCurrencyValue(currencySetting));
755
733
  const weekend = this.normalizeWeekendSnapshot(this.extractWeekendValue(weekendSetting));
756
734
  const smtp = this.normalizeSmtpSnapshot(this.extractSmtpValue(smtpSetting));
757
- const timeSheetPolicy = this.normalizeTimeSheetPolicySnapshot(this.extractTimeSheetPolicyValue(timeSheetPolicySetting));
758
735
  const globalWorkingHours = this.normalizeGlobalWorkingHoursSnapshot(this.extractGlobalWorkingHoursValue(globalWorkingHoursSetting));
736
+ const timeSheetPolicy = this.normalizeTimeSheetPolicySnapshot(this.extractTimeSheetPolicyValue(timeSheetPolicySetting), globalWorkingHours.hoursPerDay);
759
737
  this.holidayItems.set(holidays.data);
760
738
  this.form.controls.timezone.setValue({
761
739
  regionId: timezone.region?.id ?? null,
@@ -777,27 +755,18 @@ class SystemSettings {
777
755
  port: smtp.port === null ? '' : String(smtp.port),
778
756
  });
779
757
  this.form.controls.timeSheetPolicy.setValue({
780
- weekStartDay: timeSheetPolicy.weekStartDay,
781
- generationEnabled: timeSheetPolicy.generationEnabled,
782
- manualGenerationEnabled: timeSheetPolicy.manualGenerationEnabled,
783
- regularDailyHours: timeSheetPolicy.regularDailyHours,
758
+ frequency: timeSheetPolicy.frequency,
784
759
  maxDailyHours: timeSheetPolicy.maxDailyHours,
785
- allowOvertime: timeSheetPolicy.allowOvertime,
786
- requireOvertimeReason: timeSheetPolicy.requireOvertimeReason,
787
760
  requireDescription: timeSheetPolicy.requireDescription,
788
761
  allowFutureEntry: timeSheetPolicy.allowFutureEntry,
789
- pastEntryLockDays: timeSheetPolicy.pastEntryLockDays,
790
- allowedScheduleItemTypes: timeSheetPolicy.allowedScheduleItemTypes,
791
- assignedItemsOnly: timeSheetPolicy.assignedItemsOnly,
792
- allowZeroHourSubmit: timeSheetPolicy.allowZeroHourSubmit,
793
- costEnabled: timeSheetPolicy.costEnabled,
794
- rateSource: timeSheetPolicy.rateSource,
762
+ allowBackdatedEntry: timeSheetPolicy.allowBackdatedEntry,
763
+ autoLockOnApprove: timeSheetPolicy.autoLockOnApprove,
764
+ submissionDeadlineDays: timeSheetPolicy.submissionDeadlineDays,
795
765
  });
796
766
  this.form.controls.globalWorkingHours.setValue({
797
767
  timeZoneId: globalWorkingHours.timeZoneId,
798
- workDayStart: minutesToTime(globalWorkingHours.workDayStartMinute),
799
- workDayEnd: minutesToTime(globalWorkingHours.workDayEndMinute),
800
768
  workingDays: globalWorkingHours.workingDays,
769
+ hoursPerDay: globalWorkingHours.hoursPerDay,
801
770
  holidaysCsv: globalWorkingHours.holidays.join(', '),
802
771
  });
803
772
  this.form.markAsPristine();
@@ -886,35 +855,23 @@ class SystemSettings {
886
855
  }
887
856
  buildTimeSheetPolicyValue() {
888
857
  const policyForm = this.form.controls.timeSheetPolicy.getRawValue();
858
+ const hoursPerDay = clampNumber(this.form.controls.globalWorkingHours.controls.hoursPerDay.value ?? 8, 0, 24);
889
859
  return {
890
- weekStartDay: policyForm.weekStartDay ?? 0,
891
- generationEnabled: policyForm.generationEnabled,
892
- manualGenerationEnabled: policyForm.manualGenerationEnabled,
893
- regularDailyHours: policyForm.regularDailyHours ?? 8,
894
- maxDailyHours: policyForm.maxDailyHours ?? 24,
895
- allowOvertime: policyForm.allowOvertime,
896
- requireOvertimeReason: policyForm.allowOvertime
897
- ? policyForm.requireOvertimeReason
898
- : false,
860
+ frequency: normalizeFrequency(policyForm.frequency),
861
+ maxDailyHours: Math.max(hoursPerDay, clampNumber(policyForm.maxDailyHours ?? 24, 1, 24)),
899
862
  requireDescription: policyForm.requireDescription,
900
863
  allowFutureEntry: policyForm.allowFutureEntry,
901
- pastEntryLockDays: policyForm.pastEntryLockDays,
902
- allowedScheduleItemTypes: policyForm.allowedScheduleItemTypes,
903
- assignedItemsOnly: policyForm.assignedItemsOnly,
904
- allowZeroHourSubmit: policyForm.allowZeroHourSubmit,
905
- costEnabled: policyForm.costEnabled,
906
- rateSource: policyForm.costEnabled
907
- ? (policyForm.rateSource ?? 'UserHourlyRate')
908
- : 'None',
864
+ allowBackdatedEntry: policyForm.allowBackdatedEntry,
865
+ autoLockOnApprove: policyForm.autoLockOnApprove,
866
+ submissionDeadlineDays: clampNumber(policyForm.submissionDeadlineDays ?? 0, 0, 90),
909
867
  };
910
868
  }
911
869
  buildGlobalWorkingHoursValue() {
912
870
  const globalForm = this.form.controls.globalWorkingHours.getRawValue();
913
871
  return {
914
872
  timeZoneId: globalForm.timeZoneId ?? 'UTC',
915
- workDayStartMinute: timeToMinutes(globalForm.workDayStart),
916
- workDayEndMinute: timeToMinutes(globalForm.workDayEnd),
917
873
  workingDays: normalizeDotnetDays(globalForm.workingDays),
874
+ hoursPerDay: clampNumber(globalForm.hoursPerDay ?? 8, 0, 24),
918
875
  holidays: parseCsv(globalForm.holidaysCsv),
919
876
  };
920
877
  }
@@ -988,47 +945,36 @@ class SystemSettings {
988
945
  if (!isRecord(setting.value)) {
989
946
  return this.getDefaultTimeSheetPolicyValue();
990
947
  }
948
+ const value = setting.value;
991
949
  const defaults = this.getDefaultTimeSheetPolicyValue();
992
950
  return {
993
- weekStartDay: getNumber(setting.value['weekStartDay']) ?? defaults.weekStartDay,
994
- generationEnabled: getBoolean(setting.value['generationEnabled']) ??
995
- defaults.generationEnabled,
996
- manualGenerationEnabled: getBoolean(setting.value['manualGenerationEnabled']) ??
997
- defaults.manualGenerationEnabled,
998
- regularDailyHours: getNumber(setting.value['regularDailyHours']) ??
999
- defaults.regularDailyHours,
1000
- maxDailyHours: getNumber(setting.value['maxDailyHours']) ?? defaults.maxDailyHours,
1001
- allowOvertime: getBoolean(setting.value['allowOvertime']) ?? defaults.allowOvertime,
1002
- requireOvertimeReason: getBoolean(setting.value['requireOvertimeReason']) ??
1003
- defaults.requireOvertimeReason,
1004
- requireDescription: getBoolean(setting.value['requireDescription']) ??
1005
- defaults.requireDescription,
1006
- allowFutureEntry: getBoolean(setting.value['allowFutureEntry']) ??
951
+ frequency: normalizeFrequency(value['frequency'] ?? value['Frequency']),
952
+ maxDailyHours: getNumber(value['maxDailyHours'] ?? value['MaxDailyHours']) ??
953
+ defaults.maxDailyHours,
954
+ requireDescription: getBoolean(value['requireDescription'] ?? value['RequireDescription']) ?? defaults.requireDescription,
955
+ allowFutureEntry: getBoolean(value['allowFutureEntry'] ?? value['AllowFutureEntry']) ??
1007
956
  defaults.allowFutureEntry,
1008
- pastEntryLockDays: getNullableNumber(setting.value['pastEntryLockDays']),
1009
- allowedScheduleItemTypes: getStringArray(setting.value['allowedScheduleItemTypes']) ??
1010
- defaults.allowedScheduleItemTypes,
1011
- assignedItemsOnly: getBoolean(setting.value['assignedItemsOnly']) ??
1012
- defaults.assignedItemsOnly,
1013
- allowZeroHourSubmit: getBoolean(setting.value['allowZeroHourSubmit']) ??
1014
- defaults.allowZeroHourSubmit,
1015
- costEnabled: getBoolean(setting.value['costEnabled']) ?? defaults.costEnabled,
1016
- rateSource: getString(setting.value['rateSource']) ?? defaults.rateSource,
957
+ allowBackdatedEntry: getBoolean(value['allowBackdatedEntry'] ?? value['AllowBackdatedEntry']) ?? defaults.allowBackdatedEntry,
958
+ autoLockOnApprove: getBoolean(value['autoLockOnApprove'] ?? value['AutoLockOnApprove']) ??
959
+ defaults.autoLockOnApprove,
960
+ submissionDeadlineDays: getNumber(value['submissionDeadlineDays'] ?? value['SubmissionDeadlineDays']) ?? defaults.submissionDeadlineDays,
1017
961
  };
1018
962
  }
1019
963
  extractGlobalWorkingHoursValue(setting) {
1020
964
  if (!isRecord(setting.value)) {
1021
965
  return this.getDefaultGlobalWorkingHoursValue();
1022
966
  }
967
+ const value = setting.value;
1023
968
  const defaults = this.getDefaultGlobalWorkingHoursValue();
1024
969
  return {
1025
- timeZoneId: getString(setting.value['timeZoneId']) ?? defaults.timeZoneId,
1026
- workDayStartMinute: getNumber(setting.value['workDayStartMinute']) ??
1027
- defaults.workDayStartMinute,
1028
- workDayEndMinute: getNumber(setting.value['workDayEndMinute']) ??
1029
- defaults.workDayEndMinute,
1030
- workingDays: getNumberArray(setting.value['workingDays']) ?? defaults.workingDays,
1031
- holidays: getStringArray(setting.value['holidays']) ?? defaults.holidays,
970
+ timeZoneId: getString(value['timeZoneId'] ?? value['TimeZoneId']) ??
971
+ defaults.timeZoneId,
972
+ workingDays: getNumberArray(value['workingDays'] ?? value['WorkingDays']) ??
973
+ defaults.workingDays,
974
+ hoursPerDay: getNumber(value['hoursPerDay'] ?? value['HoursPerDay']) ??
975
+ defaults.hoursPerDay,
976
+ holidays: getStringArray(value['holidays'] ?? value['Holidays']) ??
977
+ defaults.holidays,
1032
978
  };
1033
979
  }
1034
980
  normalizeHolidayItem(value) {
@@ -1124,33 +1070,23 @@ class SystemSettings {
1124
1070
  port: value.port,
1125
1071
  };
1126
1072
  }
1127
- normalizeTimeSheetPolicySnapshot(value) {
1128
- const defaults = this.getDefaultTimeSheetPolicyValue();
1129
- const regularDailyHours = clampNumber(value.regularDailyHours, 0, 24);
1130
- const maxDailyHours = Math.max(regularDailyHours, clampNumber(value.maxDailyHours, 1, 24));
1131
- const allowedScheduleItemTypes = value.allowedScheduleItemTypes.length
1132
- ? value.allowedScheduleItemTypes
1133
- : defaults.allowedScheduleItemTypes;
1073
+ normalizeTimeSheetPolicySnapshot(value, hoursPerDay) {
1134
1074
  return {
1135
- ...defaults,
1136
- ...value,
1137
- weekStartDay: clampNumber(value.weekStartDay, 0, 6),
1138
- regularDailyHours,
1139
- maxDailyHours,
1140
- pastEntryLockDays: value.pastEntryLockDays === null
1141
- ? null
1142
- : clampNumber(value.pastEntryLockDays, 0, 3650),
1143
- allowedScheduleItemTypes,
1144
- rateSource: value.rateSource || defaults.rateSource,
1075
+ frequency: normalizeFrequency(value.frequency),
1076
+ maxDailyHours: Math.max(clampNumber(hoursPerDay, 0, 24), clampNumber(value.maxDailyHours, 1, 24)),
1077
+ requireDescription: value.requireDescription,
1078
+ allowFutureEntry: value.allowFutureEntry,
1079
+ allowBackdatedEntry: value.allowBackdatedEntry,
1080
+ autoLockOnApprove: value.autoLockOnApprove,
1081
+ submissionDeadlineDays: clampNumber(value.submissionDeadlineDays, 0, 90),
1145
1082
  };
1146
1083
  }
1147
1084
  normalizeGlobalWorkingHoursSnapshot(value) {
1148
1085
  const defaults = this.getDefaultGlobalWorkingHoursValue();
1149
1086
  return {
1150
1087
  timeZoneId: value.timeZoneId || defaults.timeZoneId,
1151
- workDayStartMinute: clampNumber(value.workDayStartMinute, 0, 1439),
1152
- workDayEndMinute: clampNumber(value.workDayEndMinute, 1, 1440),
1153
1088
  workingDays: normalizeDotnetDays(value.workingDays),
1089
+ hoursPerDay: clampNumber(value.hoursPerDay, 0, 24),
1154
1090
  holidays: value.holidays,
1155
1091
  };
1156
1092
  }
@@ -1235,29 +1171,20 @@ class SystemSettings {
1235
1171
  }
1236
1172
  getDefaultTimeSheetPolicyValue() {
1237
1173
  return {
1238
- weekStartDay: 0,
1239
- generationEnabled: true,
1240
- manualGenerationEnabled: true,
1241
- regularDailyHours: 8,
1174
+ frequency: 'Weekly',
1242
1175
  maxDailyHours: 24,
1243
- allowOvertime: true,
1244
- requireOvertimeReason: false,
1245
1176
  requireDescription: false,
1246
- allowFutureEntry: true,
1247
- pastEntryLockDays: null,
1248
- allowedScheduleItemTypes: ['Deliverable'],
1249
- assignedItemsOnly: false,
1250
- allowZeroHourSubmit: true,
1251
- costEnabled: true,
1252
- rateSource: 'UserHourlyRate',
1177
+ allowFutureEntry: false,
1178
+ allowBackdatedEntry: true,
1179
+ autoLockOnApprove: true,
1180
+ submissionDeadlineDays: 3,
1253
1181
  };
1254
1182
  }
1255
1183
  getDefaultGlobalWorkingHoursValue() {
1256
1184
  return {
1257
1185
  timeZoneId: this.timezones()[0]?.id ?? 'UTC',
1258
- workDayStartMinute: 480,
1259
- workDayEndMinute: 960,
1260
1186
  workingDays: [0, 1, 2, 3, 4],
1187
+ hoursPerDay: 8,
1261
1188
  holidays: [],
1262
1189
  };
1263
1190
  }
@@ -1375,7 +1302,7 @@ class SystemSettings {
1375
1302
  return JSON.stringify(left) === JSON.stringify(right);
1376
1303
  }
1377
1304
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
1378
- 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-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n\r\n <mt-system-settings-general-section\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [weekendForm]=\"form.controls.weekend\"\r\n [timezoneOptions]=\"timezoneOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n [weekendOptions]=\"weekendOptions()\"\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 [scheduleItemTypeOptions]=\"scheduleItemTypeOptions()\"\r\n [rateSourceOptions]=\"rateSourceOptions()\"\r\n [timezoneOptions]=\"timezoneOptions()\"\r\n />\r\n\r\n <mt-system-settings-smtp-section [smtpForm]=\"form.controls.smtp\" />\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"], 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: ["timezoneForm", "currencyForm", "weekendForm", "timezoneOptions", "dateFormatOptions", "timeFormatOptions", "currencyOptions", "currencyFormatOptions", "weekendOptions"] }, { kind: "component", type: SystemSettingsSmtpSection, selector: "mt-system-settings-smtp-section", inputs: ["smtpForm"] }, { kind: "component", type: SystemSettingsTimeSheetSection, selector: "mt-system-settings-timesheet-section", inputs: ["policyForm", "globalWorkingHoursForm", "dayOptions", "scheduleItemTypeOptions", "rateSourceOptions", "timezoneOptions"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1305
+ 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-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n\r\n <mt-system-settings-general-section\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [weekendForm]=\"form.controls.weekend\"\r\n [timezoneOptions]=\"timezoneOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n [weekendOptions]=\"weekendOptions()\"\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 [timezoneOptions]=\"timezoneOptions()\"\r\n [maxDailyBelowDaily]=\"form.errors?.['maxDailyBelowDaily'] ?? false\"\r\n />\r\n\r\n <mt-system-settings-smtp-section [smtpForm]=\"form.controls.smtp\" />\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"], 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: ["timezoneForm", "currencyForm", "weekendForm", "timezoneOptions", "dateFormatOptions", "timeFormatOptions", "currencyOptions", "currencyFormatOptions", "weekendOptions"] }, { kind: "component", type: SystemSettingsSmtpSection, selector: "mt-system-settings-smtp-section", inputs: ["smtpForm"] }, { kind: "component", type: SystemSettingsTimeSheetSection, selector: "mt-system-settings-timesheet-section", inputs: ["policyForm", "globalWorkingHoursForm", "dayOptions", "frequencyOptions", "timezoneOptions", "maxDailyBelowDaily"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1379
1306
  }
1380
1307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SystemSettings, decorators: [{
1381
1308
  type: Component,
@@ -1391,7 +1318,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1391
1318
  SystemSettingsGeneralSection,
1392
1319
  SystemSettingsSmtpSection,
1393
1320
  SystemSettingsTimeSheetSection,
1394
- ], 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-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n\r\n <mt-system-settings-general-section\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [weekendForm]=\"form.controls.weekend\"\r\n [timezoneOptions]=\"timezoneOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n [weekendOptions]=\"weekendOptions()\"\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 [scheduleItemTypeOptions]=\"scheduleItemTypeOptions()\"\r\n [rateSourceOptions]=\"rateSourceOptions()\"\r\n [timezoneOptions]=\"timezoneOptions()\"\r\n />\r\n\r\n <mt-system-settings-smtp-section [smtpForm]=\"form.controls.smtp\" />\r\n </div>\r\n }\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block}\n"] }]
1321
+ ], 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-holidays-section\r\n [holidays]=\"holidayTableRows()\"\r\n (addHoliday)=\"openAddHoliday()\"\r\n (editHoliday)=\"editHoliday($event)\"\r\n (deleteHoliday)=\"deleteHoliday($event)\"\r\n />\r\n\r\n <mt-system-settings-general-section\r\n [timezoneForm]=\"form.controls.timezone\"\r\n [currencyForm]=\"form.controls.currency\"\r\n [weekendForm]=\"form.controls.weekend\"\r\n [timezoneOptions]=\"timezoneOptions()\"\r\n [dateFormatOptions]=\"dateFormatOptions()\"\r\n [timeFormatOptions]=\"timeFormatOptions()\"\r\n [currencyOptions]=\"currencyOptions()\"\r\n [currencyFormatOptions]=\"currencyFormatOptions()\"\r\n [weekendOptions]=\"weekendOptions()\"\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 [timezoneOptions]=\"timezoneOptions()\"\r\n [maxDailyBelowDaily]=\"form.errors?.['maxDailyBelowDaily'] ?? false\"\r\n />\r\n\r\n <mt-system-settings-smtp-section [smtpForm]=\"form.controls.smtp\" />\r\n </div>\r\n }\r\n </div>\r\n </mt-page>\r\n</ng-container>\r\n", styles: [":host{display:block}\n"] }]
1395
1322
  }], 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 }] }] } });
1396
1323
  function isRecord(value) {
1397
1324
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -1484,18 +1411,27 @@ function clampNumber(value, min, max) {
1484
1411
  }
1485
1412
  return Math.min(Math.max(value, min), max);
1486
1413
  }
1487
- function timeToMinutes(value) {
1488
- const [hours, minutes] = value.split(':').map((part) => Number(part));
1489
- if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
1490
- return 0;
1491
- }
1492
- return clampNumber(hours * 60 + minutes, 0, 1440);
1414
+ const TIMESHEET_FREQUENCIES = [
1415
+ 'Daily',
1416
+ 'Weekly',
1417
+ 'BiWeekly',
1418
+ 'Monthly',
1419
+ ];
1420
+ function normalizeFrequency(value) {
1421
+ return typeof value === 'string' &&
1422
+ TIMESHEET_FREQUENCIES.includes(value)
1423
+ ? value
1424
+ : 'Weekly';
1493
1425
  }
1494
- function minutesToTime(value) {
1495
- const normalizedValue = clampNumber(value, 0, 1440);
1496
- const hours = Math.floor(normalizedValue / 60);
1497
- const minutes = normalizedValue % 60;
1498
- return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
1426
+ function maxDailyHoursGteHoursPerDay(group) {
1427
+ const maxDailyHours = group.get('timeSheetPolicy.maxDailyHours')?.value;
1428
+ const hoursPerDay = group.get('globalWorkingHours.hoursPerDay')?.value;
1429
+ if (typeof maxDailyHours === 'number' &&
1430
+ typeof hoursPerDay === 'number' &&
1431
+ maxDailyHours < hoursPerDay) {
1432
+ return { maxDailyBelowDaily: true };
1433
+ }
1434
+ return null;
1499
1435
  }
1500
1436
  function parseCsv(value) {
1501
1437
  return value
@@ -1542,5 +1478,5 @@ function formatDateOnly(value) {
1542
1478
  * Generated bundle index. Do not edit.
1543
1479
  */
1544
1480
 
1545
- export { DEFAULT_DATE_FORMAT_OPTIONS, DEFAULT_TIME_FORMAT_OPTIONS, DOTNET_DAY_DEFINITIONS, FIXED_CURRENCIES, RATE_SOURCE_OPTIONS, SCHEDULE_ITEM_TYPE_OPTIONS, SystemSettings, SystemSettingsHolidayEditor, WEEKEND_DAY_DEFINITIONS };
1481
+ export { DEFAULT_DATE_FORMAT_OPTIONS, DEFAULT_TIME_FORMAT_OPTIONS, DOTNET_DAY_DEFINITIONS, FIXED_CURRENCIES, RATE_SOURCE_OPTIONS, SCHEDULE_ITEM_TYPE_OPTIONS, SystemSettings, SystemSettingsHolidayEditor, TIMESHEET_FREQUENCY_OPTIONS, WEEKEND_DAY_DEFINITIONS };
1546
1482
  //# sourceMappingURL=masterteam-system-settings.mjs.map