@asteby/metacore-runtime-react 18.23.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 +39 -0
- package/dist/collection-cell.d.ts +7 -1
- package/dist/collection-cell.d.ts.map +1 -1
- package/dist/collection-cell.js +132 -27
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +4 -2
- package/dist/dynamic-columns.d.ts.map +1 -1
- package/dist/dynamic-columns.js +1 -1
- package/package.json +3 -3
- package/src/collection-cell.tsx +211 -27
- package/src/dialogs/dynamic-record.tsx +4 -0
- package/src/dynamic-columns.tsx +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
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
|
+
|
|
24
|
+
## 18.24.0
|
|
25
|
+
|
|
26
|
+
### Minor Changes
|
|
27
|
+
|
|
28
|
+
- 39c43ea: jsonb line-item `ref` cells render as relation chips (icon/photo + name).
|
|
29
|
+
|
|
30
|
+
In `CollectionCell` (table popover, inline detail view, and the read-only edit
|
|
31
|
+
field), a resolved ref sub-field — e.g. `product_id` inside a transfer's `items`
|
|
32
|
+
— now renders with the same "pro" relation look the FK table columns use: a
|
|
33
|
+
subtle deterministic tint, the resolved record's thumbnail (product photo / logo
|
|
34
|
+
/ avatar, resolved via the threaded `getImageUrl`) or a generic entity icon
|
|
35
|
+
fallback, and the resolved name — instead of a truncated uuid.
|
|
36
|
+
|
|
37
|
+
`CollectionCell` gains an optional `getImageUrl` prop, threaded from the columns
|
|
38
|
+
factory and from the record dialog's `ImageUrlContext`. Backend-agnostic: it
|
|
39
|
+
drives off the backend-injected `{ value, label, image }` sibling; an unresolved
|
|
40
|
+
ref still falls back to the scalar value.
|
|
41
|
+
|
|
3
42
|
## 18.23.0
|
|
4
43
|
|
|
5
44
|
### Minor Changes
|
|
@@ -57,6 +57,12 @@ export interface CollectionCellProps {
|
|
|
57
57
|
* behaviour is unchanged.
|
|
58
58
|
*/
|
|
59
59
|
itemFields?: ItemField[];
|
|
60
|
+
/**
|
|
61
|
+
* Resolves a stored image path to a displayable URL (same resolver the table
|
|
62
|
+
* columns use). Threaded to the ref-chip thumbnails so resolved line-item
|
|
63
|
+
* relations show a product photo / logo / avatar like the FK columns do.
|
|
64
|
+
*/
|
|
65
|
+
getImageUrl?: (path: string) => string;
|
|
60
66
|
/**
|
|
61
67
|
* Presentation mode.
|
|
62
68
|
* - `'badge'` (default): the compact count/preview badge that opens a
|
|
@@ -77,5 +83,5 @@ export interface CollectionCellProps {
|
|
|
77
83
|
* itemFields schema (localized headers + resolved ref labels) and the
|
|
78
84
|
* locale-aware generic fallback.
|
|
79
85
|
*/
|
|
80
|
-
export declare function CollectionCell({ value, maxInline, locale, t, itemFields, variant, }: CollectionCellProps): React.JSX.Element;
|
|
86
|
+
export declare function CollectionCell({ value, maxInline, locale, t, itemFields, getImageUrl, variant, }: CollectionCellProps): React.JSX.Element;
|
|
81
87
|
//# sourceMappingURL=collection-cell.d.ts.map
|
|
@@ -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;
|
|
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"}
|
package/dist/collection-cell.js
CHANGED
|
@@ -1,7 +1,37 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Generic, brand-neutral table-cell renderer for jsonb / array / object column
|
|
3
|
+
// values. Kernel-derived dynamic tables surface raw jsonb columns (line items,
|
|
4
|
+
// nested config blobs, scalar arrays) with no per-column metadata; without this
|
|
5
|
+
// they rendered as raw `JSON.stringify(value)` which is unreadable. This renders
|
|
6
|
+
// a compact trigger (count badge / inline pairs) plus a Popover with a clean
|
|
7
|
+
// mini-table — no per-addon config required, safe on any shape.
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import { Box, List } from 'lucide-react';
|
|
10
|
+
import { Avatar, AvatarFallback, AvatarImage, Badge, Popover, PopoverContent, PopoverTrigger, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, cn, } from '@asteby/metacore-ui';
|
|
11
|
+
import { getInitials, relationChipStyles } from '@asteby/metacore-ui/lib';
|
|
4
12
|
import { humanizeToken } from './dynamic-columns-helpers';
|
|
13
|
+
/**
|
|
14
|
+
* Tracks the host's dark-mode class on <html> so relation chips pick a tint
|
|
15
|
+
* tuned for the active theme. Replicated from dynamic-columns (kept local to
|
|
16
|
+
* avoid a cross-module import); mirror changes if that one evolves.
|
|
17
|
+
*/
|
|
18
|
+
function useIsDarkTheme() {
|
|
19
|
+
const [isDark, setIsDark] = React.useState(() => typeof document !== 'undefined' &&
|
|
20
|
+
document.documentElement.classList.contains('dark'));
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
if (typeof document === 'undefined')
|
|
23
|
+
return;
|
|
24
|
+
const sync = () => setIsDark(document.documentElement.classList.contains('dark'));
|
|
25
|
+
sync();
|
|
26
|
+
const observer = new MutationObserver(sync);
|
|
27
|
+
observer.observe(document.documentElement, {
|
|
28
|
+
attributes: true,
|
|
29
|
+
attributeFilter: ['class'],
|
|
30
|
+
});
|
|
31
|
+
return () => observer.disconnect();
|
|
32
|
+
}, []);
|
|
33
|
+
return isDark;
|
|
34
|
+
}
|
|
5
35
|
const UUID_LIKE_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
36
|
/**
|
|
7
37
|
* Resolves the backend-injected resolved sibling key for a ref item-field,
|
|
@@ -12,27 +42,85 @@ function siblingKeyFor(key) {
|
|
|
12
42
|
return key.endsWith('_id') ? key.slice(0, -3) : `${key}_label`;
|
|
13
43
|
}
|
|
14
44
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `formatScalar` (truncated uuid). Non-ref fields render `formatScalar(value)`.
|
|
45
|
+
* Reads the backend-injected resolved sibling for a `ref` item-field (the FK
|
|
46
|
+
* key without `_id`, else `<key>_label`). Returns the normalized `{label,
|
|
47
|
+
* image}` when present, a `{label}` for a bare-string sibling, or null when the
|
|
48
|
+
* ref is unresolved (so the caller falls back to the raw value).
|
|
20
49
|
*/
|
|
21
|
-
function
|
|
22
|
-
if (field.ref)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
50
|
+
function resolvedRefFor(field, row) {
|
|
51
|
+
if (!field.ref)
|
|
52
|
+
return null;
|
|
53
|
+
const sibling = row[siblingKeyFor(field.key)];
|
|
54
|
+
if (sibling && typeof sibling === 'object' && !Array.isArray(sibling)) {
|
|
55
|
+
const obj = sibling;
|
|
56
|
+
const label = obj.label;
|
|
57
|
+
if (label !== undefined && label !== null && label !== '') {
|
|
58
|
+
return {
|
|
59
|
+
label: String(label),
|
|
60
|
+
value: obj.value,
|
|
61
|
+
image: typeof obj.image === 'string' && obj.image !== ''
|
|
62
|
+
? obj.image
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
32
65
|
}
|
|
33
66
|
}
|
|
67
|
+
else if (typeof sibling === 'string' && sibling !== '') {
|
|
68
|
+
return { label: sibling };
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Plain-text value for one declared item-field — used for the no-JS popover
|
|
74
|
+
* `title` tooltip (which can't render JSX). Ref fields show the resolved label;
|
|
75
|
+
* everything else uses `formatScalar` (truncated uuid for unresolved ids).
|
|
76
|
+
*/
|
|
77
|
+
function itemFieldText(field, row) {
|
|
78
|
+
const ref = resolvedRefFor(field, row);
|
|
79
|
+
if (ref?.label)
|
|
80
|
+
return ref.label;
|
|
34
81
|
return formatScalar(row[field.key]);
|
|
35
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
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Visual cell for one declared item-field. A resolved `ref` renders as a
|
|
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
|
|
115
|
+
* unresolved) fields render the plain scalar.
|
|
116
|
+
*/
|
|
117
|
+
function ItemFieldCell({ field, row, getImageUrl, }) {
|
|
118
|
+
const ref = resolvedRefFor(field, row);
|
|
119
|
+
if (ref?.label) {
|
|
120
|
+
return (_jsx(RefChip, { label: ref.label, image: ref.image, getImageUrl: getImageUrl }));
|
|
121
|
+
}
|
|
122
|
+
return _jsx(_Fragment, { children: formatScalar(row[field.key]) });
|
|
123
|
+
}
|
|
36
124
|
/** Normalize an org/UI language tag to a base language code (`es-MX` → `es`). */
|
|
37
125
|
function baseLang(locale) {
|
|
38
126
|
return (locale || 'en').toLowerCase().split('-')[0];
|
|
@@ -194,20 +282,37 @@ function unionKeys(rows) {
|
|
|
194
282
|
return seen;
|
|
195
283
|
}
|
|
196
284
|
const PANEL_CLASS = 'w-auto max-w-[480px] max-h-[320px] overflow-auto p-0';
|
|
197
|
-
function MiniTable({ rows, locale, t, itemFields, }) {
|
|
285
|
+
function MiniTable({ rows, locale, t, itemFields, getImageUrl, }) {
|
|
198
286
|
// Schema-driven path: a declared `item_fields` schema fixes the column
|
|
199
287
|
// order + headers (already localized by the backend, used VERBATIM) and
|
|
200
288
|
// resolves ref columns to the injected sibling label instead of the raw
|
|
201
289
|
// uuid. Sibling/raw keys not covered by the schema are dropped from the
|
|
202
290
|
// table (the schema is the source of truth for what to surface).
|
|
203
291
|
if (itemFields && itemFields.length > 0) {
|
|
204
|
-
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:
|
|
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))) })] }));
|
|
205
293
|
}
|
|
206
|
-
|
|
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));
|
|
207
307
|
if (keys.length === 0) {
|
|
208
308
|
return _jsx("div", { className: "p-3 text-xs text-muted-foreground", children: "-" });
|
|
209
309
|
}
|
|
210
|
-
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) =>
|
|
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))) })] }));
|
|
211
316
|
}
|
|
212
317
|
function ScalarList({ values }) {
|
|
213
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))) }));
|
|
@@ -229,7 +334,7 @@ function PopoverShell({ label, title, children, icon = true, }) {
|
|
|
229
334
|
* itemFields schema (localized headers + resolved ref labels) and the
|
|
230
335
|
* locale-aware generic fallback.
|
|
231
336
|
*/
|
|
232
|
-
export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, variant = 'badge', }) {
|
|
337
|
+
export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, getImageUrl, variant = 'badge', }) {
|
|
233
338
|
const parsed = parseValue(value);
|
|
234
339
|
const inline = variant === 'inline';
|
|
235
340
|
// Empty-ish → muted dash.
|
|
@@ -253,7 +358,7 @@ export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, va
|
|
|
253
358
|
// Inline mode (detail view): render the mini-table directly, no
|
|
254
359
|
// badge/popover. The same schema-driven path applies.
|
|
255
360
|
if (inline) {
|
|
256
|
-
return (_jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields }));
|
|
361
|
+
return (_jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields, getImageUrl: getImageUrl }));
|
|
257
362
|
}
|
|
258
363
|
const count = rows.length;
|
|
259
364
|
const label = countLabel(count, locale, t);
|
|
@@ -264,7 +369,7 @@ export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, va
|
|
|
264
369
|
const title = hasSchema
|
|
265
370
|
? rows
|
|
266
371
|
.map((row) => itemFields
|
|
267
|
-
.map((field) => `${field.label}: ${
|
|
372
|
+
.map((field) => `${field.label}: ${itemFieldText(field, row)}`)
|
|
268
373
|
.join(', '))
|
|
269
374
|
.join(' | ')
|
|
270
375
|
: rows
|
|
@@ -272,7 +377,7 @@ export function CollectionCell({ value, maxInline = 3, locale, t, itemFields, va
|
|
|
272
377
|
.map(([k, v]) => `${prettifyKey(k, locale, t)}: ${formatScalar(v)}`)
|
|
273
378
|
.join(', '))
|
|
274
379
|
.join(' | ');
|
|
275
|
-
return (_jsx(PopoverShell, { label: label, title: title, children: _jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields }) }));
|
|
380
|
+
return (_jsx(PopoverShell, { label: label, title: title, children: _jsx(MiniTable, { rows: rows, locale: locale, t: t, itemFields: itemFields, getImageUrl: getImageUrl }) }));
|
|
276
381
|
}
|
|
277
382
|
// Array of scalars (or mixed). Inline mode renders the full list; badge
|
|
278
383
|
// mode previews the first N joined with a "+N" overflow trigger.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
|
|
@@ -661,6 +661,7 @@ function IconNameViewValue({ name }) {
|
|
|
661
661
|
// empty objects keep the "—" marker (CollectionCell renders a muted dash, which
|
|
662
662
|
// we normalize to the em-dash the detail view uses elsewhere).
|
|
663
663
|
function StructuredViewValue({ value, field, locale, t, }) {
|
|
664
|
+
const getImageUrl = useContext(ImageUrlContext);
|
|
664
665
|
const isEmpty = value === null ||
|
|
665
666
|
value === undefined ||
|
|
666
667
|
value === '' ||
|
|
@@ -671,16 +672,17 @@ function StructuredViewValue({ value, field, locale, t, }) {
|
|
|
671
672
|
if (isEmpty) {
|
|
672
673
|
return _jsx("p", { className: "text-sm py-1 text-muted-foreground", children: "\u2014" });
|
|
673
674
|
}
|
|
674
|
-
return (_jsx("div", { className: "text-sm py-1", children: _jsx(CollectionCell, { value: value, itemFields: field?.itemFields ?? field?.item_fields, variant: "inline", locale: locale, t: t }) }));
|
|
675
|
+
return (_jsx("div", { className: "text-sm py-1", children: _jsx(CollectionCell, { value: value, itemFields: field?.itemFields ?? field?.item_fields, variant: "inline", locale: locale, t: t, getImageUrl: getImageUrl }) }));
|
|
675
676
|
}
|
|
676
677
|
export function EditField({ field, value, onChange, record }) {
|
|
677
678
|
const { t, i18n } = useTranslation();
|
|
679
|
+
const editFieldImageUrl = useContext(ImageUrlContext);
|
|
678
680
|
// Jsonb line-items columns (e.g. Transfer.items) are action-built documents:
|
|
679
681
|
// editing the array field-by-field is out of scope. Render them READ-ONLY
|
|
680
682
|
// with the same inline table the detail view uses — a localized, ref-resolved
|
|
681
683
|
// mini-table — instead of an input that stringifies to "[object Object]".
|
|
682
684
|
if (isLineItemsField(field, value)) {
|
|
683
|
-
return (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "rounded-md border bg-muted/30 p-2", children: _jsx(CollectionCell, { value: value, itemFields: fieldItemFields(field), variant: "inline", locale: i18n.language, t: t }) }), _jsx("p", { className: "text-[11px] text-muted-foreground", children: t('datatable.readOnly', { defaultValue: 'Solo lectura' }) })] }));
|
|
685
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "rounded-md border bg-muted/30 p-2", children: _jsx(CollectionCell, { value: value, itemFields: fieldItemFields(field), variant: "inline", locale: i18n.language, t: t, getImageUrl: editFieldImageUrl }) }), _jsx("p", { className: "text-[11px] text-muted-foreground", children: t('datatable.readOnly', { defaultValue: 'Solo lectura' }) })] }));
|
|
684
686
|
}
|
|
685
687
|
if (field.type === 'boolean') {
|
|
686
688
|
return (_jsxs("div", { className: "flex items-center gap-2 py-1", children: [_jsx(Switch, { checked: !!value, onCheckedChange: onChange }), _jsx("span", { className: "text-sm text-muted-foreground", children: value ? 'Sí' : 'No' })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAiC9C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AA0BD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,gBAAgB,EAAE,cAAc,MAAM,KAAG,MACzB,CAAA;AAQrD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAG,MAAM,GAAG,SAG5D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC7B,KAAK,gBAAgB,EACrB,OAAO,OAAO,EACd,WAAW,MAAM,EACjB,SAAS,MAAM,KAChB,MAyBF,CAAA;AA8DD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAqKD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2DAA4D,CAAA;AAExF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6C5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOtE,CAAA;AAsID;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAiC9C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AA0BD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,gBAAgB,EAAE,cAAc,MAAM,KAAG,MACzB,CAAA;AAQrD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAG,MAAM,GAAG,SAG5D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC7B,KAAK,gBAAgB,EACrB,OAAO,OAAO,EACd,WAAW,MAAM,EACjB,SAAS,MAAM,KAChB,MAyBF,CAAA;AA8DD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAqKD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2DAA4D,CAAA;AAExF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6C5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAOtE,CAAA;AAsID;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CA+nBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
|
package/dist/dynamic-columns.js
CHANGED
|
@@ -736,7 +736,7 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
|
|
|
736
736
|
}
|
|
737
737
|
default: {
|
|
738
738
|
if (typeof value === 'object' && value !== null) {
|
|
739
|
-
return (_jsx(CollectionCell, { value: value, locale: currentLanguage, t: t, itemFields: col.itemFields ?? col.item_fields }));
|
|
739
|
+
return (_jsx(CollectionCell, { value: value, locale: currentLanguage, t: t, itemFields: col.itemFields ?? col.item_fields, getImageUrl: getImageUrl }));
|
|
740
740
|
}
|
|
741
741
|
if (col.key === 'description' ||
|
|
742
742
|
col.key === 'features' ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "18.
|
|
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-
|
|
38
|
-
"@asteby/metacore-
|
|
37
|
+
"@asteby/metacore-ui": "^2.5.2",
|
|
38
|
+
"@asteby/metacore-sdk": "^3.2.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@tanstack/react-router": {
|
package/src/collection-cell.tsx
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
// mini-table — no per-addon config required, safe on any shape.
|
|
7
7
|
|
|
8
8
|
import * as React from 'react'
|
|
9
|
-
import { List } from 'lucide-react'
|
|
9
|
+
import { Box, List } from 'lucide-react'
|
|
10
10
|
import {
|
|
11
|
+
Avatar,
|
|
12
|
+
AvatarFallback,
|
|
13
|
+
AvatarImage,
|
|
11
14
|
Badge,
|
|
12
15
|
Popover,
|
|
13
16
|
PopoverContent,
|
|
@@ -20,8 +23,35 @@ import {
|
|
|
20
23
|
TableRow,
|
|
21
24
|
cn,
|
|
22
25
|
} from '@asteby/metacore-ui'
|
|
26
|
+
import { getInitials, relationChipStyles } from '@asteby/metacore-ui/lib'
|
|
23
27
|
import { humanizeToken } from './dynamic-columns-helpers'
|
|
24
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Tracks the host's dark-mode class on <html> so relation chips pick a tint
|
|
31
|
+
* tuned for the active theme. Replicated from dynamic-columns (kept local to
|
|
32
|
+
* avoid a cross-module import); mirror changes if that one evolves.
|
|
33
|
+
*/
|
|
34
|
+
function useIsDarkTheme(): boolean {
|
|
35
|
+
const [isDark, setIsDark] = React.useState(
|
|
36
|
+
() =>
|
|
37
|
+
typeof document !== 'undefined' &&
|
|
38
|
+
document.documentElement.classList.contains('dark')
|
|
39
|
+
)
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
if (typeof document === 'undefined') return
|
|
42
|
+
const sync = () =>
|
|
43
|
+
setIsDark(document.documentElement.classList.contains('dark'))
|
|
44
|
+
sync()
|
|
45
|
+
const observer = new MutationObserver(sync)
|
|
46
|
+
observer.observe(document.documentElement, {
|
|
47
|
+
attributes: true,
|
|
48
|
+
attributeFilter: ['class'],
|
|
49
|
+
})
|
|
50
|
+
return () => observer.disconnect()
|
|
51
|
+
}, [])
|
|
52
|
+
return isDark
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
const UUID_LIKE_RE =
|
|
26
56
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
27
57
|
|
|
@@ -56,31 +86,144 @@ function siblingKeyFor(key: string): string {
|
|
|
56
86
|
return key.endsWith('_id') ? key.slice(0, -3) : `${key}_label`
|
|
57
87
|
}
|
|
58
88
|
|
|
89
|
+
/** The backend-injected resolved sibling for a ref item-field. */
|
|
90
|
+
interface ResolvedRef {
|
|
91
|
+
label?: string
|
|
92
|
+
value?: unknown
|
|
93
|
+
/** Optional thumbnail (product photo, logo, avatar) resolved by the backend. */
|
|
94
|
+
image?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
59
97
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `formatScalar` (truncated uuid). Non-ref fields render `formatScalar(value)`.
|
|
98
|
+
* Reads the backend-injected resolved sibling for a `ref` item-field (the FK
|
|
99
|
+
* key without `_id`, else `<key>_label`). Returns the normalized `{label,
|
|
100
|
+
* image}` when present, a `{label}` for a bare-string sibling, or null when the
|
|
101
|
+
* ref is unresolved (so the caller falls back to the raw value).
|
|
65
102
|
*/
|
|
66
|
-
function
|
|
103
|
+
function resolvedRefFor(
|
|
67
104
|
field: ItemField,
|
|
68
105
|
row: Record<string, unknown>,
|
|
69
|
-
):
|
|
70
|
-
if (field.ref)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
106
|
+
): ResolvedRef | null {
|
|
107
|
+
if (!field.ref) return null
|
|
108
|
+
const sibling = row[siblingKeyFor(field.key)]
|
|
109
|
+
if (sibling && typeof sibling === 'object' && !Array.isArray(sibling)) {
|
|
110
|
+
const obj = sibling as Record<string, unknown>
|
|
111
|
+
const label = obj.label
|
|
112
|
+
if (label !== undefined && label !== null && label !== '') {
|
|
113
|
+
return {
|
|
114
|
+
label: String(label),
|
|
115
|
+
value: obj.value,
|
|
116
|
+
image:
|
|
117
|
+
typeof obj.image === 'string' && obj.image !== ''
|
|
118
|
+
? obj.image
|
|
119
|
+
: undefined,
|
|
76
120
|
}
|
|
77
|
-
} else if (typeof sibling === 'string' && sibling !== '') {
|
|
78
|
-
return sibling
|
|
79
121
|
}
|
|
122
|
+
} else if (typeof sibling === 'string' && sibling !== '') {
|
|
123
|
+
return { label: sibling }
|
|
80
124
|
}
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Plain-text value for one declared item-field — used for the no-JS popover
|
|
130
|
+
* `title` tooltip (which can't render JSX). Ref fields show the resolved label;
|
|
131
|
+
* everything else uses `formatScalar` (truncated uuid for unresolved ids).
|
|
132
|
+
*/
|
|
133
|
+
function itemFieldText(field: ItemField, row: Record<string, unknown>): string {
|
|
134
|
+
const ref = resolvedRefFor(field, row)
|
|
135
|
+
if (ref?.label) return ref.label
|
|
81
136
|
return formatScalar(row[field.key])
|
|
82
137
|
}
|
|
83
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
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Visual cell for one declared item-field. A resolved `ref` renders as a
|
|
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
|
|
207
|
+
* unresolved) fields render the plain scalar.
|
|
208
|
+
*/
|
|
209
|
+
function ItemFieldCell({
|
|
210
|
+
field,
|
|
211
|
+
row,
|
|
212
|
+
getImageUrl,
|
|
213
|
+
}: {
|
|
214
|
+
field: ItemField
|
|
215
|
+
row: Record<string, unknown>
|
|
216
|
+
getImageUrl?: (path: string) => string
|
|
217
|
+
}): React.ReactElement {
|
|
218
|
+
const ref = resolvedRefFor(field, row)
|
|
219
|
+
if (ref?.label) {
|
|
220
|
+
return (
|
|
221
|
+
<RefChip label={ref.label} image={ref.image} getImageUrl={getImageUrl} />
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
return <>{formatScalar(row[field.key])}</>
|
|
225
|
+
}
|
|
226
|
+
|
|
84
227
|
/** Normalize an org/UI language tag to a base language code (`es-MX` → `es`). */
|
|
85
228
|
function baseLang(locale?: string): string {
|
|
86
229
|
return (locale || 'en').toLowerCase().split('-')[0]
|
|
@@ -258,11 +401,13 @@ function MiniTable({
|
|
|
258
401
|
locale,
|
|
259
402
|
t,
|
|
260
403
|
itemFields,
|
|
404
|
+
getImageUrl,
|
|
261
405
|
}: {
|
|
262
406
|
rows: Record<string, unknown>[]
|
|
263
407
|
locale?: string
|
|
264
408
|
t?: Translate
|
|
265
409
|
itemFields?: ItemField[]
|
|
410
|
+
getImageUrl?: (path: string) => string
|
|
266
411
|
}) {
|
|
267
412
|
// Schema-driven path: a declared `item_fields` schema fixes the column
|
|
268
413
|
// order + headers (already localized by the backend, used VERBATIM) and
|
|
@@ -292,7 +437,11 @@ function MiniTable({
|
|
|
292
437
|
key={field.key}
|
|
293
438
|
className="text-xs whitespace-nowrap"
|
|
294
439
|
>
|
|
295
|
-
|
|
440
|
+
<ItemFieldCell
|
|
441
|
+
field={field}
|
|
442
|
+
row={row}
|
|
443
|
+
getImageUrl={getImageUrl}
|
|
444
|
+
/>
|
|
296
445
|
</TableCell>
|
|
297
446
|
))}
|
|
298
447
|
</TableRow>
|
|
@@ -302,7 +451,20 @@ function MiniTable({
|
|
|
302
451
|
)
|
|
303
452
|
}
|
|
304
453
|
|
|
305
|
-
|
|
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))
|
|
306
468
|
if (keys.length === 0) {
|
|
307
469
|
return <div className="p-3 text-xs text-muted-foreground">-</div>
|
|
308
470
|
}
|
|
@@ -320,14 +482,27 @@ function MiniTable({
|
|
|
320
482
|
<TableBody>
|
|
321
483
|
{rows.map((row, i) => (
|
|
322
484
|
<TableRow key={i}>
|
|
323
|
-
{keys.map((key) =>
|
|
324
|
-
|
|
325
|
-
key
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
+
})}
|
|
331
506
|
</TableRow>
|
|
332
507
|
))}
|
|
333
508
|
</TableBody>
|
|
@@ -421,6 +596,12 @@ export interface CollectionCellProps {
|
|
|
421
596
|
* behaviour is unchanged.
|
|
422
597
|
*/
|
|
423
598
|
itemFields?: ItemField[]
|
|
599
|
+
/**
|
|
600
|
+
* Resolves a stored image path to a displayable URL (same resolver the table
|
|
601
|
+
* columns use). Threaded to the ref-chip thumbnails so resolved line-item
|
|
602
|
+
* relations show a product photo / logo / avatar like the FK columns do.
|
|
603
|
+
*/
|
|
604
|
+
getImageUrl?: (path: string) => string
|
|
424
605
|
/**
|
|
425
606
|
* Presentation mode.
|
|
426
607
|
* - `'badge'` (default): the compact count/preview badge that opens a
|
|
@@ -448,6 +629,7 @@ export function CollectionCell({
|
|
|
448
629
|
locale,
|
|
449
630
|
t,
|
|
450
631
|
itemFields,
|
|
632
|
+
getImageUrl,
|
|
451
633
|
variant = 'badge',
|
|
452
634
|
}: CollectionCellProps) {
|
|
453
635
|
const parsed = parseValue(value)
|
|
@@ -491,6 +673,7 @@ export function CollectionCell({
|
|
|
491
673
|
locale={locale}
|
|
492
674
|
t={t}
|
|
493
675
|
itemFields={itemFields}
|
|
676
|
+
getImageUrl={getImageUrl}
|
|
494
677
|
/>
|
|
495
678
|
)
|
|
496
679
|
}
|
|
@@ -506,7 +689,7 @@ export function CollectionCell({
|
|
|
506
689
|
itemFields!
|
|
507
690
|
.map(
|
|
508
691
|
(field) =>
|
|
509
|
-
`${field.label}: ${
|
|
692
|
+
`${field.label}: ${itemFieldText(field, row)}`
|
|
510
693
|
)
|
|
511
694
|
.join(', ')
|
|
512
695
|
)
|
|
@@ -528,6 +711,7 @@ export function CollectionCell({
|
|
|
528
711
|
locale={locale}
|
|
529
712
|
t={t}
|
|
530
713
|
itemFields={itemFields}
|
|
714
|
+
getImageUrl={getImageUrl}
|
|
531
715
|
/>
|
|
532
716
|
</PopoverShell>
|
|
533
717
|
)
|
|
@@ -1182,6 +1182,7 @@ function StructuredViewValue({
|
|
|
1182
1182
|
locale?: string
|
|
1183
1183
|
t?: (key: string, options?: any) => string
|
|
1184
1184
|
}) {
|
|
1185
|
+
const getImageUrl = useContext(ImageUrlContext)
|
|
1185
1186
|
const isEmpty =
|
|
1186
1187
|
value === null ||
|
|
1187
1188
|
value === undefined ||
|
|
@@ -1201,6 +1202,7 @@ function StructuredViewValue({
|
|
|
1201
1202
|
variant="inline"
|
|
1202
1203
|
locale={locale}
|
|
1203
1204
|
t={t}
|
|
1205
|
+
getImageUrl={getImageUrl}
|
|
1204
1206
|
/>
|
|
1205
1207
|
</div>
|
|
1206
1208
|
)
|
|
@@ -1214,6 +1216,7 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1214
1216
|
record?: any
|
|
1215
1217
|
}) {
|
|
1216
1218
|
const { t, i18n } = useTranslation()
|
|
1219
|
+
const editFieldImageUrl = useContext(ImageUrlContext)
|
|
1217
1220
|
|
|
1218
1221
|
// Jsonb line-items columns (e.g. Transfer.items) are action-built documents:
|
|
1219
1222
|
// editing the array field-by-field is out of scope. Render them READ-ONLY
|
|
@@ -1229,6 +1232,7 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1229
1232
|
variant="inline"
|
|
1230
1233
|
locale={i18n.language}
|
|
1231
1234
|
t={t}
|
|
1235
|
+
getImageUrl={editFieldImageUrl}
|
|
1232
1236
|
/>
|
|
1233
1237
|
</div>
|
|
1234
1238
|
<p className="text-[11px] text-muted-foreground">
|