Skip to content

Conversation

@joshlarson
Copy link
Contributor

@joshlarson joshlarson commented Aug 4, 2025

Screen.Recording.2025-08-04.at.1.47.05.PM.mov

No ticket, but this is pretty cool, isn't it?

Ticket: Live-update system status on subway alerts page

@joshlarson joshlarson changed the title Jdl/feat/live update subway status on alerts page feat: Live-update subway status on subway alerts page Aug 4, 2025
@joshlarson joshlarson added the dev-green Deploy to dev-green label Aug 4, 2025
@github-actions github-actions bot removed the dev-green Deploy to dev-green label Aug 5, 2025
@joshlarson joshlarson force-pushed the jdl/feat/live-update-subway-status-on-alerts-page branch from 10fdb7e to bc76c81 Compare August 6, 2025 13:39
@joshlarson joshlarson force-pushed the jdl/feat/live-update-subway-status-on-alerts-page branch from bc76c81 to 4735a54 Compare August 29, 2025 19:34
@joshlarson joshlarson marked this pull request as ready for review September 5, 2025 17:54
@joshlarson joshlarson requested a review from a team as a code owner September 5, 2025 17:54
@joshlarson joshlarson force-pushed the jdl/feat/live-update-subway-status-on-alerts-page branch from 54926dc to 74e3119 Compare September 5, 2025 20:26
Copy link
Collaborator

@thecristen thecristen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really cool!!!


@impl true
def init(_opts) do
Phoenix.PubSub.subscribe(Dotcom.PubSub, "alerts")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think you can use DotcomWeb.Endpoint.subscribe("alerts") here, as I believe the endpoint has all these (subscribe/broadcast/etc) functions available

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh - I didn't think it would work, but it seems to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh actually the thing that worked was using DotcomWeb.Endpoint for the subway_status_updated message. This suggestion isn't working for the reason that I thought: DotcomWeb.Endpoint is started after SubwayCache, which is causing SubwayCache to fail to start.

** (Mix) Could not start application dotcom: Dotcom.Application.start(:normal, []) returned an error: shutdown: failed to start child: Dotcom.SystemStatus.SubwayCache
    ** (EXIT) an exception was raised:
        ** (ArgumentError) errors were found at the given arguments:

  * 1st argument: the table identifier does not refer to an existing ETS table

            (stdlib 7.0.2) :ets.lookup(DotcomWeb.Endpoint, :pubsub_server)
            (dotcom 0.0.1) deps/phoenix/lib/phoenix/endpoint.ex:541: DotcomWeb.Endpoint.config/2
            (dotcom 0.0.1) lib/dotcom_web/endpoint.ex:4: DotcomWeb.Endpoint.pubsub_server!/0
            (dotcom 0.0.1) lib/dotcom_web/endpoint.ex:4: DotcomWeb.Endpoint.subscribe/2
            (dotcom 0.0.1) lib/dotcom/system_status/subway_cache.ex:36: Dotcom.SystemStatus.SubwayCache.init/1
            (stdlib 7.0.2) gen_server.erl:2276: :gen_server.init_it/2
            (stdlib 7.0.2) gen_server.erl:2236: :gen_server.init_it/6
            (stdlib 7.0.2) proc_lib.erl:333: :proc_lib.init_p_do_apply/3

But.... if I swap those in application.ex so that SubwayCache is started after DotcomWeb.Endpoint, then it does seem to work.

