import React, { useEffect, useMemo, useState } from 'react'; import { observer } from 'mobx-react-lite'; import { sourceFetchData } from '@wix/bex-core'; import type { BaseReferenceField, Field, Schema } from '@wix/bex-core'; import type { Control } from '@wix/bex-core/form'; import { useCollection } from '@wix/bex-core/react'; import { FormReference, FormMultiReference } from '@wix/patterns-fields'; import { useEntityPageContext } from '../../providers'; type RefRow = Record; const imageFieldOf = ( fields: Schema['fields'] | undefined, ): string | undefined => Object.entries(fields ?? {}).find(([, f]) => f?.type === 'IMAGE')?.[0]; export interface EntityPageReferenceFieldProps { field: Field; formControl: Control; data: Record; } // Adapts a referenced SchemaSource (schemaSource.references[collectionId]) to the // data-agnostic FormReference / FormMultiReference renderers: it queries the // referenced collection for candidate items and derives the referenced schema's // id/display/image fields, mirroring EditableTable's reference cell Edit. function _EntityPageReferenceField({ field, formControl, data, }: EntityPageReferenceFieldProps) { const state = useEntityPageContext(); const referencedCollectionId = (field as BaseReferenceField).referenceMetadata ?.referencedCollectionId; const refSource = referencedCollectionId ? state.schemaSource?.references?.[referencedCollectionId] : undefined; const collection = useCollection( refSource ? { queryName: `entity-page-reference-${refSource.collectionId}`, paginationMode: refSource.paginationMode, fetchData: sourceFetchData(refSource), itemKey: refSource.itemKey, itemName: refSource.itemName, } : { queryName: `entity-page-reference-${field.id}`, paginationMode: 'offset', fetchData: async () => ({ items: [], total: 0 }), itemKey: (row: RefRow) => String(row.id ?? ''), itemName: (row: RefRow) => String(row.id ?? ''), }, ); useEffect(() => collection.init(), [collection]); const [refSchema, setRefSchema] = useState(null); useEffect(() => { let active = true; refSource?.loadSchema().then((loaded) => { if (active) { setRefSchema(loaded.schema); } }); return () => { active = false; }; }, [refSource]); const idField = refSchema?.idField; const displayField = refSchema?.displayField; const imageField = imageFieldOf(refSchema?.fields); const items = useMemo(() => { if (!idField || !displayField) { return []; } return collection.result.keyedItems.map((ki) => ({ id: String((ki.item as RefRow)[idField] ?? ki.key), name: String((ki.item as RefRow)[displayField] ?? ''), image: imageField ? ((ki.item as RefRow)[imageField] as string | undefined) : undefined, })); }, [collection.result.keyedItems, idField, displayField, imageField]); // A reference with no matching source entry, or before its schema resolves, // can't render a picker — skip it rather than render a broken input. if (!refSource || !idField || !displayField) { return null; } const shared = { field, value: data?.[field.id] as never, onChange: () => {}, formControl, data, dataHook: `entity-page-field-${field.id}`, items, idField, displayField, imageField, searchValue: collection.query.search.value, onSearch: (query: string) => collection.search(query), onClearSearch: () => collection.clearSearch(), hasMore: !collection.fetchedLastPage, loadMore: collection.fetchNextPageIfNeeded, }; return field.type === 'MULTI_REFERENCE' ? ( ) : ( ); } export const EntityPageReferenceField = observer(_EntityPageReferenceField);