Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "PORT=5173 next dev ",
"dev": "set PORT=5173 && next dev ",
"build": "next build",
"lint": "next lint",
"mock": "node ./mock/index.js",
Expand Down
9 changes: 7 additions & 2 deletions src/app/(auth)/login/_components/LogInForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useForm, Controller } from 'react-hook-form';
import AccountOutlineIcon from 'mdi-react/AccountOutlineIcon';
import { Alert } from 'react-bootstrap';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import PasswordField from '@/shared/components/form/Password';
import {
FormGroup,
Expand Down Expand Up @@ -35,6 +36,10 @@ const LogInForm = ({ onSubmit, error = '' }: LogInFormProps) => {
formState: { errors },
} = useForm();
const rememberMe = watch('rememberMe');
const router = useRouter();
const handleRegisterButtonClick = () => {
router.push('/register');
};

useEffect(() => {
if (rememberMe !== undefined && typeof window !== 'undefined') {
Expand Down Expand Up @@ -122,8 +127,8 @@ const LogInForm = ({ onSubmit, error = '' }: LogInFormProps) => {
<AccountButton variant="primary" type="submit">
Sign In
</AccountButton>
<AccountButton variant="outline-primary" to="/register">
<Link href="register">Create Account</Link>
<AccountButton onClick={handleRegisterButtonClick} variant="outline-primary">
Create Account
</AccountButton>
</LoginForm>
);
Expand Down
70 changes: 39 additions & 31 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ import { USER_LOGIN } from '@/graphql/auth';
import { AUTH_TOKEN, EMAIL } from '@/shared/constants/storage';
import { useSearchParams } from '@/hooks/useSearchParams';
import { useRouter } from 'next/navigation';
import Loading from '@/shared/components/Loading';
import LogInForm from './_components/LogInForm';

const Login = () => {
const router = useRouter();
const [error, setError] = useState('');
const originUrl = useSearchParams().get('orgUrl');
const [login] = useMutation(USER_LOGIN);
const [isLoading, setIsLoading] = useState(false);

const onSubmit = async (data: { email: string; password: string; remember_me: boolean }) => {
setIsLoading(true);
const result = await login({
variables: data,
});
Expand All @@ -56,42 +59,47 @@ const Login = () => {
} else {
// for login failed
setError(`Login failed: ${result.data.login.message}`);
setIsLoading(false);
}
};

return (
<AccountWrap>
<AccountContent>
<AccountCard>
<AccountHead>
<AccountTitle>
Welcome to
<br />
<AccountLogo>
BeeQuant
<AccountLogoAccent> AI</AccountLogoAccent>
</AccountLogo>
</AccountTitle>
<h4 className="subhead">Trading smart, trading with BeeQuant AI</h4>
</AccountHead>
<LogInForm onSubmit={onSubmit} error={error} />
<AccountOr>
<p>Or Easily Using</p>
</AccountOr>
<AccountSocial>
{/* @ts-ignore - Ignoring because of complex union types incorrectly inferred */}
<AccountSocialButtonFacebook
className="account__social-btn account__social-btn--facebook"
to="/login"
>
<FacebookIcon />
</AccountSocialButtonFacebook>
<AccountSocialButtonGoogle to="/login">
<GooglePlusIcon />
</AccountSocialButtonGoogle>
</AccountSocial>
</AccountCard>
</AccountContent>
{isLoading ? (
<Loading />
) : (
<AccountContent>
<AccountCard>
<AccountHead>
<AccountTitle>
Welcome to
<br />
<AccountLogo>
BeeQuant
<AccountLogoAccent> AI</AccountLogoAccent>
</AccountLogo>
</AccountTitle>
<h4 className="subhead">Trading smart, trading with BeeQuant AI</h4>
</AccountHead>
<LogInForm onSubmit={onSubmit} error={error} />
<AccountOr>
<p>Or Easily Using</p>
</AccountOr>
<AccountSocial>
{/* @ts-ignore - Ignoring because of complex union types incorrectly inferred */}
<AccountSocialButtonFacebook
className="account__social-btn account__social-btn--facebook"
to="/login"
>
<FacebookIcon />
</AccountSocialButtonFacebook>
<AccountSocialButtonGoogle to="/login">
<GooglePlusIcon />
</AccountSocialButtonGoogle>
</AccountSocial>
</AccountCard>
</AccountContent>
)}
</AccountWrap>
);
};
Expand Down
45 changes: 35 additions & 10 deletions src/app/(auth)/register/_components/RegisterSuccess.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { screen, render } from '@testing-library/react';
import { screen, render, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter as Router } from 'react-router-dom';
import { useUserContext } from '@/hooks/userHooks';
import { useRouter } from 'next/navigation';
import RegisterSuccess from './RegisterSuccess';

jest.mock('@/containers/Layout/topbar/BasicTopbarComponents', () => ({
TopbarDownIcon: () => <div>Mocked TopbarDownIcon</div>,
}));

jest.mock('next/navigation', () => ({
useRouter: jest.fn(),
}));

const mockLocalStorage = (() => {
let store: Record<string, string> = {};
return {
Expand All @@ -26,6 +31,15 @@ Object.defineProperty(window, 'localStorage', {
value: mockLocalStorage,
});

interface MockAppRouterInstance {
push: jest.Mock;
back?: jest.Mock;
forward?: jest.Mock;
refresh?: jest.Mock;
replace?: jest.Mock;
prefetch?: jest.Mock;
}

jest.mock('@/hooks/userHooks', () => ({
useUserContext: jest.fn(),
}));
Expand All @@ -52,7 +66,7 @@ describe('RegisterSuccess component', () => {
</Router>
);

const successMessage = screen.getByText(/Your registration is successful/i);
const successMessage = screen.getByText(/We have sent you a verification email/i);
expect(successMessage).toBeInTheDocument();
});

Expand All @@ -66,14 +80,25 @@ describe('RegisterSuccess component', () => {
expect(image).toBeInTheDocument();
});

it('should render button with correct link', () => {
const { getByText } = render(
<Router>
<RegisterSuccess />
</Router>
it('triggers router push on button click', async () => {
const pushMock = jest.fn();

const mockRouterInstance: MockAppRouterInstance = {
push: pushMock,
back: jest.fn(),
forward: jest.fn(),
refresh: jest.fn(),
replace: jest.fn(),
prefetch: jest.fn(),
};

(useRouter as jest.MockedFunction<typeof useRouter>).mockReturnValue(
mockRouterInstance as unknown as ReturnType<typeof useRouter>
);
const button = getByText('Back to Login');
expect(button).toBeInTheDocument();
expect(button).toHaveAttribute('href', '/login');
const { getByText } = render(<RegisterSuccess />);
fireEvent.click(getByText('Finished sign-up. Ready to trade'));
await waitFor(() => {
expect(pushMock).toHaveBeenCalledWith('/login');
});
});
});
60 changes: 35 additions & 25 deletions src/app/(auth)/register/_components/RegisterSuccess.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import styled from 'styled-components';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import {
AccountButton,
AccountCard,
Expand All @@ -12,30 +12,40 @@ import {
AccountWrap,
} from '@/shared/components/account/AccountElements';

const RegisterSuccess = () => (
<AccountWrap>
<AccountContent>
<AccountCard>
<AccountImage src="img/success.png" alt="success" />
<AccountHead>
<AccountTitle>
<AccountLogo>
<AccountLogoAccent>
Congratulations !
<br />
</AccountLogoAccent>
</AccountLogo>
Your registration is successful
</AccountTitle>
</AccountHead>
{/* @ts-ignore - Ignoring because of complex union types that are not correctly inferred */}
<AccountButton variant="outline-primary">
<Link href="/login">Back to Login</Link>
</AccountButton>
</AccountCard>
</AccountContent>
</AccountWrap>
);
const RegisterSuccess = () => {
const router = useRouter();
const handleFinishedButtonClick = () => {
router.push('/login');
};

return (
<AccountWrap>
<AccountContent>
<AccountCard>
<AccountImage src="img/success.png" alt="success" />
<AccountHead>
<AccountTitle>
<AccountLogo>
<AccountLogoAccent>
Please check your mailbox
<br />
</AccountLogoAccent>
</AccountLogo>
We have sent you a verification email
</AccountTitle>
</AccountHead>
{/*
@ts-ignore
- Ignoring because of complex union types that are not correctly inferred
*/}
<AccountButton onClick={handleFinishedButtonClick} variant="outline-primary">
Finished sign-up. Ready to trade
</AccountButton>
</AccountCard>
</AccountContent>
</AccountWrap>
);
};

export default RegisterSuccess;

Expand Down
9 changes: 9 additions & 0 deletions src/app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import { useMutation } from '@apollo/client';
import { USER_REGISTER } from '@/graphql/auth';
import { useTitle } from '@/hooks/useTitle';
import Link from 'next/link';
import Loading from '@/shared/components/Loading';
import RegisterForm from './_components/RegisterForm';
import RegisterSuccess from './_components/RegisterSuccess';

const Register = () => {
const [register] = useMutation(USER_REGISTER);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const [isRegistered, setIsRegistered] = useState(false);

Expand All @@ -31,6 +33,7 @@ const Register = () => {
displayName: string;
ref: string;
}) => {
setIsLoading(true);
const result = await register({
variables: {
input: data,
Expand All @@ -39,15 +42,21 @@ const Register = () => {

if (result.data.register.code === 200) {
setIsRegistered(true);
setIsLoading(false);
}
// for register failed
setError(`Register failed: ${result.data.register.message}`);
setIsLoading(false);
};

if (isRegistered) {
return <RegisterSuccess />;
}

if (isLoading) {
return <Loading />;
}

return (
<AccountWrap>
<AccountContent>
Expand Down
Loading