fix: replace deprecated *ngIf with @if control flow in OTP component

- Migrated all *ngIf directives to modern @if syntax in otp.component.html
- Updated email form, OTP entry, countdown display, and success message conditionals
- Resolves ngtsc deprecation warning 6385 for better Angular 17+ compatibility
This commit is contained in:
2025-10-16 00:26:47 +05:30
parent a3e1748e56
commit 94f4305615
21 changed files with 750 additions and 960 deletions
+23
View File
@@ -0,0 +1,23 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { OtpComponent } from './otp/otp.component';
import { ReactiveFormsModule } from '@angular/forms';
import { AuthInterceptor } from '../interceptors/auth-interceptor';
@NgModule({
declarations: [
OtpComponent
],
imports: [
CommonModule,
BrowserModule,
ReactiveFormsModule
],
providers:[
AuthInterceptor
]
})
export class AuthModule { }
+16
View File
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
+35
View File
@@ -0,0 +1,35 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private readonly baseUrl = (environment.apiUrl ?? '').replace(/\/+$/, '');
constructor(private http: HttpClient) {}
private api(path: string) {
return `${this.baseUrl}${path.startsWith('/') ? '' : '/'}${path}`;
}
sendOtp(email: string): Observable<any> {
const formData = new FormData();
formData.append('email', email);
return this.http.post(this.api('/api/v1/auth/GenerateOtp'), formData);
}
verifyOtp(userId: string, otpCode: string): Observable<any> {
const body = {
UserId: userId,
OtpCode: otpCode
};
return this.http.post(this.api('/api/v1/auth/ValidateOtp'), body);
}
getApiKey(): string{
return environment.apiKey;
}
}
+16
View File
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthService} from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
+40
View File
@@ -0,0 +1,40 @@
<div class="otp-container">
<h2>🔐 Email Verification</h2>
<!-- Step 1: Enter Email -->
@if (!isOtpSent()) {
<form [formGroup]="emailForm" (ngSubmit)="sendOtp()">
<label>Email Address</label>
<input type="email" formControlName="email" placeholder="Enter your email" />
<button type="submit" [disabled]="emailForm.invalid">Send OTP</button>
</form>
}
<!-- Step 2: Enter OTP -->
@if (isOtpSent() && !isVerified()) {
<div>
<p>OTP sent to <b>{{ emailForm.value.email }}</b></p>
<form [formGroup]="otpForm" (ngSubmit)="verifyOtp()">
<label>Enter 6-digit OTP</label>
<input type="text" maxlength="6" formControlName="otp" placeholder="123456" />
<button type="submit" [disabled]="otpForm.invalid">Verify OTP</button>
</form>
<button (click)="resendOtp()" [disabled]="countdown() > 0">
Resend OTP @if (countdown() > 0) {
<span>({{ countdown() }}s)</span>
}
</button>
</div>
}
<!-- Step 3: Success -->
@if (isVerified()) {
<div>
<p class="success">✅ Your email has been verified successfully!</p>
</div>
}
<p class="message">{{ message() }}</p>
</div>
+47
View File
@@ -0,0 +1,47 @@
.otp-container {
max-width: 400px;
margin: 60px auto;
padding: 30px;
background: #fff;
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
text-align: center;
h2 {
margin-bottom: 20px;
}
input {
width: 100%;
padding: 10px;
margin: 10px 0;
font-size: 16px;
}
button {
width: 100%;
margin-top: 10px;
padding: 10px;
background-color: #007bff;
border: none;
color: white;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
&:disabled {
background: #aaa;
cursor: not-allowed;
}
}
.success {
color: green;
font-weight: 600;
}
.message {
margin-top: 15px;
color: #555;
}
}
+23
View File
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OtpComponent } from './otp.component';
describe('OtpComponent', () => {
let component: OtpComponent;
let fixture: ComponentFixture<OtpComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [OtpComponent]
})
.compileComponents();
fixture = TestBed.createComponent(OtpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
+74
View File
@@ -0,0 +1,74 @@
import { Component, signal } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-otp',
standalone: false,
templateUrl: './otp.component.html',
styleUrls: ['./otp.component.scss']
})
export class OtpComponent {
emailForm: FormGroup;
otpForm: FormGroup;
isOtpSent = signal(false);
isVerified = signal(false);
message = signal('');
countdown = signal(0);
timer: any;
constructor(private fb: FormBuilder, private authService: AuthService) {
this.emailForm = this.fb.group({
email: ['', [Validators.required, Validators.email]],
});
this.otpForm = this.fb.group({
otp: ['', [Validators.required, Validators.pattern(/^[0-9]{6}$/)]],
});
}
sendOtp() {
if (this.emailForm.invalid) return;
const email = this.emailForm.value.email;
this.authService.sendOtp(email).subscribe(() => {
this.isOtpSent.set(true);
this.message.set('OTP sent successfully!');
this.startTimer(30); // 30 seconds countdown
});
}
resendOtp() {
if (this.countdown() > 0) return;
this.sendOtp();
}
startTimer(seconds: number) {
this.countdown.set(seconds);
clearInterval(this.timer);
this.timer = setInterval(() => {
this.countdown.update(value => value - 1);
if (this.countdown() <= 0) clearInterval(this.timer);
}, 1000);
}
verifyOtp() {
if (this.otpForm.invalid) return;
const { email: userId } = this.emailForm.value;
const { otp: otpCode } = this.otpForm.value;
this.authService.verifyOtp(userId, otpCode).subscribe({
next: (res) => {
this.isVerified.set(true);
this.message.set(res.message || 'OTP verified successfully ✅');
},
error: (err) => {
if (err.status === 401 && err.error?.message) {
this.message.set(err.error.message); // "OTP Expired" or "Invalid OTP"
} else {
this.message.set('Something went wrong. Please try again.');
}
}
});
}
}