Skip to content

MOSIP-25201 - Fix (round 3) accessibility and SonarQube reliability issues in multiple components#224

Merged
mayuradesh merged 1 commit intomosip:developfrom
ymahtat-dev:develop
Nov 13, 2025
Merged

MOSIP-25201 - Fix (round 3) accessibility and SonarQube reliability issues in multiple components#224
mayuradesh merged 1 commit intomosip:developfrom
ymahtat-dev:develop

Conversation

@ymahtat-dev
Copy link
Contributor

@ymahtat-dev ymahtat-dev commented Nov 13, 2025

Summary by CodeRabbit

  • Refactor

    • Initialization now runs in background across auth, booking, time-selection, dashboard, demographic, file-upload and summary flows; startup and logout/config handling consolidated for more consistent behavior.
  • Bug Fixes

    • Safer parsing and storage handling, improved numeric checks for OTP/expiry, more reliable language detection and reload/navigation ordering, and fixes to recommended-center and slot loading sequencing.
  • Style

    • Consistent quoting, NOSONAR guidance comments, minor formatting and logging tweaks.

@coderabbitai
Copy link

coderabbitai bot commented Nov 13, 2025

Walkthrough

Eight Angular components changed lifecycle initialization: ngOnInit() is now synchronous and delegates async work to new private initializers (or initializeAfterLogout in login). Config loading and language handling were adjusted, localStorage JSON reads hardened, RegExp file-extension checks made safer, and numeric NaN checks use Number.isNaN().

Changes

Cohort / File(s) Summary
Auth component
pre-registration-ui/src/app/auth/login/login.component.ts
async ngOnInit()ngOnInit(): void; added private initializeAfterLogout(forceLogout, langCode): Promise<void> and fire-and-forget call from ngOnInit; loadConfigs() changed to return Promise<void>; switched isNaNNumber.isNaN; adjusted langCode/localStorage and browser-reload logic; centralized error logging.
Booking components
pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts,
pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts
Both: async ngOnInit()ngOnInit(): void; added private initAsync(): Promise<void> invoked fire-and-forget after determining params/localStorage; hardened JSON.parse from localStorage with try/catch; consolidated location-type filtering/sorting and deferred recommended-centers/slots calls until async init completes.
Dashboard
pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts
async ngOnInit()ngOnInit(); getIdentityJsonFormat() is now invoked fire-and-forget (no await).
Demographic
pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts
async ngOnInit()ngOnInit(): void; added private async initAsync(): Promise<void> and moved async work there (fire-and-forget); added NOSONAR comments around date-format replacement; minor formatting tweaks.
File upload
pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts
async ngOnInit()ngOnInit(): void; added initAsync() (fire-and-forget); standardized quote style; replaced extension RegExp construction with split/trim/join approach, added NOSONAR comment and null-guard before testing file extension.
Summary / Acknowledgement
pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts
async ngOnInit()ngOnInit(): void; added private async initAsync(): Promise<void> and fire-and-forget invocation; hardened localStorage parsing for multiappointment with try/catch; moved language subscription into tracked subscriptions; added NOSONAR notes in string normalization.
Summary / Preview
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts
async ngOnInit()ngOnInit(): void; added private async initAsync(): Promise<void> which awaits identity/dynamic fields/files/categories and performs post-init setup; ngOnInit fires initAsync() without awaiting; minor NOSONAR comment for DOB formatting replacement.

Sequence Diagram

sequenceDiagram
    participant Browser as Component
    participant ngInit as ngOnInit()
    participant Init as initAsync() / initializeAfterLogout()
    participant Services as Services/API

    Note over Browser,Services: Old pattern — ngOnInit awaited async work
    Browser->>ngInit: component created
    ngInit->>Services: await asyncServiceCalls()
    Services-->>ngInit: data
    ngInit-->>Browser: initialized

    Note over Browser,Services: New pattern — synchronous ngOnInit + async initializer
    Browser->>ngInit: component created
    ngInit->>Init: invoke (fire & forget)
    ngInit-->>Browser: returns immediately
    Init->>Services: await asyncServiceCalls()
    Services-->>Init: data
    Init-->>Browser: initialization completes (async)

    Note right of Init: Login uses initializeAfterLogout(forceLogout, langCode) for logout/config sequencing
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • Verify original await ordering and error handling preserved inside each new initializer.
    • Check for race conditions introduced by fire-and-forget (route params, localStorage, subscriptions).
    • Confirm Number.isNaN() substitutions and time/OTP edge cases.
    • Inspect safe JSON.parse fallbacks and file-extension RegExp null-guards.

Poem

🐰
I hopped from hooks to secret trails,
ngOnInit now quickly sails.
initAsync hums, then brings the light,
Number.isNaN keeps counts polite.
A little hop — the app sleeps light.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main objective of the PR: fixing accessibility and SonarQube reliability issues across multiple components. It accurately reflects the changeset which systematically refactors multiple component lifecycle methods.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (1)

1435-1441: Bug: assignment instead of comparison

This condition assigns rather than compares, making it always truthy.

