Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b91f50a25 | ||
|
|
501bbc1e5a | ||
|
|
dea632f67d | ||
|
|
5cd45b86c8 | ||
|
|
a9c7e51d36 | ||
|
|
e10f2ff4e5 | ||
|
|
5ab29ef770 |
No files matched your search
@@ -24,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v4
|
uses: github/codeql-action/init@v4
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
name: Dependabot Auto-merge
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto-merge:
|
|
||||||
name: Auto-merge Dependabot minor and patch updates
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.actor == 'dependabot[bot]'
|
|
||||||
steps:
|
|
||||||
- name: Fetch Dependabot metadata
|
|
||||||
id: metadata
|
|
||||||
uses: dependabot/fetch-metadata@v2.5.0
|
|
||||||
|
|
||||||
- name: Auto-merge minor and patch updates
|
|
||||||
if: |
|
|
||||||
steps.metadata.outputs.update-type == 'version-update:semver-minor' ||
|
|
||||||
steps.metadata.outputs.update-type == 'version-update:semver-patch'
|
|
||||||
run: gh pr merge --auto --squash "$PR_URL"
|
|
||||||
env:
|
|
||||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -16,13 +16,13 @@ jobs:
|
|||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
- name: Log in to GitHub Container Registry
|
||||||
uses: docker/login-action@v4
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
- name: Run Trivy vulnerability scanner
|
- name: Run Trivy vulnerability scanner
|
||||||
uses: aquasecurity/trivy-action@0.34.2
|
uses: aquasecurity/trivy-action@0.33.1
|
||||||
with:
|
with:
|
||||||
image-ref: ghcr.io/thoguet/vac-optimizer@${{ steps.build.outputs.digest }}
|
image-ref: ghcr.io/thoguet/vac-optimizer@${{ steps.build.outputs.digest }}
|
||||||
format: 'sarif'
|
format: 'sarif'
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
name: Lint Check
|
name: Lint Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
name: TypeScript Type Check
|
name: TypeScript Type Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
name: Unit Tests
|
name: Unit Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -76,7 +76,7 @@ jobs:
|
|||||||
name: Build Application
|
name: Build Application
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
echo "Build completed successfully"
|
echo "Build completed successfully"
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: build-artifacts
|
name: build-artifacts
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|||||||
Generated
+2623
-2343
File diff suppressed because it is too large.
Load diff
+32
-32
@@ -27,45 +27,45 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "21.2.0",
|
"@angular/animations": "^21.0.1",
|
||||||
"@angular/cdk": "^21.2.0",
|
"@angular/cdk": "^21.0.1",
|
||||||
"@angular/common": "21.2.0",
|
"@angular/common": "^21.0.1",
|
||||||
"@angular/compiler": "21.2.0",
|
"@angular/compiler": "^21.0.1",
|
||||||
"@angular/core": "21.2.0",
|
"@angular/core": "^21.0.1",
|
||||||
"@angular/forms": "21.2.0",
|
"@angular/forms": "^21.0.1",
|
||||||
"@angular/material": "^21.2.0",
|
"@angular/material": "^21.0.1",
|
||||||
"@angular/platform-browser": "21.2.0",
|
"@angular/platform-browser": "^21.0.1",
|
||||||
"@angular/platform-server": "21.2.0",
|
"@angular/platform-server": "^21.0.1",
|
||||||
"@angular/router": "21.2.0",
|
"@angular/router": "^21.0.1",
|
||||||
"@angular/ssr": "21.2.0",
|
"@angular/ssr": "^21.0.1",
|
||||||
"date-holidays": "^3.26.8",
|
"date-holidays": "^3.26.2",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"express": "^5.2.1",
|
"express": "^5.1.0",
|
||||||
"rxjs": "~7.8.2",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/build": "21.2.0",
|
"@angular/build": "^21.0.1",
|
||||||
"@angular/cli": "21.2.0",
|
"@angular/cli": "^21.0.1",
|
||||||
"@angular/compiler-cli": "21.2.0",
|
"@angular/compiler-cli": "^21.0.1",
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^9.39.1",
|
||||||
"@types/express": "^5.0.6",
|
"@types/express": "^5.0.1",
|
||||||
"@types/jasmine": "~6.0.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^25.3.3",
|
"@types/node": "^24.10.0",
|
||||||
"eslint": "^10.0.2",
|
"eslint": "^9.39.1",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-prettier": "^5.5.5",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"jasmine-core": "~6.1.0",
|
"jasmine-core": "~5.12.1",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
"jest-preset-angular": "^16.1.1",
|
"jest-preset-angular": "^15.0.3",
|
||||||
"karma": "~6.4.4",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
"karma-coverage": "~2.2.1",
|
"karma-coverage": "~2.2.0",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.2.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.2",
|
||||||
"typescript-eslint": "^8.56.1"
|
"typescript-eslint": "^8.46.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, effect, signal, inject, untracked } from '@angular/core';
|
import { Component, effect, signal, inject } from '@angular/core';
|
||||||
import { UserInputService } from '../../services/user-input-service';
|
import { UserInputService } from '../../services/user-input-service';
|
||||||
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';
|
||||||
@@ -34,11 +34,9 @@ export class CalendarYear {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
this.userInput.computeTriggerSignal();
|
|
||||||
|
|
||||||
this.selectedDates = this.calendarService.getSelectedDatesForYear();
|
this.selectedDates = this.calendarService.getSelectedDatesForYear();
|
||||||
const vacationData = untracked(() => this.userInput.vacationNumberSignal());
|
const vacationData = this.userInput.vacationNumberSignal();
|
||||||
const c = untracked(() => this.calendarSettingsService.get('samediMalin')());
|
const c = this.calendarSettingsService.get('samediMalin')();
|
||||||
console.log('Samedi Malin setting:', c);
|
console.log('Samedi Malin setting:', c);
|
||||||
console.log('Optimizing vacations with data:', vacationData);
|
console.log('Optimizing vacations with data:', vacationData);
|
||||||
this.isLoading.set(true);
|
this.isLoading.set(true);
|
||||||
|
|||||||
@@ -3,11 +3,7 @@
|
|||||||
<div class="listOfDays">
|
<div class="listOfDays">
|
||||||
@for (day of daysOff; track day.id) {
|
@for (day of daysOff; track day.id) {
|
||||||
<div class="day-input-wrapper" animate.enter="day-enter" animate.leave="day-leave">
|
<div class="day-input-wrapper" animate.enter="day-enter" animate.leave="day-leave">
|
||||||
<app-day-input
|
<app-day-input [day]="day" (dayChange)="updateDay($index, $event)" (deleteEntry)="removeFromList($index)"></app-day-input>
|
||||||
[day]="day"
|
|
||||||
(dayChange)="updateDay($index, $event)"
|
|
||||||
(deleteEntry)="removeFromList($index)"
|
|
||||||
></app-day-input>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<mat-card class="add-day-card mat-elevation-z2" (click)="addDay()">
|
<mat-card class="add-day-card mat-elevation-z2" (click)="addDay()">
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { provideZonelessChangeDetection } from '@angular/core';
|
import { provideZonelessChangeDetection } from '@angular/core';
|
||||||
import { UserInput } from './user-input';
|
import { UserInput } from './user-input';
|
||||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||||
import { UserInputService } from '../../services/user-input-service';
|
|
||||||
|
|
||||||
describe('UserInput', () => {
|
describe('UserInput', () => {
|
||||||
let component: UserInput;
|
let component: UserInput;
|
||||||
let fixture: ComponentFixture<UserInput>;
|
let fixture: ComponentFixture<UserInput>;
|
||||||
let userInputService: UserInputService;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
@@ -17,7 +15,6 @@ describe('UserInput', () => {
|
|||||||
|
|
||||||
fixture = TestBed.createComponent(UserInput);
|
fixture = TestBed.createComponent(UserInput);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
userInputService = TestBed.inject(UserInputService);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -25,22 +22,20 @@ describe('UserInput', () => {
|
|||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize with vacation days from service', () => {
|
it('should initialize form with values from service', () => {
|
||||||
const vacationDays = userInputService.vacationNumberSignal();
|
expect(component.userInputForm).toBeDefined();
|
||||||
expect(vacationDays).toBeDefined();
|
expect(component.userInputForm.get('CP')).toBeDefined();
|
||||||
expect(Array.isArray(vacationDays)).toBe(true);
|
expect(component.userInputForm.get('RTT')).toBeDefined();
|
||||||
expect(vacationDays.length).toBeGreaterThan(0);
|
expect(component.userInputForm.get('Others')).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a new vacation day', () => {
|
it('should have valid form controls', () => {
|
||||||
const initialLength = userInputService.vacationNumberSignal().length;
|
const cpControl = component.userInputForm.get('CP');
|
||||||
component.addDay();
|
const rttControl = component.userInputForm.get('RTT');
|
||||||
expect(userInputService.vacationNumberSignal().length).toBe(initialLength + 1);
|
const othersControl = component.userInputForm.get('Others');
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove a vacation day', () => {
|
expect(cpControl).toBeTruthy();
|
||||||
const initialLength = userInputService.vacationNumberSignal().length;
|
expect(rttControl).toBeTruthy();
|
||||||
component.removeFromList(0);
|
expect(othersControl).toBeTruthy();
|
||||||
expect(userInputService.vacationNumberSignal().length).toBe(initialLength - 1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -72,6 +72,7 @@ export class UserInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public triggerComputation() {
|
public triggerComputation() {
|
||||||
this.userInputService.requestComputation();
|
// Trigger signal update by setting a new array reference
|
||||||
|
this.userInputService.vacationNumberSignal.update((days) => [...days]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dateInput {
|
.dateInput {
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ export class SelectedDate implements SelectedDateInterface {
|
|||||||
getClasses(date: Date): string {
|
getClasses(date: Date): string {
|
||||||
let returnType: DayType | string = '';
|
let returnType: DayType | string = '';
|
||||||
if (this.type && date >= this.range.start! && date <= this.range.end!) {
|
if (this.type && date >= this.range.start! && date <= this.range.end!) {
|
||||||
returnType += this.type + ' ';
|
returnType = returnType += this.type + ' ';
|
||||||
}
|
}
|
||||||
return returnType;
|
return returnType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { provideZonelessChangeDetection } from '@angular/core';
|
import { provideZonelessChangeDetection } from '@angular/core';
|
||||||
import { UserInputService } from './user-input-service';
|
import { UserInputService } from './user-input-service';
|
||||||
import { SelectableDayType, VacDay } from './calendar-service';
|
|
||||||
|
|
||||||
describe('UserInputService', () => {
|
describe('UserInput', () => {
|
||||||
let service: UserInputService;
|
let service: UserInputService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -17,41 +16,19 @@ describe('UserInputService', () => {
|
|||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize with vacation days array', () => {
|
it('should initialize with random vacation numbers', () => {
|
||||||
const vacations = service.vacationNumberSignal();
|
const vacations = service.vacationNumberSignal();
|
||||||
expect(Array.isArray(vacations)).toBe(true);
|
expect(vacations.cp).toBeGreaterThanOrEqual(1);
|
||||||
expect(vacations.length).toBeGreaterThan(0);
|
expect(vacations.cp).toBeLessThanOrEqual(9);
|
||||||
});
|
expect(vacations.rtt).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(vacations.rtt).toBeLessThanOrEqual(9);
|
||||||
it('should have vacation days with valid properties', () => {
|
expect(vacations.other).toBeGreaterThanOrEqual(1);
|
||||||
const vacations = service.vacationNumberSignal();
|
expect(vacations.other).toBeLessThanOrEqual(9);
|
||||||
vacations.forEach((day) => {
|
|
||||||
expect(day.id).toBeDefined();
|
|
||||||
expect(day.type).toBeDefined();
|
|
||||||
expect(day.numberOfDays).toBeGreaterThanOrEqual(1);
|
|
||||||
expect(day.numberOfDays).toBeLessThanOrEqual(9);
|
|
||||||
expect(day.expiryDate).toBeInstanceOf(Date);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update vacation number signal', () => {
|
it('should update vacation number signal', () => {
|
||||||
const newVacations: VacDay[] = [
|
const newVacations = { cp: 5, rtt: 3, other: 2 };
|
||||||
{
|
|
||||||
id: 'test-id',
|
|
||||||
type: SelectableDayType.CP,
|
|
||||||
numberOfDays: 5,
|
|
||||||
expiryDate: new Date(),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
service.vacationNumberSignal.set(newVacations);
|
service.vacationNumberSignal.set(newVacations);
|
||||||
expect(service.vacationNumberSignal()).toEqual(newVacations);
|
expect(service.vacationNumberSignal()).toEqual(newVacations);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a new vacation day', () => {
|
|
||||||
const newDay = service.createVacationDay();
|
|
||||||
expect(newDay.id).toBeDefined();
|
|
||||||
expect(newDay.type).toBe(SelectableDayType.CP);
|
|
||||||
expect(newDay.numberOfDays).toBe(1);
|
|
||||||
expect(newDay.expiryDate).toBeInstanceOf(Date);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
@@ -44,9 +44,4 @@ export class UserInputService {
|
|||||||
|
|
||||||
readonly vacationNumberSignal = signal<VacDay[]>(this.getInitialValues());
|
readonly vacationNumberSignal = signal<VacDay[]>(this.getInitialValues());
|
||||||
readonly remainingVacationDaysSignal = signal<VacDay[]>([]);
|
readonly remainingVacationDaysSignal = signal<VacDay[]>([]);
|
||||||
readonly computeTriggerSignal = signal(0);
|
|
||||||
|
|
||||||
public requestComputation(): void {
|
|
||||||
this.computeTriggerSignal.update((count) => count + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+9
-3
@@ -4,8 +4,14 @@
|
|||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/app",
|
"outDir": "./out-tsc/app",
|
||||||
"types": ["node"]
|
"types": [
|
||||||
|
"node"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
"include": [
|
||||||
"exclude": ["src/**/*.spec.ts"]
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"src/**/*.spec.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
+6
-2
@@ -4,7 +4,11 @@
|
|||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/spec",
|
"outDir": "./out-tsc/spec",
|
||||||
"types": ["jasmine"]
|
"types": [
|
||||||
|
"jasmine"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"]
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user