fix(admin): a cleared poll-interval part no longer emits "null" - #889
Merged
Merged
Conversation
ajslater
added this pull request to stack #890
September 21, 2026 16:16
Base automatically changed from
fix/validation-server-field-errors
to
develop
September 21, 2026 16:17
Clearing one of the three v-number-inputs sets its model to `null`, and `String(null)` is `"null"` -- four characters, so `padStart(3, "0")` leaves it alone and the emitted duration was `"null 01:00:00"`. The server rejects that with a 400 the dialog then rendered as raw JSON, because nothing parsed the envelope's error shape. Per-part rules bound each field to the `:min`/`:max` already on its control. Vuetify's `$required` alias carves out `v === 0`, so zero -- a legitimate value here -- still passes, and the parts sit inside the dialog's v-form so `form.validate()` disables Submit. Rules do not stop the emit, though, so the composition is guarded as well: a cleared, negative or unparseable part coerces to zero. The field still reads empty to the user and the rule still says so; what gets emitted is just always a valid duration. The test reproduces "null 01:00:00" against the unguarded composition. This was the last item of the validation-parity plan; the password message wording it was also meant to carry shipped with the change-password fix, which already touched that line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ajslater
force-pushed
the
fix/validation-riders
branch
from
September 21, 2026 16:17
5f01f03 to
bf146c8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements P3f of
tasks/followups-implementation-plan.md§5.6 — the last item in the plan.The bug
duration-input.vue's threev-number-inputs each callupdate(), which emitsfieldsToDjangoDuration. Clearing a part sets its model tonull, and:…so the emit became
"null 01:00:00". Its consumer islibrary-create-update-inputs.vueforLibrary.poll_every, aDurationField, which rejects it with a 400 — the same 400 the dialog rendered as raw JSON, because nothing parsed the envelope's error shape (#888).Reproduced: reverting only the composition guard makes the test fail with
expected 'null 01:00:00' not to contain 'null'.Two halves, because rules alone are not enough
Per-part rules, bound to the
:min/:maxalready on each control ([0, 365]/[0, 23]/[0, 59]). Vuetify's$requiredalias carves outv === 0, so zero — a legitimate value here — still passes, and the parts sit inside the dialog's<v-form ref="form">soform.validate()disables Submit.A composition guard, because rules do not stop the emit. A cleared, negative or unparseable part coerces to zero. Coerced rather than refusing to emit: the field still reads empty to the user, the rule still says so, and Submit stays disabled — but whatever does get emitted is a valid duration.
The rule arrays are frozen module constants read from
data, notcomputed— they are static.Tests
frontend/tests/unit/duration-input.test.js, 7 cases — none existed. Initial parsing, normal composition, a cleared part never emitting"null", every part cleared still composing"000 00:00:00", the coercion table, zero being a legitimate value rather than an empty one, and the bounds matching the controls.make fix && make lint && make tyclean. Fullmake testgreen: 1283 pytest, 602 vitest (+7).NEWS, under Fixes: "Clearing part of a poll interval no longer produces an invalid value."
Note on the other rider
§5.6 also carried the password-message wording (five sites, one wording). That shipped with #876, which already touched the one line that disagreed — §5.6 explicitly permits folding it there if that PR was still open, and it was.
This completes the plan
All thirteen items are now open as PRs: #876 #877 #878 #879 #880 #881 #882 #883 #884 #885 #886 #887 #888 and this one.
🤖 Generated with Claude Code