Skip to content

Commit 4322cde

Browse files
committed
Part 4
1 parent 0de1f84 commit 4322cde

26 files changed

+1368
-21
lines changed

app/[locale]/globals.css

+13
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,22 @@
4646
.show-mobile{
4747
display: block !important;
4848
}
49+
.header-category{
50+
margin-top: 20px;
51+
flex-wrap: wrap;
52+
justify-content: center;
53+
gap: 20px;
54+
}
4955
}
5056
@media (max-width:360px){
5157
.hidden-small-mobile{
5258
display: none;
5359
}
60+
}
61+
62+
.ant-skeleton-paragraph{
63+
display: none !important;
64+
}
65+
.ant-skeleton-title{
66+
width: 100% !important;
5467
}

app/[locale]/page.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import Header from "@/modules/Header";
1+
import Brands from "@/modules/Brands";
2+
import Hero from "@/modules/Hero";
3+
24
export default function Home() {
35
return (
46
<>
5-
7+
<Hero/>
8+
<Brands/>
69
</>
710
);
811
}

assets/icons/index.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ export const MenuIcon = () => <svg width="24" height="24" viewBox="0 0 24 24" fi
3535
<path d="M2 12.0322C2 11.4799 2.44772 11.0322 3 11.0322H21C21.5523 11.0322 22 11.4799 22 12.0322C22 12.5845 21.5523 13.0322 21 13.0322H3C2.44772 13.0322 2 12.5845 2 12.0322Z" fill="#333333"/>
3636
<path d="M3 17.0645C2.44772 17.0645 2 17.5122 2 18.0645C2 18.6167 2.44772 19.0645 3 19.0645H21C21.5523 19.0645 22 18.6167 22 18.0645C22 17.5122 21.5523 17.0645 21 17.0645H3Z" fill="#333333"/>
3737
</svg>
38+
39+
export const ClearIcon = () => <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" className="bi bi-x-lg" viewBox="0 0 16 16">
40+
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
41+
</svg>

components/HeaderCategorySkleton.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react'
2+
import {Skeleton} from "antd"
3+
4+
const HeaderCategorySkleton = () => {
5+
return (
6+
<div className='flex justify-between w-full'>
7+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
8+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
9+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
10+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
11+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
12+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
13+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
14+
<Skeleton active className='!w-[100px]' paragraph={{ rows:0 }} />
15+
</div>
16+
)
17+
}
18+
19+
export default HeaderCategorySkleton

hooks/debounce.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"use client"
2+
import { useEffect, useState } from "react";
3+
4+
const debounce = (value:string | undefined, delay:number) => {
5+
const [debouncedValue, setDebouncedValue] = useState(value);
6+
7+
useEffect(() => {
8+
const timer = setTimeout(() => {
9+
setDebouncedValue(value);
10+
}, delay);
11+
12+
return () => {
13+
clearTimeout(timer);
14+
};
15+
}, [value, delay]);
16+
17+
return debouncedValue;
18+
};
19+
20+
export default debounce;

messages/en.json

+3
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@
88
"HeaderCenterContent":{
99
"category":"Category",
1010
"inputPlaceholder":"What are you looking for?"
11+
},
12+
"HeroContent":{
13+
"more":"More"
1114
}
1215
}

messages/ru.json

+3
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@
88
"HeaderCenterContent":{
99
"category":"Категория",
1010
"inputPlaceholder":"Что Вы ищете?"
11+
},
12+
"HeroContent":{
13+
"more":"Подробно"
1114
}
1215
}

messages/uz.json

+3
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@
88
"HeaderCenterContent":{
99
"category":"Turkum",
1010
"inputPlaceholder":"Nima qidirmoqdasiz?"
11+
},
12+
"HeroContent":{
13+
"more":"Batafsil"
1114
}
1215
}

modules/Brands/index.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use client"
2+
import React from 'react'
3+
import "./style.css"
4+
import getBrands from '@/service/getBrands'
5+
import { BrandType } from '@/types/BrandType'
6+
import Image from 'next/image'
7+
import { IMG_API } from '@/hooks/getEnv'
8+
9+
const Brands = () => {
10+
const {data:brands, isLoading} = getBrands()
11+
return (
12+
<div className='containers !py-[100px] brand-wrapper'>
13+
{isLoading ? "Loading..." : brands.map((item:BrandType) => (
14+
<div key={item.id} className='font-semibold text-[16px] text-[#134E9B] brand-item flex justify-center items-center text-center py-[20px] rounded-[6px]'>
15+
{!item.image ? item.name :
16+
<Image className='w-[200px] h-[70px] object-contain' src={`${IMG_API}/${item.image}`} alt='brand img' width={200} height={70} priority/>}
17+
</div>
18+
))}
19+
</div>
20+
)
21+
}
22+
23+
export default Brands