-    this.users[0].files[0].documentsMetaData.forEach((element) => {
-      if ((element.docCatCode = category)) {
+    this.users[0].files[0].documentsMetaData.forEach((element) => {
+      if (element.docCatCode === category) {
         return true;
       }
     });
pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1)

235-247: Async bug: forEach + async/await resolves early

getLocationNamesByCodes() resolves when the last iteration finishes, not when all finish. Use Promise.all or a for..of loop.

-  getLocationNamesByCodes() {
-    return new Promise((resolve) => {
-      if (this.locationCodes.length == 0) {
-        resolve(true);
-      }
-      this.locationCodes.forEach(async (pins,index) => {
-        await this.getLocationNames(pins);
-        if(index===this.locationCodes.length-1){
-          resolve(true);
-        }
-      });
-    });
-  }
+  async getLocationNamesByCodes() {
+    if (this.locationCodes.length === 0) { return; }
+    await Promise.all(this.locationCodes.map((code) => this.getLocationNames(code)));
+  }
pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (1)

86-107: Add error handling to prevent silent failures.

The initAsync() method contains multiple await calls but lacks error handling. If any operation fails, the error will be silently swallowed (due to the fire-and-forget pattern on lines 67, 72), and showSpinner will never be set to false, leaving the UI in a broken state.

Apply this diff to add proper error handling:

 private async initAsync(): Promise<void> {
+  try {
     await this.getUserInfo(this.preRegIds);
     // console.log(this.usersInfoArr);
     for (let i = 0; i < this.usersInfoArr.length; i++) {
       await this.getRegCenterDetails(this.usersInfoArr[i].langCode, i);
       await this.getLabelDetails(this.usersInfoArr[i].langCode, i);
       await this.getUserLangLabelDetails(this.langCode, i);
     }
     const notificationTypes = this.configService
       .getConfigByKey(appConstants.CONFIG_KEYS.mosip_notification_type)
       .split('|');
     this.notificationTypes = notificationTypes.map((item) =>
       item.toUpperCase()
     );
     await this.apiCalls();
     if (this.bookingService.getSendNotification()) {
       this.bookingService.resetSendNotification();
       await this.automaticNotification();
     }
     this.prepareAckDataForUI();
+  } catch (error) {
+    this.showErrorMessage(error);
+  } finally {
     this.showSpinner = false;
+  }
 }
🧹 Nitpick comments (6)
pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts (3)

108-143: Fire-and-forget init can race with consumers of schema/locationHierarchy

getIdentityJsonFormat() is now called without await; any code reading localStorage 'schema' or 'locationHierarchy' during/soon after ngOnInit can see stale/missing values. Consider gating consumers or awaiting once on first load. At minimum, mark a ready flag before downstream use.

If any template or method reads these keys during init, ensure it’s behind a dataLoaded flag or await the call once at startup.


23-23: Remove unused import

utf8Encode is imported but never used.

- import { utf8Encode } from "@angular/compiler/src/util";

145-173: Resolve promise on error to avoid hanging callers

getIdentityJsonFormat() never resolves on error. Even if currently fire-and-forget, future awaiters would hang.

       this.dataStorageService.getIdentityJson().subscribe(
         (response) => {
           // ...
           resolve(true);
         },
         (error) => {
-          this.showErrorMessage(error);
+          this.showErrorMessage(error);
+          resolve(false); // ensure the promise settles
         }
       );
pre-registration-ui/src/app/auth/login/login.component.ts (1)

98-99: Don’t set dir before it’s computed

localStorage 'dir' is set here before setLanguageDirection runs; it may momentarily be wrong. Let loadConfigs/setLanguageDirection update it.

-    localStorage.setItem('dir', this.dir);
pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1)

235-247: Optional: fix async forEach pattern (same issue as time-selection)

Use Promise.all or for..of to ensure all getLocationNames complete before proceeding.

-  getLocationNamesByCodes() {
-    return new Promise((resolve) => {
-      if (this.locationCodes.length == 0) {
-        resolve(true);
-      }
-      this.locationCodes.forEach(async (pins,index) => {
-        await this.getLocationNames(pins);
-        if(index===this.locationCodes.length-1){
-          resolve(true);
-        }
-      });
-    });
-  }
+  async getLocationNamesByCodes() {
+    if (this.locationCodes.length === 0) { return; }
+    await Promise.all(this.locationCodes.map((code) => this.getLocationNames(code)));
+  }
pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (1)

379-446: Consider extracting repeated string normalization to a helper function.

The same normalization pattern (stringify → remove brackets → replace commas → remove quotes) is repeated 7 times. This can be refactored into a reusable helper function to improve maintainability and reduce duplication.

Consider adding this helper method to the class:

private normalizeArrayToString(arr: any[]): string {
  return JSON.stringify(arr)
    // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
    .replace(/\[/g, "")
    // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
    .replace(/,/g, " / ")
    // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
    .replace(/"/g, " ")
    // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
    .replace(/]/g, "");
}

Then replace each occurrence:

-      this.ackDataItem["preRegIdLabels"] = JSON.stringify(
-        preRegIdLabels
-      )
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/\[/g, "")
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/,/g, " / ")
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/"/g, " ")
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/]/g, "");
+      this.ackDataItem["preRegIdLabels"] = this.normalizeArrayToString(preRegIdLabels);

Apply the same pattern for appDateLabels, contactPhoneLabels, labelNames, nameValues, labelRegCntrs, and regCntrNames.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2cbc07 and 9a2fd9b.

📒 Files selected for processing (8)
  • pre-registration-ui/src/app/auth/login/login.component.ts (3 hunks)
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (4 hunks)
  • pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-pre-registration-ui / npm-build
🔇 Additional comments (8)
pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (1)

130-141: ngOnInit/initAsync split looks good

Synchronous ngOnInit with a deferred initAsync is fine and improves determinism.

pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (2)

71-84: Synchronous ngOnInit with deferred init is fine; ensure template guards

initAsync runs unattended; keep using dataLoaded guards in the template to prevent ExpressionChanged errors or partial renders.


385-397: Good: replaceAll avoidance with NOSONAR

Regex-based replace for Angular 7 compatibility is appropriate.

pre-registration-ui/src/app/auth/login/login.component.ts (1)

311-328: Good: use Number.isNaN guard

This removes false positives from global isNaN coercion.

pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (2)

190-201: ngOnInit/initAsync split looks good

Pattern aligns with other components. Keep template guarded by data/state flags.


298-321: Good: regex replace with NOSONAR instead of replaceAll

Maintains Angular 7/browser compatibility.

Also applies to: 302-304, 306-312, 314-320

pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1)

97-101: Good: numeric conversion for config value

Casting recommendedCenterLocCode to number prevents string comparisons.

pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (1)

64-65: Good defensive coding for localStorage access.

The code properly handles the case where localStorage.getItem('multiappointment') might return null, providing a safe fallback to an empty array.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (1)
pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1)

89-114: Critical: multiappointment path never sets registrationCenter.

