Skip to content

feat(contact-show): associated sites table, report actions, and a working map - #367

Open
jirhiker wants to merge 4 commits into
stagingfrom
feat/contact-show-page
Open

feat(contact-show): associated sites table, report actions, and a working map#367
jirhiker wants to merge 4 commits into
stagingfrom
feat/contact-show-page

Conversation

@jirhiker

@jirhiker jirhiker commented Sep 1, 2026

Copy link
Copy Markdown
Member

Reworks the contact show page: associated sites become a sortable table with per-row report actions, the contact details move into the main column, and the map renders again.

Associated sites: a table, not a card each

One card per site pushed everything else below the fold and gave no way to compare sites. They are now rows in a shadcn DataTable, sortable on every column, with row click through to the site.

The old card fetched each site's well record, latest reading, and sampler from inside a per-site component. A table cannot do that, so the fetching moves into useAssociatedSiteRows, which runs the queries with useQueries and flattens the results into the row model — which is also what lets the enriched columns sort. Row building is a pure buildAssociatedSiteRow, so the fallback chains can be tested without a data provider:

  • last checked: field event date → sample date → latest reading
  • checked by: contact + organisation → contact → sampler name

useQueries returns a fresh array every render, so the combine option does the memoising. Without it the table gets a new data identity on every pass and the page spins.

Report column

Field builds that well's sheet in place, so pulling one does not cost you the contact page. It renders FieldCompilationNotesPdf — the same component the bulk field-sheet export uses — so a sheet pulled from here matches one from a batch run.

A field sheet needs the well's whole payload, so useWellPdfData is armed by the click; arming on mount would fetch all of it for every row.

Owner hands off to the chemistry report exporter at /ocotillo/chemistry-report?thing_id=<id>, which owns the year picker and section toggles. That route arrives with #354 — the button is inert until it lands.

The map was rendering nothing

AssociatedSitesMapCard read coordinates off contact.things, and the contact endpoint returns its things without a current_location. No site ever passed the filter, so the card returned null and the whole map was absent — not empty, absent. This predates the branch.

It now reads the same enriched rows, whose per-well records do carry coordinates, and which already hold the name and show path the popup needs. useAssociatedSiteRows moved up to the page, so the table and map share one fetch rather than each running its own.

The popup's "View details" opens in a new tab. Refine's Link is router-bound and cannot target one, so it is a plain anchor whose href carries the basename the router would otherwise apply — the same treatment openInNewWindow uses.

Also

  • Contact details move out of the narrow right-hand column into the main flow; the two-column Grid is gone.
  • Release status is dropped from the contact details card.

The shadcn table is a port

ui/table.tsx, ui/popover.tsx, DataTable/* and @tanstack/react-table are none of them on staging — they exist only on #347, which targets BDMS-1133 rather than staging. They are copied here unchanged, so whoever merges second gets a clean resolution rather than a real conflict.

One file will conflict: DataTable/index.ts. This branch exports the four components it needs; #347's exports eight. Take #347's version.

Verification

  • tsc --noEmit clean
  • vite build clean — only the pre-existing chunk-size notices
  • vitest run with the prism mock server up: 51 files, 431 passed, 1 skipped, 0 failed
  • biome lint src/ — 429 warnings, identical to staging's count; none from the new files
  • 13 new tests over buildAssociatedSiteRow, getSiteShowPath and latestObservation

Exercised against a running dev server, not just tests: table and sorting, the Owner and Field buttons (Field produced a sheet; Owner landed on /ocotillo/chemistry-report?thing_id=1), all three map pins, the popup link's target/rel/href, and the Edit panel.

Worth a reviewer's eye: AssociatedSitesMapCard's props changed from things to rows, so it now depends on useAssociatedSiteRows. It is only used on this page, but it is no longer a generic component.

🤖 Generated with Claude Code

One card per associated well pushed the rest of the page below the fold and
gave no way to compare sites. They become rows in a shadcn DataTable,
sortable on every column, and the contact details move out of the narrow
right-hand column into the main flow.

The card fetched each site's well record, latest reading, and sampler from
inside a per-site component. A table cannot do that, so the fetching moves
into useAssociatedSiteRows, which runs the queries with useQueries and
flattens the results into the row model — which is also what lets the
enriched columns sort. Row building is a pure function so the fallback
chains (which source wins for the last-checked date, how a sampler is
named) can be tested without a data provider.

Elevation and coordinates are dropped; the map card below already places
the sites.

The shadcn table components and @tanstack/react-table are ported from
#347, which is not yet on staging. Copied unchanged so the two resolve
cleanly when that lands.
Adds a Report column to the associated-sites table. Field builds that
well's sheet in place, so pulling one does not cost the contact page.
Owner hands off to the chemistry report exporter, which owns the year
picker and section toggles.

Field renders FieldCompilationNotesPdf — the same component the bulk field
sheet export uses — so a sheet pulled from here matches one from a batch
run. A sheet needs the well's whole payload, so useWellPdfData is armed by
the click; arming on mount would fetch all of it for every row.

Owner links to /ocotillo/chemistry-report, which arrives with #354. The
button is dead until that lands.

Two fixes found while verifying this in the browser:

- Import the PDF component by path, not through '@/components'. That barrel
  re-exports ContactShow, so importing through it put this module in a cycle
  with the table rendering it and the cell was undefined at render time,
  which took out the page — the Edit button included.
- Round depths for display. They arrive at full float precision
  (272.08212570033396), which a table column cannot carry.
The map read coordinates off contact.things, which the contact endpoint
returns without a current_location — so no site ever passed the filter and
the card returned null. Nothing was rendering at all.

Point it at the enriched rows instead. The per-well records those are built
from do carry coordinates, and they already hold the name and show path the
popup needs, so the card's own getShowPath goes with them.

useAssociatedSiteRows moves up to the page, which passes the same rows to
the table and the map rather than each fetching its own.
…e status

The popup's "View details" now opens in a new tab, so following a site does
not lose the contact you were reading. Refine's Link is router-bound and
cannot target one, so it becomes a plain anchor; the href carries the
basename the router would otherwise have applied.

Also drops the release status from the contact details card.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

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.

1 participant