modules/Brands/style.css

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.brand-wrapper{
2+
display: grid;
3+
grid-template-columns: repeat(4, 1fr);
4+
grid-template-rows: repeat(3, 100px);
5+
grid-template-areas:
6+
"a b c d"
7+
"i b g d"
8+
"i f g k"
9+
;
10+
gap: 10px;
11+
}
12+
.brand-item:nth-child(1){
13+
grid-area: a;
14+
background-color: #67B43733;
15+
}
16+
.brand-item:nth-child(2){
17+
grid-area: b;
18+
background-color: #034EA21A;
19+
20+
}
21+
.brand-item:nth-child(2) > img{
22+
transform: scale(3);
23+
}
24+
.brand-item:nth-child(3){
25+
grid-area: c;
26+
background-color: #00439C1F;
27+
}
28+
.brand-item:nth-child(4){
29+
grid-area: d;
30+
background-color: #FF670033;
31+
}
32+
.brand-item:nth-child(5){
33+
grid-area: i;
34+
background-color: #0000001A;
35+
}
36+
.brand-item:nth-child(6){
37+
grid-area: f;
38+
background-color: #006DB833;
39+
}
40+
.brand-item:nth-child(7){
41+
grid-area: g;
42+
background-color: #FF1A1F33;
43+
}
44+
.brand-item:nth-child(8){
45+
grid-area: k;
46+
background-color: #EBEFF3;
47+
}

modules/Header/HeaderCategory.tsx

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"use client"
2+
import HeaderCategorySkleton from '@/components/HeaderCategorySkleton'
3+
import { Link } from '@/i18n/navigation'
4+
import getCategory from '@/service/getCategory'
5+
import { CategoryType } from '@/types/HeaderType'
6+
import React from 'react'
7+
8+
const HeaderCategory = () => {
9+
const {data:categoryList, isLoading} = getCategory()
10+
11+
return (
12+
<div className='containers !mt-[20px] hidden header-category sm:flex items-center justify-between'>
13+
{isLoading ? <HeaderCategorySkleton/> : categoryList.map((item:CategoryType) => <Link className='text-[#545D6A] text-[16px]' key={item.id} href={`#`}>{item.name}</Link>)}
14+
</div>
15+
)
16+
}
17+
18+
export default HeaderCategory

modules/Header/HeaderCenter/HeaderPopamCategory.tsx

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"use client"
2+
import { ClearIcon } from '@/assets/icons'
23
import { Context } from '@/context/context'
34
import { IMG_API } from '@/hooks/getEnv'
45
import getCategory from '@/service/getCategory'
@@ -7,7 +8,7 @@ import Image from 'next/image'
78
import React, { useContext, useState } from 'react'
89

