fixed favorites api

This commit is contained in:
Jelaletdin12
2025-12-09 14:59:20 +05:00
parent 2857d34f4d
commit d6c163dd06
21 changed files with 467 additions and 147 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -26,30 +26,12 @@ export default function FavoritesPage() {
useRemoveFromFavorites();
const { mutate: addToCart, isPending: isAddingToCart } = useAddToCart();
const handleRemoveFromFavorites = useCallback((productId: number) => {
removeFromFavorites(productId, {
onSuccess: () => {
toast({
title: t("removed_from_favorites"),
});
},
onError: (error) => {
toast({
title: t("error"),
description: error.message,
variant: "destructive",
});
},
});
}, [removeFromFavorites, toast, t]);
const handleAddToCart = useCallback((productId: number) => {
addToCart(
{ productId },
{
const handleRemoveFromFavorites = useCallback(
(productId: number) => {
removeFromFavorites(productId, {
onSuccess: () => {
toast({
title: t("added_to_cart"),
title: t("removed_from_favorites"),
});
},
onError: (error) => {
@@ -59,20 +41,47 @@ export default function FavoritesPage() {
variant: "destructive",
});
},
}
);
}, [addToCart, toast, t]);
});
},
[removeFromFavorites, toast, t]
);
const loadingSkeleton = useMemo(() => (
<div className="container mx-auto px-4 py-8 min-h-screen">
<h1 className="text-3xl font-bold mb-6">{t("favorite_products")}</h1>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{Array.from({ length: 10 }).map((_, i) => (
<Skeleton key={i} className="w-full h-64 rounded-lg" />
))}
const handleAddToCart = useCallback(
(productId: number) => {
addToCart(
{ productId },
{
onSuccess: () => {
toast({
title: t("added_to_cart"),
});
},
onError: (error) => {
toast({
title: t("error"),
description: error.message,
variant: "destructive",
});
},
}
);
},
[addToCart, toast, t]
);
const loadingSkeleton = useMemo(
() => (
<div className="container mx-auto px-4 py-8 min-h-screen">
<h1 className="text-3xl font-bold mb-6">{t("favorite_products")}</h1>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{Array.from({ length: 10 }).map((_, i) => (
<Skeleton key={i} className="w-full h-64 rounded-lg" />
))}
</div>
</div>
</div>
), [t]);
),
[t]
);
if (isLoading) {
return loadingSkeleton;
@@ -95,7 +104,7 @@ export default function FavoritesPage() {
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{favorites.map((favorite: Favorite) => (
<ProductCard
key={favorite.created_at}
key={favorite.product.id}
productId={favorite.product.id}
product={favorite.product}
onRemove={() => handleRemoveFromFavorites(favorite.product.id)}
@@ -170,7 +179,6 @@ function ProductCard({
>
<Link href={`/product/${productId || product.slug}`} className="block">
<div className="relative aspect-square bg-gray-50">
{/* Favorite Button */}
<button
onClick={(e) => {
e.preventDefault();
@@ -183,7 +191,6 @@ function ProductCard({
<Heart className="h-5 w-5 fill-red-500 text-red-500" />
</button>
{/* Product Image */}
<Image
src={imageUrl}
alt={product.name}
@@ -193,7 +200,6 @@ function ProductCard({
priority={false}
/>
{/* Out of Stock Badge */}
{product.stock === 0 && (
<div className="absolute inset-0 bg-black/50 flex items-center justify-center">
<Badge variant="secondary" className="text-sm">
@@ -203,7 +209,6 @@ function ProductCard({
)}
</div>
{/* Product Info */}
<div className="p-3">
<h3 className="font-medium text-sm line-clamp-2 mb-2 min-h-[40px]">
{product.name}
@@ -217,7 +222,6 @@ function ProductCard({
</div>
</Link>
{/* Add to Cart Button - показывается при hover */}
{isHovered && product.stock > 0 && (
<div className="absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-white via-white to-transparent">
<Button
@@ -236,4 +240,4 @@ function ProductCard({
)}
</Card>
);
}
}

View File

@@ -45,7 +45,7 @@
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--background: #eff3f6;
--foreground: oklch(0.141 0.005 285.823);
--card: oklch(1 0 0);
--card-foreground: oklch(0.141 0.005 285.823);
@@ -120,3 +120,73 @@
@apply bg-background text-foreground;
}
}
/* Animasyonları "utilities" katmanına ekliyoruz ki Tailwind sınıfları gibi davranabilsinler */
@layer utilities {
/* Özel Renk Sınıfları (CSS değişkenlerini kullanmak için) */
.text-fg { color: var(--fg); }
.bg-bg { background-color: var(--bg); }
.stroke-primary { stroke: #005bff; }
/* Dark mode track rengi için özel sınıf */
.stroke-track {
stroke: hsla(var(--hue), 10%, 10%, 0.1);
transition: stroke var(--trans-dur);
}
@media (prefers-color-scheme: dark) {
.stroke-track {
stroke: hsla(var(--hue), 10%, 90%, 0.1);
}
}
/* Animasyon Sınıfları */
.animate-msg { animation: msg 0.3s 13.7s linear forwards; }
.animate-msgLast { animation: msg 0.3s 14s linear reverse forwards; }
.animate-cartLines { animation: cartLines 2s ease-in-out infinite; }
.animate-cartTop { animation: cartTop 2s ease-in-out infinite; }
.animate-cartWheel1 {
animation: cartWheel1 2s ease-in-out infinite;
transform: rotate(-0.25turn);
transform-origin: 43px 111px;
}
.animate-cartWheel2 {
animation: cartWheel2 2s ease-in-out infinite;
transform: rotate(0.25turn);
transform-origin: 102px 111px;
}
.animate-cartWheelStroke { animation: cartWheelStroke 2s ease-in-out infinite; }
}
/* Keyframes Tanımları */
@keyframes msg {
from { opacity: 1; visibility: visible; }
99.9% { opacity: 0; visibility: visible; }
to { opacity: 0; visibility: hidden; }
}
@keyframes cartLines {
from, to { opacity: 0; }
8%, 92% { opacity: 1; }
}
@keyframes cartTop {
from { stroke-dashoffset: -338; }
50% { stroke-dashoffset: 0; }
to { stroke-dashoffset: 338; }
}
@keyframes cartWheel1 {
from { transform: rotate(-0.25turn); }
to { transform: rotate(2.75turn); }
}
@keyframes cartWheel2 {
from { transform: rotate(0.25turn); }
to { transform: rotate(3.25turn); }
}
@keyframes cartWheelStroke {
from, to { stroke-dashoffset: 81.68; }
50% { stroke-dashoffset: 40.84; }
}

BIN
app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB