Skip to content

ui: a line-item dialog marks its required fields and names the one the server refused (#7062) - #7066

Merged
delchev merged 2 commits into
masterfrom
issue-7062-line-dialog-required
Sep 6, 2026
Merged

ui: a line-item dialog marks its required fields and names the one the server refused (#7062)#7066
delchev merged 2 commits into
masterfrom
issue-7062-line-dialog-required

Conversation

@delchev

@delchev delchev commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #7062

What was wrong

The generated Add line dialog of a document said nothing about which values were mandatory before the save, and nothing about which one was refused after it.

Filling a line but leaving a required: true relation empty, the server answers a plain 400 {"message":"The 'TaxRate' property is required"} - correct and specific. The dialog showed the generic banner "The request could not be completed. Please review your input.", marked no column, and carried no required marker on any field: the user was told the wrong field was wrong and never told which one was right.

Two halves, both in the dialog

The marker. editColumns[].required was already in the detail registration and simply never rendered - the dialog's label was a bare x-text, so only the header form marked its required fields. All three item dialogs (power document, personal, partner) now carry the same * the header form does.

The mapping. The generated controllers state a rejection in prose, naming the property in single quotes, as a plain 400 rather than a structured 422 with errorCauses - so apiErrors.messageFor had nothing but the status to work from and printed the BadRequest catalog line. Two shared helpers now read that prose:

  • apiErrors.namedProperty(err, knownNames) - the first quoted token that is a field of the form in hand. That gate is what keeps the developer-facing rule intact: an arbitrary 400 (a stack-trace reason, an internal identifier) names nothing known and still falls back to the catalog message.
  • apiErrors.messageWithLabels(err, labels) - the same message in the surface's own labels: "The 'Tax Rate' property is required".

The dialog marks that column with aria-invalid - which is what Harmonia actually colours the label and the border from (has-[[aria-invalid=true]]:text-negative on x-h-field, aria-invalid:border-negative on the control) - and shows the message in its banner. baseFormPage.applyApiError does the same for every generated header form, so both surfaces answer a named rejection alike instead of only the 422 path being wired.

Scope note

I could not reproduce why Name specifically turned red in the report: nothing in the templates binds an error to it, and Harmonia colours a field only from aria-invalid or a native :user-invalid, so that was most likely a stray native validity on that input rather than the error mapping. What this change guarantees is that the column the server named is the one explicitly marked; nothing else in the dialog is marked by generation.

Verification

IntentEmissionCoverageIT extended with assertions on the emitted marker, the aria-invalid binding and the mapping, anchored on the Roster document (its line's day is required: true) - green, 1/1, 136.7 s, on top of a full quick-build reactor. formatter:validate clean on the touched module.

delchev and others added 2 commits September 6, 2026 16:24
…e server refused (#7062)

The generated "Add line" dialog said nothing about which values were
mandatory before the save, and nothing about which one was refused after
it: a line missing a required relation came back as the generic banner
"The request could not be completed. Please review your input." with no
column marked, while the server had answered 400 "The 'TaxRate' property
is required".

Two halves, both in the dialog.

The marker: `editColumns[].required` was already in the detail
registration and simply never rendered - the label was a bare x-text, so
only the header form marked its required fields. All three item dialogs
(power document, personal, partner) now carry the same `*`.

The mapping: the generated controllers state a rejection in prose,
naming the property in single quotes, as a plain 400 rather than a
structured 422 with errorCauses - so `messageFor` had nothing but the
status to work from. Two shared helpers read that prose:
`apiErrors.namedProperty(err, knownNames)` returns the first quoted
token that IS a field of the form in hand (the gate that keeps an
arbitrary developer-facing 400 out of the UI - anything else still falls
back to the catalog message), and `messageWithLabels` rewrites the
message in the surface's own labels ("The 'Tax Rate' property is
required"). The dialog marks that column with aria-invalid - what
Harmonia actually colours the label and the border from - and shows the
message; `baseFormPage.applyApiError` does the same for every generated
header form, so the two surfaces answer a named rejection alike.

IntentEmissionCoverageIT guards the emitted marker, the aria-invalid
binding and the mapping on the Roster document, whose line carries a
required column.

Fixes #7062
The six files under modules/parsers/typescript are ANTLR output checked into
the repository; a local full build regenerates them, and they were swept into
this branch's commit. They are unformatted regenerated output, so
formatter:validate failed the code-style job on a change that has nothing to
do with the TypeScript parser. Restored to master's versions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@delchev
delchev merged commit 82e8868 into master Sep 6, 2026
10 checks passed
@delchev
delchev deleted the issue-7062-line-dialog-required branch September 6, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ui: line-item dialog - required fields carry no marker and a 400 naming the property highlights the wrong field with a generic banner

1 participant