The multiappointment branch (line 90) calls initAsync() immediately, but registrationCenter is never populated. The defensive guard at line 117 will cause initAsync() to return early, leaving the component in an incomplete state.

In contrast, the single-appointment path (lines 97-101) sets registrationCenter from route params before calling initAsync().

Based on the past review comments, registrationCenter should come from queryParams. Add a queryParams subscription that sets registrationCenter and triggers initialization only after both preRegId and registrationCenter are known:

  ngOnInit(): void {
    if (this.router.url.includes('multiappointment')) {
      const stored = localStorage.getItem('multiappointment');
      this.preRegId = stored ? [...JSON.parse(stored)] : [];
-      // we already know preRegId, we can start async init
-      void this.initAsync();
    } else {
-      // we don't know preRegId yet - wait for params then start async init
      this.activatedRoute.params.subscribe((param) => {
        this.preRegId = [param['appId']];
-        this.registrationCenter = param['regCenter'];
-        void this.initAsync();
+        if (this.registrationCenter) { void this.initAsync(); }
      });
    }
+    this.activatedRoute.queryParams.subscribe((param) => {
+      this.registrationCenter = param['regCenter'];
+      if (this.preRegId && this.preRegId.length) { void this.initAsync(); }
+    });
    this.name = this.configService.getConfigByKey(
      appConstants.CONFIG_KEYS.preregistration_identity_name
    );
🧹 Nitpick comments (4)
pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (1)

130-140: Consider adding error handling for the fire-and-forget async initialization.

Making ngOnInit synchronous is good practice. However, the fire-and-forget pattern (void this.initAsync()) means any errors thrown in initAsync will be silently swallowed, making debugging difficult.

Consider adding error handling:

-    // async part moved to helper; fire and forget
-    void this.initAsync();
+    // async part moved to helper; log errors if initialization fails
+    this.initAsync().catch(err => {
+      this.loggerService.error('Initialization failed', err);
+      this.showErrorMessage(err);
+    });
pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (2)

106-107: Remove duplicate console.log statement.

The same log statement appears twice consecutively.

Apply this diff:

     this.allLocationTypes = response[appConstants.RESPONSE]['locationHierarchyLevels'];
     console.log(this.allLocationTypes);
-    console.log(this.allLocationTypes);
     //get the recommended loc hierachy code to which booking centers are mapped

91-121: Consider adding error handling to initAsync.

Since initAsync() is invoked with the void operator, any exceptions thrown during initialization (e.g., from getUserInfo or getIdentityJsonFormat) may fail silently without user feedback. Consider wrapping the body in a try-catch block to handle errors gracefully and show appropriate error messages.

Example pattern:

private async initAsync(): Promise<void> {
  try {
    this.getErrorLabels();
    await this.getUserInfo(this.preRegId);
    // ... rest of initialization
  } catch (error) {
    console.error('Initialization failed:', error);
    this.showErrorMessage(error);
  }
}
pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (1)

380-447: Refactor repetitive string transformations into a helper function.

The label transformation logic is duplicated seven times, with 28 identical NOSONAR comments. This addresses SonarQube warnings but creates maintenance burden.

Extract the transformation into a reusable helper method:

Add this helper method to the class:

/**
 * Transforms a label array into a formatted string.
 * Uses replace() with global regex instead of replaceAll() for Angular 7 browser support.
 */
private formatLabelsForDisplay(labels: string[]): string {
  return JSON.stringify(labels)
    .replace(/\[/g, '')
    .replace(/,/g, ' / ')
    .replace(/"/g, ' ')
    .replace(/]/g, '');
}

Then refactor the prepareAckDataForUI method to use it:

-      this.ackDataItem["preRegIdLabels"] = JSON.stringify(
-        preRegIdLabels
-      )
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/\[/g, "")
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/,/g, " / ")
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/"/g, " ")
-        // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
-        .replace(/]/g, "");
+      this.ackDataItem["preRegIdLabels"] = this.formatLabelsForDisplay(preRegIdLabels);

Apply the same pattern to all seven label fields: appDateLabels, contactPhoneLabels, labelNames, nameValues, labelRegCntrs, and regCntrNames.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a2fd9b and 2fa6a31.

📒 Files selected for processing (5)
  • pre-registration-ui/src/app/auth/login/login.component.ts (3 hunks)
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (3 hunks)
  • pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-pre-registration-ui / npm-build
🔇 Additional comments (9)
pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (2)

142-161: Async initialization logic is well-structured.

The extraction of async initialization into a dedicated method improves code organization and makes ngOnInit synchronous, which aligns with Angular best practices.

Note: Ensure the caller handles errors appropriately (see comment on lines 130-140).


950-952: Extension validation properly fixed.

The refactored regex construction correctly addresses the previous review concern:

  • Extensions are split, trimmed, and filtered to remove empty values
  • Regex is anchored (^...$) to match exact extensions rather than substrings
  • Returns null when no extensions exist, preventing the match-all behavior
  • Null guard on line 962 ensures safe testing

Based on past review comments.

Also applies to: 962-962

pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1)

78-89: Race condition fix looks good.

The previous review flagged that initAsync could run before preRegId was populated. The current implementation correctly addresses this by calling initAsync() only after preRegId is assigned in both the multiappointment and route params branches.

pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1)

116-128: LGTM: defensive guards properly implemented.

The early return at line 117 prevents backend calls with an undefined registrationCenter, and the comprehensive guard at lines 124-126 prevents the "Cannot read property 'split' of null/undefined" error previously flagged.

pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2)

75-81: Memory leak fix confirmed.

The subscription is now properly tracked in the subscriptions array, resolving the memory leak flagged in the previous review.


87-108: Well-structured async initialization flow.

The refactor successfully extracts async work from ngOnInit() into a dedicated method. The sequential awaits ensure proper initialization order, and the spinner state is correctly managed.

pre-registration-ui/src/app/auth/login/login.component.ts (3)

78-79: Guarding navigation when langCode is available

