Wire SMS registration, forgot password, and one-time login.

Connect business and customer login pages to login-otp and reset-password, and document the auth flows in project context.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-05 15:10:51 +03:30
co-authored by Cursor
parent d8c9c0b017
commit c33cab40fe
12 changed files with 148 additions and 114 deletions
+7 -30
View File
@@ -11,8 +11,8 @@ import { setActiveBusiness } from '../lib/businessContext'
import {
logout as logoutRequest,
register,
resetPassword,
sendOtp,
verifyOtp,
} from '../services/authService'
import { useT } from '../i18n/useT'
import styles from './LoginPage.module.css'
@@ -22,7 +22,7 @@ type SmsStep = 'phone' | 'code'
export function LoginPage() {
const navigate = useNavigate()
const { login } = useAuth()
const { login, loginWithOtp } = useAuth()
const { businessName, logoUrl } = useTenantBranding()
const businessDomain = getBusinessDomain()
const t = useT()
@@ -184,9 +184,9 @@ export function LoginPage() {
try {
const cellNumber = toE164CellNumber(phone)
await verifyOtp(cellNumber, smsCode)
setInfo(t('forgot.info.partial'))
setTimeout(() => switchView('login'), 2500)
await resetPassword(cellNumber, smsCode, newPassword)
setInfo(t('forgot.info.success'))
setTimeout(() => switchView('login'), 2000)
} catch (err) {
handleApiError(err, t('forgot.error.verify'))
} finally {
@@ -201,14 +201,7 @@ export function LoginPage() {
try {
const cellNumber = toE164CellNumber(phone)
await verifyOtp(cellNumber, smsCode)
if (!password) {
setError(t('otp.error.password'))
return
}
await login(cellNumber, password)
await loginWithOtp(cellNumber, smsCode)
navigate('/')
} catch (err) {
handleApiError(err, t('otp.error.signIn'))
@@ -600,6 +593,7 @@ export function LoginPage() {
{error}
</div>
)}
{info && <div className={styles.info}>{info}</div>}
{smsStep === 'phone' ? (
<>
@@ -653,23 +647,6 @@ export function LoginPage() {
</div>
</div>
<div className={styles.field}>
<label htmlFor="otp-password">{t('otp.password')}</label>
<div className={styles.inputWrap}>
<Lock size={18} className={styles.inputIcon} />
<input
id="otp-password"
type={showPassword ? 'text' : 'password'}
placeholder={t('otp.passwordPlaceholder')}
value={password}
onChange={(e) => setPassword(e.target.value)}
required
minLength={8}
disabled={isSubmitting}
/>
</div>
</div>
<div className={styles.resendRow}>
{countdown > 0 ? (
<span className={styles.countdown}>