TicketSharedDraftFlyout.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed, toRef } from 'vue'
  4. import {
  5. NotificationTypes,
  6. useNotifications,
  7. } from '#shared/components/CommonNotifications/index.ts'
  8. import CommonUserAvatar from '#shared/components/CommonUserAvatar/CommonUserAvatar.vue'
  9. import type { FormRef } from '#shared/components/Form/types.ts'
  10. import { useForm } from '#shared/components/Form/useForm.ts'
  11. import { useConfirmation } from '#shared/composables/useConfirmation.ts'
  12. import type {
  13. TicketSharedDraftStartSingleQuery,
  14. TicketSharedDraftZoomShowQuery,
  15. } from '#shared/graphql/types.ts'
  16. import { convertToGraphQLId } from '#shared/graphql/utils.ts'
  17. import {
  18. MutationHandler,
  19. QueryHandler,
  20. } from '#shared/server/apollo/handler/index.ts'
  21. import type {
  22. OperationMutationFunction,
  23. OperationQueryFunction,
  24. } from '#shared/types/server/apollo/handler'
  25. import CommonButton from '#desktop/components/CommonButton/CommonButton.vue'
  26. import CommonFlyout from '#desktop/components/CommonFlyout/CommonFlyout.vue'
  27. import { closeFlyout } from '#desktop/components/CommonFlyout/useFlyout.ts'
  28. import CommonObjectAttribute from '#desktop/components/CommonObjectAttribute/CommonObjectAttribute.vue'
  29. import CommonObjectAttributeContainer from '#desktop/components/CommonObjectAttribute/CommonObjectAttributeContainer.vue'
  30. const props = defineProps<{
  31. sharedDraftId: string
  32. form: FormRef | undefined
  33. draftType: 'start' | 'detail-view'
  34. metaInformationQuery: OperationQueryFunction
  35. deleteMutation: OperationMutationFunction
  36. }>()
  37. const emit = defineEmits<{
  38. 'shared-draft-applied': [id: string]
  39. 'shared-draft-deleted': [id: string]
  40. }>()
  41. const { metaInformationQuery, deleteMutation } = props
  42. const flyoutName = 'shared-draft'
  43. const metaInformationQueryHandler = new QueryHandler(
  44. metaInformationQuery({
  45. sharedDraftId: props.sharedDraftId,
  46. }),
  47. )
  48. const metaInformationQueryResult = metaInformationQueryHandler.result()
  49. const sharedDraft = computed(() => {
  50. if (props.draftType === 'start') {
  51. return metaInformationQueryResult.value
  52. ?.ticketSharedDraftStartSingle as TicketSharedDraftStartSingleQuery['ticketSharedDraftStartSingle']
  53. }
  54. return metaInformationQueryResult.value
  55. ?.ticketSharedDraftZoomShow as TicketSharedDraftZoomShowQuery['ticketSharedDraftZoomShow']
  56. })
  57. const sharedDraftContent = computed(() => {
  58. if (props.draftType === 'start') {
  59. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  60. const content: any =
  61. sharedDraft.value as TicketSharedDraftStartSingleQuery['ticketSharedDraftStartSingle']
  62. return content.content.body
  63. }
  64. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  65. const newArticle: any =
  66. sharedDraft.value as TicketSharedDraftZoomShowQuery['ticketSharedDraftZoomShow']
  67. return newArticle.newArticle.body
  68. })
  69. const close = () => {
  70. closeFlyout(flyoutName)
  71. }
  72. const { waitForConfirmation, waitForVariantConfirmation } = useConfirmation()
  73. const { notify } = useNotifications()
  74. const sharedDrafteleteMutation = new MutationHandler(deleteMutation({}))
  75. const { isDirty, triggerFormUpdater, updateFieldValues, values } = useForm(
  76. toRef(props, 'form'),
  77. )
  78. const deleteSharedDraft = async (sharedDraftId: string) => {
  79. const confirmed = await waitForVariantConfirmation('delete')
  80. if (!confirmed) return
  81. sharedDrafteleteMutation
  82. .send({
  83. sharedDraftId,
  84. })
  85. .then(() => {
  86. // Reset shared draft internal ID, if currently set to the same value in the form.
  87. if (
  88. (props.draftType === 'start' &&
  89. convertToGraphQLId(
  90. 'Ticket::SharedDraftStart',
  91. Number(values.value.shared_draft_id),
  92. ) === sharedDraftId) ||
  93. (props.draftType === 'detail-view' &&
  94. convertToGraphQLId(
  95. 'Ticket::SharedDraftZoom',
  96. Number(values.value.shared_draft_id),
  97. ) === sharedDraftId)
  98. ) {
  99. updateFieldValues({
  100. shared_draft_id: null,
  101. })
  102. }
  103. notify({
  104. id: 'shared-draft-deleted',
  105. type: NotificationTypes.Success,
  106. message: __('Shared draft has been deleted.'),
  107. })
  108. emit('shared-draft-deleted', sharedDraftId)
  109. close()
  110. })
  111. }
  112. const applySharedDraft = async (sharedDraftId: string) => {
  113. if (isDirty.value) {
  114. const confirmed = await waitForConfirmation(
  115. __('There is existing content. Do you want to overwrite it?'),
  116. {
  117. headerTitle: __('Apply Draft'),
  118. buttonLabel: __('Overwrite Content'),
  119. buttonVariant: 'danger',
  120. },
  121. )
  122. if (!confirmed) return
  123. }
  124. const additionalParams = {
  125. sharedDraftId,
  126. draftType: props.draftType,
  127. }
  128. triggerFormUpdater({ additionalParams })
  129. // NB: Skip notifying the user via toast, since they will immediately see the shared draft applied on screen.
  130. emit('shared-draft-applied', sharedDraftId)
  131. close()
  132. }
  133. const headerTitle = computed(() => {
  134. if (props.draftType === 'start') {
  135. return __('Preview Shared Draft')
  136. }
  137. return __('Apply Shared Draft')
  138. })
  139. </script>
  140. <template>
  141. <CommonFlyout
  142. :header-title="headerTitle"
  143. :footer-action-options="{
  144. actionLabel: __('Apply'),
  145. actionButton: { variant: 'primary' },
  146. }"
  147. header-icon="file-text"
  148. :name="flyoutName"
  149. @activated="metaInformationQueryHandler.refetch()"
  150. >
  151. <div v-if="sharedDraft" class="flex flex-col gap-3">
  152. <!-- TODO: Surely we should also display the name of the shared draft somewhere, no? -->
  153. <!-- <CommonObjectAttributeContainer>
  154. <CommonObjectAttribute :label="__('Name')">
  155. {{ sharedDraft?.name }}
  156. </CommonObjectAttribute>
  157. </CommonObjectAttributeContainer> -->
  158. <div class="flex items-start gap-y-3">
  159. <CommonObjectAttributeContainer
  160. v-if="sharedDraft?.updatedBy"
  161. class="grow"
  162. >
  163. <CommonObjectAttribute :label="__('Author')">
  164. <div class="flex items-center gap-1.5">
  165. <CommonUserAvatar :entity="sharedDraft?.updatedBy" size="small" />
  166. <CommonLabel>{{ sharedDraft.updatedBy.fullname }}</CommonLabel>
  167. </div>
  168. </CommonObjectAttribute>
  169. </CommonObjectAttributeContainer>
  170. <CommonObjectAttributeContainer class="grow">
  171. <CommonObjectAttribute :label="__('Last changed')">
  172. <CommonDateTime :date-time="sharedDraft?.updatedAt" />
  173. </CommonObjectAttribute>
  174. </CommonObjectAttributeContainer>
  175. </div>
  176. <!--
  177. TODO: Think about showing more attributes here, since the body might not be present at all in the draft.
  178. But keep in mind this might not be easily possible, since we are missing some information from the query.
  179. For example, we have only `owner_id`/`state_id`/`priority_id`, what about lookup objects?!
  180. -->
  181. <CommonObjectAttributeContainer v-if="sharedDraftContent">
  182. <CommonObjectAttribute :label="__('Text')">
  183. <!-- eslint-disable vue/no-v-html -->
  184. <span v-html="sharedDraftContent" />
  185. </CommonObjectAttribute>
  186. </CommonObjectAttributeContainer>
  187. </div>
  188. <template #footer>
  189. <div class="flex items-center justify-end gap-4">
  190. <CommonButton size="large" variant="secondary" @click="close">
  191. {{ $t('Cancel & Go Back') }}
  192. </CommonButton>
  193. <CommonButton
  194. size="large"
  195. variant="danger"
  196. @click="deleteSharedDraft(sharedDraftId)"
  197. >
  198. {{ $t('Delete') }}
  199. </CommonButton>
  200. <CommonButton
  201. size="large"
  202. variant="primary"
  203. @click="applySharedDraft(sharedDraftId)"
  204. >
  205. {{ $t('Apply') }}
  206. </CommonButton>
  207. </div>
  208. </template>
  209. </CommonFlyout>
  210. </template>