From 9f225294c7a7b887489665796425c3a262bd1146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=8E=D1=82=D0=BA=D0=B8=D0=BD=D0=B0=20=D0=94=D0=B0?= =?UTF-8?q?=D1=80=D1=8C=D1=8F=20=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD?= =?UTF-8?q?=D0=B4=D1=80=D0=BE=D0=B2=D0=BD=D0=B0=20=284047910=29?= Date: Thu, 1 Feb 2024 18:49:09 +0400 Subject: [PATCH] feat: add custom form fields, fix auth modal --- package-lock.json | 43 ++++++ package.json | 3 + src/app/[locale]/(main)/@experts/page.tsx | 18 +-- .../blog/{[newsId] => [blogId]}/page.tsx | 12 +- src/app/[locale]/blog/page.tsx | 6 +- src/app/[locale]/experts/page.tsx | 17 +-- src/components/Experts/AdditionalFilter.tsx | 74 +++++++--- src/components/Experts/Experts.tsx | 30 ++-- src/components/Experts/ExpertsList.tsx | 136 +++++++++++------- src/components/Experts/Filter.tsx | 22 +-- src/components/Modals/AuthModal.tsx | 60 ++------ .../Page/Header/HeaderAuthLinks.tsx | 9 +- src/components/Page/Header/HeaderMenu.tsx | 42 ++++-- .../Page/Header/HeaderMobileMenu.tsx | 21 ++- src/components/Page/Header/index.tsx | 27 ++-- src/components/view/CustomInputPassword.tsx | 45 ++++++ src/components/view/CustomMultiSelect.tsx | 106 ++++++++++++++ src/components/view/CustomSelect.tsx | 85 +++++++++++ src/components/view/CustomSpin.tsx | 12 ++ src/components/view/index.ts | 4 + src/middleware.ts | 1 - src/styles/_default.scss | 18 ++- src/styles/_footer.scss | 2 +- src/styles/_header.scss | 4 + src/styles/_main.scss | 2 +- src/utils/filter.ts | 2 +- src/utils/storage/auth.ts | 8 +- 27 files changed, 587 insertions(+), 222 deletions(-) rename src/app/[locale]/blog/{[newsId] => [blogId]}/page.tsx (97%) create mode 100644 src/components/view/CustomInputPassword.tsx create mode 100644 src/components/view/CustomSpin.tsx diff --git a/package-lock.json b/package-lock.json index bba5b5d..48e3528 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@ant-design/icons": "^5.2.6", "antd": "^5.12.1", "axios": "^1.6.5", + "lodash": "^4.17.21", "next": "14.0.3", "next-intl": "^3.3.1", "react": "^18", @@ -22,9 +23,11 @@ }, "devDependencies": { "@next/eslint-plugin-next": "^14.0.4", + "@types/lodash": "^4.14.202", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "@types/react-slick": "^0.23.13", "autoprefixer": "^10.0.1", "eslint": "^8.55.0", "eslint-config-next": "^14.0.3", @@ -640,6 +643,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "node_modules/@types/node": { "version": "20.9.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz", @@ -675,6 +684,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-slick": { + "version": "0.23.13", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.13.tgz", + "integrity": "sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", @@ -3209,6 +3227,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -5705,6 +5728,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "@types/node": { "version": "20.9.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz", @@ -5740,6 +5769,15 @@ "@types/react": "*" } }, + "@types/react-slick": { + "version": "0.23.13", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.13.tgz", + "integrity": "sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", @@ -7606,6 +7644,11 @@ "p-locate": "^5.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", diff --git a/package.json b/package.json index ac8b2e7..f34c878 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@ant-design/icons": "^5.2.6", "antd": "^5.12.1", "axios": "^1.6.5", + "lodash": "^4.17.21", "next": "14.0.3", "next-intl": "^3.3.1", "react": "^18", @@ -23,9 +24,11 @@ }, "devDependencies": { "@next/eslint-plugin-next": "^14.0.4", + "@types/lodash": "^4.14.202", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "@types/react-slick": "^0.23.13", "autoprefixer": "^10.0.1", "eslint": "^8.55.0", "eslint-config-next": "^14.0.3", diff --git a/src/app/[locale]/(main)/@experts/page.tsx b/src/app/[locale]/(main)/@experts/page.tsx index 8f6fd3c..44eee4d 100644 --- a/src/app/[locale]/(main)/@experts/page.tsx +++ b/src/app/[locale]/(main)/@experts/page.tsx @@ -1,16 +1,9 @@ import React from 'react'; -import { getTranslations } from 'next-intl/server'; -import { getFilter } from '../../../../utils/filter'; -import { getExpertsList } from '../../../../actions/experts'; -import { getTagList } from '../../../../actions/tags'; +import { useTranslations } from 'next-intl'; import { Experts } from '../../../../components/Experts/Experts'; -export default async function ExpertsPage({ params, searchParams }: { params: { locale: string }, searchParams: { [key: string]: string | string[] | undefined } }) { - const searchData = await getTagList(params.locale); - const filter = getFilter(searchData, searchParams); - console.log('filter main page', filter); - const experts = await getExpertsList(filter, params.locale); - const t = await getTranslations('Experts'); +export default function ExpertsPage({ params }: { params: { locale: string } }) { + const t = useTranslations('Experts'); return (
@@ -21,10 +14,7 @@ export default async function ExpertsPage({ params, searchParams }: { params: {
- + ); diff --git a/src/app/[locale]/blog/[newsId]/page.tsx b/src/app/[locale]/blog/[blogId]/page.tsx similarity index 97% rename from src/app/[locale]/blog/[newsId]/page.tsx rename to src/app/[locale]/blog/[blogId]/page.tsx index f09beb4..131333a 100644 --- a/src/app/[locale]/blog/[newsId]/page.tsx +++ b/src/app/[locale]/blog/[blogId]/page.tsx @@ -3,16 +3,16 @@ import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; export const metadata: Metadata = { - title: 'Bbuddy - News item', - description: 'Bbuddy desc news item' + title: 'Bbuddy - Blog item', + description: 'Bbuddy desc blog item' }; export function generateStaticParams() { - return [{ newsId: 'news-1' }, { newsId: 'news-2' }]; + return [{ blogId: 'news-1' }, { blogId: 'news-2' }]; } -export default function NewsItem({ params }: { params: { newsId: string } }) { - if (!params?.newsId) notFound(); +export default function BlogItem({ params }: { params: { blogId: string } }) { + if (!params?.blogId) notFound(); return (
@@ -20,7 +20,7 @@ export default function NewsItem({ params }: { params: { newsId: string } }) {

6 learnings from Shivpuri to Silicon Valley

Leadership & Management
- {`news id ${params.newsId}`}
+ {`news id ${params.blogId}`}
I’m excited to kick off this series of newsletters where I’ll be sharing my experiences, learnings, and best practices which helped me to grow both in my personal and professional life. My hope is to give back to the community and help anyone connect directly with me who may have got impacted with diff --git a/src/app/[locale]/blog/page.tsx b/src/app/[locale]/blog/page.tsx index 235089f..20f2989 100644 --- a/src/app/[locale]/blog/page.tsx +++ b/src/app/[locale]/blog/page.tsx @@ -2,11 +2,11 @@ import React from 'react'; import type { Metadata } from 'next'; export const metadata: Metadata = { - title: 'Bbuddy - News', - description: 'Bbuddy desc news' + title: 'Bbuddy - Blog', + description: 'Bbuddy desc blog' }; -export default function News() { +export default function Blog() { return (
diff --git a/src/app/[locale]/experts/page.tsx b/src/app/[locale]/experts/page.tsx index 33fa566..eafd99a 100644 --- a/src/app/[locale]/experts/page.tsx +++ b/src/app/[locale]/experts/page.tsx @@ -1,9 +1,6 @@ import React from 'react'; import type { Metadata } from 'next'; -import { getTranslations } from 'next-intl/server'; -import { getFilter } from '../../../utils/filter'; -import { getExpertsList } from '../../../actions/experts'; -import { getTagList } from '../../../actions/tags'; +import { useTranslations } from 'next-intl'; import { Experts } from '../../../components/Experts/Experts'; export const metadata: Metadata = { @@ -11,12 +8,8 @@ export const metadata: Metadata = { description: 'Bbuddy desc experts' }; -export default async function ExpertsPage({ params, searchParams }: { params: { locale: string }, searchParams: { [key: string]: string | string[] | undefined } }) { - const searchData = await getTagList(params.locale); - const filter = getFilter(searchData, searchParams); - console.log('filter experts page', filter); - const experts = await getExpertsList(filter, params.locale); - const t = await getTranslations('Experts'); +export default function ExpertsPage({ params }: { params: { locale: string } }) { + const t = useTranslations('Experts'); return (
@@ -29,8 +22,8 @@ export default async function ExpertsPage({ params, searchParams }: { params: {
diff --git a/src/components/Experts/AdditionalFilter.tsx b/src/components/Experts/AdditionalFilter.tsx index b039f69..ed3f0db 100644 --- a/src/components/Experts/AdditionalFilter.tsx +++ b/src/components/Experts/AdditionalFilter.tsx @@ -1,20 +1,20 @@ 'use client'; import React, { useCallback, useState } from 'react'; -import {Button, Select} from 'antd'; +import { Button } from 'antd'; import { useSearchParams } from 'next/navigation'; import { useRouter } from '../../navigation'; import { AdditionalFilter } from '../../types/experts'; import { LOCALES } from '../../constants/locale'; import { getObjectByFilter, getObjectByAdditionalFilter } from '../../utils/filter'; -import { CustomInput } from '../view'; +import { CustomInput, CustomSelect, CustomMultiSelect } from '../view'; type ExpertAdditionalFilterProps = { searchPlaceholder: string; sortLabel: string; langLabel: string; buttonFind: string; - basePath?: string; + basePath: string; }; export const ExpertsAdditionalFilter = ({ @@ -22,28 +22,61 @@ export const ExpertsAdditionalFilter = ({ sortLabel, langLabel, buttonFind, - basePath = '/', -}): ExpertAdditionalFilterProps => { + basePath, +}: ExpertAdditionalFilterProps) => { const searchParams = useSearchParams(); const router = useRouter(); const [filter, setFilter] = useState(getObjectByAdditionalFilter(searchParams)); - const onChangeFilter = useCallback((key: string, value: any) => { - // setFilter({ - // ...filter, - // [key]: value - // }) + const onChangeInput = useCallback((e: any) => { + if (e?.target?.value) { + setFilter({ + ...filter, + text: e.target.value + }); + } else { + if (filter?.text) { + const newFilter = { ...filter }; + delete newFilter.text; + + setFilter(newFilter); + } + } + }, [filter]); + + const onChangeSort = useCallback((value: string) => { + const newFilter: AdditionalFilter = { ...filter }; + + if (value) { + newFilter.sort = value; + } else { + delete newFilter?.sort; + } + + setFilter(newFilter); + }, [filter]); + + const onChangeLang = useCallback((value: string[]) => { + const newFilter: AdditionalFilter = { ...filter }; + + if (value.length > 0) { + newFilter.language = value; + } else { + delete newFilter?.language; + } + + setFilter(newFilter); }, [filter]); const goToFilterPage = useCallback(() => { router.push({ - pathname: basePath, + pathname: basePath as any, query: { ...getObjectByFilter(searchParams), ...filter } }) - }, [filter, searchParams]); + }, [filter, searchParams, router]); return (
@@ -51,13 +84,14 @@ export const ExpertsAdditionalFilter = ({ onChangeFilter('text', e?.target?.value)} + onChange={onChangeInput} />
- onChangeFilter('language', val)} + ({ value, label }))} />
diff --git a/src/components/Experts/Experts.tsx b/src/components/Experts/Experts.tsx index 904058f..3ca44a9 100644 --- a/src/components/Experts/Experts.tsx +++ b/src/components/Experts/Experts.tsx @@ -1,28 +1,31 @@ import React from 'react'; -import { useTranslations } from 'next-intl'; -import { SearchData } from '../../types/tags'; -import { ExpertsData } from '../../types/experts'; +import { getTranslations } from 'next-intl/server'; +import { getTagList } from '../../actions/tags'; +import { getFilter } from '../../utils/filter'; +import { getExpertsList } from '../../actions/experts'; import { ExpertsFilter } from './Filter'; import { ExpertsAdditionalFilter } from './AdditionalFilter'; import { ExpertsList } from './ExpertsList'; -import { CustomPagination } from '../view/CustomPagination'; type ExpertsProps = { - searchData?: SearchData; - experts?: ExpertsData; + basePath?: string; + locale: string; }; -export const Experts = ({ searchData, experts }: ExpertsProps) => { - const t = useTranslations('Experts'); +export const Experts = async ({ basePath = '/', locale }: ExpertsProps) => { + const t = await getTranslations('Experts'); + const searchData = await getTagList(locale); + const filter = getFilter(searchData); + const experts = await getExpertsList(filter, locale); return (
@@ -32,15 +35,16 @@ export const Experts = ({ searchData, experts }: ExpertsProps) => { sortLabel={t('filter.sort')} langLabel={t('filter.language')} buttonFind={t('filter.find')} - basePath="/experts" + basePath={basePath} /> -
) diff --git a/src/components/Experts/ExpertsList.tsx b/src/components/Experts/ExpertsList.tsx index eef04e8..0a16204 100644 --- a/src/components/Experts/ExpertsList.tsx +++ b/src/components/Experts/ExpertsList.tsx @@ -1,77 +1,107 @@ 'use client'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { useSearchParams } from 'next/navigation'; import { List, Tag } from 'antd'; import { RightOutlined } from '@ant-design/icons'; +import isEqual from 'lodash/isEqual'; import Image from 'next/image'; import { Link } from '../../navigation'; -import { ExpertsData } from '../../types/experts'; +import { ExpertsData, Filter } from '../../types/experts'; +import { getObjectByFilter } from '../../utils/filter'; +import { getExpertsList } from '../../actions/experts'; +import { CustomPagination, CustomSpin } from '../view'; type ExpertListProps = { - data: ExpertsData; + data?: ExpertsData; priceTitle: string; durationTitle: string; detailButton: string; + locale: string; + baseFilter: Filter; }; export const ExpertsList = ({ data, priceTitle, durationTitle, - detailButton + detailButton, + locale, + baseFilter }: ExpertListProps) => { + const searchParams = useSearchParams(); const getTitle = (str: string, value?: any): string => (value ? str.replace('0', value) : str); + const [experts, setExperts] = useState(); - return ( - ( - - - - - )} - description={( -
- -
{`${item.name} ${item?.surname || ''}`}
- -
- {getTitle(priceTitle, item?.sessionCost)} / {getTitle(durationTitle, item?.sessionDuration)} + useEffect(() => { + const filter = { + ...baseFilter, + ...getObjectByFilter(searchParams) + }; + + if (!isEqual(baseFilter, filter)) { + getExpertsList(filter, locale) + .then((experts) => { + setExperts(experts); + }); + } else { + setExperts(data); + } + }, []); + + return experts ? ( + <> + ( + + +
+ )} + description={( +
+ +
{`${item.name} ${item?.surname || ''}`}
+ +
+ {getTitle(priceTitle, item?.sessionCost)} / {getTitle(durationTitle, item?.sessionDuration)} +
+
+ )} + /> +
+
+ {item?.tags?.slice(0, 2).map((skill) => {skill?.name})} + {item?.tags?.length > 2 + ? ( + + + {`+${item?.tags?.length - 2}`} + + + ) : null}
- )} - /> -
-
- {item?.tags?.slice(0, 2).map((skill) => {skill?.name})} - {item?.tags?.length > 2 - ? ( - - - {`+${item?.tags?.length - 2}`} - - - ) : null}
-
-
{item?.speciality}
-
{item?.specialityDesc}
-
{item?.description}
-
- - {detailButton} - - -
- - )} - /> - ); +
{item?.speciality}
+
{item?.specialityDesc}
+
{item?.description}
+
+ + {detailButton} + + +
+ + )} + /> + + + ) : ; }; diff --git a/src/components/Experts/Filter.tsx b/src/components/Experts/Filter.tsx index 3f55f0d..f08ac33 100644 --- a/src/components/Experts/Filter.tsx +++ b/src/components/Experts/Filter.tsx @@ -10,8 +10,8 @@ import { getObjectByFilter, getObjectByAdditionalFilter } from '../../utils/filt import { CustomSwitch, CustomSlider } from '../view'; type ExpertsFilterProps = { - searchData: SearchData; - basePath?: string; + searchData?: SearchData; + basePath: string; priceTitle: string; durationTitle: string; buttonApply: string; @@ -19,7 +19,7 @@ type ExpertsFilterProps = { export const ExpertsFilter = ({ searchData, - basePath = '/', + basePath, priceTitle, durationTitle, buttonApply @@ -83,7 +83,7 @@ export const ExpertsFilter = ({ const goToFilterPage = useCallback(() => { router.push({ - pathname: basePath, + pathname: basePath as any, query: { ...filter, ...getObjectByAdditionalFilter(searchParams) @@ -116,7 +116,7 @@ export const ExpertsFilter = ({ return (
- {searchData.themesGroups?.length && searchData.themesGroups.map(({ id, name, tags }) => ( + {searchData?.themesGroups?.length && searchData.themesGroups.map(({ id, name, tags }) => (

{name}

{getList(tags)} @@ -127,9 +127,9 @@ export const ExpertsFilter = ({
@@ -138,9 +138,9 @@ export const ExpertsFilter = ({
diff --git a/src/components/Modals/AuthModal.tsx b/src/components/Modals/AuthModal.tsx index f48f8a7..1ee1f51 100644 --- a/src/components/Modals/AuthModal.tsx +++ b/src/components/Modals/AuthModal.tsx @@ -3,12 +3,13 @@ import React, { FC, useState } from 'react'; import { usePathname } from 'next/navigation'; import Image from 'next/image'; +import Link from 'next/link'; 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 { CustomInput, CustomInputPassword } from '../view'; import { getAuth } from '../../actions/auth'; -import {setAuthToken} from "../../utils/storage/auth"; +import { setAuthToken } from '../../utils/storage/auth'; type AuthModalProps = { open: boolean; @@ -74,7 +75,7 @@ export const AuthModal: FC = ({ mode, updateMode }) => { - const [form] = Form.useForm<{ login: string, password: string, name: string, surname: string, phone: string }>(); + const [form] = Form.useForm<{ login: string, password: string, confirmPassword: string }>(); const [isLoading, setIsLoading] = useState(false); const paths = usePathname().split('/'); @@ -134,10 +135,9 @@ export const AuthModal: FC = ({ /> - @@ -176,25 +176,6 @@ export const AuthModal: FC = ({ {mode === 'register' && ( <>
- - - - - - - - - = ({ /> - + + +
@@ -214,22 +200,7 @@ export const AuthModal: FC = ({ > Register - or - } - > - Facebook account - - } - > - Apple account - - } - > - Google account - + updateMode('enter')}>Enter )} {mode === 'reset' && ( @@ -242,10 +213,9 @@ export const AuthModal: FC = ({ /> - @@ -277,7 +247,7 @@ export const AuthModal: FC = ({ )}
I have read and agree with the terms of the - User Agreement, Privacy Policy + User Agreement, Privacy Policy
diff --git a/src/components/Page/Header/HeaderAuthLinks.tsx b/src/components/Page/Header/HeaderAuthLinks.tsx index 6ffeaf8..e4143f1 100644 --- a/src/components/Page/Header/HeaderAuthLinks.tsx +++ b/src/components/Page/Header/HeaderAuthLinks.tsx @@ -2,10 +2,11 @@ import React, { FC, useState, useEffect } from 'react'; import { Button } from 'antd'; +import { useSelectedLayoutSegment } from 'next/navigation'; import { styled } from 'styled-components'; import { AuthModal } from '../../Modals/AuthModal'; -import {checkAuthToken} from "../../../utils/storage/auth"; -import {Link} from "../../../navigation"; +import { checkAuthToken } from '../../../utils/storage/auth'; +import { Link } from '../../../navigation'; type HeaderAuthLinksProps = { enterTitle: string; @@ -32,6 +33,8 @@ export const HeaderAuthLinks: FC = ({ }) => { const [isOpenModal, setIsOpenModal] = useState(false); const [mode, setMode] = useState<'enter' | 'register' | 'reset' | 'finish'>('enter'); + const selectedLayoutSegment = useSelectedLayoutSegment(); + const pathname = selectedLayoutSegment || ''; useEffect(() => { if (!isOpenModal) { @@ -47,7 +50,7 @@ export const HeaderAuthLinks: FC = ({ return checkAuthToken() ? (
  • - {accountTitle} + {accountTitle}
  • ) : ( diff --git a/src/components/Page/Header/HeaderMenu.tsx b/src/components/Page/Header/HeaderMenu.tsx index 1731c81..de5776b 100644 --- a/src/components/Page/Header/HeaderMenu.tsx +++ b/src/components/Page/Header/HeaderMenu.tsx @@ -1,20 +1,38 @@ -import React, { ReactNode } from 'react'; +'use client'; + +import React from 'react'; +import { useSelectedLayoutSegment } from 'next/navigation'; +import { Link } from '../../../navigation'; import { HeaderAuthLinks } from './HeaderAuthLinks'; type HeaderMenuProps = { - children: ReactNode; enterTitle: string; registerTitle: string; accountTitle: string; + linkConfig: { path: string, title: string }[]; }; -export const HeaderMenu = ({ children, enterTitle, registerTitle, accountTitle }: HeaderMenuProps) => ( -
    - -
    -); +export const HeaderMenu = ({ + enterTitle, + registerTitle, + accountTitle, + linkConfig +}: HeaderMenuProps) => { + const selectedLayoutSegment = useSelectedLayoutSegment(); + const pathname = selectedLayoutSegment || ''; + + return ( +
    + +
    + ); +} diff --git a/src/components/Page/Header/HeaderMobileMenu.tsx b/src/components/Page/Header/HeaderMobileMenu.tsx index 3a2f869..e69d060 100644 --- a/src/components/Page/Header/HeaderMobileMenu.tsx +++ b/src/components/Page/Header/HeaderMobileMenu.tsx @@ -1,17 +1,26 @@ 'use client' -import React, { FC, ReactNode, useState } from 'react'; +import React, { FC, useState } from 'react'; +import { useSelectedLayoutSegment } from 'next/navigation'; import { HeaderAuthLinks } from './HeaderAuthLinks'; +import { Link } from '../../../navigation'; type HeaderMenuMobileProps = { - children: ReactNode; + linkConfig: { path: string, title: string }[]; enterTitle: string; registerTitle: string; accountTitle: string; }; -export const HeaderMobileMenu: FC = ({ children, enterTitle, registerTitle, accountTitle }) => { +export const HeaderMobileMenu: FC = ({ + linkConfig, + enterTitle, + registerTitle, + accountTitle +}) => { const [showMobileMenu, setShowMobileMenu] = useState(false); + const selectedLayoutSegment = useSelectedLayoutSegment(); + const pathname = selectedLayoutSegment || ''; return ( <> @@ -40,7 +49,11 @@ export const HeaderMobileMenu: FC = ({ children, enterTit
      - {children} + {linkConfig.map(({ path, title }) => ( +
    • + {title} +
    • + ))}
    diff --git a/src/components/Page/Header/index.tsx b/src/components/Page/Header/index.tsx index 1413497..80497ab 100644 --- a/src/components/Page/Header/index.tsx +++ b/src/components/Page/Header/index.tsx @@ -13,11 +13,10 @@ type HeaderProps = { export const Header: FC = ({ locale }) => { const t = useTranslations('Header'); - const routes = HEAD_ROUTES.map((item) => ( -
  • - {t(`menu.${item}`)} -
  • - )); + const routes: { path: string, title: string }[] = HEAD_ROUTES.map((item) => ({ + path: item, + title: t(`menu.${item}`) + })); return ( <> @@ -30,15 +29,21 @@ export const Header: FC = ({ locale }) => { alt="" /> - - {routes} - + - - {routes} - + ); }; diff --git a/src/components/view/CustomInputPassword.tsx b/src/components/view/CustomInputPassword.tsx new file mode 100644 index 0000000..a32ee9e --- /dev/null +++ b/src/components/view/CustomInputPassword.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import styled from 'styled-components'; +import { Input as AntdInput } from 'antd'; +import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; + +const Input = styled(AntdInput.Password)` + padding: 15px 16px !important; + background: #F8F8F7 !important; + border: 1px solid #F8F8F7 !important; + border-radius: 8px !important; + color: #000 !important; + box-shadow: none !important; + + input { + background: transparent !important; + } + + &:focus, &:hover, &.ant-input-affix-wrapper-focused { + border-color: #66A5AD !important; + box-shadow: none !important; + } + + input::placeholder { + color: #000 !important; + opacity: .4 !important; + } + + &.ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) { + border-color: #ff4d4f !important; + } + + .ant-input-suffix { + opacity: .3; + } +`; + +export const CustomInputPassword = (props: any) => ( + (visible + ? + : + )} + {...props} + /> +); diff --git a/src/components/view/CustomMultiSelect.tsx b/src/components/view/CustomMultiSelect.tsx index e69de29..501e1d0 100644 --- a/src/components/view/CustomMultiSelect.tsx +++ b/src/components/view/CustomMultiSelect.tsx @@ -0,0 +1,106 @@ +import React, {useEffect, useState} from 'react'; +import styled from 'styled-components'; +import { Select as AntdSelect, Tag } from 'antd'; +import type { SelectProps } from 'antd'; + +type TagRender = SelectProps['tagRender']; + +const Select = styled(AntdSelect)` + width: 100% !important; + height: 54px !important; + + .ant-select-selector { + background-color: #F8F8F7 !important; + border-color: #F8F8F7 !important; + border-radius: 8px !important; + padding: 22px 16px 8px !important; + box-shadow: none !important; + + .ant-select-selection-item { + font-size: 15px !important; + font-weight: 400 !important; + line-height: 24px !important; + color: #313131 !important; + } + } + + .ant-select-selection-overflow-item { + margin-right: 4px; + } + + .ant-select-arrow { + color: #2c7873 !important; + } + + &.ant-select-focused, &:hover { + .ant-select-selector { + border-color: #2c7873 !important; + box-shadow: none !important; + } + } +`; + +const SelectWrap = styled.div` + position: relative; + width: 100%; +`; + +const SelectLabel = styled.div` + font-size: 15px; + font-style: normal; + font-weight: 400; + line-height: 24px; + color: #000; + opacity: .3; + position: absolute; + left: 16px; + top: 15px; + z-index: 1; + transition: all .1s ease; + + .b-multiselect__active & { + font-size: 12px; + font-weight: 300; + line-height: 14px; + top: 8px; + } +`; + +const tagRender: TagRender = (props) => { + const { label } = props; + + return ( + + {label} + + ); +}; + +export const CustomMultiSelect = (props: any) => { + const { label, value, ...other } = props; + const [isActiveLabel, setIsActiveLabel] = useState(false); + + useEffect(() => { + if (label) { + setIsActiveLabel(!!value?.length); + } else { + setIsActiveLabel(false); + } + }, [value]); + + return ( + + {label} + setIsActiveLabel(true) : undefined} + onBlur={() => setIsActiveLabel(!!value)} + {...other} + /> + + ); +}; diff --git a/src/components/view/CustomSpin.tsx b/src/components/view/CustomSpin.tsx new file mode 100644 index 0000000..844c7c6 --- /dev/null +++ b/src/components/view/CustomSpin.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Spin as AntdSpin } from 'antd'; +import { styled } from 'styled-components'; + +const Spin = styled(AntdSpin)` + width: 100%; + margin: 24px 0; +`; + +export const CustomSpin = (props: any) => ( + +); diff --git a/src/components/view/index.ts b/src/components/view/index.ts index 380f0db..b7b0905 100644 --- a/src/components/view/index.ts +++ b/src/components/view/index.ts @@ -5,3 +5,7 @@ export * from './CustomSlider'; export * from './CustomPagination'; export * from './CustomRate'; export * from './CustomInput'; +export * from './CustomInputPassword'; +export * from './CustomSelect'; +export * from './CustomMultiSelect'; +export * from './CustomSpin'; diff --git a/src/middleware.ts b/src/middleware.ts index 6ff174d..24342a4 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -9,5 +9,4 @@ export default createMiddleware({ export const config = { matcher: ['/', '/(en|ru|de|it|es|fr)/:path*'] - // matcher: ['/', '/(en|ru|de|it|es|fr).html'] }; diff --git a/src/styles/_default.scss b/src/styles/_default.scss index 96bf644..53e4b73 100644 --- a/src/styles/_default.scss +++ b/src/styles/_default.scss @@ -15,6 +15,10 @@ body{ color: #fff; } +a { + color: #66A5AD !important; +} + .b-wrapper { width: 100%; height: 100%; @@ -426,21 +430,21 @@ body{ .btn-apply { user-select: none; - outline: none; - border: none; + outline: none !important; + border: none !important; text-decoration: none; width: 100%; cursor: pointer; - border-radius: 8px; - background: #FFBD00; - box-shadow: 0px 2px 4px 0px rgba(252, 214, 70, 0.16); + border-radius: 8px !important; + background: #FFBD00 !important; + box-shadow: 0px 2px 4px 0px rgba(252, 214, 70, 0.16) !important; display: flex; gap: 10px; - height: 54px; + height: 54px !important; padding: 8px 31px; justify-content: center; align-items: center; - color: #003B46; + color: #003B46 !important; @include rem(15); font-style: normal; font-weight: 400; diff --git a/src/styles/_footer.scss b/src/styles/_footer.scss index 9ab2f0a..e22d94a 100644 --- a/src/styles/_footer.scss +++ b/src/styles/_footer.scss @@ -70,7 +70,7 @@ display: inline-flex; gap: 8px; text-decoration: none; - color: #003B46; + color: #003B46 !important; @include rem(16); font-style: normal; font-weight: 700; diff --git a/src/styles/_header.scss b/src/styles/_header.scss index 6745d43..a5e6e9f 100644 --- a/src/styles/_header.scss +++ b/src/styles/_header.scss @@ -66,6 +66,10 @@ display: inline-flex; align-items: center; text-decoration: none; + + &.active { + color: #003B46; + } } .text { diff --git a/src/styles/_main.scss b/src/styles/_main.scss index 64ad623..4575b30 100644 --- a/src/styles/_main.scss +++ b/src/styles/_main.scss @@ -199,7 +199,7 @@ @include rem(14); border-radius: 24px; gap: 6px; - color: $white; + color: $white !important; font-style: normal; font-weight: 700; line-height: 133.333%; diff --git a/src/utils/filter.ts b/src/utils/filter.ts index 286d279..f357b22 100644 --- a/src/utils/filter.ts +++ b/src/utils/filter.ts @@ -62,7 +62,7 @@ export const getObjectByFilter = (searchParams?: any): Filter | undefined => { let tags = searchParams?.getAll('themesTagIds'); if (tags && tags.length > 0) { - filter.themesTagIds = tags.map((id) => Number(id)); + filter.themesTagIds = tags.map((id: string) => Number(id)); } if (searchParams?.has('priceFrom')) { diff --git a/src/utils/storage/auth.ts b/src/utils/storage/auth.ts index 9b2696f..9f4a7df 100644 --- a/src/utils/storage/auth.ts +++ b/src/utils/storage/auth.ts @@ -7,7 +7,7 @@ export function checkAuthToken() { } export function getAuthToken() { - return 'tr'; + return ''; } // export function getAuthToken() { @@ -18,6 +18,6 @@ export function getAuthToken() { // localStorage.removeItem(AUTH_TOKEN_KEY); // } // -// export function setAuthToken(token: string) { -// localStorage.setItem(AUTH_TOKEN_KEY, token); -// } +export function setAuthToken(token: string) { + // localStorage.setItem(AUTH_TOKEN_KEY, token); +}