diff --git a/src/app/[locale]/experts/[expertId]/page.tsx b/src/app/[locale]/experts/[expertId]/page.tsx
index ca5bb14..57c1195 100644
--- a/src/app/[locale]/experts/[expertId]/page.tsx
+++ b/src/app/[locale]/experts/[expertId]/page.tsx
@@ -2,18 +2,79 @@ import React from 'react';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { Link } from '../../../../navigation';
+import { getExpertById, getExpertsList } from '../../../../actions/experts';
+import {
+ ExpertCard,
+ ExpertCertificate,
+ ExpertInformation,
+ ExpertPractice
+} from '../../../../components/Experts/ExpertDetails';
+import { Details } from '../../../../types/experts';
export const metadata: Metadata = {
title: 'Bbuddy - Experts item',
description: 'Bbuddy desc experts'
};
-export function generateStaticParams() {
- return [{ expertId: '1' }, { expertId: '2' }];
+export async function generateStaticParams({
+ params: { locale },
+}: { params: { locale: string } }) {
+ const result: { locale: string, expertId: string }[] = [];
+ const experts = await getExpertsList({
+ "themesTagIds": [
+ 1,2,3,4,5,6,7,8
+ ],
+ "priceFrom": null,
+ "priceTo": null,
+ "durationFrom": null,
+ "durationTo": null
+ }, locale);
+
+ experts?.forEach(({ id }) => {
+ result.push({ locale, expertId: id.toString() });
+ });
+
+ return result;
}
-export default function ExpertItem({ params }: { params: { expertId: string } }) {
- if (!params?.expertId) notFound();
+export default async function ExpertItem({ params: { expertId = '', locale} }: { params: { expertId: string, locale: string } }) {
+ if (!expertId) notFound();
+
+ const expert = await getExpertById(expertId, locale);
+
+ const getAssociationLevel = (accLevelId?: number) => {
+ if (accLevelId) {
+ const [cur] = (expert?.associationLevels || []).filter(({ id }) => id === accLevelId) || [];
+
+ return cur?.name || '';
+ }
+
+ return '';
+ };
+
+ const getAssociation = (accLevelId?: number) => {
+ if (accLevelId) {
+ const [curLevel] = (expert?.associationLevels || []).filter(({ id }) => id === accLevelId) || [];
+ if (curLevel) {
+ const [cur] = (expert?.associations || []).filter(({ id }) => id === curLevel.associationId) || [];
+ return cur?.name || '';
+ }
+ }
+
+ return '';
+ };
+
+ const generateDescription = ({ id, title, description, document }: Details) => (
+
+
{title}
+
{description}
+ {document && (
+
+
+
+ )}
+
+ );
return (
@@ -24,125 +85,37 @@ export default function ExpertItem({ params }: { params: { expertId: string } })
Back to experts list
-
-
-
-
-
-
-
{`Matthew Weeks | id ${params.expertId}`}
-
- 12 Practice hours
- |
- 15 Supervision per year
-
-
-
-
4/5 (out of 345)
-
-
-
-
-
-
Current Offer
-
-
Engineering & Data
-
Communication
-
Communication
-
-
- Hello, my name is Marcelo. I am a Senior UX Designer with more than 6 years of experience working
- with the largest companies in the world such as Disney, Globant and currently IBM.
- During my career, I have helped organizations solve complex problems using aesthetically pleasing
- designs with design while building teams and mentoring other designers.
- I can help you:
- Prepare for interviews, prepare your resume or CV and work on your LinkedIn profile
- Get ready for whiteboard challenges and take-home exercises
- Create a great portfolio and case study presentation.
- Provide industry information.
- Professional orientation
- Strategic thinking
+
+
- Oh, and I also speak Spanish!
-
-
Expert Background
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
Education
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
-
-
-
Professional Certification
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
Trainings | Seminars | Courses
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
MBA Information
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
Managerial Experience
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
Entrepreneurial Experience
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
Successful Cases From Practice
-
-
-
Engineering & Data
-
Communication
-
Communication
+ {expert?.publicCoachDetails?.educations && expert.publicCoachDetails.educations?.map(generateDescription)}
+ {expert?.publicCoachDetails?.certificates && expert.publicCoachDetails.certificates.length > 0 && (
+
+
Professional Certification
+ {expert.publicCoachDetails.certificates?.map((cert) => (
+
+
+ {`${getAssociationLevel(cert?.associationLevelId)} ${getAssociation(cert?.associationLevelId)}`}
+
+ {cert.document && (
+
+
+
+ )}
+
+ ))}
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
-
-
-
Engineering & Data
-
Communication
-
Communication
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, lectus nec viverra
- malesuada, ligula sem tempor risus, non posuere urna diam a libero.
-
-
+ )}
+ {expert?.publicCoachDetails?.trainings && expert.publicCoachDetails.trainings?.map(generateDescription)}
+ {expert?.publicCoachDetails?.mbas && expert.publicCoachDetails.mbas?.map(generateDescription)}
+ {expert?.publicCoachDetails?.experiences && expert.publicCoachDetails.experiences?.map(generateDescription)}
+
+
All Offers by this Expert
diff --git a/src/app/[locale]/experts/page.tsx b/src/app/[locale]/experts/page.tsx
index d1c2faa..ea273ab 100644
--- a/src/app/[locale]/experts/page.tsx
+++ b/src/app/[locale]/experts/page.tsx
@@ -1,14 +1,27 @@
import React from 'react';
import type { Metadata } from 'next';
-import { ExpertsFilter, ExpertsAdditionalFilter } from '../../../components/Experts';
+import { getExpertsList } from '../../../actions/experts';
+import { ExpertsFilter } from '../../../components/Experts/Filter';
+import { ExpertsAdditionalFilter } from '../../../components/Experts/AdditionalFilter';
+import { ExpertsList } from '../../../components/Experts/ExpertsList';
+import { CustomPagination } from '../../../components/view';
export const metadata: Metadata = {
title: 'Bbuddy - Experts',
description: 'Bbuddy desc experts'
};
-export default function Experts({ searchParams }: { searchParams: { [key: string]: string | string[] | undefined } }) {
+export default async function Experts({ params, searchParams }: { params: { locale: string }, searchParams: { [key: string]: string | string[] | undefined } }) {
console.log('search params', searchParams);
+ const data = await getExpertsList({
+ "themesTagIds": [
+ 1,2,3,4,5,6,7,8
+ ],
+ "priceFrom": null,
+ "priceTo": null,
+ "durationFrom": null,
+ "durationTo": null
+ }, params.locale);
return (
@@ -17,162 +30,17 @@ export default function Experts({ searchParams }: { searchParams: { [key: string
Find a expert
-
+
-
+
-
-
-
-
-
-
-
-
Matthew Weeks
-
- 45$ / 45min
-
-
-
-
-
-
Engineering & Data
-
Engineering & Data
-
+6
-
-
-
Senior Software Engineer
-
Auth0
-
- I have worked across a variety of organizations, lead teams, and delivered
- quality software for 8 years. In that time I've worked as an independent
- consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
- also host a podcast https://anchor.fm/work-in-programming where I break down how
- …
-
-
-
-
-
-
-
-
-
-
Matthew Weeks
-
- 45$ / 45min
-
-
-
-
-
-
Engineering & Data
-
Engineering & Data
-
+6
-
-
-
Senior Software Engineer
-
Auth0
-
- I have worked across a variety of organizations, lead teams, and delivered
- quality software for 8 years. In that time I've worked as an independent
- consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
- also host a podcast https://anchor.fm/work-in-programming where I break down how
- …
-
-
-
-
-
-
-
-
-
-
Matthew Weeks
-
- 45$ / 45min
-
-
-
-
-
-
Engineering & Data
-
Engineering & Data
-
+6
-
-
-
Senior Software Engineer
-
Auth0
-
- I have worked across a variety of organizations, lead teams, and delivered
- quality software for 8 years. In that time I've worked as an independent
- consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
- also host a podcast https://anchor.fm/work-in-programming where I break down how
- …
-
-
-
-
-
-
-
-
-
-
Matthew Weeks
-
- 45$ / 45min
-
-
-
-
-
-
Engineering & Data
-
Engineering & Data
-
+6
-
-
-
Senior Software Engineer
-
Auth0
-
- I have worked across a variety of organizations, lead teams, and delivered
- quality software for 8 years. In that time I've worked as an independent
- consultant, at agencies as a team lead, and as a senior engineer at Auth0. I
- also host a podcast https://anchor.fm/work-in-programming where I break down how
- …
-
-
-
-
-
-
+
+
diff --git a/src/components/Account/SessionsTabs.tsx b/src/components/Account/SessionsTabs.tsx
new file mode 100644
index 0000000..05906f4
--- /dev/null
+++ b/src/components/Account/SessionsTabs.tsx
@@ -0,0 +1,110 @@
+'use client';
+
+import React from 'react';
+import { Tabs } from 'antd';
+
+export const SessionsTabs = ({ intlConfig }: { intlConfig: Record
}) => {
+ const getChildren = () => (
+ <>
+
+
+
+
+
+
+
+
+
Matthew Weeks
+
+ Personal Growth Course
+
+
+ Today 10:00 AM - 10:30 AM
+
+
+
+
+
+
+
+
+
+
+
Matthew Weeks
+
+ Personal Growth Course
+
+
+ 8 december at 10:00 AM - 10:30 AM
+
+
+
+
+
+
+
+
+
+
+
Matthew Weeks
+
+ Personal Growth Course
+
+
+ 8 december at 10:00 AM - 10:30 AM
+
+
+
+
+
+ >
+ );
+
+ const tabs = [
+ {
+ key: 'upcoming',
+ label: (
+
+ {intlConfig?.upcoming || 'Tab 1'}
+ 3
+
+ ),
+ children: getChildren()
+ },
+ {
+ key: 'requested',
+ label: (
+
+ {intlConfig?.requested || 'Tab 2'}
+ 2
+
+ ),
+ children: getChildren()
+ },
+ {
+ key: 'recent',
+ label: (
+
+ {intlConfig?.recent || 'Tab 3'}
+
+ ),
+ children: getChildren()
+ }
+ ];
+
+ return (
+ ( )}
+ />
+ );
+};
diff --git a/src/components/Account/index.ts b/src/components/Account/index.ts
index 54502a1..9f820ac 100644
--- a/src/components/Account/index.ts
+++ b/src/components/Account/index.ts
@@ -1 +1,2 @@
export { AccountMenu } from './AccountMenu';
+export { SessionsTabs } from './SessionsTabs';
diff --git a/src/components/Experts/AdditionalFilter.tsx b/src/components/Experts/AdditionalFilter.tsx
index fc15404..18eb916 100644
--- a/src/components/Experts/AdditionalFilter.tsx
+++ b/src/components/Experts/AdditionalFilter.tsx
@@ -1,10 +1,11 @@
'use client';
import React, { useCallback, useState } from 'react';
-import { Input, Select } from 'antd';
+import { Select } from 'antd';
import { AdditionalFilter } from '../../types/experts';
import { INITIAL_ADD_FILTER } from '../../constants/experts';
import { LOCALES } from '../../constants/locale';
+import { CustomInput } from '../view';
export const ExpertsAdditionalFilter = () => {
const [filter, setFilter] = useState(INITIAL_ADD_FILTER);
@@ -19,15 +20,20 @@ export const ExpertsAdditionalFilter = () => {
return (
-
+ onChangeFilter('text', e?.target?.value)}
+ />
-
onChangeFilter('text', e?.target?.value)}/>
onChangeFilter('sort', val)}
options={[
- { value: 'By top views', label: 'By top views' }
+ { value: 'By top views', label: 'By top views' },
+ { value: 'By Price Ascending', label: 'By Price Ascending' },
+ { value: 'By Price Descending', label: 'By Price Descending' },
+ { value: 'By rating', label: 'By rating' }
]}
/>
diff --git a/src/components/Experts/ExpertDetails.tsx b/src/components/Experts/ExpertDetails.tsx
new file mode 100644
index 0000000..f807988
--- /dev/null
+++ b/src/components/Experts/ExpertDetails.tsx
@@ -0,0 +1,144 @@
+'use client';
+
+import React, { FC } from 'react';
+import Image from 'next/image';
+import { Tag, Image as AntdImage, Space } from 'antd';
+import { ZoomInOutlined, ZoomOutOutlined, StarFilled } from '@ant-design/icons';
+import { ExpertDetails, ExpertDocument } from '../../types/experts';
+import { Locale } from '../../types/locale';
+import { CustomRate } from '../view/CustomRate';
+
+type ExpertDetailsProps = {
+ expert: ExpertDetails;
+ locale?: string;
+};
+
+export const ExpertCard: FC
= ({ expert }) => {
+ const { publicCoachDetails } = expert || {};
+
+ return (
+
+
+
+
+
+
+
{`${publicCoachDetails?.name} ${publicCoachDetails?.surname || ''}`}
+
+ {`${publicCoachDetails?.practiceHours} Practice hours`}
+ |
+ {`${publicCoachDetails?.supervisionPerYearId} Supervision per year`}
+
+
+ } disabled />
+ 4/5 (out of 345)
+
+
+
+
+
+ );
+};
+
+export const ExpertInformation: FC = ({ expert, locale }) => {
+ const { publicCoachDetails: { tags = [], sessionCost = 0, sessionDuration = 0 } } = expert || {};
+ const isRus = locale === Locale.ru;
+
+ return (
+ <>
+ Current Offer
+
+ {tags?.map((skill) => {skill?.name} )}
+
+
+ Hello, my name is Marcelo. I am a Senior UX Designer with more than 6 years of experience working
+ with the largest companies in the world such as Disney, Globant and currently IBM.
+ During my career, I have helped organizations solve complex problems using aesthetically pleasing
+ designs with design while building teams and mentoring other designers.
+ I can help you:
+ Prepare for interviews, prepare your resume or CV and work on your LinkedIn profile
+ Get ready for whiteboard challenges and take-home exercises
+ Create a great portfolio and case study presentation.
+ Provide industry information.
+ Professional orientation
+ Strategic thinking
+
+ Oh, and I also speak Spanish!
+
+
+
+ Sign Up Now
+
+
+ {`${sessionCost}${isRus ? '₽' : '€'}`} / {`${sessionDuration}${isRus ? 'мин' : 'min'}`}
+
+
+ >
+ );
+};
+
+export const ExpertPractice: FC = ({ expert }) => {
+ const { publicCoachDetails: { practiceCases = [], themesGroups = [] } } = expert || {};
+
+ return practiceCases?.length > 0 ? (
+
+
Successful Cases From Practice
+ {practiceCases?.map(({ id, description, themesGroupIds }) => {
+ const filtered = themesGroups?.filter(({ id }) => themesGroupIds?.includes(+id));
+
+ return (
+
+ {themesGroupIds && (
+
+ {filtered?.map(({ id, name }) => (
+
{name}
+ ))}
+
+ )}
+ {description &&
{description}
}
+
+ )
+ })}
+
+ ) : null;
+};
+
+export const ExpertCertificate: FC<{ document: ExpertDocument }> = ({ document }) => (
+
+ )}
+ fallback="data:image/png;base64,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"
+ preview={{
+ src: document.fullSize?.url,
+ toolbarRender: (
+ _,
+ {
+ transform: { scale },
+ actions: { onZoomOut, onZoomIn },
+ },
+ ) => (
+
+
+
+
+ )
+ }}
+ />
+);
diff --git a/src/components/Experts/ExpertsList.tsx b/src/components/Experts/ExpertsList.tsx
new file mode 100644
index 0000000..65d5ce3
--- /dev/null
+++ b/src/components/Experts/ExpertsList.tsx
@@ -0,0 +1,66 @@
+'use client';
+
+import React from 'react';
+import { List, Tag } from 'antd';
+import { RightOutlined } from '@ant-design/icons';
+import Image from 'next/image';
+import { Link } from '../../navigation';
+import { Locale } from '../../types/locale';
+import { ExpertsData } from '../../types/experts';
+
+export const ExpertsList = ({ data, locale }: { data: ExpertsData, locale: string }) => {
+ const isRus = locale === Locale.ru;
+
+ return (
+ (
+
+
+
+
+ )}
+ description={(
+
+
+
{`${item.name} ${item?.surname || ''}`}
+
+
+ {`${item?.sessionCost}${isRus ? '₽' : '€'}`} / {`${item?.sessionDuration}${isRus ? 'мин' : 'min'}`}
+
+
+ )}
+ />
+
+
+ {item?.tags?.slice(0, 2).map((skill) => {skill?.name} )}
+ {item?.tags?.length > 2
+ ? (
+
+
+ {`+${item?.tags?.length - 2}`}
+
+
+ ) : null}
+
+
+ {item?.speciality}
+ {item?.specialityDesc}
+ {item?.description}
+
+
+ Details
+
+
+
+
+ )}
+ />
+ );
+};
diff --git a/src/components/Experts/Filter.tsx b/src/components/Experts/Filter.tsx
index c246906..f272454 100644
--- a/src/components/Experts/Filter.tsx
+++ b/src/components/Experts/Filter.tsx
@@ -1,77 +1,11 @@
'use client';
import React, { useCallback, useState } from 'react';
-import { List, Switch, Slider } from 'antd';
-import styled from 'styled-components';
+import { List } from 'antd';
import { FilterType, INITIAL_FILTER } from '../../constants/experts';
import { Filter } from '../../types/experts';
-
-const CustomSwitch = styled(Switch)`
- height: 24px !important;
- background: #F8F8F7 !important;
-
- .ant-switch-handle {
- height: 24px !important;
- width: 24px !important;
- top: 0 !important;
-
- &::before {
- box-shadow: none !important;
- background: #C4DFE6 !important;
- border-radius: 50% !important;
- }
- }
-
- &.ant-switch-checked {
- background: #2c7873 !important;
- }
-`;
-
-const WrapSlider = styled.div`
- width: 100%;
- position: relative;
- margin: 8px 0 16px -5px;
-`;
-
-const CustomSlider = styled(Slider)`
- padding-block: 7px !important;
- height: 16px !important;
-
- .ant-slider-rail {
- background-color: #E5E5E5 !important;
- }
-
- .ant-slider-track {
- background-color: #66A5AD !important;
- }
-
- .ant-slider-handle {
- width: 16px !important;
- height: 16px !important;
-
- &::before {
- width: 16px !important;
- height: 16px !important;
- inset-inline-start: 0 !important;
- inset-block-start: 0 !important;
- }
-
- &::after {
- width: 16px !important;
- height: 16px !important;
- background-color: #66A5AD !important;
- inset-inline-start: 0 !important;
- inset-block-start: 0 !important;
- box-shadow: none !important;
- }
-
- &:focus, &:hover {
- &::after {
- box-shadow: 0 0 0 10px rgba(102, 165, 173, .2) !important;
- }
- }
- }
-`;
+import { CustomSwitch, CustomSlider } from '../view';
+import {Locale} from "../../types/locale";
const dataCoaching = [
{
@@ -114,8 +48,9 @@ const dataConsultation = [
}
];
-export const ExpertsFilter = () => {
+export const ExpertsFilter = ({ locale }: { locale: string }) => {
const [filter, setFilter] = useState(INITIAL_FILTER);
+ const isRus = locale === Locale.ru;
const onChangeFilter = useCallback((key: string, value: any) => {
setFilter({
@@ -138,7 +73,7 @@ export const ExpertsFilter = () => {
{title}
onChangeFilter(key, checked)}
+ onChange={(checked: boolean) => onChangeFilter(key, checked)}
/>
@@ -155,28 +90,28 @@ export const ExpertsFilter = () => {
{getList(dataMentoring)}
Business-consultation
{getList(dataConsultation)}
-
Price from 45€ to 170€
-
+ {`Price from 45${isRus ? '₽' : '€'} to 170${isRus ? '₽' : '€'}`}
+
onChangeFilter(FilterType.Price, val)}
+ onChange={(val: any) => onChangeFilter(FilterType.Price, val)}
/>
-
- Duration from 45m to 120m
-
+
+ {`Duration from 45${isRus ? 'мин' : 'min'} to 120${isRus ? 'мин' : 'min'}`}
+
onChangeFilter(FilterType.Duration, val)}
+ onChange={(val: any) => onChangeFilter(FilterType.Duration, val)}
/>
-
+
Apply
);
diff --git a/src/components/Experts/index.ts b/src/components/Experts/index.ts
deleted file mode 100644
index 468247d..0000000
--- a/src/components/Experts/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './Filter';
-export * from './AdditionalFilter';
diff --git a/src/components/Modals/AuthModal.tsx b/src/components/Modals/AuthModal.tsx
new file mode 100644
index 0000000..c8df254
--- /dev/null
+++ b/src/components/Modals/AuthModal.tsx
@@ -0,0 +1,281 @@
+'use client';
+
+import React, { FC, useState } from 'react';
+import { usePathname } from 'next/navigation';
+import Image from 'next/image';
+import {Button, Modal as AntdModal, Form, notification} from 'antd';
+import { CloseOutlined } from '@ant-design/icons';
+import { styled } from 'styled-components';
+import { CustomInput } from '../view';
+import { getAuth } from '../../actions/auth';
+
+type AuthModalProps = {
+ open: boolean;
+ handleCancel: () => void;
+ mode: 'enter' | 'register' | 'reset' | 'finish';
+ updateMode: (mode: 'enter' | 'register' | 'reset' | 'finish') => void;
+};
+
+const Modal = styled(AntdModal)`
+ .ant-modal-content {
+ border-radius: 24px !important;
+ }
+
+ .ant-modal-close {
+ height: 64px !important;
+ width: 64px !important;
+ border-radius: 50% !important;
+ background: #fff !important;
+ top: -32px !important;
+ inset-inline-end: -32px !important;
+
+ &:active, &:hover {
+ background: #fff !important;
+ }
+ }
+`;
+
+const FilledButton = styled(Button)`
+ background: #66A5AD !important;
+ font-size: 15px !important;
+ border-radius: 8px !important;
+ height: 54px !important;
+ box-shadow: 0px 2px 4px 0px rgba(102, 165, 173, 0.32) !important;
+`;
+
+const OutlinedButton = styled(Button)`
+ display: inline-flex !important;
+ justify-content: center;
+ align-items: center;
+ gap: 16px;
+ border-color: #66A5AD !important;
+ color: #66A5AD !important;
+ font-size: 15px !important;
+ border-radius: 8px !important;
+ height: 54px !important;
+
+ span {
+ margin-inline-end: 0 !important;
+ line-height: 15px !important;
+ }
+`;
+
+const LinkButton = styled(Button)`
+ color: #66A5AD !important;
+ font-size: 15px !important;
+ height: auto !important;
+ padding: 0 !important;
+`;
+
+export const AuthModal: FC
= ({
+ open,
+ handleCancel,
+ mode,
+ updateMode
+}) => {
+ const [form] = Form.useForm<{ login: string, password: string, name: string, surname: string, phone: string }>();
+ const [isLoading, setIsLoading] = useState(false);
+ const paths = usePathname().split('/');
+
+ const onAfterClose = () => {
+ form.resetFields();
+ };
+
+ const onSubmit = () => {
+ form.validateFields().then(() => {
+ const { login, password } = form.getFieldsValue();
+ setIsLoading(true);
+ getAuth(login, password, paths[1])
+ .then(({ data }) => {
+ console.log('jwt', data.jwtToken)
+ })
+ .catch((error) => {
+ notification.error({
+ message: 'Error',
+ description: error?.response?.data?.errMessage
+ });
+ })
+ .finally(() => {
+ setIsLoading(false);
+ })
+ });
+ };
+
+ const onValidate = () => {
+
+ };
+
+ return (
+ }
+ >
+
+
+
+
+ {mode === 'enter' && (
+ <>
+
+
+
+
+
+
+
+
+ Enter
+
+
updateMode('register')}>Register
+
updateMode('reset')}
+ >
+ Forgot password?
+
+
or
+
}
+ >
+ Facebook account
+
+
}
+ >
+ Apple account
+
+
}
+ >
+ Google account
+
+ >
+ )}
+ {mode === 'register' && (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Register
+
+
or
+
}
+ >
+ Facebook account
+
+
}
+ >
+ Apple account
+
+
}
+ >
+ Google account
+
+ >
+ )}
+ {mode === 'reset' && (
+ <>
+
+
+
+
+
+
+
+
+ Reset Password
+
+
+ Enter
+
+ >
+ )}
+ {mode === 'finish' && (
+ <>
+
+ A link to reset your password has been sent
+
+ to your email
+
+
+ Enter Account
+
+ >
+ )}
+
+ I have read and agree with the terms of the
+ User Agreement, Privacy Policy
+
+
+
+ );
+};
diff --git a/src/components/Page/Header/HeaderAuthLinks.tsx b/src/components/Page/Header/HeaderAuthLinks.tsx
index ebb60ff..dc61b9c 100644
--- a/src/components/Page/Header/HeaderAuthLinks.tsx
+++ b/src/components/Page/Header/HeaderAuthLinks.tsx
@@ -1,24 +1,72 @@
-import React, { FC } from 'react';
-import { useTranslations } from 'next-intl';
+'use client';
+
+import React, { FC, useState, useEffect } from 'react';
+import { Button } from 'antd';
+import { styled } from 'styled-components';
+import { AuthModal } from '../../Modals/AuthModal';
type HeaderAuthLinksProps = {
+ enterTitle: string;
+ registerTitle: string;
separatorClass?: string;
};
-export const HeaderAuthLinks: FC = ({ separatorClass = 'b-header__nav__list__line' }) => {
- const t = useTranslations('Header');
+const LinkButton = styled(Button)`
+ color: #66A5AD !important;
+ font-size: 16px !important;
+ height: auto !important;
+ padding: 0 !important;
+ font-style: normal !important;
+ font-weight: 600 !important;
+ line-height: normal !important;
+`;
+
+export const HeaderAuthLinks: FC = ({
+ enterTitle,
+ registerTitle,
+ separatorClass = 'b-header__nav__list__line'
+}) => {
+ const [isOpenModal, setIsOpenModal] = useState(false);
+ const [mode, setMode] = useState<'enter' | 'register' | 'reset' | 'finish'>('enter');
+
+ useEffect(() => {
+ if (!isOpenModal) {
+ setMode('enter');
+ }
+ }, [isOpenModal]);
+
+ const onOpen = (mode: 'enter' | 'register' | 'reset' | 'finish') => {
+ setMode(mode);
+ setIsOpenModal(true);
+ };
return (
<>
- {t('registration')}
+ onOpen('register')}
+ >
+ {registerTitle}
+
|
- {t('enter')}
+ onOpen('enter')}
+ >
+ {enterTitle}
+
+ setIsOpenModal(false)}
+ mode={mode}
+ updateMode={setMode}
+ />
>
);
-}
+};
diff --git a/src/components/Page/Header/HeaderMenu.tsx b/src/components/Page/Header/HeaderMenu.tsx
index 3559f14..0653e86 100644
--- a/src/components/Page/Header/HeaderMenu.tsx
+++ b/src/components/Page/Header/HeaderMenu.tsx
@@ -1,12 +1,18 @@
import React, { ReactNode } from 'react';
import { HeaderAuthLinks } from './HeaderAuthLinks';
-export const HeaderMenu = ({ children }: { children: ReactNode }) => (
+type HeaderMenuProps = {
+ children: ReactNode;
+ enterTitle: string;
+ registerTitle: string;
+};
+
+export const HeaderMenu = ({ children, enterTitle, registerTitle }: HeaderMenuProps) => (
diff --git a/src/components/Page/Header/HeaderMobileMenu.tsx b/src/components/Page/Header/HeaderMobileMenu.tsx
index 00ff171..54352fc 100644
--- a/src/components/Page/Header/HeaderMobileMenu.tsx
+++ b/src/components/Page/Header/HeaderMobileMenu.tsx
@@ -5,9 +5,11 @@ import { HeaderAuthLinks } from './HeaderAuthLinks';
type HeaderMenuMobileProps = {
children: ReactNode;
+ enterTitle: string;
+ registerTitle: string;
};
-export const HeaderMobileMenu: FC = ({ children }) => {
+export const HeaderMobileMenu: FC = ({ children, enterTitle, registerTitle }) => {
const [showMobileMenu, setShowMobileMenu] = useState(false);
return (
@@ -27,7 +29,11 @@ export const HeaderMobileMenu: FC = ({ children }) => {