useTicketsMerge.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { keyBy } from 'lodash-es'
  3. import { ref, markRaw } from 'vue'
  4. import { useRouter } from 'vue-router'
  5. import {
  6. useNotifications,
  7. NotificationTypes,
  8. } from '#shared/components/CommonNotifications/index.ts'
  9. import { useConfirmation } from '#shared/composables/useConfirmation.ts'
  10. import { useTicketMergeMutation } from '#shared/entities/ticket/graphql/mutations/merge.api.ts'
  11. import { AutocompleteSearchTicketDocument } from '#shared/entities/ticket/graphql/queries/autocompleteSearchTicket.api.ts'
  12. import type { TicketById } from '#shared/entities/ticket/types.ts'
  13. import UserError from '#shared/errors/UserError.ts'
  14. import type { AutocompleteSearchTicketEntry } from '#shared/graphql/types.ts'
  15. import { MutationHandler } from '#shared/server/apollo/handler/index.ts'
  16. import { useDialog } from '#mobile/composables/useDialog.ts'
  17. import TicketMergeStatus from '../components/TicketDetailView/TicketMergeStatus.vue'
  18. import type { FormKitNode } from '@formkit/core'
  19. import type { Ref } from 'vue'
  20. export const useTicketsMerge = (
  21. sourceTicket: Ref<TicketById>,
  22. onSuccess?: () => void,
  23. ) => {
  24. const autocompleteRef = ref<{ node: FormKitNode }>()
  25. const ticketsSearchDialog = useDialog({
  26. name: 'tickets-search',
  27. prefetch: true,
  28. component: () =>
  29. import(
  30. '#mobile/components/Form/fields/FieldAutoComplete/FieldAutoCompleteInputDialog.vue'
  31. ),
  32. })
  33. const mergeHandler = new MutationHandler(useTicketMergeMutation({}))
  34. const { notify } = useNotifications()
  35. const router = useRouter()
  36. let localOptions: Record<string, AutocompleteSearchTicketEntry> = {}
  37. const { waitForConfirmation } = useConfirmation()
  38. const mergeTickets = async () => {
  39. const context = autocompleteRef.value?.node.context
  40. if (!context || mergeHandler.loading().value) return false
  41. const targetTicketId = context._value
  42. const targetTicketOption = localOptions[targetTicketId]
  43. if (!targetTicketId || !targetTicketOption) {
  44. notify({
  45. id: 'merge-ticket-error',
  46. type: NotificationTypes.Error,
  47. message: __('Please select a ticket to merge into.'),
  48. })
  49. return false
  50. }
  51. const targetTicket = targetTicketOption.ticket
  52. const confirmed = await waitForConfirmation(
  53. __('Are you sure you want to merge this ticket (#%s) into #%s?'),
  54. {
  55. textPlaceholder: [sourceTicket.value.number, targetTicket.number],
  56. },
  57. )
  58. if (!confirmed) return false
  59. try {
  60. const result = await mergeHandler.send({
  61. sourceTicketId: sourceTicket.value.id,
  62. targetTicketId,
  63. })
  64. if (!result) {
  65. return false
  66. }
  67. context.node.input(undefined)
  68. router.push(`/tickets/${targetTicket.internalId}`)
  69. return true
  70. } catch (errors) {
  71. if (errors instanceof UserError) {
  72. notify({
  73. id: 'merge-ticket-error',
  74. message: errors.generalErrors[0],
  75. type: NotificationTypes.Error,
  76. })
  77. }
  78. }
  79. return false
  80. }
  81. const mergeAndCloseModals = async () => {
  82. const isMerged = await mergeTickets()
  83. if (isMerged) {
  84. ticketsSearchDialog.close()
  85. onSuccess?.()
  86. }
  87. }
  88. const openMergeTicketsDialog = () => {
  89. const context = autocompleteRef.value?.node.context
  90. if (!context) return
  91. Object.assign(context, {
  92. onActionClick: mergeAndCloseModals,
  93. })
  94. ticketsSearchDialog.open({
  95. context,
  96. name: 'tickets-search',
  97. options: [],
  98. optionIconComponent: markRaw(TicketMergeStatus),
  99. noCloseOnSelect: true,
  100. onUpdateOptions(options: AutocompleteSearchTicketEntry[]) {
  101. localOptions = keyBy(options, 'value')
  102. },
  103. onAction() {
  104. mergeAndCloseModals()
  105. },
  106. })
  107. }
  108. return {
  109. gqlQuery: AutocompleteSearchTicketDocument,
  110. autocompleteRef,
  111. openMergeTicketsDialog,
  112. }
  113. }