Ticket.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed, useTemplateRef, toRef, watch } from 'vue'
  4. import { useTicketUpdatesSubscription } from '#shared/entities/ticket/graphql/subscriptions/ticketUpdates.api.ts'
  5. import { EnumTicketStateColorCode, type Ticket } from '#shared/graphql/types.ts'
  6. import SubscriptionHandler from '#shared/server/apollo/handler/SubscriptionHandler.ts'
  7. import { useSessionStore } from '#shared/stores/session.ts'
  8. import CommonTicketStateIndicatorIcon from '#desktop/components/CommonTicketStateIndicatorIcon/CommonTicketStateIndicatorIcon.vue'
  9. import CommonUpdateIndicator from '#desktop/components/CommonUpdateIndicator/CommonUpdateIndicator.vue'
  10. import { useUserCurrentTaskbarTabsStore } from '#desktop/entities/user/current/stores/taskbarTabs.ts'
  11. import { useTicketNumber } from '#desktop/pages/ticket/composables/useTicketNumber.ts'
  12. import type { UserTaskbarTabEntityProps } from '../types.ts'
  13. const props = defineProps<UserTaskbarTabEntityProps<Ticket>>()
  14. const { ticketNumberWithTicketHook } = useTicketNumber(
  15. toRef(props.taskbarTab, 'entity'),
  16. )
  17. const ticketUpdatesSubscription = new SubscriptionHandler(
  18. useTicketUpdatesSubscription({
  19. ticketId: props.taskbarTab.entity!.id,
  20. initial: true,
  21. }),
  22. )
  23. const ticketLink = useTemplateRef('ticket-link')
  24. const isTicketUpdated = computed(() => {
  25. if (ticketLink.value?.isExactActive) return false
  26. return props.taskbarTab.notify
  27. })
  28. const { updateTaskbarTab } = useUserCurrentTaskbarTabsStore()
  29. const updateNotifyFlag = (notify: boolean) => {
  30. if (!props.taskbarTab.taskbarTabId) return
  31. updateTaskbarTab(props.taskbarTab.taskbarTabId, {
  32. ...props.taskbarTab,
  33. notify,
  34. })
  35. }
  36. const { user } = useSessionStore()
  37. // Set the notify flag whenever the result is received from the subscription.
  38. ticketUpdatesSubscription.onSubscribed().then(() => {
  39. ticketUpdatesSubscription.onResult((result) => {
  40. if (
  41. !result.data?.ticketUpdates ||
  42. !props.taskbarTab.entity ||
  43. props.taskbarTab.notify
  44. )
  45. return
  46. const { ticket } = result.data.ticketUpdates
  47. // Skip setting the notify flag if:
  48. // - the ticket entity is in the same state
  49. // - the ticket was updated by the current user
  50. if (
  51. props.taskbarTab.entity.updatedAt === ticket?.updatedAt ||
  52. ticket?.updatedBy?.id === user?.id
  53. )
  54. return
  55. updateNotifyFlag(true)
  56. })
  57. })
  58. watch(
  59. () => ticketLink.value?.isExactActive,
  60. (isExactActive) => {
  61. if (!isExactActive) return
  62. // Reset the notify flag when the tab becomes active.
  63. if (props.taskbarTab.notify) updateNotifyFlag(false)
  64. // Scroll the tab into view when it becomes active.
  65. ticketLink.value?.$el?.scrollIntoView?.()
  66. },
  67. )
  68. const currentState = computed(() => {
  69. return props.taskbarTab.entity?.state?.name || ''
  70. })
  71. const currentTitle = computed(() => {
  72. return props.taskbarTab.entity?.title || ''
  73. })
  74. const currentStateColorCode = computed(() => {
  75. return (
  76. props.taskbarTab.entity?.stateColorCode || EnumTicketStateColorCode.Open
  77. )
  78. })
  79. const activeBackgroundColor = computed(() => {
  80. switch (currentStateColorCode.value) {
  81. case EnumTicketStateColorCode.Closed:
  82. return '!bg-green-400 text-white dark:text-white'
  83. case EnumTicketStateColorCode.Pending:
  84. return '!bg-stone-400 text-white dark:text-white'
  85. case EnumTicketStateColorCode.Escalating:
  86. return '!bg-red-300 text-white dark:text-white'
  87. case EnumTicketStateColorCode.Open:
  88. default:
  89. return '!bg-yellow-500 text-white dark:text-white'
  90. }
  91. })
  92. const currentViewTitle = computed(
  93. () => `${ticketNumberWithTicketHook.value} - ${currentTitle.value}`,
  94. )
  95. </script>
  96. <template>
  97. <CommonLink
  98. v-if="taskbarTabLink"
  99. ref="ticket-link"
  100. v-tooltip="currentViewTitle"
  101. class="flex grow gap-2 rounded-md px-2 py-3 hover:no-underline focus-visible:rounded-md focus-visible:outline-none group-hover/tab:bg-blue-600 group-hover/tab:dark:bg-blue-900"
  102. :link="taskbarTabLink"
  103. :exact-active-class="activeBackgroundColor"
  104. internal
  105. >
  106. <div class="relative">
  107. <CommonUpdateIndicator v-if="isTicketUpdated" />
  108. <CommonTicketStateIndicatorIcon
  109. class="group-focus-visible/link:text-white"
  110. :color-code="currentStateColorCode"
  111. :label="currentState"
  112. icon-size="small"
  113. />
  114. </div>
  115. <CommonLabel
  116. class="-:text-gray-300 -:dark:text-neutral-400 block truncate group-focus-visible/link:text-white group-hover/tab:dark:text-white"
  117. >
  118. {{ currentTitle }}
  119. </CommonLabel>
  120. </CommonLink>
  121. </template>
  122. <style scoped>
  123. a.router-link-active span {
  124. @apply text-white;
  125. }
  126. </style>