Build an intake form with Save draft and Send for review actions. It must submit to a server route with JavaScript disabled, preserve entered values after a server error, and identify which submitter initiated the request. Add a live reference check that may return out of order; a result for CASE-47 must not describe the field after the operator changes it to CASE-62. Save a recoverable local draft of a 120-character diagnosis, label its state separately from a server-confirmed save, and compare its base version with the server when the page reopens. Add evidence upload with Selected, Transferring, Processing, Accepted, and Rejected states. The server owns final validation and file acceptance.
Project: recoverable case intake form
Build contract
- Use native controls and a real form action so both submitter paths remain operable without the client bundle.
- Apply asynchronous field feedback only when its value and sequence still match current input; keep final uniqueness validation on the server.
- Persist a private local draft with account, record, and base version; handle quota failure, sign-out, and a newer server version.
- Send evidence with FormData and a browser-set multipart boundary; document one-shot retry identity or a chunk session with integrity and finalization.
Implementation checkpoint
function canShowReferenceResult(request, inputValue, latestSequence) {
return request.value === inputValue && request.sequence === latestSequence;
}
console.log(canShowReferenceResult({ value: "CASE-47", sequence: 3 }, "CASE-62", 4));
// Output: falseCost and boundaries
A native form serializes f controls in O(f) work and can complete without a client runtime. An eager reference check may send one request per edit; debounce reduces traffic but delays feedback. Local draft serialization costs O(k) for k stored bytes and may fail at quota. A b-byte file needs O(b) transfer; a c-byte chunk protocol introduces roughly O(b/c) requests and temporary server storage. The implementation should measure request count, interrupted-work recovery, and the accuracy of saved-state labels, because apparent speed is worthless when it conceals rejected work.
Failure drill
Disable JavaScript and verify both actions still reach distinct server behavior. Reenable it, then delay the first reference response until after the second; old feedback must be ignored even if cancellation raced with completion. Interrupt autosave after the request leaves, close the tab, and reopen after another browser advances the server version. Both draft and server text must remain available until the operator chooses. Send a file with an intentionally hand-written multipart Content-Type and observe the parse failure, then restore browser header ownership. Reject a transferred file during server processing and confirm the UI never calls it Accepted.
Acceptance checks
- Keyboard submission and both actions work without scripts.
- Out-of-order validation cannot replace feedback for a newer value.
- A local draft never claims server confirmation and does not overwrite a newer version.
- Upload transfer, processing, and acceptance remain separate states.
Common Mistakes
- Using submit() to trigger a normal validated form action.
- Treating a field-availability response as a final reservation.
- Clearing a draft before server confirmation.
- Setting multipart Content-Type manually for FormData.
Related lessons
Native Form Submission and Progressive Enhancement; Asynchronous Field Validation Races; Autosave, Draft Recovery, and Conflicts; Multipart Upload Progress and Retry Boundaries; Form submission: validate on the server and return field errors.
