Skip to content

AMM-118: Add time-based account lockout with auto-unlock#122

Open
varundeepsaini wants to merge 1 commit intoPSMRI:mainfrom
varundeepsaini:AMM-118-time-based-account-lockout
Open

AMM-118: Add time-based account lockout with auto-unlock#122
varundeepsaini wants to merge 1 commit intoPSMRI:mainfrom
varundeepsaini:AMM-118-time-based-account-lockout

Conversation

@varundeepsaini
Copy link

@varundeepsaini varundeepsaini commented Jan 7, 2026

Part of PSMRI/AMRIT#118

📋 Description

Adds admin UI functionality to view and manage user account lock status. Admins can now check if an account is locked due to failed login attempts and manually unlock it before the 24-hour auto-unlock period.

✅ Type of Change

  • New feature (non-breaking change which adds functionality)

ℹ️ Additional Information

Changes:

  • Added checkUserLockStatus() method to display lock details
  • Added unlockUserAccount() method for manual unlock
  • Added service methods for API calls
  • Added environment URLs for new endpoints

Testing:

  • Tested lock status shows correct information for locked/unlocked accounts
  • Tested unlock button successfully unlocks accounts
  • Tested error handling for edge cases

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Add Unlock action with confirmation, success/error alerts, and list refresh.
    • Add inline "Check lock status" action showing lock, expiry, and failed-attempts info.
    • Adjust button spacing in the employee management list for consistent layout.
  • Bug Fixes

    • Show server-provided error messages in login flows when previously unhandled.

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds UI controls to check and unlock user accounts, implements component methods to call new service endpoints, adds corresponding service methods and environment URLs, and surfaces fallback login error alerts.

Changes

Cohort / File(s) Summary
Employee master template
src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html
Added inline spacing adjustments for Activate/Deactivate buttons, added "Unlock" mat-raised-button that calls unlockUserAccount(...), and added an icon button to call checkUserLockStatus(...) with a tooltip.
Employee master component
src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts
Added public methods unlockUserAccount(userID: number, userName: string) and checkUserLockStatus(userID: number, userName: string) to call services, parse responses, show alerts, and refresh the list/search.
Employee master service
src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts
Added getUserLockStatus(userId: number): Observable<any> and unlockUserAccount(userId: number): Observable<any> which POST to new environment endpoints.
Environment configs
src/environments/environment.local.ts, src/environments/environment.test.ts, src/environments/environment.prod.ts
Added getUserLockStatusUrl and unlockUserAccountUrl entries (constructed from commonBaseURL + 'user/...') to expose API endpoints.
Login component
src/app/user-login/login/login.component.ts
Added fallback branches to surface response.errorMessage with error alerts for Super Admin and non-Super Admin login flows.

Sequence Diagram(s)

sequenceDiagram
    actor Admin
    participant UI as EmployeeMasterComponent (UI)
    participant Svc as EmployeeMasterNewServices
    participant API as User Lock API

    Admin->>UI: Click "Check lock status" (userID, userName)
    UI->>Svc: getUserLockStatus(userID)
    Svc->>API: POST /user/getUserLockStatus { userId }
    API-->>Svc: 200 { data: "...", statusCode: 200 }
    Svc-->>UI: return response
    UI-->>Admin: show lock-status alert
Loading
sequenceDiagram
    actor Admin
    participant UI as EmployeeMasterComponent (UI)
    participant Svc as EmployeeMasterNewServices
    participant API as User Lock API

    Admin->>UI: Click "Unlock" (confirm)
    UI->>Svc: unlockUserAccount(userID)
    Svc->>API: POST /user/unlockUserAccount { userId }
    API-->>Svc: 200 { statusCode: 200, message }
    Svc-->>UI: return response
    UI-->>Admin: show success alert, refresh list
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 I hopped to the panel and tapped with a grin,
A peek at the lock and a gentle spin.
A click to unbind, a message set free,
Back to bright logins — hooray from me! 🥕

