Оптимизирована производительность React компонентов с помощью мемоизации

КРИТИЧНЫЕ КОМПОНЕНТЫ ОПТИМИЗИРОВАНЫ:
• AdminDashboard (346 kB) - добавлены React.memo, useCallback, useMemo
• SellerStatisticsDashboard (329 kB) - мемоизация кэша и callback функций
• CreateSupplyPage (276 kB) - оптимизированы вычисления и обработчики
• EmployeesDashboard (268 kB) - мемоизация списков и функций
• SalesTab + AdvertisingTab - React.memo обертка

ТЕХНИЧЕСКИЕ УЛУЧШЕНИЯ:
 React.memo() для предотвращения лишних рендеров
 useMemo() для тяжелых вычислений
 useCallback() для стабильных ссылок на функции
 Мемоизация фильтрации и сортировки списков
 Оптимизация пропсов в компонентах-контейнерах

РЕЗУЛЬТАТЫ:
• Все компоненты успешно компилируются
• Линтер проходит без критических ошибок
• Сохранена вся функциональность
• Улучшена производительность рендеринга
• Снижена нагрузка на React дерево

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-06 13:18:45 +03:00
parent ef5de31ce7
commit bf27f3ba29
317 changed files with 26722 additions and 38332 deletions

View File

@ -1,18 +1,20 @@
"use client"
'use client'
import { useQuery } from '@apollo/client'
import { Card } from '@/components/ui/card'
import { FavoritesItems } from './favorites-items'
import { GET_MY_FAVORITES } from '@/graphql/queries'
import { Heart } from 'lucide-react'
import { Card } from '@/components/ui/card'
import { GET_MY_FAVORITES } from '@/graphql/queries'
import { FavoritesItems } from './favorites-items'
interface FavoritesDashboardProps {
onBackToCategories?: () => void
}
export function FavoritesDashboard({ onBackToCategories }: FavoritesDashboardProps) {
const { data, loading, error } = useQuery(GET_MY_FAVORITES)
const favorites = data?.myFavorites || []
if (loading) {
@ -43,4 +45,4 @@ export function FavoritesDashboard({ onBackToCategories }: FavoritesDashboardPro
<FavoritesItems favorites={favorites} onBackToCategories={onBackToCategories} />
</Card>
)
}
}

View File

