diff --git a/src/app/auth/service/Service.ts b/src/app/auth/service/Service.ts index 3e7d1b2..a1b9434 100644 --- a/src/app/auth/service/Service.ts +++ b/src/app/auth/service/Service.ts @@ -1,6 +1,8 @@ import axios from "@/config/axios"; import { + ApiResponse, AuthenticationType, + RefreshTokenResponse, RefreshTokenType, VerifyOtpType, } from "../types/Types"; @@ -15,7 +17,9 @@ export const verifyOtp = async (params: VerifyOtpType) => { return data; }; -export const refreshToken = async (params: RefreshTokenType) => { +export const refreshToken = async ( + params: RefreshTokenType +): Promise> => { const { data } = await axios.post(`/user/token`, params); return data; }; diff --git a/src/app/auth/types/Types.ts b/src/app/auth/types/Types.ts index 1fe5a4d..f82a35a 100644 --- a/src/app/auth/types/Types.ts +++ b/src/app/auth/types/Types.ts @@ -17,3 +17,22 @@ export type VerifyOtpType = { export type RefreshTokenType = { token: string; }; + +export type TokenInfo = { + token: string; + expires: string; +}; + +export type RefreshTokenResponse = { + tokenType: string; + accessToken: TokenInfo; + refreshToken: TokenInfo; +}; + +export type ApiResponse = { + status: number; + success: boolean; + results: { + data: T; + }; +}; diff --git a/src/app/cart/hooks/useCartData.ts b/src/app/cart/hooks/useCartData.ts index cebe1d3..8dfaa4f 100644 --- a/src/app/cart/hooks/useCartData.ts +++ b/src/app/cart/hooks/useCartData.ts @@ -1,10 +1,14 @@ +import { useSharedStore } from "@/share/store/sharedStore"; import * as api from "../service/Service"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; export const useGetCart = () => { + const { isLogin } = useSharedStore(); + return useQuery({ queryKey: ["cart"], queryFn: api.getCart, + enabled: isLogin, }); }; diff --git a/src/app/compare/hooks/useCompareData.ts b/src/app/compare/hooks/useCompareData.ts new file mode 100644 index 0000000..4bd237a --- /dev/null +++ b/src/app/compare/hooks/useCompareData.ts @@ -0,0 +1,18 @@ +import { useQuery } from "@tanstack/react-query"; +import * as api from "../service/Service"; + +export const useGetCompare = (productIds: string[]) => { + return useQuery({ + queryKey: ["compare", productIds], + queryFn: () => api.getCompare(productIds), + enabled: !!productIds, + }); +}; + +export const useSearchCompareProduct = (productId?: string) => { + return useQuery({ + queryKey: ["compare", productId], + queryFn: () => api.searchComparProduct(productId), + enabled: !!productId, + }); +}; diff --git a/src/app/compare/page.tsx b/src/app/compare/page.tsx index 3976dcf..448493c 100644 --- a/src/app/compare/page.tsx +++ b/src/app/compare/page.tsx @@ -1,62 +1,109 @@ +'use client' import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' import Layout from '@/hoc/Layout' import { NextPage } from 'next' import Image from 'next/image' +import { useSearchParams } from 'next/navigation' +import { useEffect, useState } from 'react'; +import { useGetCompare } from './hooks/useCompareData' +import { useLocalCart } from '@/app/product/hooks/useLocalCart' +import { toast } from '@/components/Toast' +import { CompareProduct } from './types/Types' +import { useSharedStore } from '@/share/store/sharedStore' const ComparePage: NextPage = () => { + + const [productIds, setProductIds] = useState([]); + const search = useSearchParams() + const { data, isLoading } = useGetCompare(productIds) + const { addToLocalCart } = useLocalCart() + const { isLogin } = useSharedStore() + + const products = data?.results?.products || [] + + const handleAddToCart = (product: CompareProduct) => { + if (!isLogin) { + toast('لطفا ابتدا وارد حساب کاربری خود شوید', 'error') + return + } + + if (!product._id || !product.default_variant?._id) return + + addToLocalCart(product._id, product.default_variant._id, 1) + toast('محصول به سبد خرید اضافه شد', 'success') + } + + useEffect(() => { + const productId = search.get('productId') + if (productId) { + setProductIds([productId]); + } + }, [search]); + + + if (isLoading) { + return ( + +
+
+
+
+

در حال بارگذاری...

+
+
+
+
+ ) + } + + if (products.length === 0) { + return ( + +
+
+
+

هیچ محصولی برای مقایسه انتخاب نشده است

+
+
+
+
+ ) + } + return (
-
- compare + {products.map((product, index) => ( +
+ {product.title_fa} -
- رومیزی سندس کد T-2112 طرح فرش -
+
+ {product.title_fa} +
-
- 260,000 تومان -
+
+ {product.default_variant.price.selling_price.toLocaleString()} تومان +
-
- +
+ +
-
-
- compare - -
- رومیزی سندس کد T-2112 طرح فرش -
- -
- 260,000 تومان -
- -
- -
-
+ ))}
@@ -65,19 +112,20 @@ const ComparePage: NextPage = () => {
-
-
وزن
+ {products[0]?.specifications?.map((spec, specIndex) => ( +
+
{spec.title}
-
-
- 350 g -
-
- - 350 g +
+ {products.map((product, productIndex) => ( +
+ {product.specifications?.[specIndex]?.values?.join(', ') || '-'} + {productIndex < products.length - 1 && } +
+ ))}
-
+ ))}
) diff --git a/src/app/compare/service/Service.ts b/src/app/compare/service/Service.ts new file mode 100644 index 0000000..a8db42e --- /dev/null +++ b/src/app/compare/service/Service.ts @@ -0,0 +1,17 @@ +import axios from "@/config/axios"; + +import { CompareResponse } from "../types/Types"; + +export const getCompare = async ( + productIds: string[] +): Promise => { + const { data } = await axios.get(`/product/compare?productIds=${productIds}`); + return data; +}; + +export const searchComparProduct = async (productId?: string) => { + const { data } = await axios.get( + `/product/compare/search?productId=${productId}` + ); + return data; +}; diff --git a/src/app/compare/types/Types.ts b/src/app/compare/types/Types.ts new file mode 100644 index 0000000..298b0e4 --- /dev/null +++ b/src/app/compare/types/Types.ts @@ -0,0 +1,94 @@ +export interface CompareResponse { + status: number; + success: boolean; + results: { + products: CompareProduct[]; + }; +} + +export interface CompareProduct { + _id: number; + url: string; + title_fa: string; + title_en: string; + seoTitle: string; + seoDescription: string | null; + source: string; + description: string; + metaDescription: string; + tags: string[]; + advantages: string[]; + disAdvantages: string[]; + imagesUrl: ImagesUrl; + isFake: string; + specifications: Specification[]; + default_variant: Variant; + variants: Variant[]; +} + +export interface ImagesUrl { + cover: string; + list: string[]; +} + +export interface Specification { + title: string; + values: string[]; +} + +export interface Variant { + _id: string; + market_status: string; + price: Price; + stock: number; + postingTime: number; + isFreeShip: boolean; + isWholeSale: boolean; + shop: Shop; + shipmentMethod: ShipmentMethod[]; + warranty: Warranty; + meterage: Meterage; +} + +export interface Price { + order_limit: number; + retailPrice: number; + selling_price: number; + is_specialSale: boolean; + discount_percent: number; + specialSale_order_limit: number | null; + specialSale_quantity: number | null; + specialSale_endDate: string | null; +} + +export interface Shop { + _id: string; + shopName: string; + shopCode: number; + owner: string; + shopDescription: string; + telephoneNumber: string; + isChatActive: boolean; + shopPostalCode: string; + logo: string; +} + +export interface ShipmentMethod { + _id: number; + name: string; + description: string; + deliveryTime: number; + deliveryType: string; +} + +export interface Warranty { + _id: number; + duration: string; + logoUrl: string; + name: string; +} + +export interface Meterage { + _id: number; + value: string; +} diff --git a/src/app/home/components/Banners3.tsx b/src/app/home/components/Banners3.tsx index f0bfec8..7471866 100644 --- a/src/app/home/components/Banners3.tsx +++ b/src/app/home/components/Banners3.tsx @@ -1,15 +1,24 @@ +'use client' import { FC } from 'react' +import { useGetLanding } from '../hooks/useHomeData' +import Image from 'next/image' const Banners3: FC = () => { + + const { data } = useGetLanding() + return ( -
-
-
+
+
+
+ {data?.results?.banners?.[0]?.altText
-
+
+ {data?.results?.banners?.[2]?.altText
-
+
+ {data?.results?.banners?.[0]?.altText
) diff --git a/src/app/home/components/Banners4.tsx b/src/app/home/components/Banners4.tsx index 720dcd8..de259fe 100644 --- a/src/app/home/components/Banners4.tsx +++ b/src/app/home/components/Banners4.tsx @@ -1,18 +1,28 @@ +'use client' import { FC } from 'react' +import { useGetLanding } from '../hooks/useHomeData' +import Image from 'next/image' const Banners4: FC = () => { + const { data } = useGetLanding() + return (
+ {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/product/components/ActionProduct.tsx b/src/app/product/components/ActionProduct.tsx index c03a267..e70eddd 100644 --- a/src/app/product/components/ActionProduct.tsx +++ b/src/app/product/components/ActionProduct.tsx @@ -1,5 +1,5 @@ 'use client' -import { Chart2, Heart, Hierarchy2, Notification, RowHorizontal } from 'iconsax-react' +import { Heart, Hierarchy2, RowHorizontal } from 'iconsax-react' import { FC, useEffect, useState } from 'react' import { Product } from '@/types/product.types' import { useAddWishlist, useRemoveWishlist } from '@/app/products/hooks/useProductsData' @@ -8,6 +8,7 @@ import { toast } from '@/components/Toast' import { useCheckWishProduct } from '../hooks/useProductData' import { useProductStore } from '../store/Store' import PriceHistory from './PriceHistory' +import Link from 'next/link' interface ActionProductProps { product: Product @@ -18,7 +19,6 @@ const ActionProduct: FC = ({ product }) => { const { variantId } = useProductStore() const { isLogin } = useSharedStore() const [isWishlist, setIsWishlist] = useState(false) - const [isCompare, setIsCompare] = useState(false) const [isWishlistLoading, setIsWishlistLoading] = useState(false) const { mutate: addWishlist } = useAddWishlist() @@ -72,11 +72,6 @@ const ActionProduct: FC = ({ product }) => { } }, [data]) - const handleCompare = () => { - setIsCompare(!isCompare) - // TODO: API call to add/remove from compare - } - const handleShare = () => { if (navigator.share) { navigator.share({ @@ -135,12 +130,14 @@ const ActionProduct: FC = ({ product }) => { - + + +
) diff --git a/src/config/axios.ts b/src/config/axios.ts index 402eba0..90d4ec9 100644 --- a/src/config/axios.ts +++ b/src/config/axios.ts @@ -8,6 +8,7 @@ import { removeRefreshToken, } from "./func"; import { refreshToken } from "../app/auth/service/Service"; +import { ApiResponse, RefreshTokenResponse } from "../app/auth/types/Types"; import { BASE_URL } from "./const"; declare global { @@ -47,32 +48,32 @@ axiosInstance.interceptors.response.use( if (!refreshTokenValue) { await removeRefreshToken(); await removeToken(); - window.location.href = `/auth`; + window.location.href = `/`; return; } - const { data } = await refreshToken({ + const data: ApiResponse = await refreshToken({ token: refreshTokenValue, }); - if (data?.accessToken?.token) { - await setToken(data?.accessToken?.token); - if (data.refreshToken?.token) { - await setRefreshToken(data.refreshToken?.token); + if (data?.results?.data?.accessToken?.token) { + await setToken(data?.results?.data?.accessToken?.token); + if (data?.results?.data?.refreshToken?.token) { + await setRefreshToken(data?.results?.data?.refreshToken?.token); } - originalRequest.headers.Authorization = `Bearer ${data.accessToken.token}`; + originalRequest.headers.Authorization = `Bearer ${data.results.data.accessToken.token}`; return axiosInstance(originalRequest); } else { await removeRefreshToken(); await removeToken(); - window.location.href = `/auth`; + window.location.href = `/`; return; } } catch (refreshError) { console.error("Token refresh failed:", refreshError); await removeToken(); await removeRefreshToken(); - window.location.href = `/auth`; + window.location.href = `/`; return Promise.reject(refreshError); } finally { window.isRefreshingToken = false; diff --git a/src/config/const.ts b/src/config/const.ts index da5fde4..e034054 100644 --- a/src/config/const.ts +++ b/src/config/const.ts @@ -1,6 +1,6 @@ export const TOKEN_NAME = "sh_token"; export const REFRESH_TOKEN_NAME = "sh_refresh_token"; export const BASE_URL = "https://shop-api.dev.danakcorp.com"; -// export const BASE_URL = "https://api-sondos.run.danakcorp.com"; +// export const BASE_URL = "https://api.shinan.ir"; export const PRIMARY_COLOR = "#015699"; diff --git a/src/config/func.ts b/src/config/func.ts index 2d6d79a..6d89f51 100644 --- a/src/config/func.ts +++ b/src/config/func.ts @@ -38,7 +38,7 @@ export const getToken = async () => { return localStorage.getItem(TOKEN_NAME); }; -export const getRefreshToken = () => { +export const getRefreshToken = async () => { if (typeof window === "undefined") { return null; }