From 8aad6e4ceff7fc91ed46fdb2603ef657cae2cee2 Mon Sep 17 00:00:00 2001 From: TO Date: Sat, 1 Nov 2025 20:59:50 +0100 Subject: [PATCH] Enhance calendar settings dialog and optimize date selection logic --- src/app/app.html | 2 +- src/app/app.ts | 2 + .../components/calendar-year/calendar-year.ts | 4 +- .../calendar-settings-dialog.html | 60 +++++++------- .../calendar-settings-dialog.ts | 1 + src/app/services/calendar-service.ts | 81 ++++++++++++------- 6 files changed, 89 insertions(+), 61 deletions(-) diff --git a/src/app/app.html b/src/app/app.html index b594aa2..e60e2bc 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -1,6 +1,6 @@
-

{{ title() }}

+

beach_access {{ title() }}

diff --git a/src/app/app.ts b/src/app/app.ts index c62efae..835574a 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -5,6 +5,7 @@ import { MatDivider } from '@angular/material/divider'; import { UserInput } from './components/user-input/user-input'; import { CalendarYear } from './components/calendar-year/calendar-year'; import { DarkLightModeButton } from './components/dark-light-mode-button/dark-light-mode-button'; +import { MatIcon } from '@angular/material/icon'; @Component({ selector: 'app-root', @@ -15,6 +16,7 @@ import { DarkLightModeButton } from './components/dark-light-mode-button/dark-li CalendarYear, DarkLightModeButton, DarkLightModeButton, + MatIcon, ], templateUrl: './app.html', styleUrl: './app.scss', diff --git a/src/app/components/calendar-year/calendar-year.ts b/src/app/components/calendar-year/calendar-year.ts index 676cbfd..ca8d3ae 100644 --- a/src/app/components/calendar-year/calendar-year.ts +++ b/src/app/components/calendar-year/calendar-year.ts @@ -24,7 +24,9 @@ export class CalendarYear { effect(() => { this.selectedDates = this.calendarService.getSelectedDatesForYear(); const vacationData = this.userInput.vacationNumberSignal(); - this.calendarSettingsService.samediMalin(); + const c = this.calendarSettingsService.samediMalin(); + console.log('Samedi Malin setting:', c); + console.log('Optimizing vacations with data:', vacationData); this.isLoading.set(true); // Simulate server-side calculation with a small delay setTimeout(() => { diff --git a/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.html b/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.html index e9a4646..ee13da6 100644 --- a/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.html +++ b/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.html @@ -1,33 +1,29 @@ -@defer (hydrate on viewport) { -
-
-

Calendar Settings

-

Configure your calendar settings here.

-
-
-
- - Afficher les numéros de semaine - -
-
- - Activer le Samedi Malin - -
-
- +
+
+

Calendar Settings

+

Configure your calendar settings here.

-} @placeholder { -

loading ...

-} +
+
+ + Afficher les numéros de semaine + +
+
+ + Activer le Samedi Malin + +
+
+ +
diff --git a/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.ts b/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.ts index e0e17ef..24255e5 100644 --- a/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.ts +++ b/src/app/components/dialog/calendar-settings-dialog/calendar-settings-dialog.ts @@ -4,6 +4,7 @@ import { MatCheckbox } from '@angular/material/checkbox'; import { MatDialogRef } from '@angular/material/dialog'; import { CalendarSettingsService } from '../../../services/calendar-settings-service'; import { MatSlideToggle } from '@angular/material/slide-toggle'; +import { CdkFixedSizeVirtualScroll } from '@angular/cdk/scrolling'; @Component({ selector: 'app-calendar-settings-dialog', diff --git a/src/app/services/calendar-service.ts b/src/app/services/calendar-service.ts index 37cfd20..361bb83 100644 --- a/src/app/services/calendar-service.ts +++ b/src/app/services/calendar-service.ts @@ -217,6 +217,7 @@ export class SelectedDate implements SelectedDateInterface { isSelected(date: Date): boolean { return ( + this.type !== DayType.TODAY && this.range.start !== null && this.range.end !== null && date >= this.range.start && @@ -262,6 +263,9 @@ export class SelectedDates implements SelectedDateInterface { _datesSelected: SelectedDate[] = []; datesSelected: SelectedDate[] = []; + private minDate: Date | null = null; + private maxDate: Date | null = null; + update(): void { this.datesSelected = this._datesSelected.slice(); this.grouping(); @@ -270,15 +274,38 @@ export class SelectedDates implements SelectedDateInterface { push(selectedDate: SelectedDate) { this._datesSelected.push(selectedDate); this.datesSelected = this._datesSelected.slice(); + this.updateMinMaxDates(); this.grouping(); } + private updateMinMaxDates(): void { + if (this._datesSelected.length === 0) { + this.minDate = null; + this.maxDate = null; + return; + } + + this.minDate = this._datesSelected.reduce((min, date) => { + if (!date.range.start) return min; + if (!min) return date.range.start; + return date.range.start < min ? date.range.start : min; + }, this._datesSelected[0].range.start); + + this.maxDate = this._datesSelected.reduce((max, date) => { + if (!date.range.end) return max; + if (!max) return date.range.end; + return date.range.end > max ? date.range.end : max; + }, this._datesSelected[0].range.end); + } + // make a "mother group" with type = null when two or more groups are adjacent or overlapping; keep the children groups grouping(): void { if (this._datesSelected.length <= 1) { return; } + const oneDayInMs = 24 * 60 * 60 * 1000; + // Sort dates by start date const sorted = [...this._datesSelected].sort((a, b) => { const aStart = a.range.start?.getTime() ?? 0; @@ -289,25 +316,23 @@ export class SelectedDates implements SelectedDateInterface { // Group adjacent or overlapping dates const groups: SelectedDate[][] = []; let currentGroup: SelectedDate[] = [sorted[0]]; + let currentGroupMaxEnd = sorted[0].range.end?.getTime() ?? 0; for (let i = 1; i < sorted.length; i++) { const current = sorted[i]; - const previous = sorted[i - 1]; - - const prevEnd = previous.range.end?.getTime() ?? 0; const currStart = current.range.start?.getTime() ?? 0; + const currEnd = current.range.end?.getTime() ?? 0; - // Check if dates are adjacent (next day) or overlapping - const oneDayInMs = 24 * 60 * 60 * 1000; - const isAdjacent = currStart - prevEnd <= oneDayInMs; - - if (isAdjacent) { + // Check if current date is adjacent or overlapping with the maximum end of the current group + if (currStart - currentGroupMaxEnd <= oneDayInMs) { currentGroup.push(current); + currentGroupMaxEnd = Math.max(currentGroupMaxEnd, currEnd); } else { if (currentGroup.length > 1) { groups.push(currentGroup); } currentGroup = [current]; + currentGroupMaxEnd = Math.max(currentGroupMaxEnd, currEnd); } } @@ -316,26 +341,19 @@ export class SelectedDates implements SelectedDateInterface { groups.push(currentGroup); } - // Create mother groups and rebuild datesSelected + // Create mother groups and add to result const result: SelectedDate[] = [...this._datesSelected]; for (const group of groups) { - const groupStart = group.reduce((min, date) => { - const start = date.range.start?.getTime() ?? 0; - return min === 0 ? start : Math.min(min, start); - }, 0); + const groupStart = Math.min(...group.map((d) => d.range.start?.getTime() ?? Infinity)); + const groupEnd = Math.max(...group.map((d) => d.range.end?.getTime() ?? 0)); - const groupEnd = group.reduce((max, date) => { - const end = date.range.end?.getTime() ?? 0; - return Math.max(max, end); - }, 0); - - const motherGroup = new SelectedDate( - null, - new DateRange(new Date(groupStart), new Date(groupEnd)), + result.push( + new SelectedDate( + null, + new DateRange(new Date(groupStart), new Date(groupEnd)), + ), ); - - result.push(motherGroup); } this.datesSelected = result; @@ -513,6 +531,9 @@ export class SelectedDates implements SelectedDateInterface { } samediMalin(vacationsNumber: VacationsNumber): void { + const now = new Date(); + now.setHours(0, 0, 0, 0); + const samedi_ferie = this.datesSelected.filter( (date) => date.type === DayType.CLOSED_DAY && date.range.start?.getDay() === 6, ); @@ -549,15 +570,21 @@ export class SelectedDates implements SelectedDateInterface { const oneDayAfter = new Date(sundayDate); oneDayAfter.setDate(sundayDate.getDate() + 1); - // Check if we can apply each strategy + // Check if we can apply each strategy (and not in the past) const canApplyTwoBeforeStrategy = - !this.isSelected(twoDaysBefore1) && !this.isSelected(twoDaysBefore2); + twoDaysBefore1 >= now && + !this.isSelected(twoDaysBefore1) && + !this.isSelected(twoDaysBefore2); const canApplyTwoAfterStrategy = - !this.isSelected(twoDaysAfter1) && !this.isSelected(twoDaysAfter2); + twoDaysAfter1 >= now && + !this.isSelected(twoDaysAfter1) && + !this.isSelected(twoDaysAfter2); const canApplyMixedStrategy = - !this.isSelected(oneDayBefore) && !this.isSelected(oneDayAfter); + oneDayBefore >= now && + !this.isSelected(oneDayBefore) && + !this.isSelected(oneDayAfter); // Test all applicable strategies and choose the one with the best heuristic score const strategies: { apply: () => void; heuristic: number }[] = [];