useTicketCreate.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { NotificationTypes } from '#shared/components/CommonNotifications/types.ts'
  3. import { useNotifications } from '#shared/components/CommonNotifications/useNotifications.ts'
  4. import { populateEditorNewLines } from '#shared/components/Form/fields/FieldEditor/utils.ts'
  5. import type { FormRef, FormSubmitData } from '#shared/components/Form/types.ts'
  6. import { setErrors } from '#shared/components/Form/utils.ts'
  7. import { useCheckBodyAttachmentReference } from '#shared/composables/form/useCheckBodyAttachmentReference.ts'
  8. import { useObjectAttributeFormData } from '#shared/entities/object-attributes/composables/useObjectAttributeFormData.ts'
  9. import { useObjectAttributes } from '#shared/entities/object-attributes/composables/useObjectAttributes.ts'
  10. import { ticketCreateArticleType } from '#shared/entities/ticket/composables/useTicketCreateArticleType.ts'
  11. import { useTicketCreateMutation } from '#shared/entities/ticket/graphql/mutations/create.api.ts'
  12. import UserError from '#shared/errors/UserError.ts'
  13. import {
  14. EnumObjectManagerObjects,
  15. type TicketCreateInput,
  16. } from '#shared/graphql/types.ts'
  17. import { isGraphQLId, convertToGraphQLId } from '#shared/graphql/utils.ts'
  18. import MutationHandler from '#shared/server/apollo/handler/MutationHandler.ts'
  19. import { GraphQLErrorTypes } from '#shared/types/error.ts'
  20. import { convertFilesToAttachmentInput } from '#shared/utils/files.ts'
  21. import { useTicketCreateView } from './useTicketCreateView.ts'
  22. import type { TicketFormData } from '../types.ts'
  23. import type { ApolloError } from '@apollo/client/core'
  24. import type { Ref } from 'vue'
  25. export const useTicketCreate = (
  26. form: Ref<FormRef | undefined>,
  27. redirectAfterCreate: (internalId?: number) => void,
  28. ) => {
  29. const { isTicketCustomer } = useTicketCreateView()
  30. const { notify } = useNotifications()
  31. const notifySuccess = () => {
  32. notify({
  33. id: 'ticket-create-success',
  34. type: NotificationTypes.Success,
  35. message: __('Ticket has been created successfully.'),
  36. })
  37. }
  38. const handleTicketCreateError = (error: UserError | ApolloError) => {
  39. if ('graphQLErrors' in error) {
  40. const graphQLErrors = error.graphQLErrors?.[0]
  41. // Treat this as successful, because it happens when you create a ticket inside a group, where you only
  42. // have create permission, but not view permission.
  43. if (graphQLErrors?.extensions?.type === GraphQLErrorTypes.Forbidden) {
  44. notifySuccess()
  45. return () => redirectAfterCreate()
  46. }
  47. notify({
  48. id: 'ticket-create-error',
  49. message: __('Ticket could not be created.'),
  50. type: NotificationTypes.Error,
  51. })
  52. } else {
  53. if (error instanceof UserError && form.value?.formNode) {
  54. setErrors(form.value?.formNode, error)
  55. return
  56. }
  57. notify({
  58. id: 'ticket-create-error',
  59. message: error.generalErrors[0],
  60. type: NotificationTypes.Error,
  61. })
  62. }
  63. }
  64. const ticketCreateMutation = new MutationHandler(
  65. useTicketCreateMutation({}),
  66. {
  67. errorShowNotification: false,
  68. },
  69. )
  70. const {
  71. missingBodyAttachmentReference,
  72. bodyAttachmentReferenceConfirmation,
  73. } = useCheckBodyAttachmentReference()
  74. const getCustomerVariable = (customerId: string) => {
  75. return isGraphQLId(customerId) ? { id: customerId } : { email: customerId }
  76. }
  77. const createTicket = async (formData: FormSubmitData<TicketFormData>) => {
  78. // Check for possible missing attached files and ask for confirmation.
  79. // With return false, the form submit is stopped.
  80. if (
  81. missingBodyAttachmentReference(formData.body, formData.attachments) &&
  82. (await bodyAttachmentReferenceConfirmation())
  83. ) {
  84. return false
  85. }
  86. const { attributesLookup: ticketObjectAttributesLookup } =
  87. useObjectAttributes(EnumObjectManagerObjects.Ticket)
  88. const { internalObjectAttributeValues, additionalObjectAttributeValues } =
  89. useObjectAttributeFormData(ticketObjectAttributesLookup.value, formData)
  90. // The customerId has an special handling, so we need to extract it from the internalObjectAttributeValues.
  91. const { customerId, ...internalValues } = internalObjectAttributeValues
  92. let sharedDraftId
  93. if (formData.shared_draft_id) {
  94. sharedDraftId = convertToGraphQLId(
  95. 'Ticket::SharedDraftStart',
  96. formData.shared_draft_id as string | number,
  97. )
  98. }
  99. const input = {
  100. ...internalValues,
  101. sharedDraftId,
  102. customer: customerId
  103. ? getCustomerVariable(customerId as string)
  104. : undefined,
  105. article: {
  106. cc: formData.cc,
  107. body: populateEditorNewLines(formData.body),
  108. sender: isTicketCustomer.value
  109. ? 'Customer'
  110. : ticketCreateArticleType[formData.articleSenderType].sender,
  111. type: isTicketCustomer.value
  112. ? 'web'
  113. : ticketCreateArticleType[formData.articleSenderType].type,
  114. contentType: 'text/html',
  115. security: formData.security,
  116. },
  117. objectAttributeValues: additionalObjectAttributeValues,
  118. } as TicketCreateInput
  119. if (formData.attachments && input.article && form.value?.formId) {
  120. input.article.attachments = convertFilesToAttachmentInput(
  121. form.value.formId,
  122. formData.attachments,
  123. )
  124. }
  125. if (formData.link_ticket_id) {
  126. const linkObjectId = convertToGraphQLId(
  127. 'Ticket',
  128. formData.link_ticket_id as string | number,
  129. )
  130. input.links = [
  131. {
  132. linkObjectId,
  133. linkType: 'child',
  134. },
  135. ]
  136. }
  137. if (formData.externalReferences) {
  138. input.externalReferences = formData.externalReferences
  139. }
  140. return ticketCreateMutation
  141. .send({ input })
  142. .then((result) => {
  143. if (result?.ticketCreate?.ticket) {
  144. notifySuccess()
  145. return () => {
  146. const ticket = result.ticketCreate?.ticket
  147. redirectAfterCreate(
  148. ticket?.policy.update ? ticket.internalId : undefined,
  149. )
  150. }
  151. }
  152. return null
  153. })
  154. .catch(handleTicketCreateError)
  155. }
  156. return {
  157. createTicket,
  158. isTicketCustomer,
  159. }
  160. }