@asteby/metacore-runtime-react 18.28.2 → 18.28.3
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,11 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 18.28.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e5e9fca: feat(action-modal): a field-action modal now renders the acted-on record's related-lists (model metadata.relations) below the form, as read-only context — e.g. the reception history of a transfer shown right inside the "Recibir" modal. Create actions (no record) render nothing; reuses the same DynamicRelations the detail view uses.
|
|
8
|
+
|
|
3
9
|
## 18.28.2
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAgDA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AA0FhD,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAiDlB"}
|
|
@@ -15,6 +15,7 @@ import { toast } from 'sonner';
|
|
|
15
15
|
import { useApi } from './api-context';
|
|
16
16
|
import { DynamicIcon } from './dynamic-icon';
|
|
17
17
|
import { DynamicLineItems } from './dynamic-line-items';
|
|
18
|
+
import { DynamicRelations } from './dynamic-relations';
|
|
18
19
|
import { DynamicSelectField } from './dynamic-select-field';
|
|
19
20
|
import { DynamicDateField } from './dynamic-date-field';
|
|
20
21
|
import { UploadField } from './upload-field';
|
|
@@ -137,6 +138,34 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
137
138
|
const api = useApi();
|
|
138
139
|
const [formData, setFormData] = useState({});
|
|
139
140
|
const [executing, setExecuting] = useState(false);
|
|
141
|
+
// Related records to surface BELOW the form, as read-only context for the
|
|
142
|
+
// record being acted on — e.g. the reception history of a transfer while
|
|
143
|
+
// receiving against it. Sourced from the model's metadata.relations (the
|
|
144
|
+
// same declarative relations the detail view renders). Only row actions on a
|
|
145
|
+
// real record show them; create actions (no record.id) render nothing.
|
|
146
|
+
const [relations, setRelations] = useState([]);
|
|
147
|
+
const recordId = record?.id;
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
let cancelled = false;
|
|
150
|
+
if (!open || recordId == null) {
|
|
151
|
+
setRelations([]);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
api.get(`/metadata/table/${model}`)
|
|
155
|
+
.then((res) => {
|
|
156
|
+
if (cancelled)
|
|
157
|
+
return;
|
|
158
|
+
const rels = res?.data?.relations ?? res?.data?.data?.relations ?? [];
|
|
159
|
+
setRelations(Array.isArray(rels) ? rels : []);
|
|
160
|
+
})
|
|
161
|
+
.catch(() => {
|
|
162
|
+
if (!cancelled)
|
|
163
|
+
setRelations([]);
|
|
164
|
+
});
|
|
165
|
+
return () => {
|
|
166
|
+
cancelled = true;
|
|
167
|
+
};
|
|
168
|
+
}, [open, model, recordId, api]);
|
|
140
169
|
useEffect(() => {
|
|
141
170
|
if (open && action.fields) {
|
|
142
171
|
const defaults = {};
|
|
@@ -209,12 +238,12 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
209
238
|
: hasLineItems
|
|
210
239
|
? '820px'
|
|
211
240
|
: undefined;
|
|
212
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90vh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-lg'), style: { maxHeight: '90vh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), action.label] }), action.confirmMessage && _jsx(DialogDescription, { children: action.confirmMessage })] }),
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
241
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90vh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-lg'), style: { maxHeight: '90vh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), action.label] }), action.confirmMessage && _jsx(DialogDescription, { children: action.confirmMessage })] }), _jsxs("div", { className: "-mx-1 grid min-h-0 flex-1 gap-4 overflow-y-auto px-1 py-4 sm:grid-cols-2", children: [action.fields?.map((field) => {
|
|
242
|
+
const fullWidth = isLineItemsField(field) ||
|
|
243
|
+
resolveWidget(field) === 'textarea' ||
|
|
244
|
+
resolveWidget(field) === 'richtext';
|
|
245
|
+
return (_jsxs("div", { className: 'grid gap-2 ' + (fullWidth ? 'sm:col-span-2' : ''), children: [_jsxs(Label, { htmlFor: field.key, children: [field.label, field.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData)] }, field.key));
|
|
246
|
+
}), relations.length > 0 && (_jsx("div", { className: "sm:col-span-2", children: _jsx(DynamicRelations, { record: record, relations: relations }) }))] }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), action.label] })] })] }) }));
|
|
218
247
|
}
|
|
219
248
|
function renderField(field, value, onChange,
|
|
220
249
|
// Full current form values — lets a line-items grid (and any cascading
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "18.28.
|
|
3
|
+
"version": "18.28.3",
|
|
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": {
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"typescript": "^6.0.0",
|
|
65
65
|
"vitest": "^4.0.0",
|
|
66
66
|
"zustand": "^5.0.0",
|
|
67
|
-
"@asteby/metacore-
|
|
68
|
-
"@asteby/metacore-
|
|
67
|
+
"@asteby/metacore-ui": "2.5.2",
|
|
68
|
+
"@asteby/metacore-sdk": "3.2.0"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "tsc -p tsconfig.json",
|
|
@@ -39,6 +39,7 @@ import { toast } from 'sonner'
|
|
|
39
39
|
import { useApi } from './api-context'
|
|
40
40
|
import { DynamicIcon } from './dynamic-icon'
|
|
41
41
|
import { DynamicLineItems } from './dynamic-line-items'
|
|
42
|
+
import { DynamicRelations } from './dynamic-relations'
|
|
42
43
|
import { DynamicSelectField } from './dynamic-select-field'
|
|
43
44
|
import { DynamicDateField } from './dynamic-date-field'
|
|
44
45
|
import { UploadField } from './upload-field'
|
|
@@ -270,6 +271,32 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
270
271
|
const api = useApi()
|
|
271
272
|
const [formData, setFormData] = useState<Record<string, any>>({})
|
|
272
273
|
const [executing, setExecuting] = useState(false)
|
|
274
|
+
// Related records to surface BELOW the form, as read-only context for the
|
|
275
|
+
// record being acted on — e.g. the reception history of a transfer while
|
|
276
|
+
// receiving against it. Sourced from the model's metadata.relations (the
|
|
277
|
+
// same declarative relations the detail view renders). Only row actions on a
|
|
278
|
+
// real record show them; create actions (no record.id) render nothing.
|
|
279
|
+
const [relations, setRelations] = useState<any[]>([])
|
|
280
|
+
const recordId = record?.id
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
let cancelled = false
|
|
283
|
+
if (!open || recordId == null) {
|
|
284
|
+
setRelations([])
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
api.get(`/metadata/table/${model}`)
|
|
288
|
+
.then((res) => {
|
|
289
|
+
if (cancelled) return
|
|
290
|
+
const rels = res?.data?.relations ?? res?.data?.data?.relations ?? []
|
|
291
|
+
setRelations(Array.isArray(rels) ? rels : [])
|
|
292
|
+
})
|
|
293
|
+
.catch(() => {
|
|
294
|
+
if (!cancelled) setRelations([])
|
|
295
|
+
})
|
|
296
|
+
return () => {
|
|
297
|
+
cancelled = true
|
|
298
|
+
}
|
|
299
|
+
}, [open, model, recordId, api])
|
|
273
300
|
|
|
274
301
|
useEffect(() => {
|
|
275
302
|
if (open && action.fields) {
|
|
@@ -389,6 +416,11 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
389
416
|
</div>
|
|
390
417
|
)
|
|
391
418
|
})}
|
|
419
|
+
{relations.length > 0 && (
|
|
420
|
+
<div className="sm:col-span-2">
|
|
421
|
+
<DynamicRelations record={record} relations={relations} />
|
|
422
|
+
</div>
|
|
423
|
+
)}
|
|
392
424
|
</div>
|
|
393
425
|
<DialogFooter className="shrink-0">
|
|
394
426
|
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={executing}>
|