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

View File

@@ -0,0 +1,80 @@
import React from "react";
const Preloader: React.FC = () => {
return (
// bg-bg ve text-fg bizim CSS'te tanımladığımız değişkenleri kullanır.
// Standart Tailwind sınıflarını (flex, min-h-screen) düzen için kullanıyoruz.
<div className="flex flex-col items-center justify-center min-h-screen text-fg font-sans transition-colors duration-300">
<div className="text-center max-w-[20em] w-full">
{/* SVG Konteyner */}
<svg
className="block mx-auto mb-6 w-32 h-32"
role="img"
aria-label="Shopping cart line animation"
viewBox="0 0 128 128"
xmlns="http://www.w3.org/2000/svg"
>
<g
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="8"
>
{/* Track (Arka plan izleri) */}
<g className="stroke-track">
<polyline points="4,4 21,4 26,22 124,22 112,64 35,64 39,80 106,80" />
<circle cx="43" cy="111" r="13" />
<circle cx="102" cy="111" r="13" />
</g>
{/* Hareketli Çizgiler */}
{/* animate-cartLines sınıfı globals.css'ten geliyor */}
<g className="stroke-primary animate-cartLines">
<polyline
className="animate-cartTop"
points="4,4 21,4 26,22 124,22 112,64 35,64 39,80 106,80"
strokeDasharray="338 338"
strokeDashoffset="-338"
/>
<g className="animate-cartWheel1">
<circle
className="animate-cartWheelStroke"
cx="43"
cy="111"
r="13"
strokeDasharray="81.68 81.68"
strokeDashoffset="81.68"
/>
</g>
<g className="animate-cartWheel2">
<circle
className="animate-cartWheelStroke"
cx="102"
cy="111"
r="13"
strokeDasharray="81.68 81.68"
strokeDashoffset="81.68"
/>
</g>
</g>
</g>
</svg>
{/* Yükleniyor Yazıları */}
<div className="relative h-6">
<p className="absolute w-full animate-msg text-lg">
Bringing you the goods
</p>
<p className="absolute w-full opacity-0 invisible animate-msgLast text-lg">
This is taking long. Somethings wrong.
</p>
</div>
</div>
</div>
);
};
export default Preloader;

66
components/icons.tsx Normal file
View File

@@ -0,0 +1,66 @@
export const FavoriteIcon = () => (
<svg
fill="gray"
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-1sekacc"
data-testid="FavoriteBorderIcon"
viewBox="0 0 24 24"
>
<path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3m-4.4 15.55-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05" />
</svg>
);
export const OrderIcon = () => (
<svg
fill="gray"
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-1sekacc"
data-testid="LocalShippingIcon"
viewBox="0 0 24 24"
>
<path d="M20 8h-3V4H3c-1.1 0-2 .9-2 2v11h2c0 1.66 1.34 3 3 3s3-1.34 3-3h6c0 1.66 1.34 3 3 3s3-1.34 3-3h2v-5zM6 18.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5m13.5-9 1.96 2.5H17V9.5zm-1.5 9c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5" />
</svg>
);
export const CartIcon = () => (
<svg
fill="gray"
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-1sekacc"
data-testid="ShoppingBasketIcon"
viewBox="0 0 24 24"
>
<path d="m17.21 9-4.38-6.56c-.19-.28-.51-.42-.83-.42s-.64.14-.83.43L6.79 9H2c-.55 0-1 .45-1 1 0 .09.01.18.04.27l2.54 9.27c.23.84 1 1.46 1.92 1.46h13c.92 0 1.69-.62 1.93-1.46l2.54-9.27L23 10c0-.55-.45-1-1-1zM9 9l3-4.4L15 9zm3 8c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2" />
</svg>
);
export const CategoryIcon = () => (
<svg
fill="white"
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-nfmerv"
data-testid="WidgetsIcon"
viewBox="0 0 24 24"
>
<path d="M13 13v8h8v-8zM3 21h8v-8H3zM3 3v8h8V3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66z" />
</svg>
);
export const SearchIcon = () => (
<svg
fill="white"
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-1sekacc"
data-testid="SearchIcon"
viewBox="0 0 20 20"
>
<path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14" />
</svg>
);
export const ProfileIcon = () => (
<svg
fill="gray"
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-1sekacc"
data-testid="FaceIcon"
viewBox="0 0 24 24"
>
<path d="M9 11.75a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5m6 0a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.41 0-8-3.59-8-8 0-.29.02-.58.05-.86 2.36-1.05 4.23-2.98 5.21-5.37a9.97 9.97 0 0 0 10.41 3.97c.21.71.33 1.47.33 2.26 0 4.41-3.59 8-8 8" />
</svg>
);

