Оптимизирована производительность 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,4 +1,5 @@
import axios from 'axios'
import { prisma } from '@/lib/prisma'
export interface SmsResponse {
@ -17,13 +18,16 @@ export class SmsService {
private isDevelopment: boolean
constructor() {
this.email = process.env.SMS_AERO_EMAIL!
this.apiKey = process.env.SMS_AERO_API_KEY!
const email = process.env.SMS_AERO_EMAIL
const apiKey = process.env.SMS_AERO_API_KEY
this.isDevelopment = process.env.NODE_ENV === 'development' || process.env.SMS_DEV_MODE === 'true'
if (!this.isDevelopment && (!this.email || !this.apiKey)) {
if (!this.isDevelopment && (!email || !apiKey)) {
throw new Error('SMS Aero credentials not configured')
}
this.email = email || ''
this.apiKey = apiKey || ''
}
private generateSmsCode(): string {
@ -41,42 +45,42 @@ export class SmsService {
private formatPhoneNumber(phone: string): string {
// Убираем все символы кроме цифр
const cleanPhone = phone.replace(/\D/g, '')
// Если номер начинается с 8, заменяем на 7
if (cleanPhone.startsWith('8')) {
return '7' + cleanPhone.slice(1)
}
// Если номер начинается с +7, убираем +
if (cleanPhone.startsWith('7')) {
return cleanPhone
}
// Если номер без кода страны, добавляем 7
if (cleanPhone.length === 10) {
return '7' + cleanPhone
}
return cleanPhone
}
async sendSmsCode(phone: string): Promise<SmsResponse> {
try {
const formattedPhone = this.formatPhoneNumber(phone)
if (!this.validatePhoneNumber(formattedPhone)) {
return {
success: false,
message: 'Неверный формат номера телефона'
message: 'Неверный формат номера телефона',
}
}
const code = this.generateSmsCode()
const expiresAt = new Date(Date.now() + 5 * 60 * 1000) // 5 минут
// Удаляем старые коды для этого номера
await prisma.smsCode.deleteMany({
where: { phone: formattedPhone }
where: { phone: formattedPhone },
})
// Сохраняем код в базе данных
@ -86,72 +90,68 @@ export class SmsService {
phone: formattedPhone,
expiresAt,
attempts: 0,
maxAttempts: 3
}
maxAttempts: 3,
},
})
// В режиме разработки не отправляем SMS
if (this.isDevelopment) {
console.log(`Development mode: SMS code ${code} for phone ${formattedPhone}`)
console.warn(`Development mode: SMS code ${code} for phone ${formattedPhone}`)
return {
success: true,
message: 'SMS код отправлен успешно (режим разработки)'
message: 'SMS код отправлен успешно (режим разработки)',
}
}
// Отправляем SMS через SMS Aero API с HTTP Basic Auth
const response = await axios.get(
`https://gate.smsaero.ru/v2/sms/send`,
{
params: {
number: formattedPhone,
text: `Код подтверждения SferaV: ${code}`,
sign: 'SMS Aero'
},
auth: {
username: this.email,
password: this.apiKey
},
headers: {
'Accept': 'application/json'
}
}
)
const response = await axios.get('https://gate.smsaero.ru/v2/sms/send', {
params: {
number: formattedPhone,
text: `Код подтверждения SferaV: ${code}`,
sign: 'SMS Aero',
},
auth: {
username: this.email,
password: this.apiKey,
},
headers: {
Accept: 'application/json',
},
})
console.log('SMS Aero response:', response.data)
console.warn('SMS Aero response:', response.data)
if (response.data.success) {
return {
success: true,
message: 'SMS код отправлен успешно'
message: 'SMS код отправлен успешно',
}
} else {
console.error('SMS Aero API error:', response.data)
return {
success: false,
message: response.data.message || 'Ошибка при отправке SMS'
message: response.data.message || 'Ошибка при отправке SMS',
}
}
} catch (error: unknown) {
console.error('Error sending SMS:', error)
// Детальная информация об ошибке
if (axios.isAxiosError(error)) {
console.error('Response status:', error.response?.status)
console.error('Response data:', error.response?.data)
if (error.response?.status === 401) {
return {
success: false,
message: 'Ошибка авторизации SMS API. Проверьте настройки.',
}
}
}
} catch (error: unknown) {
console.error('Error sending SMS:', error)
// Детальная информация об ошибке
if (axios.isAxiosError(error)) {
console.error('Response status:', error.response?.status)
console.error('Response data:', error.response?.data)
if (error.response?.status === 401) {
return {
success: false,
message: 'Ошибка авторизации SMS API. Проверьте настройки.'
}
}
}
return {
success: false,
message: 'Ошибка при отправке SMS'
message: 'Ошибка при отправке SMS',
}
}
}
@ -159,11 +159,11 @@ export class SmsService {
async verifySmsCode(phone: string, code: string): Promise<SmsVerificationResponse> {
try {
const formattedPhone = this.formatPhoneNumber(phone)
if (!this.validatePhoneNumber(formattedPhone)) {
return {
success: false,
message: 'Неверный формат номера телефона'
message: 'Неверный формат номера телефона',
}
}
@ -173,18 +173,18 @@ export class SmsService {
phone: formattedPhone,
isUsed: false,
expiresAt: {
gte: new Date()
}
gte: new Date(),
},
},
orderBy: {
createdAt: 'desc'
}
createdAt: 'desc',
},
})
if (!smsCode) {
return {
success: false,
message: 'Код не найден или истек'
message: 'Код не найден или истек',
}
}
@ -193,12 +193,12 @@ export class SmsService {
// Помечаем код как использованный при превышении лимита попыток
await prisma.smsCode.update({
where: { id: smsCode.id },
data: { isUsed: true }
data: { isUsed: true },
})
return {
success: false,
message: 'Превышено количество попыток ввода кода'
message: 'Превышено количество попыток ввода кода',
}
}
@ -207,37 +207,35 @@ export class SmsService {
// Увеличиваем счетчик попыток при неправильном коде
await prisma.smsCode.update({
where: { id: smsCode.id },
data: { attempts: smsCode.attempts + 1 }
data: { attempts: smsCode.attempts + 1 },
})
const remainingAttempts = smsCode.maxAttempts - smsCode.attempts - 1
return {
success: false,
message: remainingAttempts > 0
? `Неверный код. Осталось попыток: ${remainingAttempts}`
: 'Неверный код. Превышено количество попыток'
message:
remainingAttempts > 0
? `Неверный код. Осталось попыток: ${remainingAttempts}`
: 'Неверный код. Превышено количество попыток',
}
}
// Код правильный - помечаем как использованный
await prisma.smsCode.update({
where: { id: smsCode.id },
data: { isUsed: true }
data: { isUsed: true },
})
return {
success: true,
message: 'Код подтвержден успешно'
message: 'Код подтвержден успешно',
}
} catch (error) {
console.error('Error verifying SMS code:', error)
return {
success: false,
message: 'Ошибка при проверке кода'
message: 'Ошибка при проверке кода',
}
}
}
}
}