T hoguet/issue16 (#61)
* feat: enhance loading spinner with progress indication and add warning for unplaced vacation days * feat: upgrade Angular dependencies to version 21 and implement new animation features - Updated Angular dependencies in package.json to version 21.x.x. - Added `provideAnimationsAsync` to app configuration for improved animation handling. - Refactored user input component to utilize a dynamic list of vacation days with animations. - Introduced a new `DayInput` component for managing individual vacation days. - Enhanced styling and animations for user input and day input components. - Updated calendar service to support new vacation day structure. - Adjusted tests to accommodate changes in component structure and dependencies. * feat: enhance vacation optimization logic and UI components * Fix signal immutability violations and cleanup unused code (#62) * Initial plan * fix: address PR review comments for signal immutability and cleanup Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> * fix: add --legacy-peer-deps for Angular 21 compatibility (#63) * Initial plan * fix: add --legacy-peer-deps to npm ci for Angular 21 compatibility Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> * Fix unit tests for VacDay[] type migration (#64) * Initial plan * fix: update unit tests to match VacDay[] type structure Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: THoguet <61246905+THoguet@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
3272622abf
commit
d3515f89e5
16 files changed
+15752
-15364
No files matched your search
@@ -3,14 +3,14 @@ import { UserInputService } from '../../services/user-input-service';
|
||||
import { MatCard, MatCardContent } from '@angular/material/card';
|
||||
import { CalendarMonth } from '../calendar-month/calendar-month';
|
||||
import { CalendarService, SelectedDates } from '../../services/calendar-service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
||||
import { CalendarSettingsService } from '../../services/calendar-settings-service';
|
||||
import { DateCacheService } from '../../services/date-cache.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-calendar-year',
|
||||
imports: [MatCard, CalendarMonth, MatCardContent, CommonModule, MatProgressSpinner],
|
||||
imports: [MatCard, CalendarMonth, MatCardContent, MatProgressSpinner],
|
||||
templateUrl: './calendar-year.html',
|
||||
styleUrl: './calendar-year.scss',
|
||||
})
|
||||
@@ -45,9 +45,10 @@ export class CalendarYear {
|
||||
queueMicrotask(() => {
|
||||
void (async () => {
|
||||
if (this.selectedDates) {
|
||||
// Create a copy to avoid mutating the original signal data
|
||||
// Create a deep copy to avoid mutating the original signal data
|
||||
const vacationDaysCopy = vacationData.map((vd) => ({ ...vd }));
|
||||
const remainingDays = await this.selectedDates.optimizeVacations(
|
||||
{ ...vacationData },
|
||||
vacationDaysCopy,
|
||||
this.calendarSettingsService,
|
||||
this.calendarService,
|
||||
(progress: number) => {
|
||||
|
||||
@@ -1,29 +1,40 @@
|
||||
<div class="user-input">
|
||||
<p>Optimisez vos jours de vacances en fonction des jours fériés et des week-ends !</p>
|
||||
<form [formGroup]="userInputForm">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Combien de CP par an ?</mat-label>
|
||||
<input matInput type="number" max="365" min="0" formControlName="CP" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Combien de jours de RTT par an ?</mat-label>
|
||||
<input matInput type="number" max="365" min="0" formControlName="RTT" />
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Autres jours</mat-label>
|
||||
<input matInput type="number" max="365" min="0" formControlName="Others" />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<div class="listOfDays">
|
||||
@for (day of daysOff; track day.id) {
|
||||
<div class="day-input-wrapper" animate.enter="day-enter" animate.leave="day-leave">
|
||||
<app-day-input [day]="day" (dayChange)="updateDay($index, $event)" (deleteEntry)="removeFromList($index)"></app-day-input>
|
||||
</div>
|
||||
}
|
||||
<mat-card class="add-day-card mat-elevation-z2" (click)="addDay()">
|
||||
<button matFab aria-label="Add entry">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
<h3>Ajouter un jour de vacances</h3>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button matFab extended (click)="triggerComputation()">
|
||||
<mat-icon>calculate</mat-icon>
|
||||
Calculer
|
||||
</button>
|
||||
<app-calendar-settings></app-calendar-settings>
|
||||
</div>
|
||||
@if (hasRemainingDays()) {
|
||||
<mat-card class="mat-elevation-z2 warning-card">
|
||||
<mat-card-content class="warning-content">
|
||||
<mat-icon color="warn" aria-hidden="true">warning</mat-icon>
|
||||
<span>
|
||||
{{ remainingDays() }} jour{{ remainingDays() > 1 ? 's' : '' }} de vacances non
|
||||
placé{{ remainingDays() > 1 ? 's' : '' }}. Veuillez les placer manuellement.
|
||||
</span>
|
||||
<mat-icon color="warn">warning</mat-icon>
|
||||
<div class="warning">
|
||||
<span>
|
||||
{{ remainingDays() }} jour{{ remainingDays() > 1 ? 's' : '' }} de vacances
|
||||
non placé{{ remainingDays() > 1 ? 's' : '' }}:
|
||||
</span>
|
||||
@for (day of remainingDaysDetails(); track $index) {
|
||||
<span class="remaining-day-detail">- {{ day }}</span>
|
||||
}
|
||||
<span> Veuillez les placer manuellement. </span>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
<app-calendar-settings></app-calendar-settings>
|
||||
</div>
|
||||
@@ -25,7 +25,79 @@
|
||||
}
|
||||
}
|
||||
|
||||
.listOfDays {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.day-input-wrapper {
|
||||
animation: slide-fade-in 200ms ease-out;
|
||||
|
||||
@starting-style {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
.day-enter {
|
||||
animation: slide-fade-in 200ms ease-out;
|
||||
}
|
||||
|
||||
.day-leave {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
transition:
|
||||
opacity 200ms ease-in,
|
||||
transform 200ms ease-in;
|
||||
}
|
||||
|
||||
@keyframes slide-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.add-day-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.add-day-card h3 {
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.warning {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:host {
|
||||
max-width: 100%;
|
||||
}
|
||||
@@ -1,19 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideZonelessChangeDetection } from '@angular/core';
|
||||
import { UserInput } from './user-input';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
import { UserInputService } from '../../services/user-input-service';
|
||||
|
||||
describe('UserInput', () => {
|
||||
let component: UserInput;
|
||||
let fixture: ComponentFixture<UserInput>;
|
||||
let userInputService: UserInputService;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserInput],
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
providers: [provideZonelessChangeDetection(), provideAnimationsAsync()],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserInput);
|
||||
component = fixture.componentInstance;
|
||||
userInputService = TestBed.inject(UserInputService);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@@ -21,20 +25,22 @@ describe('UserInput', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should initialize form with values from service', () => {
|
||||
expect(component.userInputForm).toBeDefined();
|
||||
expect(component.userInputForm.get('CP')).toBeDefined();
|
||||
expect(component.userInputForm.get('RTT')).toBeDefined();
|
||||
expect(component.userInputForm.get('Others')).toBeDefined();
|
||||
it('should initialize with vacation days from service', () => {
|
||||
const vacationDays = userInputService.vacationNumberSignal();
|
||||
expect(vacationDays).toBeDefined();
|
||||
expect(Array.isArray(vacationDays)).toBe(true);
|
||||
expect(vacationDays.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have valid form controls', () => {
|
||||
const cpControl = component.userInputForm.get('CP');
|
||||
const rttControl = component.userInputForm.get('RTT');
|
||||
const othersControl = component.userInputForm.get('Others');
|
||||
it('should add a new vacation day', () => {
|
||||
const initialLength = userInputService.vacationNumberSignal().length;
|
||||
component.addDay();
|
||||
expect(userInputService.vacationNumberSignal().length).toBe(initialLength + 1);
|
||||
});
|
||||
|
||||
expect(cpControl).toBeTruthy();
|
||||
expect(rttControl).toBeTruthy();
|
||||
expect(othersControl).toBeTruthy();
|
||||
it('should remove a vacation day', () => {
|
||||
const initialLength = userInputService.vacationNumberSignal().length;
|
||||
component.removeFromList(0);
|
||||
expect(userInputService.vacationNumberSignal().length).toBe(initialLength - 1);
|
||||
});
|
||||
});
|
||||
@@ -1,79 +1,78 @@
|
||||
import { Component, OnInit, OnDestroy, effect, computed } from '@angular/core';
|
||||
import { MatFormField, MatLabel } from '@angular/material/form-field';
|
||||
import { Component, computed, inject, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { UserInputService } from '../../services/user-input-service';
|
||||
import { VacationsNumber } from '../../services/calendar-service';
|
||||
import { debounceTime, takeUntil, Subject } from 'rxjs';
|
||||
import { CalendarSettings } from '../calendar-settings/calendar-settings';
|
||||
import { MatCard, MatCardContent } from '@angular/material/card';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { DayInput } from '../userInput/day-input/day-input';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { tooltipTypeMapping, DayType, VacDay } from '../../services/calendar-service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-input',
|
||||
imports: [
|
||||
MatFormField,
|
||||
MatLabel,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
CalendarSettings,
|
||||
MatCard,
|
||||
MatCardContent,
|
||||
MatIcon,
|
||||
MatButtonModule,
|
||||
DayInput,
|
||||
],
|
||||
templateUrl: './user-input.html',
|
||||
styleUrl: './user-input.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UserInput implements OnInit, OnDestroy {
|
||||
constructor(private userInputService: UserInputService) {
|
||||
// Watch for signal changes and update form
|
||||
effect(() => {
|
||||
const vacationData = this.userInputService.vacationNumberSignal();
|
||||
this.userInputForm.patchValue(
|
||||
{
|
||||
CP: vacationData.cp,
|
||||
RTT: vacationData.rtt,
|
||||
Others: vacationData.other,
|
||||
},
|
||||
{ emitEvent: false },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
userInputForm: FormGroup = new FormGroup({
|
||||
CP: new FormControl<number | null>(null),
|
||||
RTT: new FormControl<number | null>(null),
|
||||
Others: new FormControl<number | null>(null),
|
||||
});
|
||||
export class UserInput {
|
||||
private userInputService: UserInputService = inject(UserInputService);
|
||||
|
||||
protected remainingDays = computed(() => {
|
||||
const remaining = this.userInputService.remainingVacationDaysSignal();
|
||||
return remaining.cp + remaining.rtt + remaining.other;
|
||||
return remaining.reduce((acc, day) => acc + day.numberOfDays, 0);
|
||||
});
|
||||
|
||||
protected remainingDaysDetails = computed(() => {
|
||||
const remaining = this.userInputService.remainingVacationDaysSignal();
|
||||
return remaining
|
||||
.filter((day) => day.numberOfDays > 0)
|
||||
.map((day) => {
|
||||
const typeName = tooltipTypeMapping[day.type as unknown as DayType];
|
||||
const expiryDate = day.expiryDate.toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
});
|
||||
return `${day.numberOfDays} ${typeName} (exp. ${expiryDate})`;
|
||||
});
|
||||
});
|
||||
|
||||
protected hasRemainingDays = computed(() => this.remainingDays() > 0);
|
||||
|
||||
ngOnInit() {
|
||||
this.userInputForm.valueChanges
|
||||
.pipe(debounceTime(1500), takeUntil(this.destroy$))
|
||||
.subscribe(() => this.submitForm());
|
||||
protected get daysOff() {
|
||||
return this.userInputService.vacationNumberSignal();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
public addDay() {
|
||||
this.userInputService.vacationNumberSignal.update((days) => [
|
||||
...days,
|
||||
this.userInputService.createVacationDay(),
|
||||
]);
|
||||
}
|
||||
|
||||
private submitForm() {
|
||||
if (this.userInputForm.valid) {
|
||||
const vacationData: VacationsNumber = {
|
||||
cp: this.userInputForm.get('CP')?.value || 0,
|
||||
rtt: this.userInputForm.get('RTT')?.value || 0,
|
||||
other: this.userInputForm.get('Others')?.value || 0,
|
||||
};
|
||||
this.userInputService.vacationNumberSignal.set(vacationData);
|
||||
}
|
||||
public removeFromList(index: number) {
|
||||
this.userInputService.vacationNumberSignal.update((days) =>
|
||||
days.filter((_, i) => i !== index),
|
||||
);
|
||||
}
|
||||
|
||||
public updateDay(index: number, updatedDay: VacDay) {
|
||||
this.userInputService.vacationNumberSignal.update((days) =>
|
||||
days.map((day, i) => (i === index ? updatedDay : day)),
|
||||
);
|
||||
}
|
||||
|
||||
public triggerComputation() {
|
||||
// Trigger signal update by setting a new array reference
|
||||
this.userInputService.vacationNumberSignal.update((days) => [...days]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<!-- <mat-list-item role="listitem"> -->
|
||||
<mat-card class="card">
|
||||
<mat-form-field class="numberInput">
|
||||
<input
|
||||
matInput
|
||||
type="number"
|
||||
min="0"
|
||||
[value]="day().numberOfDays"
|
||||
(input)="onInput($event)"
|
||||
/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="dayTypeInput">
|
||||
<mat-select (valueChange)="onTypeChange($event)" [value]="day().type">
|
||||
<mat-option></mat-option>
|
||||
@for (dayType of selectableDayTypes; track $index) {
|
||||
<mat-option [value]="dayType">{{ tooltipTypeMapping[dayType] }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="dateInput">
|
||||
<mat-label>Choose a date</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="datetime"
|
||||
[matDatepicker]="picker"
|
||||
[value]="day().expiryDate"
|
||||
(dateChange)="addEvent($event)"
|
||||
/>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<button matFab aria-label="Delete entry" (click)="deleteEntry.emit()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</mat-card>
|
||||
<!-- </mat-list-item> -->
|
||||
@@ -0,0 +1,21 @@
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dateInput {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.numberInput {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
::ng-deep .mat-mdc-form-field-subscript-wrapper {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
|
||||
import { DayInput } from './day-input';
|
||||
import { SelectableDayType, VacDay } from '../../../services/calendar-service';
|
||||
|
||||
describe('DayInput', () => {
|
||||
let component: DayInput;
|
||||
let fixture: ComponentFixture<DayInput>;
|
||||
|
||||
const mockVacDay: VacDay = {
|
||||
id: 'test-id',
|
||||
type: SelectableDayType.CP,
|
||||
numberOfDays: 5,
|
||||
expiryDate: new Date('2025-12-31'),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DayInput],
|
||||
providers: [provideAnimationsAsync()],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DayInput);
|
||||
component = fixture.componentInstance;
|
||||
fixture.componentRef.setInput('day', mockVacDay);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Component, input, output, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { SelectableDayType, tooltipTypeMapping, VacDay } from '../../../services/calendar-service';
|
||||
import { MatCard } from '@angular/material/card';
|
||||
import { MatOption, MatSelect } from '@angular/material/select';
|
||||
import { MatInput, MatInputModule, MatLabel } from '@angular/material/input';
|
||||
import {
|
||||
MatDatepicker,
|
||||
MatDatepickerInput,
|
||||
MatDatepickerInputEvent,
|
||||
MatDatepickerToggle,
|
||||
} from '@angular/material/datepicker';
|
||||
import {
|
||||
DateAdapter,
|
||||
MAT_DATE_FORMATS,
|
||||
MAT_DATE_LOCALE,
|
||||
NativeDateAdapter,
|
||||
} from '@angular/material/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
class CustomDateAdapter extends NativeDateAdapter {
|
||||
override format(date: Date, displayFormat: unknown): string {
|
||||
if (displayFormat === 'input') {
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
return `${day}/${month}/${year}`;
|
||||
}
|
||||
return super.format(date, displayFormat as object);
|
||||
}
|
||||
|
||||
override parse(value: string): Date | null {
|
||||
if (!value) return null;
|
||||
const parts = value.split('/');
|
||||
if (parts.length === 3) {
|
||||
const day = parseInt(parts[0], 10);
|
||||
const month = parseInt(parts[1], 10) - 1;
|
||||
const year = parseInt(parts[2], 10);
|
||||
if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const MY_DATE_FORMATS = {
|
||||
parse: {
|
||||
dateInput: 'input',
|
||||
},
|
||||
display: {
|
||||
dateInput: 'input',
|
||||
monthYearLabel: { year: 'numeric', month: 'short' },
|
||||
dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },
|
||||
monthYearA11yLabel: { year: 'numeric', month: 'long' },
|
||||
},
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-day-input',
|
||||
imports: [
|
||||
MatIconModule,
|
||||
MatCard,
|
||||
MatSelect,
|
||||
MatOption,
|
||||
MatInput,
|
||||
MatDatepicker,
|
||||
MatLabel,
|
||||
MatDatepickerToggle,
|
||||
MatDatepickerInput,
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
],
|
||||
templateUrl: './day-input.html',
|
||||
styleUrl: './day-input.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{ provide: DateAdapter, useClass: CustomDateAdapter },
|
||||
{ provide: MAT_DATE_LOCALE, useValue: 'en-GB' },
|
||||
{ provide: MAT_DATE_FORMATS, useValue: MY_DATE_FORMATS },
|
||||
],
|
||||
})
|
||||
export class DayInput {
|
||||
public day = input.required<VacDay>();
|
||||
public dayChange = output<VacDay>();
|
||||
public deleteEntry = output<void>();
|
||||
|
||||
protected readonly selectableDayTypes = Object.values(SelectableDayType);
|
||||
protected readonly tooltipTypeMapping = tooltipTypeMapping;
|
||||
|
||||
onInput(event: Event) {
|
||||
const value = Number((event.target as HTMLInputElement).value);
|
||||
this.dayChange.emit({ ...this.day(), numberOfDays: value });
|
||||
}
|
||||
|
||||
onTypeChange(type: SelectableDayType) {
|
||||
this.dayChange.emit({ ...this.day(), type });
|
||||
}
|
||||
|
||||
addEvent(event: MatDatepickerInputEvent<Date>) {
|
||||
this.dayChange.emit({ ...this.day(), expiryDate: event.value! });
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user