V1 done still need to work on SSR
This commit is contained in:
1 parent
87558d7da5
commit
7e40ce0f6d
9 files changed
+237
-45
No files matched your search
@@ -32,8 +32,10 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 26px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: 2px;
|
||||||
border-radius: 40%;
|
border-radius: 40%;
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
@@ -70,7 +72,7 @@
|
|||||||
border-color: #1976d2;
|
border-color: #1976d2;
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
width: 26px !important;
|
width: 26px !important;
|
||||||
height: 26px !important;
|
height: 22px !important;
|
||||||
border-style: dashed !important;
|
border-style: dashed !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +87,7 @@
|
|||||||
.closed-day {
|
.closed-day {
|
||||||
border: 2px solid #0ee3ff;
|
border: 2px solid #0ee3ff;
|
||||||
width: 26px !important;
|
width: 26px !important;
|
||||||
height: 26px !important;
|
height: 22px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rtt {
|
.rtt {
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
<div class="calendar-year">
|
<div class="calendar-year">
|
||||||
|
@if (isLoading()) {
|
||||||
|
<div class="loading-overlay">
|
||||||
|
<div class="loading-spinner">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<p>Calcul en cours...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@for (month of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; track $index) {
|
@for (month of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; track $index) {
|
||||||
<mat-card>
|
<mat-card>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
|
|||||||
@@ -5,4 +5,52 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
max-width: 60vw;
|
max-width: 60vw;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 4px solid #f3f3f3;
|
||||||
|
border-top: 4px solid #3498db;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,32 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, effect, signal } from '@angular/core';
|
||||||
import { UserInputService } from '../../services/user-input-service';
|
import { UserInputService } from '../../services/user-input-service';
|
||||||
import { DateRange } from '@angular/material/datepicker';
|
|
||||||
import { MatCard, MatCardContent } from '@angular/material/card';
|
import { MatCard, MatCardContent } from '@angular/material/card';
|
||||||
import { CalendarMonth } from "../calendar-month/calendar-month";
|
import { CalendarMonth } from "../calendar-month/calendar-month";
|
||||||
import { CalendarService, SelectedDates, VacationsNumber } from '../../services/calendar-service';
|
import { CalendarService, SelectedDates, VacationsNumber } from '../../services/calendar-service';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-calendar-year',
|
selector: 'app-calendar-year',
|
||||||
imports: [MatCard, CalendarMonth, MatCardContent],
|
imports: [MatCard, CalendarMonth, MatCardContent, CommonModule],
|
||||||
templateUrl: './calendar-year.html',
|
templateUrl: './calendar-year.html',
|
||||||
styleUrl: './calendar-year.scss',
|
styleUrl: './calendar-year.scss',
|
||||||
})
|
})
|
||||||
export class CalendarYear implements OnInit {
|
export class CalendarYear implements OnInit {
|
||||||
|
protected isLoading = signal(true);
|
||||||
|
|
||||||
constructor(private userInput: UserInputService, private calendarService: CalendarService) {
|
constructor(private userInput: UserInputService, private calendarService: CalendarService) {
|
||||||
|
effect(() => {
|
||||||
|
this.selectedDates = this.calendarService.getSelectedDatesForYear(this.year);
|
||||||
|
const vacationData = this.userInput.vacationNumberSignal();
|
||||||
|
this.isLoading.set(true);
|
||||||
|
// Simulate server-side calculation with a small delay
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.selectedDates) {
|
||||||
|
this.selectedDates.optimizeVacations(vacationData);
|
||||||
|
}
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected selectedDates: SelectedDates = new SelectedDates();
|
protected selectedDates: SelectedDates = new SelectedDates();
|
||||||
@@ -21,9 +35,6 @@ export class CalendarYear implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.selectedDates = this.calendarService.getSelectedDatesForYear(this.year);
|
this.selectedDates = this.calendarService.getSelectedDatesForYear(this.year);
|
||||||
const vacationsNumber: VacationsNumber = {
|
this.isLoading.set(false);
|
||||||
cp: 10, rtt: 5, other: 0
|
|
||||||
};
|
|
||||||
this.selectedDates.optimizeVacations(vacationsNumber);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="user-input">
|
<div class="user-input">
|
||||||
<p>Optimisez vos jours de vacances en fonction des jours fériés et des week-ends !</p>
|
<p>Optimisez vos jours de vacances en fonction des jours fériés et des week-ends !</p>
|
||||||
<form [formGroup]="userInputForm" (ngSubmit)="submitForm()" class="form">
|
<form [formGroup]="userInputForm">
|
||||||
<mat-form-field appearance="fill">
|
<mat-form-field appearance="fill">
|
||||||
<mat-label>Combien de CP par an ?</mat-label>
|
<mat-label>Combien de CP par an ?</mat-label>
|
||||||
<input matInput type="number" max="365" min="0" formControlName="CP" />
|
<input matInput type="number" max="365" min="0" formControlName="CP" />
|
||||||
@@ -13,6 +13,5 @@
|
|||||||
<mat-label>Autres jours</mat-label>
|
<mat-label>Autres jours</mat-label>
|
||||||
<input matInput type="number" max="365" min="0" formControlName="Others" />
|
<input matInput type="number" max="365" min="0" formControlName="Others" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button matButton="elevated" type="submit" [disabled]="userInputForm.invalid">Valider</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|||||||
@@ -1,27 +1,60 @@
|
|||||||
import { Component, Input, input, signal, WritableSignal } from '@angular/core';
|
import { Component, OnInit, OnDestroy, effect } from '@angular/core';
|
||||||
import { MatFormField, MatLabel } from '@angular/material/form-field';
|
import { MatFormField, MatLabel } from '@angular/material/form-field';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatCalendar } from "@angular/material/datepicker";
|
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { UserInputService } from '../../services/user-input-service';
|
import { UserInputService } from '../../services/user-input-service';
|
||||||
import { MatAnchor, MatButton } from "@angular/material/button";
|
import { VacationsNumber } from '../../services/calendar-service';
|
||||||
|
import { debounceTime, takeUntil, Subject } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-input',
|
selector: 'app-user-input',
|
||||||
imports: [MatFormField, MatLabel, MatInputModule, ReactiveFormsModule, MatAnchor, MatButton],
|
imports: [MatFormField, MatLabel, MatInputModule, ReactiveFormsModule],
|
||||||
templateUrl: './user-input.html',
|
templateUrl: './user-input.html',
|
||||||
styleUrl: './user-input.scss',
|
styleUrl: './user-input.scss',
|
||||||
})
|
})
|
||||||
export class UserInput {
|
export class UserInput implements OnInit, OnDestroy {
|
||||||
constructor(private userInput: UserInputService) { }
|
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({
|
userInputForm: FormGroup = new FormGroup({
|
||||||
CP: new FormControl<number | null>(null, Validators.required),
|
CP: new FormControl<number | null>(null),
|
||||||
RTT: new FormControl<number | null>(null, Validators.required),
|
RTT: new FormControl<number | null>(null),
|
||||||
Others: new FormControl<number | null>(null, Validators.required),
|
Others: new FormControl<number | null>(null),
|
||||||
});
|
});
|
||||||
|
|
||||||
submitForm() {
|
ngOnInit() {
|
||||||
console.log("prout");
|
this.userInputForm.valueChanges
|
||||||
|
.pipe(
|
||||||
|
debounceTime(2500),
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
)
|
||||||
|
.subscribe(() => this.submitForm());
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
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.setVacationNumber(vacationData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,25 +339,100 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
computeHeuristics(): number {
|
computeHeuristics(): number {
|
||||||
// Logic to compute heuristics on selected dates
|
if (this.datesSelected.length === 0) {
|
||||||
// use fibonacci sequence as heuristic points
|
return 0;
|
||||||
|
|
||||||
const fibonacci = [0, 1];
|
|
||||||
for (let i = 2; i <= 10; i++) {
|
|
||||||
fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const heuristicPoints = this.datesSelected.reduce((acc, dateSelected) => {
|
let totalScore = 0;
|
||||||
const days = dateSelected.getLengthInDays();
|
|
||||||
return acc + (fibonacci[days] || 0);
|
// Sort all dates by start date to find next closest dates
|
||||||
}, 0);
|
const sorted = [...this.datesSelected].filter(date => date.type !== null).sort((a, b) => {
|
||||||
console.log(`Heuristic Points: ${heuristicPoints}`);
|
const aStart = a.range.start?.getTime() ?? 0;
|
||||||
return heuristicPoints;
|
const bStart = b.range.start?.getTime() ?? 0;
|
||||||
|
return aStart - bStart;
|
||||||
|
});
|
||||||
|
|
||||||
|
// PRIMARY: Optimize adjacency - reduce "little holes" between vacation periods
|
||||||
|
for (let i = 0; i < sorted.length - 1; i++) {
|
||||||
|
const current = sorted[i];
|
||||||
|
const next = sorted[i + 1];
|
||||||
|
|
||||||
|
// Get dates as UTC strings to remove timezone issues
|
||||||
|
const currentEndDate = new Date(current.range.end ?? new Date());
|
||||||
|
const nextStartDate = new Date(next.range.start ?? new Date());
|
||||||
|
|
||||||
|
// Convert to date-only strings (YYYY-MM-DD) to compare calendar days
|
||||||
|
const currentEndString = currentEndDate.toISOString().split('T')[0];
|
||||||
|
const nextStartString = nextStartDate.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
// Parse back to dates at UTC midnight
|
||||||
|
const currentEndDateNormalized = new Date(currentEndString + 'T00:00:00Z');
|
||||||
|
const nextStartDateNormalized = new Date(nextStartString + 'T00:00:00Z');
|
||||||
|
|
||||||
|
const currentEndTime = currentEndDateNormalized.getTime();
|
||||||
|
const nextStartTime = nextStartDateNormalized.getTime();
|
||||||
|
|
||||||
|
const oneDayInMs = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
// Check if adjacent (next day) - this is the best case!
|
||||||
|
if (nextStartTime - currentEndTime === oneDayInMs) {
|
||||||
|
totalScore += 10; // Big bonus for adjacency
|
||||||
|
} else if (nextStartTime > currentEndTime) {
|
||||||
|
// Penalty based on days between the two dates
|
||||||
|
const daysBetween = Math.floor((nextStartTime - currentEndTime) / oneDayInMs) - 1;
|
||||||
|
// Ensure daysBetween is non-negative before taking sqrt
|
||||||
|
totalScore -= (Math.sqrt(Math.max(0, daysBetween)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECONDARY: Penalize uneven monthly distribution to encourage spread
|
||||||
|
const monthCounts = new Map<number, number>(); // Use year*12+month as key to avoid strings
|
||||||
|
for (const selectedDate of this.datesSelected) {
|
||||||
|
if (!selectedDate.range.start || !selectedDate.range.end) continue;
|
||||||
|
|
||||||
|
// Count days in this selected date range
|
||||||
|
const daysInRange = selectedDate.getLengthInDays();
|
||||||
|
|
||||||
|
// For each day in the range, add it to the appropriate month
|
||||||
|
let currentDate = new Date(selectedDate.range.start);
|
||||||
|
for (let i = 0; i < daysInRange; i++) {
|
||||||
|
const monthKey = currentDate.getFullYear() * 12 + currentDate.getMonth();
|
||||||
|
monthCounts.set(monthKey, (monthCounts.get(monthKey) ?? 0) + 1);
|
||||||
|
currentDate.setDate(currentDate.getDate() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply small penalty for uneven distribution (secondary priority)
|
||||||
|
for (const count of monthCounts.values()) {
|
||||||
|
totalScore -= count; // Smaller penalty than gap filling
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
optimizeVacations(vacationsNumber: VacationsNumber): void {
|
optimizeVacations(vacationsNumber: VacationsNumber): void {
|
||||||
this.samediMalin(vacationsNumber);
|
this.samediMalin(vacationsNumber);
|
||||||
this.lookForVacationRec();
|
while (vacationsNumber.cp > 0) {
|
||||||
|
const result = this.lookForVacationRec(DayType.CP);
|
||||||
|
if (result.heuristic === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vacationsNumber.cp -= 1;
|
||||||
|
}
|
||||||
|
while (vacationsNumber.rtt > 0) {
|
||||||
|
const result = this.lookForVacationRec(DayType.RTT);
|
||||||
|
if (result.heuristic === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vacationsNumber.rtt -= 1;
|
||||||
|
}
|
||||||
|
while (vacationsNumber.other > 0) {
|
||||||
|
const result = this.lookForVacationRec(DayType.OTHER);
|
||||||
|
if (result.heuristic === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vacationsNumber.other -= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
samediMalin(vacationsNumber: VacationsNumber): void {
|
samediMalin(vacationsNumber: VacationsNumber): void {
|
||||||
@@ -418,7 +493,7 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
apply: () => {
|
apply: () => {
|
||||||
this._datesSelected = [...tempDatesBackup];
|
this._datesSelected = [...tempDatesBackup];
|
||||||
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(twoDaysBefore1, twoDaysBefore2)));
|
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(twoDaysBefore1, twoDaysBefore2)));
|
||||||
vacationsNumber.cp -= 1;
|
vacationsNumber.cp -= 2;
|
||||||
},
|
},
|
||||||
heuristic: this.computeHeuristics()
|
heuristic: this.computeHeuristics()
|
||||||
});
|
});
|
||||||
@@ -433,7 +508,7 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
apply: () => {
|
apply: () => {
|
||||||
this._datesSelected = [...tempDatesBackup];
|
this._datesSelected = [...tempDatesBackup];
|
||||||
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(twoDaysAfter1, twoDaysAfter2)));
|
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(twoDaysAfter1, twoDaysAfter2)));
|
||||||
vacationsNumber.cp -= 1;
|
vacationsNumber.cp -= 2;
|
||||||
},
|
},
|
||||||
heuristic: this.computeHeuristics()
|
heuristic: this.computeHeuristics()
|
||||||
});
|
});
|
||||||
@@ -450,7 +525,7 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
this._datesSelected = [...tempDatesBackup];
|
this._datesSelected = [...tempDatesBackup];
|
||||||
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(oneDayBefore, oneDayBefore)));
|
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(oneDayBefore, oneDayBefore)));
|
||||||
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(oneDayAfter, oneDayAfter)));
|
this.push(new SelectedDate(DayType.CP, new DateRange<Date>(oneDayAfter, oneDayAfter)));
|
||||||
vacationsNumber.cp -= 1;
|
vacationsNumber.cp -= 2;
|
||||||
},
|
},
|
||||||
heuristic: this.computeHeuristics()
|
heuristic: this.computeHeuristics()
|
||||||
});
|
});
|
||||||
@@ -472,7 +547,7 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
lookForVacationRec(vacType: DayType): { apply: () => void; heuristic: number } {
|
lookForVacationRec(vacType: DayType): { apply: () => void; heuristic: number } {
|
||||||
const daysOfYear = CalendarService.monthsInYear(2026);
|
const daysOfYear = CalendarService.monthsInYear(2026);
|
||||||
const tempDatesBackup = [...this._datesSelected];
|
const tempDatesBackup = [...this._datesSelected];
|
||||||
let strategies: { apply: () => void; heuristic: number }[] = [];
|
let strategies: { apply: () => void; heuristic: number; value: Date }[] = [];
|
||||||
for (const month of daysOfYear) {
|
for (const month of daysOfYear) {
|
||||||
for (const week of month) {
|
for (const week of month) {
|
||||||
for (const day of week) {
|
for (const day of week) {
|
||||||
@@ -483,13 +558,14 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
// mark these days as vacation
|
// mark these days as vacation
|
||||||
const startDay = new Date(day);
|
const startDay = new Date(day);
|
||||||
const endDay = new Date(day);
|
const endDay = new Date(day);
|
||||||
this.push(new SelectedDate(vacType, new DateRange<Date>(startDay, endDay)));
|
this.datesSelected.push(new SelectedDate(vacType, new DateRange<Date>(startDay, endDay)));
|
||||||
strategies.push({
|
strategies.push({
|
||||||
apply: () => {
|
apply: () => {
|
||||||
this._datesSelected = [...tempDatesBackup];
|
this._datesSelected = [...tempDatesBackup];
|
||||||
this.push(new SelectedDate(vacType, new DateRange<Date>(startDay, endDay)));
|
this.push(new SelectedDate(vacType, new DateRange<Date>(startDay, endDay)));
|
||||||
},
|
},
|
||||||
heuristic: this.computeHeuristics()
|
heuristic: this.computeHeuristics(),
|
||||||
|
value: startDay
|
||||||
});
|
});
|
||||||
this._datesSelected = [...tempDatesBackup];
|
this._datesSelected = [...tempDatesBackup];
|
||||||
this.datesSelected = this._datesSelected.slice();
|
this.datesSelected = this._datesSelected.slice();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable, signal } from '@angular/core';
|
import { Injectable, signal } from '@angular/core';
|
||||||
import Holidays, { HolidaysTypes } from 'date-holidays';
|
import Holidays, { HolidaysTypes } from 'date-holidays';
|
||||||
|
import { VacationsNumber } from './calendar-service';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -7,7 +8,21 @@ import Holidays, { HolidaysTypes } from 'date-holidays';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class UserInputService {
|
export class UserInputService {
|
||||||
calculateVacationOptimization(nbCP: number, nbRTT: number, nbOther: number, year: number, closedDays: Date[]) {
|
private getRandomNumber(): number {
|
||||||
// Logic to calculate vacation optimization based on user input
|
return Math.floor(Math.random() * 9) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vacationNumberSignal = signal<VacationsNumber>({
|
||||||
|
cp: this.getRandomNumber(),
|
||||||
|
rtt: this.getRandomNumber(),
|
||||||
|
other: this.getRandomNumber()
|
||||||
|
});
|
||||||
|
|
||||||
|
get vacationNumber() {
|
||||||
|
return this.vacationNumberSignal();
|
||||||
|
}
|
||||||
|
|
||||||
|
setVacationNumber(vacationsNumber: VacationsNumber) {
|
||||||
|
this.vacationNumberSignal.set(vacationsNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user