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:
@@ -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 { }
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user