Wrapping an async function in setTimeout is an anti-pattern. The setTimeout callback should remain synchronous, and the async operations should be handled with proper promise chaining or await. Consider restructuring this to use setTimeout(() => { void asyncFunction(); }) or better yet, replace setTimeout with a proper async delay mechanism.
Wrapping an async function in setTimeout is an anti-pattern. The setTimeout callback should remain synchronous, and the async operations should be handled with proper promise chaining or await. Consider restructuring this to use `setTimeout(() => { void asyncFunction(); })` or better yet, replace setTimeout with a proper async delay mechanism.
Using setTimeout(resolve, 0) on every iteration (potentially hundreds of times) will significantly degrade performance. This creates unnecessary macrotasks and event loop overhead. Consider updating progress less frequently (e.g., every 10 iterations or based on a time threshold) or using requestAnimationFrame for UI updates.
Using `setTimeout(resolve, 0)` on every iteration (potentially hundreds of times) will significantly degrade performance. This creates unnecessary macrotasks and event loop overhead. Consider updating progress less frequently (e.g., every 10 iterations or based on a time threshold) or using `requestAnimationFrame` for UI updates.
The constant MAX_DAYS_WITH_SAME_HEURISTIC (value 9) lacks documentation explaining why this specific threshold was chosen. Add a comment explaining the rationale behind this magic number to improve maintainability.
The constant `MAX_DAYS_WITH_SAME_HEURISTIC` (value 9) lacks documentation explaining why this specific threshold was chosen. Add a comment explaining the rationale behind this magic number to improve maintainability.
[nitpick] The comment should be updated to reflect the new value. It now says 'Big bonus' but the change from 10 to 15 represents a 50% increase. Consider updating the comment to explain why this specific value was chosen or remove the generic 'Big bonus' comment if the value speaks for itself.
totalScore += 15; // 50% increased bonus for adjacency; adjacency is highly valued in the scoring system
[nitpick] The comment should be updated to reflect the new value. It now says 'Big bonus' but the change from 10 to 15 represents a 50% increase. Consider updating the comment to explain why this specific value was chosen or remove the generic 'Big bonus' comment if the value speaks for itself.
```suggestion:-0+0
totalScore += 15; // 50% increased bonus for adjacency; adjacency is highly valued in the scoring system
```
Using !important to override Material Design styles is an anti-pattern. Consider using a more specific CSS selector or Angular's ViewEncapsulation to properly scope styles without relying on !important.
width: 100px;
height: 100px;
Using `!important` to override Material Design styles is an anti-pattern. Consider using a more specific CSS selector or Angular's ViewEncapsulation to properly scope styles without relying on `!important`.
```suggestion:-0+0
width: 100px;
height: 100px;
```
[nitpick] Console warnings in production code should be avoided or wrapped in a logging service. Consider using a proper logging service that can be configured for different environments (development vs production).
[nitpick] Console warnings in production code should be avoided or wrapped in a logging service. Consider using a proper logging service that can be configured for different environments (development vs production).
This PR enhances the vacation optimization feature by adding visual progress feedback during optimization and displaying warnings when vacation days cannot be placed automatically. The optimization algorithm has been made asynchronous to allow UI updates during processing.
Key Changes
Made vacation optimization asynchronous with progress callbacks
Added remaining vacation days tracking with a warning UI
Enhanced heuristic logic to stop optimization when too many days have equal placement quality
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
File
Description
src/app/services/user-input-service.ts
Added signal to track remaining unplaced vacation days
src/app/services/calendar-service.ts
Converted optimization to async, added progress tracking and stop condition for ambiguous placements
src/app/components/user-input/user-input.ts
Added computed properties to display remaining vacation days warning
**Review:** Commented
## Pull Request Overview
This PR enhances the vacation optimization feature by adding visual progress feedback during optimization and displaying warnings when vacation days cannot be placed automatically. The optimization algorithm has been made asynchronous to allow UI updates during processing.
### Key Changes
- Made vacation optimization asynchronous with progress callbacks
- Added remaining vacation days tracking with a warning UI
- Enhanced heuristic logic to stop optimization when too many days have equal placement quality
### Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
<details>
<summary>Show a summary per file</summary>
| File | Description |
| ---- | ----------- |
| src/app/services/user-input-service.ts | Added signal to track remaining unplaced vacation days |
| src/app/services/calendar-service.ts | Converted optimization to async, added progress tracking and stop condition for ambiguous placements |
| src/app/components/user-input/user-input.ts | Added computed properties to display remaining vacation days warning |
| src/app/components/user-input/user-input.html | Added warning card UI for unplaced vacation days |
| src/app/components/user-input/user-input.scss | Added styles for warning card component |
| src/app/components/calendar-year/calendar-year.ts | Integrated progress tracking with loading spinner |
| src/app/components/calendar-year/calendar-year.html | Updated spinner to show progress percentage |
| src/app/components/calendar-year/calendar-year.scss | Styled progress spinner with percentage overlay |
</details>
@copilot open a new pull request to apply changes based on this feedback
`@copilot` open a new pull request to apply changes based on [this feedback](https://gitlab.nessar.fr/Nessar/vac-optimizer/-/merge_requests/49#discussion_r2535792120)
The magic number 9 for MAX_DAYS_WITH_SAME_HEURISTIC lacks explanation. Add a comment explaining why 9 is the threshold (e.g., based on testing, represents approximately one week of vacation days, or other reasoning).
private static readonly NO_VALID_STRATEGY_HEURISTIC = -1;
// MAX_DAYS_WITH_SAME_HEURISTIC is set to 9 to represent approximately one week of vacation days (5 workdays + 2 weekends) plus a buffer for common extended leave patterns, based on user testing and business requirements.
The magic number 9 for MAX_DAYS_WITH_SAME_HEURISTIC lacks explanation. Add a comment explaining why 9 is the threshold (e.g., based on testing, represents approximately one week of vacation days, or other reasoning).
```suggestion:-0+0
private static readonly NO_VALID_STRATEGY_HEURISTIC = -1;
// MAX_DAYS_WITH_SAME_HEURISTIC is set to 9 to represent approximately one week of vacation days (5 workdays + 2 weekends) plus a buffer for common extended leave patterns, based on user testing and business requirements.
```
Using setTimeout(resolve, 0) in a potentially tight loop during optimization could significantly slow down the process. For a vacation with 20+ days, this adds 20+ microtask delays. Consider throttling progress updates (e.g., only update every 5% or every N days) instead of on every iteration.
// Helper function to update progress, throttled to every 5%
let lastReportedProgress = 0;
const PROGRESS_STEP = 5; // percent
const updateProgress = async () => {
if (progressCallback && totalDays > 0) {
const progress = Math.round((processedDays / totalDays) * 100);
if (progress - lastReportedProgress >= PROGRESS_STEP || progress === 100) {
progressCallback(progress);
lastReportedProgress = progress;
// Allow UI to update with a microtask
await new Promise((resolve) => setTimeout(resolve, 0));
}
Using `setTimeout(resolve, 0)` in a potentially tight loop during optimization could significantly slow down the process. For a vacation with 20+ days, this adds 20+ microtask delays. Consider throttling progress updates (e.g., only update every 5% or every N days) instead of on every iteration.
```suggestion:-0+0
// Helper function to update progress, throttled to every 5%
let lastReportedProgress = 0;
const PROGRESS_STEP = 5; // percent
const updateProgress = async () => {
if (progressCallback && totalDays > 0) {
const progress = Math.round((processedDays / totalDays) * 100);
if (progress - lastReportedProgress >= PROGRESS_STEP || progress === 100) {
progressCallback(progress);
lastReportedProgress = progress;
// Allow UI to update with a microtask
await new Promise((resolve) => setTimeout(resolve, 0));
}
```
The warning icon lacks an aria-label or other accessible text for screen readers. Add aria-label=\"Warning\" or use aria-hidden=\"true\" if the text description is sufficient.
The warning icon lacks an aria-label or other accessible text for screen readers. Add `aria-label=\"Warning\"` or use `aria-hidden=\"true\"` if the text description is sufficient.
```suggestion:-0+0
<mat-icon color="warn" aria-hidden="true">warning</mat-icon>
```
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/app/components/user-input/user-input.ts:28
The component uses constructor injection for UserInputService, but the coding guidelines specify using the inject() function instead. Consider changing to private userInputService = inject(UserInputService); at the class level for consistency with Angular best practices.
**Review:** Commented
## Pull Request Overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
<details>
<summary>Comments suppressed due to low confidence (1)</summary>
**src/app/components/user-input/user-input.ts:28**
* The component uses constructor injection for `UserInputService`, but the coding guidelines specify using the `inject()` function instead. Consider changing to `private userInputService = inject(UserInputService);` at the class level for consistency with Angular best practices.
```
constructor(private userInputService: UserInputService) {
```
</details>
@copilot open a new pull request to apply changes based on this feedback;
also remove constructor injection and use inject()
`@copilot` open a new pull request to apply changes based on [this feedback](https://gitlab.nessar.fr/Nessar/vac-optimizer/-/merge_requests/49#r2535792156);
also remove constructor injection and use inject()
The spinner should have an accessible label to inform screen reader users about the loading state and progress. The mat-spinner component should include aria-label or be wrapped in an element with an appropriate label.
The spinner should have an accessible label to inform screen reader users about the loading state and progress. The `mat-spinner` component should include `aria-label` or be wrapped in an element with an appropriate label.
Add an `aria-label` to the spinner:
```html
<mat-spinner
[mode]="'determinate'"
[value]="loadingProgress()"
[attr.aria-label]="'Optimisation en cours: ' + loadingProgress() + ' pourcent'">
</mat-spinner>
```
```suggestion:-0+0
<mat-spinner
[mode]="'determinate'"
[value]="loadingProgress()"
[attr.aria-label]="'Optimisation en cours: ' + loadingProgress() + ' pourcent'">
</mat-spinner>
```
Using setTimeout(resolve, 0) for every single day placement could cause performance issues when optimizing a large number of vacation days. Each setTimeout adds overhead and forces the event loop to cycle unnecessarily.
Consider batching progress updates or using a minimum time interval between updates:
constupdateProgress=async()=>{if(progressCallback&&totalDays>0){constprogress=Math.round((processedDays/totalDays)*100);// Only update if progress changed by at least 1%
if(progress!==this.lastProgress){progressCallback(progress);this.lastProgress=progress;// Allow UI to update with a microtask
awaitnewPromise((resolve)=>setTimeout(resolve,0));}}};
let lastYieldTime = Date.now();
const YIELD_INTERVAL_MS = 100;
const updateProgress = async () => {
if (progressCallback && totalDays > 0) {
const progress = Math.round((processedDays / totalDays) * 100);
if (progress - lastReportedProgress >= PROGRESS_STEP || progress === 100) {
progressCallback(progress);
lastReportedProgress = progress;
const now = Date.now();
if (now - lastYieldTime >= YIELD_INTERVAL_MS) {
lastYieldTime = now;
// Allow UI to update with a microtask
await new Promise((resolve) => setTimeout(resolve, 0));
}
Using `setTimeout(resolve, 0)` for every single day placement could cause performance issues when optimizing a large number of vacation days. Each `setTimeout` adds overhead and forces the event loop to cycle unnecessarily.
Consider batching progress updates or using a minimum time interval between updates:
```typescript
const updateProgress = async () => {
if (progressCallback && totalDays > 0) {
const progress = Math.round((processedDays / totalDays) * 100);
// Only update if progress changed by at least 1%
if (progress !== this.lastProgress) {
progressCallback(progress);
this.lastProgress = progress;
// Allow UI to update with a microtask
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
};
```
Or update progress only every N days:
```typescript
if (processedDays % 5 === 0 || processedDays === totalDays) {
await updateProgress();
}
```
```suggestion:-7+0
let lastYieldTime = Date.now();
const YIELD_INTERVAL_MS = 100;
const updateProgress = async () => {
if (progressCallback && totalDays > 0) {
const progress = Math.round((processedDays / totalDays) * 100);
if (progress - lastReportedProgress >= PROGRESS_STEP || progress === 100) {
progressCallback(progress);
lastReportedProgress = progress;
const now = Date.now();
if (now - lastYieldTime >= YIELD_INTERVAL_MS) {
lastYieldTime = now;
// Allow UI to update with a microtask
await new Promise((resolve) => setTimeout(resolve, 0));
}
```
The warning message should have an aria-live attribute to announce changes to screen reader users. When the warning appears dynamically, assistive technologies should be notified.
The warning message should have an `aria-live` attribute to announce changes to screen reader users. When the warning appears dynamically, assistive technologies should be notified.
Add `aria-live="polite"` to the `mat-card` element:
```html
<mat-card class="mat-elevation-z2 warning-card" aria-live="polite">
```
```suggestion:-0+0
<mat-card class="mat-elevation-z2 warning-card" aria-live="polite">
```
[nitpick] The warning icon should have an aria-label to provide context for screen readers. While aria-hidden="true" prevents the icon from being announced, the warning text should be properly associated with the icon's semantic meaning.
Consider wrapping the content in a semantic element with an appropriate role or adding an aria-label to the parent span:
<spanrole="alert"><mat-iconcolor="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></span>
[nitpick] The warning icon should have an `aria-label` to provide context for screen readers. While `aria-hidden="true"` prevents the icon from being announced, the warning text should be properly associated with the icon's semantic meaning.
Consider wrapping the content in a semantic element with an appropriate role or adding an `aria-label` to the parent span:
```html
<span role="alert">
<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>
</span>
```
```suggestion:-3+0
<span role="alert">
<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>
```
Source assignees: admin-backup (GitLab user 3).
Source requested reviewers: admin-backup (GitLab user 3).
The linked history record retains original IDs and source dates. No historical identity was merged into nessar or granted new authentication permissions.
<!-- gitlab-to-gitea-assignment:35:MergeRequest:49 -->
Preserved source assignment/reviewer metadata. Historical accounts stay inactive.
Source assignees: admin-backup (GitLab user 3).
Source requested reviewers: admin-backup (GitLab user 3).
The linked history record retains original IDs and source dates. No historical identity was merged into nessar or granted new authentication permissions.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
…g for unplaced vacation days
assigned to
@Copilotrequested review from
@Copilotcloses #16
Wrapping an async function in setTimeout is an anti-pattern. The setTimeout callback should remain synchronous, and the async operations should be handled with proper promise chaining or await. Consider restructuring this to use
setTimeout(() => { void asyncFunction(); })or better yet, replace setTimeout with a proper async delay mechanism.Using
setTimeout(resolve, 0)on every iteration (potentially hundreds of times) will significantly degrade performance. This creates unnecessary macrotasks and event loop overhead. Consider updating progress less frequently (e.g., every 10 iterations or based on a time threshold) or usingrequestAnimationFramefor UI updates.The constant
MAX_DAYS_WITH_SAME_HEURISTIC(value 9) lacks documentation explaining why this specific threshold was chosen. Add a comment explaining the rationale behind this magic number to improve maintainability.[nitpick] The comment should be updated to reflect the new value. It now says 'Big bonus' but the change from 10 to 15 represents a 50% increase. Consider updating the comment to explain why this specific value was chosen or remove the generic 'Big bonus' comment if the value speaks for itself.
Using
!importantto override Material Design styles is an anti-pattern. Consider using a more specific CSS selector or Angular's ViewEncapsulation to properly scope styles without relying on!important.[nitpick] Console warnings in production code should be avoided or wrapped in a logging service. Consider using a proper logging service that can be configured for different environments (development vs production).
Review: Commented
Pull Request Overview
This PR enhances the vacation optimization feature by adding visual progress feedback during optimization and displaying warnings when vacation days cannot be placed automatically. The optimization algorithm has been made asynchronous to allow UI updates during processing.
Key Changes
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
@copilotopen a new pull request to apply changes based on this feedback@THoguetI've opened a new pull request, #50, to work on those changes. Once the pull request is ready, I'll request review from you.mentioned in merge request !50
requested review from
@CopilotThe magic number 9 for MAX_DAYS_WITH_SAME_HEURISTIC lacks explanation. Add a comment explaining why 9 is the threshold (e.g., based on testing, represents approximately one week of vacation days, or other reasoning).
Using
setTimeout(resolve, 0)in a potentially tight loop during optimization could significantly slow down the process. For a vacation with 20+ days, this adds 20+ microtask delays. Consider throttling progress updates (e.g., only update every 5% or every N days) instead of on every iteration.The warning icon lacks an aria-label or other accessible text for screen readers. Add
aria-label=\"Warning\"or usearia-hidden=\"true\"if the text description is sufficient.Review: Commented
Pull Request Overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/app/components/user-input/user-input.ts:28
UserInputService, but the coding guidelines specify using theinject()function instead. Consider changing toprivate userInputService = inject(UserInputService);at the class level for consistency with Angular best practices.requested review from
@Copilot@copilotopen a new pull request to apply changes based on this feedback;also remove constructor injection and use inject()
@THoguetI've opened a new pull request, #51, to work on those changes. Once the pull request is ready, I'll request review from you.mentioned in merge request !51
The spinner should have an accessible label to inform screen reader users about the loading state and progress. The
mat-spinnercomponent should includearia-labelor be wrapped in an element with an appropriate label.Add an
aria-labelto the spinner:Using
setTimeout(resolve, 0)for every single day placement could cause performance issues when optimizing a large number of vacation days. EachsetTimeoutadds overhead and forces the event loop to cycle unnecessarily.Consider batching progress updates or using a minimum time interval between updates:
Or update progress only every N days:
The warning message should have an
aria-liveattribute to announce changes to screen reader users. When the warning appears dynamically, assistive technologies should be notified.Add
aria-live="polite"to themat-cardelement:[nitpick] The warning icon should have an
aria-labelto provide context for screen readers. Whilearia-hidden="true"prevents the icon from being announced, the warning text should be properly associated with the icon's semantic meaning.Consider wrapping the content in a semantic element with an appropriate role or adding an
aria-labelto the parent span:Review: Commented
Pull Request Overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Merged by: THoguet at 2025-11-17 23:45:05 UTC
Preserved source assignment/reviewer metadata. Historical accounts stay inactive.
Source assignees: admin-backup (GitLab user 3).
Source requested reviewers: admin-backup (GitLab user 3).
The linked history record retains original IDs and source dates. No historical identity was merged into nessar or granted new authentication permissions.
Preserved GitLab !49 history: source attribution, exact timestamps, discussion grouping and review positions. Historical diff snapshots: 1; visible source notes: 28.
gitlab-35-mergerequest-49.md (SHA-256
d0a7b18ec0567cd7168c56bad7670f86c9c7461b3cd39db5bbf394827042d946)gitlab-35-mergerequest-49.json (SHA-256
8f46b28f7d6ee53d4c428fc65c95c411577358a0337500d55c970bdade5f18da)Old CI job execution and reports are omitted. Original source files and rollback backups remain protected.