Обновления системы после анализа и оптимизации архитектуры
- Обновлена схема Prisma с новыми полями и связями - Актуализированы правила системы в rules-complete.md - Оптимизированы GraphQL типы, запросы и мутации - Улучшены компоненты интерфейса и валидация данных - Исправлены критические ESLint ошибки: удалены неиспользуемые импорты и переменные - Добавлены тестовые файлы для проверки функционала 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -31,6 +31,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { UPDATE_USER_PROFILE, UPDATE_ORGANIZATION_BY_INN } from '@/graphql/mutations'
|
||||
import { GET_ME } from '@/graphql/queries'
|
||||
@ -87,6 +88,9 @@ export function UserSettings() {
|
||||
// API ключи маркетплейсов
|
||||
wildberriesApiKey: '',
|
||||
ozonApiKey: '',
|
||||
|
||||
// Рынок для поставщиков
|
||||
market: '',
|
||||
})
|
||||
|
||||
// Загружаем данные организации при монтировании компонента
|
||||
@ -130,10 +134,13 @@ export function UserSettings() {
|
||||
} = {}
|
||||
try {
|
||||
if (org.managementPost && typeof org.managementPost === 'string') {
|
||||
customContacts = JSON.parse(org.managementPost)
|
||||
// Проверяем, что строка начинается с { или [, иначе это не JSON
|
||||
if (org.managementPost.trim().startsWith('{') || org.managementPost.trim().startsWith('[')) {
|
||||
customContacts = JSON.parse(org.managementPost)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Ошибка парсинга managementPost:', e)
|
||||
} catch {
|
||||
// Игнорируем ошибки парсинга
|
||||
}
|
||||
|
||||
setFormData({
|
||||
@ -154,6 +161,7 @@ export function UserSettings() {
|
||||
corrAccount: customContacts?.bankDetails?.corrAccount || '',
|
||||
wildberriesApiKey: '',
|
||||
ozonApiKey: '',
|
||||
market: org.market || 'none',
|
||||
})
|
||||
}
|
||||
}, [user])
|
||||
@ -290,7 +298,6 @@ export function UserSettings() {
|
||||
})
|
||||
|
||||
// TODO: Сохранить партнерский код в базе данных
|
||||
console.warn('Partner code generated:', partnerCode)
|
||||
} catch (error) {
|
||||
console.error('Error generating partner link:', error)
|
||||
setSaveMessage({ type: 'error', text: 'Ошибка при генерации ссылки' })
|
||||
@ -342,7 +349,7 @@ export function UserSettings() {
|
||||
avatar: avatarUrl,
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
update: (cache, { data }: { data?: any }) => {
|
||||
if (data?.updateUserProfile?.success) {
|
||||
// Обновляем кеш Apollo Client
|
||||
try {
|
||||
@ -358,8 +365,8 @@ export function UserSettings() {
|
||||
},
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Cache update error:', error)
|
||||
} catch {
|
||||
// Игнорируем ошибки обновления кеша
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -518,6 +525,7 @@ export function UserSettings() {
|
||||
const handleInputChange = (field: string, value: string) => {
|
||||
let processedValue = value
|
||||
|
||||
|
||||
// Применяем маски и валидации
|
||||
switch (field) {
|
||||
case 'orgPhone':
|
||||
@ -582,6 +590,59 @@ export function UserSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка наличия изменений в форме
|
||||
const hasFormChanges = () => {
|
||||
if (!user?.organization) return false
|
||||
|
||||
const org = user.organization
|
||||
|
||||
// Извлекаем текущий телефон из organization.phones
|
||||
let currentOrgPhone = '+7'
|
||||
if (org.phones && Array.isArray(org.phones) && org.phones.length > 0) {
|
||||
currentOrgPhone = org.phones[0].value || org.phones[0] || '+7'
|
||||
}
|
||||
|
||||
// Извлекаем текущий email из organization.emails
|
||||
let currentEmail = ''
|
||||
if (org.emails && Array.isArray(org.emails) && org.emails.length > 0) {
|
||||
currentEmail = org.emails[0].value || org.emails[0] || ''
|
||||
}
|
||||
|
||||
// Извлекаем дополнительные данные из managementPost
|
||||
let customContacts: any = {}
|
||||
try {
|
||||
if (org.managementPost && typeof org.managementPost === 'string') {
|
||||
// Проверяем, что строка начинается с { или [, иначе это не JSON
|
||||
if (org.managementPost.trim().startsWith('{') || org.managementPost.trim().startsWith('[')) {
|
||||
customContacts = JSON.parse(org.managementPost)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore parse errors
|
||||
}
|
||||
|
||||
// Нормализуем значения для сравнения
|
||||
const normalizeValue = (value: string | null | undefined) => value || ''
|
||||
const normalizeMarketValue = (value: string | null | undefined) => value || 'none'
|
||||
|
||||
// Проверяем изменения в полях
|
||||
const changes = [
|
||||
normalizeValue(formData.orgPhone) !== normalizeValue(currentOrgPhone),
|
||||
normalizeValue(formData.managerName) !== normalizeValue(user?.managerName),
|
||||
normalizeValue(formData.telegram) !== normalizeValue(customContacts?.telegram),
|
||||
normalizeValue(formData.whatsapp) !== normalizeValue(customContacts?.whatsapp),
|
||||
normalizeValue(formData.email) !== normalizeValue(currentEmail),
|
||||
normalizeMarketValue(formData.market) !== normalizeMarketValue(org.market),
|
||||
normalizeValue(formData.bankName) !== normalizeValue(customContacts?.bankDetails?.bankName),
|
||||
normalizeValue(formData.bik) !== normalizeValue(customContacts?.bankDetails?.bik),
|
||||
normalizeValue(formData.accountNumber) !== normalizeValue(customContacts?.bankDetails?.accountNumber),
|
||||
normalizeValue(formData.corrAccount) !== normalizeValue(customContacts?.bankDetails?.corrAccount),
|
||||
]
|
||||
|
||||
const hasChanges = changes.some(changed => changed)
|
||||
return hasChanges
|
||||
}
|
||||
|
||||
// Проверка наличия ошибок валидации
|
||||
const hasValidationErrors = () => {
|
||||
const fields = [
|
||||
@ -658,6 +719,7 @@ export function UserSettings() {
|
||||
bik?: string
|
||||
accountNumber?: string
|
||||
corrAccount?: string
|
||||
market?: string
|
||||
} = {}
|
||||
|
||||
// orgName больше не редактируется - устанавливается только при регистрации
|
||||
@ -670,6 +732,7 @@ export function UserSettings() {
|
||||
if (formData.bik?.trim()) inputData.bik = formData.bik.trim()
|
||||
if (formData.accountNumber?.trim()) inputData.accountNumber = formData.accountNumber.trim()
|
||||
if (formData.corrAccount?.trim()) inputData.corrAccount = formData.corrAccount.trim()
|
||||
if (formData.market) inputData.market = formData.market
|
||||
|
||||
const result = await updateUserProfile({
|
||||
variables: {
|
||||
@ -715,7 +778,6 @@ export function UserSettings() {
|
||||
}
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
console.warn('Invalid date string:', dateString)
|
||||
return 'Неверная дата'
|
||||
}
|
||||
|
||||
@ -724,8 +786,7 @@ export function UserSettings() {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error formatting date:', error, dateString)
|
||||
} catch {
|
||||
return 'Ошибка даты'
|
||||
}
|
||||
}
|
||||
@ -833,9 +894,9 @@ export function UserSettings() {
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
disabled={hasValidationErrors() || isSaving || !hasFormChanges()}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
hasValidationErrors() || isSaving || !hasFormChanges() ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
@ -1070,9 +1131,9 @@ export function UserSettings() {
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
disabled={hasValidationErrors() || isSaving || !hasFormChanges()}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
hasValidationErrors() || isSaving || !hasFormChanges() ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
@ -1256,6 +1317,41 @@ export function UserSettings() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Настройка рынка для поставщиков */}
|
||||
{user?.organization?.type === 'WHOLESALE' && (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label className="text-white/80 text-sm mb-2 flex items-center gap-2">
|
||||
🏪 Физический рынок
|
||||
</Label>
|
||||
{isEditing ? (
|
||||
<Select value={formData.market || 'none'} onValueChange={(value) => handleInputChange('market', value)}>
|
||||
<SelectTrigger className="glass-input text-white h-10 text-sm">
|
||||
<SelectValue placeholder="Выберите рынок" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="glass-card">
|
||||
<SelectItem value="none">Не указан</SelectItem>
|
||||
<SelectItem value="sadovod" className="text-white">Садовод</SelectItem>
|
||||
<SelectItem value="tyak-moscow" className="text-white">ТЯК Москва</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
<Input
|
||||
value={formData.market && formData.market !== 'none' ?
|
||||
(formData.market === 'sadovod' ? 'Садовод' :
|
||||
formData.market === 'tyak-moscow' ? 'ТЯК Москва' :
|
||||
formData.market) : 'Не указан'}
|
||||
readOnly
|
||||
className="glass-input text-white h-10 read-only:opacity-70"
|
||||
/>
|
||||
)}
|
||||
<p className="text-white/50 text-xs mt-1">
|
||||
Физический рынок, где работает поставщик. Товары наследуют рынок от организации.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
@ -1297,7 +1393,7 @@ export function UserSettings() {
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
disabled={hasValidationErrors() || isSaving || !hasFormChanges()}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
@ -1404,7 +1500,7 @@ export function UserSettings() {
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={hasValidationErrors() || isSaving}
|
||||
disabled={hasValidationErrors() || isSaving || !hasFormChanges()}
|
||||
className={`glass-button text-white cursor-pointer ${
|
||||
hasValidationErrors() || isSaving ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
|
Reference in New Issue
Block a user