Добавлены модели услуг и расходников для фулфилмент центров, реализованы соответствующие мутации и запросы в GraphQL. Обновлен конфигурационный файл и добавлен новый компонент Toaster в макет приложения. Обновлены зависимости в package.json и package-lock.json.
This commit is contained in:
@ -546,4 +546,92 @@ export const MARK_MESSAGES_AS_READ = gql`
|
||||
mutation MarkMessagesAsRead($conversationId: ID!) {
|
||||
markMessagesAsRead(conversationId: $conversationId)
|
||||
}
|
||||
`
|
||||
|
||||
// Мутации для услуг
|
||||
export const CREATE_SERVICE = gql`
|
||||
mutation CreateService($input: ServiceInput!) {
|
||||
createService(input: $input) {
|
||||
success
|
||||
message
|
||||
service {
|
||||
id
|
||||
name
|
||||
description
|
||||
price
|
||||
imageUrl
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const UPDATE_SERVICE = gql`
|
||||
mutation UpdateService($id: ID!, $input: ServiceInput!) {
|
||||
updateService(id: $id, input: $input) {
|
||||
success
|
||||
message
|
||||
service {
|
||||
id
|
||||
name
|
||||
description
|
||||
price
|
||||
imageUrl
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const DELETE_SERVICE = gql`
|
||||
mutation DeleteService($id: ID!) {
|
||||
deleteService(id: $id)
|
||||
}
|
||||
`
|
||||
|
||||
// Мутации для расходников
|
||||
export const CREATE_SUPPLY = gql`
|
||||
mutation CreateSupply($input: SupplyInput!) {
|
||||
createSupply(input: $input) {
|
||||
success
|
||||
message
|
||||
supply {
|
||||
id
|
||||
name
|
||||
description
|
||||
price
|
||||
quantity
|
||||
imageUrl
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const UPDATE_SUPPLY = gql`
|
||||
mutation UpdateSupply($id: ID!, $input: SupplyInput!) {
|
||||
updateSupply(id: $id, input: $input) {
|
||||
success
|
||||
message
|
||||
supply {
|
||||
id
|
||||
name
|
||||
description
|
||||
price
|
||||
quantity
|
||||
imageUrl
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const DELETE_SUPPLY = gql`
|
||||
mutation DeleteSupply($id: ID!) {
|
||||
deleteSupply(id: $id)
|
||||
}
|
||||
`
|
@ -48,6 +48,35 @@ export const GET_ME = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const GET_MY_SERVICES = gql`
|
||||
query GetMyServices {
|
||||
myServices {
|
||||
id
|
||||
name
|
||||
description
|
||||
price
|
||||
imageUrl
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GET_MY_SUPPLIES = gql`
|
||||
query GetMySupplies {
|
||||
mySupplies {
|
||||
id
|
||||
name
|
||||
description
|
||||
price
|
||||
quantity
|
||||
imageUrl
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// Запросы для контрагентов
|
||||
export const SEARCH_ORGANIZATIONS = gql`
|
||||
query SearchOrganizations($type: OrganizationType, $search: String) {
|
||||
|
@ -423,6 +423,64 @@ export const resolvers = {
|
||||
// TODO: Здесь будет логика получения списка чатов
|
||||
// Пока возвращаем пустой массив, так как таблица сообщений еще не создана
|
||||
return []
|
||||
},
|
||||
|
||||
// Мои услуги
|
||||
myServices: async (_: unknown, __: unknown, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что это фулфилмент центр
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Услуги доступны только для фулфилмент центров')
|
||||
}
|
||||
|
||||
return await prisma.service.findMany({
|
||||
where: { organizationId: currentUser.organization.id },
|
||||
include: { organization: true },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
})
|
||||
},
|
||||
|
||||
// Мои расходники
|
||||
mySupplies: async (_: unknown, __: unknown, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что это фулфилмент центр
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Расходники доступны только для фулфилмент центров')
|
||||
}
|
||||
|
||||
return await prisma.supply.findMany({
|
||||
where: { organizationId: currentUser.organization.id },
|
||||
include: { organization: true },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@ -1746,6 +1804,296 @@ export const resolvers = {
|
||||
// TODO: Здесь будет логика обновления статуса сообщений
|
||||
// Пока возвращаем успешный ответ
|
||||
return true
|
||||
},
|
||||
|
||||
// Создать услугу
|
||||
createService: async (_: unknown, args: { input: { name: string; description?: string; price: number; imageUrl?: string } }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что это фулфилмент центр
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Услуги доступны только для фулфилмент центров')
|
||||
}
|
||||
|
||||
try {
|
||||
const service = await prisma.service.create({
|
||||
data: {
|
||||
name: args.input.name,
|
||||
description: args.input.description,
|
||||
price: args.input.price,
|
||||
imageUrl: args.input.imageUrl,
|
||||
organizationId: currentUser.organization.id
|
||||
},
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Услуга успешно создана',
|
||||
service
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating service:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при создании услуги'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Обновить услугу
|
||||
updateService: async (_: unknown, args: { id: string; input: { name: string; description?: string; price: number; imageUrl?: string } }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что услуга принадлежит текущей организации
|
||||
const existingService = await prisma.service.findFirst({
|
||||
where: {
|
||||
id: args.id,
|
||||
organizationId: currentUser.organization.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!existingService) {
|
||||
throw new GraphQLError('Услуга не найдена или нет доступа')
|
||||
}
|
||||
|
||||
try {
|
||||
const service = await prisma.service.update({
|
||||
where: { id: args.id },
|
||||
data: {
|
||||
name: args.input.name,
|
||||
description: args.input.description,
|
||||
price: args.input.price,
|
||||
imageUrl: args.input.imageUrl
|
||||
},
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Услуга успешно обновлена',
|
||||
service
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating service:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при обновлении услуги'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Удалить услугу
|
||||
deleteService: async (_: unknown, args: { id: string }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что услуга принадлежит текущей организации
|
||||
const existingService = await prisma.service.findFirst({
|
||||
where: {
|
||||
id: args.id,
|
||||
organizationId: currentUser.organization.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!existingService) {
|
||||
throw new GraphQLError('Услуга не найдена или нет доступа')
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.service.delete({
|
||||
where: { id: args.id }
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('Error deleting service:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
// Создать расходник
|
||||
createSupply: async (_: unknown, args: { input: { name: string; description?: string; price: number; quantity: number; imageUrl?: string } }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что это фулфилмент центр
|
||||
if (currentUser.organization.type !== 'FULFILLMENT') {
|
||||
throw new GraphQLError('Расходники доступны только для фулфилмент центров')
|
||||
}
|
||||
|
||||
try {
|
||||
const supply = await prisma.supply.create({
|
||||
data: {
|
||||
name: args.input.name,
|
||||
description: args.input.description,
|
||||
price: args.input.price,
|
||||
quantity: args.input.quantity,
|
||||
imageUrl: args.input.imageUrl,
|
||||
organizationId: currentUser.organization.id
|
||||
},
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Расходник успешно создан',
|
||||
supply
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating supply:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при создании расходника'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Обновить расходник
|
||||
updateSupply: async (_: unknown, args: { id: string; input: { name: string; description?: string; price: number; quantity: number; imageUrl?: string } }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что расходник принадлежит текущей организации
|
||||
const existingSupply = await prisma.supply.findFirst({
|
||||
where: {
|
||||
id: args.id,
|
||||
organizationId: currentUser.organization.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!existingSupply) {
|
||||
throw new GraphQLError('Расходник не найден или нет доступа')
|
||||
}
|
||||
|
||||
try {
|
||||
const supply = await prisma.supply.update({
|
||||
where: { id: args.id },
|
||||
data: {
|
||||
name: args.input.name,
|
||||
description: args.input.description,
|
||||
price: args.input.price,
|
||||
quantity: args.input.quantity,
|
||||
imageUrl: args.input.imageUrl
|
||||
},
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Расходник успешно обновлен',
|
||||
supply
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating supply:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при обновлении расходника'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Удалить расходник
|
||||
deleteSupply: async (_: unknown, args: { id: string }, context: Context) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' }
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true }
|
||||
})
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError('У пользователя нет организации')
|
||||
}
|
||||
|
||||
// Проверяем, что расходник принадлежит текущей организации
|
||||
const existingSupply = await prisma.supply.findFirst({
|
||||
where: {
|
||||
id: args.id,
|
||||
organizationId: currentUser.organization.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!existingSupply) {
|
||||
throw new GraphQLError('Расходник не найден или нет доступа')
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.supply.delete({
|
||||
where: { id: args.id }
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('Error deleting supply:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -22,6 +22,12 @@ export const typeDefs = gql`
|
||||
|
||||
# Список чатов (последние сообщения с каждым контрагентом)
|
||||
conversations: [Conversation!]!
|
||||
|
||||
# Услуги организации
|
||||
myServices: [Service!]!
|
||||
|
||||
# Расходники организации
|
||||
mySupplies: [Supply!]!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
@ -61,6 +67,16 @@ export const typeDefs = gql`
|
||||
sendImageMessage(receiverOrganizationId: ID!, fileUrl: String!, fileName: String!, fileSize: Int!, fileType: String!): MessageResponse!
|
||||
sendFileMessage(receiverOrganizationId: ID!, fileUrl: String!, fileName: String!, fileSize: Int!, fileType: String!): MessageResponse!
|
||||
markMessagesAsRead(conversationId: ID!): Boolean!
|
||||
|
||||
# Работа с услугами
|
||||
createService(input: ServiceInput!): ServiceResponse!
|
||||
updateService(id: ID!, input: ServiceInput!): ServiceResponse!
|
||||
deleteService(id: ID!): Boolean!
|
||||
|
||||
# Работа с расходниками
|
||||
createSupply(input: SupplyInput!): SupplyResponse!
|
||||
updateSupply(id: ID!, input: SupplyInput!): SupplyResponse!
|
||||
deleteSupply(id: ID!): Boolean!
|
||||
}
|
||||
|
||||
# Типы данных
|
||||
@ -281,6 +297,58 @@ export const typeDefs = gql`
|
||||
messageData: Message
|
||||
}
|
||||
|
||||
# Типы для услуг
|
||||
type Service {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String
|
||||
price: Float!
|
||||
imageUrl: String
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
organization: Organization!
|
||||
}
|
||||
|
||||
input ServiceInput {
|
||||
name: String!
|
||||
description: String
|
||||
price: Float!
|
||||
imageUrl: String
|
||||
}
|
||||
|
||||
type ServiceResponse {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
service: Service
|
||||
}
|
||||
|
||||
# Типы для расходников
|
||||
type Supply {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String
|
||||
price: Float!
|
||||
quantity: Int!
|
||||
imageUrl: String
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
organization: Organization!
|
||||
}
|
||||
|
||||
input SupplyInput {
|
||||
name: String!
|
||||
description: String
|
||||
price: Float!
|
||||
quantity: Int!
|
||||
imageUrl: String
|
||||
}
|
||||
|
||||
type SupplyResponse {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
supply: Supply
|
||||
}
|
||||
|
||||
# JSON скаляр
|
||||
scalar JSON
|
||||
`
|
Reference in New Issue
Block a user