I thought it was considered virtuous to start the Endpoint last - am I doing evil by swapping them around?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah you're right, well I don't think it's evil per se, but we may as well stick with the convention of having Endpoint last

] ++
if Application.get_env(:dotcom, :env) != :test do
[
{Dotcom.SystemStatus.SubwayCache, []}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Why not start this in test too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because having it running causes some funky errors, which I think are related to the lack of existence of Dotcom.Utils.DateTime.Mock.

If I adopt this suggestion, and also have this run in test, then I get this error 👇, because init tries to call a function that calls now/0.

** (Mix) Could not start application dotcom: Dotcom.Application.start(:normal, []) returned an error: shutdown: failed to start child: Dotcom.SystemStatus.SubwayCache
    ** (EXIT) an exception was raised:
        ** (Mox.UnexpectedCallError) no expectation defined for Dotcom.Utils.DateTime.Mock.now/0 in process #PID<0.538.0> with args []
            (mox 1.2.0) lib/mox.ex:903: Mox.__dispatch__/4
            (dotcom 0.0.1) lib/dotcom/system_status.ex:22: Dotcom.SystemStatus.subway_status/0
            (dotcom 0.0.1) lib/dotcom/system_status/subway_cache.ex:31: Dotcom.SystemStatus.SubwayCache.init/1
            (stdlib 7.0.2) gen_server.erl:2276: :gen_server.init_it/2
            (stdlib 7.0.2) gen_server.erl:2236: :gen_server.init_it/6
            (stdlib 7.0.2) proc_lib.erl:333: :proc_lib.init_p_do_apply/3

If I leave init as-is, then the alerts repo tests start failing for the same underlying reason, but in a different way:

11:16:32.216 [error] GenServer Dotcom.SystemStatus.SubwayCache terminating
** (Mox.UnexpectedCallError) no expectation defined for Dotcom.Utils.DateTime.Mock.now/0 in process #PID<0.525.0> with args []
    (mox 1.2.0) lib/mox.ex:903: Mox.__dispatch__/4
    (dotcom 0.0.1) lib/dotcom/system_status.ex:22: Dotcom.SystemStatus.subway_status/0
    (dotcom 0.0.1) lib/dotcom/system_status/subway_cache.ex:42: Dotcom.SystemStatus.SubwayCache.handle_info/2
    (stdlib 7.0.2) gen_server.erl:2434: :gen_server.try_handle_info/3
    (stdlib 7.0.2) gen_server.erl:2420: :gen_server.handle_msg/3
    (stdlib 7.0.2) proc_lib.erl:333: :proc_lib.init_p_do_apply/3
Last message: :alerts_updated

☝️ happens the first time the alerts cache store tries to publish an :alerts_updated message. That doesn't cause any tests to fail the first time, but then the second, third, etc times :alerts_updated messages are published, we get errors like 👇

     ** (exit) exited in: GenServer.call(Alerts.Cache.Store, {:update, [%Alerts.Alert{id: "orange_alert", active_period
: [], banner: "", cause: "", created_at: nil, description: "", effect: :unknown, header: "", image: nil, image_alternat
ive_text: nil, informed_entity: %Alerts.InformedEntitySet{activities: MapSet.new([]), direction_id: MapSet.new([nil]),
entities: [%Alerts.InformedEntity{activities: MapSet.new([]), direction_id: nil, facility: nil, route: "Orange", route_
type: nil, stop: nil, trip: nil}], facility: MapSet.new([nil]), route: MapSet.new(["Orange"]), route_type: MapSet.new([
nil]), stop: MapSet.new([nil]), trip: MapSet.new([nil])}, lifecycle: :unknown, priority: :low, severity: 5, updated_at:
 ~U[2025-09-15 14:21:30.221355Z], url: ""}, %Alerts.Alert{id: "red_alert", active_period: [], banner: "", cause: "", cr
eated_at: nil, description: "", effect: :unknown, header: "", image: nil, image_alternative_text: nil, informed_entity:
 %Alerts.InformedEntitySet{activities: MapSet.new([]), direction_id: MapSet.new([nil]), entities: [%Alerts.InformedEnti
ty{activities: MapSet.new([]), direction_id: nil, facility: nil, route: "Red", route_type: nil, stop: nil, trip: nil}],
 facility: MapSet.new([nil]), route: MapSet.new(["Red"]), route_type: MapSet.new([nil]), stop: MapSet.new([nil]), trip:
 MapSet.new([nil])}, lifecycle: :unknown, priority: :low, severity: 5, updated_at: ~U[2025-09-15 14:21:30.221355Z], url
: ""}, %Alerts.Alert{id: "blue_alert", active_period: [], banner: "", cause: "", created_at: nil, description: "", effe
ct: :unknown, header: "", image: nil, image_alternative_text: nil, informed_entity: %Alerts.InformedEntitySet{activitie
s: MapSet.new([]), direction_id: MapSet.new([nil]), entities: [%Alerts.InformedEntity{activities: MapSet.new([]), direc
tion_id: nil, facility: nil, route: "Blue", route_type: nil, stop: nil, trip: nil}], facility: MapSet.new([nil]), route
: MapSet.new(["Blue"]), route_type: MapSet.new([nil]), stop: MapSet.new([nil]), trip: MapSet.new([nil])}, lifecycle: :u
nknown, priority: :low, severity: 5, updated_at: ~U[2025-09-15 14:21:30.221355Z], url: ""}], nil}, 5000)
         ** (EXIT) no process: the process is not alive or there's no process currently associated with the given name,
 possibly because its application isn't started
     code: Store.update([orange_alert, red_alert, blue_alert], nil)
     stacktrace:
       (elixir 1.18.4) lib/gen_server.ex:1121: GenServer.call/3
       (dotcom 0.0.1) lib/alerts/cache/store.ex:34: Alerts.Cache.Store.update/2
       test/alerts/repo_test.exs:53: (test)

I'm sure there's a more elegant way to do this, but blocking the genserver in test did seem to work! 😅

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I think we typically just add this to the test module to mock that:

setup _ do
  stub_with(Dotcom.Utils.DateTime.Mock, Dotcom.Utils.DateTime)

  :ok
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That stub would somehow need to get initialized before the application starts in the test. I haven't figured out yet how to do that!

I tried adding stub_with(Dotcom.Utils.DateTime.Mock, Dotcom.Utils.DateTime) to the individual *_case.ex setup blocks, but those get executed after init is called, which means that I can't get around

** (Mix) Could not start application dotcom: Dotcom.Application.start(:normal, []) returned an error: shutdown: failed to start child: Dotcom.SystemStatus.SubwayCache
    ** (EXIT) an exception was raised:
        ** (Mox.UnexpectedCallError) no expectation defined for Dotcom.Utils.DateTime.Mock.now/0 in process #PID<0.538.0> with args []
            (mox 1.2.0) lib/mox.ex:903: Mox.__dispatch__/4
            (dotcom 0.0.1) lib/dotcom/system_status.ex:22: Dotcom.SystemStatus.subway_status/0
            (dotcom 0.0.1) lib/dotcom/system_status/subway_cache.ex:31: Dotcom.SystemStatus.SubwayCache.init/1
            (stdlib 7.0.2) gen_server.erl:2276: :gen_server.init_it/2
            (stdlib 7.0.2) gen_server.erl:2236: :gen_server.init_it/6
            (stdlib 7.0.2) proc_lib.erl:333: :proc_lib.init_p_do_apply/3

Reverting this change combined with the stub_with suggestion looks promising, but we still need to add the stub_with clause to any test whose execution path goes through Alerts.Cache.Store. We can shortcut some of the pain by adding the setup clause to ConnCase, but some of the tests that touch Alerts.Cache.Store use ExUnit.Case, which we can't modify.

I don't love this for two reasons:

  1. I actually thought that this change was good, and I'm not keen on reverting it.
  2. The way that it fails makes it flaky and hard to debug - the first failure crashes the GenServer, which doesn't raise an error; the second failure then crashes and raises an error because the GenServer doesn't exist anymore, but gives no clue about when or why the GenServer crashed to begin with. This'll crop up again any time we add a new test and forget to add the stub_with somewhere where we should have added it, but then the error we get will be flaky and not easily tied to the offending test.

Would you be up for pairing on this at some point? Hopefully there's a better solution that I'm not thinking of right now?

@@ -0,0 +1,60 @@
defmodule Dotcom.SystemStatus.SubwayCache do
Copy link
Contributor Author

@joshlarson joshlarson Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was kind of torn about doing this versus a @cacheable decoration. There were a few reasons I went this route, but none of them are dealbreakers:

  • I knew how to use GenServers in this way, and (to me at least), the code was pretty easy to write and read.
  • I wasn't sure about redis serializing and deserializing subway status in a way that would preserve status equality when things aren't changing (thus avoiding spamming open sockets with noop subway_status_updated messages)
  • I wasn't sold on the idea of having to bust the subway status cache whenever alerts change - needing to do that kind of manipulation feels like it defeats the purpose of the @cacheable decoration to begin with.
  • There's no real need for a TTL, since status will be recomputed whenever alerts are updated.

There are some potential downsides that I can see:

  • This is storing subway status state in a different way than any of the other pieces of state, which creates cognitive load.
  • Each dotcom node needs to store subway status (performance problem?)
  • If for some reason the alerts fetcher doesn't run on a particular node, then sockets connected to that node will have outdated subway status data.

I tested out ☝️ locally and in deployed environments and it seemed to work, so I'm not convinced that the downsides are actual problems, but I did want to list them for full transparency.

What do you all think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question I have is I'm not sure we need this intermediate layer... couldn't the LiveView itself subscribe to the alerts, then recompute subway_status() in its handle_info callback?

In answer to your actual question, I think the GenServer's a fine way to handle this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Talked offline, but want to post this here also)

This was a potentially premature optimization - without SubwayCache, the alerts fetcher will notify every connected browser session every time an alerts fetch happens (typically once per minute). With SubwayCache, the connected browser sessions will only get notified when subway status itself changes.


msgid "Main Hotline"
msgstr "Línea caliente principal"
msgstr "Hotline principal"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, did we run into a using-different-versions-of-libretranslate problem again? 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uggggh. I'm pinned on version 1.7.2. What's everyone else using??

@github-actions github-actions bot removed the dev-green Deploy to dev-green label Oct 7, 2025
@thecristen thecristen merged commit 3f088e3 into main Oct 8, 2025
22 checks passed
@thecristen thecristen deleted the jdl/feat/live-update-subway-status-on-alerts-page branch October 8, 2025 19:29
thecristen added a commit that referenced this pull request Oct 14, 2025
* fix: Don't pattern-match on the string `"Now"` (#2733)

* fix: Show Orange Line headways in Schedule Finder (#2734)

* cleanup: Remove redundant Green Line schedule accordion (#2735)

* chore: Replace commuter-rail alerts page with a LiveView (#2732)

* fix: Remove trip planner from `robots.txt` to allow search engines to crawl it (#2738)

* fix: Images in embedded alerts with no descriptions (#2736)

* fix: Don't double-wrap alerts-page LiveViews in `container` elements (#2739)

* cleanup: Remove old `MTicket` plug (#2740)

* fix(cms): handle case where _format=json is not the last item

The previous documentation indicated that `_format=json` would always be
the final item in the query string. This is not always the case, so our
replacement strategy needs to handle it appearing anywhere.

We do this with a regular expression which can get find the string in
any location, and replace it with either an empty string (if it's at
the beginning or end) or a single `&` if it's in the middle

Test cases are also updated to handle all three positions.

* [Translate] system status (#2730)

* fix: Use the correct URL for the commuter rail LiveView and add tests (#2737)

* feat(Components): CR survey banner (#2743)

* feat(Components): CR survey banner

* add the survey banner to commuter rail pages

* fix(TripPlan.Loops): refine interline logic (#2746)

to be considered a loop for merging legs, legs must be on the same route.

* Monthly upgrade (#2742)

* build(deps): bump actions/upload-pages-artifact from 3 to 4 (#2709)

Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](actions/upload-pages-artifact@v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump axios from 1.8.2 to 1.12.2 (#2747)

Bumps [axios](https://github.com/axios/axios) from 1.8.2 to 1.12.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.8.2...v1.12.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Localization for the datepicker in dotcom (#2749)

* localization test

* extract

* fix: Alert counts for multi-trip alerts in commuter rail status (#2744)

* fix: Don't crash the planned disruptions page if an alert is recurring and "until further notice" (#2751)

* Update base.csv (#2752)

Adding in "The RIDE" to list of terms to not translate.

* chore(deps): bump tmp and artillery (#2745)

Removes [tmp](https://github.com/raszi/node-tmp). It's no longer used after updating ancestor dependency [artillery](https://github.com/artilleryio/artillery). These dependencies need to be updated together.


Removes `tmp`

Updates `artillery` from 2.0.22 to 2.0.26
- [Release notes](https://github.com/artilleryio/artillery/releases)
- [Commits](artilleryio/artillery@artillery-2.0.22...artillery-2.0.26)

---
updated-dependencies:
- dependency-name: tmp
  dependency-version: 
  dependency-type: indirect
- dependency-name: artillery
  dependency-version: 2.0.26
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump aws-actions/configure-aws-credentials from 4 to 5 (#2748)

Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 4 to 5.
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](aws-actions/configure-aws-credentials@v4...v5)

---
updated-dependencies:
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(SearchHits): formatted search hits

* Improve search hit text and icon formatting
- Add file name text wrap
- Prevent icons from shrinking thanks to flexbox

* fix(TimetableLoader): distinguish error vs empty (#2755)

* feedback(SearchHits): more times

* chore: delete more things

* chore: delete known unused templates + styles (#2756)

* fix: contain input in narrow screen width

* feat: add bus stop id

* fix: restore tailwind for the JS-based dropdown

* feat: Live-update subway status on subway alerts page (#2675)

* feat: Live-update subway status on subway alerts page

* Update gettext reference line numbers

* fix: Dialyzer errors

* cleanup: Call system status directly on init

* cleanup: Abstract `SubwayCache.subscribe/0` and make the topic name a module attr

* cleanup: Use DotcomWeb.Endpoint rather than Phoenix.PubSub for braodcast/subscribe

* cleanup: Use `DotcomWeb.Endpoint` for alert update broadcasting as well

* fix: Update translations

* cleanup: Abstract `Alerts.Cache.Store.subscribe/0`

* docs: Add docs for `SubwayCache.Behaviour`

* cleanup: Remove unnecessary `handle_info` call

* livebook: Move "No alerts!" section to the top

---------

Co-authored-by: Cristen Jones <cjones3@mbta.com>

* refactor: Simplify boundary between `<.alerts_commuter_rail_status />` and `SystemStatus.CommuterRail.commuter_rail_status/0` (#2750)

* tests: Update `CommuterRailStatusTest` setup to use alerts instead of CR status output

* livebook: Add livebooks that set up CR status edge cases

* refactor: Have `<.alerts_commuter_rail_status />` use structured train/service impact info instead of unstructured `alert_counts`

* cleanup: Remove explicit `serivce_today?` in backend struct (in favor of `status`: `:no_scheduled_service`)

* tests: Update `Dotcom.SystemStatus.CommuterRailTest` to not assert on `alert_counts`

* cleanup: Remove `alert_counts`

* cleanup: Have `commuter_rail_status/0` return a list instead of a map

* cleanup: Remove a layer of excess nesting in `<.alerts_commuter_rail_status />`

* cleanup: Restore `count_or_time/1`

* gettext: Update translations

* fix integration tests

* update failing JS test

* fix broken smoke test

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Josh Larson <jlarson@mbta.com>
Co-authored-by: Paul Swartz <pswartz@mbta.com>
Co-authored-by: Anthony Shull <ashull@mbta.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cdong-lgtm <cdong@mbta.com>
thecristen added a commit that referenced this pull request Oct 14, 2025
* chore: phoenix_live_view config

* chore: remove unused searchbar form

* chore: remove existing search page

# Conflicts:
#	lib/dotcom_web/controllers/search_controller.ex
#	test/dotcom_web/controllers/search_controller_test.exs

* chore: update phoenix_live_view

* deps(mix): add live_isolated_component

* feat(Live.SearchPage): interactive search UI

* temporary: Faker in all envs

* update translations

* feedback: nicer stream situation

- avoids disappearing results while loading
- displays total result count
- removes "load more" when end has been reached

* patch broken hit highlight

not sure why this broke now

* update for new backend format

* feat(SearchHits): formatted search hits (#2753)

* fix: Don't pattern-match on the string `"Now"` (#2733)

* fix: Show Orange Line headways in Schedule Finder (#2734)

* cleanup: Remove redundant Green Line schedule accordion (#2735)

* chore: Replace commuter-rail alerts page with a LiveView (#2732)

* fix: Remove trip planner from `robots.txt` to allow search engines to crawl it (#2738)

* fix: Images in embedded alerts with no descriptions (#2736)

* fix: Don't double-wrap alerts-page LiveViews in `container` elements (#2739)

* cleanup: Remove old `MTicket` plug (#2740)

* fix(cms): handle case where _format=json is not the last item

The previous documentation indicated that `_format=json` would always be
the final item in the query string. This is not always the case, so our
replacement strategy needs to handle it appearing anywhere.

We do this with a regular expression which can get find the string in
any location, and replace it with either an empty string (if it's at
the beginning or end) or a single `&` if it's in the middle

Test cases are also updated to handle all three positions.

* [Translate] system status (#2730)

* fix: Use the correct URL for the commuter rail LiveView and add tests (#2737)

* feat(Components): CR survey banner (#2743)

* feat(Components): CR survey banner

* add the survey banner to commuter rail pages

* fix(TripPlan.Loops): refine interline logic (#2746)

to be considered a loop for merging legs, legs must be on the same route.

* Monthly upgrade (#2742)

* build(deps): bump actions/upload-pages-artifact from 3 to 4 (#2709)

Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](actions/upload-pages-artifact@v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump axios from 1.8.2 to 1.12.2 (#2747)

Bumps [axios](https://github.com/axios/axios) from 1.8.2 to 1.12.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.8.2...v1.12.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Localization for the datepicker in dotcom (#2749)

* localization test

* extract

* fix: Alert counts for multi-trip alerts in commuter rail status (#2744)

* fix: Don't crash the planned disruptions page if an alert is recurring and "until further notice" (#2751)

* Update base.csv (#2752)

Adding in "The RIDE" to list of terms to not translate.

* chore(deps): bump tmp and artillery (#2745)

Removes [tmp](https://github.com/raszi/node-tmp). It's no longer used after updating ancestor dependency [artillery](https://github.com/artilleryio/artillery). These dependencies need to be updated together.


Removes `tmp`

Updates `artillery` from 2.0.22 to 2.0.26
- [Release notes](https://github.com/artilleryio/artillery/releases)
- [Commits](artilleryio/artillery@artillery-2.0.22...artillery-2.0.26)

---
updated-dependencies:
- dependency-name: tmp
  dependency-version: 
  dependency-type: indirect
- dependency-name: artillery
  dependency-version: 2.0.26
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump aws-actions/configure-aws-credentials from 4 to 5 (#2748)

Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 4 to 5.
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](aws-actions/configure-aws-credentials@v4...v5)

---
updated-dependencies:
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(SearchHits): formatted search hits

* Improve search hit text and icon formatting
- Add file name text wrap
- Prevent icons from shrinking thanks to flexbox

* fix(TimetableLoader): distinguish error vs empty (#2755)

* feedback(SearchHits): more times

* chore: delete more things

* chore: delete known unused templates + styles (#2756)

* fix: contain input in narrow screen width

* feat: add bus stop id

* fix: restore tailwind for the JS-based dropdown

* feat: Live-update subway status on subway alerts page (#2675)

* feat: Live-update subway status on subway alerts page

* Update gettext reference line numbers

* fix: Dialyzer errors

* cleanup: Call system status directly on init

* cleanup: Abstract `SubwayCache.subscribe/0` and make the topic name a module attr

* cleanup: Use DotcomWeb.Endpoint rather than Phoenix.PubSub for braodcast/subscribe

* cleanup: Use `DotcomWeb.Endpoint` for alert update broadcasting as well

* fix: Update translations

* cleanup: Abstract `Alerts.Cache.Store.subscribe/0`

* docs: Add docs for `SubwayCache.Behaviour`

* cleanup: Remove unnecessary `handle_info` call

* livebook: Move "No alerts!" section to the top

---------

Co-authored-by: Cristen Jones <cjones3@mbta.com>

* refactor: Simplify boundary between `<.alerts_commuter_rail_status />` and `SystemStatus.CommuterRail.commuter_rail_status/0` (#2750)

* tests: Update `CommuterRailStatusTest` setup to use alerts instead of CR status output

* livebook: Add livebooks that set up CR status edge cases

* refactor: Have `<.alerts_commuter_rail_status />` use structured train/service impact info instead of unstructured `alert_counts`

* cleanup: Remove explicit `serivce_today?` in backend struct (in favor of `status`: `:no_scheduled_service`)

* tests: Update `Dotcom.SystemStatus.CommuterRailTest` to not assert on `alert_counts`

* cleanup: Remove `alert_counts`

* cleanup: Have `commuter_rail_status/0` return a list instead of a map

* cleanup: Remove a layer of excess nesting in `<.alerts_commuter_rail_status />`

* cleanup: Restore `count_or_time/1`

* gettext: Update translations

* fix integration tests

* update failing JS test

* fix broken smoke test

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Josh Larson <jlarson@mbta.com>
Co-authored-by: Paul Swartz <pswartz@mbta.com>
Co-authored-by: Anthony Shull <ashull@mbta.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cdong-lgtm <cdong@mbta.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Josh Larson <jlarson@mbta.com>
Co-authored-by: Paul Swartz <pswartz@mbta.com>
Co-authored-by: Anthony Shull <ashull@mbta.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cdong-lgtm <cdong@mbta.com>
thecristen added a commit that referenced this pull request Oct 15, 2025
* deps(mix): install algolia_ex

* format js

* remove hackney

* refactor(SearchService): search using Algolia

* feat: restore/fix click analytics

* Another translation update

* feedback & test fix

* feat(Live.SearchPage): interactive search UI (#2731)

* chore: phoenix_live_view config

* chore: remove unused searchbar form

* chore: remove existing search page

# Conflicts:
#	lib/dotcom_web/controllers/search_controller.ex
#	test/dotcom_web/controllers/search_controller_test.exs

* chore: update phoenix_live_view

* deps(mix): add live_isolated_component

* feat(Live.SearchPage): interactive search UI

* temporary: Faker in all envs

* update translations

* feedback: nicer stream situation

- avoids disappearing results while loading
- displays total result count
- removes "load more" when end has been reached

* patch broken hit highlight

not sure why this broke now

* update for new backend format

* feat(SearchHits): formatted search hits (#2753)

* fix: Don't pattern-match on the string `"Now"` (#2733)

* fix: Show Orange Line headways in Schedule Finder (#2734)

* cleanup: Remove redundant Green Line schedule accordion (#2735)

* chore: Replace commuter-rail alerts page with a LiveView (#2732)

* fix: Remove trip planner from `robots.txt` to allow search engines to crawl it (#2738)

* fix: Images in embedded alerts with no descriptions (#2736)

* fix: Don't double-wrap alerts-page LiveViews in `container` elements (#2739)

* cleanup: Remove old `MTicket` plug (#2740)

* fix(cms): handle case where _format=json is not the last item

The previous documentation indicated that `_format=json` would always be
the final item in the query string. This is not always the case, so our
replacement strategy needs to handle it appearing anywhere.

We do this with a regular expression which can get find the string in
any location, and replace it with either an empty string (if it's at
the beginning or end) or a single `&` if it's in the middle

Test cases are also updated to handle all three positions.

* [Translate] system status (#2730)

* fix: Use the correct URL for the commuter rail LiveView and add tests (#2737)

* feat(Components): CR survey banner (#2743)

* feat(Components): CR survey banner

* add the survey banner to commuter rail pages

* fix(TripPlan.Loops): refine interline logic (#2746)

to be considered a loop for merging legs, legs must be on the same route.

* Monthly upgrade (#2742)

* build(deps): bump actions/upload-pages-artifact from 3 to 4 (#2709)

Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](actions/upload-pages-artifact@v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump axios from 1.8.2 to 1.12.2 (#2747)

Bumps [axios](https://github.com/axios/axios) from 1.8.2 to 1.12.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.8.2...v1.12.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Localization for the datepicker in dotcom (#2749)

* localization test

* extract

* fix: Alert counts for multi-trip alerts in commuter rail status (#2744)

* fix: Don't crash the planned disruptions page if an alert is recurring and "until further notice" (#2751)

* Update base.csv (#2752)

Adding in "The RIDE" to list of terms to not translate.

* chore(deps): bump tmp and artillery (#2745)

Removes [tmp](https://github.com/raszi/node-tmp). It's no longer used after updating ancestor dependency [artillery](https://github.com/artilleryio/artillery). These dependencies need to be updated together.


Removes `tmp`

Updates `artillery` from 2.0.22 to 2.0.26
- [Release notes](https://github.com/artilleryio/artillery/releases)
- [Commits](artilleryio/artillery@artillery-2.0.22...artillery-2.0.26)

---
updated-dependencies:
- dependency-name: tmp
  dependency-version: 
  dependency-type: indirect
- dependency-name: artillery
  dependency-version: 2.0.26
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump aws-actions/configure-aws-credentials from 4 to 5 (#2748)

Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 4 to 5.
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](aws-actions/configure-aws-credentials@v4...v5)

---
updated-dependencies:
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(SearchHits): formatted search hits

* Improve search hit text and icon formatting
- Add file name text wrap
- Prevent icons from shrinking thanks to flexbox

* fix(TimetableLoader): distinguish error vs empty (#2755)

* feedback(SearchHits): more times

* chore: delete more things

* chore: delete known unused templates + styles (#2756)

* fix: contain input in narrow screen width

* feat: add bus stop id

* fix: restore tailwind for the JS-based dropdown

* feat: Live-update subway status on subway alerts page (#2675)

* feat: Live-update subway status on subway alerts page

* Update gettext reference line numbers

* fix: Dialyzer errors

* cleanup: Call system status directly on init

* cleanup: Abstract `SubwayCache.subscribe/0` and make the topic name a module attr

* cleanup: Use DotcomWeb.Endpoint rather than Phoenix.PubSub for braodcast/subscribe

* cleanup: Use `DotcomWeb.Endpoint` for alert update broadcasting as well

* fix: Update translations

* cleanup: Abstract `Alerts.Cache.Store.subscribe/0`

* docs: Add docs for `SubwayCache.Behaviour`

* cleanup: Remove unnecessary `handle_info` call

* livebook: Move "No alerts!" section to the top

---------

Co-authored-by: Cristen Jones <cjones3@mbta.com>

* refactor: Simplify boundary between `<.alerts_commuter_rail_status />` and `SystemStatus.CommuterRail.commuter_rail_status/0` (#2750)

* tests: Update `CommuterRailStatusTest` setup to use alerts instead of CR status output

* livebook: Add livebooks that set up CR status edge cases

* refactor: Have `<.alerts_commuter_rail_status />` use structured train/service impact info instead of unstructured `alert_counts`

* cleanup: Remove explicit `serivce_today?` in backend struct (in favor of `status`: `:no_scheduled_service`)

* tests: Update `Dotcom.SystemStatus.CommuterRailTest` to not assert on `alert_counts`

* cleanup: Remove `alert_counts`

* cleanup: Have `commuter_rail_status/0` return a list instead of a map

* cleanup: Remove a layer of excess nesting in `<.alerts_commuter_rail_status />`

* cleanup: Restore `count_or_time/1`

* gettext: Update translations

* fix integration tests

* update failing JS test

* fix broken smoke test

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Josh Larson <jlarson@mbta.com>
Co-authored-by: Paul Swartz <pswartz@mbta.com>
Co-authored-by: Anthony Shull <ashull@mbta.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cdong-lgtm <cdong@mbta.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Josh Larson <jlarson@mbta.com>
Co-authored-by: Paul Swartz <pswartz@mbta.com>
Co-authored-by: Anthony Shull <ashull@mbta.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cdong-lgtm <cdong@mbta.com>

* fix flawed substring

this broke on Freetown's stop address

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Josh Larson <jlarson@mbta.com>
Co-authored-by: Paul Swartz <pswartz@mbta.com>
Co-authored-by: Anthony Shull <ashull@mbta.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: cdong-lgtm <cdong@mbta.com>
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.

3 participants