Show recently sent request forms on the builder (PMS-764) #519

Merged
longjacksonle merged 2 commits from feat/PMS-764-recently-sent-panel into main 2026-08-12 05:27:26 +02:00

PMS-764. A sent request form had exactly one status surface and nothing led to it.

What it was

/companies/{id} has a "Request forms" card: every link sent to that client, with status and expiry. It is good, and it is the only place any of that appears anywhere in the SPA (CompanyRequestFormsCard, the sole reader of link status).

/admin/forms lists the definitions with Edit, Preview and Send, and refers to nothing ever having been sent. You send a form, get a toast reading "Request form sent to someone@example.com", and the page you are standing on looks exactly as it did before. The send modal does say "you ... see what has already been sent, on their company page" - but on its second step, after a company has been picked, so it is read while sending rather than when wondering, and it is prose, not a link.

What it is now

A "Recently sent" panel under the definitions: Form, Client, Sent to, Status, Expires, newest first.

  • The Client cell links to that company's detail page. That is the way in the ticket asked for: the full per-client history, and everything else about the client, is one click from the row.
  • It refreshes when a send completes, so the row appears the moment it is created. That is the disclosure that matters - the answer to "where did that go?" turns up in the place you were already looking, at the moment you first ask.
  • Capped at the 8 most recent, and it says how many it is not showing. A truncated list that keeps quiet reads as "this is everything", which is the wrong impression from a panel whose job is telling you what has been sent.
  • Empty state names Send on a form above as the way to start.

No server change

GET /form-request-links already takes company_id as an optional filter; without it mokosh-server returns the tenant's whole list, ORDER BY created_at DESC, and the DTO already carries company_id and company_name beside the form name, recipient, expiry and used_at. Status is derived through the same RequestLink::status the company card uses, so the two surfaces cannot come to disagree about what Submitted means (there, Submitted deliberately wins over Expired: a link used before it lapsed is still a request that came in).

Known limitation, deliberately accepted

The endpoint has no limit, so the tenant-wide fetch returns every link the tenant has ever issued and the panel renders the first eight. Rows are small and current volumes are tens, so it is not worth coupling a frontend fix to a server deploy today. If a tenant's history grows into the thousands, the endpoint should take ?limit= and this panel should pass it. Say the word and I will open that as a follow-up.

Not doing

  • A sent count per definition row. Needs a server-side aggregate, and answers a different question ("how popular is this form") than the one asked ("did they reply").
  • Anything about notifying on submission. A submitted form already becomes a ticket; that is separate from discoverability.

Checks

