add cart no token replace auth login

This commit is contained in:
Samandar Turgunboyev
2026-03-04 16:36:22 +05:00
parent f04ae13c39
commit 95d17b274f
6 changed files with 78 additions and 30 deletions

View File

@@ -7,19 +7,13 @@ import { Card } from '@/shared/ui/card';
import { Skeleton } from '@/shared/ui/skeleton';
import { ProductCard } from '@/widgets/categories/ui/product-card';
import { useQuery } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { Heart } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useEffect } from 'react';
export default function Favourite() {
const router = useRouter();
const t = useTranslations();
const {
data: favourite,
isLoading,
error,
} = useQuery({
const { data: favourite, isLoading } = useQuery({
queryKey: ['favourite_product'],
queryFn: () => product_api.favouuriteProduct(),
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) {
return (
<div className="min-h-screen py-12">

View File

@@ -3,11 +3,13 @@
import { cart_api } from '@/features/cart/lib/api';
import { product_api } from '@/shared/config/api/product/api';
import { BASE_URL } from '@/shared/config/api/URLs';
import { useRouter } from '@/shared/config/i18n/navigation';
import { useCartId } from '@/shared/hooks/cartId';
import formatPrice from '@/shared/lib/formatPrice';
import { cn } from '@/shared/lib/utils';
import { Input } from '@/shared/ui/input';
import { Skeleton } from '@/shared/ui/skeleton';
import { userStore } from '@/widgets/welcome/lib/hook';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { Heart, Minus, Plus, Shield, ShoppingCart, Truck } from 'lucide-react';
@@ -22,6 +24,8 @@ const ProductDetail = () => {
const { product } = useParams<{ product: string }>();
const queryClient = useQueryClient();
const { cart_id } = useCartId();
const { user } = userStore();
const router = useRouter();
const [quantity, setQuantity] = useState<number | string>(1);
@@ -46,6 +50,23 @@ const ProductDetail = () => {
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 isGram = measurement === 'gr';
@@ -164,7 +185,10 @@ const ProductDetail = () => {
/* ---------------- HANDLERS ---------------- */
const handleAddToCart = () => {
// ✅ Debounce-ni bekor qil - double request oldini olish
if (user == null) {
router.push('/auth');
return;
}
if (debounceRef.current) clearTimeout(debounceRef.current);
isManualInputRef.current = false;
@@ -323,9 +347,18 @@ const ProductDetail = () => {
<button
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',
)}
onClick={(e) => {
e.stopPropagation();
if (user == null) {
router.push('/auth');
return;
} else {
favouriteMutation.mutate(String(data?.id));
}
}}
>
<Heart
className={data?.liked ? 'fill-red-500 text-red-500' : ''}

View File

@@ -6,6 +6,7 @@ import { removeRefToken, removeToken } from '@/shared/lib/token';
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/avatar';
import { Button } from '@/shared/ui/button';
import { banner_api } from '@/widgets/welcome/lib/api';
import { userStore } from '@/widgets/welcome/lib/hook';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { Headset, Home, LogOut } from 'lucide-react';
import { useTranslations } from 'next-intl';
@@ -17,6 +18,7 @@ const Profile = () => {
const router = useRouter();
const t = useTranslations();
const queryClient = useQueryClient();
const { setUser } = userStore();
const { data: me, isError } = useQuery({
queryKey: ['get_me'],
@@ -119,6 +121,7 @@ const Profile = () => {
removeToken();
removeRefToken();
setCartId(null);
setUser(null);
queryClient.refetchQueries();
}}
className="w-full justify-start gap-3 text-red-500 hover:text-red-600 hover:bg-red-50 mt-4"

View File

@@ -16,6 +16,7 @@ import { Button } from '@/shared/ui/button';
import { Card, CardContent } from '@/shared/ui/card';
import { Input } from '@/shared/ui/input';
import { FlyingAnimationPortal } from '@/widgets/animation/FlyingAnimationPortal';
import { userStore } from '@/widgets/welcome/lib/hook';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { Heart, Minus, Plus } from 'lucide-react';
@@ -41,6 +42,7 @@ export function ProductCard({
const [animated, setAnimated] = useState(false);
const debounceRef = useRef<NodeJS.Timeout | null>(null);
const imageRef = useRef<HTMLDivElement>(null);
const { user } = userStore();
/** ✅ Measurement */
const measurementName = product.meansurement?.name ?? null;
@@ -67,7 +69,6 @@ export function ProductCard({
onError: (err: AxiosError) => {
const detail = (err.response?.data as { detail?: string })?.detail;
toast.error(detail || err.message, {
richColors: true,
position: 'top-center',
@@ -176,8 +177,9 @@ export function ProductCard({
queryClient.refetchQueries({ queryKey: ['favourite_product'] });
},
onError: () => {
toast.error(t('Tizimga kirilmagan'), {
onError: (err: AxiosError) => {
const detail = (err.response?.data as { detail?: string })?.detail;
toast.error(detail || err.message, {
richColors: true,
position: 'top-center',
});
@@ -215,7 +217,12 @@ export function ProductCard({
<Button
onClick={(e) => {
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"
>
@@ -261,11 +268,16 @@ export function ProductCard({
<Button
onClick={(e) => {
e.stopPropagation();
addToCart({
product: String(product.id),
quantity: defaultQty,
cart: cart_id!,
});
if (user) {
addToCart({
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"
>

View File

@@ -453,7 +453,14 @@ const Navbar = () => {
<Button
variant={'ghost'}
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"
>
<Heart className="size-4 text-foreground" />
@@ -461,7 +468,14 @@ const Navbar = () => {
<Button
variant={'ghost'}
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"
>
<ShoppingCart className="size-4 text-foreground" />

View File

@@ -6,10 +6,10 @@ type State = {
};
type Actions = {
setUser: (qty: UserRes) => void;
setUser: (qty: UserRes | null) => void;
};
export const userStore = create<State & Actions>((set) => ({
user: null,
setUser: (user: UserRes) => set(() => ({ user })),
setUser: (user: UserRes | null) => set(() => ({ user })),
}));