Thanks for adding the langCode truthiness check before building the dashboard URL; this removes the null-segment navigation gap highlighted earlier.


157-158: Safer NaN guard in OTP reload flow

The switch to Number.isNaN avoids the coercion pitfalls of the global isNaN, which keeps the expiry logic honest.


311-312: Consistent NaN protection during timer setup

Using Number.isNaN here keeps the timer’s defaulting logic aligned with the reload path—nice consistency improvement.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (1)

71-83: Add error handling for fire-and-forget async initialization.

The initAsync() call is fire-and-forget without error handling. If initialization fails, the error becomes an unhandled promise rejection and dataLoaded remains false indefinitely, leaving the UI in a loading state.

Attach explicit rejection handling:

   ngOnInit() {
     this.activatedRoute.params.subscribe((param) => {
       this.preRegId = param["appId"];
     });
     if (this.ltrLangs.includes(this.userPrefLanguage)) {
       this.userPrefLanguageDir = "ltr";
     } else {
       this.userPrefLanguageDir = "rtl";
     }
     this.getLanguageLabels();
     // kick off async initialisation (we ignore the returned Promise on purpose)
-    void this.initAsync();
+    this.initAsync().catch(err => {
+      console.error('Initialization failed:', err);
+      this.dataLoaded = true; // allow UI to render in error state
+    });
   }
♻️ Duplicate comments (1)
pre-registration-ui/src/app/auth/login/login.component.ts (1)

89-99: Config loading race condition still exists.

The previous review identified that loadConfigs() doesn't wait for the config to be set before handleBrowserReload() runs. While error handling was added, the underlying race condition remains: handleBrowserReload() will execute with potentially empty/default config values.

Based on past review comments

The fix from the previous review was not fully applied. Change loadConfigs() to return a Promise:

- loadConfigs(): void {
-   this.dataService.getConfig().subscribe((response) => {
+ async loadConfigs(): Promise<void> {
+   return new Promise((resolve) => {
+     this.dataService.getConfig().subscribe((response) => {
       this.configService.setConfig(response);
       this.appVersion = this.configService.getConfigByKey(
         "preregistration.ui.version"
       );
       // ... rest of config setup
       this.showSpinner = false;
+      resolve();
     });
+   });
  }

This ensures await this.loadConfigs() on line 94 actually waits for the config to be loaded.

🧹 Nitpick comments (2)
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (1)

85-162: LGTM with a suggestion: Consider adding early-exit validation.

The async initialization sequence is well-structured with proper await chains.

Consider adding validation after critical steps to fail fast:

   private async initAsync(): Promise<void> {
     await this.getIdentityJsonFormat();
+    if (!this.identityData || this.identityData.length === 0) {
+      throw new Error('Failed to load identity data');
+    }
     // await this.getResidentDetails();
     // await this.getGenderDetails();
     await this.getDynamicFieldValues(null);
     await this.getUserInfo();
+    if (!this.user || !this.user.request) {
+      throw new Error('Failed to load user info');
+    }
     await this.getUserFiles();
pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1)

78-97: Add error handling for fire-and-forget async initialization.

The initAsync() calls lack error handling. Consider adding rejection handling for better error resilience.

       }
-      void this.initAsync(); // start only after preRegId is known
+      this.initAsync().catch(err => {
+        console.error('Initialization failed:', err);
+        this.pageLoaded = true;
+      }); // start only after preRegId is known
     } else {
       this.activatedRoute.params.subscribe((param) => {
         this.preRegId = [param['appId']];
-        void this.initAsync(); // start only after param is known
+        this.initAsync().catch(err => {
+          console.error('Initialization failed:', err);
+          this.pageLoaded = true;
+        }); // start only after param is known
       });
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2fa6a31 and 5b43197.

📒 Files selected for processing (6)
  • pre-registration-ui/src/app/auth/login/login.component.ts (4 hunks)
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (4 hunks)
  • pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-pre-registration-ui / npm-build
🔇 Additional comments (12)
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (1)

386-386: NOSONAR comment is appropriate.

The justification for using replace() with a global regex instead of replaceAll() for Angular 7 browser compatibility is valid.

pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (2)

194-234: Initialization sequence is well-orchestrated.

The initialization flow properly sequences config loading, language setup, form initialization, and conditional consent dialogs. The synchronous lifecycle hook pattern is correctly implemented.


298-319: NOSONAR comments are justified.

All NOSONAR comments correctly explain why replace() with global regex is used instead of String.replaceAll() for Angular 7 browser compatibility.

pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (2)

89-118: Previous race condition concerns addressed.

The initialization flow now ensures preRegId and registrationCenter are set before calling initAsync(). The JSON.parse is properly guarded with try-catch.

Based on past review comments


120-134: Good defensive programming with registrationCenter guard.

The early return when registrationCenter is missing prevents the race condition identified in previous reviews. The lunchEndTime null guard also properly prevents the "Cannot read property 'split' of null/undefined" error.

Based on past review comments

pre-registration-ui/src/app/auth/login/login.component.ts (2)

75-87: Good guard for langCode before navigation.

The addition of && langCode in the navigation guard prevents navigating with null/empty segments, addressing the previous review concern.

Based on past review comments


157-159: Correct usage of Number.isNaN().

Using Number.isNaN() instead of isNaN() is the proper approach, as it doesn't perform type coercion and gives more reliable results for numeric validation.

Also applies to: 310-328

pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (2)

78-98: Previous race condition and JSON.parse concerns properly addressed.

The initialization flow now ensures preRegId is set before initAsync() is called, and the JSON.parse is wrapped in proper error handling with fallback to empty array.

Based on past review comments


105-112: Good NaN validation with fallback.

The addition of Number.isNaN() check with a sensible default value (1) prevents the filter comparison issues identified in previous reviews. The error logging helps with debugging.

Based on past review comments

pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (3)

62-92: All previous concerns properly addressed.

The initialization properly handles:

  • JSON.parse errors with try-catch and fallback
  • Error handling for the fire-and-forget async call with spinner state cleanup
  • Subscription tracking for I18N language files

Excellent implementation of the recommendations from previous reviews.

Based on past review comments


94-115: Well-structured async initialization sequence.

The initialization properly sequences user info loading, registration center details, label fetching, and notification setup. The logic is clear and maintainable.


390-454: NOSONAR comments are appropriate and consistent.

All NOSONAR comments correctly explain the browser compatibility constraint requiring replace() with global regex instead of String.replaceAll().

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (1)

71-83: Add error handling for fire-and-forget async initialization.

The initAsync() call lacks error handling. If initialization fails during data fetching or processing, the component will remain in an incomplete state (dataLoaded stays false) without providing user feedback.

Add rejection handling:

  ngOnInit() {
    this.activatedRoute.params.subscribe((param) => {
      this.preRegId = param["appId"];
    });
    if (this.ltrLangs.includes(this.userPrefLanguage)) {
      this.userPrefLanguageDir = "ltr";
    } else {
      this.userPrefLanguageDir = "rtl";
    }
    this.getLanguageLabels();
    // kick off async initialisation (we ignore the returned Promise on purpose)
-   void this.initAsync();
+   this.initAsync().catch(err => {
+     console.error('Initialization failed:', err);
+     this.dataLoaded = false;
+     this.showErrorMessage(err);
+   });
  }
♻️ Duplicate comments (1)
pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (1)

190-193: Add error handling for fire-and-forget async initialization.

Similar to previous reviews, the initAsync() call lacks error handling. If initialization fails during form setup or config loading, the component will remain in an incomplete state (primaryuserForm stays false) without user feedback.

Add rejection handling:

  ngOnInit(): void {
-   void this.initAsync();
+   this.initAsync().catch(err => {
+     console.error('Initialization failed:', err);
+     this.primaryuserForm = false;
+     this.showErrorMessage(err);
+   });
  }
🧹 Nitpick comments (4)
pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (2)

78-98: Consider consistent error handling for both initialization paths.

The multiappointment path has error handling for initAsync() (line 71), but the route-param path does not (line 95). While this may be acceptable in "Chill" mode, consistency would improve maintainability.

Apply this diff for consistency:

    } else {
      this.activatedRoute.params.subscribe((param) => {
        this.preRegId = [param['appId']];
-       void this.initAsync(); // start only after param is known
+       this.initAsync().catch(error => {
+         console.error('Initialization failed:', error);
+         this.pageLoaded = true;
+       });
      });
    }

115-133: Remove duplicate console.log statement.

Line 119 duplicates the logging statement from line 118.

Apply this diff:

      //get all location types from db
      this.allLocationTypes = response[appConstants.RESPONSE]['locationHierarchyLevels'];
      console.log(this.allLocationTypes);
-     console.log(this.allLocationTypes);
      //get the recommended loc hierachy code to which booking centers are mapped
pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2)

