@asteby/metacore-runtime-react 30.0.4 → 30.0.6
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 +14 -0
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +12 -2
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +6 -8
- package/package.json +3 -3
- package/src/dialogs/dynamic-record.tsx +15 -0
- package/src/dynamic-select-field.tsx +6 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 30.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c8b6269: DynamicSelect: no anidar "Crear X" en self-FK (p. ej. empresa matriz) y vacío del picker más claro al abrir.
|
|
8
|
+
|
|
9
|
+
## 30.0.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 9604dc4: fix(runtime): OptionThumb cae a iniciales si el avatar 404 (no caja invisible)
|
|
14
|
+
- Updated dependencies [a0c3e39]
|
|
15
|
+
- @asteby/metacore-ui@2.14.3
|
|
16
|
+
|
|
3
17
|
## 30.0.4
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
// SDK stays transport- and host-agnostic.
|
|
13
13
|
import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
|
14
14
|
import { useTranslation } from 'react-i18next';
|
|
15
|
+
/** Model key of the open create/edit dialog — used to hide "+" on self-FK pickers
|
|
16
|
+
* (e.g. Customer.parent_id) so they don't nest another "Crear Cliente" modal. */
|
|
17
|
+
const RecordDialogModelContext = createContext(undefined);
|
|
15
18
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Button, Input, Textarea, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Skeleton, Badge, Popover, PopoverContent, PopoverTrigger, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Calendar, } from '@asteby/metacore-ui/primitives';
|
|
16
19
|
import { cn } from '@asteby/metacore-ui/lib';
|
|
17
20
|
import { toast } from 'sonner';
|
|
@@ -648,7 +651,7 @@ export function DynamicRecordDialog({ open, onOpenChange, mode, model, recordId,
|
|
|
648
651
|
setStepIndex(Math.min(clampedStep + 1, groups.length - 1));
|
|
649
652
|
};
|
|
650
653
|
const goBackStep = () => setStepIndex(Math.max(clampedStep - 1, 0));
|
|
651
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-2xl max-h-[90dvh] flex flex-col p-0 gap-0 overflow-hidden", style: { maxHeight: '90dvh' }, children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: config.description })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: loading ? (_jsx(LoadingSkeleton, {})) : modalMeta ? (_jsx(ModelContext.Provider, { value: model, children: _jsx(ImageUrlContext.Provider, { value: getImageUrl, children: _jsx(TimeZoneContext.Provider, { value: timeZone, children: _jsxs(CurrencyContext.Provider, { value: currency, children: [_jsxs("form", { id: "dynamic-record-form", onSubmit: handleSubmit, className: "grid gap-y-4", children: [isSteps && (_jsx(WizardProgress, { groups: groups, stepIndex: clampedStep })), (isSteps ? [groups[clampedStep]] : groups).map(group => (_jsx(FieldSection, { group: group, children: _jsx("div", { className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: renderFields(group.fields) }) }, group.key))), record?.external_url && (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2", children: _jsx("div", { className: "sm:col-span-2 min-w-0", children: _jsxs("a", { href: record.external_url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Ver en ", record.external_provider ?? 'proveedor externo'] }) }) }))] }), !isCreate && record && relations.length > 0 && (_jsx("div", { className: "mt-6", children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true, canCreate: mode === 'edit', canEdit: mode === 'edit', canDelete: mode === 'edit', onChange: handleChildChange }) }))] }) }) }) })) : null }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0 sm:justify-between", children: [isView && onOpenFullPage ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground", onClick: () => { onOpenChange(false); onOpenFullPage(); }, children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), "Ver p\u00E1gina completa"] })) : _jsx("span", {}), _jsxs("div", { className: "flex items-center gap-2", children: [isSteps && clampedStep > 0 ? (_jsx(Button, { variant: "outline", onClick: goBackStep, disabled: saving || deleting, children: "Anterior" })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || deleting, children: config.cancelLabel })), isView && onDelete && (_jsxs(Button, { variant: "destructive", onClick: handleDelete, disabled: deleting || loading, children: [deleting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), deleting ? 'Eliminando...' : 'Eliminar'] })), isView && onEdit && (_jsx(Button, { onClick: onEdit, disabled: deleting || loading, children: "Editar" })), isEditable && isSteps && !isLastStep && (_jsx(Button, { type: "button", onClick: goNextStep, disabled: saving || loading, children: "Siguiente" })), isEditable && (!isSteps || isLastStep) && (_jsxs(Button, { type: "submit", form: "dynamic-record-form", disabled: saving || loading, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), saving ? config.submittingLabel : config.submitLabel] }))] })] })] }) }));
|
|
654
|
+
return (_jsx(RecordDialogModelContext.Provider, { value: model, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-2xl max-h-[90dvh] flex flex-col p-0 gap-0 overflow-hidden", style: { maxHeight: '90dvh' }, children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: config.description })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: loading ? (_jsx(LoadingSkeleton, {})) : modalMeta ? (_jsx(ModelContext.Provider, { value: model, children: _jsx(ImageUrlContext.Provider, { value: getImageUrl, children: _jsx(TimeZoneContext.Provider, { value: timeZone, children: _jsxs(CurrencyContext.Provider, { value: currency, children: [_jsxs("form", { id: "dynamic-record-form", onSubmit: handleSubmit, className: "grid gap-y-4", children: [isSteps && (_jsx(WizardProgress, { groups: groups, stepIndex: clampedStep })), (isSteps ? [groups[clampedStep]] : groups).map(group => (_jsx(FieldSection, { group: group, children: _jsx("div", { className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: renderFields(group.fields) }) }, group.key))), record?.external_url && (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2", children: _jsx("div", { className: "sm:col-span-2 min-w-0", children: _jsxs("a", { href: record.external_url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Ver en ", record.external_provider ?? 'proveedor externo'] }) }) }))] }), !isCreate && record && relations.length > 0 && (_jsx("div", { className: "mt-6", children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true, canCreate: mode === 'edit', canEdit: mode === 'edit', canDelete: mode === 'edit', onChange: handleChildChange }) }))] }) }) }) })) : null }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0 sm:justify-between", children: [isView && onOpenFullPage ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground", onClick: () => { onOpenChange(false); onOpenFullPage(); }, children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), "Ver p\u00E1gina completa"] })) : _jsx("span", {}), _jsxs("div", { className: "flex items-center gap-2", children: [isSteps && clampedStep > 0 ? (_jsx(Button, { variant: "outline", onClick: goBackStep, disabled: saving || deleting, children: "Anterior" })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || deleting, children: config.cancelLabel })), isView && onDelete && (_jsxs(Button, { variant: "destructive", onClick: handleDelete, disabled: deleting || loading, children: [deleting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), deleting ? 'Eliminando...' : 'Eliminar'] })), isView && onEdit && (_jsx(Button, { onClick: onEdit, disabled: deleting || loading, children: "Editar" })), isEditable && isSteps && !isLastStep && (_jsx(Button, { type: "button", onClick: goNextStep, disabled: saving || loading, children: "Siguiente" })), isEditable && (!isSteps || isLastStep) && (_jsxs(Button, { type: "submit", form: "dynamic-record-form", disabled: saving || loading, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), saving ? config.submittingLabel : config.submitLabel] }))] })] })] }) }) }));
|
|
652
655
|
}
|
|
653
656
|
function LoadingSkeleton() {
|
|
654
657
|
return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Skeleton, { className: "h-3.5 w-24" }), _jsx(Skeleton, { className: "h-9 w-full" })] }, i))) }));
|
|
@@ -953,6 +956,7 @@ function StructuredViewValue({ value, field, locale, t, }) {
|
|
|
953
956
|
export function EditField({ field, value, onChange, record }) {
|
|
954
957
|
const { t, i18n } = useTranslation();
|
|
955
958
|
const editFieldImageUrl = useContext(ImageUrlContext);
|
|
959
|
+
const dialogModel = useContext(RecordDialogModelContext);
|
|
956
960
|
// Jsonb line-items columns (e.g. Transfer.items) are action-built documents:
|
|
957
961
|
// editing the array field-by-field is out of scope. Render them READ-ONLY
|
|
958
962
|
// with the same inline table the detail view uses — a localized, ref-resolved
|
|
@@ -994,12 +998,18 @@ export function EditField({ field, value, onChange, record }) {
|
|
|
994
998
|
field.type === 'dynamic_select' ||
|
|
995
999
|
field.widget === 'dynamic_select') &&
|
|
996
1000
|
!field.options?.length) {
|
|
1001
|
+
const ref = getFieldRef(field);
|
|
1002
|
+
// Self-FK (Customer.parent_id → Customer): nesting another "Crear Cliente"
|
|
1003
|
+
// on top of the open create dialog confuses operators — select only.
|
|
1004
|
+
const hideSelfCreate = !!dialogModel &&
|
|
1005
|
+
!!ref &&
|
|
1006
|
+
dialogModel.replace(/_/g, '').toLowerCase() === ref.replace(/_/g, '').toLowerCase();
|
|
997
1007
|
return (_jsx(DynamicSelectField, { field: field, value: value, onChange: onChange,
|
|
998
1008
|
// Seed the trigger with the related record's NAME (from the
|
|
999
1009
|
// backend-injected FK sibling, key without `_id`) so an existing
|
|
1000
1010
|
// selection shows the label, not the raw uuid — without waiting
|
|
1001
1011
|
// for the popover to open and fetch a page.
|
|
1002
|
-
seedOption: fkSeedOption(field, value, record) }));
|
|
1012
|
+
seedOption: fkSeedOption(field, value, record), hideCreate: hideSelfCreate }));
|
|
1003
1013
|
}
|
|
1004
1014
|
if (field.type === 'search' && field.searchEndpoint) {
|
|
1005
1015
|
return _jsx(SearchField, { field: field, value: value, onChange: onChange });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AA0CA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,IAAS,GACZ,EAAE;IACC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,+
|
|
1
|
+
{"version":3,"file":"dynamic-select-field.d.ts","sourceRoot":"","sources":["../src/dynamic-select-field.tsx"],"names":[],"mappings":"AA0CA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,gDAAgD,CAAA;AAEjF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,IAAS,GACZ,EAAE;IACC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,+BA4BA;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACvB,MAAM,EACN,IAAS,GACZ,EAAE;IACC,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,sCA4BA;AAWD,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;IAC1B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAAA;IAChD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAgB,EAChB,aAAoB,EACpB,kBAA0B,EAC1B,UAAkB,GACrB,EAAE,uBAAuB,+BA8SzB;AAED,eAAe,kBAAkB,CAAA"}
|
|
@@ -46,8 +46,11 @@ export const DEFAULT_DEPENDS_HINT = 'Selecciona primero el campo del que depende
|
|
|
46
46
|
* federated addon don't always survive the host's class scan.
|
|
47
47
|
*/
|
|
48
48
|
export function OptionThumb({ image, name, size = 20, }) {
|
|
49
|
+
const [broken, setBroken] = useState(false);
|
|
49
50
|
const box = { width: size, height: size };
|
|
50
|
-
|
|
51
|
+
// Missing or 404 image (bare avatar filenames often 404) → initials, never
|
|
52
|
+
// an invisible box that looks like "no avatar".
|
|
53
|
+
if (!image || broken) {
|
|
51
54
|
return _jsx(InitialsAvatar, { name: name, size: size, rounded: "sm", tone: "neutral" });
|
|
52
55
|
}
|
|
53
56
|
// A lucide icon name stored where an image url/path is expected (the `icon`
|
|
@@ -55,12 +58,7 @@ export function OptionThumb({ image, name, size = 20, }) {
|
|
|
55
58
|
if (isLucideIconName(image)) {
|
|
56
59
|
return (_jsx("span", { className: "flex shrink-0 items-center justify-center rounded-sm bg-muted", style: box, "aria-hidden": true, children: _jsx(DynamicIcon, { name: image, className: "size-4" }) }));
|
|
57
60
|
}
|
|
58
|
-
return (_jsx("img", { src: image, alt: "", "aria-hidden": true, loading: "lazy", className: "shrink-0 rounded-sm object-cover", style: box,
|
|
59
|
-
// A broken image url shouldn't leave a torn-icon glyph; collapse to
|
|
60
|
-
// the neutral placeholder background instead.
|
|
61
|
-
onError: (e) => {
|
|
62
|
-
e.currentTarget.style.visibility = 'hidden';
|
|
63
|
-
} }));
|
|
61
|
+
return (_jsx("img", { src: image, alt: "", "aria-hidden": true, loading: "lazy", className: "shrink-0 rounded-sm object-cover", style: box, onError: () => setBroken(true) }));
|
|
64
62
|
}
|
|
65
63
|
/**
|
|
66
64
|
* Leading visual for an option: a photo thumbnail (FK relations with an image),
|
|
@@ -230,7 +228,7 @@ export function DynamicSelectField({ field, value, onChange, seedOption, depends
|
|
|
230
228
|
: 'Sin opciones'
|
|
231
229
|
: debounced
|
|
232
230
|
? 'Sin resultados'
|
|
233
|
-
: '
|
|
231
|
+
: 'Sin resultados — escribe para filtrar' })), !loading && options.length > 0 && (_jsx(CommandGroup, { className: "max-h-64 overflow-auto", children: options.map((opt) => {
|
|
234
232
|
const isSel = String(opt.id) === String(value);
|
|
235
233
|
return (_jsxs(CommandItem, { value: String(opt.id), onSelect: () => handlePick(opt), children: [_jsx(Check, { className: 'mr-2 size-4 shrink-0 ' + (isSel ? 'opacity-100' : 'opacity-0') }), _jsx(OptionLead, { option: opt, size: 24 }), descriptionAsBadge ? (_jsxs("div", { className: "ml-2 flex min-w-0 flex-1 items-center gap-2", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: opt.label }), opt.description ? (_jsx(Badge, { variant: "secondary", className: "shrink-0 font-normal tabular-nums", children: opt.description })) : null] })) : (_jsxs("div", { className: "ml-2 flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate", children: opt.label }), opt.description && (_jsx("span", { className: "text-muted-foreground truncate text-xs", children: opt.description }))] }))] }, String(opt.id)));
|
|
236
234
|
}) }))] })] }) })] }), scanEnabled && (_jsx(Button, { type: "button", variant: "outline", size: "icon", className: "size-9 shrink-0", onClick: () => setScanOpen(true), title: "Escanear con la c\u00E1mara", "aria-label": "Escanear c\u00F3digo de barras con la c\u00E1mara", children: _jsx(ScanLine, { className: "size-4" }) })), fieldRef && !hideCreate && !useStatic && (_jsx(Button, { type: "button", variant: "outline", size: "icon", className: "size-9 shrink-0", onClick: openCreate, title: `Crear ${field.label ?? fieldRef}`, "aria-label": `Crear ${field.label ?? fieldRef}`, children: _jsx(Plus, { className: "size-4" }) })), scanEnabled && (_jsx(BarcodeScanner, { open: scanOpen, onClose: () => setScanOpen(false), onDetected: handleScanDetected, continuous: false, position: "fixed", title: `Escanear ${field.label ?? ''}`.trim() }))] }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "30.0.
|
|
3
|
+
"version": "30.0.6",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"sonner": ">=1.7",
|
|
39
39
|
"zustand": ">=5",
|
|
40
40
|
"@asteby/metacore-sdk": "^3.4.0",
|
|
41
|
-
"@asteby/metacore-ui": "^2.14.
|
|
41
|
+
"@asteby/metacore-ui": "^2.14.3"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@tanstack/react-router": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "^4.0.0",
|
|
69
69
|
"zustand": "^5.0.0",
|
|
70
70
|
"@asteby/metacore-sdk": "3.4.0",
|
|
71
|
-
"@asteby/metacore-ui": "2.14.
|
|
71
|
+
"@asteby/metacore-ui": "2.14.3"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsc -p tsconfig.json",
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'
|
|
13
13
|
import { useTranslation } from 'react-i18next'
|
|
14
14
|
import type { ModelSchema } from './types'
|
|
15
|
+
|
|
16
|
+
/** Model key of the open create/edit dialog — used to hide "+" on self-FK pickers
|
|
17
|
+
* (e.g. Customer.parent_id) so they don't nest another "Crear Cliente" modal. */
|
|
18
|
+
const RecordDialogModelContext = createContext<string | undefined>(undefined)
|
|
15
19
|
import {
|
|
16
20
|
Dialog,
|
|
17
21
|
DialogContent,
|
|
@@ -951,6 +955,7 @@ export function DynamicRecordDialog({
|
|
|
951
955
|
const goBackStep = () => setStepIndex(Math.max(clampedStep - 1, 0))
|
|
952
956
|
|
|
953
957
|
return (
|
|
958
|
+
<RecordDialogModelContext.Provider value={model}>
|
|
954
959
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
955
960
|
<DialogContent className="sm:max-w-2xl max-h-[90dvh] flex flex-col p-0 gap-0 overflow-hidden" style={{ maxHeight: '90dvh' }}>
|
|
956
961
|
<DialogHeader className="p-6 pb-4 border-b shrink-0">
|
|
@@ -1095,6 +1100,7 @@ export function DynamicRecordDialog({
|
|
|
1095
1100
|
</DialogFooter>
|
|
1096
1101
|
</DialogContent>
|
|
1097
1102
|
</Dialog>
|
|
1103
|
+
</RecordDialogModelContext.Provider>
|
|
1098
1104
|
)
|
|
1099
1105
|
}
|
|
1100
1106
|
|
|
@@ -1653,6 +1659,7 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1653
1659
|
}) {
|
|
1654
1660
|
const { t, i18n } = useTranslation()
|
|
1655
1661
|
const editFieldImageUrl = useContext(ImageUrlContext)
|
|
1662
|
+
const dialogModel = useContext(RecordDialogModelContext)
|
|
1656
1663
|
|
|
1657
1664
|
// Jsonb line-items columns (e.g. Transfer.items) are action-built documents:
|
|
1658
1665
|
// editing the array field-by-field is out of scope. Render them READ-ONLY
|
|
@@ -1741,6 +1748,13 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1741
1748
|
field.widget === 'dynamic_select') &&
|
|
1742
1749
|
!field.options?.length
|
|
1743
1750
|
) {
|
|
1751
|
+
const ref = getFieldRef(field as ActionFieldDef)
|
|
1752
|
+
// Self-FK (Customer.parent_id → Customer): nesting another "Crear Cliente"
|
|
1753
|
+
// on top of the open create dialog confuses operators — select only.
|
|
1754
|
+
const hideSelfCreate =
|
|
1755
|
+
!!dialogModel &&
|
|
1756
|
+
!!ref &&
|
|
1757
|
+
dialogModel.replace(/_/g, '').toLowerCase() === ref.replace(/_/g, '').toLowerCase()
|
|
1744
1758
|
return (
|
|
1745
1759
|
<DynamicSelectField
|
|
1746
1760
|
field={field as ActionFieldDef}
|
|
@@ -1751,6 +1765,7 @@ export function EditField({ field, value, onChange, record }: {
|
|
|
1751
1765
|
// selection shows the label, not the raw uuid — without waiting
|
|
1752
1766
|
// for the popover to open and fetch a page.
|
|
1753
1767
|
seedOption={fkSeedOption(field, value, record)}
|
|
1768
|
+
hideCreate={hideSelfCreate}
|
|
1754
1769
|
/>
|
|
1755
1770
|
)
|
|
1756
1771
|
}
|
|
@@ -69,8 +69,11 @@ export function OptionThumb({
|
|
|
69
69
|
name?: string | null
|
|
70
70
|
size?: number
|
|
71
71
|
}) {
|
|
72
|
+
const [broken, setBroken] = useState(false)
|
|
72
73
|
const box = { width: size, height: size }
|
|
73
|
-
|
|
74
|
+
// Missing or 404 image (bare avatar filenames often 404) → initials, never
|
|
75
|
+
// an invisible box that looks like "no avatar".
|
|
76
|
+
if (!image || broken) {
|
|
74
77
|
return <InitialsAvatar name={name} size={size} rounded="sm" tone="neutral" />
|
|
75
78
|
}
|
|
76
79
|
// A lucide icon name stored where an image url/path is expected (the `icon`
|
|
@@ -90,11 +93,7 @@ export function OptionThumb({
|
|
|
90
93
|
loading="lazy"
|
|
91
94
|
className="shrink-0 rounded-sm object-cover"
|
|
92
95
|
style={box}
|
|
93
|
-
|
|
94
|
-
// the neutral placeholder background instead.
|
|
95
|
-
onError={(e) => {
|
|
96
|
-
e.currentTarget.style.visibility = 'hidden'
|
|
97
|
-
}}
|
|
96
|
+
onError={() => setBroken(true)}
|
|
98
97
|
/>
|
|
99
98
|
)
|
|
100
99
|
}
|
|
@@ -430,7 +429,7 @@ export function DynamicSelectField({
|
|
|
430
429
|
: 'Sin opciones'
|
|
431
430
|
: debounced
|
|
432
431
|
? 'Sin resultados'
|
|
433
|
-
: '
|
|
432
|
+
: 'Sin resultados — escribe para filtrar'}
|
|
434
433
|
</CommandEmpty>
|
|
435
434
|
)}
|
|
436
435
|
{!loading && options.length > 0 && (
|