🚥 Pre-merge checks | ✅ 3
✅ 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 accurately describes the main feature added: time-based account lockout management with auto-unlock capabilities, which is reflected in the new checkUserLockStatus and unlockUserAccount methods.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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: 1

🤖 Fix all issues with AI agents
In
@src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts:
- Around line 1579-1619: The checkUserLockStatus method currently calls
JSON.parse(response.data) without guarding against malformed JSON; wrap the
parse in a try-catch around JSON.parse(response.data) (inside
checkUserLockStatus) to catch SyntaxError and handle it by logging the error and
showing a user-friendly dialogService.alert (e.g., 'Invalid lock status data
received' or fallback to the error path), and ensure subsequent logic only runs
when parsing succeeds (return early from the subscription error branch on parse
failure).
🧹 Nitpick comments (2)
src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html (2)

85-94: Inline styles for spacing could be consolidated.

The margin-right: 5px; styling is repeated across multiple buttons. While inline styles work, consider extracting these to a CSS class for better maintainability.

♻️ Optional refactor to use CSS class

In your component CSS file, add:

.action-button-spacing {
  margin-right: 5px;
}

Then apply to buttons:

-              style="margin-right: 5px;"
+              class="action-button-spacing"

95-104: Consider button color semantics for the Unlock action.

The Unlock button uses color="warn" (typically red), which might suggest a destructive or dangerous action. Since unlocking is a positive administrative action to restore access, consider using color="accent" (green) to align with the Activate button's semantics.

♻️ Suggested color adjustment
               <button
                 *ngIf="element.deleted === true"
                 mat-raised-button
-                color="warn"
+                color="accent"
                 matTooltip="Unlock account locked due to failed login attempts"
                 (click)="unlockUserAccount(element.userID, element.userName)"
                 style="margin-right: 5px;"
               >
                 Unlock
               </button>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66872f4 and f86014d.

📒 Files selected for processing (7)
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts
  • src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts
  • src/app/user-login/login/login.component.ts
  • src/environments/environment.local.ts
  • src/environments/environment.prod.ts
  • src/environments/environment.test.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-25T11:01:28.621Z
Learnt from: devikasuresh20
Repo: PSMRI/ADMIN-UI PR: 50
File: src/app/app-provider-admin/provider-admin/activities/work-location-mapping/work-location-mapping.component.ts:1981-1981
Timestamp: 2024-11-25T11:01:28.621Z
Learning: In the `filterComponentList` method of `WorkLocationMappingComponent` in `src/app/app-provider-admin/provider-admin/activities/work-location-mapping/work-location-mapping.component.ts`, retain the manual filtering implementation as it provides better control over the filtering logic.

Applied to files:

  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts
🧬 Code graph analysis (1)
src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts (3)
src/environments/environment.local.ts (1)
  • environment (40-513)
src/environments/environment.prod.ts (1)
  • environment (40-514)
src/environments/environment.test.ts (1)
  • environment (36-506)
🔇 Additional comments (9)
src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html (2)

105-114: LGTM - Consistent spacing added to Deactivate button.

The margin-right styling maintains visual consistency with other action buttons.


115-121: LGTM - Check Lock Status button is well implemented.

The button provides admins with proactive visibility into user lock status. The tooltip clearly describes the action, and the info icon is semantically appropriate.

src/environments/environment.test.ts (1)

225-229: LGTM - New API endpoints are well organized.

The new User Account Lock Management endpoints are properly defined with clear comments and follow the existing URL pattern conventions. The placement near other user-related endpoints is logical.

src/environments/environment.prod.ts (1)

229-233: LGTM - Production environment endpoints are consistent.

The endpoint definitions match the test environment configuration, ensuring consistency across environments.

src/app/user-login/login/login.component.ts (2)

179-218: Excellent improvement - unhandled errors now properly surface to users.

The addition of the else branch ensures that error messages not matching specific patterns are displayed to users instead of being silently ignored. This improves the user experience and aids in debugging.


239-290: LGTM - Consistent error handling pattern for regular user authentication.

The fallback error handler mirrors the Super Admin flow, providing consistent error messaging across both authentication paths.

src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts (1)

192-202: LGTM - New service methods follow existing patterns.

The getUserLockStatus() and unlockUserAccount() methods are well implemented and consistent with the existing service architecture. They correctly use environment-based URLs and return Observables for reactive handling.

♻️ Optional ES6 shorthand for cleaner syntax

The object literal can use ES6 property shorthand since the key and value names match:

   getUserLockStatus(userId: number): Observable<any> {
-    return this.http.post(environment.getUserLockStatusUrl, {
-      userId: userId,
-    });
+    return this.http.post(environment.getUserLockStatusUrl, { userId });
   }

   unlockUserAccount(userId: number): Observable<any> {
-    return this.http.post(environment.unlockUserAccountUrl, {
-      userId: userId,
-    });
+    return this.http.post(environment.unlockUserAccountUrl, { userId });
   }
src/environments/environment.local.ts (1)

230-234: LGTM!

The new URL constants are properly structured, follow existing naming conventions, and are appropriately grouped with clear comments. The placement adjacent to related user account URLs is logical.

src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts (1)

1538-1577: LGTM!

The unlock implementation follows established patterns in this component (similar to activateDeactivate). The confirmation dialog provides clear context, response handling is appropriate, and side effects (refresh list, reset search) align with user expectations.

@varundeepsaini varundeepsaini force-pushed the AMM-118-time-based-account-lockout branch from f86014d to 885dcbc Compare January 14, 2026 12:15
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

🤖 Fix all issues with AI agents
In
`@src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html`:
- Around line 95-104: The Unlock button is hidden by the wrong condition
(*ngIf="element.deleted === true"), preventing admins from unlocking active but
locked users; update the template where the button with
(click)="unlockUserAccount(element.userID, element.userName)" is declared so the
button is shown for all users (remove or replace the *ngIf) — or, if you prefer
stricter control, change the condition to a lock-specific check (e.g.,
element.locked === true) instead of element.deleted.
🧹 Nitpick comments (1)
src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts (1)

191-202: LGTM with optional style refinement.

The new service methods follow the existing patterns in this file. Consider using ES6 shorthand property syntax for cleaner code.

✨ Optional: Use ES6 shorthand property syntax
  getUserLockStatus(userId: number): Observable<any> {
    return this.http.post(environment.getUserLockStatusUrl, {
-      userId: userId,
+      userId,
    });
  }

  unlockUserAccount(userId: number): Observable<any> {
    return this.http.post(environment.unlockUserAccountUrl, {
-      userId: userId,
+      userId,
    });
  }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f86014d and 885dcbc.

📒 Files selected for processing (7)
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts
  • src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts
  • src/app/user-login/login/login.component.ts
  • src/environments/environment.local.ts
  • src/environments/environment.prod.ts
  • src/environments/environment.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/environments/environment.local.ts
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts
  • src/app/user-login/login/login.component.ts
  • src/environments/environment.prod.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts (1)
src/environments/environment.test.ts (1)
  • environment (36-506)
🔇 Additional comments (2)
src/environments/environment.test.ts (1)

225-229: LGTM!

The new API endpoint URLs for user lock management are properly defined using the commonBaseURL template literal, following the existing naming conventions in the file. The comment accurately identifies these as Common-API endpoints.

src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html (1)

115-121: LGTM!

The "Check lock status" button is correctly shown for all users without any conditional, allowing admins to check the lock status of any account regardless of its activation state.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@varundeepsaini varundeepsaini force-pushed the AMM-118-time-based-account-lockout branch from 885dcbc to 0172709 Compare January 14, 2026 12:23
@drtechie drtechie closed this Feb 20, 2026
@drtechie
Copy link
Member

Closing due to inactivity.

@drtechie drtechie reopened this Feb 20, 2026
@drtechie
Copy link
Member

@varundeepsaini is this tested locally?

@drtechie drtechie requested a review from snehar-nd February 20, 2026 13:14
@drtechie
Copy link
Member

@snehar-nd please review.

@varundeepsaini
Copy link
Author

Yes all the prs are tested

Signed-off-by: Varun Deep Saini <varun.23bcs10048@ms.sst.scaler.com>
@varundeepsaini varundeepsaini force-pushed the AMM-118-time-based-account-lockout branch from 0172709 to 8ded910 Compare March 1, 2026 17:48
@varundeepsaini varundeepsaini requested a review from snehar-nd March 1, 2026 17:48
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 1, 2026

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.

🧹 Nitpick comments (2)
src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts (2)

1601-1607: Double confirmation dialog when unlocking from lock status check.

When a user confirms the unlock prompt here, unlockUserAccount() displays another confirmation dialog. This results in the user needing to confirm twice to complete the unlock action.

Consider either:

  1. Calling the service directly here (bypassing the second confirmation), or
  2. Creating a private helper method that performs the unlock without confirmation
♻️ Suggested refactor to avoid double confirmation

Extract the unlock logic into a private method and call it directly:

+  private performUnlock(userID: number) {
+    this.employeeMasterNewService.unlockUserAccount(userID).subscribe(
+      (response: any) => {
+        if (response && response.statusCode === 200) {
+          this.dialogService.alert(
+            'User account unlocked successfully. The user can now log in.',
+            'success',
+          );
+          this.getAllUserDetails();
+          this.searchTerm = null;
+        } else {
+          this.dialogService.alert(
+            response?.errorMessage || 'Failed to unlock user account',
+            'error',
+          );
+        }
+      },
+      (err) => {
+        console.log('error', err);
+        this.dialogService.alert(
+          'Error unlocking user account. Please try again.',
+          'error',
+        );
+      },
+    );
+  }
+
   unlockUserAccount(userID: number, userName: string) {
     this.dialogService
       .confirm(
         'Confirm Unlock',
         `Are you sure you want to unlock the account for user "${userName}"?...`,
       )
       .subscribe(
         (res) => {
           if (res) {
-            this.employeeMasterNewService.unlockUserAccount(userID).subscribe(
-              // ... existing logic
-            );
+            this.performUnlock(userID);
           }
         },
         // ...
       );
   }

Then in checkUserLockStatus, call this.performUnlock(userID) directly instead of this.unlockUserAccount(userID, userName).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts`
around lines 1601 - 1607, The confirm dialog in checkUserLockStatus leads to a
duplicate confirmation because unlockUserAccount(userID, userName) itself shows
a confirm; refactor by adding a private helper (e.g., performUnlock(userID:
string, userName?: string)) that calls the service to unlock and handles
success/error without prompting, then update checkUserLockStatus to call
this.performUnlock(userID, userName) instead of unlockUserAccount(...); keep
unlockUserAccount as the public method that first shows the confirmation and
then calls performUnlock so existing callers remain unchanged.

1594-1627: Consider defensive checks for optional response fields.

If the API response is missing expected fields like lockTimestamp, failedAttempts, or remainingTime, the UI will display "undefined" to the user. Consider adding fallback values.

🛡️ Example defensive handling
           if (data.isLockedDueToFailedAttempts) {
             const message = `Account is LOCKED due to failed login attempts.\n\n` +
-              `Locked at: ${data.lockTimestamp}\n` +
-              `Failed attempts: ${data.failedAttempts}\n` +
-              `Time remaining: ${data.remainingTime}\n` +
+              `Locked at: ${data.lockTimestamp || 'Unknown'}\n` +
+              `Failed attempts: ${data.failedAttempts ?? 'Unknown'}\n` +
+              `Time remaining: ${data.remainingTime || 'Unknown'}\n` +
               (data.unlockTime ? `Auto-unlock at: ${data.unlockTime}\n` : '') +
               `\nWould you like to unlock this account now?`;

Apply similar patterns to the other message constructions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts`
around lines 1594 - 1627, The constructed messages in
employee-master-new.component (the branches that call this.dialogService.confirm
and this.dialogService.alert) use optional API fields directly
(data.lockTimestamp, data.failedAttempts, data.remainingTime, data.unlockTime)
and can render "undefined"; update those message constructions to apply
defensive defaults for each optional field (e.g., local consts like
lockTimestamp = data.lockTimestamp ?? 'N/A', failedAttempts =
data.failedAttempts ?? '0', remainingTime = data.remainingTime ?? 'Unknown',
unlockTime = data.unlockTime ?? '') and use those variables when building the
strings; ensure the confirm/alert calls and the unlockUserAccount(userID,
userName) flow remain unchanged but reference the sanitized values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts`:
- Around line 1601-1607: The confirm dialog in checkUserLockStatus leads to a
duplicate confirmation because unlockUserAccount(userID, userName) itself shows
a confirm; refactor by adding a private helper (e.g., performUnlock(userID:
string, userName?: string)) that calls the service to unlock and handles
success/error without prompting, then update checkUserLockStatus to call
this.performUnlock(userID, userName) instead of unlockUserAccount(...); keep
unlockUserAccount as the public method that first shows the confirmation and
then calls performUnlock so existing callers remain unchanged.
- Around line 1594-1627: The constructed messages in
employee-master-new.component (the branches that call this.dialogService.confirm
and this.dialogService.alert) use optional API fields directly
(data.lockTimestamp, data.failedAttempts, data.remainingTime, data.unlockTime)
and can render "undefined"; update those message constructions to apply
defensive defaults for each optional field (e.g., local consts like
lockTimestamp = data.lockTimestamp ?? 'N/A', failedAttempts =
data.failedAttempts ?? '0', remainingTime = data.remainingTime ?? 'Unknown',
unlockTime = data.unlockTime ?? '') and use those variables when building the
strings; ensure the confirm/alert calls and the unlockUserAccount(userID,
userName) flow remain unchanged but reference the sanitized values.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0172709 and 8ded910.

📒 Files selected for processing (7)
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts
  • src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts
  • src/app/user-login/login/login.component.ts
  • src/environments/environment.local.ts
  • src/environments/environment.prod.ts
  • src/environments/environment.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/app/user-login/login/login.component.ts
  • src/environments/environment.local.ts
  • src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html
  • src/environments/environment.prod.ts
  • src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts
  • src/environments/environment.test.ts

}

getUserLockStatus(userId: number): Observable<any> {
return this.http.post(environment.getUserLockStatusUrl, {
Copy link
Contributor

Choose a reason for hiding this comment

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

@varundeepsaini As I see here, you are getting the status of only one user detials. What if I want to render value for all lock/ Unlock button.
We need to get this lock/unlock staus in the get admin-api/m/SearchEmployee4 API response, so that we can rend all values on load.

@snehar-nd
Copy link
Contributor

@varundeepsaini Please do the changes based on the below senario.
Business Rules
Have the Lock/ Unlock button/toggle button.
If the account is Deactivated, Lock/Unlock button must be disabled and Login is not allowed

If the account is Activated
Lock/Unlock button visible.

Based on lock status:
If unlocked → show Lock
If locked → show Unlock

and please let me know, In this admin-api/m/SearchEmployee4 API , are we getting the Lock / Unlock status? if not, please update that from BE.

Based on the previous Active/ Deactive state, you can have initial values for Lock and Unlock status.

Account State Matrix
Activated | Locked | Allowed Login | Lock Button | Unlock Button
Yes | No | No | Show Lock | Hidden
Yes |Yes | No | Hidden | Show Unlock
No | Any | No | Disabled | Disabled

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.

3 participants