62-92: Consider consistent error handling for both initialization paths.

The multiappointment path includes error handling for initAsync() (lines 71-74), but the route-param path does not (line 79). For consistency and robustness, both paths should handle initialization failures.

Apply this diff:

    } else {
      // wait for appId from route before starting async init
      this.activatedRoute.params.subscribe((param) => {
        this.preRegIds = [param['appId']];
-       void this.initAsync();
+       this.initAsync().catch(error => {
+         console.error('Initialization failed:', error);
+         this.showSpinner = false;
+       });
      });
    }

387-454: Consider consolidating repetitive NOSONAR comments.

While the NOSONAR justification is valid, repeating the identical comment on every .replace() call in each chain is verbose. Consider placing a single comment before each chain.

Example refactoring for one chain:

+     // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
      this.ackDataItem["preRegIdLabels"] = JSON.stringify(
        preRegIdLabels
      )
-       // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
        .replace(/\[/g, "")
-       // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
        .replace(/,/g, " / ")
-       // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
        .replace(/"/g, " ")
-       // NOSONAR: Cannot use String.replaceAll() due to Angular 7 browser support; using replace + global regex instead
        .replace(/]/g, "");

Apply similar refactoring to the other chains (appDateLabels, contactPhoneLabels, etc.).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b43197 and 369389b.

📒 Files selected for processing (8)
  • pre-registration-ui/src/app/auth/login/login.component.ts (4 hunks)
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (4 hunks)
  • pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (3 hunks)
  • pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts
  • pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts
  • pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts
  • pre-registration-ui/src/app/auth/login/login.component.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-pre-registration-ui / npm-build
🔇 Additional comments (6)
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (2)

85-162: LGTM!

The async initialization logic is well-structured with proper sequencing of asynchronous operations. The method correctly orchestrates data fetching and sets the dataLoaded flag upon completion.


385-397: LGTM!

The NOSONAR comment appropriately justifies the use of replace() with global regex instead of replaceAll() for Angular 7 browser compatibility.

pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (2)

194-234: LGTM!

The async initialization method properly orchestrates the component setup sequence, including language initialization, form setup, and conditional consent flow.


298-320: LGTM!

The NOSONAR comments consistently and appropriately justify the use of replace() with global regex patterns for Angular 7 compatibility throughout the date format localization logic.

pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1)

105-112: LGTM!

The NaN validation properly addresses the previous review concern by safely parsing the config value, validating with Number.isNaN(), and providing a sensible fallback. This prevents downstream comparison issues.

pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (1)

94-115: LGTM!

The async initialization properly orchestrates user info fetching, registration center details, label loading, and notification setup. The sequential processing ensures data is available when needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1)

94-126: Harden JSON.parse and resolve race condition with registrationCenter.

Two reliability concerns:

  1. Line 95: JSON.parse will throw if localStorage contains malformed JSON.
  2. Lines 101-103 vs 125: The queryParams subscription sets registrationCenter asynchronously, but initAsync continues synchronously and calls getSlotsforCenter(this.registrationCenter) at line 125. If the subscription hasn't fired yet, registrationCenter will be undefined.

