Skip to content

fix(client): align term, smart-tag and settings icons with the design system - #343

Merged
centralcmd merged 3 commits into
mainfrom
claude/admiring-hawking-jo8tfp
Oct 22, 2026
Merged

centralcmd merged 3 commits into
mainfrom
claude/admiring-hawking-jo8tfp

Conversation

@centralcmd

@centralcmd centralcmd commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Summary

Terms and smart tags. The New term item in the contract row menu used sell (price tag). The design system (Contracts.jsx) and every other term surface (term dialog, Terms count, term-changed event, term history chart) use the typeset §. Fixed. Because sell had already meant Terms in the contract record, smart tags there had been moved to local_offer. They are back on sell, the same as on accounts, properties and the AccountSmartTagsSection default.

Concept Glyph
Term § (gavel where only a font ligature works, e.g. settings rows)
Smart tag sell
Transaction tag local_offer

System settings. 15 rows and 1 group icon differed from the design system's catalogue (system-settings-data.js). The client now uses the design system's icon wherever both sides declare the row, so all 66 rows match.

  • The design system lacked five Contracts rows the client already ships: terms cap, smart-tags cap, the "Ending soon" and "Upcoming transactions" windows, and the upcoming-transactions cap. They were added there.
  • The terms cap uses gavel, because the icon font has no § ligature.
  • The upcoming-transactions cap uses receipt_long, so it no longer shares an icon with the contracts-in-summary cap in the same group.

Design-system artifact: versions 37 and 38.

Type of Change

  • Bug fix

Testing Steps

  1. Contracts → open a row's ⋯ menu → New term shows §.
  2. The contract record's counts strip and Smart tags section show sell.
  3. dotnet test Odyssey.Client.Tests: 3730 passed, 0 failed.
    • A new source lint, New_term_carries_the_term_glyph, covers the menu glyph.
    • SettingRowIconTests confirms every new settings icon exists in the bundled font.

Related Issues

None.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SyMiAsjC8g6gxvnRddT1Ks

The contract row menu's "New term" item led with `sell`, the price-tag
glyph, where the design system (Contracts.jsx) and every other term
surface — the term dialog, the record's Terms count, the term-changed
event and the term history chart — use the typeset `§`.

That same misuse is why the contract record moved its smart-tag glyph to
`local_offer`: `sell` already meant Terms in its counts strip. With terms
on `§`, the contract host returns to `sell` for smart tags, matching the
account and property records and the AccountSmartTagsSection default;
`local_offer` stays the transaction-tag glyph. The design system's
Contracts.jsx counts strip was aligned to `sell` in the same change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyMiAsjC8g6gxvnRddT1Ks
Fifteen settings rows and one group carried icons the design system's
catalogue (system-settings-data.js) does not use, among them the price-tag
`sell` on the terms cap. The client now takes the design system's icon
for every row both sides declare.

The design system lacked five Contracts rows the client already ships
(terms cap, smart-tags cap, the ending-soon and upcoming-transactions
windows, and the upcoming-transactions cap); they were added there in the
same change. The terms cap uses `gavel`: the icon font has no `§`
ligature, and the row must not read as a tag. The upcoming-transactions
cap moved to `receipt_long` so it no longer shares `format_list_numbered`
with the contracts-in-summary cap in the same group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyMiAsjC8g6gxvnRddT1Ks
@centralcmd centralcmd changed the title fix(client): use the § term glyph on the contract row's new term action fix(client): align term, smart-tag and settings icons with the design system Oct 22, 2026

Copy link
Copy Markdown
Owner Author

build-and-test failed on a6a29ad in one test: ContractPartyTileTests.View_leads_the_menu_and_opens_the_accounts_page_for_an_account_party. It expected the URL to end in /accounts and got http://localhost/.

I don't think this PR caused it:

  • That commit only changes icon strings in the Settings.razor.cs catalogue. The test renders ContractDetailView and clicks the tile's View item, which doesn't touch the settings catalogue.
  • The same test passed on this PR's previous head (41fb044).
  • Locally it passes 5 of 5 runs, and the full Odyssey.Client.Tests suite passes (3730 tests).

My guess is a timing race: the detail view's async sections re-render the menu between FindAll and Click, so the click can land on a detached item. No fix for it exists yet. I'm re-running the failed job once. If it fails again I'll treat the failure as real.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

✅ PR was reviewed by Security Agent. No security-relevant changes detected: Approved.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

