From 225329f342062f85bd4749c6d6bf72695e22a4d4 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Thu, 16 Oct 2025 11:23:38 +0330 Subject: [PATCH] increable offers + fix bugs --- src/app/home/components/Banners3.tsx | 19 ++- src/app/home/components/Banners4.tsx | 17 ++- src/app/home/components/Carousel.tsx | 31 ++-- src/app/home/components/HotOffer.tsx | 27 ++-- src/app/privacy/service/Service.ts | 13 +- src/app/privacy/types/Types.ts | 39 +++++ .../product/components/BaseInformation.tsx | 45 ++++-- src/app/product/components/Reviews.tsx | 2 +- src/app/product/components/SingleProduct.tsx | 7 +- src/app/products/[id]/page.tsx | 16 ++- src/components/ProductCard.tsx | 44 +----- src/share/Footer.tsx | 133 +++++++++--------- src/types/landing.types.ts | 61 ++++++-- 13 files changed, 291 insertions(+), 163 deletions(-) create mode 100644 src/app/privacy/types/Types.ts diff --git a/src/app/home/components/Banners3.tsx b/src/app/home/components/Banners3.tsx index 964c4bd..f5cd82a 100644 --- a/src/app/home/components/Banners3.tsx +++ b/src/app/home/components/Banners3.tsx @@ -2,6 +2,7 @@ import { FC } from 'react' import { useGetLanding } from '../hooks/useHomeData' import Image from 'next/image' +import Link from 'next/link' const Banners3: FC = () => { @@ -10,15 +11,21 @@ const Banners3: FC = () => { return (
-
data?.results?.banners?.[1]?.linkUrl && window.open(data.results.banners[1].linkUrl, '_blank')}> - {data?.results?.banners?.[1]?.altText +
+ + {data?.results?.banners?.[0]?.altText +
-
data?.results?.banners?.[2]?.linkUrl && window.open(data.results.banners[2].linkUrl, '_blank')}> - {data?.results?.banners?.[2]?.altText +
+ + {data?.results?.banners?.[2]?.altText +
-
data?.results?.banners?.[0]?.linkUrl && window.open(data.results.banners[0].linkUrl, '_blank')}> - {data?.results?.banners?.[0]?.altText +
+ + {data?.results?.banners?.[0]?.altText +
) diff --git a/src/app/home/components/Banners4.tsx b/src/app/home/components/Banners4.tsx index aa23669..1cf0cf0 100644 --- a/src/app/home/components/Banners4.tsx +++ b/src/app/home/components/Banners4.tsx @@ -2,6 +2,7 @@ import { FC } from 'react' import { useGetLanding } from '../hooks/useHomeData' import Image from 'next/image' +import Link from 'next/link' const Banners4: FC = () => { const { data } = useGetLanding() @@ -9,20 +10,28 @@ const Banners4: FC = () => { return (
- {data?.results?.banners?.[0]?.altText + + {data?.results?.banners?.[0]?.altText +
- {data?.results?.banners?.[0]?.altText + + {data?.results?.banners?.[0]?.altText +
- {data?.results?.banners?.[0]?.altText + + {data?.results?.banners?.[0]?.altText +
- {data?.results?.banners?.[0]?.altText + + {data?.results?.banners?.[0]?.altText +
diff --git a/src/app/home/components/Carousel.tsx b/src/app/home/components/Carousel.tsx index b3b408f..7b92cb0 100644 --- a/src/app/home/components/Carousel.tsx +++ b/src/app/home/components/Carousel.tsx @@ -3,6 +3,7 @@ import { FC, useState, useEffect } from 'react' import { Swiper, SwiperSlide } from 'swiper/react'; import { Pagination } from 'swiper/modules'; import Image from 'next/image'; +import Link from 'next/link'; import { useGetLanding } from '../hooks/useHomeData'; const Carousel: FC = () => { @@ -32,17 +33,25 @@ const Carousel: FC = () => { {sliders.map((slider) => ( -
slider.linkUrl && window.open(slider.linkUrl, '_blank')} - > - {slider.altText} -
+ {slider.linkUrl ? ( + + {slider.altText} + + ) : ( +
+ {slider.altText} +
+ )}
))}
diff --git a/src/app/home/components/HotOffer.tsx b/src/app/home/components/HotOffer.tsx index ac69b9f..849f197 100644 --- a/src/app/home/components/HotOffer.tsx +++ b/src/app/home/components/HotOffer.tsx @@ -4,9 +4,19 @@ import ProductCard from '@/components/ProductCard' import Image from 'next/image' import { Swiper, SwiperSlide } from 'swiper/react' import { SHOP_CONFIG } from '@/config/const' +import { useGetLanding } from '../hooks/useHomeData' const HotOffer: FC = () => { + + const { data } = useGetLanding() + + const incredibleOffers = data?.results?.incredibleOffers || [] + + if (!incredibleOffers.length) { + return null + } + return (
@@ -41,18 +51,11 @@ const HotOffer: FC = () => { }, }} > - - - - - - - - - - - - + {incredibleOffers.map((product) => ( + + + + ))}
diff --git a/src/app/privacy/service/Service.ts b/src/app/privacy/service/Service.ts index 0595d78..4bf86e4 100644 --- a/src/app/privacy/service/Service.ts +++ b/src/app/privacy/service/Service.ts @@ -1,6 +1,15 @@ import axios from "@/config/axios"; +import { SiteSettingResponse } from "@/app/privacy/types/Types"; -export const getSiteSetting = async (name: string) => { - const { data } = await axios.get(`/site-setting?name=${name}`); +export const getSiteSetting = async ( + name: string +): Promise => { + const params = new URLSearchParams(); + if (name !== "all") { + params.append("name", name); + } + const { data } = await axios.get( + `/site-setting${params.toString() ? `?${params.toString()}` : ""}` + ); return data; }; diff --git a/src/app/privacy/types/Types.ts b/src/app/privacy/types/Types.ts new file mode 100644 index 0000000..7c49064 --- /dev/null +++ b/src/app/privacy/types/Types.ts @@ -0,0 +1,39 @@ +export interface DownloadAppDetail { + pic: string; + url: string; + _id: string; + id: string; +} + +export interface SocialMediaLink { + icon: string; + url: string; + _id: string; + id: string; +} + +export interface SiteSetting { + _id: string; + siteLogo: string; + footerDescription: string; + footerAddress: string; + footerEmail: string; + footerPhone: string; + downloadAppDetails: DownloadAppDetail[]; + socialMediaLinks: SocialMediaLink[]; + shipmentContent: string; + faqContent: string; + policyContent: string; + returnRulesContent: string; + jobsContent: string; + createdAt: string; + updatedAt: string; +} + +export interface SiteSettingResponse { + status: number; + success: boolean; + results: { + siteSetting: SiteSetting; + }; +} diff --git a/src/app/product/components/BaseInformation.tsx b/src/app/product/components/BaseInformation.tsx index 1bf8cfe..03a1c37 100644 --- a/src/app/product/components/BaseInformation.tsx +++ b/src/app/product/components/BaseInformation.tsx @@ -1,21 +1,34 @@ 'use client' import { Star1 } from 'iconsax-react' -import { FC, useEffect } from 'react' -import { Product } from '@/types/product.types' +import { FC, useEffect, useMemo } from 'react' +import { Product, Review } from '@/types/product.types' import Image from 'next/image' import { clx } from '@/helpers/utils' import { useProductStore } from '../store/Store' interface BaseInformationProps { product: Product + reviews?: Review[] } -const BaseInformation: FC = ({ product }) => { +const BaseInformation: FC = ({ product, reviews = [] }) => { const { setVariantId, variantId } = useProductStore() const brand = product.brand + // محاسبه میانگین امتیاز و تعداد دیدگاه‌ها + const { averageRating, reviewsCount } = useMemo(() => { + if (!reviews.length) { + return { averageRating: 0, reviewsCount: 0 } + } + const totalRating = reviews.reduce((sum, review) => sum + review.rating, 0) + return { + averageRating: Number((totalRating / reviews.length).toFixed(1)), + reviewsCount: reviews.length + } + }, [reviews]) + // تابع برای انتخاب variant const handleVariantSelect = (variantId: string) => { setVariantId(variantId) @@ -43,14 +56,28 @@ const BaseInformation: FC = ({ product }) => { {brand.title_fa}
-
+
+ + {reviewsCount > 0 ? `${averageRating} امتیاز` : '۰ امتیاز'} + +
-
- 8 دیدگاه -
+
{/* نمایش variant ها */} diff --git a/src/app/product/components/Reviews.tsx b/src/app/product/components/Reviews.tsx index 7bbe9cb..3df9bb3 100644 --- a/src/app/product/components/Reviews.tsx +++ b/src/app/product/components/Reviews.tsx @@ -33,7 +33,7 @@ const Reviews: FC = ({ reviews = [] }) => { } return ( -
+

نظرات خریداران

diff --git a/src/app/product/components/SingleProduct.tsx b/src/app/product/components/SingleProduct.tsx index 41b4053..f0833e9 100644 --- a/src/app/product/components/SingleProduct.tsx +++ b/src/app/product/components/SingleProduct.tsx @@ -18,6 +18,7 @@ import Questions from '@/app/product/components/Questions' import { useGetDetailProduct } from '@/app/product/hooks/useProductData' import PageLoading from '@/components/PageLoading' import { notFound } from 'next/navigation' +import { SHOP_CONFIG } from '@/config/const' interface SingleProductProps { id: string @@ -42,7 +43,7 @@ const SingleProduct = ({ id }: SingleProductProps) => { - فروشگاه آناهیتا + {SHOP_CONFIG.fullName} {categoryPath.map((category, index) => (
@@ -51,7 +52,7 @@ const SingleProduct = ({ id }: SingleProductProps) => { {index === categoryPath.length - 1 ? ( {category.title_fa} ) : ( - + {category.title_fa} )} @@ -68,7 +69,7 @@ const SingleProduct = ({ id }: SingleProductProps) => {
- +
diff --git a/src/app/products/[id]/page.tsx b/src/app/products/[id]/page.tsx index fd226ad..2978c73 100644 --- a/src/app/products/[id]/page.tsx +++ b/src/app/products/[id]/page.tsx @@ -21,6 +21,7 @@ import { useInfiniteCategoryProductsData } from '../hooks/useInfiniteProductsDat import { IProduct } from '@/types/landing.types' import { Product } from '@/types/products.types' import { useSearchParams, useParams } from 'next/navigation' +import { SHOP_CONFIG } from '@/config/const' const Products: NextPage = () => { const [showMobileFilters, setShowMobileFilters] = useState(false) @@ -109,8 +110,19 @@ const Products: NextPage = () => { market_status: defaultVariant?.market_status || 'active', price: price, stock: defaultVariant?.stock || 0, + postingTime: defaultVariant?.postingTime || 0, isFreeShip: defaultVariant?.isFreeShip || false, - isWholeSale: defaultVariant?.isWholeSale || false + isWholeSale: defaultVariant?.isWholeSale || false, + shop: defaultVariant?.shop || { _id: '', shopName: '', shopDescription: '', logo: '' }, + shipmentMethod: defaultVariant?.shipmentMethod?.map(method => ({ + _id: method._id, + name: method.name, + description: method.description, + deliveryTime: method.deliveryTime, + deliveryType: method.deliveryType as "Standard" | "SameDay" | "Express" + })) || [], + warranty: defaultVariant?.warranty || { _id: 0, duration: '', logoUrl: '', name: '' }, + meterage: [] }, variants: (product.variants || []).map(variant => ({ ...variant, @@ -128,7 +140,7 @@ const Products: NextPage = () => { - فروشگاه آناهیتا + {SHOP_CONFIG.fullName} {data?.pages?.[0]?.results.breadcrumb?.map((item, index) => (
diff --git a/src/components/ProductCard.tsx b/src/components/ProductCard.tsx index ef3b141..2c119cd 100644 --- a/src/components/ProductCard.tsx +++ b/src/components/ProductCard.tsx @@ -30,10 +30,10 @@ const ProductCard: FC = ({ item }) => { if (!item?._id || !item?.variants[0]?._id) return if (isWished) { - removeWishlist({ productId: item._id.toString(), variantId: item.variants[0]._id }) + removeWishlist({ productId: String(item._id), variantId: item.variants[0]._id }) setIsWished(false) } else { - addWishlist({ productId: item._id.toString(), variantId: item.variants[0]._id }) + addWishlist({ productId: String(item._id), variantId: item.variants[0]._id }) setIsWished(true) } } @@ -53,54 +53,24 @@ const ProductCard: FC = ({ item }) => {

{item.title_fa}

- {/*
- - { - item.variants.map((variant) => { - if (variant.meterage) { - return
{variant?.meterage?.value}
- } - - }) - } -
- -
- { - item.variants.map((variant) => { - if (variant.color) { - return
-
-
- } - }) - } - { - item.variants.map((variant) => { - if (variant.size) { - return
{variant?.size?.value}
- } - }) - } -
*/}
- {item.default_variant.price.selling_price && item.default_variant.price.selling_price > 0 + {item?.default_variant?.price?.selling_price && item.default_variant.price.selling_price > 0 ? `${NumberFormat(item.default_variant.price.selling_price)} تومان` : 'بدون قیمت' }
- {((item.default_variant.price.discount_percent > 0 && item.default_variant.price.selling_price > 0) || - (item.default_variant.price.is_specialSale && item.default_variant.price.selling_price > 0)) && ( + {((item?.default_variant?.price?.discount_percent > 0 && item.default_variant.price.selling_price > 0) || + (item?.default_variant?.price?.is_specialSale && item.default_variant.price.selling_price > 0)) && (
{NumberFormat(item.default_variant.price.retailPrice)}
)}
- {((item.default_variant.price.discount_percent > 0 && item.default_variant.price.selling_price > 0) || - (item.default_variant.price.is_specialSale && item.default_variant.price.selling_price > 0)) && ( + {((item?.default_variant?.price?.discount_percent > 0 && item.default_variant.price.selling_price > 0) || + (item?.default_variant?.price?.is_specialSale && item.default_variant.price.selling_price > 0)) && (
{item.default_variant.price.discount_percent > 0 ? `${item.default_variant.price.discount_percent}%` diff --git a/src/share/Footer.tsx b/src/share/Footer.tsx index 38b9c27..c1c68f8 100644 --- a/src/share/Footer.tsx +++ b/src/share/Footer.tsx @@ -1,12 +1,17 @@ +'use client' import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' -import { Call, Instagram, Location, Sms, Youtube, Whatsapp } from 'iconsax-react' +import { Call, Location, Sms } from 'iconsax-react' import Image from 'next/image' import Link from 'next/link' import { FC } from 'react' import { SHOP_CONFIG } from '@/config/const' +import { useGetSiteSetting } from '@/app/privacy/hooks/usePrivacyData' const Footer: FC = () => { + + const { data: siteSetting } = useGetSiteSetting('all') + return (
@@ -15,31 +20,36 @@ const Footer: FC = () => { width={100} height={100} alt='logo' - src='/images/logo.png' + src={siteSetting?.results?.siteSetting?.siteLogo || '/images/logo.png'} className='w-full h-auto max-h-20 max-w-20 object-contain' />
-
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده +
-
- -
تلفن: 67341
-
- -
- -
کدپستی: 1136834843
-
-
- -
- تهران، میدان امام خمینی، خیابان فردوسی، خیابان سرهنگ سخائی - نبش کوچه بهنیا، پلاک40، طبقه چهارم، واحد12 + {siteSetting?.results?.siteSetting?.footerPhone && ( +
+ +
تلفن: {siteSetting.results.siteSetting.footerPhone}
-
+ )} + + {siteSetting?.results?.siteSetting?.footerEmail && ( +
+ +
ایمیل: {siteSetting.results.siteSetting.footerEmail}
+
+ )} + + {siteSetting?.results?.siteSetting?.footerAddress && ( +
+ +
+ {siteSetting.results.siteSetting.footerAddress} +
+
+ )}
@@ -48,31 +58,26 @@ const Footer: FC = () => {
-
-
- دانلود اپلیکیشن + {siteSetting?.results?.siteSetting?.downloadAppDetails && siteSetting.results.siteSetting.downloadAppDetails.length > 0 && ( +
+
+ دانلود اپلیکیشن +
+
+ {siteSetting.results.siteSetting.downloadAppDetails.map((app) => ( + + download app + + ))} +
-
- - download from app store - - - download from google play - -
-
+ )}
@@ -90,29 +95,29 @@ const Footer: FC = () => {
-
-
- ما را در شبکه های اجتماعی دنبال کنید -
+ {siteSetting?.results?.siteSetting?.socialMediaLinks && siteSetting.results.siteSetting.socialMediaLinks.length > 0 && ( +
+
+ ما را در شبکه های اجتماعی دنبال کنید +
-
- -
- -
- - -
- -
- - -
- -
- +
+ {siteSetting.results.siteSetting.socialMediaLinks.map((social) => ( + +
+ social media +
+ + ))} +
-
+ )}
diff --git a/src/types/landing.types.ts b/src/types/landing.types.ts index 701f4ea..ce2c158 100644 --- a/src/types/landing.types.ts +++ b/src/types/landing.types.ts @@ -1,8 +1,27 @@ export interface IShop { _id: string; - shopName: string; - shopDescription: string; - logo: string; + shopName?: string; + shopDescription?: string; + logo?: string; +} + +export interface IBrand { + _id: string; + status: string; + title_en: string; + title_fa: string; + images: string[]; + logoUrl: string; +} + +export interface ISize { + _id: number; + value: string; +} + +export interface ISpecification { + title: string; + values: string[]; } export interface IPrice { @@ -21,8 +40,14 @@ export interface IDefaultVariant { market_status: string; price: IPrice; stock: number; + postingTime: number; isFreeShip: boolean; isWholeSale: boolean; + shop: IShop; + shipmentMethod: IShipmentMethod[]; + warranty: IWarranty; + size?: ISize; + meterage: IMeterage[]; } export interface IShipmentMethod { @@ -70,18 +95,27 @@ export interface IProduct { _id: number; title_fa: string; title_en: string; - seoTitle: string | null; - seoDescription: string | null; - model: string; + source?: string; + seoTitle?: string | null; + seoDescription?: string | null; + metaDescription?: string; + model?: string; description: string; tags: string[]; - shop: IShop; - status: string; + advantages?: string[]; + disAdvantages?: string[]; + shop?: IShop; + status?: string; imagesUrl: IImagesUrl; - url: string; + url?: string; + isFake?: string; + voice?: string | null; + specifications?: ISpecification[]; + brand?: IBrand; + category?: ICategory; default_variant: IDefaultVariant; - variants: IVariant[]; - isWishlist: boolean; + variants: (IVariant | { _id: string })[]; + isWishlist?: boolean; } export interface ICategory { @@ -91,7 +125,10 @@ export interface ICategory { icon: string; imageUrl: string; description: string; - url: string; + theme?: string; + leaf?: boolean; + parent?: string; + url?: string; } export interface ISlider {