feat(ui): configure planning horizon alongside leave balances

This commit is contained in:
nessar authored and nessar committed 2026-10-08 06:49:06 +00:00
1 parent 964abd3546
commit 31d4333783
14 files changed
+196 -139

No files matched your search

@@ -2,53 +2,6 @@
<mat-dialog-content>
<p>{{ tr().settings.description }}</p>
<div class="settings-controls">
<fieldset class="planning-dates">
<legend>{{ tr().settings.planningHorizon }}</legend>
<p id="planning-dates-help">{{ tr().settings.planningDatesHelp }}</p>
<mat-form-field appearance="outline">
<mat-label>{{ tr().settings.planningStart }}</mat-label>
<input
matInput
type="date"
name="planningStart"
required
#planningStart
[value]="calendarSettings.planningDates().start"
[attr.aria-invalid]="!!calendarSettings.planningHorizonError()"
aria-describedby="planning-dates-help planning-dates-error"
(input)="
calendarSettings.setPlanningDates(
planningStart.value,
lastPlanningDate.value
)
"
/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>{{ tr().settings.lastPlanningDate }}</mat-label>
<input
matInput
type="date"
name="lastPlanningDate"
required
#lastPlanningDate
[value]="calendarSettings.planningDates().lastPlanningDate"
[attr.aria-invalid]="!!calendarSettings.planningHorizonError()"
aria-describedby="planning-dates-help planning-dates-error"
(input)="
calendarSettings.setPlanningDates(
planningStart.value,
lastPlanningDate.value
)
"
/>
</mat-form-field>
@if (calendarSettings.planningHorizonError(); as error) {
<p id="planning-dates-error" class="validation-error" role="alert">
{{ tr().settings[error] }}
</p>
}
</fieldset>
<mat-form-field appearance="outline">
<mat-label>{{ tr().settings.selectTheme }}</mat-label>
<mat-select
@@ -20,26 +20,3 @@ mat-dialog-content > p {
mat-form-field {
width: 100%;
}
.planning-dates {
min-width: 0;
margin: 0 0 1rem;
padding: 0;
border: 0;
legend {
font-weight: 600;
}
p {
margin: 0.5rem 0 1rem;
color: var(--ui-text-secondary);
}
.validation-error {
color: var(--ui-error-text);
background: var(--ui-error-bg);
padding: 0.75rem;
border-radius: 0.5rem;
}
}
@@ -1,7 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideZonelessChangeDetection } from '@angular/core';
import { UserSettings } from './user-settings';
import { CalendarSettingsService } from '../../services/calendar-settings-service';
import { TranslationService } from '../../services/translation-service';
describe('UserSettings', () => {
@@ -16,7 +15,6 @@ describe('UserSettings', () => {
}).compileComponents();
TestBed.inject(TranslationService).setLanguage('en');
TestBed.inject(CalendarSettingsService).setPlanningDates('2024-02-29', '2027-02-28');
fixture = TestBed.createComponent(UserSettings);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -24,49 +22,8 @@ describe('UserSettings', () => {
afterEach(() => localStorage.clear());
it.each(['', '2024-02-30', 'not-a-date'])(
'reports an invalid date entry without saving it: %s',
(value) => {
const root = fixture.nativeElement as HTMLElement;
const start = root.querySelector<HTMLInputElement>('input[name="planningStart"]')!;
start.value = value;
start.dispatchEvent(new Event('input'));
fixture.detectChanges();
expect(root.querySelector('[role="alert"]')?.textContent).toContain(
'two valid calendar dates',
);
expect(TestBed.inject(CalendarSettingsService).planningHorizon()).toBeNull();
expect(JSON.parse(localStorage.getItem('calendarSettings.planningDates')!)).toEqual({
start: '2024-02-29',
lastPlanningDate: '2027-02-28',
});
},
);
it('edits inclusive dates with an inline validation indication while preserving the saved horizon', () => {
const root = fixture.nativeElement as HTMLElement;
const start = root.querySelector<HTMLInputElement>('input[name="planningStart"]')!;
const last = root.querySelector<HTMLInputElement>('input[name="lastPlanningDate"]')!;
expect(start.value).toBe('2024-02-29');
expect(last.value).toBe('2027-02-28');
expect(root.textContent).toContain('Last planning date (inclusive)');
last.value = '2024-02-28';
last.dispatchEvent(new Event('input'));
fixture.detectChanges();
expect(root.querySelector('[role="alert"]')?.textContent).toContain('on or after');
expect(last.getAttribute('aria-invalid')).toBe('true');
expect(TestBed.inject(CalendarSettingsService).planningHorizon()).toBeNull();
last.value = '2024-02-29';
last.dispatchEvent(new Event('input'));
fixture.detectChanges();
expect(root.querySelector('[role="alert"]')).toBeNull();
expect(TestBed.inject(CalendarSettingsService).planningHorizon()).toEqual({
start: '2024-02-29',
end: '2024-03-01',
});
});
it('should create', () => {
expect(component).toBeTruthy();
expect(fixture.nativeElement.querySelector('input[type="date"]')).toBeNull();
});
});
@@ -9,7 +9,6 @@ import {
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { MatInputModule } from '@angular/material/input';
import { CalendarSettingsService } from '../../services/calendar-settings-service';
import { ThemeService } from '../../services/theme-service';
import { TranslationService } from '../../services/translation-service';
@@ -25,7 +24,6 @@ import { TranslationService } from '../../services/translation-service';
MatFormFieldModule,
MatSelectModule,
MatSlideToggle,
MatInputModule,
],
templateUrl: './user-settings.html',
styleUrl: './user-settings.scss',