✅ PR was reviewed by Accessibility Agent and no issues were found: Approved.

Scope checked (frontend changes present, icon-only): the "New term" row action now uses the literal § glyph. OdsMenu.razor routes non-ligature icons to odc-menu-item-glyph with aria-hidden="true", so the item's accessible name stays "New term". The smart-tag count icon (sell) goes through OdsRecordCount/OdsCollapsible as a decorative glyph beside a text label. Dropping the explicit Icon="local_offer" falls back to the sell default, with no change to the section's name, role or state. The Settings catalogue icon swaps are string-only; the labels and descriptions that carry the meaning are unchanged, so nothing is conveyed by icon alone (1.1.1, 1.4.1 unaffected). No changes to focus, keyboard, contrast, ARIA or live regions.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

✅ PR was reviewed by Senior Architect Agent and no blocking issues were found: Approved.

Scope: client-only icon changes, so there is no API, DTO, migration or auth impact. Checked against the live tree:

  • OdsMenu renders a non-ligature Icon as a typographic glyph (odc-menu-item-glyph exists in odyssey-components.css), so "§" on the "New term" item is valid. It matches AddTermDialog and the Terms OdsRecordCount.
  • Every new settings icon is in Odyssey.Client.Tests/MaterialIconsLigatures.txt, which is what SettingRowIconTests asserts against, so none can fall back to literal text. I checked all 15 new names.
  • Dropping Icon="local_offer" from ContractDetailView and from the test harness falls back to the AccountSmartTagsSection default "sell". That matches the ContractsCard count and the account and property hosts.
  • No other test or source still pins the removed contract local_offer or the old settings icons.

Non-blocking nit: the new AccountSmartTagsSection.Icon doc comment says transaction tags carry local_offer. TransactionDetailPanel.razor:41 uses sell for a transaction's tags, while OdsTransactionTagPicker and NavModel use local_offer. Consider narrowing the wording to "tag administration" or dropping that clause so the comment doesn't claim more than the tree shows.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

❌ Senior Tester Agent review of PR #343 found one gap and one minor item. I did not run the suite (no code behaviour changed); this is a read of the diff and the surrounding tests.

What is sound

  • New_term_carries_the_term_glyph (Odyssey.Client.Tests/ContractsCardRowActionTests.cs, around line 120) is a good lint. It reads ContractsCard.razor.cs through CardSource(), which strips comments, so a doc comment cannot satisfy it. It pins Icon = "§" directly against Label = "New term". It would fail on the old sell value, so it works as a regression test.
  • Dropping .Add(s => s.Icon, "local_offer") from the ContractSmartTagSurfaceTests harness (line 767) matches the removal of Icon="local_offer" in ContractDetailView.razor. The harness now renders the component the way the page does.
  • The settings icon changes are covered by the existing SettingRowIconTests, which checks every catalogue icon against the bundled ligature list.

1. Missing regression guard for the smart-tag half of the fix (principle: untested behaviour change)

  • The PR moves smart tags from local_offer to sell in three places:
    • the OdsRecordCount in ContractsCard.razor;
    • the contract record's AccountSmartTagsSection icon;
    • the Icon parameter default.
  • Nothing asserts any of them, and Odyssey.Client.Tests has no "sell" assertion at all. Removing the explicit Icon from the harness also removes the only place the section icon was pinned.
  • Someone could change ContractsCard.razor back to local_offer, or have ContractDetailView.razor pass an icon again, and every test would stay green. Only the New term half would be caught.
  • Suggested fix, in the same style as the new lint:
    • Add a source lint on ContractsCard.razor (comments stripped): Assert.Matches(@"new OdsRecordCount\(\s*""sell""\s*,\s*c\.SmartTagCount", source), plus Assert.DoesNotContain("\"local_offer\"", source).
    • Add an assertion on ContractDetailView.razor that the AccountSmartTagsSection tag has no Icon= attribute.
    • Or add a bUnit assertion in ContractSmartTagSurfaceTests that the section header glyph is sell.
  • The glyph table in the PR description (Term §, Smart tag sell, Transaction tag local_offer) could also be a single test that checks the three do not collide.

2. Minor: two intents in the PR have no test

  • The description says receipt_long was chosen so the upcoming-transactions cap no longer shares an icon with contractMaxSummaryContracts in the same group. No test asserts that icons within a settings group are distinct. The ligature-existence test cannot catch a later duplicate.
  • Optionally, add a distinct-icons-per-section guard to SettingRowIconTests, or accept the gap. The design-system parity check is correctly skipped in CI per CLAUDE.md, so I am not asking for the artifact's values to be pinned as literals.