Apply this diff to fix both issues:

   private async initAsync(): Promise<void> {
     if (this.router.url.includes("multiappointment")) {
-      this.preRegId = [...JSON.parse(localStorage.getItem("multiappointment"))];
+      const stored = localStorage.getItem("multiappointment");
+      try {
+        this.preRegId = stored ? [...JSON.parse(stored)] : [];
+      } catch {
+        this.preRegId = [];
+      }
     } else {
       this.activatedRoute.params.subscribe((param) => {
         this.preRegId = [param["appId"]];
       });
     }
-    this.activatedRoute.queryParams.subscribe((param) => {
-      this.registrationCenter = param["regCenter"];
-    });
+    // Wait for registrationCenter before proceeding
+    await new Promise<void>(resolve => {
+      this.activatedRoute.queryParams.subscribe((param) => {
+        this.registrationCenter = param["regCenter"];
+        resolve();
+      });
+    });
+    if (!this.registrationCenter) {
+      console.error('registrationCenter is missing');
+      this.spinner = false;
+      return;
+    }
     this.name = this.configService.getConfigByKey(
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (1)

71-83: Add error handling for fire-and-forget async initialization.

The initAsync() call at line 82 has no rejection handling. If initialization fails (e.g., network error, API failure), the component will be in an incomplete state without user feedback, and dataLoaded will never become true.

Apply this diff:

     this.getLanguageLabels();
-    // kick off async initialisation (we ignore the returned Promise on purpose)
-    void this.initAsync();
+    this.initAsync().catch(err => {
+      console.error('Initialization failed:', err);
+      this.dataLoaded = false;
+      this.showErrorMessage(err);
+    });
   }
♻️ Duplicate comments (1)
pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (1)

89-91: Add error handling for fire-and-forget async initialization.

The fire-and-forget call to initAsync() lacks rejection handling. If initialization fails, users won't see an error and the spinner may remain visible indefinitely.

Apply this diff:

   ngOnInit(): void {
-    void this.initAsync();
+    this.initAsync().catch(err => {
+      console.error('Initialization failed:', err);
+      this.spinner = false;
+      this.showErrorMessage(err);
+    });
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 369389b and dcc8ae8.

📒 Files selected for processing (8)
  • pre-registration-ui/src/app/auth/login/login.component.ts (5 hunks)
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (4 hunks)
  • pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (3 hunks)
  • pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts
  • pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-pre-registration-ui / npm-build
🔇 Additional comments (2)
pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (1)

190-237: LGTM! Correct error handling pattern.

The implementation properly handles initialization errors by catching rejections, logging them, and setting primaryuserForm to false to prevent form rendering in an incomplete state. The initAsync() method correctly orchestrates the sequential initialization steps with await.

pre-registration-ui/src/app/auth/login/login.component.ts (1)

76-138: LGTM! Initialization refactoring addresses previous concerns.

The implementation correctly:

  1. Guards navigation (line 79): Only navigates when langCode is truthy, preventing null route segments.
  2. Handles errors (line 83): Catches initialization failures and logs them.
  3. Awaits config (lines 111-138): loadConfigs() now properly awaits the config response using pipe(first()).toPromise(), ensuring handleBrowserReload() runs after the config is applied.
  4. Uses Number.isNaN() (lines 157, 310): Correctly checks for NaN values.

The refactoring successfully resolves the race condition where handleBrowserReload() would execute before config was available.

…ssues in multiple components

Signed-off-by: Youssef MAHTAT <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts (2)

145-173: Promise never settles on error; track subscription

getIdentityJsonFormat() awaits an Http call but never resolves/rejects on error, risking hung flows; also the subscription isn't added to this.subscriptions.

   async getIdentityJsonFormat() {
     return new Promise((resolve, reject) => {
-      this.dataStorageService.getIdentityJson().subscribe(
+      const subs = this.dataStorageService.getIdentityJson().subscribe(
         (response) => {
@@
           resolve(true);
         },
         (error) => {
           this.showErrorMessage(error);
+          resolve(false); // or: reject(error)
         }
       );
+      this.subscriptions.push(subs);
     });
   }

374-376: Fix NPE: identityObj.langCode read when identityObj is falsy

Else-if references identityObj.langCode but identityObj is falsy by definition here; this will throw.

-        } else if (identityObj.langCode) {
-          dataAvailableLanguages = [identityObj.langCode];
+        } else if (preregData && preregData.langCode) {
+          dataAvailableLanguages = [preregData.langCode];
         }
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (1)

85-163: Awaited Promises can hang on error (no resolve/reject)

getIdentityJsonFormat, getDynamicFieldValues, getUserInfo, and getDocumentCategories never settle on error → initAsync can hang indefinitely.

   async getIdentityJsonFormat() {
     return new Promise((resolve, reject) => {
-      this.dataStorageService.getIdentityJson().subscribe((response) => {
+      const subs = this.dataStorageService.getIdentityJson().subscribe((response) => {
@@
-      },
-      (error) => {
-        this.showErrorMessage(error);
-      });
+      }, (error) => {
+        this.showErrorMessage(error);
+        resolve(false); // or reject(error)
+      });
+      this.subscriptions.push(subs);
     });
   }
@@
   getUserInfo() {
     return new Promise((resolve) => {
-      this.dataStorageService
+      const subs = this.dataStorageService
         .getUser(this.preRegId.toString())
-        .subscribe((response) => {
+        .subscribe((response) => {
@@
-        (error) => {
-          this.showErrorMessage(error);
-        });
+        (error) => {
+          this.showErrorMessage(error);
+          resolve(false);
+        });
+      this.subscriptions.push(subs);
     });
   }
@@
   getDocumentCategories() {
     const applicantcode = localStorage.getItem("applicantType");
     return new Promise((resolve) => {
-      this.dataStorageService
+      const subs = this.dataStorageService
         .getDocumentCategories(applicantcode)
-        .subscribe((response) => {
+        .subscribe((response) => {
@@
-        (error) => {
-          this.showErrorMessage(error);
-        });
+        (error) => {
+          this.showErrorMessage(error);
+          resolve(false);
+        });
+      this.subscriptions.push(subs);
     });
   }
@@
   async getDynamicFieldValues(pageNo) {
@@
-        (error) => {
-          this.showErrorMessage(error);
-        })
+        (error) => {
+          this.showErrorMessage(error);
+          resolve(false);
+        })
pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (2)

197-237: Ensure awaited helpers settle on error to avoid hanging init

initAsync awaits methods (getIdentityJsonFormat, getConsentMessage, getDynamicFieldValues, setFormControlValues path uses more) whose error paths don’t resolve/reject.

-    await this.getIdentityJsonFormat();
+    const idOk = await this.getIdentityJsonFormat();
+    if (idOk === false) { this.primaryuserForm = false; return; }
@@
-    await this.getConsentMessage();
+    const consentOk = await this.getConsentMessage();
+    if (consentOk === false) { /* optional: proceed without consent */ }

And update the helpers:

   async getIdentityJsonFormat() {
     return new Promise((resolve, reject) => {
-      this.dataStorageService.getIdentityJson().subscribe(
+      const subs = this.dataStorageService.getIdentityJson().subscribe(
@@
-        (error) => {
-          this.showErrorMessage(error);
-        }
+        (error) => {
+          this.showErrorMessage(error);
+          resolve(false); // or reject(error)
+        }
       );
+      this.subscriptions.push(subs);
     });
   }
@@
   private getConsentMessage() {
     return new Promise((resolve, reject) => {
-      this.subscriptions.push(
-        this.dataStorageService.getGuidelineTemplate("consent").subscribe(
+      this.subscriptions.push(
+        this.dataStorageService.getGuidelineTemplate("consent").subscribe(
           (response) => {
@@
-          (error) => {
-            this.isConsentMessage = false;
-            this.showErrorMessage(error);
-          }
+          (error) => {
+            this.isConsentMessage = false;
+            this.showErrorMessage(error);
+            resolve(false); // or reject(error)
+          }
         )
       );
     });
   }
@@
   async getDynamicFieldValues(pageNo) {
@@
-        (error) => {
-          this.showErrorMessage(error);
-        })
+        (error) => {
+          this.showErrorMessage(error);
+          resolve(false);
+        })

388-408: Consent message fetch should resolve on error

Currently the Promise can hang when the HTTP call errors; settle the Promise as shown above to keep initAsync from deadlocking.

pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (2)

163-181: Settle Promise on error and track subscription

getIdentityJsonFormat never resolves on error; awaiting it can hang; also track the subscription.

   async getIdentityJsonFormat() {
     return new Promise((resolve) => {
-      this.dataStorageService.getIdentityJson().subscribe((response) => {
+      const subs = this.dataStorageService.getIdentityJson().subscribe((response) => {
@@
-      },
-      (error) => {
-        this.showErrorMessage(error);
-      });
+      }, (error) => {
+        this.showErrorMessage(error);
+        resolve(false);
+      });
+      this.subscriptions.push(subs);
     });
   }

235-262: Same issue for getUserInfo: settle on error

Awaiters of getUserInfo() may hang on error. Resolve or reject in the error path.

   getUserInfo() {
     return new Promise((resolve) => {
-      this.dataStorageService
+      const subs = this.dataStorageService
         .getUser(this.preRegId.toString())
         .subscribe((response) => {
@@
-        (error) => {
-          this.showErrorMessage(error);
-        });
+        (error) => {
+          this.showErrorMessage(error);
+          resolve(false);
+        });
+      this.subscriptions.push(subs);
     });
   }
pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (1)

120-205: Fix race: resolve getUserInfo only after all async fetches complete

The Promise resolves when the last index iterates, not when all async tasks finish; data may be incomplete.

-  getUserInfo(preRegIds: string[]) {
-    return new Promise((resolve) => {
-      preRegIds.forEach(async (prid: any, index) => {
-        await this.getUserDetails(prid).then(async (user) => {
-          // ... inner async work
-        });
-        if (index === preRegIds.length - 1) {
-          resolve(true);
-        }
-      });
-    });
-  }
+  getUserInfo(preRegIds: string[]) {
+    return Promise.all(
+      preRegIds.map(async (prid: any) => {
+        const user = await this.getUserDetails(prid);
+        // ... do appointment + push usersInfoArr/applicantContactDetails
+      })
+    ).then(() => true).catch(() => false);
+  }
♻️ Duplicate comments (2)
pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (2)

89-104: Start async init only after both preRegId and registrationCenter are known; add rejection handling

Current flow can run before queryParams arrive, causing undefined registrationCenter in downstream calls; also missing JSON.parse guard and .catch.

-  ngOnInit(): void {
-    void this.initAsync();
-  }
+  ngOnInit(): void {
+    if (this.router.url.includes('multiappointment')) {
+      const stored = localStorage.getItem('multiappointment');
+      try {
+        this.preRegId = stored ? [...JSON.parse(stored)] : [];
+      } catch {
+        this.preRegId = [];
+      }
+    } else {
+      this.activatedRoute.params.subscribe((param) => {
+        this.preRegId = [param['appId']];
+        if (this.registrationCenter) { this.initAsync().catch(e => { console.error('Initialization failed:', e); this.spinner = false; }); }
+      });
+    }
+    this.activatedRoute.queryParams.subscribe((param) => {
+      this.registrationCenter = param['regCenter'];
+      if (this.preRegId && this.preRegId.length) { this.initAsync().catch(e => { console.error('Initialization failed:', e); this.spinner = false; }); }
+    });
+  }

121-126: Null‑guard lunchEndTime and gate slot loading

Avoid split() on null/undefined and don’t call backend with undefined center id.

-    if (this.temp[0]) {
-      this.registrationCenterLunchTime =
-        this.temp[0].registrationCenter.lunchEndTime.split(":");
-    }
-    this.getSlotsforCenter(this.registrationCenter);
+    if (this.temp && this.temp[0] && this.temp[0].registrationCenter && this.temp[0].registrationCenter.lunchEndTime) {
+      this.registrationCenterLunchTime = this.temp[0].registrationCenter.lunchEndTime.split(':');
+    } else {
+      this.registrationCenterLunchTime = [];
+    }
+    if (this.registrationCenter) {
+      this.getSlotsforCenter(this.registrationCenter);
+    }
🧹 Nitpick comments (6)
pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts (1)

108-143: Guard fire‑and‑forget init and declare return type

Make ngOnInit explicit and avoid silent failures by catching async errors.

-  ngOnInit() {
+  ngOnInit(): void {
@@
-    this.getIdentityJsonFormat();
+    void this.getIdentityJsonFormat().catch(err => {
+      this.loggerService.error('getIdentityJsonFormat failed', err);
+    });
pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (2)

71-83: Add rejection handling to fire‑and‑forget init

Prevent unhandled promise rejections and surface failures.

-    // kick off async initialisation (we ignore the returned Promise on purpose)
-    void this.initAsync();
+    // kick off async initialisation
+    this.initAsync().catch(err => {
+      console.error('Preview init failed:', err);
+    });

188-196: Harden JSON.parse for localStorage read

Malformed data will throw and break init.

-        JSON.parse(localStorage.getItem(appConstants.LANGUAGE_CODE_VALUES)).forEach(
+        const raw = localStorage.getItem(appConstants.LANGUAGE_CODE_VALUES);
+        let parsed: any[] = [];
+        try { parsed = raw ? JSON.parse(raw) : []; } catch { parsed = []; }
+        parsed.forEach(
pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (2)

331-339: Guard JSON.parse on session data

localStorage corruption will crash initialization.

-      this.dataCaptureLanguages = JSON.parse(
-        localStorage.getItem(appConstants.DATA_CAPTURE_LANGUAGES)
-      );
-      this.dataCaptureLanguagesLabels = JSON.parse(
-        localStorage.getItem(appConstants.DATA_CAPTURE_LANGUAGE_LABELS)
-      );
+      try {
+        this.dataCaptureLanguages = JSON.parse(localStorage.getItem(appConstants.DATA_CAPTURE_LANGUAGES) || '[]');
+      } catch { this.dataCaptureLanguages = []; }
+      try {
+        this.dataCaptureLanguagesLabels = JSON.parse(localStorage.getItem(appConstants.DATA_CAPTURE_LANGUAGE_LABELS) || '[]');
+      } catch { this.dataCaptureLanguagesLabels = []; }

300-323: NOSONAR comments are fine; consider consolidating repeated replace

Not blocking, but you can extract a helper to DRY the repeated global replace blocks.

pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (1)

130-141: Add rejection handling to async init

Catch and log failures to avoid silent init errors.

-    // async part moved to helper; fire and forget
-    void this.initAsync();
+    // async part moved to helper
+    this.initAsync().catch(err => {
+      console.error('FileUpload init failed:', err);
+      this.dataLoaded = true; // optional: unblock UI
+    });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dcc8ae8 and a985033.

📒 Files selected for processing (8)
  • pre-registration-ui/src/app/auth/login/login.component.ts (5 hunks)
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts (1 hunks)
  • pre-registration-ui/src/app/feature/booking/time-selection/time-selection.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/dashboard/dashboard/dashboard.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (4 hunks)
  • pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (3 hunks)
  • pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2 hunks)
  • pre-registration-ui/src/app/feature/summary/preview/preview.component.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pre-registration-ui/src/app/feature/booking/center-selection/center-selection.component.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: pre-registration-ui / build-dockers
🔇 Additional comments (6)
pre-registration-ui/src/app/feature/demographic/demographic/demographic.component.ts (1)

190-195: Good: ngOnInit now logs and disables form on init failure

The catch handler prevents silent failures and sets a safe state.

pre-registration-ui/src/app/feature/file-upload/file-upload/file-upload.component.ts (1)

950-963: LGTM: extension regex is now anchored and guarded

The new construction prevents substring matches and handles empty configuration safely.

pre-registration-ui/src/app/feature/summary/acknowledgement/acknowledgement.component.ts (2)

62-75: Good: guarded parse and error‑handled init

The try/catch around JSON.parse and the .catch on initAsync improve resilience.


85-91: Good: track language-file subscription for cleanup

Subscription is now pushed for ngOnDestroy.

pre-registration-ui/src/app/auth/login/login.component.ts (2)

76-87: Good: guarded redirect and centralized async init with catch

The added langCode guard and error handling reduce navigation and init edge cases.


111-138: Config loading awaits first emission; OTP reload logic now consistent

Use of pipe(first()).toPromise() ensures config is set before proceeding; NaN checks modernized. Looks good.

@mayuradesh mayuradesh merged commit d9656cc into mosip:develop Nov 13, 2025
8 checks passed
ymahtat-dev added a commit to ymahtat-dev/pre-registration-ui that referenced this pull request Nov 14, 2025
…ssues in multiple components (mosip#224)

Signed-off-by: Youssef MAHTAT <[email protected]>
ymahtat-dev added a commit to ymahtat-dev/pre-registration-ui that referenced this pull request Nov 14, 2025
…ssues in multiple components (mosip#224)

Signed-off-by: Youssef MAHTAT <[email protected]>
mayuradesh pushed a commit that referenced this pull request Nov 19, 2025
…ultiple components (#225)

* MOSIP-25201 - Fix accessibility and SonarQube reliability issues in multiple components (#198)

Signed-off-by: Youssef MAHTAT <[email protected]>

* MOSIP-25201 - Fix (round 2) accessibility and SonarQube reliability issues in multiple components (#209)

Signed-off-by: Youssef MAHTAT <[email protected]>

* MOSIP-25201 - Fix (round 3) accessibility and SonarQube reliability issues in multiple components (#224)

Signed-off-by: Youssef MAHTAT <[email protected]>

* MOSIP-25201 - Fix (round 4) remove NOSONAR comments (#226)

Signed-off-by: Youssef MAHTAT <[email protected]>

---------

Signed-off-by: Youssef MAHTAT <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants