@asteby/metacore-runtime-react 25.1.3 → 25.1.5
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 +22 -0
- package/dist/action-modal-dispatcher.d.ts.map +1 -1
- package/dist/action-modal-dispatcher.js +76 -13
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +58 -8
- package/dist/dialogs/normalize-submit.d.ts +17 -4
- package/dist/dialogs/normalize-submit.d.ts.map +1 -1
- package/dist/dialogs/normalize-submit.js +43 -10
- package/dist/server-error.d.ts +26 -1
- package/dist/server-error.d.ts.map +1 -1
- package/dist/server-error.js +66 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/extract-field-errors.test.ts +102 -0
- package/src/__tests__/normalize-ref-fields.test.ts +45 -0
- package/src/action-modal-dispatcher.tsx +84 -12
- package/src/dialogs/dynamic-record.tsx +65 -8
- package/src/dialogs/normalize-submit.ts +46 -17
- package/src/server-error.ts +87 -2
- package/src/types.ts +8 -0
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "25.1.
|
|
3
|
+
"version": "25.1.5",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"typescript": "^6.0.0",
|
|
68
68
|
"vitest": "^4.0.0",
|
|
69
69
|
"zustand": "^5.0.0",
|
|
70
|
-
"@asteby/metacore-
|
|
71
|
-
"@asteby/metacore-
|
|
70
|
+
"@asteby/metacore-sdk": "3.3.0",
|
|
71
|
+
"@asteby/metacore-ui": "2.10.1"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsc -p tsconfig.json",
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { extractFieldErrors, localizeFieldIssue, type FieldIssue } from '../server-error'
|
|
3
|
+
|
|
4
|
+
// A minimal i18next-like translator: honors defaultValue and does {{var}}
|
|
5
|
+
// interpolation, so the tests assert BOTH the resolved Spanish default and that
|
|
6
|
+
// the field label / code params are interpolated.
|
|
7
|
+
const t = (_key: string, opts?: { defaultValue?: string; [k: string]: unknown }) => {
|
|
8
|
+
let out = opts?.defaultValue ?? _key
|
|
9
|
+
if (opts) {
|
|
10
|
+
for (const [k, v] of Object.entries(opts)) {
|
|
11
|
+
if (k === 'defaultValue') continue
|
|
12
|
+
out = out.replace(new RegExp(`{{\\s*${k}\\s*}}`, 'g'), String(v))
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return out
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('extractFieldErrors', () => {
|
|
19
|
+
it('normalizes object entries {code,params} from an axios error', () => {
|
|
20
|
+
const err = {
|
|
21
|
+
response: {
|
|
22
|
+
data: {
|
|
23
|
+
success: false,
|
|
24
|
+
message: 'validation failed',
|
|
25
|
+
errors: { name: [{ code: 'required', params: {} }], sku: [{ code: 'duplicate' }] },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
expect(extractFieldErrors(err)).toEqual({
|
|
30
|
+
name: [{ code: 'required', params: {} }],
|
|
31
|
+
sku: [{ code: 'duplicate', params: undefined }],
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('normalizes string entries to {message}', () => {
|
|
36
|
+
const err = { errors: { name: ['El nombre es obligatorio'], sku: 'Duplicado' } }
|
|
37
|
+
expect(extractFieldErrors(err)).toEqual({
|
|
38
|
+
name: [{ message: 'El nombre es obligatorio' }],
|
|
39
|
+
sku: [{ message: 'Duplicado' }],
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('accepts a bare response body as well as an axios error', () => {
|
|
44
|
+
const bare = { errors: { qty: [{ code: 'invalid_type', params: { expected: 'number' } }] } }
|
|
45
|
+
expect(extractFieldErrors(bare)).toEqual({
|
|
46
|
+
qty: [{ code: 'invalid_type', params: { expected: 'number' } }],
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('carries code params through (allowed / ref)', () => {
|
|
51
|
+
const err = {
|
|
52
|
+
errors: {
|
|
53
|
+
status: [{ code: 'invalid_option', params: { allowed: ['a', 'b'] } }],
|
|
54
|
+
owner: [{ code: 'not_found', params: { ref: 'users' } }],
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
expect(extractFieldErrors(err)).toEqual({
|
|
58
|
+
status: [{ code: 'invalid_option', params: { allowed: ['a', 'b'] } }],
|
|
59
|
+
owner: [{ code: 'not_found', params: { ref: 'users' } }],
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('returns undefined when there is no usable errors map', () => {
|
|
64
|
+
expect(extractFieldErrors(undefined)).toBeUndefined()
|
|
65
|
+
expect(extractFieldErrors({ response: { data: { message: 'boom' } } })).toBeUndefined()
|
|
66
|
+
expect(extractFieldErrors({ errors: 'oops' })).toBeUndefined() // string, not a map
|
|
67
|
+
expect(extractFieldErrors({ errors: ['a', 'b'] })).toBeUndefined() // array, not a map
|
|
68
|
+
expect(extractFieldErrors({ errors: {} })).toBeUndefined() // empty
|
|
69
|
+
expect(extractFieldErrors({ errors: { x: [null, ''] } })).toBeUndefined() // nothing normalizes
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('localizeFieldIssue', () => {
|
|
74
|
+
it('required → interpolates label', () => {
|
|
75
|
+
expect(localizeFieldIssue({ code: 'required' }, 'Nombre', t)).toBe('El campo Nombre es obligatorio')
|
|
76
|
+
})
|
|
77
|
+
it('invalid_option', () => {
|
|
78
|
+
expect(localizeFieldIssue({ code: 'invalid_option', params: { allowed: ['a'] } }, 'Estado', t)).toBe(
|
|
79
|
+
'El valor de Estado no es válido',
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
it('not_found', () => {
|
|
83
|
+
expect(localizeFieldIssue({ code: 'not_found', params: { ref: 'users' } }, 'Responsable', t)).toBe(
|
|
84
|
+
'El Responsable seleccionado no existe',
|
|
85
|
+
)
|
|
86
|
+
})
|
|
87
|
+
it('duplicate', () => {
|
|
88
|
+
expect(localizeFieldIssue({ code: 'duplicate' }, 'SKU', t)).toBe('Ya existe un registro con ese SKU')
|
|
89
|
+
})
|
|
90
|
+
it('invalid_type', () => {
|
|
91
|
+
expect(localizeFieldIssue({ code: 'invalid_type', params: { expected: 'number' } }, 'Cantidad', t)).toBe(
|
|
92
|
+
'El campo Cantidad tiene un formato inválido',
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
it('unknown code → generic fallback with label', () => {
|
|
96
|
+
expect(localizeFieldIssue({ code: 'weird_code' }, 'Campo', t)).toBe('Campo: valor inválido')
|
|
97
|
+
})
|
|
98
|
+
it('message passthrough (pre-localized) ignores code/label', () => {
|
|
99
|
+
const issue: FieldIssue = { message: 'Texto ya localizado' }
|
|
100
|
+
expect(localizeFieldIssue(issue, 'Ignorado', t)).toBe('Texto ya localizado')
|
|
101
|
+
})
|
|
102
|
+
})
|
|
@@ -60,4 +60,49 @@ describe('normalizeRefFieldsForSubmit', () => {
|
|
|
60
60
|
normalizeRefFieldsForSubmit(input, fields)
|
|
61
61
|
expect(input.category_id).toBe('')
|
|
62
62
|
})
|
|
63
|
+
|
|
64
|
+
// --- Fase 4: explicit `nullable` flag from the kernel contract ---
|
|
65
|
+
|
|
66
|
+
it('nulls an empty ref when the explicit nullable:true flag is present', () => {
|
|
67
|
+
const out = normalizeRefFieldsForSubmit(
|
|
68
|
+
{ supplier_id: '' },
|
|
69
|
+
[{ key: 'supplier_id', ref: 'suppliers', nullable: true }],
|
|
70
|
+
)
|
|
71
|
+
expect(out.supplier_id).toBeNull()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('respects an empty value when nullable:false (does not null)', () => {
|
|
75
|
+
const out = normalizeRefFieldsForSubmit(
|
|
76
|
+
{ supplier_id: '' },
|
|
77
|
+
[{ key: 'supplier_id', ref: 'suppliers', nullable: false }],
|
|
78
|
+
)
|
|
79
|
+
expect(out.supplier_id).toBe('')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('nulls an empty NON-uuid optional ref via explicit nullable:true', () => {
|
|
83
|
+
// A ref over a non-uuid column: the uuid-only heuristic never covered
|
|
84
|
+
// this, but the explicit flag + ref-shape does.
|
|
85
|
+
const out = normalizeRefFieldsForSubmit(
|
|
86
|
+
{ warehouse_code: '' },
|
|
87
|
+
[{ key: 'warehouse_code', type: 'dynamic_select', nullable: true }],
|
|
88
|
+
)
|
|
89
|
+
expect(out.warehouse_code).toBeNull()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('does NOT null a nullable:true plain-text field (empty string stays)', () => {
|
|
93
|
+
const out = normalizeRefFieldsForSubmit(
|
|
94
|
+
{ note: '' },
|
|
95
|
+
[{ key: 'note', type: 'text', nullable: true }],
|
|
96
|
+
)
|
|
97
|
+
expect(out.note).toBe('')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('falls back to the heuristic when the flag is undefined (older host)', () => {
|
|
101
|
+
const out = normalizeRefFieldsForSubmit(
|
|
102
|
+
{ category_id: '', name: '' },
|
|
103
|
+
fields, // fields carry no `nullable` flag
|
|
104
|
+
)
|
|
105
|
+
expect(out.category_id).toBeNull() // ref → heuristic nulls it
|
|
106
|
+
expect(out.name).toBe('') // plain text stays
|
|
107
|
+
})
|
|
63
108
|
})
|
|
@@ -35,7 +35,8 @@ import {
|
|
|
35
35
|
} from '@asteby/metacore-ui/primitives'
|
|
36
36
|
import { Loader2 } from 'lucide-react'
|
|
37
37
|
import { toast } from 'sonner'
|
|
38
|
-
import { toastServerError, toastServerSuccess } from './server-error'
|
|
38
|
+
import { toastServerError, toastServerSuccess, extractFieldErrors, localizeFieldIssue } from './server-error'
|
|
39
|
+
import type { Translate } from './server-error'
|
|
39
40
|
import { useApi } from './api-context'
|
|
40
41
|
import { DynamicIcon } from './dynamic-icon'
|
|
41
42
|
import { DynamicLineItems } from './dynamic-line-items'
|
|
@@ -403,6 +404,44 @@ function RecordPreview({ model, record }: { model: string; record: any }) {
|
|
|
403
404
|
)
|
|
404
405
|
}
|
|
405
406
|
|
|
407
|
+
/** Humanize a field key for a label fallback ("unit_price" → "Unit Price"). */
|
|
408
|
+
function humanizeKey(k: string): string {
|
|
409
|
+
return k.replace(/[._-]+/g, ' ').replace(/\b\w/g, c => c.toUpperCase())
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Localize a failed action submit's per-field `errors` (422 map or `{errors}`
|
|
413
|
+
* body) into `{ [fieldKey]: localizedMessage }`, using each field's label.
|
|
414
|
+
* Returns undefined when there is no usable per-field map. */
|
|
415
|
+
function localizeActionFieldErrors(
|
|
416
|
+
err: unknown,
|
|
417
|
+
fields: readonly ActionFieldDef[] | undefined,
|
|
418
|
+
t: Translate,
|
|
419
|
+
): Record<string, string> | undefined {
|
|
420
|
+
const map = extractFieldErrors(err)
|
|
421
|
+
if (!map) return undefined
|
|
422
|
+
const labelFor = (k: string) => {
|
|
423
|
+
const f = (fields ?? []).find(x => x.key === k)
|
|
424
|
+
return f?.label ? t(f.label, { defaultValue: f.label }) : humanizeKey(k)
|
|
425
|
+
}
|
|
426
|
+
const out: Record<string, string> = {}
|
|
427
|
+
for (const [k, issues] of Object.entries(map)) out[k] = localizeFieldIssue(issues[0], labelFor(k), t)
|
|
428
|
+
return out
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** Toast a failed action: a summary + localized per-field lines when the server
|
|
432
|
+
* returned a per-field `errors` map, else the standard cause-carrying toast.
|
|
433
|
+
* Used where inline rendering isn't wired (confirm / multi-step wizard). */
|
|
434
|
+
function toastActionError(err: unknown, fields: readonly ActionFieldDef[] | undefined, t: Translate): void {
|
|
435
|
+
const localized = localizeActionFieldErrors(err, fields, t)
|
|
436
|
+
if (localized) {
|
|
437
|
+
toast.error(t('dynamic.validation_failed', { defaultValue: 'Revisa los campos marcados' }), {
|
|
438
|
+
description: Object.values(localized).join('\n'),
|
|
439
|
+
})
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
toastServerError(err, { t })
|
|
443
|
+
}
|
|
444
|
+
|
|
406
445
|
function ConfirmActionDialog({ open, onOpenChange, action, model, record, endpoint, onSuccess }: ActionModalProps) {
|
|
407
446
|
const { t } = useTranslation()
|
|
408
447
|
const api = useApi()
|
|
@@ -422,10 +461,10 @@ function ConfirmActionDialog({ open, onOpenChange, action, model, record, endpoi
|
|
|
422
461
|
onOpenChange(false)
|
|
423
462
|
onSuccess()
|
|
424
463
|
} else {
|
|
425
|
-
|
|
464
|
+
toastActionError({ response: { data: res.data } }, action.fields, t)
|
|
426
465
|
}
|
|
427
466
|
} catch (err: any) {
|
|
428
|
-
|
|
467
|
+
toastActionError(err, action.fields, t)
|
|
429
468
|
} finally {
|
|
430
469
|
setExecuting(false)
|
|
431
470
|
}
|
|
@@ -469,6 +508,8 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
469
508
|
const api = useApi()
|
|
470
509
|
const [formData, setFormData] = useState<Record<string, any>>({})
|
|
471
510
|
const [executing, setExecuting] = useState(false)
|
|
511
|
+
// Per-field validation errors (localized), shown inline under each input.
|
|
512
|
+
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({})
|
|
472
513
|
// Related records to surface BELOW the form, as read-only context for the
|
|
473
514
|
// record being acted on — e.g. the reception history of a transfer while
|
|
474
515
|
// receiving against it. Sourced from the model's metadata.relations (the
|
|
@@ -512,29 +553,57 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
512
553
|
defaults[field.key] = field.defaultValue ?? (field.type === 'boolean' ? false : '')
|
|
513
554
|
}
|
|
514
555
|
setFormData(defaults)
|
|
556
|
+
setFieldErrors({})
|
|
515
557
|
}
|
|
516
558
|
}, [open, action.fields, record])
|
|
517
559
|
|
|
518
|
-
const updateField = (key: string, value: any) =>
|
|
560
|
+
const updateField = (key: string, value: any) => {
|
|
561
|
+
setFormData((prev: Record<string, any>) => ({ ...prev, [key]: value }))
|
|
562
|
+
setFieldErrors(prev => {
|
|
563
|
+
if (!prev[key]) return prev
|
|
564
|
+
const next = { ...prev }
|
|
565
|
+
delete next[key]
|
|
566
|
+
return next
|
|
567
|
+
})
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const handleActionError = (err: unknown) => {
|
|
571
|
+
const localized = localizeActionFieldErrors(err, action.fields, t)
|
|
572
|
+
if (localized) {
|
|
573
|
+
setFieldErrors(localized)
|
|
574
|
+
toast.error(t('dynamic.validation_failed', { defaultValue: 'Revisa los campos marcados' }))
|
|
575
|
+
return
|
|
576
|
+
}
|
|
577
|
+
toastServerError(err, { t })
|
|
578
|
+
}
|
|
519
579
|
|
|
520
580
|
const execute = async () => {
|
|
521
581
|
if (action.fields) {
|
|
582
|
+
// Client-side required check → mark ALL missing fields inline.
|
|
583
|
+
const missing: Record<string, string> = {}
|
|
522
584
|
for (const field of action.fields) {
|
|
523
585
|
if (!field.required) continue
|
|
524
586
|
if (isLineItemsField(field)) {
|
|
525
587
|
const rows = formData[field.key]
|
|
526
588
|
if (!Array.isArray(rows) || rows.length === 0) {
|
|
527
|
-
|
|
528
|
-
|
|
589
|
+
missing[field.key] = t('validation.line_items_required', {
|
|
590
|
+
defaultValue: '{{label}} requiere al menos un renglón',
|
|
591
|
+
label: tl(field.label),
|
|
592
|
+
})
|
|
529
593
|
}
|
|
530
594
|
continue
|
|
531
595
|
}
|
|
532
596
|
if (!formData[field.key] && formData[field.key] !== false) {
|
|
533
|
-
|
|
534
|
-
return
|
|
597
|
+
missing[field.key] = localizeFieldIssue({ code: 'required' }, tl(field.label), t)
|
|
535
598
|
}
|
|
536
599
|
}
|
|
600
|
+
if (Object.keys(missing).length) {
|
|
601
|
+
setFieldErrors(missing)
|
|
602
|
+
toast.error(t('dynamic.validation_failed', { defaultValue: 'Revisa los campos marcados' }))
|
|
603
|
+
return
|
|
604
|
+
}
|
|
537
605
|
}
|
|
606
|
+
setFieldErrors({})
|
|
538
607
|
setExecuting(true)
|
|
539
608
|
try {
|
|
540
609
|
const url = buildActionUrl(endpoint, model, record.id, action.key)
|
|
@@ -544,10 +613,10 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
544
613
|
onOpenChange(false)
|
|
545
614
|
onSuccess()
|
|
546
615
|
} else {
|
|
547
|
-
|
|
616
|
+
handleActionError({ response: { data: res.data } })
|
|
548
617
|
}
|
|
549
618
|
} catch (err: any) {
|
|
550
|
-
|
|
619
|
+
handleActionError(err)
|
|
551
620
|
} finally {
|
|
552
621
|
setExecuting(false)
|
|
553
622
|
}
|
|
@@ -609,6 +678,9 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
|
|
|
609
678
|
{tl(field.label)}
|
|
610
679
|
</FieldLabel>
|
|
611
680
|
{renderField(field, formData[field.key], (v: any) => updateField(field.key, v), formData)}
|
|
681
|
+
{fieldErrors[field.key] && (
|
|
682
|
+
<p className="text-destructive text-xs mt-1">{fieldErrors[field.key]}</p>
|
|
683
|
+
)}
|
|
612
684
|
</FieldCell>
|
|
613
685
|
)
|
|
614
686
|
})}
|
|
@@ -749,10 +821,10 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
|
|
|
749
821
|
onOpenChange(false)
|
|
750
822
|
onSuccess()
|
|
751
823
|
} else {
|
|
752
|
-
|
|
824
|
+
toastActionError({ response: { data: res.data } }, steps.flatMap(s => s.fields ?? []), t)
|
|
753
825
|
}
|
|
754
826
|
} catch (err: any) {
|
|
755
|
-
|
|
827
|
+
toastActionError(err, steps.flatMap(s => s.fields ?? []), t)
|
|
756
828
|
} finally {
|
|
757
829
|
setExecuting(false)
|
|
758
830
|
}
|
|
@@ -48,7 +48,7 @@ import { format, parseISO } from 'date-fns'
|
|
|
48
48
|
import { es } from 'date-fns/locale'
|
|
49
49
|
import { ExternalLink, Loader2, CalendarIcon, ChevronDown, Check, Upload, X as XIcon } from 'lucide-react'
|
|
50
50
|
import { useApi } from '../api-context'
|
|
51
|
-
import { toastServerError } from '../server-error'
|
|
51
|
+
import { toastServerError, extractFieldErrors, localizeFieldIssue } from '../server-error'
|
|
52
52
|
import { DynamicSelectField, OptionLead, OptionThumb } from '../dynamic-select-field'
|
|
53
53
|
import { DynamicRelations } from '../dynamic-relations'
|
|
54
54
|
import { useOptionsResolver, type ResolvedOption } from '../use-options-resolver'
|
|
@@ -497,6 +497,10 @@ export function DynamicRecordDialog({
|
|
|
497
497
|
const [relations, setRelations] = useState<RelationMeta[]>([])
|
|
498
498
|
const [record, setRecord] = useState<any | null>(null)
|
|
499
499
|
const [formValues, setFormValues] = useState<Record<string, any>>({})
|
|
500
|
+
// Per-field validation errors (localized strings), keyed by field.key. Shown
|
|
501
|
+
// inline under each input; populated from a 422 `errors` map or the client
|
|
502
|
+
// required-field check, cleared per-field on change and wholesale on reopen.
|
|
503
|
+
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({})
|
|
500
504
|
const [loading, setLoading] = useState(false)
|
|
501
505
|
const [saving, setSaving] = useState(false)
|
|
502
506
|
const [deleting, setDeleting] = useState(false)
|
|
@@ -511,6 +515,9 @@ export function DynamicRecordDialog({
|
|
|
511
515
|
if (!open) return
|
|
512
516
|
if (!isCreate && !recordId) return
|
|
513
517
|
|
|
518
|
+
// Fresh open → drop any validation errors from a prior submit.
|
|
519
|
+
setFieldErrors({})
|
|
520
|
+
|
|
514
521
|
let cancelled = false
|
|
515
522
|
|
|
516
523
|
// Seed instantly from the row the table already has so view/edit render
|
|
@@ -671,19 +678,55 @@ export function DynamicRecordDialog({
|
|
|
671
678
|
onChange?.()
|
|
672
679
|
}, [api, endpoint, model, recordId, isCreate, modalMeta, onChange])
|
|
673
680
|
|
|
681
|
+
// The human label for a field key, for localizing validation errors. Falls
|
|
682
|
+
// back to a humanized key when the field is unknown (e.g. a server-side key
|
|
683
|
+
// with no matching form field).
|
|
684
|
+
const labelForKey = (key: string): string => {
|
|
685
|
+
const f = (modalMeta?.fields ?? []).find(x => x.key === key)
|
|
686
|
+
if (f?.label) return f.label
|
|
687
|
+
return key.replace(/[._-]+/g, ' ').replace(/\b\w/g, c => c.toUpperCase())
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Turn a failed submit (422 `errors` map, or a bare `{errors}` body) into
|
|
691
|
+
// inline field errors + a summary toast. When there is no field map, fall
|
|
692
|
+
// back to the existing single cause-carrying toast.
|
|
693
|
+
const handleSubmitError = (err: unknown) => {
|
|
694
|
+
const map = extractFieldErrors(err)
|
|
695
|
+
if (map) {
|
|
696
|
+
const next: Record<string, string> = {}
|
|
697
|
+
for (const [key, issues] of Object.entries(map)) {
|
|
698
|
+
next[key] = localizeFieldIssue(issues[0], labelForKey(key), t)
|
|
699
|
+
}
|
|
700
|
+
setFieldErrors(next)
|
|
701
|
+
toast.error(t('dynamic.validation_failed', { defaultValue: 'Revisa los campos marcados' }))
|
|
702
|
+
return
|
|
703
|
+
}
|
|
704
|
+
toastServerError(err, { t, fallback: t('dynamic.save_error', { defaultValue: 'No se pudo guardar' }) })
|
|
705
|
+
}
|
|
706
|
+
|
|
674
707
|
const handleSubmit = async (e?: React.FormEvent) => {
|
|
675
708
|
e?.preventDefault()
|
|
676
709
|
if (!modalMeta) return
|
|
677
710
|
|
|
678
711
|
if (isEditable) {
|
|
712
|
+
// Collect ALL missing required fields (not just the first) and mark
|
|
713
|
+
// each inline instead of a single toast.
|
|
714
|
+
const missing: Record<string, string> = {}
|
|
679
715
|
for (const field of modalMeta.fields ?? []) {
|
|
680
716
|
if (field.required && !formValues[field.key] && formValues[field.key] !== 0 && formValues[field.key] !== false) {
|
|
681
|
-
|
|
682
|
-
return
|
|
717
|
+
missing[field.key] = localizeFieldIssue({ code: 'required' }, field.label, t)
|
|
683
718
|
}
|
|
684
719
|
}
|
|
720
|
+
if (Object.keys(missing).length) {
|
|
721
|
+
setFieldErrors(missing)
|
|
722
|
+
toast.error(t('dynamic.validation_failed', { defaultValue: 'Revisa los campos marcados' }))
|
|
723
|
+
return
|
|
724
|
+
}
|
|
685
725
|
}
|
|
686
726
|
|
|
727
|
+
// Required check passed → clear any prior validation errors.
|
|
728
|
+
setFieldErrors({})
|
|
729
|
+
|
|
687
730
|
// Empty reference pickers → null (not "" / nil-UUID) so nullable FK
|
|
688
731
|
// columns accept them instead of raising a 23503 FK violation.
|
|
689
732
|
const payload = normalizeRefFieldsForSubmit(formValues, modalMeta.fields)
|
|
@@ -734,10 +777,10 @@ export function DynamicRecordDialog({
|
|
|
734
777
|
// Surface the server's real cause (`details`) as the toast
|
|
735
778
|
// description, not just the generic headline. `res.data` is the
|
|
736
779
|
// `{ success:false, message, details }` envelope.
|
|
737
|
-
|
|
780
|
+
handleSubmitError(res.data)
|
|
738
781
|
}
|
|
739
782
|
} catch (err: any) {
|
|
740
|
-
|
|
783
|
+
handleSubmitError(err)
|
|
741
784
|
} finally {
|
|
742
785
|
setSaving(false)
|
|
743
786
|
}
|
|
@@ -798,9 +841,17 @@ export function DynamicRecordDialog({
|
|
|
798
841
|
record={record}
|
|
799
842
|
value={formValues[field.key] ?? ''}
|
|
800
843
|
mode={mode}
|
|
801
|
-
|
|
844
|
+
error={fieldErrors[field.key]}
|
|
845
|
+
onChange={val => {
|
|
802
846
|
setFormValues((prev: Record<string, any>) => ({ ...prev, [field.key]: val }))
|
|
803
|
-
|
|
847
|
+
// Clear this field's error as soon as the user edits it.
|
|
848
|
+
setFieldErrors(prev => {
|
|
849
|
+
if (!prev[field.key]) return prev
|
|
850
|
+
const next = { ...prev }
|
|
851
|
+
delete next[field.key]
|
|
852
|
+
return next
|
|
853
|
+
})
|
|
854
|
+
}}
|
|
804
855
|
/>
|
|
805
856
|
</FieldCell>
|
|
806
857
|
)
|
|
@@ -910,9 +961,11 @@ interface FieldRowProps {
|
|
|
910
961
|
value: any
|
|
911
962
|
mode: 'view' | 'edit' | 'create'
|
|
912
963
|
onChange: (val: any) => void
|
|
964
|
+
/** Localized validation error for this field, shown in red under the input. */
|
|
965
|
+
error?: string
|
|
913
966
|
}
|
|
914
967
|
|
|
915
|
-
function FieldRow({ field, record, value, mode, onChange }: FieldRowProps) {
|
|
968
|
+
function FieldRow({ field, record, value, mode, onChange, error }: FieldRowProps) {
|
|
916
969
|
// A `readonly` field is server/system-generated (e.g. the GitHub addon's
|
|
917
970
|
// `number`/`github_url`, filled by the API after the outbound create). On
|
|
918
971
|
// CREATE it is excluded from the form entirely (see `visibleFields`); on EDIT
|
|
@@ -937,6 +990,10 @@ function FieldRow({ field, record, value, mode, onChange }: FieldRowProps) {
|
|
|
937
990
|
) : (
|
|
938
991
|
<EditField field={field} value={value} onChange={onChange} record={record} />
|
|
939
992
|
)}
|
|
993
|
+
|
|
994
|
+
{error && mode !== 'view' && (
|
|
995
|
+
<p className="text-destructive text-xs mt-1">{error}</p>
|
|
996
|
+
)}
|
|
940
997
|
</div>
|
|
941
998
|
)
|
|
942
999
|
}
|
|
@@ -15,26 +15,40 @@ type RefFieldMeta = Record<string, any>
|
|
|
15
15
|
* (e.g. products.category_id, a nullable `ref`) rejects a non-null value that
|
|
16
16
|
* points at no row: sending `""`/nil-UUID triggers
|
|
17
17
|
* insert ... violates foreign key constraint "fk_products_category" (23503)
|
|
18
|
-
* even though the user left the picker empty.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* even though the user left the picker empty.
|
|
19
|
+
*
|
|
20
|
+
* Nullability decision (Fase 4): prefer the EXPLICIT `nullable` flag the kernel
|
|
21
|
+
* (v0.77.1+) now serves per field (`modelbase.FieldDef.Nullable`, `!Required`).
|
|
22
|
+
* When `field.nullable === true` an empty value is submitted as `null`; when
|
|
23
|
+
* `field.nullable === false` the value is respected. When the flag is absent
|
|
24
|
+
* (undefined — older hosts that don't serve it yet) we fall back to the legacy
|
|
25
|
+
* type-based heuristic (`getFieldRef` / dynamic_select / search). This keeps
|
|
26
|
+
* backward-compat while letting the contract, not a guess, drive the decision.
|
|
27
|
+
*
|
|
28
|
+
* The explicit flag also lets us null an empty OPTIONAL ref whose column is NOT
|
|
29
|
+
* a uuid (which the uuid-only heuristic never covered). To avoid nulling a plain
|
|
30
|
+
* optional text field — where `""` is a legitimate value, not "no relation" — we
|
|
31
|
+
* gate the explicit path on the field ALSO looking like a reference/relation
|
|
32
|
+
* (`getFieldRef` / dynamic_select / search). A nullable non-ref scalar keeps its
|
|
33
|
+
* `""`. This is generic — every addon's optional relations benefit. Does not
|
|
34
|
+
* mutate `values`.
|
|
22
35
|
*/
|
|
23
36
|
export function normalizeRefFieldsForSubmit(
|
|
24
37
|
values: Record<string, any>,
|
|
25
38
|
fields: RefFieldMeta[] | undefined,
|
|
26
39
|
): Record<string, any> {
|
|
27
|
-
const
|
|
28
|
-
(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const isRefField = (f: RefFieldMeta): boolean =>
|
|
41
|
+
!!getFieldRef(f as unknown as ActionFieldDef) ||
|
|
42
|
+
f.type === 'dynamic_select' ||
|
|
43
|
+
f.widget === 'dynamic_select' ||
|
|
44
|
+
f.type === 'search'
|
|
45
|
+
|
|
46
|
+
// Index fields by key so we can consult the explicit `nullable` flag.
|
|
47
|
+
const fieldByKey = new Map<string, RefFieldMeta>()
|
|
48
|
+
for (const f of fields ?? []) {
|
|
49
|
+
if (f && typeof f.key === 'string') fieldByKey.set(f.key, f)
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
const out: Record<string, any> = { ...values }
|
|
39
53
|
for (const [k, v] of Object.entries(out)) {
|
|
40
54
|
// A nil UUID is never a real target row — null it out on any field.
|
|
@@ -42,8 +56,23 @@ export function normalizeRefFieldsForSubmit(
|
|
|
42
56
|
out[k] = null
|
|
43
57
|
continue
|
|
44
58
|
}
|
|
45
|
-
|
|
46
|
-
if (
|
|
59
|
+
const isEmpty = v === '' || v == null
|
|
60
|
+
if (!isEmpty) continue
|
|
61
|
+
|
|
62
|
+
const f = fieldByKey.get(k)
|
|
63
|
+
const nullable = f?.nullable
|
|
64
|
+
if (nullable === true) {
|
|
65
|
+
// Explicit contract flag wins. Only null a reference/relation so a
|
|
66
|
+
// legitimately-empty optional plain-text field keeps its `""`.
|
|
67
|
+
if (f && isRefField(f)) out[k] = null
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
if (nullable === false) {
|
|
71
|
+
// Explicitly non-nullable → respect the empty value as-is.
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
// No explicit flag (older host): fall back to the type-based heuristic.
|
|
75
|
+
if (f && isRefField(f)) out[k] = null
|
|
47
76
|
}
|
|
48
77
|
return out
|
|
49
78
|
}
|