mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge pull request #562 from Tria-plc/alpha
feat: ( forget password ) add phone number
This commit is contained in:
@@ -29,12 +29,12 @@ export default function LoginPage() {
|
||||
const [emailFocused, setEmailFocused] = useState(false);
|
||||
const [passwordFocused, setPasswordFocused] = useState(false);
|
||||
|
||||
const [view, setView] = useState<'login' | 'forgot'>('login');
|
||||
const [forgotEmail, setForgotEmail] = useState('');
|
||||
const [forgotLoading, setForgotLoading] = useState(false);
|
||||
const [forgotError, setForgotError] = useState('');
|
||||
const [forgotSent, setForgotSent] = useState(false);
|
||||
const [forgotFocused, setForgotFocused] = useState(false);
|
||||
const [view, setView] = useState<'login' | 'forgot'>('login');
|
||||
const [forgotIdentifier, setForgotIdentifier] = useState('');
|
||||
const [forgotLoading, setForgotLoading] = useState(false);
|
||||
const [forgotError, setForgotError] = useState('');
|
||||
const [forgotSent, setForgotSent] = useState(false);
|
||||
const [forgotFocused, setForgotFocused] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const { login } = useAuthStore();
|
||||
@@ -66,13 +66,13 @@ export default function LoginPage() {
|
||||
setForgotLoading(true);
|
||||
setForgotError('');
|
||||
try {
|
||||
await iamAuthApi.forgotPassword(forgotEmail);
|
||||
await iamAuthApi.forgotPassword(forgotIdentifier.trim());
|
||||
setForgotSent(true);
|
||||
} catch (err: any) {
|
||||
const msg = err.response?.data?.message || err.message || '';
|
||||
setForgotError(
|
||||
msg === 'user_not_found'
|
||||
? 'No account found with that email address.'
|
||||
? 'No account found with that email or phone number.'
|
||||
: msg || 'Failed to send the reset link. Please try again.'
|
||||
);
|
||||
} finally {
|
||||
@@ -209,7 +209,7 @@ export default function LoginPage() {
|
||||
<div className="flex justify-end mt-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setView('forgot'); setForgotEmail(email); setError(''); }}
|
||||
onClick={() => { setView('forgot'); setForgotIdentifier(email); setError(''); }}
|
||||
className="text-xs font-medium text-[rgb(20,113,76)] hover:underline"
|
||||
>
|
||||
Forgot password?
|
||||
@@ -260,7 +260,7 @@ export default function LoginPage() {
|
||||
Reset your password
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
Enter your email address and we'll send a reset link to the phone number on your account.
|
||||
Enter your email or phone number and we'll send a reset link to the phone number on your account.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -295,10 +295,10 @@ export default function LoginPage() {
|
||||
)}
|
||||
|
||||
<form onSubmit={handleForgotSubmit} className="space-y-4 animate-fade-up" style={{ animationDelay: '80ms' }}>
|
||||
{/* Email field */}
|
||||
{/* Email or phone field */}
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider mb-2">
|
||||
Email address
|
||||
Email or phone number
|
||||
</label>
|
||||
<div className={`relative rounded-xl transition-all duration-200 ${
|
||||
forgotFocused
|
||||
@@ -306,15 +306,15 @@ export default function LoginPage() {
|
||||
: 'ring-1 ring-gray-200 dark:ring-gray-800'
|
||||
}`}>
|
||||
<input
|
||||
type="email"
|
||||
value={forgotEmail}
|
||||
onChange={(e) => { setForgotEmail(e.target.value); setForgotError(''); }}
|
||||
type="text"
|
||||
value={forgotIdentifier}
|
||||
onChange={(e) => { setForgotIdentifier(e.target.value); setForgotError(''); }}
|
||||
onFocus={() => setForgotFocused(true)}
|
||||
onBlur={() => setForgotFocused(false)}
|
||||
className="w-full px-4 py-3 rounded-xl bg-white dark:bg-gray-900 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-600 text-sm focus:outline-none"
|
||||
placeholder="name@edr.com"
|
||||
placeholder="name@edr.com or +251..."
|
||||
required
|
||||
autoComplete="email"
|
||||
autoComplete="username"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -322,9 +322,9 @@ export default function LoginPage() {
|
||||
{/* Submit */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={forgotLoading || !forgotEmail}
|
||||
disabled={forgotLoading || !forgotIdentifier.trim()}
|
||||
className="group w-full mt-2 flex items-center justify-center gap-2 py-3 px-4 rounded-xl font-semibold text-sm text-white transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
style={{ background: forgotLoading || !forgotEmail
|
||||
style={{ background: forgotLoading || !forgotIdentifier.trim()
|
||||
? 'rgb(20,113,76)'
|
||||
: `linear-gradient(135deg, rgb(20,113,76) 0%, rgb(16,143,96) 100%)`
|
||||
}}
|
||||
|
||||
@@ -4,8 +4,10 @@ const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
|
||||
|
||||
|
||||
export const iamAuthApi = {
|
||||
forgotPassword: (email: string) =>
|
||||
axios.post(`${API_URL}/v1/auth/forgot-password`, { email }),
|
||||
// `identifier` can be an email address or a phone number — the IAM accepts
|
||||
// either in the `email` field of the forgot-password body.
|
||||
forgotPassword: (identifier: string) =>
|
||||
axios.post(`${API_URL}/v1/auth/forgot-password`, { email: identifier }),
|
||||
|
||||
resetPassword: (data: {
|
||||
userId: string;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Train, MailCheck, ArrowLeft } from 'lucide-react';
|
||||
import { iamAuthApi } from '@/lib/api/auth';
|
||||
|
||||
export default function ForgotPasswordPage() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [identifier, setIdentifier] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [sent, setSent] = useState(false);
|
||||
@@ -16,13 +16,13 @@ export default function ForgotPasswordPage() {
|
||||
setLoading(true);
|
||||
setError('');
|
||||
try {
|
||||
await iamAuthApi.forgotPassword(email);
|
||||
await iamAuthApi.forgotPassword(identifier.trim());
|
||||
setSent(true);
|
||||
} catch (err: any) {
|
||||
const msg = err.response?.data?.message || err.message || '';
|
||||
setError(
|
||||
msg === 'user_not_found'
|
||||
? 'No account found with that email address.'
|
||||
? 'No account found with that email or phone number.'
|
||||
: msg || 'Failed to send the reset link. Please try again.'
|
||||
);
|
||||
} finally {
|
||||
@@ -41,7 +41,7 @@ export default function ForgotPasswordPage() {
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">Reset your password</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-2">
|
||||
Enter your email and we'll send a reset link to the phone number on your account.
|
||||
Enter your email or phone number and we'll send a reset link to the phone number on your account.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -69,19 +69,19 @@ export default function ForgotPasswordPage() {
|
||||
)}
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300">Email</label>
|
||||
<label className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300">Email or phone number</label>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => { setEmail(e.target.value); setError(''); }}
|
||||
type="text"
|
||||
value={identifier}
|
||||
onChange={(e) => { setIdentifier(e.target.value); setError(''); }}
|
||||
className="input-field"
|
||||
placeholder="your@email.com"
|
||||
autoComplete="email"
|
||||
placeholder="your@email.com or +251..."
|
||||
autoComplete="username"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit" className="btn-primary w-full" disabled={loading || !email}>
|
||||
<button type="submit" className="btn-primary w-full" disabled={loading || !identifier.trim()}>
|
||||
{loading ? 'Sending...' : 'Send reset link'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -8,8 +8,10 @@ const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
|
||||
// current password is wrong on change-password, and OTP failures must surface
|
||||
// as inline errors, not a logout.
|
||||
export const iamAuthApi = {
|
||||
forgotPassword: (email: string) =>
|
||||
axios.post(`${API_URL}/v1/auth/forgot-password`, { email }),
|
||||
// `identifier` can be an email address or a phone number — the IAM accepts
|
||||
// either in the `email` field of the forgot-password body.
|
||||
forgotPassword: (identifier: string) =>
|
||||
axios.post(`${API_URL}/v1/auth/forgot-password`, { email: identifier }),
|
||||
|
||||
// Re-sends the registration verification code for a still-pending account.
|
||||
resendRegistrationCode: (data: { email: string; phoneNumber: string }) =>
|
||||
|
||||
Reference in New Issue
Block a user