diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index b7d4052..0781bff 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,6 +4,7 @@ import { Montserrat, Roboto } from 'next/font/google';
import '@core/styles/globals.scss';
import '@core/styles/reset.scss';
import { Footer, Header } from '@/widgets';
+import { ModalProvider } from '@core/providers/modal-provider';
const roboto = Roboto({
subsets: ['cyrillic'],
@@ -30,7 +31,9 @@ export default function RootLayout({
- {children}
+
+ {children}
+
diff --git a/src/core/providers/modal-provider.tsx b/src/core/providers/modal-provider.tsx
new file mode 100644
index 0000000..af19951
--- /dev/null
+++ b/src/core/providers/modal-provider.tsx
@@ -0,0 +1,41 @@
+'use client';
+
+import {
+ useState,
+ useContext,
+ useCallback,
+ ReactNode,
+ createContext,
+} from 'react';
+import { Modal } from '@shared/ui/modal';
+
+const ModalContext = createContext({
+ hideModal: () => {},
+ showModal: (content: ReactNode) => {},
+});
+
+const useModal = () => useContext(ModalContext);
+
+const ModalProvider = ({ children }: { children: ReactNode }) => {
+ const [modalContent, setModalContent] = useState(null);
+
+ const showModal = useCallback((content: ReactNode) => {
+ setModalContent(content);
+ }, []);
+
+ const hideModal = useCallback(() => {
+ setModalContent(null);
+ }, []);
+
+ return (
+
+ {children}
+ {/* Ваш Modal компонент здесь */}
+
+ {modalContent}
+
+
+ );
+};
+
+export { useModal, ModalProvider };
diff --git a/src/core/styles/variables.scss b/src/core/styles/variables.scss
index 5c8c13e..e314f32 100644
--- a/src/core/styles/variables.scss
+++ b/src/core/styles/variables.scss
@@ -24,6 +24,7 @@ $color-darkgray: #999999;
$color-text: #333333;
$color-text-light: #777777;
$color-green: #23A455;
+$color-green-hover: #23A455C2;
$color-link: #333333;
$color-link-hover: #009283;
$color-error: #ff0000;
diff --git a/src/feature/article/consultation-modal/index.ts b/src/feature/article/consultation-modal/index.ts
new file mode 100644
index 0000000..5ecdd1f
--- /dev/null
+++ b/src/feature/article/consultation-modal/index.ts
@@ -0,0 +1 @@
+export * from './ui';
diff --git a/src/feature/article/consultation-modal/styles.module.scss b/src/feature/article/consultation-modal/styles.module.scss
new file mode 100644
index 0000000..8bb35ad
--- /dev/null
+++ b/src/feature/article/consultation-modal/styles.module.scss
@@ -0,0 +1,46 @@
+.Form {
+ display: flex;
+ flex-direction: column;
+ gap: rem(16px);
+
+ @include iflaptop {
+ gap: rem(20px);
+ }
+}
+
+.Title {
+ font-family: $font-roboto;
+ font-weight: $font-medium;
+ font-size: rem(20px);
+ line-height: 130%;
+ color: $color-text;
+
+ @include ifdesktop {
+ font-size: rem(24px);
+ }
+}
+
+.Description {
+ font-family: $font-roboto;
+ font-weight: $font-regular;
+ font-size: rem(16px);
+ line-height: 100%;
+ color: $color-text;
+}
+
+.Agreement {
+ font-family: $font-roboto;
+ font-weight: $font-regular;
+ font-size: rem(14px);
+ line-height: 100%;
+ color: $color-text;
+
+ a {
+ color: $color-green;
+
+ &:hover {
+ color: $color-green;
+ text-decoration: underline;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/feature/article/consultation-modal/ui.tsx b/src/feature/article/consultation-modal/ui.tsx
new file mode 100644
index 0000000..beba03d
--- /dev/null
+++ b/src/feature/article/consultation-modal/ui.tsx
@@ -0,0 +1,145 @@
+'use client';
+
+import s from './styles.module.scss';
+import { Button, Input } from '@/shared/ui';
+import { PhoneInput, TextArea } from '@shared/ui';
+import { z } from 'zod';
+import { isValidPhoneNumber } from 'libphonenumber-js/min';
+import { Controller, useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { sendFormFn } from '@shared/api/api.service';
+import toast from 'react-hot-toast';
+import { ModalContent } from '@shared/ui/modal/modal-content';
+import { useModal } from '@core/providers/modal-provider';
+import Link from 'next/link';
+
+const FormSchema = z.object({
+ name: z
+ .string()
+ .min(3, { message: 'Поле должно содержать не менее 3-х букв' })
+ .regex(/^[A-Za-zА-Яа-яЁё]+(?:[ '-][A-Za-zА-Яа-яЁё]+)*$/, {
+ message: 'Поле содержит некорректные символы',
+ }),
+ phone: z.string().refine(isValidPhoneNumber, 'Некорректный номер телефона'),
+ message: z
+ .string()
+ .min(21, { message: 'Оставьте сообщение мин. 20 символов' }),
+});
+type TForm = z.infer;
+
+const defaultValues = {
+ name: '',
+ phone: '',
+ message: '',
+};
+
+type ConsultationModalProps = {
+ className?: string;
+};
+
+function ConsultationModal({}: ConsultationModalProps) {
+ const {
+ handleSubmit,
+ control,
+ reset,
+ clearErrors,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(FormSchema),
+ defaultValues,
+ });
+
+ const modal = useModal();
+
+ const onSubmit = async (data: TForm) => {
+ const payload = {
+ ...data,
+ form: 'consultation-modal-form',
+ };
+
+ try {
+ await sendFormFn(payload);
+ toast.success('Заявка на консультацию принята');
+ } catch (e) {
+ toast.error('Ошибка при отправке заявки...', {
+ duration: 3000,
+ });
+ } finally {
+ modal.hideModal();
+ reset(defaultValues);
+ }
+ };
+
+ return (
+
+
+
+ );
+}
+
+export { ConsultationModal };
diff --git a/src/feature/article/index.ts b/src/feature/article/index.ts
index 70b0661..b164fea 100644
--- a/src/feature/article/index.ts
+++ b/src/feature/article/index.ts
@@ -1,3 +1,4 @@
export * from './related-articles';
export * from './consultation';
export * from './sidebar';
+export * from './consultation-modal';
diff --git a/src/feature/article/sidebar/styles.module.scss b/src/feature/article/sidebar/styles.module.scss
index fe0d7a7..5b60176 100644
--- a/src/feature/article/sidebar/styles.module.scss
+++ b/src/feature/article/sidebar/styles.module.scss
@@ -2,7 +2,7 @@
.Sidebar {
display: none;
- @include iflaptop{
+ @include iflaptop {
display: flex;
flex-direction: column;
gap: rem(40px);
@@ -54,7 +54,7 @@
color: $color-white;
text-transform: uppercase;
- @include ifdesktop{
+ @include ifdesktop {
font-size: rem(32px);
}
}
@@ -90,6 +90,14 @@
line-height: 130%;
color: $color-text;
list-style: unset;
+
+ a {
+ color: $color-green;
+ }
+ a:hover {
+ color: $color-green-hover;
+ text-decoration: underline;
+ }
}
}
}
\ No newline at end of file
diff --git a/src/feature/article/sidebar/ui.tsx b/src/feature/article/sidebar/ui.tsx
index c37b0a2..a7e7673 100644
--- a/src/feature/article/sidebar/ui.tsx
+++ b/src/feature/article/sidebar/ui.tsx
@@ -1,7 +1,12 @@
+'use client';
+
import s from './styles.module.scss';
import Link from 'next/link';
import { Button } from '@shared/ui';
import { TSidebar } from '@shared/types/sidebar';
+import { useModal } from '@core/providers/modal-provider';
+import { ConsultationModal } from '@/feature/article';
+import { CONTACTS } from '@shared/const/contacts';
type SidebarProps = TSidebar;
@@ -12,6 +17,11 @@ function Sidebar({
warrantiesTitle,
warranties,
}: SidebarProps) {
+ const modal = useModal();
+ const openModal = () => modal.showModal();
+
+ const callTo = `tel:${CONTACTS.PHONE}`;
+
return (