Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 2x 2x 2x 2x 2x 1x 1x 1x 1x 6x 6x 6x 6x 6x 2x 2x 2x 6x 5x 6x 9x 8x 1x 1x 7x 7x 7x 6x 9x 9x 1x 7x 6x 5x 5x 5x 6x 1x 1x 6x | import { computed, onMounted, ref, watch } from 'vue'
import type { Ref } from 'vue'
import { getActivePinia } from 'pinia'
import { useEntitiesStore } from '@/stores/entities'
import api from '@/services/api'
import type { ReferenceOption } from './useFlowMemberReference'
interface EntityItem {
id: string
[key: string]: any
}
/**
* Derive a human-readable label for an item.
*
* The label is built from the first text/email/number/select field found in the
* entity's field definitions. Falls back to the item ID when no suitable field
* is found or when the value is empty.
*/
function buildItemLabel(
item: EntityItem,
fieldDefs: { fieldId?: string; type?: string }[]
): string {
const labelFieldTypes = ['text', 'email', 'number', 'select', 'textarea']
for (const field of fieldDefs) {
Iif (!field.fieldId || !labelFieldTypes.includes(field.type ?? '')) continue
const raw = item[field.fieldId]
if (!raw && raw !== 0) continue
Iif (typeof raw === 'object') {
// Multilingual: pick any available language
const first = Object.values(raw).find((v) => v)
if (first) return String(first)
} else {
return String(raw)
}
}
return item.id
}
/** Per-entity item cache — avoids refetching on every render. */
const itemCache = new Map<string, EntityItem[]>()
export function useEntityItemReference(entityId: Ref<string | undefined>) {
const hasPinia = !!getActivePinia()
const entitiesStore = hasPinia ? useEntitiesStore() : null
const items = ref<EntityItem[]>([])
const loading = ref(false)
const fieldDefs = computed(() => {
Iif (!entitiesStore || !entityId.value) return []
const entity =
entitiesStore.entities.find((e) => e.id === entityId.value) ??
(entitiesStore.currentEntity?.id === entityId.value ? entitiesStore.currentEntity : null)
return entity?.fields ?? []
})
const itemOptions = computed<ReferenceOption[]>(() =>
items.value.map((item) => ({
value: item.id,
label: buildItemLabel(item, fieldDefs.value)
}))
)
const itemLabelById = computed(() => new Map(itemOptions.value.map((o) => [o.value, o.label])))
async function ensureItemsLoaded(force = false) {
if (!entityId.value) return
if (!force && itemCache.has(entityId.value)) {
items.value = itemCache.get(entityId.value)!
return
}
loading.value = true
try {
const response = await api.get(`/v1/entities/${entityId.value}/items`, {
params: { limit: 200 }
})
const fetched: EntityItem[] = response.data.items ?? response.data ?? []
itemCache.set(entityId.value, fetched)
items.value = fetched
} catch {
items.value = []
} finally {
loading.value = false
}
}
if (hasPinia) {
onMounted(() => {
void ensureItemsLoaded().catch(() => {})
})
watch(
() => entityId.value,
(newId, oldId) => {
Eif (newId && newId !== oldId) {
void ensureItemsLoaded().catch(() => {})
}
}
)
}
return {
itemOptions,
itemLabelById,
itemsLoading: loading,
ensureItemsLoaded
}
}
|