update: make login page

This commit is contained in:
BunyodL 2025-04-24 00:36:50 +05:00
parent 1a679d029e
commit e2e13129ed
12 changed files with 576 additions and 8 deletions

View File

@ -14,10 +14,12 @@
"@radix-ui/react-collapsible": "^1.1.8",
"@radix-ui/react-dialog": "^1.1.11",
"@radix-ui/react-dropdown-menu": "^2.1.11",
"@radix-ui/react-label": "^2.1.4",
"@radix-ui/react-navigation-menu": "^1.2.10",
"@radix-ui/react-select": "^2.2.2",
"@radix-ui/react-slot": "^1.2.0",
"@radix-ui/react-tabs": "^1.1.8",
"@radix-ui/react-toast": "^1.2.11",
"@reduxjs/toolkit": "^2.7.0",
"aos": "^2.3.4",
"class-variance-authority": "^0.7.1",
@ -29,7 +31,9 @@
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.56.1",
"react-redux": "^9.2.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.2.0",
"tailwindcss-animate": "^1.0.7",
"tw-animate-css": "^1.2.6",

90
pnpm-lock.yaml generated
View File

@ -10,7 +10,7 @@ importers:
dependencies:
'@hookform/resolvers':
specifier: ^5.0.1
version: 5.0.1(react-hook-form@7.56.0(react@19.1.0))
version: 5.0.1(react-hook-form@7.56.1(react@19.1.0))
'@radix-ui/react-collapsible':
specifier: ^1.1.8
version: 1.1.8(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@ -20,6 +20,9 @@ importers:
'@radix-ui/react-dropdown-menu':
specifier: ^2.1.11
version: 2.1.11(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-label':
specifier: ^2.1.4
version: 2.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-navigation-menu':
specifier: ^1.2.10
version: 1.2.10(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@ -32,6 +35,9 @@ importers:
'@radix-ui/react-tabs':
specifier: ^1.1.8
version: 1.1.8(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-toast':
specifier: ^1.2.11
version: 1.2.11(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@reduxjs/toolkit':
specifier: ^2.7.0
version: 2.7.0(react-redux@9.2.0(@types/react@19.1.2)(react@19.1.0)(redux@5.0.1))(react@19.1.0)
@ -65,9 +71,15 @@ importers:
react-dom:
specifier: ^19.0.0
version: 19.1.0(react@19.1.0)
react-hook-form:
specifier: ^7.56.1
version: 7.56.1(react@19.1.0)
react-redux:
specifier: ^9.2.0
version: 9.2.0(@types/react@19.1.2)(react@19.1.0)(redux@5.0.1)
sonner:
specifier: ^2.0.3
version: 2.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
tailwind-merge:
specifier: ^3.2.0
version: 3.2.0
@ -630,6 +642,19 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-label@2.1.4':
resolution: {integrity: sha512-wy3dqizZnZVV4ja0FNnUhIWNwWdoldXrneEyUcVtLYDAt8ovGS4ridtMAOGgXBBIfggL4BOveVWsjXDORdGEQg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-menu@2.1.11':
resolution: {integrity: sha512-sbFI4Qaw02J0ogmR9tOMsSqsdrGNpUanlPYAqTE2JJafow8ecHtykg4fSTjNHBdDl4deiKMK+RhTEwyVhP7UDA==}
peerDependencies:
@ -769,6 +794,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-toast@1.2.11':
resolution: {integrity: sha512-Ed2mlOmT+tktOsu2NZBK1bCSHh/uqULu1vWOkpQTVq53EoOuZUZw7FInQoDB3uil5wZc2oe0XN9a7uVZB7/6AQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-use-callback-ref@1.1.1':
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
@ -2228,8 +2266,8 @@ packages:
peerDependencies:
react: ^19.1.0
react-hook-form@7.56.0:
resolution: {integrity: sha512-U2QQgx5z2Y8Z0qlXv3W19hWHJgfKdWMz0O/osuY+o+CYq568V2R/JhzC6OAXfR8k24rIN0Muan2Qliaq9eKs/g==}
react-hook-form@7.56.1:
resolution: {integrity: sha512-qWAVokhSpshhcEuQDSANHx3jiAEFzu2HAaaQIzi/r9FNPm1ioAvuJSD4EuZzWd7Al7nTRKcKPnBKO7sRn+zavQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
@ -2392,6 +2430,12 @@ packages:
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
sonner@2.0.3:
resolution: {integrity: sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==}
peerDependencies:
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@ -2721,10 +2765,10 @@ snapshots:
'@floating-ui/utils@0.2.9': {}
'@hookform/resolvers@5.0.1(react-hook-form@7.56.0(react@19.1.0))':
'@hookform/resolvers@5.0.1(react-hook-form@7.56.1(react@19.1.0))':
dependencies:
'@standard-schema/utils': 0.3.0
react-hook-form: 7.56.0(react@19.1.0)
react-hook-form: 7.56.1(react@19.1.0)
'@humanfs/core@0.19.1': {}
@ -3020,6 +3064,15 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.2
'@radix-ui/react-label@2.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
optionalDependencies:
'@types/react': 19.1.2
'@types/react-dom': 19.1.2(@types/react@19.1.2)
'@radix-ui/react-menu@2.1.11(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
@ -3194,6 +3247,26 @@ snapshots:
'@types/react': 19.1.2
'@types/react-dom': 19.1.2(@types/react@19.1.2)
'@radix-ui/react-toast@1.2.11(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
'@radix-ui/react-collection': 1.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-portal': 1.1.6(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.2)(react@19.1.0)
'@radix-ui/react-visually-hidden': 1.2.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
optionalDependencies:
'@types/react': 19.1.2
'@types/react-dom': 19.1.2(@types/react@19.1.2)
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.2)(react@19.1.0)':
dependencies:
react: 19.1.0
@ -4714,7 +4787,7 @@ snapshots:
react: 19.1.0
scheduler: 0.26.0
react-hook-form@7.56.0(react@19.1.0):
react-hook-form@7.56.1(react@19.1.0):
dependencies:
react: 19.1.0
@ -4922,6 +4995,11 @@ snapshots:
is-arrayish: 0.3.2
optional: true
sonner@2.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
source-map-js@1.2.1: {}
stable-hash@0.0.5: {}

93
src/app/login/page.tsx Normal file
View File

@ -0,0 +1,93 @@
import { Building2, Fuel, User } from 'lucide-react';
import Link from 'next/link';
import { LoginForm } from '@/features/auth/login-form';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/shared/shadcn-ui/card';
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from '@/shared/shadcn-ui/tabs';
export default function LoginPage() {
return (
<div className='flex min-h-screen flex-col'>
<main className='flex-1'>
<div className='container max-w-6xl py-16'>
<div className='mb-12 flex flex-col items-center text-center'>
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
<Fuel className='h-6 w-6 text-red-600' />
</div>
<h1 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
Вход в личный кабинет
</h1>
<p className='max-w-2xl text-gray-600'>
Войдите в личный кабинет, чтобы получить доступ к информации о
ваших бонусах, истории операций и другим возможностям.
</p>
</div>
<div className='mx-auto max-w-md'>
<Tabs defaultValue='bonus' className='w-full'>
<TabsList className='mb-8 grid w-full grid-cols-2'>
<TabsTrigger value='bonus' className='text-base'>
<User className='mr-2 h-4 w-4' /> Бонусный клиент
</TabsTrigger>
<TabsTrigger value='corporate' className='text-base'>
<Building2 className='mr-2 h-4 w-4' /> Корпоративный клиент
</TabsTrigger>
</TabsList>
<TabsContent value='bonus'>
<Card>
<CardHeader>
<CardTitle>Вход для бонусных клиентов</CardTitle>
<CardDescription>
Введите номер телефона и номер бонусной карты для входа в
личный кабинет.
</CardDescription>
</CardHeader>
<CardContent className='space-y-4'>
<LoginForm />
</CardContent>
</Card>
</TabsContent>
<TabsContent value='corporate'>
<Card>
<CardHeader>
<CardTitle>Вход для корпоративных клиентов</CardTitle>
<CardDescription>
Введите номер телефона и номер корпоративной карты для
входа в личный кабинет.
</CardDescription>
</CardHeader>
<CardContent className='space-y-4'>
<LoginForm />
</CardContent>
</Card>
</TabsContent>
</Tabs>
<div className='mt-8 text-center text-sm text-gray-500'>
<p>
Возникли проблемы со входом?{' '}
<Link href='/contact' className='text-red-600 hover:underline'>
Свяжитесь с нами
</Link>
</p>
</div>
</div>
</div>
</main>
</div>
);
}

View File

@ -0,0 +1 @@
export { LoginForm } from './ui/login-form';

View File

@ -0,0 +1,18 @@
import { z } from 'zod';
export const loginFormSchema = z.object({
phoneNumber: z
.string()
.trim()
.regex(/^[0-9+\-() ]*$/, {
message:
'Phone number can only contain numbers, spaces, and the following symbols: + - ( )',
})
.refine((val) => !val || val.length >= 5, {
message:
'Phone number is too short. Please enter a complete phone number',
}),
cardNumber: z.string().min(16).trim(),
});
export type LoginFormData = z.infer<typeof loginFormSchema>;

View File

@ -0,0 +1,108 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { useRouter } from 'next/navigation';
import { useForm } from 'react-hook-form';
import { toast } from 'sonner';
import { Button } from '@/shared/shadcn-ui/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/shared/shadcn-ui/form';
import { Input } from '@/shared/shadcn-ui/input';
import { LoginFormData, loginFormSchema } from '../model/login-form.schema';
interface LoginFormProps {
// onSubmit: (data: any) => Promise<void>;
}
export const LoginForm = ({}: LoginFormProps) => {
const router = useRouter();
// const [login, results] = useLoginMutation();
const form = useForm<LoginFormData>({
resolver: zodResolver(loginFormSchema),
defaultValues: {
phoneNumber: '',
cardNumber: '',
},
});
const onSubmit = async (data: LoginFormData) => {
// const response = await login(data).unwrap();
// const user = response.data;
// 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');
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4'>
<FormField
control={form.control}
name='phoneNumber'
render={({ field }) => (
<FormItem>
<FormLabel>Номер телефона</FormLabel>
<FormControl>
<Input
type='tel'
placeholder='+992 XXX XX XX XX'
{...field}
inputMode='tel'
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='cardNumber'
render={({ field }) => (
<FormItem>
<FormLabel>Номер карты</FormLabel>
<FormControl>
<Input
type='text'
placeholder='XXXX-XXXX-XXXX-XXXX'
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
// isLoading={results.isLoading}
// title='Login'
type='submit'
className='w-full'
// variant={'default'}
// disabled={loading}
>
Войти
</Button>
</form>
</Form>
);
};

View File

@ -5,6 +5,7 @@ import { Provider } from 'react-redux';
import { store } from '../store';
import { ThemeProvider } from '../theme/theme-provider';
import { AosProvider } from './aos-provider';
import { Toaster } from './toaster';
type ProvidersProps = {
children: React.ReactNode;
@ -19,7 +20,10 @@ export const Providers = ({ children }: ProvidersProps) => {
enableSystem
disableTransitionOnChange
>
<AosProvider>{children}</AosProvider>
<AosProvider>
{children}
<Toaster />
</AosProvider>
</ThemeProvider>
</Provider>
);

View File

@ -0,0 +1,31 @@
'use client';
import { useTheme } from 'next-themes';
import { Toaster as Sonner } from 'sonner';
type ToasterProps = React.ComponentProps<typeof Sonner>;
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = 'system' } = useTheme();
return (
<Sonner
theme={theme as ToasterProps['theme']}
className='toaster group'
toastOptions={{
classNames: {
toast:
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
description: 'group-[.toast]:text-muted-foreground',
actionButton:
'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
cancelButton:
'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
},
}}
{...props}
/>
);
};
export { Toaster };

View File

@ -0,0 +1,179 @@
'use client';
import * as LabelPrimitive from '@radix-ui/react-label';
import { Slot } from '@radix-ui/react-slot';
import * as React from 'react';
import {
Controller,
type ControllerProps,
type FieldPath,
type FieldValues,
FormProvider,
useFormContext,
} from 'react-hook-form';
import { cn } from '@/shared/lib/utils';
import { Label } from '@/shared/shadcn-ui/label';
const Form = FormProvider;
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue,
);
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();
const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>');
}
const { id } = itemContext;
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};
type FormItemContextValue = {
id: string;
};
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue,
);
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId();
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn('space-y-2', className)} {...props} />
</FormItemContext.Provider>
);
});
FormItem.displayName = 'FormItem';
const FormLabel = React.forwardRef<
React.ComponentRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();
return (
<Label
ref={ref}
className={cn(error && 'text-destructive', className)}
htmlFor={formItemId}
{...props}
/>
);
});
FormLabel.displayName = 'FormLabel';
const FormControl = React.forwardRef<
React.ComponentRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } =
useFormField();
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
);
});
FormControl.displayName = 'FormControl';
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();
return (
<p
ref={ref}
id={formDescriptionId}
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
);
});
FormDescription.displayName = 'FormDescription';
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message ?? '') : children;
if (!body) {
return null;
}
return (
<p
ref={ref}
id={formMessageId}
className={cn('text-destructive text-sm font-medium', className)}
{...props}
>
{body}
</p>
);
});
FormMessage.displayName = 'FormMessage';
export {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
useFormField,
};

View File

@ -0,0 +1,22 @@
import * as React from 'react';
import { cn } from '@/shared/lib/utils';
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
'border-input bg-background ring-offset-background file:text-foreground placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-base file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className,
)}
ref={ref}
{...props}
/>
);
},
);
Input.displayName = 'Input';
export { Input };

View File

@ -0,0 +1,26 @@
'use client';
import * as LabelPrimitive from '@radix-ui/react-label';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';
import { cn } from '@/shared/lib/utils';
const labelVariants = cva(
'text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
);
const Label = React.forwardRef<
React.ComponentRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
));
Label.displayName = LabelPrimitive.Root.displayName;
export { Label };

View File

@ -1,3 +1,5 @@
import Link from 'next/link';
import { Logo } from '@/shared/assets/logo';
import { Button } from '@/shared/shadcn-ui/button';
@ -12,7 +14,9 @@ export function Header() {
<DesktopNav />
<div className='flex items-center gap-6 md:contents'>
<MobileNav />
<Link href={'/login'}>
<Button>Вход</Button>
</Link>
</div>
</div>
</header>