useDisplayObjectAttributes.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { keyBy, get } from 'lodash-es'
  3. import { computed } from 'vue'
  4. import type { ObjectAttribute } from '#shared/entities/object-attributes/types/store.ts'
  5. import type { ObjectAttributeValue } from '#shared/graphql/types.ts'
  6. import { useSessionStore } from '#shared/stores/session.ts'
  7. import type { ObjectLike } from '#shared/types/utils.ts'
  8. import { camelize } from '#shared/utils/formatter.ts'
  9. import type { AttributeDeclaration } from './types.ts'
  10. import type { Dictionary } from 'ts-essentials'
  11. import type { Component } from 'vue'
  12. export interface ObjectAttributesDisplayOptions {
  13. object: ObjectLike
  14. attributes: ObjectAttribute[]
  15. skipAttributes?: string[]
  16. accessors?: Record<string, string>
  17. }
  18. interface AttributeField {
  19. attribute: ObjectAttribute
  20. component: Component
  21. value: unknown
  22. link: Maybe<string>
  23. }
  24. const attributesDeclarations = import.meta.glob<AttributeDeclaration>(
  25. './attributes/Attribute*/index.ts',
  26. { eager: true, import: 'default' },
  27. )
  28. const definitionsByType = Object.values(attributesDeclarations).reduce(
  29. (acc, declaration) => {
  30. declaration.dataTypes.forEach((type) => {
  31. acc[type] = declaration.component
  32. })
  33. return acc
  34. },
  35. {} as Record<string, Component>,
  36. )
  37. export const useDisplayObjectAttributes = (
  38. options: ObjectAttributesDisplayOptions,
  39. ) => {
  40. const attributesObject = computed<Dictionary<ObjectAttributeValue>>(() => {
  41. return keyBy(options.object.objectAttributeValues || {}, 'attribute.name')
  42. })
  43. const getValue = (key: string) => {
  44. const accessor = options.accessors?.[key]
  45. if (accessor) {
  46. return get(options.object, accessor)
  47. }
  48. if (key in attributesObject.value) {
  49. return attributesObject.value[key].value
  50. }
  51. if (key in options.object) {
  52. return options.object[key]
  53. }
  54. return options.object[camelize(key)]
  55. }
  56. const isEmpty = (value: unknown) => {
  57. if (Array.isArray(value)) {
  58. return value.length === 0
  59. }
  60. // null or undefined or ''
  61. return value == null || value === ''
  62. }
  63. const getLink = (name: string) => {
  64. const attribute = attributesObject.value[name]
  65. return attribute?.renderedLink || null
  66. }
  67. const session = useSessionStore()
  68. const fields = computed<AttributeField[]>(() => {
  69. return options.attributes
  70. .filter((attribute) => !attribute.isStatic)
  71. .map((attribute) => {
  72. let value = getValue(attribute.name)
  73. if (typeof value !== 'boolean' && !value) {
  74. value = attribute.dataOption?.default
  75. }
  76. return {
  77. attribute,
  78. component: definitionsByType[attribute.dataType],
  79. value,
  80. link: getLink(attribute.name),
  81. }
  82. })
  83. .filter(({ attribute, value, component }) => {
  84. if (!component) return false
  85. const dataOption = attribute.dataOption || {}
  86. if (
  87. 'permission' in dataOption &&
  88. dataOption.permission &&
  89. !session.hasPermission(dataOption.permission)
  90. ) {
  91. return false
  92. }
  93. if (isEmpty(value)) {
  94. return false
  95. }
  96. return !options.skipAttributes?.includes(attribute.name)
  97. })
  98. })
  99. return {
  100. fields,
  101. }
  102. }