banner
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import {
|
import {
|
||||||
|
ApiResponse,
|
||||||
AuthenticationType,
|
AuthenticationType,
|
||||||
|
RefreshTokenResponse,
|
||||||
RefreshTokenType,
|
RefreshTokenType,
|
||||||
VerifyOtpType,
|
VerifyOtpType,
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
@@ -15,7 +17,9 @@ export const verifyOtp = async (params: VerifyOtpType) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const refreshToken = async (params: RefreshTokenType) => {
|
export const refreshToken = async (
|
||||||
|
params: RefreshTokenType
|
||||||
|
): Promise<ApiResponse<RefreshTokenResponse>> => {
|
||||||
const { data } = await axios.post(`/user/token`, params);
|
const { data } = await axios.post(`/user/token`, params);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,3 +17,22 @@ export type VerifyOtpType = {
|
|||||||
export type RefreshTokenType = {
|
export type RefreshTokenType = {
|
||||||
token: string;
|
token: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TokenInfo = {
|
||||||
|
token: string;
|
||||||
|
expires: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RefreshTokenResponse = {
|
||||||
|
tokenType: string;
|
||||||
|
accessToken: TokenInfo;
|
||||||
|
refreshToken: TokenInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiResponse<T> = {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
results: {
|
||||||
|
data: T;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
import { useSharedStore } from "@/share/store/sharedStore";
|
||||||
import * as api from "../service/Service";
|
import * as api from "../service/Service";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
export const useGetCart = () => {
|
export const useGetCart = () => {
|
||||||
|
const { isLogin } = useSharedStore();
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["cart"],
|
queryKey: ["cart"],
|
||||||
queryFn: api.getCart,
|
queryFn: api.getCart,
|
||||||
|
enabled: isLogin,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
});
|
||||||
|
};
|
||||||
+87
-39
@@ -1,62 +1,109 @@
|
|||||||
|
'use client'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import Layout from '@/hoc/Layout'
|
import Layout from '@/hoc/Layout'
|
||||||
import { NextPage } from 'next'
|
import { NextPage } from 'next'
|
||||||
import Image from 'next/image'
|
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 ComparePage: NextPage = () => {
|
||||||
|
|
||||||
|
const [productIds, setProductIds] = useState<string[]>([]);
|
||||||
|
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 (
|
||||||
|
<Layout>
|
||||||
|
<div className='mt-24 px-20'>
|
||||||
|
<div className='flex justify-center items-center min-h-[400px]'>
|
||||||
|
<div className='text-center'>
|
||||||
|
<div className='animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto'></div>
|
||||||
|
<p className='mt-4 text-gray-600'>در حال بارگذاری...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (products.length === 0) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className='mt-24 px-20'>
|
||||||
|
<div className='flex justify-center items-center min-h-[400px]'>
|
||||||
|
<div className='text-center'>
|
||||||
|
<p className='text-gray-600'>هیچ محصولی برای مقایسه انتخاب نشده است</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<div className='mt-24 px-20'>
|
<div className='mt-24 px-20'>
|
||||||
<div className='flex border-b border-border'>
|
<div className='flex border-b border-border'>
|
||||||
<div className='w-[300px] flex flex-col justify-center items-center border-l border-border pb-5'>
|
{products.map((product, index) => (
|
||||||
|
<div key={product._id} className={`w-[300px] flex flex-col justify-center items-center ${index < products.length - 1 ? 'border-l border-border' : ''} pb-5`}>
|
||||||
<Image
|
<Image
|
||||||
src='https://shinan.storage.iran.liara.space/media-file/30661b3bde87834d3feae30953d5fc63232d78f181e7fed0.png'
|
src={product.imagesUrl.cover}
|
||||||
alt='compare'
|
alt={product.title_fa}
|
||||||
width={150}
|
width={150}
|
||||||
height={150}
|
height={150}
|
||||||
className='max-w-[100px] max-h-[100px] object-contain'
|
className='max-w-[100px] max-h-[100px] object-contain'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='mt-6 font-light text-sm'>
|
<div className='mt-6 font-light text-sm text-center'>
|
||||||
رومیزی سندس کد T-2112 طرح فرش
|
{product.title_fa}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8 text-sm font-light'>
|
<div className='mt-8 text-sm font-light'>
|
||||||
260,000 <span className='text-[10px]'>تومان</span>
|
{product.default_variant.price.selling_price.toLocaleString()} <span className='text-[10px]'>تومان</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-5'>
|
<div className='mt-5'>
|
||||||
<Button
|
<Button
|
||||||
|
onClick={() => handleAddToCart(product)}
|
||||||
|
disabled={!product.default_variant.stock}
|
||||||
|
className={!product.default_variant.stock ? 'opacity-50 cursor-not-allowed' : ''}
|
||||||
>
|
>
|
||||||
افزودن به سبد
|
{product.default_variant.stock > 0 ? 'افزودن به سبد' : 'ناموجود'}
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='w-[300px] flex flex-col justify-center items-center border-l border-border pb-5'>
|
|
||||||
<Image
|
|
||||||
src='https://shinan.storage.iran.liara.space/media-file/30661b3bde87834d3feae30953d5fc63232d78f181e7fed0.png'
|
|
||||||
alt='compare'
|
|
||||||
width={150}
|
|
||||||
height={150}
|
|
||||||
className='max-w-[100px] max-h-[100px] object-contain'
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className='mt-6 font-light text-sm'>
|
|
||||||
رومیزی سندس کد T-2112 طرح فرش
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-8 text-sm font-light'>
|
|
||||||
260,000 <span className='text-[10px]'>تومان</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-5'>
|
|
||||||
<Button
|
|
||||||
>
|
|
||||||
افزودن به سبد
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8'>
|
<div className='mt-8'>
|
||||||
@@ -65,19 +112,20 @@ const ComparePage: NextPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-9 border-b border-border pb-7'>
|
{products[0]?.specifications?.map((spec, specIndex) => (
|
||||||
<div className='text-[#999999] text-sm'>وزن</div>
|
<div key={specIndex} className='mt-9 border-b border-border pb-7'>
|
||||||
|
<div className='text-[#999999] text-sm'>{spec.title}</div>
|
||||||
|
|
||||||
<div className='flex mt-6 font-light'>
|
<div className='flex mt-6 font-light'>
|
||||||
<div className='w-[300px]'>
|
{products.map((product, productIndex) => (
|
||||||
350 g
|
<div key={product._id} className='w-[300px] flex items-center'>
|
||||||
</div>
|
{product.specifications?.[specIndex]?.values?.join(', ') || '-'}
|
||||||
<div className='w-[300px] flex gap-6'>
|
{productIndex < products.length - 1 && <Separator orientation='vertical' className='ml-6' />}
|
||||||
<Separator orientation='vertical' />
|
|
||||||
350 g
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import axios from "@/config/axios";
|
||||||
|
|
||||||
|
import { CompareResponse } from "../types/Types";
|
||||||
|
|
||||||
|
export const getCompare = async (
|
||||||
|
productIds: string[]
|
||||||
|
): Promise<CompareResponse> => {
|
||||||
|
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;
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,15 +1,24 @@
|
|||||||
|
'use client'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
import { useGetLanding } from '../hooks/useHomeData'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
const Banners3: FC = () => {
|
const Banners3: FC = () => {
|
||||||
|
|
||||||
|
const { data } = useGetLanding()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex gap-4 h-[500px]'>
|
<div className='flex gap-4'>
|
||||||
<div className='flex flex-col gap-4 h-full flex-1'>
|
<div className='flex flex-col gap-2 h-full flex-1'>
|
||||||
<div className='flex-1 bg-red-50 h-full'>
|
<div className='flex-1 h-full'>
|
||||||
|
<Image src={data?.results?.banners?.[1]?.imageUrl as string} height={500} width={1000} className='flex-1 rounded-lg' alt={data?.results?.banners?.[0]?.altText as string} />
|
||||||
</div>
|
</div>
|
||||||
<div className='flex-1 bg-red-50 h-full'>
|
<div className='flex-1 h-full'>
|
||||||
|
<Image src={data?.results?.banners?.[2]?.imageUrl as string} height={500} width={1000} className='flex-1 rounded-lg' alt={data?.results?.banners?.[2]?.altText as string} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex-1 bg-blue-50 h-full'>
|
<div className='flex-1 h-full'>
|
||||||
|
<Image src={data?.results?.banners?.[0]?.imageUrl as string} height={1000} width={1000} className='flex-1 rounded-lg h-full object-cover' alt={data?.results?.banners?.[0]?.altText as string} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,18 +1,28 @@
|
|||||||
|
'use client'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
import { useGetLanding } from '../hooks/useHomeData'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
const Banners4: FC = () => {
|
const Banners4: FC = () => {
|
||||||
|
const { data } = useGetLanding()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex gap-6 h-[340px]'>
|
<div className='flex gap-6 h-[340px]'>
|
||||||
<div className='flex-1 bg-blue-100 h-full rounded-[10px]'>
|
<div className='flex-1 bg-blue-100 h-full rounded-[10px]'>
|
||||||
|
<Image src={data?.results?.banners?.[3]?.imageUrl as string} height={340} width={1000} className='flex-1 rounded-lg' alt={data?.results?.banners?.[0]?.altText as string} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='w-[158px] flex flex-col gap-6'>
|
<div className='w-[158px] flex flex-col gap-6'>
|
||||||
<div className='w-[158px] h-[158px] rounded-[10px] bg-slate-100' />
|
<div className='w-[158px] h-[158px] rounded-[10px] bg-slate-100'>
|
||||||
<div className='w-[158px] h-[158px] rounded-[10px] bg-slate-200' />
|
<Image src={data?.results?.banners?.[4]?.imageUrl as string} height={158} width={1000} className='flex-1 rounded-lg' alt={data?.results?.banners?.[0]?.altText as string} />
|
||||||
|
</div>
|
||||||
|
<div className='w-[158px] h-[158px] rounded-[10px] bg-slate-200'>
|
||||||
|
<Image src={data?.results?.banners?.[5]?.imageUrl as string} height={158} width={1000} className='flex-1 rounded-lg' alt={data?.results?.banners?.[0]?.altText as string} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='w-[400px] bg-amber-50'>
|
<div className='w-[400px] bg-amber-50'>
|
||||||
|
<Image src={data?.results?.banners?.[6]?.imageUrl as string} height={400} width={1000} className='flex-1 rounded-lg' alt={data?.results?.banners?.[0]?.altText as string} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'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 { FC, useEffect, useState } from 'react'
|
||||||
import { Product } from '@/types/product.types'
|
import { Product } from '@/types/product.types'
|
||||||
import { useAddWishlist, useRemoveWishlist } from '@/app/products/hooks/useProductsData'
|
import { useAddWishlist, useRemoveWishlist } from '@/app/products/hooks/useProductsData'
|
||||||
@@ -8,6 +8,7 @@ import { toast } from '@/components/Toast'
|
|||||||
import { useCheckWishProduct } from '../hooks/useProductData'
|
import { useCheckWishProduct } from '../hooks/useProductData'
|
||||||
import { useProductStore } from '../store/Store'
|
import { useProductStore } from '../store/Store'
|
||||||
import PriceHistory from './PriceHistory'
|
import PriceHistory from './PriceHistory'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
interface ActionProductProps {
|
interface ActionProductProps {
|
||||||
product: Product
|
product: Product
|
||||||
@@ -18,7 +19,6 @@ const ActionProduct: FC<ActionProductProps> = ({ product }) => {
|
|||||||
const { variantId } = useProductStore()
|
const { variantId } = useProductStore()
|
||||||
const { isLogin } = useSharedStore()
|
const { isLogin } = useSharedStore()
|
||||||
const [isWishlist, setIsWishlist] = useState(false)
|
const [isWishlist, setIsWishlist] = useState(false)
|
||||||
const [isCompare, setIsCompare] = useState(false)
|
|
||||||
const [isWishlistLoading, setIsWishlistLoading] = useState(false)
|
const [isWishlistLoading, setIsWishlistLoading] = useState(false)
|
||||||
|
|
||||||
const { mutate: addWishlist } = useAddWishlist()
|
const { mutate: addWishlist } = useAddWishlist()
|
||||||
@@ -72,11 +72,6 @@ const ActionProduct: FC<ActionProductProps> = ({ product }) => {
|
|||||||
}
|
}
|
||||||
}, [data])
|
}, [data])
|
||||||
|
|
||||||
const handleCompare = () => {
|
|
||||||
setIsCompare(!isCompare)
|
|
||||||
// TODO: API call to add/remove from compare
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleShare = () => {
|
const handleShare = () => {
|
||||||
if (navigator.share) {
|
if (navigator.share) {
|
||||||
navigator.share({
|
navigator.share({
|
||||||
@@ -135,12 +130,14 @@ const ActionProduct: FC<ActionProductProps> = ({ product }) => {
|
|||||||
|
|
||||||
<PriceHistory productId={product._id.toString()} />
|
<PriceHistory productId={product._id.toString()} />
|
||||||
|
|
||||||
|
<Link href={`/compare?productId=${product._id}`}>
|
||||||
<button
|
<button
|
||||||
className='p-2 rounded-full bg-white/80 backdrop-blur-sm hover:bg-white transition-colors'
|
className='p-2 rounded-full bg-white/80 backdrop-blur-sm hover:bg-white transition-colors'
|
||||||
title='مقایسه کالا'
|
title='مقایسه کالا'
|
||||||
>
|
>
|
||||||
<RowHorizontal size={20} color='#333333' variant='Bulk' />
|
<RowHorizontal size={20} color='#333333' variant='Bulk' />
|
||||||
</button>
|
</button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
+10
-9
@@ -8,6 +8,7 @@ import {
|
|||||||
removeRefreshToken,
|
removeRefreshToken,
|
||||||
} from "./func";
|
} from "./func";
|
||||||
import { refreshToken } from "../app/auth/service/Service";
|
import { refreshToken } from "../app/auth/service/Service";
|
||||||
|
import { ApiResponse, RefreshTokenResponse } from "../app/auth/types/Types";
|
||||||
import { BASE_URL } from "./const";
|
import { BASE_URL } from "./const";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -47,32 +48,32 @@ axiosInstance.interceptors.response.use(
|
|||||||
if (!refreshTokenValue) {
|
if (!refreshTokenValue) {
|
||||||
await removeRefreshToken();
|
await removeRefreshToken();
|
||||||
await removeToken();
|
await removeToken();
|
||||||
window.location.href = `/auth`;
|
window.location.href = `/`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { data } = await refreshToken({
|
const data: ApiResponse<RefreshTokenResponse> = await refreshToken({
|
||||||
token: refreshTokenValue,
|
token: refreshTokenValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data?.accessToken?.token) {
|
if (data?.results?.data?.accessToken?.token) {
|
||||||
await setToken(data?.accessToken?.token);
|
await setToken(data?.results?.data?.accessToken?.token);
|
||||||
if (data.refreshToken?.token) {
|
if (data?.results?.data?.refreshToken?.token) {
|
||||||
await setRefreshToken(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);
|
return axiosInstance(originalRequest);
|
||||||
} else {
|
} else {
|
||||||
await removeRefreshToken();
|
await removeRefreshToken();
|
||||||
await removeToken();
|
await removeToken();
|
||||||
window.location.href = `/auth`;
|
window.location.href = `/`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (refreshError) {
|
} catch (refreshError) {
|
||||||
console.error("Token refresh failed:", refreshError);
|
console.error("Token refresh failed:", refreshError);
|
||||||
await removeToken();
|
await removeToken();
|
||||||
await removeRefreshToken();
|
await removeRefreshToken();
|
||||||
window.location.href = `/auth`;
|
window.location.href = `/`;
|
||||||
return Promise.reject(refreshError);
|
return Promise.reject(refreshError);
|
||||||
} finally {
|
} finally {
|
||||||
window.isRefreshingToken = false;
|
window.isRefreshingToken = false;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
export const TOKEN_NAME = "sh_token";
|
export const TOKEN_NAME = "sh_token";
|
||||||
export const REFRESH_TOKEN_NAME = "sh_refresh_token";
|
export const REFRESH_TOKEN_NAME = "sh_refresh_token";
|
||||||
export const BASE_URL = "https://shop-api.dev.danakcorp.com";
|
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";
|
export const PRIMARY_COLOR = "#015699";
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ export const getToken = async () => {
|
|||||||
return localStorage.getItem(TOKEN_NAME);
|
return localStorage.getItem(TOKEN_NAME);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRefreshToken = () => {
|
export const getRefreshToken = async () => {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user