@asteby/metacore-runtime-react 18.24.0 → 18.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 18.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - adb1c52: `CollectionCell` renders resolved relation references as "pro" chips in EVERY
8
+ path — including the schema-less generic one.
9
+
10
+ Previously only the declared-`item_fields` path resolved a jsonb line-item ref
11
+ (e.g. `product_id`) to a relation chip. The generic path (used by the full-page
12
+ record detail and any jsonb without a declared schema) dumped the
13
+ backend-injected resolved sibling object as raw `"{…}"` AND showed the raw uuid
14
+ in a duplicate column. Now the generic path:
15
+ - detects the backend-injected `{ value, label, image }` ref siblings,
16
+ - renders them as the same relation chip (subtle tint + thumbnail or entity icon
17
+ - name) the FK table columns use, and
18
+ - hides the raw `<key>_id` twin column,
19
+
20
+ so an unconfigured jsonb line-items blob reads as first-class relations
21
+ (foto/nombre) instead of uuid soup. The shared chip is extracted as `RefChip`
22
+ and reused by the schema (`ItemFieldCell`) and generic paths.
23
+
3
24
  ## 18.24.0
4
25
 
5
26
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"collection-cell.d.ts","sourceRoot":"","sources":["../src/collection-cell.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAkD9B,sFAAsF;AACtF,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,MAAM,CAAA;AAE9D;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACtB,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAA;IACX,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAA;CACf;AA6KD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACvB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,EACf,CAAC,CAAC,EAAE,SAAS,GACd,MAAM,CAWR;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACtB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,EACf,CAAC,CAAC,EAAE,SAAS,GACd,MAAM,CAcR;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAYnD;AAuMD,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,OAAO,CAAA;IACd,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,CAAC,CAAC,EAAE,SAAS,CAAA;IACb;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,SAAa,EACb,MAAM,EACN,CAAC,EACD,UAAU,EACV,WAAW,EACX,OAAiB,GACpB,EAAE,mBAAmB,qBA2HrB"}
1
+ {"version":3,"file":"collection-cell.d.ts","sourceRoot":"","sources":["../src/collection-cell.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAkD9B,sFAAsF;AACtF,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,MAAM,CAAA;AAE9D;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACtB,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAA;IACX,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAA;CACf;AAoND;;;;;;GAMG;AACH,wBAAgB,WAAW,CACvB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,EACf,CAAC,CAAC,EAAE,SAAS,GACd,MAAM,CAWR;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACtB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,EACf,CAAC,CAAC,EAAE,SAAS,GACd,MAAM,CAcR;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAYnD;AAiOD,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,OAAO,CAAA;IACd,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,CAAC,CAAC,EAAE,SAAS,CAAA;IACb;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,SAAa,EACb,MAAM,EACN,CAAC,EACD,UAAU,EACV,WAAW,EACX,OAAiB,GACpB,EAAE,mBAAmB,qBA2HrB"}
@@ -80,18 +80,44 @@ function itemFieldText(field, row) {
80
80
  return ref.label;
81
81
  return formatScalar(row[field.key]);
82
82
  }
83
+ /**
84
+ * A backend-resolved relation reference shaped `{ value, label, image? }` — the
85
+ * sibling the backend injects for a ref (FK column or jsonb item-field), the
86
+ * SAME shape the relation table columns use. Lets a jsonb line item read as a
87
+ * first-class relation, not a raw uuid.
88
+ */
89
+ function resolvedRefObject(v) {
90
+ if (!isPlainObject(v))
91
+ return null;
92
+ const label = v.label;
93
+ if (label === undefined || label === null || label === '')
94
+ return null;
95
+ return {
96
+ label: String(label),
97
+ value: v.value,
98
+ image: typeof v.image === 'string' && v.image !== '' ? v.image : undefined,
99
+ };
100
+ }
101
+ /**
102
+ * The "pro" relation chip — subtle deterministic tint + the related record's
103
+ * thumbnail (product photo / logo / avatar) or a generic entity icon + the
104
+ * resolved name. The exact look the FK table columns use, so resolved relations
105
+ * read consistently everywhere (table popover, detail view, edit, line items).
106
+ */
107
+ function RefChip({ label, image, getImageUrl, }) {
108
+ const isDark = useIsDarkTheme();
109
+ return (_jsxs("span", { className: "inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium", style: relationChipStyles(label, { isDark }), title: label, children: [image ? (_jsxs(Avatar, { className: "shrink-0 rounded-sm ring-1 ring-border/40", style: { width: 18, height: 18 }, children: [_jsx(AvatarImage, { src: getImageUrl ? getImageUrl(image) : image, alt: label, className: "object-cover" }), _jsx(AvatarFallback, { className: "rounded-sm bg-primary/10 text-[8px] font-bold text-primary", children: getInitials(label) })] })) : (_jsx(Box, { className: "h-3 w-3 shrink-0 opacity-70" })), _jsx("span", { className: "truncate", children: label })] }));
110
+ }
83
111
  /**
84
112
  * Visual cell for one declared item-field. A resolved `ref` renders as a
85
- * relation chip (subtle deterministic tint + product thumbnail or a generic
86
- * entity icon + name) the same "pro" look the table FK columns use — so jsonb
87
- * line items read like first-class relations instead of raw uuids. Non-ref (or
113
+ * relation chip (foto/ícono + nombre) the "pro" FK-column look — so jsonb line
114
+ * items read like first-class relations instead of raw uuids. Non-ref (or
88
115
  * unresolved) fields render the plain scalar.
89
116
  */
90
117
  function ItemFieldCell({ field, row, getImageUrl, }) {
91
- const isDark = useIsDarkTheme();
92
118
  const ref = resolvedRefFor(field, row);
93
119
  if (ref?.label) {
94
- return (_jsxs("span", { className: "inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium", style: relationChipStyles(ref.label, { isDark }), title: ref.label, children: [ref.image ? (_jsxs(Avatar, { className: "shrink-0 rounded-sm ring-1 ring-border/40", style: { width: 18, height: 18 }, children: [_jsx(AvatarImage, { src: getImageUrl ? getImageUrl(ref.image) : ref.image, alt: ref.label, className: "object-cover" }), _jsx(AvatarFallback, { className: "rounded-sm bg-primary/10 text-[8px] font-bold text-primary", children: getInitials(ref.label) })] })) : (_jsx(Box, { className: "h-3 w-3 shrink-0 opacity-70" })), _jsx("span", { className: "truncate", children: ref.label })] }));
120
+ return (_jsx(RefChip, { label: ref.label, image: ref.image, getImageUrl: getImageUrl }));
95
121
  }
96
122
  return _jsx(_Fragment, { children: formatScalar(row[field.key]) });
97
123
  }
@@ -265,11 +291,28 @@ function MiniTable({ rows, locale, t, itemFields, getImageUrl, }) {
265
291
  if (itemFields && itemFields.length > 0) {
266
292
  return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: itemFields.map((field) => (_jsx(TableHead, { className: "text-xs whitespace-nowrap", children: field.label }, field.key))) }) }), _jsx(TableBody, { children: rows.map((row, i) => (_jsx(TableRow, { children: itemFields.map((field) => (_jsx(TableCell, { className: "text-xs whitespace-nowrap", children: _jsx(ItemFieldCell, { field: field, row: row, getImageUrl: getImageUrl }) }, field.key))) }, i))) })] }));
