feat: add api to connect user completion form to backend

This commit is contained in:
Koosha Lahouti
2025-08-05 18:59:54 -07:00
parent 0f9ef06742
commit 3e23fae993
3 changed files with 66 additions and 37 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useMemo } from 'react'; import { useMemo } from 'react';
import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { LocalizationProvider } from '@mui/x-date-pickers'; import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
@@ -12,7 +12,6 @@ interface DateOfBirthProps {
export function DateOfBirth({ value, onChange }: DateOfBirthProps) { export function DateOfBirth({ value, onChange }: DateOfBirthProps) {
const { t, i18n } = useTranslation('completionForm'); const { t, i18n } = useTranslation('completionForm');
const isFarsi = i18n.language === 'fa' || i18n.language === 'fa-IR'; const isFarsi = i18n.language === 'fa' || i18n.language === 'fa-IR';
// const [birthDate, setBirthDate] = useState<Date | null>(null);
const Adapter = useMemo(() => { const Adapter = useMemo(() => {
return isFarsi ? AdapterDateFnsJalali : AdapterDateFns; return isFarsi ? AdapterDateFnsJalali : AdapterDateFns;

View File

@@ -1,15 +1,17 @@
import React, { useEffect, useState } from 'react';
import { Box, Typography } from '@mui/material'; import { Box, Typography } from '@mui/material';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Logo from '@/components/Logo';
import { PersonalInfoFields } from './PersonalInfoFields'; import { PersonalInfoFields } from './PersonalInfoFields';
import { PasswordSection } from './PasswordSection'; import { PasswordSection } from './PasswordSection';
import { EmailSection } from './EmailSection'; import { EmailSection } from './EmailSection';
import { SubmitSection } from './SubmitSection'; import { SubmitSection } from './SubmitSection';
import Logo from '@/components/Logo';
import apiClient from '@/lib/apiClient'; import apiClient from '@/lib/apiClient';
import { loginWithPassword } from '@/lib/authToken';
export function UserCompletionForm() { export function UserCompletionForm() {
const { t } = useTranslation('completionForm'); const { t } = useTranslation('completionForm');
const USERNAME = '+989353989651';
const [firstName, setFirstName] = useState(''); const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState(''); const [lastName, setLastName] = useState('');
@@ -61,11 +63,9 @@ export function UserCompletionForm() {
}, [password, validPassword]); }, [password, validPassword]);
useEffect(() => { useEffect(() => {
let timer: ReturnType<typeof setInterval>; let timer: NodeJS.Timeout;
if (buttonState === 'counting' && countdown > 0) { if (buttonState === 'counting' && countdown > 0) {
timer = setInterval(() => { timer = setInterval(() => setCountdown((prev) => prev - 1), 1000);
setCountdown((prev) => prev - 1);
}, 1000);
} }
if (countdown === 0) { if (countdown === 0) {
setButtonState('default'); setButtonState('default');
@@ -74,7 +74,7 @@ export function UserCompletionForm() {
}, [buttonState, countdown]); }, [buttonState, countdown]);
const toPersianDigits = (str: string) => const toPersianDigits = (str: string) =>
str.replace(/\d/g, (d: string) => '۰۱۲۳۴۵۶۷۸۹'[parseInt(d)]); str.replace(/\d/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[parseInt(d, 10)]);
const getButtonLabel = () => { const getButtonLabel = () => {
if (buttonState === 'sent') return t('completion.sent'); if (buttonState === 'sent') return t('completion.sent');
@@ -108,40 +108,39 @@ export function UserCompletionForm() {
setCodeSent(false); setCodeSent(false);
setEmailVerified(false); setEmailVerified(false);
}; };
const STATICTOKEN = 'Bearer abcdef1234567890';
const handleSubmit = async () => { const handleSubmit = async () => {
setLoading(true); setLoading(true);
setError(null); setError(null);
setSuccess(false); setSuccess(false);
try { try {
await loginWithPassword(USERNAME, password);
const { data } = await apiClient.post<{ const { data } = await apiClient.post<{
success: boolean; success: boolean;
errorCode: number; errorCode: number;
message: string; message: string;
validations: { property: string; message: string }[]; validations: { property: string; message: string }[];
}>( }>('/User/CompleteUserInformation', {
'/User/CompleteUserInformation', firstName,
{ lastName,
firstName, gender: sex === 'female' ? 2 : 1,
lastName, nationalId,
gender: sex === 'female' ? 2 : 1, savePassword: showPasswordSection,
nationalId, password: showPasswordSection ? password : undefined,
savePassword: showPasswordSection, saveEmail: showEmail,
password: showPasswordSection ? password : undefined, email: showEmail ? email : undefined,
saveEmail: showEmail, birthDate,
email: showEmail ? email : undefined, });
birthDate,
},
{ headers: { Authorization: STATICTOKEN } },
);
if (data.success) { if (data.success) {
setSuccess(true); setSuccess(true);
} else { } else {
setError(data.message || 'Validation error'); setError(data.message || 'Validation error');
} }
} catch (err) { } catch (err: any) {
setError((err as Error).message || 'An error occurred'); setError(err.message || 'An error occurred');
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -152,8 +151,8 @@ export function UserCompletionForm() {
sx={{ sx={{
backgroundColor: 'background.default', backgroundColor: 'background.default',
minHeight: '100vh', minHeight: '100vh',
flexDirection: 'column',
display: 'flex', display: 'flex',
flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
p: { xs: 1, sm: 2, md: 3 }, p: { xs: 1, sm: 2, md: 3 },
@@ -165,7 +164,7 @@ export function UserCompletionForm() {
<Box <Box
sx={{ sx={{
width: '100%', width: '100%',
maxWidth: '730px', maxWidth: 730,
backgroundColor: 'background.paper', backgroundColor: 'background.paper',
border: '1px solid #ccc', border: '1px solid #ccc',
display: 'flex', display: 'flex',
@@ -175,14 +174,7 @@ export function UserCompletionForm() {
py: { xs: 3, sm: 4 }, py: { xs: 3, sm: 4 },
}} }}
> >
<Box <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
sx={{
display: 'flex',
flexDirection: 'column',
width: '100%',
gap: 1,
}}
>
<Typography variant="h5" color="text.primary"> <Typography variant="h5" color="text.primary">
{t('completion.title')} {t('completion.title')}
</Typography> </Typography>
@@ -190,6 +182,7 @@ export function UserCompletionForm() {
{t('completion.description')} {t('completion.description')}
</Typography> </Typography>
</Box> </Box>
<PersonalInfoFields <PersonalInfoFields
firstName={firstName} firstName={firstName}
setFirstName={setFirstName} setFirstName={setFirstName}
@@ -204,6 +197,7 @@ export function UserCompletionForm() {
country={country} country={country}
setCountry={setCountry} setCountry={setCountry}
/> />
<PasswordSection <PasswordSection
showPasswordSection={showPasswordSection} showPasswordSection={showPasswordSection}
setShowPasswordSection={setShowPasswordSection} setShowPasswordSection={setShowPasswordSection}
@@ -219,6 +213,7 @@ export function UserCompletionForm() {
validPassword={validPassword} validPassword={validPassword}
showValidations={showPasswordValidations} showValidations={showPasswordValidations}
/> />
<EmailSection <EmailSection
showEmail={showEmail} showEmail={showEmail}
setShowEmail={setShowEmail} setShowEmail={setShowEmail}
@@ -236,6 +231,7 @@ export function UserCompletionForm() {
isVerifyingCode={isVerifyingCode} isVerifyingCode={isVerifyingCode}
handleEditEmail={handleEditEmail} handleEditEmail={handleEditEmail}
/> />
<SubmitSection <SubmitSection
onSubmit={handleSubmit} onSubmit={handleSubmit}
loading={loading} loading={loading}

34
src/lib/authToken.ts Normal file
View File

@@ -0,0 +1,34 @@
// src/lib/authService.ts
import axios from 'axios';
export interface TokenResponse {
access_token: string;
expires_in: number;
refresh_token: string;
}
const authClient = axios.create({
baseURL: 'https://account.business-harmony.com',
timeout: 10000,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
export async function loginWithPassword(
username: string,
password: string,
): Promise<TokenResponse> {
const body = new URLSearchParams();
body.set('grant_type', 'password');
body.set('username', username);
body.set('password', password);
body.set('client_id', 'harmony_identity');
body.set('scope', 'openid harmony_identity profile offline_access');
const { data } = await authClient.post<TokenResponse>(
'/connect/token',
body.toString(),
);
localStorage.setItem('authToken', data.access_token);
return data;
}