@@ -25,7 +25,7 @@ import {
2525} from '@object-ui/components' ;
2626import { ChevronDown , ChevronRight , Copy , Check , Eye , EyeOff } from 'lucide-react' ;
2727import { SchemaRenderer } from '@object-ui/react' ;
28- import { getCellRenderer , resolveCellRendererType , SelectField , BooleanField } from '@object-ui/fields' ;
28+ import { getCellRenderer , resolveCellRendererType , SelectField , BooleanField , LookupField , UserField , coerceToSafeValue } from '@object-ui/fields' ;
2929import type { DetailViewSection as DetailViewSectionType , DetailViewField , FieldMetadata } from '@object-ui/types' ;
3030import { applyDetailAutoLayout } from './autoLayout' ;
3131import { useDetailTranslation } from './useDetailTranslation' ;
@@ -53,6 +53,29 @@ export function getResponsiveSpanClass(span: number | undefined, columns: number
5353 return '' ;
5454}
5555
56+ /**
57+ * Field types that carry a `reference_to` for relational metadata but are NOT
58+ * edited via the lookup picker (they have their own dedicated inputs/renderers).
59+ * Used so the inline-edit branch doesn't hijack them into a record picker.
60+ */
61+ const TEXTUAL_REF_FALLBACK_TYPES = new Set ( [ 'formula' , 'summary' , 'rollup' , 'auto_number' ] ) ;
62+
63+ /**
64+ * Extract the id a reference widget expects from a value that may already be
65+ * an `$expand`-ed record object (`{ id, name, ... }`), an array of those, or a
66+ * bare id. Mirrors the display logic in `LookupCellRenderer` so edit-mode and
67+ * read-mode agree on which id a relationship points at.
68+ */
69+ function extractLookupId ( value : unknown ) : unknown {
70+ if ( value == null ) return value ;
71+ if ( Array . isArray ( value ) ) return value . map ( extractLookupId ) ;
72+ if ( typeof value === 'object' ) {
73+ const obj = value as Record < string , unknown > ;
74+ return obj . id ?? obj . _id ?? obj . value ?? '' ;
75+ }
76+ return value ;
77+ }
78+
5679export interface VirtualScrollOptions {
5780 /** Enable virtual scrolling for large field sets */
5881 enabled ?: boolean ;
@@ -74,6 +97,8 @@ export interface DetailSectionProps {
7497 isEditing ?: boolean ;
7598 /** Callback when a field value changes during inline editing */
7699 onFieldChange ?: ( field : string , value : any ) => void ;
100+ /** DataSource used by reference (lookup/master_detail/user) widgets during inline editing */
101+ dataSource ?: any ;
77102 /** Virtual scrolling configuration for sections with many fields */
78103 virtualScroll ?: VirtualScrollOptions ;
79104}
@@ -86,6 +111,7 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
86111 objectName,
87112 isEditing = false ,
88113 onFieldChange,
114+ dataSource,
89115 virtualScroll,
90116} ) => {
91117 const [ isCollapsed , setIsCollapsed ] = React . useState ( section . defaultCollapsed ?? false ) ;
@@ -267,6 +293,28 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
267293 />
268294 ) ;
269295 }
296+ // Reference fields (lookup / master_detail / tree / user / owner)
297+ // store an id but may arrive `$expand`-ed as a record object. A
298+ // plain text input would stringify that to "[object Object]", so
299+ // render the real picker and feed it the id extracted from the
300+ // (possibly expanded) value.
301+ const isUserRef = editType === 'user' || editType === 'owner' ;
302+ const isLookupRef =
303+ editType === 'lookup' ||
304+ editType === 'master_detail' ||
305+ editType === 'tree' ||
306+ ( ! ! enrichedField . reference_to && ! TEXTUAL_REF_FALLBACK_TYPES . has ( editType as string ) ) ;
307+ if ( isUserRef || isLookupRef ) {
308+ const RefWidget = isUserRef ? UserField : LookupField ;
309+ return (
310+ < RefWidget
311+ field = { enrichedField as any }
312+ value = { extractLookupId ( value ) }
313+ onChange = { ( v : any ) => onFieldChange ?.( field . name , v ) }
314+ dataSource = { dataSource }
315+ />
316+ ) ;
317+ }
270318 const isDate = editType === 'date' || editType === 'datetime' ;
271319 const inputType = editType === 'number' ? 'number' : isDate ? 'date' : 'text' ;
272320 // <input type="date"> needs a YYYY-MM-DD string; raw ISO
@@ -282,7 +330,12 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
282330 const d = new Date ( s ) ;
283331 return isNaN ( d . getTime ( ) ) ? '' : d . toLocaleDateString ( 'en-CA' ) ;
284332 } ) ( )
285- : String ( value ) ;
333+ // Coerce objects (e.g. an unexpanded reference that slipped
334+ // through type detection) to a readable label rather than
335+ // leaking "[object Object]" into the input.
336+ : typeof value === 'object'
337+ ? String ( coerceToSafeValue ( value ) ?? '' )
338+ : String ( value ) ;
286339 return (
287340 < input
288341 type = { inputType }
0 commit comments