267
293
  }
268
- const keys = unionKeys(rows);
294
+ // Generic (no-schema) path — still relation-AWARE. The backend injects a
295
+ // resolved-ref sibling object (`{value,label,image}`) next to each FK id
296
+ // inside the items (e.g. `product` next to `product_id`). Without a declared
297
+ // schema we'd otherwise dump that object as "{…}" AND the raw uuid in a
298
+ // duplicate column. Instead: detect those sibling objects, render them as
299
+ // relation chips, and HIDE their raw `<key>_id` twin — so even an
300
+ // unconfigured jsonb blob reads "pro" (foto/nombre, no uuid soup).
301
+ const allKeys = unionKeys(rows);
302
+ const refKeys = new Set(allKeys.filter((k) => rows.some((r) => resolvedRefObject(r[k]) !== null)));
303
+ const hiddenTwins = new Set();
304
+ for (const rk of refKeys)
305
+ hiddenTwins.add(`${rk}_id`);
306
+ const keys = allKeys.filter((k) => !hiddenTwins.has(k));
269
307
  if (keys.length === 0) {
270
308
  return _jsx("div", { className: "p-3 text-xs text-muted-foreground", children: "-" });
271
309
  }
272
- return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: keys.map((key) => (_jsx(TableHead, { className: "text-xs whitespace-nowrap", children: prettifyKey(key, locale, t) }, key))) }) }), _jsx(TableBody, { children: rows.map((row, i) => (_jsx(TableRow, { children: keys.map((key) => (_jsx(TableCell, { className: "text-xs whitespace-nowrap", children: formatScalar(row[key]) }, key))) }, i))) })] }));
310
+ return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: keys.map((key) => (_jsx(TableHead, { className: "text-xs whitespace-nowrap", children: prettifyKey(key, locale, t) }, key))) }) }), _jsx(TableBody, { children: rows.map((row, i) => (_jsx(TableRow, { children: keys.map((key) => {
311
+ const ref = refKeys.has(key)
312
+ ? resolvedRefObject(row[key])
313
+ : null;
314
+ return (_jsx(TableCell, { className: "text-xs whitespace-nowrap", children: ref ? (_jsx(RefChip, { label: ref.label, image: ref.image, getImageUrl: getImageUrl })) : (formatScalar(row[key])) }, key));
315
+ }) }, i))) })] }));
273
316
  }
