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

This commit is contained in:
Bivekich
2025-07-28 09:50:09 +03:00
parent e56d4e309d
commit 5bd09fbca2
26 changed files with 56668 additions and 17 deletions

View File

@ -135,7 +135,7 @@ export function UserSettings() {
orgPhone: orgPhone || '+7',
managerName: user?.managerName || '',
telegram: customContacts?.telegram || '',
whatsapp: customContacts?.whatsapp || '+7',
whatsapp: customContacts?.whatsapp || '',
email: email,
orgName: org.name || '',
address: org.address || '',
@ -374,12 +374,17 @@ export function UserSettings() {
return emailRegex.test(email)
}
const formatPhoneInput = (value: string) => {
const formatPhoneInput = (value: string, isOptional: boolean = false) => {
// Убираем все нецифровые символы
const digitsOnly = value.replace(/\D/g, '')
// Если строка пустая, возвращаем +7
if (!digitsOnly) return '+7'
// Если строка пустая
if (!digitsOnly) {
// Для необязательных полей возвращаем пустую строку
if (isOptional) return ''
// Для обязательных полей возвращаем +7
return '+7'
}
// Если пользователь ввел первую цифру не 7, добавляем 7 перед ней
let cleaned = digitsOnly
@ -391,7 +396,7 @@ export function UserSettings() {
cleaned = cleaned.slice(0, 11)
// Форматируем в зависимости от длины
if (cleaned.length <= 1) return '+7'
if (cleaned.length <= 1) return isOptional && cleaned === '7' ? '' : '+7'
if (cleaned.length <= 4) return `+7 (${cleaned.slice(1)}`
if (cleaned.length <= 7) return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4)}`
if (cleaned.length <= 9) return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7)}`
@ -400,17 +405,24 @@ export function UserSettings() {
return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7, 9)}-${cleaned.slice(9, 11)}`
}
const handlePhoneInputChange = (field: string, value: string, inputRef?: React.RefObject<HTMLInputElement>) => {
const handlePhoneInputChange = (field: string, value: string, inputRef?: React.RefObject<HTMLInputElement>, isOptional: boolean = false) => {
const currentInput = inputRef?.current
const currentCursorPosition = currentInput?.selectionStart || 0
const currentValue = formData[field as keyof typeof formData] as string || ''
// Если пользователь пытается удалить +7, предотвращаем это
if (value.length < 2) {
// Для необязательных полей разрешаем пустое значение
if (isOptional && value.length < 2) {
const formatted = formatPhoneInput(value, true)
setFormData(prev => ({ ...prev, [field]: formatted }))
return
}
// Для обязательных полей если пользователь пытается удалить +7, предотвращаем это
if (!isOptional && value.length < 2) {
value = '+7'
}
const formatted = formatPhoneInput(value)
const formatted = formatPhoneInput(value, isOptional)
setFormData(prev => ({ ...prev, [field]: formatted }))
// Вычисляем новую позицию курсора
@ -905,15 +917,12 @@ export function UserSettings() {
<Input
ref={whatsappInputRef}
value={formData.whatsapp || ''}
onChange={(e) => handlePhoneInputChange('whatsapp', e.target.value, whatsappInputRef)}
onChange={(e) => handlePhoneInputChange('whatsapp', e.target.value, whatsappInputRef, true)}
onKeyDown={(e) => {
// Предотвращаем удаление +7
if ((e.key === 'Backspace' || e.key === 'Delete') &&
whatsappInputRef.current?.selectionStart <= 2) {
e.preventDefault()
}
// Для WhatsApp разрешаем полное удаление (поле необязательное)
// Никаких ограничений на удаление
}}
placeholder="+7 (999) 999-99-99"
placeholder="Необязательно"
readOnly={!isEditing}
className={`glass-input text-white placeholder:text-white/40 h-10 read-only:opacity-70 ${
getFieldError('whatsapp', formData.whatsapp) ? 'border-red-400' : ''

View File

@ -5046,6 +5046,139 @@ export const resolvers = {
},
};
// Мутации для категорий
const categoriesMutations = {
// Создать категорию
createCategory: async (
_: unknown,
args: { input: { name: string } }
) => {
try {
// Проверяем есть ли уже категория с таким именем
const existingCategory = await prisma.category.findUnique({
where: { name: args.input.name }
});
if (existingCategory) {
return {
success: false,
message: 'Категория с таким названием уже существует'
};
}
const category = await prisma.category.create({
data: {
name: args.input.name
}
});
return {
success: true,
message: 'Категория успешно создана',
category
};
} catch (error) {
console.error('Ошибка создания категории:', error);
return {
success: false,
message: 'Ошибка при создании категории'
};
}
},
// Обновить категорию
updateCategory: async (
_: unknown,
args: { id: string, input: { name: string } }
) => {
try {
// Проверяем существует ли категория
const existingCategory = await prisma.category.findUnique({
where: { id: args.id }
});
if (!existingCategory) {
return {
success: false,
message: 'Категория не найдена'
};
}
// Проверяем не занято ли имя другой категорией
const duplicateCategory = await prisma.category.findFirst({
where: {
name: args.input.name,
id: { not: args.id }
}
});
if (duplicateCategory) {
return {
success: false,
message: 'Категория с таким названием уже существует'
};
}
const category = await prisma.category.update({
where: { id: args.id },
data: {
name: args.input.name
}
});
return {
success: true,
message: 'Категория успешно обновлена',
category
};
} catch (error) {
console.error('Ошибка обновления категории:', error);
return {
success: false,
message: 'Ошибка при обновлении категории'
};
}
},
// Удалить категорию
deleteCategory: async (
_: unknown,
args: { id: string }
) => {
try {
// Проверяем существует ли категория
const existingCategory = await prisma.category.findUnique({
where: { id: args.id }
});
if (!existingCategory) {
throw new GraphQLError('Категория не найдена');
}
// Проверяем есть ли товары в этой категории
const productsCount = await prisma.product.count({
where: { categoryId: args.id }
});
if (productsCount > 0) {
throw new GraphQLError('Нельзя удалить категорию, в которой есть товары');
}
await prisma.category.delete({
where: { id: args.id }
});
return true;
} catch (error) {
console.error('Ошибка удаления категории:', error);
if (error instanceof GraphQLError) {
throw error;
}
throw new GraphQLError('Ошибка при удалении категории');
}
}
};
// Логистические мутации
const logisticsMutations = {
// Создать логистический маршрут
@ -5228,9 +5361,10 @@ const logisticsMutations = {
},
};
// Добавляем логистические мутации к основным резолверам
// Добавляем дополнительные мутации к основным резолверам
resolvers.Mutation = {
...resolvers.Mutation,
...categoriesMutations,
...logisticsMutations,
};