View File

@@ -11,7 +11,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import Logo from "@/public/logo.png";
import Logo from "@/public/logo.webp";
import CategoryMenu from "./ui/CategoryMenu";
import SearchBar from "./ui/SearchBar";
import AuthDialog from "./ui/AuthDialog";
@@ -19,6 +19,7 @@ import ActionButtons from "./ui/ActionButtons";
import LanguageSelector from "./ui/LanguageSelector";
import { useAuthStatus, useLogout } from "@/lib/hooks/useAuth";
import { useTranslations } from "next-intl";
import { CategoryIcon } from "../icons";
interface HeaderProps {
locale?: string;
@@ -76,7 +77,7 @@ export default function Header({ locale = "ru" }: HeaderProps) {
className="hidden gap-2 rounded-xl font-bold sm:flex hover:bg-[#005bff] bg-[#005bff] text-white"
size="lg"
>
{isCategoryOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
{isCategoryOpen ? <X className="h-5 w-5" /> : <CategoryIcon />}
{t("common.catalog")}
</Button>

View File

@@ -16,6 +16,7 @@ import { useCart, useFavorites, useOrders } from "@/lib/hooks";
import { Skeleton } from "@/components/ui/skeleton";
import { useTranslations } from "next-intl";
import { useLogout } from "@/lib/hooks/useAuth";
import { CartIcon, FavoriteIcon, OrderIcon, ProfileIcon } from "@/components/icons";
interface ActionButtonsProps {
isAuthenticated: boolean;
@@ -70,21 +71,21 @@ export default function ActionButtons({
const buttons: ActionButtonData[] = useMemo(() => [
{
icon: <Truck className="h-5 w-5 text-gray-600" />,
icon: <OrderIcon />,
label: t("common.orders"),
href: "/orders",
badgeCount: ordersCount,
isLoading: ordersLoading,
},
{
icon: <Heart className="h-5 w-5 text-gray-600" />,
icon: <FavoriteIcon />,
label: t("common.favorites"),
href: "/favorites",
badgeCount: favoritesCount,
isLoading: favoritesLoading,
},
{
icon: <ShoppingCart className="h-5 w-5 text-gray-600" />,
icon: <CartIcon />,
label: t("common.cart"),
href: "/cart",
badgeCount: cartCount,
@@ -101,7 +102,7 @@ export default function ActionButtons({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="flex-col gap-0.5 h-auto px-2 py-2">
<User className="h-5 w-5 text-gray-600" />
<ProfileIcon />
<span className="text-xs text-gray-700">{t("profile")}</span>
</Button>
</DropdownMenuTrigger>
@@ -118,7 +119,7 @@ export default function ActionButtons({
</DropdownMenu>
) : (
<Button variant="ghost" size="sm" className="flex-col gap-0.5 h-auto px-2 py-2" onClick={onAuthClick}>
<User className="h-5 w-5 text-gray-600" />
<ProfileIcon />
<span className="text-xs text-gray-700">{t("common.login")}</span>
</Button>
)}

View File

@@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { toast } from "sonner";
import Logo from "@/public/logo.png";
import Logo from "@/public/logo.webp";
import { useLogin, useVerifyToken } from "@/lib/hooks/useAuth";
import { useTranslations } from "next-intl";

View File

@@ -13,6 +13,7 @@ import {
import { useRouter } from "next/navigation";
import { useSearchProducts } from "@/features/search/hooks/useSearch";
import Image from "next/image";
import { SearchIcon } from "@/components/icons";
interface SearchBarProps {
isMobile: boolean;
@@ -158,7 +159,7 @@ export default function SearchBar({
size="icon"
className="h-auto hover:bg-[#005bff] cursor-pointer bg-transparent flex items-center mr-1.5 text-white"
>
<Search className="h-5 w-5" />
<SearchIcon />
</Button>
<SearchResults />
</div>