274
317
  function ScalarList({ values }) {
275
318
  return (_jsx("ul", { className: "p-3 space-y-1", children: values.map((v, i) => (_jsx("li", { className: "text-xs text-foreground", children: formatScalar(v) }, i))) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "18.24.0",
3
+ "version": "18.25.0",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,8 +34,8 @@
34
34
  "react-i18next": ">=13",
35
35
  "sonner": ">=1.7",
36
36
  "zustand": ">=5",
37
- "@asteby/metacore-sdk": "^3.2.0",
38
- "@asteby/metacore-ui": "^2.5.2"
37
+ "@asteby/metacore-ui": "^2.5.2",
38
+ "@asteby/metacore-sdk": "^3.2.0"
39
39
  },
40
40
  "peerDependenciesMeta": {
41
41
  "@tanstack/react-router": {
@@ -136,11 +136,74 @@ function itemFieldText(field: ItemField, row: Record<string, unknown>): string {
136
136
  return formatScalar(row[field.key])
137
137
  }
138
138
 
139
+ /**
140
+ * A backend-resolved relation reference shaped `{ value, label, image? }` — the
141
+ * sibling the backend injects for a ref (FK column or jsonb item-field), the
142
+ * SAME shape the relation table columns use. Lets a jsonb line item read as a
143
+ * first-class relation, not a raw uuid.
144
+ */
145
+ function resolvedRefObject(
146
+ v: unknown,
147
+ ): (ResolvedRef & { label: string }) | null {
148
+ if (!isPlainObject(v)) return null
149
+ const label = v.label
150
+ if (label === undefined || label === null || label === '') return null
151
+ return {
152
+ label: String(label),
153
+ value: v.value,
154
+ image:
155
+ typeof v.image === 'string' && v.image !== '' ? v.image : undefined,
156
+ }
157
+ }
158
+
159
+ /**
160
+ * The "pro" relation chip — subtle deterministic tint + the related record's
161
+ * thumbnail (product photo / logo / avatar) or a generic entity icon + the
162
+ * resolved name. The exact look the FK table columns use, so resolved relations
163
+ * read consistently everywhere (table popover, detail view, edit, line items).
164
+ */
165
+ function RefChip({
166
+ label,
167
+ image,
168
+ getImageUrl,
169
+ }: {
170
+ label: string
171
+ image?: string
172
+ getImageUrl?: (path: string) => string
173
+ }): React.ReactElement {
174
+ const isDark = useIsDarkTheme()
175
+ return (
176
+ <span
177
+ className="inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium"
178
+ style={relationChipStyles(label, { isDark })}
179
+ title={label}
180
+ >
181
+ {image ? (
182
+ <Avatar
183
+ className="shrink-0 rounded-sm ring-1 ring-border/40"
184
+ style={{ width: 18, height: 18 }}
185
+ >
186
+ <AvatarImage
187
+ src={getImageUrl ? getImageUrl(image) : image}
188
+ alt={label}
189
+ className="object-cover"
190
+ />
191
+ <AvatarFallback className="rounded-sm bg-primary/10 text-[8px] font-bold text-primary">
192
+ {getInitials(label)}
193
+ </AvatarFallback>
194
+ </Avatar>
195
+ ) : (
196
+ <Box className="h-3 w-3 shrink-0 opacity-70" />
197
+ )}
198
+ <span className="truncate">{label}</span>
199
+ </span>
200
+ )
201
+ }
202
+
139
203
  /**
140
204
  * Visual cell for one declared item-field. A resolved `ref` renders as a
141
- * relation chip (subtle deterministic tint + product thumbnail or a generic
142
- * entity icon + name) the same "pro" look the table FK columns use — so jsonb
143
- * line items read like first-class relations instead of raw uuids. Non-ref (or
205
+ * relation chip (foto/ícono + nombre) the "pro" FK-column look — so jsonb line
206
+ * items read like first-class relations instead of raw uuids. Non-ref (or
144
207
  * unresolved) fields render the plain scalar.
145
208
  */
146
209
  function ItemFieldCell({
@@ -152,34 +215,10 @@ function ItemFieldCell({
152
215
  row: Record<string, unknown>
153
216
  getImageUrl?: (path: string) => string
154
217
  }): React.ReactElement {
155
- const isDark = useIsDarkTheme()
156
218
  const ref = resolvedRefFor(field, row)
157
219
  if (ref?.label) {
158
220
  return (
159
- <span
160
- className="inline-flex max-w-[220px] items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium"
161
- style={relationChipStyles(ref.label, { isDark })}
162
- title={ref.label}
163
- >
164
- {ref.image ? (
165
- <Avatar
166
- className="shrink-0 rounded-sm ring-1 ring-border/40"
167
- style={{ width: 18, height: 18 }}
168
- >
169
- <AvatarImage
170
- src={getImageUrl ? getImageUrl(ref.image) : ref.image}
171
- alt={ref.label}
172
- className="object-cover"
173
- />
174
- <AvatarFallback className="rounded-sm bg-primary/10 text-[8px] font-bold text-primary">
175
- {getInitials(ref.label)}
176
- </AvatarFallback>
177
- </Avatar>
178
- ) : (
179
- <Box className="h-3 w-3 shrink-0 opacity-70" />
180
- )}
181
- <span className="truncate">{ref.label}</span>
182
- </span>
221
+ <RefChip label={ref.label} image={ref.image} getImageUrl={getImageUrl} />
183
222
  )
184
223
  }
185
224
  return <>{formatScalar(row[field.key])}</>
@@ -412,7 +451,20 @@ function MiniTable({
412
451
  )
413
452
  }
414
453
 
415
- const keys = unionKeys(rows)
454
+ // Generic (no-schema) path — still relation-AWARE. The backend injects a
455
+ // resolved-ref sibling object (`{value,label,image}`) next to each FK id
456
+ // inside the items (e.g. `product` next to `product_id`). Without a declared
457
+ // schema we'd otherwise dump that object as "{…}" AND the raw uuid in a
458
+ // duplicate column. Instead: detect those sibling objects, render them as
459
+ // relation chips, and HIDE their raw `<key>_id` twin — so even an
460
+ // unconfigured jsonb blob reads "pro" (foto/nombre, no uuid soup).
461
+ const allKeys = unionKeys(rows)
462
+ const refKeys = new Set(
463
+ allKeys.filter((k) => rows.some((r) => resolvedRefObject(r[k]) !== null))
464
+ )
465
+ const hiddenTwins = new Set<string>()
466
+ for (const rk of refKeys) hiddenTwins.add(`${rk}_id`)
467
+ const keys = allKeys.filter((k) => !hiddenTwins.has(k))
416
468
  if (keys.length === 0) {
417
469
  return <div className="p-3 text-xs text-muted-foreground">-</div>
418
470
  }
@@ -430,14 +482,27 @@ function MiniTable({
430
482
  <TableBody>
431
483
  {rows.map((row, i) => (
432
484
  <TableRow key={i}>
433
- {keys.map((key) => (
434
- <TableCell
435
- key={key}
436
- className="text-xs whitespace-nowrap"
437
- >
438
- {formatScalar(row[key])}
439
- </TableCell>
440
- ))}
485
+ {keys.map((key) => {
486
+ const ref = refKeys.has(key)
487
+ ? resolvedRefObject(row[key])
488
+ : null
489
+ return (
490
+ <TableCell
491
+ key={key}
492
+ className="text-xs whitespace-nowrap"
493
+ >
494
+ {ref ? (
495
+ <RefChip
496
+ label={ref.label}
497
+ image={ref.image}
498
+ getImageUrl={getImageUrl}
499
+ />
500
+ ) : (
501
+ formatScalar(row[key])
502
+ )}
503
+ </TableCell>
504
+ )
505
+ })}
441
506
  </TableRow>
442
507
  ))}
443
508
  </TableBody>