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