Skip to content

Staging To Prod - #189

Merged
shweta2101 merged 152 commits into
productionfrom
staging
Nov 10, 2026
Merged

shweta2101 merged 152 commits into
productionfrom
staging

Conversation

@shweta2101

Copy link
Copy Markdown
Contributor

Summary

Promote the current staging release to production.

This release introduces comprehensive GTFS Pathways editing in Rapid 3, workspace merging with conflict resolution, expanded review workflows, improved workspace creation permissions, and imagery enhancements including Panoramax support. It also consolidates frontend API usage, updates core dependencies, and improves deployment caching, loading, and error reporting.

Changes

Features

Bug fixes

Maintenance

Deployment considerations

  • API compatibility: Production must expose the unified workspace API routes used by this release, including workspace creation checks, merge operations, workspace fixmes, and related lifecycle endpoints.
  • Configuration change: VITE_NEW_API_URL is no longer used. VITE_API_URL must point to the production API containing the consolidated endpoints.
  • Editor configuration: Verify the production VITE_RAPID3_URL and Pathways-related service URLs before deployment.
  • Sentry: The deployment workflow now sets ENV=production, which is used as the Sentry environment.
  • Caching: Hashed /_nuxt/* assets are cached for one year, while build manifests remain uncached. Confirm Azure Static Web Apps applies the updated routing configuration.
  • Dependency risk: MapLibre GL is upgraded across a major version; production map rendering should receive focused smoke testing.
  • Database migrations: None identified in this frontend repository.
  • Production has one unique previous release merge commit (Staging to Prod sync #156), but its tree matches the previous staging release; no separate production-only code changes were identified.

Testing

Tests were added or updated throughout this release, but no test commands were executed while preparing this PR summary.

  • Run npm run typecheck.
  • Run npm test.
  • Run the relevant Playwright end-to-end suite.
  • Verify GTFS Pathways import, editing, validation, and export to TDEI.
  • Verify workspace merge target selection, conflict resolution, and completion.
  • Test workspace creation as POC, data generator, administrator, and unauthorized users.
  • Verify review fixmes and TDEI feedback resolution.
  • Verify Rapid authentication recovery after an expired or rejected session.
  • Verify workspace map extent, custom imagery persistence, and Panoramax imagery.
  • Smoke-test dashboard project-group persistence and workspace metadata.
  • Verify production map rendering after the MapLibre GL upgrade.
  • Confirm SPA loading, static asset caching, and Sentry production reporting.

Rollback

Redeploy the previous production commit, 9612488, if frontend issues are found.

Because this release depends on consolidated backend endpoints and updated editor behavior, coordinate the rollback with any related backend or Rapid deployments if those services were changed for this release.

cyrossignol and others added 30 commits October 13, 2025 10:28
Bumps the npm_and_yarn group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.4.11` | `3.4.13` |
| [esbuild](https://github.com/evanw/esbuild) | `0.27.7` | `0.28.2` |
| [fast-uri](https://github.com/fastify/fast-uri) | `3.1.2` | `3.1.5` |
| [immutable](https://github.com/immutable-js/immutable-js) | `4.3.8` | `4.3.9` |
| [shell-quote](https://github.com/ljharb/shell-quote) | `1.8.4` | `1.10.0` |
| [svgo](https://github.com/svg/svgo) | `4.0.1` | `4.0.2` |
| [tar](https://github.com/isaacs/node-tar) | `7.5.16` | `7.5.22` |



Updates `dompurify` from 3.4.11 to 3.4.13
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](cure53/DOMPurify@3.4.11...3.4.13)

Updates `esbuild` from 0.27.7 to 0.28.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](evanw/esbuild@v0.27.7...v0.28.2)

Updates `fast-uri` from 3.1.2 to 3.1.5
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](fastify/fast-uri@v3.1.2...v3.1.5)

Updates `immutable` from 4.3.8 to 4.3.9
- [Release notes](https://github.com/immutable-js/immutable-js/releases)
- [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md)
- [Commits](immutable-js/immutable-js@v4.3.8...v4.3.9)

Updates `shell-quote` from 1.8.4 to 1.10.0
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](ljharb/shell-quote@v1.8.4...v1.10.0)

Updates `svgo` from 4.0.1 to 4.0.2
- [Release notes](https://github.com/svg/svgo/releases)
- [Commits](svg/svgo@v4.0.1...v4.0.2)

Updates `tar` from 7.5.16 to 7.5.22
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](isaacs/node-tar@v7.5.16...v7.5.22)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.12
  dependency-type: direct:production
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: indirect
- dependency-name: fast-uri
  dependency-version: 3.1.4
  dependency-type: indirect
- dependency-name: immutable
  dependency-version: 4.3.9
  dependency-type: indirect
- dependency-name: shell-quote
  dependency-version: 1.10.0
  dependency-type: indirect
- dependency-name: svgo
  dependency-version: 4.0.2
  dependency-type: indirect
- dependency-name: tar
  dependency-version: 7.5.21
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
re: https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/3618

This uses a few tricks to remove language that references OpenStreetMap:

- In `edit.vue` use CSS to hide blocks of text that are not needed
- In `rapid3.ts` override  the UiSuccess prototype so that it will
  avoid trying to fetch the OSM Community Index
- In `rapid3.ts`, add a 'localechange' listener on the LocalizationSystem
  so that after language strings are loaded, we swap in our replacement strings.
  This will work for any language, but the strings will appear in English.
  We use `prependListener` here so that our replacements can sneak in before
  other parts of the UI try redrawing and relocalizing.
new-api.workspaces-dev.sidewalks.washington.edu currently resolves to the
frontend host, which 404s every path and makes the dev proxy hang (e.g.
/new-api/v1/workspaces/{id}/bbox never returns). The main backend
(api.workspaces-dev → ws-backend-dev) serves the same /api/v1/* endpoints,
so route /new-api there. Revert once new-api DNS is fixed in the dev env.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce useUserCapabilities composable to fetch and evaluate user project-group roles and creation permissions. Integrate capability checks across the app (AppNavbar, DatasetPicker, dashboard, and all workspace create flows) to filter datasets, populate/select eligible project groups, disable create actions when unauthorized, and redirect users without creation rights. Add raw dataset payload to Tdei types and map it through TdeiClient, update fixtures, and add unit and e2e tests covering role-based creation scenarios.
A multi-changeset import leaves its later batches out of the first-changeset
common ancestor, so those elements read as "created" in both workspaces and
were kept twice (e.g. ~191 duplicated ways in a real merge). When the two
copies are content-identical — same geometry and tags, ignoring per-workspace
metadata — they are the same imported element: collapse to one instead of
remapping B's into a duplicate. Genuinely distinct same-id elements are still
remapped, and a divergent copy is kept (not silently merged away).

Adds elementContentEqual/tagsEqual to util/osm-merge, with regression tests.
Surfacing geometry/tag conflicts on such batched-import elements still needs
the full import in the ancestor; left as a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nction-ci

Resolves a conflict in services/workspaces.ts deleteWorkspace: develop's
PR #151 (report OSM cleanup failure) and this branch both reworked it.
Took develop's version (parallel delete via Promise.allSettled returning
DeleteWorkspaceResult), dropped the now-unused OsmApiClientError import,
and removed this branch's obsolete OSM-first deleteWorkspace tests from
test/unit/services/workspaces-lifecycle.test.ts (develop's workspaces.test.ts
already covers the new behavior).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ents

remapCollidingIds decided whether a created-on-both-sides element was a
duplicate by comparing raw content, including a way's or relation's raw
child IDs. When a child node was created on both sides but differs, it is
re-identified, yet a parent that referenced it by the same raw ID still
read as identical and was collapsed onto A's copy — dropping one side's
parent and orphaning the re-identified child.

Decide children before parents and compare a parent against its effective
(post-remap) references, so a parent whose child was re-identified reads as
distinct and is kept. Identical parents still collapse to one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#preInit now patches Rapid.UiSuccess.prototype and registers an l10n
localechange listener; the fake lacked both, so init() threw before
initAsync() painted the editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge resolution kept this branch's vitest import line, but
develop's deleteWorkspace tests use vi.fn(), failing typecheck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Use the real l10n key map_data.osmhidden (was map_data.layers.osmhidden).
- Apply the English string overrides only to English locales, writing to
  'en' and the active variant, so other languages keep their translations.
- Scope the OSM-hiding CSS to the Rapid 3 container so Rapid 2 and
  Pathways are unaffected.
- Also hide the note editor's view-on-OSM / report footer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This is a branch for getting Rapid v3.0.0 prerelease 3 available in
workspaces.
Includes the following changes/fixes:

- Override user interface elements and strings that refer to
OpenStreetMap:
   https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/3618
   
- Better selection of background imagery layer
   https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4007/
   
- Fix for switching workspaces after Rapid has loaded
   https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4417/

- Add Custom Dataset screen (bring your own data) now supports files.
This adds a file picker, drag-and-drop support, and various code related
to custom datasets.
In prerelease 2, we only supported loading Custom Datasets from a URL.
Data files are stored in browser's `IndexedDB` storage so that they can
be reloaded later.
   re: https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/3987/

- Rapid's edit history is now stored in `IndexedDB` too.
   - Before:
     - Single edit session stored in `LocalStorage`
- Mutex lock prevented multiple browser tabs from backing up the user's
edits
- Serialization of the user's editing session blocked main thread (long
edit sessions caused slowdowns)
- Size limitations of `LocalStorage` ("you have too many edits to back
up")
- On restore screen, a simple "We found unsaved changes - Keep/Discard"
with no detail about what the user did.
   - Now:
- Multiple edit sessions can be saved, even across different browser
tabs
     - `IndexedDB` is async - doesn't block main thread / no slowdowns
- `IndexedDB` size can store much more data than `LocalStorage` -
effectively no limit
- New Restore screen, we now show the user a list of their unsaved edits
from previous sessions,
details about when, where, what they did, and offer options to
restore/delete.
- If a legacy edit session is found in `LocalStorage`, it will be
upgraded to a new edit session in `IndexedDB`.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

- Hide Rapid editor messages and links that refer to OpenStreetMap.
- Remove Rapid’s `nsi` service and replace OpenStreetMap-related
localization strings with Workspace labels.
- Customize the post-save screen with an empty community-index response
and disable community-data rendering.
- Update the end-to-end Rapid stub to include `nsi`, `UiSuccess`, and
localization hooks.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The dashboard's Pathways Editor button now links to the edit page with
editor=rapid3 (falling back to the Pathways editor when Rapid 3 isn't
configured). The edit page now honors editor=rapid3 for any datatype
instead of only osw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the Pathways editor's preset data (workspaces-pathways-editor
data/pathways*.json @ 9a0534a) into a Rapid 3 schema asset, and merge it
in Rapid3Manager#preStart for pathways workspaces. The OSM presets and
categories are deleted first; OSM fields are kept for Rapid's own UI.
FontAwesome icons Rapid 3 doesn't bundle are swapped for bundled ones.

The edit page passes the datatype to Rapid3Manager.init and reloads
instead of reusing a Rapid 3 instance set up for another datatype.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Tag reversed_signposted_as (was reverse_signposted_as).
- is_bidirectional and wheelchair_boarding offer the GTFS enum codes
  (0/1, 0/1/2) with readable labels, which Rapid3Manager copies into
  Rapid's tagging strings for pathways workspaces.
- level_id, parent_station and zone_id are free-text IDs, not numbers
  or a paid/unpaid choice.
- Drop level_id from pathway presets; it's a stops.txt column only.
- Platform preset also matches vertices, so platforms that end a
  pathway are recognized.
- Unit tests check presets against the stops.txt/pathways.txt columns
  and enum values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Features drawn in the editor have no stop_id/pathway_id tag, so they
exported with empty IDs (and pathways ending at them with empty
from/to_stop_id). Keep the tagged ID when present, since other feed files
reference it, and otherwise use the OSM element ID with a ws-n/ws-w
prefix. Duplicated tags (e.g. from copy-paste) stay on the first feature;
later ones get generated IDs. Stops are now resolved before pathways, so
element order doesn't matter, and untagged elements no longer throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Write all stops.txt and pathways.txt columns from the GTFS reference,
  filled from same-named tags, instead of a fixed subset (length,
  stop_code, wheelchair_boarding, stop_access, etc. were dropped).
- Quote CSV fields containing commas, quotes or line breaks.
- A pathway's to_stop_id is its way's last node, not its second, and
  untagged bend nodes are no longer exported as stops.
- from/to_stop_id always come from the way's nodes, never stale tags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Levels are drawn as areas tagged with the levels.txt columns (a Level
preset; level_index marks the area as a level). A panel next to Rapid 3
on pathways workspaces:
- draws a new level outline (Rapid's area mode, pre-tagged as a level),
- lists levels by level_index and shows one level at a time, hiding
  stops, boxes and pathways on other levels while keeping connections
  to other levels (and their far ends) visible,
- flags stops with no level_id and level areas with no level_id, and
  assigns the shown level to stops,
- connects two stops picked from lists grouped by level (e.g. an
  elevator), as an undoable edit.

Level visibility wraps Rapid's FilterSystem isHidden/filterScene, since
a pathway's level comes from its end stops rather than its tags.

The export writes levels.txt from level areas, keeping the original
dataset's levels unless a drawn level replaces them, and no longer
treats level boxes as pathways or their corners as stops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Each level in the panel says whether it has an outline (or how many);
  levels only stops use get a button to draw one, keeping their level_id.
- New level outlines get a generated level_id (L<index>, suffixed if
  taken); level areas missing one get a "Fill level_id" button.
- Entrances are no longer flagged for a missing level: they're where
  riders enter from the street, and level_id is optional in GTFS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove the Connect stops section and the Rapid3LevelTools.connect it
used. "Draw level" becomes "Add level", since it creates a level (by
drawing its outline).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The level panel lists levels highest first. Levels with an outline can
be dragged (or moved with the arrow keys on their handle) to reorder
them, which renumbers level_index on their outlines as one undoable
edit: the ground level (0) keeps 0 and the others are numbered by their
distance above or below it. Levels only stops use stay at the bottom,
since level_index lives on the outline. The index is no longer shown
in each level's tile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each level row shows its outline status on a second line; levels
without one read "No outline, add one", where "add one" starts
drawing the outline. The status moved out of the level's select
button so the link isn't a button nested in a button; the whole row
now carries the selected highlight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GTFS levels don't belong to a station; they're linked through stops,
which carry both level_id and parent_station (a boarding area's via its
platform). With more than one station, the panel lists each station's
levels under its name, repeating shared levels (e.g. a street level),
with levels no station's stops use yet grouped last. Reordering stays
within a station. Levels without an outline now show a dimmed handle
whose tooltip says to add an outline first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The level panel's top entry is now "No station": it shows only
features not tied to a station, with the levels no station uses listed
under it, and is hidden when there's nothing outside a station. Each
station's group ends with "No level", showing that station's features
without a level_id (e.g. its street entrances), the station itself and
pathways touching them. Clicking the selected entry again shows
everything, which is also the initial state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For pathways workspaces, the raw tag editor shows a muted label next to
coded values (pathway_mode 5 -> Elevator, location_type, is_bidirectional,
wheelchair_boarding, stop_access), by wrapping the editor's render; the
input keeps the raw code. The labels live in one module, which the
schema's field option labels are checked against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For coded keys (pathway_mode, location_type, is_bidirectional,
wheelchair_boarding, stop_access) the raw tag editor's value dropdown now
lists every code with its label ("5 Elevator"), instead of taginfo's
bare values; picking one enters the code. Done by wrapping the taginfo
service's values lookup for those keys only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For pathways workspaces, a "GTFS references" validator warns when:
- parent_station doesn't match a stop_id, or names the wrong kind of
  stop (a station; a platform for boarding areas), is set on a station,
  or is missing where GTFS requires it (entrances, generic nodes,
  boarding areas);
- level_id doesn't match a drawn level outline;
- a stop_id is shared by several stops.
Issues show in Rapid's Issues panel and on the selected feature. The
checks are plain functions over an index of stops and levels, built once
per graph.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…first

Clicking a station's name in the level panel selects the station in
Rapid (opening it in the sidebar). Each station's "No level" entry is
now "Street / No level" and comes first, above the station's levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jeffmaki and others added 26 commits October 30, 2026 11:41
With no map location in the URL, the editors opened at zoom 2 over 0,0.
Fetch the workspace's bbox first and put a location that fits it in the
URL for the editor to start from. A location already in the URL is kept,
and an empty workspace or a failed lookup keeps the editor's default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The background was chosen by what covers the middle of the view, so local
imagery covering only part of a workspace could win and leave the rest of
it black. Check imagery against points across the workspace's extent, and
prefer what covers all of them, falling back to the old choice when
nothing does or the extent can't be fetched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The create pages said only OpenSidewalks files could be imported, but
  GTFS Pathways files can be too.
- Test that level_index values like 0.0 and 1.0 read as 0 and 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rapid's close_nodes check only tells points apart by OSM's level and
layer tags, so on pathways workspaces it warned about stops stacked on
different floors, and about a station beside a generic node. Allow points
with different level_ids or location_types to share a spot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A level with no outline or no level_index now shows an error icon on
  its row, which stays below the numbered levels.
- A pathway with no stops at its ends was listed nowhere; it's on no
  level, so it now appears under "No level".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Levels with stops but no outline were only outlined on first opening the
editor, and not at all if Rapid offered to restore unsaved edits. Outline
them after every whole-workspace load, and after the restore prompt is
dismissed or a restore finishes, never during one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moving any level also numbered every level with a shape but no
level_index, which cleared their errors without anyone choosing a number.
Now only the moved level takes a number from its position.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Background imagery now comes from, in order: the URL's background=, the
imagery the user last picked for this workspace in this browser, then our
pick from imagery covering the whole workspace. A URL or saved choice only
has to cover the center of the view, so a deliberate partial-coverage
choice like county imagery stays. Adding a start location to the URL also
keeps the rest of its hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
### Changes Implemented
* **Permission Validation on Create:** Clicking "Create Workspace" now
calls the permission check API first, blocking creation and displaying
the server reason if permission is denied.
* **In-Flight Visual Feedback:** The button displays a spinner with
"Checking permissions..." and an informational banner while the check
API runs.
* **Dataset Clear Button:** Added a one-click clear (`✕`) button inside
the dataset picker to easily reset selected or typed text.
* **Dataset Picker Reset on Focus:** Re-focusing the dataset input after
an unmatched search automatically restores the full list of datasets
instead of staying stuck on "No datasets found".

### Test Steps

1. **Test Dataset Clear & Recovery:**
   * Go to `/workspace/create/tdei` and select a Project Group.
* Type non-existent text in the Dataset field to trigger "No datasets
found", click outside the field, then click back into it — verify all
datasets reappear.
* Select a dataset and click the `✕` button — verify the field clears
and the dropdown resets.

2. **Test Denied Creation (Restricted User / Dataset):**
   * Select a dataset where you do not have creation access.
* Click **Create Workspace** — verify the button briefly says "Checking
permissions...", creation stops, and a red error banner shows the denial
reason.

3. **Test Successful Creation (Authorized User):**
   * Select a valid dataset where you have creation access.
* Click **Create Workspace** — verify "Checking permissions..." briefly
appears, the check succeeds, and the workspace is created successfully.
   
<img width="1470" height="956" alt="Screenshot 2026-10-29 at 6 06 39 PM"
src="https://github.com/user-attachments/assets/ac027076-26e0-4f9f-9c2a-95d39e751397"
/>
<img width="1470" height="956" alt="Screenshot 2026-10-29 at 6 06 41 PM"
src="https://github.com/user-attachments/assets/fed881dd-92c7-469a-9189-63b6bcca2914"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary

- Added a clear button and focus/blur handling to `DatasetPicker`. The
picker reloads the full dataset list when needed, including after an
unmatched search.
- Updated `useWorkspaceCreateCheck` to expose `checking` and `check`.
The check returns the backend result when available and ignores stale
results.
- Updated the TDEI workspace creation flow to check permissions before
starting creation. The page displays a checking status, disables the
create button during the check, and displays the denial reason when
creation is not allowed.
- Added unit tests for dataset picker behavior and permission-check
state and invocation.

Test execution results were not provided.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The export kept the first outline's tags, losing whatever the others said,
and an outline with no level_index wiped the original's. Outlines now fill
in each other's blanks and the original's, and ones that disagree stop the
export with a message naming them. The editor flags any level with more
than one outline, since it's one row in levels.txt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Choosing a background now fetches the workspace's extent, so these tests
loaded services/index, whose caches open IndexedDB. The rejections failed
the run even though the tests passed. Stub the client they need.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A pathway with no stops at its ends has no station, and the No station
  group had no contents list, so these were still shown nowhere. It now
  expands like No level, and shows when only such a pathway is there.
- A row no longer repeats a name equal to the level_index.
- Test the close-points filter with points differing only by level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… it (#182)

Makes the editors open somewhere useful, and stops black tiles when
imagery covers only part of a workspace.

**Start at the data's extent**
- With no `#map=` in the URL, the editors opened at zoom 2 over 0,0. The
page now fetches the workspace's bbox and puts a location that fits it
in the URL before the editor loads, so it works for Rapid 2, Rapid 3 and
the Pathways editor.
- A location in the URL is kept. An empty workspace or a failed lookup
keeps the editor's default. The rest of the hash, such as `background=`,
is kept.

**Background imagery order**
The background was chosen by what covered the middle of the view, so
local imagery covering part of a workspace could win and leave the rest
black (King County ortho on Sound Transit: tiles outside the county
return HTTP 400 or solid black). It now comes from, in order:
1. `background=` in the URL, if it covers the center of the view.
2. The user's saved pick for this workspace, if it covers the center of
the view. Partial coverage is fine because they chose it.
3. Our pick, only from sources covering the whole extent (nine points
across the bbox): the current imagery, then the area's best, then the
first. Worldwide sources count as full coverage.
4. If nothing covers the whole extent, what covers the center of the
view.

Picks are saved per workspace in the browser's local storage, from the
Background pane only, so they don't follow a user to another computer.

**Known limitation:** Rapid doesn't draw workspace data below zoom 12,
so a workspace that only fits at a lower zoom opens showing the area but
none of its data until you zoom in.

**Testing**
- Tried on Stage in real Rapid 3: Sound Transit now chooses Bing Aerial
instead of King County ortho.
- Not yet tried in real Rapid: picks sticking across loads, start at
extent, and Rapid 2. These have unit and e2e tests against fake editors.
- Lint and all unit tests pass. In e2e, everything passed except the two
sign-in tests that hard-code `localhost:3000`, because the suite ran on
port 3100 beside a dev server; CI's run on 3000 is the full check.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Custom imagery declares no coverage, so it counted as worldwide and followed
the user into workspaces it doesn't cover. Only a choice can show it now.
Rapid has one custom slot, so a saved custom pick keeps its own template.
Document the imagery order in CLAUDE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Makes the Stations panel read like a building, and flags levels that
GTFS can't export properly.

**Level rows**
- Rows run from the highest `level_index` down through 0 to the negative
ones. Dragging and the arrow keys still renumber correctly: higher in
the list means a higher floor. The services keep their lowest-first
order, so the validators and level dropdowns are unchanged.
- Each row shows its `level_index`, or `–` if it has none. A row doesn't
repeat a `level_id` that equals the index, or a name that equals the ID.
- A level with no outline or no `level_index` has a red error icon
explaining why, and sits below the numbered levels. It gets a number
when that level itself is moved; moving another level leaves it blank,
so the error stays until someone chooses.
- "Street / No level" is now "No level". It lists nodes with no
`level_id` and the edges touching them.
- "No station" expands the same way. A pathway with no stops at either
end used to appear nowhere and is now listed there.

**Automatic outlines**
- Levels with stops but no outline were only outlined on first opening
the editor, and not at all if Rapid offered to restore edits. They're
now outlined after every whole-workspace load, including workspace
switches, and after the restore prompt is dismissed or a restore
finishes. It never runs during a restore.
- A level still gets one outline, however many stations use it, so the
POSM hierarchy matches `levels.txt`. A shared level across distant
stations keeps its large box and its "N km across" warning, which can be
ignored.

**Exporting levels without losing data**
- `levels.txt` kept the first outline's tags when a `level_id` had
several, dropping whatever the others said, and a blank `level_index`
wiped the original dataset's value. Outlines now fill in each other's
blanks and the original's, and `1` and `1.0` count as the same index.
- **Behavior change:** outlines that really disagree on `level_index` or
`level_name` now stop the export (Download and TDEI), with an error
naming the level and values. A workspace with such outlines today won't
export until they're fixed.

**Validation**
- A level with more than one outline gets a warning, which says when the
conflict will block export.
- Pathways workspaces no longer get Rapid's "Point is too close to
Station" warning for points with different `level_id`s or
`location_type`s, since Rapid only compares OSM's level and layer tags.
TDEI's `tdei_stops_share_position` is unchanged: it allows different
levels but not different types on one level, and it's INFO only.

**Create pages**
- The file import text now says "OpenSidewalks or GTFS Pathways".

**Testing**
- Tried on Stage data in real Rapid 3: level order, dragging and "No
level".
- Not tried in real Rapid: the outlining after restores and workspace
switches, numbering a moved blank level, the "No station" contents, and
the repeated-outline warning. These have unit tests against a fake
editor.
- The export changes have unit tests only.
- Lint and all unit tests pass. In e2e, everything passed except the two
sign-in tests that hard-code `localhost:3000`, because the suite ran on
port 3100 beside a dev server; CI's run on 3000 is the full check.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Follow-up to #182, whose branch got this commit just after it merged.

Custom imagery declares no coverage, so #182's check treated it as
worldwide. A custom layer chosen on one workspace then followed the user
into workspaces it doesn't cover, and left them black.

- **We never pick custom imagery.** Only someone choosing it for that
workspace, or a `background=` in the URL, can show it. A new workspace
resets to imagery covering its whole extent.
- **A custom pick keeps its own template.** Rapid has one custom slot,
so setting a different custom URL on another workspace would otherwise
change this one.
- **`CLAUDE.md`** gets a "Background imagery" section with the order for
choosing imagery.

A custom layer chosen for a workspace always sticks there, since we
can't tell whether it covers the area.

**Testing:** only against fake imagery, not real Rapid. Lint, the type
check and unit tests pass with CI's commands. In e2e, everything passed
except the two sign-in tests that hard-code `localhost:3000`, because
the suite ran on port 3100 beside a dev server; CI's run on 3000 is the
full check.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Replace the old kartaview util with a new util/street-images.ts that extracts ext:street_images or ext:kartaview_url tags and resolves legacy KartaView/OpenStreetCam storage URLs into the CDN-proxied form. Update components and pages: rename UI label from "KartaView" to "Street Image" and switch imports/logic in review.vue to use streetImageUrl and resolveStreetImageUrl. Remove util/kartaview.ts and add unit tests (test/unit/util/street-images.test.ts) covering extraction and URL resolution behavior.
Trim trailing blank lines at end of test/unit/util/street-images.test.ts and util/street-images.ts for code cleanliness. No functional changes are made.
## DevBoard Task
https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4639

### Changes Implemented
- Added support for reading street-level image URLs from both
`ext:street_images` and `ext:kartaview_url` tags on reviewed map
elements.
- Updated image URL resolution so self-hosted Panoramax URLs (and other
standard image URLs) are loaded directly as-is, while retaining CDN
proxy conversion only for legacy KartaView/OpenStreetCam storage URLs.
- Replaced KartaView-specific naming and UI caption text (`KartaView` →
`Street Image`) with generic street-image terminology.
- Added unit tests covering `ext:street_images`, `ext:kartaview_url`,
Panoramax URLs, KartaView CDN URLs, and legacy KartaView/OpenStreetCam
URLs.

### Impacted Areas for Testing
- **Review Queue - Panoramax Image Display:** Select a changeset in the
workspace review page and click a map feature containing an
`ext:street_images` tag (e.g., a Panoramax `/api/pictures/.../hd.jpg`
URL). Verify the image thumbnail loads in the bottom-left preview panel
on desktop and via the **"View photo submission"** button on smaller
screens.
- **Review Queue - KartaView Backward Compatibility:** Click a map
feature containing an `ext:kartaview_url` tag (both `cdn.kartaview.org`
links and legacy `kartaview.org` / `openstreetcam.org` storage links)
and verify the image still displays as expected.
- **Image Viewer & External Link:** Click the thumbnail (or **"View
photo submission"** button) to verify the full-screen photo viewer modal
opens, and click the **Street Image** link in the thumbnail caption to
confirm it opens the image URL in a new browser tab.
- **Elements Without Photos:** Click a map feature that has neither
`ext:street_images` nor `ext:kartaview_url` and confirm no image panel
or photo button is shown.

### Screenshots
<img width="1469" height="877" alt="Screenshot 2026-11-04 at 5 59 15 PM"
src="https://github.com/user-attachments/assets/b0a4d6f4-86f5-42f1-800e-162ed3206703"
/>

<img width="1470" height="956" alt="Screenshot 2026-11-05 at 11 52
43 AM"
src="https://github.com/user-attachments/assets/84991967-c333-4dd9-8ec1-6cc72d58eb71"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary

- Added `streetImageUrl` to read trimmed `ext:street_images` values,
with `ext:kartaview_url` as a fallback.
- Added `resolveStreetImageUrl` to preserve standard image URLs and
existing KartaView CDN URLs. It converts URLs on legacy KartaView and
OpenStreetCam hosts to CDN-proxied URLs.
- Updated the review page to use the new helpers and changed the image
link label to “Street Image.”
- Added unit tests for tag extraction and URL resolution.

## Testing

Test execution results are not available.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Add a user-scoped dashboard selection utility and integrate it into the dashboard UI. Introduce util/dashboard-selection.ts (read/write/get key/resolve) to persist the selected project group and a per-project-group remembered workspace in localStorage, with safe parsing and legacy sessionStorage handling. Update pages/dashboard.vue to load/save the selection, resolve project group IDs, remember per-group workspaces, clear stale ?workspace= query params when switching to an empty project group, and remove the old sessionStorage helper functions. Adjust workspace members messaging in pages/workspace/[id]/settings/members.vue to show an access-denied message when appropriate and update the E2E test to expect the new text. Add unit tests for dashboard selection persistence (test/unit/util/dashboard-selection.test.ts) and update the e2e spec (test/e2e/settings-members.spec.ts).
Make dashboard selection read/write resilient to storage failures: add try/catch in loadDashboardSelection and saveDashboardSelection, merge latest stored state before writing, and update save API to accept the updated project group/workspace. Add helper utilities and small JSDoc comments in util/dashboard-selection.ts, and guard against invalid workspace IDs. Update workspace members UI to show a conditional message when access is denied and adjust E2E tests to assert the new structure. Add unit tests to cover storage get/set failures and a small doc update in street-images.ts.
…d fix non-POC members tab message (#186)

## DevBoard Task
https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4601

**Changes Implemented**
- Persist the selected project group and last selected workspace per
group in `localStorage` (per user).
- Stop previous workspace selections from overriding the active project
group on return to the dashboard.
- Clear stale `?workspace=` URL query parameters when switching to an
empty project group.
- Fall back to the first available project group if access to the saved
group is removed.

**Impacted Areas for Testing**
- Select an empty project group, navigate away, and return to the
dashboard to confirm the empty project group stays selected.
- Select a non-default workspace in one project group, switch to another
group and back, and confirm that workspace stays selected.
- Open the dashboard in a new tab or reload to confirm the last selected
project group persists.
- Open a direct `/dashboard?workspace=<id>` link to confirm it still
opens that workspace and its project group.

<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 17 PM"
src="https://github.com/user-attachments/assets/02fa466d-3b24-4060-b9eb-dedceabeb94c"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 26 PM"
src="https://github.com/user-attachments/assets/d34533fd-c891-4ed8-85da-b03f6732ea14"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 34 PM"
src="https://github.com/user-attachments/assets/7cb4de93-29c9-4d37-95f9-f1612dd758ce"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 45 PM"
src="https://github.com/user-attachments/assets/96caaabd-4c21-4435-8c95-ace0b7a6ed5a"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 59 PM"
src="https://github.com/user-attachments/assets/d5a7aa65-d85f-4c52-a8bd-714ee15dcbfa"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 53 46 PM"
src="https://github.com/user-attachments/assets/01d4d8fb-c3c7-4a42-94ee-0caf2af66db5"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 53 57 PM"
src="https://github.com/user-attachments/assets/6bb22db1-4135-4e62-bbcd-4142e065d399"
/>


---

## DevBoard Task

https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4400

**Changes Implemented**
- Replace `"No one has a TDEI POC role in this project group."` with
`"You do not have permission to view the member list."` on the Members
tab.
- Show `"You do not have permission to view the member list."` in the
Data Generators and Workspace Members sections when a non-POC user is
denied access (403).

**Impacted Areas for Testing**
- View Workspace Settings -> Members as a non-POC user and confirm all
three sections show `"You do not have permission to view the member
list."`
- View Workspace Settings -> Members as a POC user and confirm member
lists and empty role messages still display normally.

<img width="1470" height="880" alt="Screenshot 2026-11-05 at 6 11 46 PM"
src="https://github.com/user-attachments/assets/4738ae9d-473d-4d8c-8f94-7cab244a6cb2"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->

## Summary

- Persist the selected project group and each group’s last selected
workspace in user-scoped `localStorage`. Restore saved selections when
available, fall back to the first available group or workspace, and
clear stale `?workspace=` parameters for empty groups. Use the legacy
session-stored project group as a fallback.
- Show “You do not have permission to view the member list.” in the
Project Group Admins, Data Generators, and Workspace Members sections
when access is denied. Preserve the existing empty-list messages when
access is allowed.
- Add dashboard-selection unit tests and update the unauthorized-user
E2E expectations.

## Checks

- CI, build, and test results: not available.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## DevBoard Task
https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4639

### Changes Implemented
- Added support for reading street-level image URLs from both
`ext:street_images` and `ext:kartaview_url` tags on reviewed map
elements.
- Updated image URL resolution so self-hosted Panoramax URLs (and other
standard image URLs) are loaded directly as-is, while retaining CDN
proxy conversion only for legacy KartaView/OpenStreetCam storage URLs.
- Replaced KartaView-specific naming and UI caption text (`KartaView` →
`Street Image`) with generic street-image terminology.
- Added unit tests covering `ext:street_images`, `ext:kartaview_url`,
Panoramax URLs, KartaView CDN URLs, and legacy KartaView/OpenStreetCam
URLs.

### Impacted Areas for Testing
- **Review Queue - Panoramax Image Display:** Select a changeset in the
workspace review page and click a map feature containing an
`ext:street_images` tag (e.g., a Panoramax `/api/pictures/.../hd.jpg`
URL). Verify the image thumbnail loads in the bottom-left preview panel
on desktop and via the **"View photo submission"** button on smaller
screens.
- **Review Queue - KartaView Backward Compatibility:** Click a map
feature containing an `ext:kartaview_url` tag (both `cdn.kartaview.org`
links and legacy `kartaview.org` / `openstreetcam.org` storage links)
and verify the image still displays as expected.
- **Image Viewer & External Link:** Click the thumbnail (or **"View
photo submission"** button) to verify the full-screen photo viewer modal
opens, and click the **Street Image** link in the thumbnail caption to
confirm it opens the image URL in a new browser tab.
- **Elements Without Photos:** Click a map feature that has neither
`ext:street_images` nor `ext:kartaview_url` and confirm no image panel
or photo button is shown.

### Screenshots
<img width="1469" height="877" alt="Screenshot 2026-11-04 at 5 59 15 PM"
src="https://github.com/user-attachments/assets/b0a4d6f4-86f5-42f1-800e-162ed3206703"
/>

<img width="1470" height="956" alt="Screenshot 2026-11-05 at 11 52
43 AM"
src="https://github.com/user-attachments/assets/84991967-c333-4dd9-8ec1-6cc72d58eb71"
/>


## DevBoard Task
https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4601

**Changes Implemented**
- Persist the selected project group and last selected workspace per
group in `localStorage` (per user).
- Stop previous workspace selections from overriding the active project
group on return to the dashboard.
- Clear stale `?workspace=` URL query parameters when switching to an
empty project group.
- Fall back to the first available project group if access to the saved
group is removed.

**Impacted Areas for Testing**
- Select an empty project group, navigate away, and return to the
dashboard to confirm the empty project group stays selected.
- Select a non-default workspace in one project group, switch to another
group and back, and confirm that workspace stays selected.
- Open the dashboard in a new tab or reload to confirm the last selected
project group persists.
- Open a direct `/dashboard?workspace=<id>` link to confirm it still
opens that workspace and its project group.

<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 17 PM"
src="https://github.com/user-attachments/assets/02fa466d-3b24-4060-b9eb-dedceabeb94c"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 26 PM"
src="https://github.com/user-attachments/assets/d34533fd-c891-4ed8-85da-b03f6732ea14"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 34 PM"
src="https://github.com/user-attachments/assets/7cb4de93-29c9-4d37-95f9-f1612dd758ce"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 45 PM"
src="https://github.com/user-attachments/assets/96caaabd-4c21-4435-8c95-ace0b7a6ed5a"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 52 59 PM"
src="https://github.com/user-attachments/assets/d5a7aa65-d85f-4c52-a8bd-714ee15dcbfa"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 53 46 PM"
src="https://github.com/user-attachments/assets/01d4d8fb-c3c7-4a42-94ee-0caf2af66db5"
/>
<img width="1470" height="956" alt="Screenshot 2026-11-05 at 5 53 57 PM"
src="https://github.com/user-attachments/assets/6bb22db1-4135-4e62-bbcd-4142e065d399"
/>


---

## DevBoard Task

https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/4400

**Changes Implemented**
- Replace `"No one has a TDEI POC role in this project group."` with
`"You do not have permission to view the member list."` on the Members
tab.
- Show `"You do not have permission to view the member list."` in the
Data Generators and Workspace Members sections when a non-POC user is
denied access (403).

**Impacted Areas for Testing**
- View Workspace Settings -> Members as a non-POC user and confirm all
three sections show `"You do not have permission to view the member
list."`
- View Workspace Settings -> Members as a POC user and confirm member
lists and empty role messages still display normally.

<img width="1470" height="880" alt="Screenshot 2026-11-05 at 6 11 46 PM"
src="https://github.com/user-attachments/assets/4738ae9d-473d-4d8c-8f94-7cab244a6cb2"
/>
@coderabbitai

coderabbitai Bot commented Nov 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 86569570-3dfe-4443-893f-89563a248e69

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@shweta2101
shweta2101 merged commit 6f3429f into production Nov 10, 2026
3 checks passed

This branch was successfully deployed

1 active deployment
stage — e3149909 Deployed Nov 9, 2026 by github-actions[bot] via build-and-deploy #113
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.

6 participants