910
const HeaderPopamCategory = () => {
10-
const {openCategory} = useContext(Context)
11+
const {openCategory, setOpenCategory} = useContext(Context)
1112
const {data:categoryList, isLoading} = getCategory()
1213
const [childCategory, setChildCategory] = useState<CategoryType>()
1314
const [activeCategory, setActiveCategory] = useState<number | null>(null)
@@ -16,17 +17,18 @@ const HeaderPopamCategory = () => {
1617
setActiveCategory(obj.id)
1718
}
1819
return (
19-
<div className={`flex ${openCategory ? "h-[570px] border-[2px] border-slate-400" : "h-0 overflow-hidden"} duration-300 absolute w-full top-[96px] shadow-lg `}>
20-
<ul className='w-[30%] space-y-[14px] py-[43px] px-[32px] h-[100%] bg-[#EBEFF3]'>
21-
{categoryList.map((item:CategoryType) => <li id={String(item.id)} onMouseEnter={() => handleShowCategoryChild(item)} key={item.id} className={`${activeCategory == item.id && "bg-[#134E9B] text-white"} rounded-[5px] text-[#454545] duration-300 cursor-pointer flex py-[12px] pl-[32px] items-center gap-[16px]`}>
20+
<div className={`flex ${openCategory ? "h-[100vh] lg:h-[570px] shadow-lg shadow-slate-300" : "h-0 overflow-hidden"} duration-300 z-50 left-0 fixed lg:absolute w-full top-0 lg:top-[96px] shadow-lg `}>
21+
<ul className='w-[60%] md:w-[30%] space-y-[14px] py-[15px] sm:py-[43px] px-[15px] sm:px-[32px] h-[100%] bg-[#EBEFF3]'>
22+
{categoryList.map((item:CategoryType) => <li id={String(item.id)} onMouseEnter={() => handleShowCategoryChild(item)} key={item.id} className={`${activeCategory == item.id && "bg-white sm:bg-[#134E9B] text-black sm:text-white"} rounded-[5px] text-[#454545] duration-300 cursor-pointer flex py-[8px] sm:py-[12px] pl-[10px] sm:pl-[32px] items-center gap-[16px]`}>
2223
<Image className='w-[22px] h-[20px]' src={`${IMG_API}/${item.icon}`} alt='Category Icon' width={22} height={20} priority/>
23-
<span className='text-[16px] leading-[100%]'>{item.name}</span>
24+
<span className='text-[14px] sm:text-[16px] leading-[100%]'>{item.name}</span>
2425
</li>)}
2526
</ul>
26-
<ul className='w-[70%] h-[100%] bg-white pt-[55px] pl-[73px]'>
27+
<ul className='w-[40%] md:w-[70%] h-[100%] bg-white pt-[20px] sm:pt-[55px] pl-[15px] sm:pl-[73px]'>
2728
<strong className='font-semibold text-[16px] text-[#000000B2] leading-[100%] mb-[25px] inline-block'>{childCategory?.name}</strong>
2829
{childCategory?.subCategories.map((item:CategoryType) => <li className='text-[14px] text-[#545D6A]' key={String(item.id)}>{item.name}</li>)}
2930
</ul>
31+
<button onClick={() => setOpenCategory(false)} className='absolute top-2 right-2 md:hidden'> <ClearIcon/> </button>
3032
</div>
3133
)
3234
}

modules/Header/HeaderCenter/HeaderSearch.tsx

+40-4
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,54 @@ import { ArrowDownIcon, SearchIcon } from '@/assets/icons'
33
import Button from '@/components/Button'
44
import Input from '@/components/Input'
55
import { Context } from '@/context/context'
6+
import debounce from '@/hooks/debounce'
7+
import { instance } from '@/hooks/instance'
8+
import { CategoryType } from '@/types/HeaderType'
69
import { useTranslations } from 'next-intl'
7-
import React, { FC, useContext } from 'react'
10+
import React, { FC, useContext, useEffect, useState } from 'react'
811

912
const HeaderSearch:FC<{isMobile?:boolean}> = ({isMobile}) => {
1013
const t = useTranslations("HeaderCenterContent")
1114
const {setOpenCategory, openCategory} = useContext(Context)
12-
return (
15+
16+
const [isLoading, setIsLoading] = useState<boolean>(false)
17+
const [searchValue, setSearchValue] = useState<string>("empty")
18+
const [value, setValue] = useState<string>("")
19+
const [categorySearchData, setCategorySearchData] = useState<CategoryType[]>([])
20+
const name = debounce(searchValue, 800)
21+
22+
function handleSearch(text:string){
23+
setIsLoading(true)
24+
setValue(text)
25+
if(text){
26+
setSearchValue(text)
27+
}
28+
else{
29+
setIsLoading(false)
30+
setSearchValue("empty")
31+
}
32+
}
33+
34+
useEffect(() => {
35+
instance().get("/categories/search", {params:{name}}).then(res => {
36+
setIsLoading(false)
37+
setCategorySearchData(res.data)
38+
})
39+
},[name])
40+
41+
function handleSearchClick(data:string){
42+
setValue(data)
43+
setSearchValue("empty")
44+
}
45+
return (
1346
<div className={`flex gap-[5px] sm:gap-[10px] ${isMobile ? "hidden-searchbar" :""}`}>
14-
<Button onBlur={() => setOpenCategory(false)} onClick={() => setOpenCategory(!openCategory)} extraClass='!w-[35%] !h-[40px] sm:!w-[160px] sm:!h-[47px]' icon={<ArrowDownIcon extrClass={`${openCategory && "rotate-[180deg]"}`}/>} iconPosition={"right"}>{t("category")}</Button>
47+
<Button onClick={() => setOpenCategory(!openCategory)} extraClass='!w-[35%] !h-[40px] sm:!w-[160px] sm:!h-[47px]' icon={<ArrowDownIcon extrClass={`${openCategory && "rotate-[180deg]"}`}/>} iconPosition={"right"}>{t("category")}</Button>
1548
<div className={`relative ${!isMobile && "w-[65%] sm:w-full"}`}>
16-
<Input extraClass={`${!isMobile && "!w-full"}`} placeholder={t("inputPlaceholder")} type='text' name='search'/>
49+
<Input value={value} onChange={(e) => handleSearch(e.target.value)} extraClass={`${!isMobile && "!w-full"}`} placeholder={t("inputPlaceholder")} type='text' name='search'/>
1750
<Button extraClass='!absolute top-0 right-0 !w-[40px] !h-[40px] sm:!h-[48px] sm:!w-[60px] !p-0'> <SearchIcon extraClass='w-[16px] h-[16px] sm:w-[20px] sm:h-[20px]'/> </Button>
51+
<ul className={`absolute ${categorySearchData.length > 0 || isLoading ? "" : "h-0 opacity-0"} duration-300 overflow-hidden z-50 w-full sm:w-[60%] py-[20px] shadow-lg shadow-slate-400 bg-white rounded-md`}>
52+
{isLoading ? <li className='pl-[20px] sm:pl-[40px] text-[13px] sm:text-[16px]'>Loading...</li> : categorySearchData.map((item:CategoryType) => <li className='pl-[20px] sm:pl-[40px] py-[8px] sm:py-[14px] border-b-[1px] border-slate-300 hover:bg-[#134E9B] hover:text-white text-[13px] sm:text-[16px] duration-300 cursor-pointer' onClick={() => handleSearchClick(item.name)} key={item.id}>{item.name}</li>)}
53+
</ul>
1854
</div>
1955
</div>
2056
)

modules/Header/HeaderCenter/index.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
1+
"use client"
12
import { Link } from '@/i18n/navigation'
23
import { useTranslations } from 'next-intl'
34
import Image from 'next/image'
4-
import React from 'react'
5+
import React, { useContext } from 'react'
56
import HeaderSearch from './HeaderSearch'
67
import HeaderActions from './HeaderActions'
78
import Button from '@/components/Button'
89
import { MenuIcon } from '@/assets/icons'
910
import HeaderPopamCategory from './HeaderPopamCategory'
11+
import { Context } from '@/context/context'
1012

1113
const HeaderCenter = () => {
14+
const {setOpenCategory} = useContext(Context)
1215
const t = useTranslations("HeaderCenterContent")
1316
return (
14-
<div className='containers relative flex items-center justify-between !py-[25px] sm:!py-[30px]'>
17+
<div className='containers relative flex items-center justify-between !pt-[25px] sm:!pt-[30px]'>
1518
<Link className='flex items-center gap-[1px]' href={"/"}>
1619
<Image className='w-[48px] scale-[1.3] sm:scale-[1.5] h-[48px]' src={"/logo.svg"} alt='Logo' width={48} height={48} priority/>
1720
<strong className='text-[33px] sm:text-[36px] font-black text-[#134E9B] leading-[100%]'>Ashyo</strong>
1821
</Link>
1922
<HeaderSearch isMobile={true}/>
2023
<HeaderActions/>
2124
<Link className='text-[#203F68] hidden-small-mobile sm:hidden font-semibold text-[14px] leading-[130%]' href={`tel:+998711234567`}>+998 (71) 123-45-67</Link>
22-
<Button extraClass='!bg-transparent sm:hidden !p-0'> <MenuIcon/></Button>
25+
<Button onClick={() => setOpenCategory(true)} extraClass='!bg-transparent sm:hidden !p-0'> <MenuIcon/></Button>
2326
<HeaderPopamCategory/>
2427
</div>
2528
)

modules/Header/HeaderTop.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const HeaderTop = () => {
3636
return (
3737
<div className='py-[11px] bg-[#EBEFF3] hidden sm:block'>
3838
<div className='containers flex items-center justify-between'>
39-
<nav className='flex items-center gap-[28px]'>
39+
<nav className='flex items-center gap-[15px] md:gap-[28px]'>
4040
{navList.map((item: HeaderTopNavListType) => <Link className='flex hover:text-[#134E9B] duration-300 items-center gap-[11px] text-[14px] leading-[130%] text-[#545D6A]' key={item.id} href={item.path}>
4141
<span>{item.icon && item.icon}</span>
4242
{item.title}

modules/Header/index.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import React from 'react'
22
import HeaderTop from './HeaderTop'
33
import HeaderCenter from './HeaderCenter'
44
import HeaderMobile from './HeaderMobile'
5+
import HeaderCategory from './HeaderCategory'
56

67
const Header = () => {
78
return (
8-
<header>
9+
<header className='pb-[29px]'>
910
<HeaderTop/>
1011
<HeaderCenter/>
1112
<HeaderMobile/>
13+
<HeaderCategory/>
1214
</header>
1315
)
1416
}

0 commit comments

Comments
 (0)