max / goingson
1 file changed,
+14 insertions,
-0 deletions
| @@ -533,6 +533,20 @@ async function apiCall(promise, options = {}) { | |||
| 533 | 533 | } catch (err) { | |
| 534 | 534 | if (button) setButtonLoading(button, false); | |
| 535 | 535 | const message = GoingsOn.utils.getErrorMessage(err, errorMessage); | |
| 536 | + | ||
| 537 | + | // Surface a field-level validation error on the offending input while the | |
| 538 | + | // modal is still open. The backend sends details.field and the frontend | |
| 539 | + | // already has showFieldError plumbing, but nothing connected them on a | |
| 540 | + | // server rejection (ultra-fuzz Run #28 UX) — it only showed a generic | |
| 541 | + | // toast. The toast below still fires as a fallback. | |
| 542 | + | const field = err?.details?.field; | |
| 543 | + | if (field && typeof GoingsOn.utils.showFieldError === 'function') { | |
| 544 | + | const safe = (window.CSS && CSS.escape) ? CSS.escape(field) : field; | |
| 545 | + | const input = document.querySelector(`#modal-content [name="${safe}"]`) | |
| 546 | + | || document.getElementById(field); | |
| 547 | + | if (input) GoingsOn.utils.showFieldError(input, message); | |
| 548 | + | } | |
| 549 | + | ||
| 536 | 550 | const toastOpts = {}; | |
| 537 | 551 | if (retry) { | |
| 538 | 552 | toastOpts.action = { label: 'Retry', fn: retry }; |