feat(rm-handlers): D1 — issue create form (POST-redirect-GET)#22
Merged
Conversation
The depth follow-on to the D2 list chrome on the same /issues surface.
17 years of Redmine taught users one flow: list page → "New issue" link →
form with Subject (required) + Description (optional) → submit → land on
the new issue's detail page. This module ships that.
New sibling module `crates/rm-handlers/src/issues_form.rs`:
GET /issues/new — render an empty form
POST /issues — accept submission, validate, persist, 303 to
/issues/<id> on success; re-render with inline
errors on validation failure
Per Plan §8 file ownership, the D-track form work lives in this sibling
so the W1 list/detail file stays the merge-conflict boundary for its own
width. The two routers compose at the workspace level — `rm-server`
merges issues::router + issues_form::router.
Validation:
- subject required (trimmed, whitespace-only rejected)
- subject ≤ 255 chars (Redmine's column cap; fail at the form
boundary rather than DB-side opacity)
- description trimmed; empty → None in NewIssue
On validation failure the form re-renders WITH the user's input
preserved (HTML-escaped through the kit) and an error block above the
fields. POST-redirect-GET on success so refreshing the new issue's page
doesn't re-submit.
Out of scope (named, gated): edit (PATCH /issues/:id, D1.2), CSRF token
(rm-auth integration), Markdown preview, attachments, status/priority/
tracker/assignee fields (wait for the FK columns to land on IssueRow
via W2/W3 taxonomy + W4 actor — the form grows one input per FK without
chrome change).
Tests: 14 new — 6 unit on the pure `validate()` helper, 8 integration
covering GET empty render, POST 303 + Location, POST trims subject,
POST empty desc, POST empty subject error, POST whitespace-only subject,
POST overlong subject, POST XSS payload escaped on re-render.
cargo +1.95 test -p rm-handlers --lib: 89 passed (was 75; +14). fmt +
clippy --all-targets -- -D warnings clean.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
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.
What
The depth follow-on to #21's D2 chrome on the same
/issuessurface — closes the most-iconic Redmine flow:New sibling module
crates/rm-handlers/src/issues_form.rs:Per Plan §8 file ownership, the D-track form lives in a sibling to
issues.rsso the W1 list/detail file stays the merge-conflict boundary for its own width.rm-servermerges both routers.Behavior
/issues/<id>(POST-redirect-GET)Subject is required.Subject must be 255 characters or fewer.NoneinNewIssueSome(String)User's input is preserved on validation failure (no retyping). The kit HTML-escapes the echoed values on re-render — XSS-safe by construction.
#99 trap-spellbook pre-flight (5 questions cleared)
NewIssuestore row (already canonical) + the kit'sFormSourceZero diagnostic signatures present.
Out of scope (named, gated)
PATCH /issues/:id) — D1.2 follow-on; the kit'sFormSource.record_idis the seam already present.csrf_token: ""for now; rm-auth integration is its own follow-on (rolling token enforcement applies once the auth infra is shared).IssueRow. When they do, the form grows one input per FK; no chrome change./issueslist page — one-line follow-on (literally<a href="/issues/new">New issue</a>in the chrome).Build
1.95.cargo test -p rm-handlers --lib→ 89 passed (was 75 after feat(rm-handlers): D2 — issue list filter, sort, paginate (17-yr Redmine UX) #21; +14 new — 6 unit onvalidate(), 8 integration).cargo fmt --checkclean;cargo clippy --all-targets -- -D warningsclean (matching CI's exact gate).Cross-refs
html_form.rs—FormSource/FormFieldSource/render_formconsumed here.