{ "CI_job_history_included": false, "approvals": [], "diff_versions": [ { "commits": [ { "authored_date": null, "commit_author_id": null, "committed_date": null, "committer_id": null, "merge_request_commits_metadata_id": 202, "merge_request_diff_id": 106, "message": null, "project_id": null, "relative_order": 0, "sha": null, "trailers": {} }, { "authored_date": null, "commit_author_id": null, "committed_date": null, "committer_id": null, "merge_request_commits_metadata_id": 203, "merge_request_diff_id": 106, "message": null, "project_id": null, "relative_order": 1, "sha": null, "trailers": {} } ], "files": [ { "a_mode": "100644", "b_mode": "100644", "binary": false, "deleted_file": false, "diff": "@@ -7,6 +7,7 @@ import { CommonModule } from '@angular/common';\n import { MatProgressSpinner } from '@angular/material/progress-spinner';\n import { CalendarSettingsService } from '../../services/calendar-settings-service';\n import { DateCacheService } from '../../services/date-cache.service';\n+import { LoggerService } from '../../services/logger.service';\n \n @Component({\n \tselector: 'app-calendar-year',\n@@ -19,6 +20,7 @@ export class CalendarYear {\n \tprivate userInput = inject(UserInputService);\n \tprivate calendarService = inject(CalendarService);\n \tprivate calendarSettingsService = inject(CalendarSettingsService);\n+\tprivate logger = inject(LoggerService);\n \n \tprotected isLoading = signal(true);\n \tprotected loadingProgress = signal(0);\n@@ -53,6 +55,7 @@ export class CalendarYear {\n \t\t\t\t\t\t\t(progress: number) => {\n \t\t\t\t\t\t\t\tthis.loadingProgress.set(progress);\n \t\t\t\t\t\t\t},\n+\t\t\t\t\t\t\tthis.logger,\n \t\t\t\t\t\t);\n \t\t\t\t\t\t// Update the remaining vacation days signal\n \t\t\t\t\t\tthis.userInput.remainingVacationDaysSignal.set(remainingDays);\n", "encoded_file_path": false, "external_diff_offset": null, "external_diff_size": null, "generated": false, "merge_request_diff_id": 106, "new_file": false, "new_path": null, "old_path": "src/app/components/calendar-year/calendar-year.ts", "project_id": 35, "relative_order": 0, "renamed_file": false, "too_large": false }, { "a_mode": "100644", "b_mode": "100644", "binary": false, "deleted_file": false, "diff": "@@ -6,6 +6,7 @@ import weekOfYear from 'dayjs/plugin/weekOfYear';\n import isoWeek from 'dayjs/plugin/isoWeek';\n import { CalendarSettingsService } from './calendar-settings-service';\n import { DateCacheService } from './date-cache.service';\n+import { LoggerService } from './logger.service';\n \n dayjs.extend(weekOfYear);\n dayjs.extend(isoWeek);\n@@ -21,6 +22,7 @@ const allTypesOptions: HolidaysTypes.Options = {\n })\n export class CalendarService {\n \tprivate dateCache = inject(DateCacheService);\n+\tprivate logger = inject(LoggerService);\n \n \t// Cache for holiday timestamps\n \tprivate holidayTimestampsCache: Set | null = null;\n@@ -541,6 +543,7 @@ export class SelectedDates implements SelectedDateInterface {\n \t\tcalendarSettingsService: CalendarSettingsService,\n \t\tcalendarService: CalendarService,\n \t\tprogressCallback?: (progress: number) => void,\n+\t\tlogger?: LoggerService,\n \t): Promise {\n \t\tif (calendarSettingsService.get('samediMalin')()) this.samediMalin(vacationsNumber);\n \n@@ -600,8 +603,8 @@ export class SelectedDates implements SelectedDateInterface {\n \n \t\t// Inform user if not all days were placed\n \t\tconst remainingDays = vacationsNumber.cp + vacationsNumber.rtt + vacationsNumber.other;\n-\t\tif (remainingDays > 0) {\n-\t\t\tconsole.warn(\n+\t\tif (remainingDays > 0 && logger) {\n+\t\t\tlogger.warn(\n \t\t\t\t`Optimization stopped: ${remainingDays} of ${totalDays} vacation days remain unplaced. ` +\n \t\t\t\t\t`Too many days have equivalent placement quality - please place remaining days manually.`,\n \t\t\t);\n", "encoded_file_path": false, "external_diff_offset": null, "external_diff_size": null, "generated": false, "merge_request_diff_id": 106, "new_file": false, "new_path": null, "old_path": "src/app/services/calendar-service.ts", "project_id": 35, "relative_order": 1, "renamed_file": false, "too_large": false }, { "a_mode": "0", "b_mode": "100644", "binary": false, "deleted_file": false, "diff": "@@ -0,0 +1,52 @@\n+import { TestBed } from '@angular/core/testing';\n+import { LoggerService } from './logger.service';\n+\n+describe('LoggerService', () => {\n+\tlet service: LoggerService;\n+\tlet consoleLogSpy: jasmine.Spy;\n+\tlet consoleWarnSpy: jasmine.Spy;\n+\tlet consoleErrorSpy: jasmine.Spy;\n+\tlet consoleDebugSpy: jasmine.Spy;\n+\n+\tbeforeEach(() => {\n+\t\tTestBed.configureTestingModule({});\n+\t\tservice = TestBed.inject(LoggerService);\n+\n+\t\t// Spy on console methods\n+\t\tconsoleLogSpy = spyOn(console, 'log');\n+\t\tconsoleWarnSpy = spyOn(console, 'warn');\n+\t\tconsoleErrorSpy = spyOn(console, 'error');\n+\t\tconsoleDebugSpy = spyOn(console, 'debug');\n+\t});\n+\n+\tit('should be created', () => {\n+\t\texpect(service).toBeTruthy();\n+\t});\n+\n+\tit('should log messages', () => {\n+\t\tservice.log('test message');\n+\t\texpect(consoleLogSpy).toHaveBeenCalledWith('test message');\n+\t});\n+\n+\tit('should log warnings', () => {\n+\t\tservice.warn('test warning');\n+\t\texpect(consoleWarnSpy).toHaveBeenCalledWith('test warning');\n+\t});\n+\n+\tit('should log errors', () => {\n+\t\tservice.error('test error');\n+\t\texpect(consoleErrorSpy).toHaveBeenCalledWith('test error');\n+\t});\n+\n+\tit('should log debug messages', () => {\n+\t\tservice.debug('test debug');\n+\t\texpect(consoleDebugSpy).toHaveBeenCalledWith('test debug');\n+\t});\n+\n+\tit('should pass additional arguments to console methods', () => {\n+\t\tconst arg1 = { key: 'value' };\n+\t\tconst arg2 = [1, 2, 3];\n+\t\tservice.log('test', arg1, arg2);\n+\t\texpect(consoleLogSpy).toHaveBeenCalledWith('test', arg1, arg2);\n+\t});\n+});\n", "encoded_file_path": false, "external_diff_offset": null, "external_diff_size": null, "generated": false, "merge_request_diff_id": 106, "new_file": true, "new_path": null, "old_path": "src/app/services/logger.service.spec.ts", "project_id": 35, "relative_order": 2, "renamed_file": false, "too_large": false }, { "a_mode": "0", "b_mode": "100644", "binary": false, "deleted_file": false, "diff": "@@ -0,0 +1,47 @@\n+import { Injectable } from '@angular/core';\n+\n+/**\n+ * Logger service that wraps console methods.\n+ * Can be configured to disable logging in production environments.\n+ */\n+@Injectable({\n+\tprovidedIn: 'root',\n+})\n+export class LoggerService {\n+\tprivate isProduction = false; // Can be configured based on environment\n+\n+\t/**\n+\t * Log an informational message\n+\t */\n+\tlog(message: string, ...args: unknown[]): void {\n+\t\tif (!this.isProduction) {\n+\t\t\tconsole.log(message, ...args);\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Log a warning message\n+\t */\n+\twarn(message: string, ...args: unknown[]): void {\n+\t\tif (!this.isProduction) {\n+\t\t\tconsole.warn(message, ...args);\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Log an error message\n+\t */\n+\terror(message: string, ...args: unknown[]): void {\n+\t\t// Always log errors, even in production\n+\t\tconsole.error(message, ...args);\n+\t}\n+\n+\t/**\n+\t * Log a debug message\n+\t */\n+\tdebug(message: string, ...args: unknown[]): void {\n+\t\tif (!this.isProduction) {\n+\t\t\tconsole.debug(message, ...args);\n+\t\t}\n+\t}\n+}\n", "encoded_file_path": false, "external_diff_offset": null, "external_diff_size": null, "generated": false, "merge_request_diff_id": 106, "new_file": true, "new_path": null, "old_path": "src/app/services/logger.service.ts", "project_id": 35, "relative_order": 3, "renamed_file": false, "too_large": false } ], "metadata": { "base_commit_sha": "54c2160f0dd293f8c1c78342e155a65d891041fd", "commits_count": 2, "created_at": "2026-05-03T15:59:55.834478", "diff_type": 1, "files_count": 4, "head_commit_sha": "95f927386dd28aee5510866a32356d0dd300ebb9", "id": 106, "merge_request_id": 107, "project_id": 35, "start_commit_sha": "54c2160f0dd293f8c1c78342e155a65d891041fd", "state": "collected", "stored_externally": null, "updated_at": "2026-05-03T15:59:56.159086" } } ], "discussions": [ { "id": "a4fb78ad4be9fe1e9c1a67166f68e1f321d22118", "individual_note": true, "notes": [ { "author": { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" }, "body": "assigned to `@Copilot`", "commands_changes": {}, "confidential": false, "created_at": "2025-11-17T23:30:04.000Z", "id": 448, "imported": true, "imported_from": "github", "internal": false, "noteable_id": 107, "noteable_iid": 51, "noteable_type": "MergeRequest", "project_id": 35, "resolvable": false, "system": true, "type": null, "updated_at": "2025-11-17T23:30:04.000Z" } ], "resolvable": false }, { "id": "2efc95f642a3469ee34cb562bed0ab669b8af9b9", "individual_note": true, "notes": [ { "author": { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" }, "body": "assigned to `@THoguet`", "commands_changes": {}, "confidential": false, "created_at": "2025-11-17T23:30:04.000Z", "id": 449, "imported": true, "imported_from": "github", "internal": false, "noteable_id": 107, "noteable_iid": 51, "noteable_type": "MergeRequest", "project_id": 35, "resolvable": false, "system": true, "type": null, "updated_at": "2025-11-17T23:30:04.000Z" } ], "resolvable": false }, { "id": "ed6e35d3196950f4e8e02fb6c4f7697f615bcb94", "individual_note": true, "notes": [ { "author": { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" }, "body": "mentioned in merge request !49", "commands_changes": {}, "confidential": false, "created_at": "2025-11-17T23:30:06.000Z", "id": 452, "imported": true, "imported_from": "github", "internal": false, "noteable_id": 107, "noteable_iid": 51, "noteable_type": "MergeRequest", "project_id": 35, "resolvable": false, "system": true, "type": null, "updated_at": "2026-05-03T16:02:05.197Z" } ], "resolvable": false }, { "id": "55acb92b18cd69e0bef3ed75a161fe45edbb05e5", "individual_note": true, "notes": [ { "author": { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" }, "body": "changed title from **{-[WIP] WIP address feedback on loading spinner enhancement-}** to **{+Replace console.warn with LoggerService in calendar optimization+}**", "commands_changes": {}, "confidential": false, "created_at": "2025-11-17T23:42:25.000Z", "id": 450, "imported": true, "imported_from": "github", "internal": false, "noteable_id": 107, "noteable_iid": 51, "noteable_type": "MergeRequest", "project_id": 35, "resolvable": false, "system": true, "type": null, "updated_at": "2025-11-17T23:42:25.000Z" } ], "resolvable": false }, { "id": "747791f2773213d3adcfdd156493fd139e69aad2", "individual_note": true, "notes": [ { "author": { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" }, "body": "requested review from `@THoguet`", "commands_changes": {}, "confidential": false, "created_at": "2025-11-17T23:42:32.000Z", "id": 451, "imported": true, "imported_from": "github", "internal": false, "noteable_id": 107, "noteable_iid": 51, "noteable_type": "MergeRequest", "project_id": 35, "resolvable": false, "system": true, "type": null, "updated_at": "2025-11-17T23:42:32.000Z" } ], "resolvable": false } ], "format_version": 1, "notes": [ { "author_id": 3, "change_position": null, "commit_id": null, "confidential": null, "created_at": "2025-11-17T23:30:04", "discussion_id": "a4fb78ad4be9fe1e9c1a67166f68e1f321d22118", "id": 448, "imported_from": 4, "internal": false, "last_edited_at": null, "line_code": null, "namespace_id": null, "note": "assigned to `@Copilot`", "noteable_id": 107, "noteable_type": "MergeRequest", "organization_id": null, "original_position": null, "position": null, "project_id": 35, "resolved_at": null, "resolved_by_id": null, "resolved_by_push": null, "review_id": null, "source_iid": 51, "source_item_project_id": 35, "st_diff": null, "system": true, "type": null, "updated_at": "2025-11-17T23:30:04", "updated_by_id": null }, { "author_id": 3, "change_position": null, "commit_id": null, "confidential": null, "created_at": "2025-11-17T23:30:04", "discussion_id": "2efc95f642a3469ee34cb562bed0ab669b8af9b9", "id": 449, "imported_from": 4, "internal": false, "last_edited_at": null, "line_code": null, "namespace_id": null, "note": "assigned to `@THoguet`", "noteable_id": 107, "noteable_type": "MergeRequest", "organization_id": null, "original_position": null, "position": null, "project_id": 35, "resolved_at": null, "resolved_by_id": null, "resolved_by_push": null, "review_id": null, "source_iid": 51, "source_item_project_id": 35, "st_diff": null, "system": true, "type": null, "updated_at": "2025-11-17T23:30:04", "updated_by_id": null }, { "author_id": 3, "change_position": null, "commit_id": null, "confidential": null, "created_at": "2025-11-17T23:30:06", "discussion_id": "ed6e35d3196950f4e8e02fb6c4f7697f615bcb94", "id": 452, "imported_from": 4, "internal": false, "last_edited_at": null, "line_code": null, "namespace_id": null, "note": "mentioned in merge request !49", "noteable_id": 107, "noteable_type": "MergeRequest", "organization_id": null, "original_position": null, "position": null, "project_id": 35, "resolved_at": null, "resolved_by_id": null, "resolved_by_push": null, "review_id": null, "source_iid": 51, "source_item_project_id": 35, "st_diff": null, "system": true, "type": null, "updated_at": "2026-05-03T16:02:05.197128", "updated_by_id": null }, { "author_id": 3, "change_position": null, "commit_id": null, "confidential": null, "created_at": "2025-11-17T23:42:25", "discussion_id": "55acb92b18cd69e0bef3ed75a161fe45edbb05e5", "id": 450, "imported_from": 4, "internal": false, "last_edited_at": null, "line_code": null, "namespace_id": null, "note": "changed title from **{-[WIP] WIP address feedback on loading spinner enhancement-}** to **{+Replace console.warn with LoggerService in calendar optimization+}**", "noteable_id": 107, "noteable_type": "MergeRequest", "organization_id": null, "original_position": null, "position": null, "project_id": 35, "resolved_at": null, "resolved_by_id": null, "resolved_by_push": null, "review_id": null, "source_iid": 51, "source_item_project_id": 35, "st_diff": null, "system": true, "type": null, "updated_at": "2025-11-17T23:42:25", "updated_by_id": null }, { "author_id": 3, "change_position": null, "commit_id": null, "confidential": null, "created_at": "2025-11-17T23:42:32", "discussion_id": "747791f2773213d3adcfdd156493fd139e69aad2", "id": 451, "imported_from": 4, "internal": false, "last_edited_at": null, "line_code": null, "namespace_id": null, "note": "requested review from `@THoguet`", "noteable_id": 107, "noteable_type": "MergeRequest", "organization_id": null, "original_position": null, "position": null, "project_id": 35, "resolved_at": null, "resolved_by_id": null, "resolved_by_push": null, "review_id": null, "source_iid": 51, "source_item_project_id": 35, "st_diff": null, "system": true, "type": null, "updated_at": "2025-11-17T23:42:32", "updated_by_id": null } ], "preexisting_missing_alternate_head": false, "source_assignees": [], "source_author": { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" }, "source_created_at": "2025-11-17T23:30:03.000Z", "source_discussion_locked": null, "source_id": 107, "source_iid": 51, "source_item_metadata": { "assignee": null, "assignees": [], "author": { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" }, "blocking_discussions_resolved": true, "closed_at": null, "closed_by": null, "created_at": "2025-11-17T23:30:03.000Z", "description": "Addresses feedback from #49 to wrap console warnings in a configurable logging service and confirms inject() usage throughout.\n\n## Changes\n\n- **New LoggerService**: Environment-aware wrapper for console methods with production suppression flag\n- **Updated calendar-service.ts**: Added LoggerService parameter to `optimizeVacations()` method signature\n- **Updated calendar-year.ts**: Inject and pass LoggerService to optimization flow\n\n## Example\n\n```typescript\n// Before\nconsole.warn(`Optimization stopped: ${remainingDays} days remain unplaced.`);\n\n// After \nlogger?.warn(`Optimization stopped: ${remainingDays} days remain unplaced.`);\n```\n\nThe LoggerService can be configured via `isProduction` flag to disable non-error logging in production builds.\n\n\n---\n\n✨ Let Copilot coding agent [set things up for you](https://gitlab.nessar.fr/Nessar/vac-optimizer/-/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.\n", "detailed_merge_status": "unchecked", "discussion_locked": null, "downvotes": 0, "draft": false, "force_remove_source_branch": null, "has_conflicts": false, "id": 107, "iid": 51, "imported": true, "imported_from": "github", "labels": [], "merge_after": null, "merge_commit_sha": null, "merge_status": "unchecked", "merge_user": null, "merge_when_pipeline_succeeds": false, "merged_at": null, "merged_by": null, "milestone": null, "prepared_at": null, "project_id": 35, "reference": "!51", "references": { "full": "nessar/vac-optimizer!51", "relative": "!51", "short": "!51" }, "reviewers": [ { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" } ], "sha": "95f927386dd28aee5510866a32356d0dd300ebb9", "should_remove_source_branch": null, "source_branch": "copilot/sub-pr-49", "source_project_id": 35, "squash": false, "squash_commit_sha": null, "squash_on_merge": false, "state": "closed", "target_branch": "THoguet/issue16", "target_project_id": 35, "task_completion_status": { "completed_count": 0, "count": 0 }, "time_stats": { "human_time_estimate": null, "human_total_time_spent": null, "time_estimate": 0, "total_time_spent": 0 }, "title": "Replace console.warn with LoggerService in calendar optimization", "updated_at": "2025-11-17T23:44:59.000Z", "upvotes": 0, "user_notes_count": 0, "web_url": "https://gitlab.nessar.fr/nessar/vac-optimizer/-/merge_requests/51", "work_in_progress": false }, "source_kind": "MergeRequest", "source_path": "nessar/vac-optimizer", "source_project_id": 35, "source_reviewers": [ { "avatar_url": "https://secure.gravatar.com/avatar/5e51e115b51304582ee7b2cd8f249fe3ee71b46510e381f74c032e6de81d2757?s=80&d=identicon", "id": 3, "locked": false, "name": "admin", "public_email": null, "state": "active", "username": "admin-backup", "web_url": "https://gitlab.nessar.fr/admin-backup" } ], "source_updated_at": "2025-11-17T23:44:59.000Z", "state_events": [ { "close_after_error_tracking_resolve": false, "close_auto_resolve_prometheus_alert": false, "created_at": "2025-11-17T23:44:59+00:00", "epic_id": null, "id": 224, "imported_from": 4, "issue_id": null, "merge_request_id": 107, "namespace_id": 38, "source_commit": null, "source_merge_request_id": null, "state": 2, "user_id": 3 } ], "target_number": 129 }