CommonUserAvatar.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. <!-- Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { computed } from 'vue'
  4. import { useApplicationStore } from '@shared/stores/application'
  5. import { getInitials } from '@shared/utils/formatter'
  6. import { i18n } from '@shared/i18n'
  7. import CommonAvatar from '../CommonAvatar/CommonAvatar.vue'
  8. import type { AvatarSize } from '../CommonAvatar'
  9. import type { AvatarUser } from './types'
  10. import logo from './assets/logo.svg'
  11. export interface Props {
  12. entity: AvatarUser
  13. size?: AvatarSize
  14. personal?: boolean
  15. decorative?: boolean
  16. }
  17. const props = defineProps<Props>()
  18. const initials = computed(() => {
  19. const { lastname, firstname, email } = props.entity
  20. return getInitials(firstname, lastname, email)
  21. })
  22. const colors = [
  23. 'bg-gray',
  24. 'bg-red-bright',
  25. 'bg-yellow',
  26. 'bg-blue',
  27. 'bg-green',
  28. 'bg-pink',
  29. 'bg-orange',
  30. ]
  31. const fullName = computed(() => {
  32. const { lastname, firstname } = props.entity
  33. return [firstname, lastname].filter(Boolean).join(' ')
  34. })
  35. const colorClass = computed(() => {
  36. const { email, id } = props.entity
  37. // TODO ID is mangled by gql, maybe backend should send "isSystem"-like property?
  38. if (id === '1') return 'bg-white'
  39. // TODO it's better to use ID, so if someone changes name the color won't change
  40. const name = [fullName.value, email].filter(Boolean).join('')
  41. if (!name || name === ' ' || name === '-') return colors[0]
  42. // get color based on mod of the fullname length
  43. // so it stays consistent between different interfaces and logins
  44. return colors[name.length % 5]
  45. })
  46. const sources = ['facebook', 'twitter']
  47. const icon = computed(() => {
  48. const { source } = props.entity
  49. if (source && sources.includes(source)) return `mobile-${source}`
  50. return null
  51. })
  52. const application = useApplicationStore()
  53. const image = computed(() => {
  54. if (icon.value) return null
  55. if (props.entity.id === '1') return logo
  56. if (!props.entity.image) return null
  57. // Support the inline data URI as an image source.
  58. if (props.entity.image.startsWith('data:')) return props.entity.image
  59. // we're using the REST api here to get the image and to also use the browser image cache
  60. // TODO: this should be re-evaluated when the desktop app is going to be implemented
  61. const apiUrl = String(application.config.api_path)
  62. return `${apiUrl}/users/image/${props.entity.image}`
  63. })
  64. const isVip = computed(() => {
  65. return !props.personal && props.entity.vip
  66. })
  67. const className = computed(() => {
  68. const classes = [colorClass.value]
  69. if (props.entity.outOfOffice) {
  70. classes.push('opacity-100 grayscale-[70%]')
  71. } else if (props.entity.active === false) {
  72. classes.push('opacity-20 grayscale')
  73. }
  74. return classes
  75. })
  76. const label = computed(() => {
  77. let label = i18n.t('Avatar')
  78. const name = fullName.value || props.entity.email
  79. if (name) label += ` (${name})`
  80. if (isVip.value) label += ` (${i18n.t('VIP')})`
  81. return label
  82. })
  83. </script>
  84. <template>
  85. <CommonAvatar
  86. :initials="initials"
  87. :size="size"
  88. :icon="icon"
  89. :class="className"
  90. :image="image"
  91. :vip="isVip"
  92. :decorative="decorative"
  93. :aria-label="label"
  94. />
  95. </template>