feat(forms): public client request-form page #485
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/PMS-730-public-request-form-page"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
First slice of the frontend for the client-request loop (mokosh-server #495, #497, #498, #499): the page a client actually lands on.
Why this surface first
The loop has four frontend surfaces: the admin form builder, the send-a-link action, this public page, and the measured-duration readout on the KB article. Three of them have a fallback, since an MSP user can drive the API directly in a pinch. This one does not: a client cannot curl. Until it exists, nothing an agent does with the other three reaches anybody.
It also had an exact precedent to follow in
portal_set_password.rs, which is the same shape (emailed token, no session, status-contract-driven copy).What it does
/request-forms/:tokenfetchesGET /api/v1/public/request-forms/{token}, renders the form, and posts the answers back to the same path. No bearer is sent: the token in the path is the only credential the visitor has.The page renders whatever field set the server hands it. Types, labels, option sets, length bounds and cross-field rules all come from the definition (PMS-731), so adding a request type server-side needs no change here.
Details worth reviewing
Three things a naive client would get wrong, each covered by a unit test. A checkbox sends a real JSON bool rather than the string
"false", because the server type-checks each field and would reject the string. A blank optional answer is omitted rather than sent as"", which is what the server does with it anyway after trimming. And a field the server will require through arequired_ifrule is marked required as soon as its condition holds, so the asterisk onforward_toappears whenmailbox_handlingis set toforward, not on submit.Validation follows
docs/form-conventions.md. Submit evaluates every required field and sets each failed slot before bailing once, so one missing answer never masks another, and whitespace is not accepted as an answer.Each server status gets its own copy. 410 says the request is already with us; 400 says ask for a new link. The server deliberately does not distinguish expired from unknown from malformed (it would otherwise be an oracle for which token ids exist), so neither does this page. A 422 is routed to the offending inputs, with any message about a field this form does not render falling back to the form-level banner instead of vanishing.
Forward compatibility. An unknown rule kind from a newer server deserialises to a no-op rather than failing the render, and an unknown field type degrades to a text input, leaving the server as the authority on validation either way.
A new fetch helper.
get_typedmirrors the existingpost_typed:get_authed_typedwould attach whatever bearer is in memory, and the plaingetcollapses failures to aString, which would make 410 and 400 indistinguishable.The gate that mattered most
lib.rscarries the MAPPS-396 recurrence test asserting that every link mokosh-server emails resolves to a real route rather than the catch-all NotFound. It exists because the portal setup link sat in customers' inboxes with no page behind it. The request link is exactly that case, so the entry inEMAILED_LINKSmatters more than the route declaration does.Verification
cargo check --target wasm32-unknown-unknown,cargo fmt --all --check,cargo test(255 passed, 0 failed, including 3 new unit tests and the emailed-link gate), and both guard scripts (check-theme-tokens,check-runner-labels).just check-clippyreports two pre-existing failures insrc/pages/projects.rsandsrc/pages/time.rs(manual_div_ceilandunnecessary_sort_by). Both reproduce on unmodifiedmainwith a local clippy newer than CI's, so they are not from this branch and are left alone; nothing in the new code trips a lint.Next slices, not in this PR
The admin form builder, the send-a-link action, and the measured-duration readout on the KB article page.