Добавлены модели и функциональность для работы с избранными товарами, включая мутации и запросы в GraphQL. Обновлены компоненты для отображения и управления избранным, улучшен интерфейс взаимодействия с пользователем. Реализована логика добавления и удаления товаров из избранного.

This commit is contained in:
Bivekich
2025-07-17 19:36:41 +03:00
parent f377fbab5f
commit 3d28051bde
12 changed files with 1074 additions and 141 deletions

View File

@ -4,7 +4,7 @@ import { useQuery } from '@apollo/client'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { GET_CATEGORIES, GET_MY_CART } from '@/graphql/queries'
import { Package2, ArrowRight, Sparkles, ShoppingCart } from 'lucide-react'
import { Package2, ArrowRight, Sparkles, ShoppingCart, Heart } from 'lucide-react'
interface Category {
id: string
@ -16,9 +16,10 @@ interface Category {
interface MarketCategoriesProps {
onSelectCategory: (categoryId: string, categoryName: string) => void
onShowCart?: () => void
onShowFavorites?: () => void
}
export function MarketCategories({ onSelectCategory, onShowCart }: MarketCategoriesProps) {
export function MarketCategories({ onSelectCategory, onShowCart, onShowFavorites }: MarketCategoriesProps) {
const { data, loading, error } = useQuery(GET_CATEGORIES)
const { data: cartData } = useQuery(GET_MY_CART)
@ -67,16 +68,29 @@ export function MarketCategories({ onSelectCategory, onShowCart }: MarketCategor
</div>
</div>
{/* Кнопка корзины */}
{onShowCart && (
<Button
onClick={onShowCart}
className="bg-gradient-to-r from-purple-500/20 to-pink-500/20 hover:from-purple-500/30 hover:to-pink-500/30 text-white border-purple-500/30 hover:border-purple-400/50 transition-all duration-200 shadow-lg px-6 py-3"
>
<ShoppingCart className="h-5 w-5 mr-2" />
Корзина {uniqueItemsCount > 0 && `(${uniqueItemsCount})`}
</Button>
)}
{/* Кнопки корзины и избранного */}
<div className="flex items-center space-x-3">
{onShowFavorites && (
<Button
onClick={onShowFavorites}
variant="outline"
className="bg-gradient-to-r from-red-500/20 to-pink-500/20 hover:from-red-500/30 hover:to-pink-500/30 text-white border-red-500/30 hover:border-red-400/50 transition-all duration-200 shadow-lg px-6 py-3"
>
<Heart className="h-5 w-5 mr-2" />
Избранное
</Button>
)}
{onShowCart && (
<Button
onClick={onShowCart}
className="bg-gradient-to-r from-purple-500/20 to-pink-500/20 hover:from-purple-500/30 hover:to-pink-500/30 text-white border-purple-500/30 hover:border-purple-400/50 transition-all duration-200 shadow-lg px-6 py-3"
>
<ShoppingCart className="h-5 w-5 mr-2" />
Корзина {uniqueItemsCount > 0 && `(${uniqueItemsCount})`}
</Button>
)}
</div>
</div>
{/* Категории */}

View File

@ -12,9 +12,10 @@ import { MarketWholesale } from './market-wholesale'
import { MarketProducts } from './market-products'
import { MarketCategories } from './market-categories'
import { MarketRequests } from './market-requests'
import { FavoritesDashboard } from '../favorites/favorites-dashboard'
export function MarketDashboard() {
const [productsView, setProductsView] = useState<'categories' | 'products' | 'cart'>('categories')
const [productsView, setProductsView] = useState<'categories' | 'products' | 'cart' | 'favorites'>('categories')
const [selectedCategory, setSelectedCategory] = useState<{ id: string; name: string } | null>(null)
const handleSelectCategory = (categoryId: string, categoryName: string) => {
@ -32,6 +33,11 @@ export function MarketDashboard() {
setSelectedCategory(null)
}
const handleShowFavorites = () => {
setProductsView('favorites')
setSelectedCategory(null)
}
return (
<div className="h-screen bg-gradient-smooth flex overflow-hidden">
<Sidebar />
@ -122,15 +128,17 @@ export function MarketDashboard() {
<TabsContent value="products" className="flex-1 overflow-hidden mt-6">
<Card className="glass-card h-full overflow-hidden p-0">
{productsView === 'categories' ? (
<MarketCategories onSelectCategory={handleSelectCategory} onShowCart={handleShowCart} />
<MarketCategories onSelectCategory={handleSelectCategory} onShowCart={handleShowCart} onShowFavorites={handleShowFavorites} />
) : productsView === 'products' ? (
<MarketProducts
selectedCategoryId={selectedCategory?.id}
selectedCategoryName={selectedCategory?.name}
onBackToCategories={handleBackToCategories}
/>
) : productsView === 'cart' ? (
<MarketRequests onBackToCategories={handleBackToCategories} />
) : (
<MarketRequests />
<FavoritesDashboard onBackToCategories={handleBackToCategories} />
)}
</Card>
</TabsContent>

View File

@ -4,9 +4,14 @@ import { useQuery } from '@apollo/client'
import { CartItems } from '../cart/cart-items'
import { CartSummary } from '../cart/cart-summary'
import { GET_MY_CART } from '@/graphql/queries'
import { ShoppingCart, Package } from 'lucide-react'
import { ShoppingCart, Package, ArrowLeft } from 'lucide-react'
import { Button } from '@/components/ui/button'
export function MarketRequests() {
interface MarketRequestsProps {
onBackToCategories?: () => void
}
export function MarketRequests({ onBackToCategories }: MarketRequestsProps) {
const { data, loading, error } = useQuery(GET_MY_CART)
const cart = data?.myCart
@ -39,6 +44,16 @@ export function MarketRequests() {
<div className="h-full w-full flex flex-col">
{/* Заголовок */}
<div className="flex items-center space-x-3 p-6 border-b border-white/10">
{onBackToCategories && (
<Button
onClick={onBackToCategories}
variant="ghost"
size="sm"
className="text-white/70 hover:text-white hover:bg-white/10 p-2"
>
<ArrowLeft className="h-5 w-5" />
</Button>
)}
<ShoppingCart className="h-6 w-6 text-purple-400" />
<div>
<h1 className="text-xl font-bold text-white">Мои заявки</h1>

View File

@ -8,14 +8,15 @@ import {
ShoppingCart,
Eye,
ChevronLeft,
ChevronRight
ChevronRight,
Heart
} from 'lucide-react'
import { OrganizationAvatar } from './organization-avatar'
import { Input } from '@/components/ui/input'
import Image from 'next/image'
import { useMutation } from '@apollo/client'
import { ADD_TO_CART } from '@/graphql/mutations'
import { GET_MY_CART } from '@/graphql/queries'
import { useMutation, useQuery } from '@apollo/client'
import { ADD_TO_CART, ADD_TO_FAVORITES, REMOVE_FROM_FAVORITES } from '@/graphql/mutations'
import { GET_MY_CART, GET_MY_FAVORITES } from '@/graphql/queries'
import { toast } from 'sonner'
interface Product {
@ -58,6 +59,11 @@ export function ProductCard({ product }: ProductCardProps) {
const [isImageDialogOpen, setIsImageDialogOpen] = useState(false)
const [quantity, setQuantity] = useState(1)
// Запрос избранного для проверки статуса
const { data: favoritesData } = useQuery(GET_MY_FAVORITES)
const favorites = favoritesData?.myFavorites || []
const isFavorite = favorites.some((fav: Product) => fav.id === product.id)
const [addToCart, { loading: addingToCart }] = useMutation(ADD_TO_CART, {
refetchQueries: [{ query: GET_MY_CART }],
onCompleted: (data) => {
@ -74,6 +80,36 @@ export function ProductCard({ product }: ProductCardProps) {
}
})
const [addToFavorites, { loading: addingToFavorites }] = useMutation(ADD_TO_FAVORITES, {
refetchQueries: [{ query: GET_MY_FAVORITES }],
onCompleted: (data) => {
if (data.addToFavorites.success) {
toast.success(data.addToFavorites.message)
} else {
toast.error(data.addToFavorites.message)
}
},
onError: (error) => {
toast.error('Ошибка при добавлении в избранное')
console.error('Error adding to favorites:', error)
}
})
const [removeFromFavorites, { loading: removingFromFavorites }] = useMutation(REMOVE_FROM_FAVORITES, {
refetchQueries: [{ query: GET_MY_FAVORITES }],
onCompleted: (data) => {
if (data.removeFromFavorites.success) {
toast.success(data.removeFromFavorites.message)
} else {
toast.error(data.removeFromFavorites.message)
}
},
onError: (error) => {
toast.error('Ошибка при удалении из избранного')
console.error('Error removing from favorites:', error)
}
})
const displayPrice = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
@ -96,6 +132,22 @@ export function ProductCard({ product }: ProductCardProps) {
}
}
const handleToggleFavorite = async () => {
try {
if (isFavorite) {
await removeFromFavorites({
variables: { productId: product.id }
})
} else {
await addToFavorites({
variables: { productId: product.id }
})
}
} catch (error) {
console.error('Error toggling favorite:', error)
}
}
const nextImage = () => {
setCurrentImageIndex((prev) => (prev + 1) % images.length)
}
@ -225,24 +277,59 @@ export function ProductCard({ product }: ProductCardProps) {
/>
</div>
{/* Кнопка добавления в заявки */}
<Button
onClick={handleAddToCart}
disabled={addingToCart}
className="w-full h-8 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white border-0 text-xs"
>
<ShoppingCart className="h-3 w-3 mr-1" />
{addingToCart ? 'Добавление...' : 'В заявки'}
</Button>
{/* Кнопки действий */}
<div className="flex items-center space-x-2">
{/* Кнопка добавления в заявки */}
<Button
onClick={handleAddToCart}
disabled={addingToCart}
className="flex-1 h-8 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white border-0 text-xs"
>
<ShoppingCart className="h-3 w-3 mr-1" />
{addingToCart ? 'Добавление...' : 'В заявки'}
</Button>
{/* Кнопка избранного */}
<Button
onClick={handleToggleFavorite}
disabled={addingToFavorites || removingFromFavorites}
variant="outline"
size="sm"
className={`h-8 w-8 p-0 transition-all ${
isFavorite
? 'bg-red-500/20 border-red-500/30 text-red-400 hover:bg-red-500/30'
: 'bg-white/5 border-white/20 text-white/60 hover:bg-red-500/20 hover:border-red-500/30 hover:text-red-400'
}`}
>
<Heart className={`h-4 w-4 ${isFavorite ? 'fill-current' : ''}`} />
</Button>
</div>
</div>
) : (
<Button
disabled
className="w-full h-8 bg-gray-500/20 text-gray-400 border-0 text-xs cursor-not-allowed"
>
<ShoppingCart className="h-3 w-3 mr-1" />
Недоступно
</Button>
<div className="flex items-center space-x-2">
<Button
disabled
className="flex-1 h-8 bg-gray-500/20 text-gray-400 border-0 text-xs cursor-not-allowed"
>
<ShoppingCart className="h-3 w-3 mr-1" />
Недоступно
</Button>
{/* Кнопка избранного (всегда доступна) */}
<Button
onClick={handleToggleFavorite}
disabled={addingToFavorites || removingFromFavorites}
variant="outline"
size="sm"
className={`h-8 w-8 p-0 transition-all ${
isFavorite
? 'bg-red-500/20 border-red-500/30 text-red-400 hover:bg-red-500/30'
: 'bg-white/5 border-white/20 text-white/60 hover:bg-red-500/20 hover:border-red-500/30 hover:text-red-400'
}`}
>
<Heart className={`h-4 w-4 ${isFavorite ? 'fill-current' : ''}`} />
</Button>
</div>
)}
</div>