calendar done needs to do heuristic

This commit is contained in:
nessar committed 2025-10-26 03:53:45 +01:00
1 parent 6ed0bdeca7
commit 9ccbe2534a
22 files changed
+16146 -15540

No files matched your search

@@ -0,0 +1,26 @@
<div class="calendar">
<h3>{{ getMonthName() }}</h3>
<div class="row header">
<p class="day">L</p>
<p class="day">M</p>
<p class="day">M</p>
<p class="day">J</p>
<p class="day">V</p>
<p class="day">S</p>
<p class="day">D</p>
</div>
<div class="cols">
@for (week of weeksInAMonth; track $index) {
<div class="row week">
@for (day of week; track $index) {
@if (day === null) {
<p class="day empty"></p>
} @else {
<p [class.start]="selectedDates.isStart(day)" [class.end]="selectedDates.isEnd(day)" [class.selected]="selectedDates.isSelected(day)"
[class]="selectedDates.getClasses(day)" class="day number" [matTooltip]="selectedDates.getTooltip(day)" [matTooltipShowDelay]="500">{{ day.getDate() }}</p>
}
}
</div>
}
</div>
</div>
@@ -0,0 +1,101 @@
.calendar {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
mat-card {
width: 100%;
max-width: 400px;
}
h3 {
margin: 0 0 8px 0;
font-weight: normal;
}
}
.row {
display: flex;
flex-direction: row;
justify-content: flex-start;
width: 100%;
&.header {
font-weight: bold;
margin-bottom: 8px;
}
.day {
text-align: center;
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
margin: 0;
border-radius: 40%;
&.empty {
visibility: hidden;
}
&.number:hover {
background-color: #e0e0e0;
color: black;
cursor: default;
}
}
.selected {
cursor: help !important;
}
.selected:not(.start):not(.end) {
border-radius: 0;
}
>.start:not(.end) {
border-radius: 40% 0 0 40%;
}
>.end:not(.start) {
border-radius: 0 40% 40% 0;
}
}
.today {
border-color: #1976d2;
border-width: 2px;
width: 26px !important;
height: 26px !important;
border-style: dashed !important;
}
.other-month {
color: #9e9e9e;
}
.weekend {
background-color: #ffca39;
}
.closed-day {
border: 2px solid #0ee3ff;
width: 26px !important;
height: 26px !important;
}
.rtt {
background-color: #99ff87;
}
.cp {
background-color: #eca0ff;
}
.other {
background-color: #ff9b9b;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CalendarMonth } from './calendar-month';
describe('CalendarMonth', () => {
let component: CalendarMonth;
let fixture: ComponentFixture<CalendarMonth>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarMonth]
})
.compileComponents();
fixture = TestBed.createComponent(CalendarMonth);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,36 @@
import { Component, Input, OnInit } from '@angular/core';
import { CalendarService, Month, monthNames, SelectedDates } from '../../services/calendar-service';
import { MatTooltip } from '@angular/material/tooltip';
@Component({
selector: 'app-calendar-month',
imports: [MatTooltip],
templateUrl: './calendar-month.html',
styleUrl: './calendar-month.scss',
})
export class CalendarMonth implements OnInit {
@Input({ required: true })
monthIndex!: number;
@Input({ required: true })
year!: number;
currentMonth: Date = new Date();
weeksInAMonth: Month = [];
@Input({ required: true })
selectedDates!: SelectedDates;
getMonthName(): string {
return monthNames[this.monthIndex];
}
constructor(private calendarService: CalendarService) { }
ngOnInit(): void {
this.weeksInAMonth = this.calendarService.weeksInMonth(this.monthIndex, this.year);
}
}
@@ -0,0 +1,9 @@
<div class="calendar-year">
@for (month of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; track $index) {
<mat-card>
<mat-card-content>
<app-calendar-month [monthIndex]="month" [year]="year" [selectedDates]="selectedDates"></app-calendar-month>
</mat-card-content>
</mat-card>
}
</div>
@@ -0,0 +1,8 @@
.calendar-year {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 16px;
max-width: 60vw;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CalendarYear } from './calendar-year';
describe('CalendarYear', () => {
let component: CalendarYear;
let fixture: ComponentFixture<CalendarYear>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CalendarYear]
})
.compileComponents();
fixture = TestBed.createComponent(CalendarYear);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { UserInputService } from '../../services/user-input-service';
import { DateRange } from '@angular/material/datepicker';
import { MatCard, MatCardContent } from '@angular/material/card';
import { CalendarMonth } from "../calendar-month/calendar-month";
import { CalendarService, SelectedDates } from '../../services/calendar-service';
@Component({
selector: 'app-calendar-year',
imports: [MatCard, CalendarMonth, MatCardContent],
templateUrl: './calendar-year.html',
styleUrl: './calendar-year.scss',
})
export class CalendarYear implements OnInit {
constructor(private userInput: UserInputService, private calendarService: CalendarService) {
}
protected selectedDates: SelectedDates = new SelectedDates();
protected year: number = 2026;
ngOnInit(): void {
this.selectedDates = this.calendarService.getSelectedDatesForYear(this.year);
}
}
@@ -0,0 +1,18 @@
<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" (ngSubmit)="submitForm()" class="form">
<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>
<button matButton="elevated" type="submit" [disabled]="userInputForm.invalid">Valider</button>
</form>
</div>
@@ -0,0 +1,22 @@
.user-input {
display: flex;
flex-direction: column;
margin: 1rem;
label {
font-weight: bold;
margin-bottom: 0.5rem;
}
.error-message {
color: #ff0000;
font-size: 0.875rem;
margin-top: 0.25rem;
}
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserInput } from './user-input';
describe('UserInput', () => {
let component: UserInput;
let fixture: ComponentFixture<UserInput>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserInput]
})
.compileComponents();
fixture = TestBed.createComponent(UserInput);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,27 @@
import { Component, Input, input, signal, WritableSignal } from '@angular/core';
import { MatFormField, MatLabel } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatCalendar } from "@angular/material/datepicker";
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { UserInputService } from '../../services/user-input-service';
import { MatAnchor, MatButton } from "@angular/material/button";
@Component({
selector: 'app-user-input',
imports: [MatFormField, MatLabel, MatInputModule, ReactiveFormsModule, MatAnchor, MatButton],
templateUrl: './user-input.html',
styleUrl: './user-input.scss',
})
export class UserInput {
constructor(private userInput: UserInputService) { }
userInputForm: FormGroup = new FormGroup({
CP: new FormControl<number | null>(null, Validators.required),
RTT: new FormControl<number | null>(null, Validators.required),
Others: new FormControl<number | null>(null, Validators.required),
});
submitForm() {
console.log("prout");
}
}