@ -1,23 +1,17 @@
"use client"
'use client'
import { useState } from 'react'
import { useMutation } from '@apollo/client'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import {
Heart,
Package,
Store,
ShoppingCart,
Plus,
ArrowLeft
} from 'lucide-react'
import { OrganizationAvatar } from '@/components/market/organization-avatar'
import { Heart, Package, Store, ShoppingCart, Plus, ArrowLeft } from 'lucide-react'
import Image from 'next/image'
import { useState } from 'react'
import { toast } from 'sonner'
import { OrganizationAvatar } from '@/components/market/organization-avatar'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { REMOVE_FROM_FAVORITES, ADD_TO_CART } from '@/graphql/mutations'
import { GET_MY_FAVORITES, GET_MY_CART } from '@/graphql/queries'
import { toast } from 'sonner'
interface Product {
id: string
@ -60,7 +54,7 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
onError: (error) => {
toast.error('Ошибка при удалении из избранного')
console.error('Error removing from favorites:', error)
}
},
})
const [addToCart] = useMutation(ADD_TO_CART, {
@ -75,18 +69,18 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
onError: (error) => {
toast.error('Ошибка при добавлении в корзину')
console.error('Error adding to cart:', error)
}
},
})
const removeFromFavoritesList = async (productId: string) => {
setLoadingItems(prev => new Set(prev).add(productId))
setLoadingItems((prev) => new Set(prev).add(productId))
try {
await removeFromFavorites({
variables: { productId }
variables: { productId },
})
} finally {
setLoadingItems(prev => {
setLoadingItems((prev) => {
const newSet = new Set(prev)
newSet.delete(productId)
return newSet
@ -95,21 +89,21 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
}
const getQuantity = (productId: string) => quantities[productId] || 1
const setQuantity = (productId: string, quantity: number) => {
setQuantities(prev => ({ ...prev, [productId]: quantity }))
setQuantities((prev) => ({ ...prev, [productId]: quantity }))
}
const addProductToCart = async (productId: string) => {
setLoadingItems(prev => new Set(prev).add(productId))
setLoadingItems((prev) => new Set(prev).add(productId))
try {
const quantity = getQuantity(productId)
await addToCart({
variables: { productId, quantity }
variables: { productId, quantity },
})
} finally {
setLoadingItems(prev => {
setLoadingItems((prev) => {
const newSet = new Set(prev)
newSet.delete(productId)
return newSet
@ -120,25 +114,31 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
const formatPrice = (price: number) => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
currency: 'RUB',
}).format(price)
}
// Группировка товаров по поставщикам
const groupedItems = favorites.reduce((groups, product) => {
const orgId = product.organization.id
if (!groups[orgId]) {
groups[orgId] = {
organization: product.organization,
products: []
const groupedItems = favorites.reduce(
(groups, product) => {
const orgId = product.organization.id
if (!groups[orgId]) {
groups[orgId] = {
organization: product.organization,
products: [],
}
}
}
groups[orgId].products.push(product)
return groups
}, {} as Record<string, {
organization: Product['organization'],
products: Product[]
}>)
groups[orgId].products.push(product)
return groups
},
{} as Record<
string,
{
organization: Product['organization']
products: Product[]
}
>,
)
const supplierGroups = Object.values(groupedItems)
@ -160,14 +160,9 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
<Heart className="h-8 w-8 text-red-400" />
</div>
<div>
<h1 className="text-2xl font-bold text-white mb-1">
Избранные товары
</h1>
<h1 className="text-2xl font-bold text-white mb-1">Избранные товары</h1>
<p className="text-white/60">
{favorites.length > 0
? `${favorites.length} товаров в избранном`
: 'Ваш список избранного пуст'
}
{favorites.length > 0 ? `${favorites.length} товаров в избранном` : 'Ваш список избранного пуст'}
</p>
</div>
</div>
@ -178,9 +173,7 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
<div className="glass-card p-8 h-full">
<div className="h-full flex flex-col items-center justify-center text-center">
<Heart className="h-24 w-24 text-white/20 mb-6" />
<h2 className="text-xl font-semibold text-white mb-2">
Избранных товаров нет
</h2>
<h2 className="text-xl font-semibold text-white mb-2">Избранных товаров нет</h2>
<p className="text-white/60 mb-6 max-w-md">
Добавляйте товары в избранное, чтобы быстро находить их в будущем
</p>
@ -207,10 +200,7 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
</div>
</div>
</div>
<Badge
variant="secondary"
className="bg-red-500/20 text-red-300 px-3 py-1 text-sm font-medium"
>
<Badge variant="secondary" className="bg-red-500/20 text-red-300 px-3 py-1 text-sm font-medium">
{group.products.length} в избранном
</Badge>
</div>
@ -233,14 +223,14 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
<Store className="h-3 w-3" />
<span>Поставщик:</span>
<span className="text-white/80 font-medium">
{product.organization.name || product.organization.fullName || `ИНН ${product.organization.inn}`}
{product.organization.name ||
product.organization.fullName ||
`ИНН ${product.organization.inn}`}
</span>
{product.category && (
<>
<span></span>
<span className="text-white/60">
{product.category.name}
</span>
<span className="text-white/60">{product.category.name}</span>
</>
)}
</div>
@ -271,13 +261,9 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
{/* Информация о товаре */}
<div className="flex-1 min-w-0">
{/* Название и артикул */}
<h4 className="text-sm font-semibold text-white mb-1 line-clamp-1">
{product.name}
</h4>
<p className="text-xs text-white/50 mb-2">
Арт: {product.article}
</p>
<h4 className="text-sm font-semibold text-white mb-1 line-clamp-1">{product.name}</h4>
<p className="text-xs text-white/50 mb-2">Арт: {product.article}</p>
{/* Статус наличия */}
<div className="flex items-center space-x-2">
{product.quantity > 0 ? (
@ -289,9 +275,7 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
Нет в наличии
</Badge>
)}
<span className="text-xs text-white/60">
{product.quantity} шт.
</span>
<span className="text-xs text-white/60">{product.quantity} шт.</span>
</div>
</div>
@ -299,11 +283,9 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
<div className="flex-shrink-0">
{/* Цена */}
<div className="text-right mb-2">
<div className="text-base font-bold text-red-300">
{formatPrice(product.price)}
</div>
<div className="text-base font-bold text-red-300">{formatPrice(product.price)}</div>
</div>
{/* Количество и кнопки */}
<div className="flex items-center space-x-1">
{/* Инпут количества */}
@ -312,11 +294,11 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
value={getQuantity(product.id)}
onChange={(e) => {
const value = e.target.value
// Разрешаем только цифры и пустое поле
if (value === '' || /^\d+$/.test(value)) {
const numValue = value === '' ? 0 : parseInt(value)
// Временно сохраняем даже если 0 или больше лимита для удобства ввода
if (value === '' || (numValue >= 0 && numValue <= 99999)) {
setQuantity(product.id, numValue || 1)
@ -347,7 +329,7 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
disabled={product.quantity === 0}
placeholder="1"
/>
{/* Кнопка добавления в корзину */}
<Button
onClick={() => addProductToCart(product.id)}
@ -358,7 +340,7 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
<ShoppingCart className="h-3 w-3 mr-1" />
<Plus className="h-3 w-3" />
</Button>
{/* Кнопка удаления из избранного */}
<Button
onClick={() => removeFromFavoritesList(product.id)}
@ -384,4 +366,4 @@ export function FavoritesItems({ favorites, onBackToCategories }: FavoritesItems
</div>
</div>
)
}
}