TicketSidebarCustomerContent.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed, type ComputedRef } from 'vue'
  4. import CommonUserAvatar from '#shared/components/CommonUserAvatar/CommonUserAvatar.vue'
  5. import ObjectAttributes from '#shared/components/ObjectAttributes/ObjectAttributes.vue'
  6. import type { ObjectAttribute } from '#shared/entities/object-attributes/types/store.ts'
  7. import { useTicketView } from '#shared/entities/ticket/composables/useTicketView.ts'
  8. import type { Organization, UserQuery } from '#shared/graphql/types.ts'
  9. import type { ObjectLike } from '#shared/types/utils.ts'
  10. import { normalizeEdges } from '#shared/utils/helpers.ts'
  11. import { useFlyout } from '#desktop/components/CommonFlyout/useFlyout.ts'
  12. import type { MenuItem } from '#desktop/components/CommonPopoverMenu/types.ts'
  13. import CommonSectionCollapse from '#desktop/components/CommonSectionCollapse/CommonSectionCollapse.vue'
  14. import CommonSimpleEntityList from '#desktop/components/CommonSimpleEntityList/CommonSimpleEntityList.vue'
  15. import { EntityType } from '#desktop/components/CommonSimpleEntityList/types.ts'
  16. import NavigationMenuList from '#desktop/components/NavigationMenu/NavigationMenuList.vue'
  17. import { NavigationMenuDensity } from '#desktop/components/NavigationMenu/types.ts'
  18. import type { TicketInformation } from '#desktop/entities/ticket/types.ts'
  19. import { useTicketInformation } from '#desktop/pages/ticket/composables/useTicketInformation.ts'
  20. import {
  21. type TicketSidebarContentProps,
  22. TicketSidebarScreenType,
  23. } from '#desktop/pages/ticket/types/sidebar.ts'
  24. import TicketSidebarContent from '../TicketSidebarContent.vue'
  25. interface Props extends TicketSidebarContentProps {
  26. customer: UserQuery['user']
  27. secondaryOrganizations: ReturnType<
  28. typeof normalizeEdges<Partial<Organization>>
  29. >
  30. objectAttributes: ObjectAttribute[]
  31. }
  32. const props = defineProps<Props>()
  33. const persistentStates = defineModel<ObjectLike>({ required: true })
  34. defineEmits<{
  35. 'load-more-secondary-organizations': []
  36. }>()
  37. const CUSTOMER_FLYOUT_KEY = 'ticket-change-customer'
  38. const { open: openChangeCustomerFlyout } = useFlyout({
  39. name: CUSTOMER_FLYOUT_KEY,
  40. component: () =>
  41. import(
  42. '#desktop/pages/ticket/components/TicketDetailView/actions/TicketChangeCustomer/TicketChangeCustomerFlyout.vue'
  43. ),
  44. })
  45. let ticket: TicketInformation['ticket']
  46. let isTicketAgent: ComputedRef<boolean>
  47. let isTicketEditable: ComputedRef<boolean>
  48. // :TODO find a way to provide the ticket via prop
  49. if (props.context.screenType === TicketSidebarScreenType.TicketDetailView) {
  50. ;({ ticket } = useTicketInformation())
  51. ;({ isTicketAgent, isTicketEditable } = useTicketView(ticket))
  52. }
  53. const actions = computed<MenuItem[]>(() => [
  54. {
  55. key: CUSTOMER_FLYOUT_KEY,
  56. label: __('Change customer'),
  57. icon: 'person',
  58. show: () => ticket && isTicketAgent.value && isTicketEditable.value,
  59. onClick: () =>
  60. openChangeCustomerFlyout({
  61. ticket,
  62. }),
  63. },
  64. ])
  65. </script>
  66. <template>
  67. <TicketSidebarContent
  68. v-model="persistentStates.scrollPosition"
  69. :title="sidebarPlugin.title"
  70. :icon="sidebarPlugin.icon"
  71. :entity="customer"
  72. :actions="actions"
  73. >
  74. <div class="flex gap-2">
  75. <CommonUserAvatar v-if="customer" :entity="customer" size="normal" />
  76. <div class="flex flex-col justify-center gap-px">
  77. <CommonLabel size="large" class="text-gray-300 dark:text-neutral-400">
  78. {{ customer?.fullname }}
  79. </CommonLabel>
  80. <CommonLink
  81. v-if="customer?.organization"
  82. :link="`/organizations/${customer.organization?.internalId}`"
  83. class="text-sm leading-snug"
  84. >
  85. {{ customer?.organization.name }}
  86. </CommonLink>
  87. </div>
  88. </div>
  89. <ObjectAttributes
  90. :attributes="objectAttributes"
  91. :object="customer"
  92. :skip-attributes="['firstname', 'lastname']"
  93. />
  94. <CommonSimpleEntityList
  95. v-if="secondaryOrganizations.totalCount"
  96. id="customer-secondary-organizations"
  97. v-model="persistentStates.collapseOrganizations"
  98. :type="EntityType.Organization"
  99. :label="__('Secondary organizations')"
  100. :entity="secondaryOrganizations"
  101. @load-more="$emit('load-more-secondary-organizations')"
  102. />
  103. <CommonSectionCollapse
  104. id="customer-tickets"
  105. v-model="persistentStates.collapseTickets"
  106. :title="__('Tickets')"
  107. >
  108. <NavigationMenuList
  109. class="mt-1"
  110. :density="NavigationMenuDensity.Dense"
  111. :items="[
  112. {
  113. label: __('open tickets'),
  114. icon: 'check-circle-no',
  115. iconColor: 'fill-yellow-500',
  116. count: customer?.ticketsCount?.open || 0,
  117. route: '/search/ticket/open',
  118. },
  119. {
  120. label: __('closed tickets'),
  121. icon: 'check-circle-outline',
  122. iconColor: 'fill-green-400',
  123. count: customer?.ticketsCount?.closed || 0,
  124. route: '/search/ticket/closed',
  125. },
  126. ]"
  127. />
  128. </CommonSectionCollapse>
  129. </TicketSidebarContent>
  130. </template>