just check clean, just test 315 pass, three of them new (the cap reports what it hides, a short or empty list claims nothing hidden, and the server's newest-first ordering is preserved).

Worth a look on the deploy, since the point of it is what a new user sees: open Request Forms, send one, and confirm the row appears with "Awaiting reply" and the client's name as a link.

PMS-764. A sent request form had exactly one status surface and nothing led to it. ## What it was `/companies/{id}` has a "Request forms" card: every link sent to that client, with status and expiry. It is good, and it is the only place any of that appears anywhere in the SPA (`CompanyRequestFormsCard`, the sole reader of link status). `/admin/forms` lists the definitions with Edit, Preview and Send, and refers to nothing ever having been sent. You send a form, get a toast reading "Request form sent to someone@example.com", and the page you are standing on looks exactly as it did before. The send modal does say "you ... see what has already been sent, on their company page" - but on its second step, after a company has been picked, so it is read while sending rather than when wondering, and it is prose, not a link. ## What it is now A **"Recently sent"** panel under the definitions: Form, Client, Sent to, Status, Expires, newest first. - **The Client cell links to that company's detail page.** That is the way in the ticket asked for: the full per-client history, and everything else about the client, is one click from the row. - **It refreshes when a send completes**, so the row appears the moment it is created. That is the disclosure that matters - the answer to "where did that go?" turns up in the place you were already looking, at the moment you first ask. - **Capped at the 8 most recent, and it says how many it is not showing.** A truncated list that keeps quiet reads as "this is everything", which is the wrong impression from a panel whose job is telling you what has been sent. - Empty state names Send on a form above as the way to start. ## No server change `GET /form-request-links` already takes `company_id` as an **optional** filter; without it mokosh-server returns the tenant's whole list, `ORDER BY created_at DESC`, and the DTO already carries `company_id` and `company_name` beside the form name, recipient, expiry and `used_at`. Status is derived through the same `RequestLink::status` the company card uses, so the two surfaces cannot come to disagree about what Submitted means (there, Submitted deliberately wins over Expired: a link used before it lapsed is still a request that came in). ## Known limitation, deliberately accepted The endpoint has no `limit`, so the tenant-wide fetch returns every link the tenant has ever issued and the panel renders the first eight. Rows are small and current volumes are tens, so it is not worth coupling a frontend fix to a server deploy today. If a tenant's history grows into the thousands, the endpoint should take `?limit=` and this panel should pass it. Say the word and I will open that as a follow-up. ## Not doing - **A sent count per definition row.** Needs a server-side aggregate, and answers a different question ("how popular is this form") than the one asked ("did they reply"). - **Anything about notifying on submission.** A submitted form already becomes a ticket; that is separate from discoverability. ## Checks `just check` clean, `just test` 315 pass, three of them new (the cap reports what it hides, a short or empty list claims nothing hidden, and the server's newest-first ordering is preserved). Worth a look on the deploy, since the point of it is what a new user sees: open Request Forms, send one, and confirm the row appears with "Awaiting reply" and the client's name as a link.
PMS-764: a sent request form had exactly one status surface, the "Request forms" card on the company detail page, and nothing led to it. You sent a form from /admin/forms, got a toast reading "Request form sent to someone@example.com", and the page you were standing on looked exactly as it had before. Whether the client ever replied lived on a page you had to already know about. The send modal does say "you ... see what has already been sent, on their company page", but only on its second step, after a company is picked, so it is read while sending rather than when wondering, and it is prose rather than a link.

A "Recently sent" panel now sits under the definitions: form, client, recipient, status, expiry, newest first. The client cell links to that company's detail page, which is the way in the ticket asked for: the full per-client history, and everything else about them, is one click from the row. It refreshes when a send completes, so the row appears at the moment it is created, which is when "where did that go?" is actually being asked.

No server change. `GET /form-request-links` already takes `company_id` as an OPTIONAL filter and returns the tenant's whole list without it, ordered newest first, with each row already naming its client. Status is derived by the same `RequestLink::status` the company card uses, so the two surfaces cannot come to disagree about what Submitted means.

Capped at the 8 most recent, and the panel says how many it is not showing rather than presenting a truncated list as the whole story. The endpoint has no limit of its own, so the fetch still returns every link the tenant has issued; rows are small and volumes are tens, so that is not worth coupling this to a server deploy today. If a tenant's history grows into the thousands the endpoint should take `?limit=` and this panel should pass it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U7UHqTQ4tXXoHQ1sbXcavx
test(forms): cover the recently-sent cap
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 2m35s
Create release / Create release from merged PR (pull_request) Has been skipped
28c46bbeea
PMS-764: the panel shows the newest eight and reports the rest as a count. A truncated list that keeps quiet about being truncated reads as "this is everything", which is the wrong impression from a panel whose whole job is telling you what has been sent. Also that a short list claims nothing hidden, that an empty one does the same, and that the server's newest-first ordering is preserved, since reordering it would stop "recently sent" meaning recently sent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U7UHqTQ4tXXoHQ1sbXcavx
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-08-12 05:25:16 +02:00
longjacksonle deleted branch feat/PMS-764-recently-sent-panel 2026-08-12 05:27:26 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!519
No description provided.