TicketSidebarSharedDraftStartContent.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { createMessage, getNode } from '@formkit/core'
  4. import { computed, ref } from 'vue'
  5. import { useRoute } from 'vue-router'
  6. import {
  7. NotificationTypes,
  8. useNotifications,
  9. } from '#shared/components/CommonNotifications/index.ts'
  10. import { useTicketSharedDraftStartCreateMutation } from '#shared/entities/ticket-shared-draft-start/graphql/mutations/ticketSharedDraftStartCreate.api.ts'
  11. import { useTicketSharedDraftStartUpdateMutation } from '#shared/entities/ticket-shared-draft-start/graphql/mutations/ticketSharedDraftStartUpdate.api.ts'
  12. import type { TicketSharedDraftStartListQuery } from '#shared/graphql/types.ts'
  13. import { convertToGraphQLId } from '#shared/graphql/utils.ts'
  14. import { MutationHandler } from '#shared/server/apollo/handler/index.ts'
  15. import type { ObjectLike } from '#shared/types/utils.ts'
  16. import { removeSignatureFromBody } from '#shared/utils/dom.ts'
  17. import CommonButton from '#desktop/components/CommonButton/CommonButton.vue'
  18. import { useTicketSharedDraft } from '#desktop/pages/ticket/composables/useTicketSharedDraft.ts'
  19. import type { TicketSidebarContentProps } from '#desktop/pages/ticket/types/sidebar.ts'
  20. import TicketSidebarContent from '../TicketSidebarContent.vue'
  21. interface Props extends TicketSidebarContentProps {
  22. sharedDraftStartList: TicketSharedDraftStartListQuery['ticketSharedDraftStartList']
  23. }
  24. const props = defineProps<Props>()
  25. const persistentStates = defineModel<ObjectLike>({ required: true })
  26. const groupId = computed(() =>
  27. convertToGraphQLId('Group', Number(props.context.formValues.group_id)),
  28. )
  29. const currentSharedDraftId = computed(() =>
  30. convertToGraphQLId(
  31. 'Ticket::SharedDraftStart',
  32. Number(props.context.formValues.shared_draft_id),
  33. ),
  34. )
  35. const sharedDraftTitle = ref('')
  36. const { notify } = useNotifications()
  37. const sharedDraftStartCreateMutation = new MutationHandler(
  38. useTicketSharedDraftStartCreateMutation(),
  39. )
  40. const unsupportedFields = [
  41. 'articleSenderType',
  42. 'attachments',
  43. 'group_id',
  44. 'security',
  45. 'shared_draft_id',
  46. 'ticket_duplicate_detection',
  47. ]
  48. const supportedFields = () =>
  49. Object.fromEntries(
  50. Object.entries(props.context.formValues).filter(
  51. ([field]) => !unsupportedFields.includes(field),
  52. ),
  53. )
  54. const sharedDraftContent = () => ({
  55. ...supportedFields(),
  56. formSenderType: props.context.formValues.articleSenderType, // different key
  57. cc: ((props.context.formValues.cc as string[]) || []).join(', '),
  58. tags: ((props.context.formValues.tags as string[]) || []).join(', '),
  59. body: removeSignatureFromBody(props.context.formValues.body),
  60. })
  61. const route = useRoute()
  62. const sharedDraftTitleNodeId = computed(
  63. () => `sharedDraftTitle-${route.meta.taskbarTabEntityKey}`,
  64. )
  65. const createSharedDraft = async () => {
  66. const sharedDraftTitleNode = getNode(sharedDraftTitleNodeId.value)
  67. if (!sharedDraftTitleNode) return
  68. // Trigger field validation.
  69. sharedDraftTitleNode.store.set(
  70. createMessage({
  71. key: 'submitted',
  72. value: true,
  73. visible: false,
  74. }),
  75. )
  76. // Check if the field passed validation.
  77. if (Object.keys(sharedDraftTitleNode.context?.messages || {}).length) return
  78. sharedDraftStartCreateMutation
  79. .send({
  80. name: sharedDraftTitle.value.trim(),
  81. input: {
  82. formId: props.context.form?.formId as string,
  83. groupId: groupId.value,
  84. content: sharedDraftContent(),
  85. },
  86. })
  87. .then(() => {
  88. sharedDraftTitleNode.reset()
  89. notify({
  90. id: 'shared-draft-created',
  91. type: NotificationTypes.Success,
  92. message: __('Shared draft has been created successfully.'),
  93. })
  94. })
  95. }
  96. const sharedDraftStartUpdateMutation = new MutationHandler(
  97. useTicketSharedDraftStartUpdateMutation(),
  98. )
  99. const updateSharedDraft = () => {
  100. if (!currentSharedDraftId.value) return
  101. sharedDraftStartUpdateMutation
  102. .send({
  103. sharedDraftId: currentSharedDraftId.value,
  104. input: {
  105. formId: props.context.form?.formId as string,
  106. groupId: groupId.value,
  107. content: sharedDraftContent(),
  108. },
  109. })
  110. .then(() => {
  111. notify({
  112. id: 'shared-draft-updated',
  113. type: NotificationTypes.Success,
  114. message: __('Shared draft has been updated successfully.'),
  115. })
  116. })
  117. }
  118. const { openSharedDraftFlyout } = useTicketSharedDraft()
  119. const openFlyout = (sharedDraftStartId: string) => {
  120. openSharedDraftFlyout('start', sharedDraftStartId, props.context.form)
  121. }
  122. </script>
  123. <template>
  124. <TicketSidebarContent
  125. v-model="persistentStates.scrollPosition"
  126. :title="sidebarPlugin.title"
  127. :icon="sidebarPlugin.icon"
  128. >
  129. <FormKit
  130. :id="sharedDraftTitleNodeId"
  131. v-model="sharedDraftTitle"
  132. type="text"
  133. :label="__('Create a shared draft')"
  134. :placeholder="__('Name')"
  135. validation="required:trim"
  136. link="/"
  137. link-icon="plus-square-fill"
  138. :link-label="__('Create Shared Draft')"
  139. @link-click.prevent="createSharedDraft"
  140. @keypress.enter.prevent="createSharedDraft"
  141. />
  142. <div class="py-1">
  143. <div
  144. v-if="sharedDraftStartList?.length"
  145. class="flex flex-col divide-y divide-solid divide-neutral-100 dark:divide-gray-900"
  146. >
  147. <div
  148. v-for="sharedDraftStart in sharedDraftStartList"
  149. :key="sharedDraftStart.id"
  150. class="flex items-center gap-1.5 py-2.5"
  151. >
  152. <div class="flex grow flex-col">
  153. <CommonLink
  154. v-tooltip="sharedDraftStart.name"
  155. link="#"
  156. class="line-clamp-1"
  157. :aria-label="$t('Preview Shared Draft')"
  158. @click.prevent="openFlyout(sharedDraftStart.id)"
  159. >{{ sharedDraftStart.name }}</CommonLink
  160. >
  161. <CommonLabel
  162. class="line-clamp-1 text-stone-200 dark:text-neutral-500"
  163. size="small"
  164. >
  165. <CommonDateTime :date-time="sharedDraftStart.updatedAt" />
  166. <template v-if="sharedDraftStart.updatedBy">
  167. <span v-tooltip="sharedDraftStart.updatedBy.fullname">
  168. &bull; {{ sharedDraftStart.updatedBy.fullname }}
  169. </span>
  170. </template>
  171. </CommonLabel>
  172. </div>
  173. <CommonButton
  174. v-if="currentSharedDraftId === sharedDraftStart.id"
  175. v-tooltip="__('Update Shared Draft')"
  176. variant="submit"
  177. size="small"
  178. icon="arrow-repeat"
  179. @click="updateSharedDraft"
  180. />
  181. </div>
  182. </div>
  183. <CommonLabel
  184. v-else
  185. class="text-stone-200 dark:text-neutral-500"
  186. size="small"
  187. >
  188. {{ $t('No shared drafts yet') }}
  189. </CommonLabel>
  190. </div>
  191. </TicketSidebarContent>
  192. </template>