chore(release): v1.2.0 - #362
Merged
Merged
Conversation
…e-new-or-remove-old-cypress-tests test(cypress): add new tests for current UI & remove outdated tests
Scaffolding for the inline geothermal data-upload grid: - geothermal-data-provider fetcher now sends a Bearer token from getAccessToken() and refreshes once on 401, mirroring the ocotillo provider. Prerequisite for writing edits back to the geothermal API. - Register geothermalResources so refine is aware of them for routing and access control. - Mount GeothermalRoutes at /geothermal/*; the UI was previously scaffolded but unreachable. Nav still comes from config/navigation.ts, so nothing new appears in the sidebar yet; a gated nav entry lands with the grid page. Also adds the proposed-approach doc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lift the Glide Data Grid patterns out of the throwaway DataGridPage specimen into an entity-agnostic component under src/components/grid: - EditableDataGrid<T>: generic over row shape + a GridColumnSpec column model (id, title, kind, editable, getValue/setValue, validate, onClick). Owns theme, auto-sizing, cell-kind dispatch (text/number/uri), and edit-to-row mapping; edits lift back via onRowsChange so the parent keeps the source of truth (needed for the Phase 3 batch save). - gdgTheme: shared light/dark theme constants + useGdgTheme hook, off ColorModeContext. Removes the duplicated theme block from the demo. - useElementSize: callback-ref ResizeObserver hook that attaches even when the grid mounts inside a portal (fixes the bulk-add modal case). Refactor DataGridPage (main grid + bulk-add modal) to consume the new component, proving the extraction against real data. The specimen stays as reference until the geothermal grid page lands, then gets deleted. validate on GridColumnSpec is defined but not yet surfaced — reserved for inline cell validation in Phase 3. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an inline-editable spreadsheet of the records belonging to a well,
built on the reusable EditableDataGrid from Phase 1.
- GeoThermalRecordsGrid (pages/geothermal/wells/records-grid.tsx): reads
wells/{id}/records through the geothermal provider, renders an editable
grid, and accumulates cell edits in local state with a dirty-row count.
No write-back yet — a disabled "Save changes" button marks where the
Phase 3 batch save lands.
- Admin-gated via canManageGeothermal (useAccessCapabilities), per the
BDMS-878 decision that both editing and entering rows require
Geothermal.Admin. Query is disabled until the check passes.
- Route wells/records-grid/:id mounted in GeothermalRoutes; reachable
from the well show page via a gated "Open data-entry grid" button.
Columns are PROVISIONAL — they mirror the current IWellRecord shape
(11 string fields) since the geothermal API contract is not finalized.
The column list is a single localized array to swap once the real
field set and types are pinned.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire write-back for the geothermal records grid. No autosave — edits
accumulate and a "Save changes" action flushes every dirty row.
Provider:
- geothermal create/update now map FastAPI/Pydantic 422/409 payloads
({ detail: [{ loc, msg }] }) to Refine fieldErrors, mirroring the
ocotillo provider. Shared throwOnWriteError + buildFieldErrors helpers;
strips the leading body. segment from field paths.
EditableDataGrid:
- new cellErrors(rowIndex) => { colId: msg } prop; errored cells render
with an error-tinted background so rejected fields surface inline.
Records grid:
- Save flushes dirty rows via provider.update through Promise.allSettled
(no server bulk endpoint → one request per row). Per-row tracking:
saved rows update their snapshot and clear dirty; failed rows stay
dirty for retry and tint their rejected cells from fieldErrors. Toolbar
shows an "n saved, m failed" summary and disables Save while in flight
or when nothing is dirty.
New-row creation (create path) is Phase 4; this only saves edits to
existing rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add new-record entry to the geothermal records grid, flushing through the Phase 3 batch save via the provider create path. - "Add N rows" appends blank rows to the same grid. New rows carry a client-only temp id (new:N) in OBJECTID until the server assigns a real one, so their ID cell stays blank and create is distinguished from update. No separate bulk-add modal / upload step — entry happens inline in the same spreadsheet, so Glide's copy/paste-from-Excel works for new rows too. - Save now builds a pending-op list: changed existing rows -> update, non-blank new rows -> create (blank appended rows are ignored). Runs all through Promise.allSettled with per-row tracking. A created row adopts the server-returned record (real OBJECTID) and stops being new; failed rows stay pending for retry with their cells tinted from fieldErrors. Unsaved count and summary now include creates. Assumes create returns the created record; reconciliation of the temp row relies on that response. The demo BulkAddModal stays as its own reference and is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the pure save logic out of the records-grid page into
recordsGridLogic.ts (rowKey, isNewRow, isBlankNew, makeBlankRecord,
flattenFieldErrors, computePendingOps) so it is unit-testable without
mounting the Glide canvas. The page imports from it; behavior unchanged.
Tests:
- recordsGridLogic.test.ts (9): computePendingOps splits changed
existing rows -> update and non-blank new rows -> create, skips blank
appended rows and unchanged rows, preserves row indices for mixed
batches; makeBlankRecord / isNewRow / isBlankNew / flattenFieldErrors.
- geothermal-data-provider.test.ts (5): create/update map 422 and 409
Pydantic payloads to fieldErrors (stripping the body. prefix, mirrored
onto .errors), throw the raw Response for non-validation statuses and
for a 422 without a detail array, and return { data } on success.
fetch + getAccessToken + settings are mocked.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flip SHOW_EXAMPLE_NAV to true. The sandbox "Example" section already carries a "Data Grid" item linking to /example/data-grid (the Glide Data Grid specimen); it was just gated off. No new plumbing — the item, route (App.tsx) and page (pages/example/DataGridPage.tsx) all exist. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Match the label users refer to. Only the visible label + tooltip change; the /example/* routes and specimen pages are untouched. Also add .claude/launch.json (npm run dev on :5173) for local preview. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Sandbox "Data Grid" item is the ocotillo-wells design specimen, not the geothermal grid. Add a distinct "Geothermal Records" entry that opens the real geothermal data-entry grid. - GeoThermalRecordsGridPicker: the geothermal grid is scoped to one well (records-grid/:id), so the Sandbox entry lands on a picker that lists geothermal wells and navigates into the chosen well's grid. Admin-gated (canManageGeothermal), same as the grid. - Route wells/records-grid (index) -> picker, alongside the existing wells/records-grid/:id -> grid. - Sandbox nav gains a "Geothermal Records" sub-item; the section's open/active detection now also matches the geothermal grid path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add canEnterGeothermalData(canManageGeothermal) = canManageGeothermal || import.meta.env.DEV. Under `vite` dev (and vitest) DEV is true, so the records grid, its well picker, and the well-show entry button skip the "You need the Geothermal Admin role" block for local testing. Production builds (`vite build`) set DEV=false, so the gate stays fully enforced when deployed. Single greppable helper (BYPASS_GEOTHERMAL_GATE) to remove if the bypass is ever unwanted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The local dev fallback for VITE_NMBGMR_GEOTHERMAL_API_URL was :8008, but the geothermal API runs on :8000. Prod is unaffected — it sets the env var explicitly; this only changes the empty-env local default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The geothermal wells live under thing/geothermal-well (mirroring
thing/water-well), not a top-level /wells. Update every geothermal
resource string:
- wells list (list page, records-grid picker) -> thing/geothermal-well
- well detail + bore (show page) -> thing/geothermal-well
- records (grid read + save, show page grid) ->
thing/geothermal-well/{id}/records
Route paths (/geothermal/wells/...) are unchanged — only the API
resources moved. Records nesting is inferred from the prior
wells/{id}/records pattern; adjust if the real records path differs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The thing/geothermal-well endpoint returns { items, total, page, size,
pages }, but the provider's getList returned the whole envelope as
`data`, so consumers got an object where they expected an array —
GeoThermalRecordsGridPicker crashed on `wells.map`.
- getList now unwraps { items, total } when present, still accepts a
bare array (older endpoints), and falls back to an empty list for any
other shape (never returns a non-array).
- Picker also filters out rows without a usable OBJECTID, since a Radix
SelectItem with an empty value throws.
- Tests: getList unwraps the envelope, handles a bare array, and returns
[] for an empty envelope.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GET /thing/geothermal-well returns snake_case fields (well_data_id,
name, county, well_type, status, operator, ...), not the guessed
OBJECTID/WellDataID. The picker filtered on OBJECTID (null), so all 8
wells were dropped ("No wells found").
- IWell: real geothermal well shape; well_data_id (UUID) is the id used
for detail/records routes.
- Picker: id/value = well_data_id, label = "name — county, well_type",
filter on well_data_id.
- List page: columns name/well_type/status/county/operator, row id and
edit/show targets = well_data_id.
- Show page: detail fields -> name / well_data_id / county.
Records grid + its columns (IWellRecord) are unchanged — the records
model stays; the /records endpoint is not implemented yet (404).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Spec for inventorying new geothermal wells via CSV upload or direct Glide-grid entry, feeding one editable grid + batch create. Grounded in the real thing/geothermal-well contract and reusing EditableDataGrid, the provider create + fieldErrors mapping, and the batch-save pattern. Decisions folded in: papaparse for CSV, create-all (server 409 on conflict), dropdowns for enum fields, Sandbox nav. Open: required-field set (not in OpenAPI) and dropdown value source. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First phase of the well-inventory feature (spec: docs/geothermal-well-inventory.md). An editable Glide grid for entering new geothermal wells directly. - GeoThermalWellInventory: EditableDataGrid over blank well drafts (WellDraft = well fields minus the server-assigned well_data_id). Columns for every user-entered field (text + number kinds); "Add rows" appends blank rows; a live "N wells to add" count tracks non-blank drafts. Admin-gated via canEnterGeothermalData (dev-bypassed). - Route wells/inventory; Sandbox nav gains "Geothermal Inventory" and its open/active detection covers the inventory path. Entries are local-only for now. Batch create (POST) is P2, CSV load P3, and dropdown/date/boolean editors + validation P4 — the "Create wells" button is present but disabled until P2. Verified: grid renders, cell edits commit, and the count updates live. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- accessControl.test.ts: geothermal resources (geothermal.dashboard, geothermal.geothermal_wells) are now registered, so the decision- complete matrix must account for them. They belong to the geothermal portal — granted to Geothermal roles (list/show), not AMP. Updated the expected routable set and per-scenario access accordingly. Fixes the Vitest Test Suite failure. - navigation.ts: gate SHOW_EXAMPLE_NAV on import.meta.env.DEV so the WIP Sandbox section doesn't ship to production (Copilot review). - records-grid.tsx: the ID column held a string OBJECTID (and "new:N" temp ids) in a number cell; switch to a read-only text cell, blank for new rows (Copilot review). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the inventory "Create wells" action. Every non-blank draft POSTs to thing/geothermal-well via the geothermal provider (one request per row — no server bulk endpoint), through Promise.allSettled with per-row tracking: - created wells drop out of the grid; a "N created" summary reports them. - failed rows stay for retry with their rejected cells tinted from the provider's Pydantic fieldErrors (422/409), and remapped to the surviving rows' new indices. - cleanDraft strips empty fields so the payload carries only entered data. - Create button enables only with non-blank rows and shows "Creating…" while in flight. Verified live: a filled row POSTs, the failure is tracked per row (the backend create endpoint isn't implemented yet — GET-only), the row is retained, and the summary shows "0 created, 1 failed". Success path (drop + count) mirrors the tested records-grid save. CSV load is P3; dropdown/date/boolean cells + client validation P4. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add CSV upload + a matching downloadable template to the inventory grid.
- inventoryFields.ts: extract the well-draft model (field lists, headers,
isBlankDraft, cleanDraft) so the grid, CSV, and tests share one source.
- inventoryCsv.ts (papaparse): parseCsvFile loads a File into well drafts;
mapRecordsToDrafts (pure) matches headers to fields case-insensitively
and trimmed, coerces number fields (dropping unparseable), collects
unknown headers, and skips empty rows. buildTemplateCsv emits a header
row of the canonical field names.
- inventory.tsx: "Upload CSV" (hidden file input) loads parsed rows into
the grid with a status line ("Loaded N rows · ignored columns: … · M
malformed rows skipped"); "Download template" saves the header CSV.
- Tests for the mapper (coercion, case-insensitive match, unknown-header
collection, empty-row skip) and the template header row.
Verified live: uploading a CSV populates the grid, maps recognized
columns, and reports an ignored unknown column. Dropdown/date/boolean
cells + client validation land in P4.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finish the inventory grid with typed editors and client-side validation. EditableDataGrid: - add 'boolean' (native checkbox) and 'dropdown' cell kinds; dropdown uses @glideapps/glide-data-grid-cells (allCells custom renderers) with a per-column `options` list. CellValue widened to include boolean. Inventory: - enum fields (well_type, well_class, status) render as dropdowns; has_geothermal_data as a checkbox. Provisional option lists in inventoryFields (ENUM_OPTIONS) — observed from live data, confirm with backend. - required-field validation (REQUIRED_FIELDS / missingRequired, also provisional): non-blank rows missing a required field tint those cells and block "Create wells", with a "N rows missing required fields" note. Validation errors merge with server fieldErrors for display. Verified live: a CSV load tints the incomplete row's required cells, shows the missing-rows note, and disables Create; the complete row is clean. No console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per decision, county/state are reverse-geocoded from latitude/longitude by the backend, auto-filling only when left blank. So the frontend does no geocoding and no longer requires county/state — latitude/longitude are the required location inputs; county/state stay optional (a user or CSV may still provide them, which the server keeps). - REQUIRED_FIELDS drops county/state (keeps name, api, well_type, latitude, longitude). - Spec §6/§6a document the server-side derivation contract. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a Save for later action that persists the non-blank inventory rows to localStorage, so entered/imported data survives navigation and reloads. - loadDraft/saveDraft/hasDraft helpers (guarded for unavailable storage). - On mount, restore any saved draft (+ a few blank rows) and show a "Restored N saved rows" note; otherwise start with blank rows. - "Save for later" button writes the current non-blank rows and shows a "Saved N rows for later" note; saving an empty grid clears the cache. - After a successful create, keep an existing saved draft in sync with the surviving rows so created wells don't reappear on reload. Verified live: load rows → Save for later → reload restores them with the status note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Set rowMarkers="number" on the inventory EditableDataGrid so each row shows its number in the left gutter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- LocationPickerModal: a Dialog with a react-map-gl satellite map; click
to drop/move a pin, confirm to return lat/lon. Rendered per row (keyed)
so it opens with that row's current coordinates.
- Inventory: a read-only "Location" column whose cell opens the picker
for its row ("📍 Pin on map", or the current coords); confirming writes
latitude/longitude back to that row.
- EditableDataGrid: onClick now receives the row index (needed to target
the right row); add an optional per-column `format` that overrides only
the displayed string, not the stored edit value.
- Coordinates display rounded to 7 decimal places (formatCoord) in the
lat/lon cells, the Location column, and the picker readout — full
precision is kept for save.
- Header: "Total depth" → "Total Depth (ft)".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-toolchain feat(biome.json): replace ESLint and Prettier with Biome
Adds a debounced search box to the top-left panel stack on the map view. Selecting a result fits the map to the result's bounding box (or eases to its center) and drops a marker; results are biased toward the current viewport center. Geocoding goes to Photon, komoot's OpenStreetMap-backed service. Photon needs no account or token, which keeps the map free of API keys the way basemaps.ts already does, and unlike Nominatim its usage policy permits search-as-you-type. Photon returns address components rather than a formatted label and reports `extent` as [minLon, maxLat, maxLon, minLat], so utils/geocode.ts composes the display label and reorders the box into the [west, south, east, north] order fitBounds expects. Results are filtered to the US client side, since Photon has no country parameter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two cost fixes now that the ephemeral path is proven working. The API container was provisioned 2 vCPU and 2Gi. The expensive part of startup -- migrate plus seed -- measured in seconds on the first successful run, so the headroom was buying nothing. Drop it to 1 vCPU and 1536Mi, taking the service from 3 vCPU / 4Gi to 2 vCPU / 3.5Gi across both containers. The sizing matters more than it looks: the database is the instance, so the service cannot scale to zero and bills continuously from the moment it is deployed until teardown. Second, deploying a preview with backend=staging now deletes any ephemeral API left over from an earlier ephemeral deploy of the same branch. Dropping the preview-backend label and pushing used to redeploy the frontend against staging and silently strand the old always-warm API, with nothing reclaiming it until the PR closed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A query like "socorro" matches both the city and the county relation, and both compose to "Socorro, New Mexico" — two identical rows in the dropdown that fly to different extents. Where a label repeats, Photon's own type classification is now appended to each of the colliding rows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…and-preview-deployments ci(preview): on-demand previews from any branch, teardown, ephemeral backend
…hed layers Layer resolution scored partial matches: exact 100, prefix 60, suffix 50, substring 20, with no minimum score and `exists: Boolean(bestMatch)`. Any substring hit bound a layer. With `locations` removed from the catalog per BDMS-978, the Locations layer would have silently rebound to `rock_sample_locations` (normalized, it ends with "locations"), rendering that collection twice on the map and displacing the real rock-sample entry on the collections page through its `seenCollectionIds` dedupe. Resolution is now exact against `id`/`collection_id`, then `name`/`title`, candidates tried in order. Separators and case are still normalized so one candidate covers both `water_elevation_contours` and its display title. A layer either names a published collection or does not exist. The regex token scorer that backstopped the water-elevation layers is gone too. Verified every registered layer against the deployed catalog: all 18 that survive the BDMS-977/978/979 trim resolve exactly, in both the map hook and the collections page. Removes the layers whose collections those tickets unpublish: ogc-locations locations BDMS-978 ogc-latest-depth-to-water latest_depth_to_water_wells BDMS-977 ogc-average-tds avg_tds_wells BDMS-977 ogc-other-thing-types other_things BDMS-979 All three bound by title, not id, which is why the shorter candidate lists still matched them today. Also removes ogc-water-elevation-contours: the catalog publishes no contour collection and never did, so the entry was dead before this change. Its derived counterpart, computed from water_elevation_wells via turf, was gated on `!waterElevationContours.exists` -- always true -- so that gate is now dropped and the derived layer is unconditional. Same runtime behavior. DEFAULT_VISIBLE_LAYERS pointed at ogc-latest-depth-to-water, so the map would have opened with nothing. It now opens on ogc-water-well-summary, the layer BDMS-977 names as making latest-depth-to-water redundant. DEPTH_LEGEND, latestDepthToWaterColorFromFeature, and averageTdsColorFromFeature had no remaining consumers and are removed. Merge order matters: this must not reach staging before the pygeoapi catalog change deploys, or four working layers disappear with nothing behind it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AGENTS.md already documents that feature, fix, chore, docs, and CI branches all base off staging, but there was no CLAUDE.md on any branch, so Claude Code never loaded it. Cutting a branch from the default branch, production, produces a PR into staging that drags every commit staging is missing. Imports AGENTS.md and restates the branching rule up front. Carries the model-attribution frontmatter the repo's documentation rule requires. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Sandbox nav group mixed a typography specimen with three WIP geothermal grids, and its visibility flag treated staging as a place to try things out. Previews are the sandbox now: a PR preview can run against an ephemeral API nobody else shares, while staging is a pre-production release branch. - Remove the Sandbox nav group and its SHOW_EXAMPLE_NAV flag. The pages it linked (/example/typography and the three geothermal grids) stay routable by URL; a geothermal nav group lands separately. - Remove the Sandbox access-control resource, the 'Sandbox' parent on ocotillo.hydrograph-correction, and the Sandbox label case in the Refine sider. - Document the staging-vs-preview rule in AGENTS.md and update the docs that described nav placement as Sandbox. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…x-concept chore(nav): drop the Sandbox concept
…p-colors feat(map): color map symbols with the viridis palette
…-catalog Conflicts came from the viridis colour work (#350) editing the very layers this branch removes, so every conflict was the same shape: this branch deletes a block, staging edits it. Resolved in favour of the deletions, which is the point of the branch — the collections behind them are not published: - ogcLayerUtils: DEPTH_LEGEND, averageTdsColorFromFeature, latestDepthToWaterColorFromFeature - useThingLayers: locationsLayer, latestDepthToWaterLayer, averageTdsLayer, waterElevationContoursLayer, waterElevationContoursLayerStyled, otherThingTypesLayer Staging's changes to the layers this branch keeps merged cleanly and are untouched. Nothing references the removed symbols afterwards; typecheck is clean and the unit suite passes (252 tests). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DEPTH_CLASS_COLORS only fed latestDepthToWaterColorFromFeature, which went with the unpublished layers. Staging added the constant alongside the viridis work, so the merge kept it with nothing left to use it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The catalog publishes geothermal_wells_bht, geothermal_wells_temperature_profile, bht_measurements, temp_depth_measurements, heat_flow, and dst, but no layer named any of them, so none reached the map or the collections page. The collections page even carried a Geothermal group described as reserved for these "when they are published" -- they are published. Registers all six in both registries, with the collection id as the first candidate and the display title as the fallback. Exact matching means the id is what binds; the titles carry em dashes, which normalize away, so either form resolves. The map's layer panel had no geothermal group -- getLayerGroupKey would have dropped all six into Reference. Adds the group between Climate and Geoscience, matched on 'geothermal', 'bht', 'temp-depth', 'heat-flow', and an exact 'ogc-dst'. The dst key is compared whole rather than by substring, since three letters collide too easily. The check runs first, before the groundwater branch, so a name like ogc-geothermal-wells-temperature-profile cannot be claimed by another group later. Popup labels are added for all six. No layer-specific popup rows yet -- the property shapes are unverified, so the generic renderer handles them until someone confirms what each collection carries. Layers are off by default, as every layer other than the summary is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The API now serves ready-made QGIS and ArcGIS Pro artifacts for our OGC API layers, so a user can open our data in a desktop GIS without configuring a connection by hand. Nothing in the UI surfaced them. Adds a panel above the dataset groups with the QGIS connections file, the QGIS import instructions, and a copyable service URL for ArcGIS Pro, which has no importable connection file. Per-layer .qlr / .lyrx downloads render on the collection row they belong to, matched on the catalogue's `collection` field against the collection id the page already resolves. Layer ids are API-side config and change, so nothing here hardcodes them, and hrefs and filenames are used verbatim from the catalogue: CORS on the API exposes no headers, so `Content-Disposition` is unreadable from JS. Anonymous artifacts are plain anchors; the authenticated internal-connections file is the one blob round-trip, gated on AMP viewer access. Types are hand-written zod for now. The committed openapi-auth.json snapshot has no /gis paths, and the only source for them is an unmerged API branch whose spec also carries unrelated unreleased endpoints and eight changed schemas, including WellResponse and ThingResponse. Refreshing the whole snapshot to reach /gis would regenerate all of that here. src/utils/gisArtifacts.ts is scoped to the GIS surface and marked for replacement once /gis is in the deployed spec. Contract and the follow-up notes: docs/gis-artifact-downloads-contract.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The card grid buries the catalogue: four group cards, each a column of paragraphs, so comparing datasets means scrolling past descriptions. A table puts all 24 on one screen — which is what this page is for — so it is now the default, with a Cards/Table toggle in the header to get the old layout back. Group membership is carried by row tint plus a labelled band per group rather than a repeated column, and the collection id is dropped from the dataset column: it is machine detail, and the title is what a reader scans by. The page also runs wider than the old `lg` container, which left the table cramped — full width up to a widescreen monitor, then ten of twelve columns so rows do not run the whole span of a very wide display. The "Admin View" overline is gone; the nav already gates who gets here. buildCollectionRows and the shared collection field resolution live in src/utils/collectionsView.ts so both views agree on what a dataset is called. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…uate-and-determine-public-pages docs(public-page): add public page content audit
…-view-632abf feat(map): BDMS-905. add place/address geocoder search to the map view
…t-downloads feat(collections): desktop GIS downloads and a table view for the datasets page
Bump the version to 1.2.0. staging has read 1.1.0 since July, because the 1.1.1 and 1.1.2 hotfix bumps went straight to production and were never merged back. Also restore editor access to Unassociated Assets. The v1.1.1 hotfix (cac1710) changed both the nav entry and the access-control policy, but staging carries only part of that: the policy here had reverted to adminRoles while production grants editorRoles. Merging staging into production would have kept production's nav line and taken staging's policy, leaving a nav link editors can see behind a permission check that turns them away. Git reports no conflict on that file, so nothing would have flagged it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Back-merge production so staging stops drifting from it. The 1.1.1 and 1.1.2 hotfixes shipped straight to production and were never merged back, which is why staging still read 1.1.0 and why the Unassociated Assets policy had reverted to admin-only here. Conflict resolutions: - package.json: keep 1.2.0. - .gitignore: keep staging's Claude Code entries; production has none. - authentik-provider: keep staging's buildAuthentikUrl helper. It strips trailing slashes from the base URL (src/config/auth.ts:7), so the v1.1.1 fix for a trailing slash in AUTHENTIK_URL survives. - accessControl.test: keep both sides. hydrograph-correction is new on staging, asset-unassociated comes from the v1.1.1 hotfix, and editors should have both. With this in place the staging into production merge is conflict-free. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chore(release): prepare v1.2.0
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.
Release v1.2.0. 180 commits, 36 merged PRs, 199 files changed.
Merging this deploys to production.
Release prep already done
PR #361 landed on staging first and handled three things:
1.2.0. staging had read1.1.0since July, because the1.1.1and1.1.2bumps went straight to production on hotfix branches and never came back.cac1710) changed both the nav entry and the access-control policy, but staging carried only part of it. Left alone, this merge would have shipped a nav link editors can see behind a permission check that turns them away, with git reporting no conflict on the file that mattered.What is in the release
Larger pieces:
Fixes: USGS Water Service API link (BDMS-1110), geothermal API URL, hydrograph corrector editor access, well show OSE POD and USGS cards.
Verification
b416371editorRoleson the unassociated-assets policy,editorAndAboveon the nav entry, and staging'sbuildAuthentikUrlhelper, which preserves the v1.1.1 trailing-slash fixtsccleanBefore merging, two open items
PR #346 (v1.1.2) is still open against production. Its payloads are already on staging, the PostHog disclosure in
src/components/Auth.tsxand BDMS-903 via PR #325, so this release supersedes it. It should be closed rather than merged.An earlier version of this description called
tmp/wellpy-samples/scratch data and suggested deleting it before release. That was wrong on both counts, and I am correcting it here so nobody acts on it.Those four files are test fixtures.
src/components/Hydrographs/hydrographCorrection.test.tsreads them from disk in seven places, anddocs/hydrograph-correction-gap-analysis.mddocuments what each one broke in the parser. Deleting them turns those tests red.They also never reach users. Vite copies
public/only, andvite.config.tssets nopublicDiroverride, so nothing undertmp/is bundled or served. The cost is about 198 KB of repo weight.The directory name is the problem, not the files. Moving them to
src/test/fixtures/wellpy/and updating the seven paths would stop the next reader drawing the same wrong conclusion I did. That is a separate cleanup, not a release blocker.🤖 Generated with Claude Code