Compare commits
3 Commits
fe52df1b7a
...
b5b20b054d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5b20b054d | ||
|
|
a78467947d | ||
|
|
935b7f72e5 |
59
src/app/api/auth/login/route.ts
Normal file
59
src/app/api/auth/login/route.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { LoginData } from '@/entities/auth/model/types';
|
||||||
|
|
||||||
|
export const GET = async (req: NextRequest) => {
|
||||||
|
if (req.method !== 'GET') {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Method is not supported' },
|
||||||
|
{ status: 405 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { searchParams } = req.nextUrl;
|
||||||
|
|
||||||
|
const phoneNumber = searchParams.get('phoneNumber');
|
||||||
|
const cardNumber = searchParams.get('cardNumber');
|
||||||
|
const type = searchParams.get('type');
|
||||||
|
|
||||||
|
if (!phoneNumber || !cardNumber || !type) {
|
||||||
|
return NextResponse.json({ error: 'Bad request' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const loginRes = await fetch(
|
||||||
|
`https://test.oriyo.tj/api/client/login?type=${type}&phone=${phoneNumber}&uid=${cardNumber}`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!loginRes.ok) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Error during login' },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = (await loginRes.json()) as LoginData;
|
||||||
|
|
||||||
|
const token = data.token;
|
||||||
|
if (!token) {
|
||||||
|
return NextResponse.json({ error: 'No auth token' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = NextResponse.json({ success: true });
|
||||||
|
|
||||||
|
response.cookies.set('token', token, {
|
||||||
|
httpOnly: true,
|
||||||
|
path: '/',
|
||||||
|
maxAge: 2 * 60 * 60,
|
||||||
|
secure: process.env.NODE_ENV === 'production',
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('login error:', error);
|
||||||
|
return NextResponse.json({ error: 'Server error' }, { status: 500 });
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import LoginPage from "@/pages-templates/login";
|
import LoginPage from '@/pages-templates/login';
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
return <LoginPage/>
|
return <LoginPage />;
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/entities/auth/api/login.api.ts
Normal file
24
src/entities/auth/api/login.api.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||||
|
|
||||||
|
import { LoginParams, LoginResponse } from '../model/contracts/login.contract';
|
||||||
|
|
||||||
|
export const loginAPI = createApi({
|
||||||
|
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
|
||||||
|
endpoints: (build) => ({
|
||||||
|
login: build.query<LoginResponse, LoginParams>({
|
||||||
|
query: (data) => {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
type: data.type,
|
||||||
|
phoneNumber: data.phoneNumber,
|
||||||
|
cardNumber: data.cardNumber,
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: `/auth/login?${params}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { useLazyLoginQuery } = loginAPI;
|
||||||
1
src/entities/auth/index.ts
Normal file
1
src/entities/auth/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { useLazyLoginQuery } from './api/login.api';
|
||||||
9
src/entities/auth/model/contracts/login.contract.ts
Normal file
9
src/entities/auth/model/contracts/login.contract.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { LoginFormData } from '@/features/auth/login-form/model/login-form.schema';
|
||||||
|
|
||||||
|
export interface LoginResponse {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoginParams extends LoginFormData {
|
||||||
|
type: 'bonus' | 'corporate';
|
||||||
|
}
|
||||||
7
src/entities/auth/model/types/index.ts
Normal file
7
src/entities/auth/model/types/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface LoginData {
|
||||||
|
card_id: number;
|
||||||
|
created_at: string;
|
||||||
|
phone: string;
|
||||||
|
token: string;
|
||||||
|
uid: string;
|
||||||
|
}
|
||||||
@ -6,13 +6,15 @@ export const loginFormSchema = z.object({
|
|||||||
.trim()
|
.trim()
|
||||||
.regex(/^[0-9+\-() ]*$/, {
|
.regex(/^[0-9+\-() ]*$/, {
|
||||||
message:
|
message:
|
||||||
'Phone number can only contain numbers, spaces, and the following symbols: + - ( )',
|
'Номер телефона может содержать только цифры, пробелы и следующие символы: + - ( )',
|
||||||
})
|
})
|
||||||
.refine((val) => !val || val.length >= 5, {
|
.min(5, 'Номер телефона слишком короткий. Введите полный номер телефона')
|
||||||
message:
|
.max(13, 'Номер телефона не может быть длиннее 13 символов'),
|
||||||
'Phone number is too short. Please enter a complete phone number',
|
cardNumber: z
|
||||||
}),
|
.string()
|
||||||
cardNumber: z.string().min(16).trim(),
|
.min(6, 'Неверный номер карты. Введите полный номер карты')
|
||||||
|
.max(20, 'Номер карты не может быть длиннее 20 символов')
|
||||||
|
.trim(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type LoginFormData = z.infer<typeof loginFormSchema>;
|
export type LoginFormData = z.infer<typeof loginFormSchema>;
|
||||||
|
|||||||
@ -5,7 +5,10 @@ import { useRouter } from 'next/navigation';
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { useLazyLoginQuery } from '@/entities/auth';
|
||||||
|
|
||||||
|
import { SubmitButton } from '@/shared/components/submit-button';
|
||||||
|
import { useLanguage } from '@/shared/language';
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -17,17 +20,16 @@ import {
|
|||||||
import { Input } from '@/shared/shadcn-ui/input';
|
import { Input } from '@/shared/shadcn-ui/input';
|
||||||
|
|
||||||
import { LoginFormData, loginFormSchema } from '../model/login-form.schema';
|
import { LoginFormData, loginFormSchema } from '../model/login-form.schema';
|
||||||
import { useLanguage } from '@/shared/language';
|
|
||||||
|
|
||||||
interface LoginFormProps {
|
interface LoginFormProps {
|
||||||
// onSubmit: (data: any) => Promise<void>;
|
type: 'bonus' | 'corporate';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LoginForm = ({}: LoginFormProps) => {
|
export const LoginForm = ({ type }: LoginFormProps) => {
|
||||||
const {t} = useLanguage()
|
const { t } = useLanguage();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
// const [login, results] = useLoginMutation();
|
const [login, { isLoading: isLoginLoading }] = useLazyLoginQuery();
|
||||||
|
|
||||||
const form = useForm<LoginFormData>({
|
const form = useForm<LoginFormData>({
|
||||||
resolver: zodResolver(loginFormSchema),
|
resolver: zodResolver(loginFormSchema),
|
||||||
@ -38,34 +40,30 @@ export const LoginForm = ({}: LoginFormProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async (data: LoginFormData) => {
|
const onSubmit = async (data: LoginFormData) => {
|
||||||
// const response = await login(data).unwrap();
|
try {
|
||||||
// const user = response.data;
|
await login({ ...data, type }).unwrap();
|
||||||
// dispatch(
|
|
||||||
// setCredentials({
|
|
||||||
// user: {
|
|
||||||
// accessToken: user.accessToken,
|
|
||||||
// affiliateId: user.affiliateId,
|
|
||||||
// email: user.email,
|
|
||||||
// id: user.id,
|
|
||||||
// role: user.role,
|
|
||||||
// username: user.username,
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
// );
|
|
||||||
toast.success('Logged in successfully!');
|
|
||||||
|
|
||||||
router.push('/customer-dashboard');
|
toast.success('Logged in successfully!');
|
||||||
|
router.push(
|
||||||
|
type === 'bonus' ? '/customer-dashboard' : '/corporate-dashboard',
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
toast.error('An error occured during login');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form} >
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4 mx-auto'>
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className='mx-auto space-y-4'
|
||||||
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name='phoneNumber'
|
name='phoneNumber'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem className='flex flex-col'>
|
||||||
<FormLabel>{t("auth.phoneNumber")}</FormLabel>
|
<FormLabel>{t('auth.phoneNumber')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type='tel'
|
type='tel'
|
||||||
@ -82,8 +80,8 @@ export const LoginForm = ({}: LoginFormProps) => {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name='cardNumber'
|
name='cardNumber'
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem className='flex flex-col'>
|
||||||
<FormLabel>{t("auth.cardNumber")}</FormLabel>
|
<FormLabel>{t('auth.cardNumber')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type='text'
|
type='text'
|
||||||
@ -95,16 +93,14 @@ export const LoginForm = ({}: LoginFormProps) => {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<SubmitButton
|
||||||
// isLoading={results.isLoading}
|
isLoading={isLoginLoading}
|
||||||
// title='Login'
|
|
||||||
type='submit'
|
type='submit'
|
||||||
className='w-full'
|
className='w-full'
|
||||||
// variant={'default'}
|
disabled={isLoginLoading}
|
||||||
// disabled={loading}
|
|
||||||
>
|
>
|
||||||
Войти
|
Войти
|
||||||
</Button>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|||||||
36
src/shared/components/submit-button.tsx
Normal file
36
src/shared/components/submit-button.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Loader2Icon } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Button, type ButtonProps } from '@/shared/shadcn-ui/button';
|
||||||
|
|
||||||
|
interface SubmitButtonProps extends ButtonProps {
|
||||||
|
title?: string;
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SubmitButton = ({
|
||||||
|
title = 'Отправить',
|
||||||
|
size = 'default',
|
||||||
|
type = 'submit',
|
||||||
|
className,
|
||||||
|
disabled,
|
||||||
|
isLoading,
|
||||||
|
onClick,
|
||||||
|
...props
|
||||||
|
}: SubmitButtonProps) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={onClick}
|
||||||
|
type={type}
|
||||||
|
size={size}
|
||||||
|
className={className}
|
||||||
|
disabled={isLoading || disabled}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<Loader2Icon className='animate-spin' />
|
||||||
|
) : (
|
||||||
|
(props.children ?? title)
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,6 +1,10 @@
|
|||||||
import { combineReducers } from '@reduxjs/toolkit';
|
import { combineReducers } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import { loginAPI } from '@/entities/auth/api/login.api';
|
||||||
|
|
||||||
import { baseAPI } from '@/shared/api/base-api';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
export const rootReducer = combineReducers({
|
export const rootReducer = combineReducers({
|
||||||
[baseAPI.reducerPath]: baseAPI.reducer,
|
[baseAPI.reducerPath]: baseAPI.reducer,
|
||||||
|
[loginAPI.reducerPath]: loginAPI.reducer,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user