feat(components): give every detail page a breadcrumb trail #497

Merged
longjacksonle merged 7 commits from feat/PMS-746-detail-breadcrumbs into main 2026-08-09 04:24:19 +02:00

Closes PMS-746. Follow-up to PMS-745 (#496), which fixed the reported Assets case and surfaced that the gap was not specific to Assets.

Before this PR, 4 of 9 non-portal detail pages had a breadcrumb trail (contract, rate card, company, and asset as of #496). On the other five, opening a record left the browser Back button or the sidebar as the only route back to its list. Their New/Edit siblings mostly already had a trail from MAPPS-294, which is what made the detail pages read as an oversight rather than a decision.

Now consistent across all nine: ticket, project, invoice, quote and contact detail each render <List> > <record>.

One shared builder, not five copies. detail_breadcrumbs(list_label, list_route, title) lives next to Breadcrumbs in components/layout.rs. It is a plain function rather than a component so a trail can be asserted without standing up a router, which is what its test does. AssetDetailPage moves onto it in the last commit: PMS-745 landed a page-local asset_detail_crumbs before there was anywhere shared to put it, so this is the same output with one implementation. That page-local test is removed along with the function it covered.

Two judgement calls worth flagging in review:

  • Quotes lost its "Back to Quotes" button. Quote detail was the only detail page with a back affordance, but it sat in the action cluster, which is not where any other page puts it. The crumb is the same destination in the standard place, so keeping both would have been two routes to the list on one page. The action cluster now holds only verbs that act on the quote.
  • The contact trail is flat: Contacts > <name>. A contact also belongs to a company, and ticket and project detail can likewise be reached from a company context banner. A parent that reflects how the page was reached is a real feature with its own design questions; this PR keeps the trail predictable and leaves that as a separate ticket if anyone asks for it.

Out of scope and deliberately untouched: the Knowledge Base, which has its own KbBreadcrumb reflecting a real category hierarchy, and the portal detail pages, which are a different shell.

269 lib tests pass. clippy -D warnings, fmt, the theme-token guard and the cancel-route guard are all clean.

Verify on staging: open a ticket, a project, an invoice, a quote and a contact; each should show its list name followed by the record, and the first crumb should return to the list. On the quote page, confirm the header no longer carries a "Back to Quotes" button.

Closes PMS-746. Follow-up to PMS-745 (#496), which fixed the reported Assets case and surfaced that the gap was not specific to Assets. Before this PR, 4 of 9 non-portal detail pages had a breadcrumb trail (contract, rate card, company, and asset as of #496). On the other five, opening a record left the browser Back button or the sidebar as the only route back to its list. Their New/Edit siblings mostly already had a trail from MAPPS-294, which is what made the detail pages read as an oversight rather than a decision. Now consistent across all nine: ticket, project, invoice, quote and contact detail each render `<List> > <record>`. **One shared builder, not five copies.** `detail_breadcrumbs(list_label, list_route, title)` lives next to `Breadcrumbs` in `components/layout.rs`. It is a plain function rather than a component so a trail can be asserted without standing up a router, which is what its test does. `AssetDetailPage` moves onto it in the last commit: PMS-745 landed a page-local `asset_detail_crumbs` before there was anywhere shared to put it, so this is the same output with one implementation. That page-local test is removed along with the function it covered. **Two judgement calls worth flagging in review:** - **Quotes lost its "Back to Quotes" button.** Quote detail was the only detail page with a back affordance, but it sat in the action cluster, which is not where any other page puts it. The crumb is the same destination in the standard place, so keeping both would have been two routes to the list on one page. The action cluster now holds only verbs that act on the quote. - **The contact trail is flat: `Contacts > <name>`.** A contact also belongs to a company, and ticket and project detail can likewise be reached from a company context banner. A parent that reflects how the page was reached is a real feature with its own design questions; this PR keeps the trail predictable and leaves that as a separate ticket if anyone asks for it. Out of scope and deliberately untouched: the Knowledge Base, which has its own `KbBreadcrumb` reflecting a real category hierarchy, and the portal detail pages, which are a different shell. 269 lib tests pass. clippy `-D warnings`, fmt, the theme-token guard and the cancel-route guard are all clean. **Verify on staging:** open a ticket, a project, an invoice, a quote and a contact; each should show its list name followed by the record, and the first crumb should return to the list. On the quote page, confirm the header no longer carries a "Back to Quotes" button.
Every detail page needs the same two crumbs: the list it came from, then the record itself, inert because it is the page you are already on. PMS-745 built that by hand for Assets; five more pages need it, so the builder moves next to Breadcrumbs instead of being copied per page.

Kept as a plain function rather than a component so a page's trail can be asserted without standing up a router.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011myibMMwyb6za3GVWJGkiX
The header carried a title and actions but no breadcrumbs, so an open ticket had no in-page route back to the list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011myibMMwyb6za3GVWJGkiX
Same gap as the ticket page: title and actions, no route back to the list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011myibMMwyb6za3GVWJGkiX
Same gap as the ticket and project pages: title and actions, no route back to the list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011myibMMwyb6za3GVWJGkiX
The quote detail page was the one detail page with a back affordance, but it sat in the action cluster as a "Back to Quotes" button, which is not where any other detail page puts it. Replaced by the standard trail, leaving the actions holding only verbs that act on the quote.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011myibMMwyb6za3GVWJGkiX
Company detail already had a trail; contact detail did not.

The trail stays flat (Contacts > name) even though a contact also belongs to a company. A company-aware parent would have to depend on how the page was reached, which is a bigger change than this consistency pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011myibMMwyb6za3GVWJGkiX
refactor(assets): build the detail trail with the shared helper
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 1m25s
Create release / Create release from merged PR (pull_request) Has been skipped
eeafda621f
PMS-745 landed a page-local asset_detail_crumbs a few days before there was a shared builder. Same output, one implementation; the page-local test goes with it, covered now by the builder's own test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011myibMMwyb6za3GVWJGkiX
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-08-09 04:24:06 +02:00
longjacksonle deleted branch feat/PMS-746-detail-breadcrumbs 2026-08-09 04:24:19 +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!497
No description provided.