add cart no token replace auth login
This commit is contained in:
@@ -7,19 +7,13 @@ import { Card } from '@/shared/ui/card';
|
|||||||
import { Skeleton } from '@/shared/ui/skeleton';
|
import { Skeleton } from '@/shared/ui/skeleton';
|
||||||
import { ProductCard } from '@/widgets/categories/ui/product-card';
|
import { ProductCard } from '@/widgets/categories/ui/product-card';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { AxiosError } from 'axios';
|
|
||||||
import { Heart } from 'lucide-react';
|
import { Heart } from 'lucide-react';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
export default function Favourite() {
|
export default function Favourite() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const {
|
const { data: favourite, isLoading } = useQuery({
|
||||||
data: favourite,
|
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
} = useQuery({
|
|
||||||
queryKey: ['favourite_product'],
|
queryKey: ['favourite_product'],
|
||||||
queryFn: () => product_api.favouuriteProduct(),
|
queryFn: () => product_api.favouuriteProduct(),
|
||||||
select(data) {
|
select(data) {
|
||||||
@@ -27,14 +21,6 @@ export default function Favourite() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if ((error as AxiosError)?.status === 403) {
|
|
||||||
router.replace('/auth');
|
|
||||||
} else if ((error as AxiosError)?.status === 401) {
|
|
||||||
router.replace('/auth');
|
|
||||||
}
|
|
||||||
}, [error]);
|
|
||||||
|
|
||||||
if (favourite && favourite.results.length === 0) {
|
if (favourite && favourite.results.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<div className="min-h-screen py-12">
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
import { cart_api } from '@/features/cart/lib/api';
|
import { cart_api } from '@/features/cart/lib/api';
|
||||||
import { product_api } from '@/shared/config/api/product/api';
|
import { product_api } from '@/shared/config/api/product/api';
|
||||||
import { BASE_URL } from '@/shared/config/api/URLs';
|
import { BASE_URL } from '@/shared/config/api/URLs';
|
||||||
|
import { useRouter } from '@/shared/config/i18n/navigation';
|
||||||
import { useCartId } from '@/shared/hooks/cartId';
|
import { useCartId } from '@/shared/hooks/cartId';
|
||||||
import formatPrice from '@/shared/lib/formatPrice';
|
import formatPrice from '@/shared/lib/formatPrice';
|
||||||
import { cn } from '@/shared/lib/utils';
|
import { cn } from '@/shared/lib/utils';
|
||||||
import { Input } from '@/shared/ui/input';
|
import { Input } from '@/shared/ui/input';
|
||||||
import { Skeleton } from '@/shared/ui/skeleton';
|
import { Skeleton } from '@/shared/ui/skeleton';
|
||||||
|
import { userStore } from '@/widgets/welcome/lib/hook';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { Heart, Minus, Plus, Shield, ShoppingCart, Truck } from 'lucide-react';
|
import { Heart, Minus, Plus, Shield, ShoppingCart, Truck } from 'lucide-react';
|
||||||
@@ -22,6 +24,8 @@ const ProductDetail = () => {
|
|||||||
const { product } = useParams<{ product: string }>();
|
const { product } = useParams<{ product: string }>();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { cart_id } = useCartId();
|
const { cart_id } = useCartId();
|
||||||
|
const { user } = userStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const [quantity, setQuantity] = useState<number | string>(1);
|
const [quantity, setQuantity] = useState<number | string>(1);
|
||||||
|
|
||||||
@@ -46,6 +50,23 @@ const ProductDetail = () => {
|
|||||||
enabled: !!cart_id,
|
enabled: !!cart_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const favouriteMutation = useMutation({
|
||||||
|
mutationFn: (productId: string) => product_api.favourite(productId),
|
||||||
|
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['product_detail'] });
|
||||||
|
queryClient.refetchQueries({ queryKey: ['favourite_product'] });
|
||||||
|
},
|
||||||
|
|
||||||
|
onError: (err: AxiosError) => {
|
||||||
|
const detail = (err.response?.data as { detail?: string })?.detail;
|
||||||
|
toast.error(detail || err.message, {
|
||||||
|
richColors: true,
|
||||||
|
position: 'top-center',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const measurement = data?.meansurement?.name?.toLowerCase() || '';
|
const measurement = data?.meansurement?.name?.toLowerCase() || '';
|
||||||
const isGram = measurement === 'gr';
|
const isGram = measurement === 'gr';
|
||||||
|
|
||||||
@@ -164,7 +185,10 @@ const ProductDetail = () => {
|
|||||||
|
|
||||||
/* ---------------- HANDLERS ---------------- */
|
/* ---------------- HANDLERS ---------------- */
|
||||||
const handleAddToCart = () => {
|
const handleAddToCart = () => {
|
||||||
// ✅ Debounce-ni bekor qil - double request oldini olish
|
if (user == null) {
|
||||||
|
router.push('/auth');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||||
isManualInputRef.current = false;
|
isManualInputRef.current = false;
|
||||||
|
|
||||||
@@ -323,9 +347,18 @@ const ProductDetail = () => {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
'p-3 rounded-lg border',
|
'p-3 rounded-lg border cursor-pointer',
|
||||||
data?.liked ? 'border-red-500 bg-red-50' : 'border-gray-300',
|
data?.liked ? 'border-red-500 bg-red-50' : 'border-gray-300',
|
||||||
)}
|
)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (user == null) {
|
||||||
|
router.push('/auth');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
favouriteMutation.mutate(String(data?.id));
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Heart
|
<Heart
|
||||||
className={data?.liked ? 'fill-red-500 text-red-500' : ''}
|
className={data?.liked ? 'fill-red-500 text-red-500' : ''}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { removeRefToken, removeToken } from '@/shared/lib/token';
|
|||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/avatar';
|
||||||
import { Button } from '@/shared/ui/button';
|
import { Button } from '@/shared/ui/button';
|
||||||
import { banner_api } from '@/widgets/welcome/lib/api';
|
import { banner_api } from '@/widgets/welcome/lib/api';
|
||||||
|
import { userStore } from '@/widgets/welcome/lib/hook';
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { Headset, Home, LogOut } from 'lucide-react';
|
import { Headset, Home, LogOut } from 'lucide-react';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
@@ -17,6 +18,7 @@ const Profile = () => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const { setUser } = userStore();
|
||||||
|
|
||||||
const { data: me, isError } = useQuery({
|
const { data: me, isError } = useQuery({
|
||||||
queryKey: ['get_me'],
|
queryKey: ['get_me'],
|
||||||
@@ -119,6 +121,7 @@ const Profile = () => {
|
|||||||
removeToken();
|
removeToken();
|
||||||
removeRefToken();
|
removeRefToken();
|
||||||
setCartId(null);
|
setCartId(null);
|
||||||
|
setUser(null);
|
||||||
queryClient.refetchQueries();
|
queryClient.refetchQueries();
|
||||||
}}
|
}}
|
||||||
className="w-full justify-start gap-3 text-red-500 hover:text-red-600 hover:bg-red-50 mt-4"
|
className="w-full justify-start gap-3 text-red-500 hover:text-red-600 hover:bg-red-50 mt-4"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { Button } from '@/shared/ui/button';
|
|||||||
import { Card, CardContent } from '@/shared/ui/card';
|
import { Card, CardContent } from '@/shared/ui/card';
|
||||||
import { Input } from '@/shared/ui/input';
|
import { Input } from '@/shared/ui/input';
|
||||||
import { FlyingAnimationPortal } from '@/widgets/animation/FlyingAnimationPortal';
|
import { FlyingAnimationPortal } from '@/widgets/animation/FlyingAnimationPortal';
|
||||||
|
import { userStore } from '@/widgets/welcome/lib/hook';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { Heart, Minus, Plus } from 'lucide-react';
|
import { Heart, Minus, Plus } from 'lucide-react';
|
||||||
@@ -41,6 +42,7 @@ export function ProductCard({
|
|||||||
const [animated, setAnimated] = useState(false);
|
const [animated, setAnimated] = useState(false);
|
||||||
const debounceRef = useRef<NodeJS.Timeout | null>(null);
|
const debounceRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const imageRef = useRef<HTMLDivElement>(null);
|
const imageRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { user } = userStore();
|
||||||
|
|
||||||
/** ✅ Measurement */
|
/** ✅ Measurement */
|
||||||
const measurementName = product.meansurement?.name ?? null;
|
const measurementName = product.meansurement?.name ?? null;
|
||||||
@@ -67,7 +69,6 @@ export function ProductCard({
|
|||||||
|
|
||||||
onError: (err: AxiosError) => {
|
onError: (err: AxiosError) => {
|
||||||
const detail = (err.response?.data as { detail?: string })?.detail;
|
const detail = (err.response?.data as { detail?: string })?.detail;
|
||||||
|
|
||||||
toast.error(detail || err.message, {
|
toast.error(detail || err.message, {
|
||||||
richColors: true,
|
richColors: true,
|
||||||
position: 'top-center',
|
position: 'top-center',
|
||||||
@@ -176,8 +177,9 @@ export function ProductCard({
|
|||||||
queryClient.refetchQueries({ queryKey: ['favourite_product'] });
|
queryClient.refetchQueries({ queryKey: ['favourite_product'] });
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: () => {
|
onError: (err: AxiosError) => {
|
||||||
toast.error(t('Tizimga kirilmagan'), {
|
const detail = (err.response?.data as { detail?: string })?.detail;
|
||||||
|
toast.error(detail || err.message, {
|
||||||
richColors: true,
|
richColors: true,
|
||||||
position: 'top-center',
|
position: 'top-center',
|
||||||
});
|
});
|
||||||
@@ -215,7 +217,12 @@ export function ProductCard({
|
|||||||
<Button
|
<Button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
favouriteMutation.mutate(String(product.id));
|
if (user == null) {
|
||||||
|
router.push('/auth');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
favouriteMutation.mutate(String(product.id));
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="absolute top-2 right-2 z-10 bg-white hover:bg-gray-200 rounded-full p-2 shadow"
|
className="absolute top-2 right-2 z-10 bg-white hover:bg-gray-200 rounded-full p-2 shadow"
|
||||||
>
|
>
|
||||||
@@ -261,11 +268,16 @@ export function ProductCard({
|
|||||||
<Button
|
<Button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
addToCart({
|
|
||||||
product: String(product.id),
|
if (user) {
|
||||||
quantity: defaultQty,
|
addToCart({
|
||||||
cart: cart_id!,
|
product: String(product.id),
|
||||||
});
|
quantity: defaultQty,
|
||||||
|
cart: cart_id!,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
router.push('/auth');
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="w-full bg-white border border-slate-300 text-slate-700"
|
className="w-full bg-white border border-slate-300 text-slate-700"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -453,7 +453,14 @@ const Navbar = () => {
|
|||||||
<Button
|
<Button
|
||||||
variant={'ghost'}
|
variant={'ghost'}
|
||||||
className="h-10 max-lg:hidden cursor-pointer border border-slate-200"
|
className="h-10 max-lg:hidden cursor-pointer border border-slate-200"
|
||||||
onClick={() => router.push('/favourite')}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (token) {
|
||||||
|
router.push('/favourite');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push('/auth');
|
||||||
|
}}
|
||||||
aria-label="my favouurite product"
|
aria-label="my favouurite product"
|
||||||
>
|
>
|
||||||
<Heart className="size-4 text-foreground" />
|
<Heart className="size-4 text-foreground" />
|
||||||
@@ -461,7 +468,14 @@ const Navbar = () => {
|
|||||||
<Button
|
<Button
|
||||||
variant={'ghost'}
|
variant={'ghost'}
|
||||||
id="cart-icon"
|
id="cart-icon"
|
||||||
onClick={() => router.push('/cart')}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (token) {
|
||||||
|
router.push('/cart');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push('/auth');
|
||||||
|
}}
|
||||||
className="h-10 relative max-lg:hidden cursor-pointer border border-slate-200"
|
className="h-10 relative max-lg:hidden cursor-pointer border border-slate-200"
|
||||||
>
|
>
|
||||||
<ShoppingCart className="size-4 text-foreground" />
|
<ShoppingCart className="size-4 text-foreground" />
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ type State = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Actions = {
|
type Actions = {
|
||||||
setUser: (qty: UserRes) => void;
|
setUser: (qty: UserRes | null) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const userStore = create<State & Actions>((set) => ({
|
export const userStore = create<State & Actions>((set) => ({
|
||||||
user: null,
|
user: null,
|
||||||
setUser: (user: UserRes) => set(() => ({ user })),
|
setUser: (user: UserRes | null) => set(() => ({ user })),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user