Notes, no action required

  • The Icon parameter on AccountSmartTagsSection now has no caller that overrides it. It is harmless, but it could be removed later.
  • The PR is a draft, so none of this blocks the draft state. Item 1 should be addressed before it is marked ready.

Generated by Claude Code

Copy link
Copy Markdown
Owner Author

✅ "PR was reviewed by Senior Frontend Agent and no issues were found: Approved."

Verified against design-system artifact version 1792697499-128a (frontend scope only):

  • Contracts.jsx uses § for the "New term" action and the Terms count, and sell for the Smart tags count. The client now matches.
  • AccountSmartTagsSection.jsx defaults to sell, so dropping the local_offer override in ContractDetailView.razor is correct.
  • OdsMenu renders a non-ligature Icon as a literal glyph, so Icon = "§" renders properly.
  • Settings: every icon changed in Settings.razor.cs equals the icon system-settings-data.js declares for the same row, including import_contacts for the Journal import & export group. The fileAnalysis* rows are keyed differently in the artifact (ai*), and I matched those by title and icon. SettingRowIconTests covers the font-ligature allow-list.
  • No stray local_offer is left on a contract or smart-tag surface. The remaining uses are transaction tags, nav and tax statements, which is the intended split.

Non-blocking nit:

  • AccountSmartTagsSection.Icon now has no caller that overrides it. Consider removing the parameter in a follow-up, or leave it if another host is expected to need it.

Generated by Claude Code

The review found nothing guarding the smart-tag half of the glyph fix:
the harness override was the only test naming the section's icon, and
dropping it left a revert to local_offer passing. A source lint now pins
the counts-strip glyph and that ContractDetailView takes the section's
sell default rather than overriding it.

The section's doc comment no longer claims transaction tags always use
local_offer; the transaction detail panel shows them under sell, as the
design system does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyMiAsjC8g6gxvnRddT1Ks

Copy link
Copy Markdown
Owner Author

Responses to the review round, addressed in 1dcdd7d:

  • Tests 1 (no guard for the smart-tag glyph): fixed. The new test Smart_tags_carry_the_smart_tag_glyph checks two things: the counts strip uses OdsRecordCount("sell", c.SmartTagCount…), and ContractDetailView puts no Icon= on AccountSmartTagsSection. I checked that it fails when the local_offer override is put back.
  • Tests 2 (icons distinct within a group): not added. The catalogue repeats icons within groups by design. All four import/export groups use sd_storage for both size caps, Email uses send twice and Journal limits uses link twice, all matching the design system. A per-group uniqueness guard would fail on rows that are correct. receipt_long was a one-off tidy-up, not a rule.
  • Architect nit (doc comment overstates local_offer): fixed. The comment now only says smart tags use sell and terms use §. The transaction detail panel shows tags with sell, as the design system's Transactions.jsx does.
  • Frontend nit (AccountSmartTagsSection.Icon has no overriding caller): keeping it. It mirrors the design system component's icon prop, which defaults to sell. Removing it would be a separate API change to the component, beyond this PR's scope.

Generated by Claude Code

Copy link
Copy Markdown
Owner Author

✅ PR was reviewed by Senior Tester Agent and no issues were found.

This is a re-review of head 1dcdd7d against my earlier findings. I read the diff and did not run the suite.

  • Finding 1 is resolved. Smart_tags_carry_the_smart_tag_glyph pins new OdsRecordCount("sell", c.SmartTagCount… in ContractsCard.razor. It also asserts that the AccountSmartTagsSection tag in ContractDetailView.razor sets no Icon=. Both checks read comment-stripped source (RazorSource removes @* … *@), so a comment cannot satisfy them. The author reports the test fails with the local_offer override restored.
  • Finding 2 is withdrawn. The catalogue repeats icons within groups on purpose to match the design system, for example sd_storage in the import/export groups. A per-group uniqueness guard would fail on correct rows.

Generated by Claude Code

@centralcmd
centralcmd marked this pull request as ready for review October 22, 2026 20:10
@centralcmd centralcmd self-assigned this Oct 22, 2026
@centralcmd
centralcmd merged commit 0282fce into main Oct 22, 2026
8 checks passed
@centralcmd
centralcmd deleted the claude/admiring-hawking-jo8tfp branch October 22, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants