feat: enhance dynamic forms and popups for About, Contact, and Resume sections; add project detail popup
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { inject, Injectable, PLATFORM_ID } from '@angular/core';
|
||||
import { BehaviorSubject, map, Observable, of } from 'rxjs';
|
||||
import { BehaviorSubject, map, Observable } from 'rxjs';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { Router } from '@angular/router';
|
||||
@@ -107,11 +107,11 @@ export class AuthService {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
logout(): Observable<void> {
|
||||
logout(): void {
|
||||
this.http.post<void>(this.api('/api/v1/auth/logout'), {}).subscribe();
|
||||
this.accessToken = null;
|
||||
this.safeRemoveToken();
|
||||
this.router.navigate(['/admin/login']);
|
||||
return of();
|
||||
}
|
||||
|
||||
refreshToken(): Observable<RefreshTokenResponse> {
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
<form [formGroup]="otpForm" (ngSubmit)="verifyOtp()">
|
||||
<label for="otp">Enter 6-digit OTP</label>
|
||||
<input id="otp" type="text" maxlength="6" formControlName="otp" placeholder="123456" />
|
||||
@if (isError()) {
|
||||
<div class="error-banner">
|
||||
<i class="fa-solid fa-circle-exclamation"></i>
|
||||
<span>{{ message() }}</span>
|
||||
</div>
|
||||
}
|
||||
<button type="submit" [disabled]="otpForm.invalid">
|
||||
Verify OTP
|
||||
</button>
|
||||
|
||||
@@ -132,6 +132,40 @@ button {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: #ff6b6b;
|
||||
font-size: 13px;
|
||||
margin: 6px 0 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.error-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(255, 80, 80, 0.1);
|
||||
border: 1px solid rgba(255, 80, 80, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
margin: 10px 0 10px;
|
||||
color: #ff6b6b;
|
||||
font-size: 13px;
|
||||
animation: shakeError 0.4s ease;
|
||||
|
||||
i {
|
||||
font-size: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shakeError {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
20% { transform: translateX(-6px); }
|
||||
40% { transform: translateX(6px); }
|
||||
60% { transform: translateX(-4px); }
|
||||
80% { transform: translateX(4px); }
|
||||
}
|
||||
|
||||
.info-text {
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -82,9 +82,9 @@ export class OtpComponent implements OnInit {
|
||||
error: (err) => {
|
||||
this.isError.set(true);
|
||||
if (err.status === 401 && err.error?.message) {
|
||||
console.log(err.error.message); // "OTP Expired" or "Invalid OTP"
|
||||
this.message.set(err.error.message);
|
||||
} else {
|
||||
console.log('Something went wrong. Please try again.');
|
||||
this.message.set('Something went wrong. Please try again.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user