Skip to content

Commit d8bc5ba

Browse files
feat: show new stop page layout & features (#3491)
* fix(autocomplete): send CSRF token in POST * fix(DotcomWeb.Router): consolidate pipelines * refactor(StopMapLive): get stop_id from session * feat: Add new_stop_page feature flag * feat: show new stop page layout & features * !fixup(autocomplete): send CSRF token in POST * feedback: remove redundant variable * Revert "feat: show new stop page layout & features" This reverts commit 67bf452. # Conflicts: # lib/dotcom_web/templates/stop/show.html.heex * chore: decouple stops_path :show * chore: assign user_agent & remove conn reference * feedback: remove unneeded clause * feat(StopInformationLive): replace stop page * feat: show enabled feature flags in root template * feat: show new stop page layout & features (again) * fixup: fix station amenity layout * fixup: remove entrance and child stop icons * fixup: gettext * Update lib/dotcom_web/live/stop_information_live.ex Co-authored-by: Josh Larson <jlarson@mbta.com> * chore: retire stop map preview route * fixup - delete old StopMapLive * fix(AppStoreControllerTest): use consistent header --------- Co-authored-by: Josh Larson <jlarson@mbta.com>
1 parent f170e80 commit d8bc5ba

33 files changed

Lines changed: 820 additions & 696 deletions

‎assets/css/_stop-page.scss‎

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,26 +66,10 @@
6666
}
6767

6868
.station-amenities {
69-
display: grid;
70-
grid-gap: 1rem;
71-
grid-template-columns: repeat(3, 1fr);
72-
73-
@include media-breakpoint-down(lg) {
74-
grid-template-columns: repeat(2, 1fr);
75-
}
76-
@include media-breakpoint-down(sm) {
77-
grid-gap: unset;
78-
grid-template-columns: repeat(1, 1fr);
79-
}
80-
8169
.c-descriptive-link {
8270
height: auto;
8371
padding: 0;
8472

85-
@include media-breakpoint-down(sm) {
86-
margin-bottom: 1rem;
87-
}
88-
8973
&[disabled] {
9074
background-color: $gray-lightest;
9175
border-color: $gray-light;

‎assets/ts/ui/autocomplete/__tests__/sources-test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ describe("algoliaSource", () => {
239239
algoliaQuery: query,
240240
algoliaIndexesWithParams: indexes
241241
}),
242-
headers: { "Content-Type": "application/json" },
242+
headers: { "Content-Type": "application/json", "X-CSRF-Token": "" },
243243
method: "POST"
244244
});
245245
});

‎assets/ts/ui/autocomplete/sources.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,14 @@ export const algoliaSource = (
163163
: AlgoliaItemTemplate
164164
},
165165
getItems() {
166+
const token = document
167+
.querySelector('meta[name="csrf-token"]')
168+
?.getAttribute("content");
166169
return fetch("/search/query", {
167170
method: "POST",
168171
headers: {
169-
"Content-Type": "application/json"
172+
"Content-Type": "application/json",
173+
"X-CSRF-Token": token || ""
170174
},
171175
body: JSON.stringify({
172176
algoliaQuery: query,

‎config/config.exs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ config :laboratory,
141141
{:in_seat_transfers, "In Seat Transfers",
142142
"Show in-seat transfers available for certain trips"},
143143
{:line_diagram, "New Line Diagram",
144-
"Adds a tab to the schedules page for the new line diagram"}
144+
"Adds a tab to the schedules page for the new line diagram"},
145+
{:new_stop_page, "New Stop Page Layout",
146+
"Reconfigures the stop page and replaces the React view"}
145147
],
146148
cookie: [
147149
# one month,

‎lib/algolia/object.ex‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ defimpl Algolia.Object, for: Stops.Stop do
99
@stops_repo Application.compile_env!(:dotcom, :repo_modules)[:stops]
1010

1111
def object_id(stop), do: "stop-" <> stop.id
12-
def url(stop), do: Util.site_path(:stop_path, [:show, stop])
12+
13+
def url(stop),
14+
do:
15+
DotcomWeb.Router.Helpers.live_path(
16+
DotcomWeb.Endpoint,
17+
DotcomWeb.StopInformationLive,
18+
stop.id
19+
)
1320

1421
def data(stop) do
1522
routes_for_stop = @routes_repo.by_stop(stop.id, include: "")

‎lib/dotcom_web/components/components.ex‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,13 @@ defmodule DotcomWeb.Components do
300300
"""
301301
def dialog_modal(assigns) do
302302
~H"""
303-
<dialog id={@modal_id} class="mbta-modal min-w-[40%] min-h-[50%]" closedby="any" data-open={@open}>
303+
<dialog
304+
id={@modal_id}
305+
class="mbta-modal min-w-[40%] min-h-[50%]"
306+
closedby="any"
307+
data-open={@open}
308+
phx-update="ignore"
309+
>
304310
<form method="dialog" class="flex justify-between gap-md items-center">
305311
<h1 :if={@modal_heading} class="h4 m-0">
306312
{render_slot(@modal_heading)}

‎lib/dotcom_web/controllers/old_site_redirect_controller.ex‎

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,26 @@ defmodule DotcomWeb.OldSiteRedirectController do
1818
"""
1919
def schedules_and_maps(conn, %{"path" => [_mode, "lines", "stations" | _], "stopId" => stop_id}) do
2020
case @stops_repo.old_id_to_gtfs_id(stop_id) do
21-
nil -> permanent_redirect(conn, mode_path(conn, :index))
22-
gtfs_id -> permanent_redirect(conn, stop_path(conn, :show, gtfs_id))
21+
nil ->
22+
permanent_redirect(conn, mode_path(conn, :index))
23+
24+
gtfs_id ->
25+
permanent_redirect(
26+
conn,
27+
live_path(DotcomWeb.Endpoint, DotcomWeb.StopInformationLive, gtfs_id)
28+
)
2329
end
2430
end
2531

2632
def schedules_and_maps(conn, %{"path" => [mode, "lines", "stations" | _]}) do
27-
redirect_mode =
33+
redirect_path =
2834
case mode do
29-
"rail" -> :commuter_rail
30-
"boats" -> :ferry
31-
_ -> :subway
35+
"rail" -> commuter_rail_stops_path(DotcomWeb.Endpoint, :list)
36+
"boats" -> ferry_stops_path(DotcomWeb.Endpoint, :list)
37+
_ -> subway_stops_path(DotcomWeb.Endpoint, :list)
3238
end
3339

34-
permanent_redirect(conn, stop_path(conn, :show, redirect_mode))
40+
permanent_redirect(conn, redirect_path)
3541
end
3642

3743
def schedules_and_maps(conn, %{"path" => ["rail" | _]}) do

‎lib/dotcom_web/controllers/stop_controller.ex‎

Lines changed: 3 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,14 @@ defmodule DotcomWeb.StopController do
66
use Dotcom.Gettext.Sigils
77
use DotcomWeb, :controller
88

9-
import Alerts.Alert, only: [global_banner_alert?: 1, routewide?: 1]
10-
import Dotcom.Alerts.StartTime, only: [active_in_next_n_days?: 3]
11-
12-
alias Alerts.Repo, as: AlertsRepo
139
alias Dotcom.TransitNearMe
1410
alias Leaflet.MapData.Polyline
1511
alias Plug.Conn
1612
alias RoutePatterns.RoutePattern
1713
alias Routes.Route
1814
alias Services.Service
1915
alias Stops.Stop
20-
alias Util.AndOr
2116

22-
@alerts_repo Application.compile_env!(:dotcom, :repo_modules)[:alerts]
23-
@facilities_repo Application.compile_env!(:dotcom, :repo_modules)[:facilities]
2417
@route_patterns_repo Application.compile_env!(:dotcom, :repo_modules)[:route_patterns]
2518
@routes_repo Application.compile_env!(:dotcom, :repo_modules)[:routes]
2619
@stops_repo Application.compile_env!(:dotcom, :repo_modules)[:stops]
@@ -31,10 +24,11 @@ defmodule DotcomWeb.StopController do
3124
}
3225

3326
def index(conn, _params) do
34-
redirect(conn, to: stop_path(conn, :show, :subway))
27+
redirect(conn, to: subway_stops_path(conn, :list))
3528
end
3629

37-
def show(conn, %{"id" => mode}) when mode in ["subway", "commuter-rail", "ferry"] do
30+
def list(conn, _params) do
31+
["stops", mode] = conn.path_info
3832
mode_atom = Route.type_atom(mode)
3933
{mattapan, stop_info} = get_stop_info()
4034

@@ -49,90 +43,6 @@ defmodule DotcomWeb.StopController do
4943
|> render("index.html")
5044
end
5145

52-
@spec show(Plug.Conn.t(), map()) :: Plug.Conn.t()
53-
def show(conn, %{"id" => stop_id} = params) do
54-
stop =
55-
stop_id
56-
|> URI.decode_www_form()
57-
|> @stops_repo.get()
58-
59-
if stop do
60-
if @stops_repo.has_parent?(stop) do
61-
conn
62-
|> redirect(to: stop_path(conn, :show, @stops_repo.get_parent(stop)))
63-
|> halt()
64-
else
65-
routes_by_stop = @routes_repo.by_stop(stop_id, include: "stop.connecting_stops")
66-
one_way_fares = Fares.Format.one_way_ranges(routes_by_stop)
67-
accessible? = accessible?(stop)
68-
69-
amenities =
70-
@facilities_repo.get_for_stop(stop_id)
71-
|> Dotcom.StopAmenity.from_stop_facilities()
72-
73-
conn
74-
|> assign(:breadcrumbs, breadcrumbs(stop, routes_by_stop))
75-
|> meta_description(stop, routes_by_stop)
76-
|> assign_alerts()
77-
|> assign_stop_banner_alerts(routes_by_stop |> Enum.map(& &1.id), conn.assigns.date_time)
78-
|> render("show.html", %{
79-
stop: stop,
80-
amenity_param: Map.get(params, "amenity", "") |> String.to_atom(),
81-
one_way_fares: one_way_fares,
82-
routes_by_stop: routes_by_stop,
83-
parking_amenity: Enum.find(amenities, &(&1.type == :parking)),
84-
bike_amenity: Enum.find(amenities, &(&1.type == :bike)),
85-
elevator_amenity: Enum.find(amenities, &(&1.type == :elevator)),
86-
escalator_amenity: Enum.find(amenities, &(&1.type == :escalator)),
87-
accessibility_amenity: Enum.find(amenities, &(&1.type == :accessibility)),
88-
fare_amenity: Enum.find(amenities, &(&1.type == :fare)),
89-
accessible?: accessible?
90-
})
91-
end
92-
else
93-
check_cms_or_404(conn)
94-
end
95-
end
96-
97-
defp assign_stop_banner_alerts(conn, route_ids, date_time) do
98-
all_alerts_for_stop = conn.assigns.alerts
99-
100-
all_routewide_alerts =
101-
@alerts_repo.by_route_ids(route_ids, date_time)
102-
|> Enum.filter(&routewide?/1)
103-
104-
banner_alerts =
105-
(all_alerts_for_stop ++ all_routewide_alerts)
106-
|> Enum.filter(&banner_alert?(&1, date_time))
107-
108-
assign(conn, :banner_alerts, banner_alerts)
109-
end
110-
111-
defp banner_alert?(alert, now) do
112-
!global_banner_alert?(alert) &&
113-
banner_alert_active_effect?(alert) &&
114-
active_in_next_n_days?(alert, 7, now)
115-
end
116-
117-
defp banner_alert_active_effect?(alert) do
118-
alert.effect in [
119-
:access_issue,
120-
:detour,
121-
:dock_closure,
122-
:dock_issue,
123-
:service_change,
124-
:elevator_closure,
125-
:notice,
126-
:shuttle,
127-
:station_closure,
128-
:station_issue,
129-
:stop_closure,
130-
:stop_moved,
131-
:stop_shoveling,
132-
:suspension
133-
]
134-
end
135-
13646
@spec get(Conn.t(), map) :: Conn.t()
13747
def get(conn, %{"id" => stop_id}) do
13848
json(conn, @stops_repo.get(stop_id))
@@ -240,15 +150,6 @@ defmodule DotcomWeb.StopController do
240150
Map.put(route_pattern, :representative_trip_polyline, polyline)
241151
end
242152

243-
@doc "Redirect users who type in a URL with a slash to the correct URL"
244-
def stop_with_slash_redirect(conn, %{"path" => path}) do
245-
real_id = Enum.join(path, "/")
246-
247-
conn
248-
|> redirect(to: stop_path(conn, :show, real_id))
249-
|> halt
250-
end
251-
252153
@spec get_stop_info :: {DetailedStopGroup.t(), [DetailedStopGroup.t()]}
253154
defp get_stop_info do
254155
[:subway, :commuter_rail, :ferry]
@@ -325,87 +226,4 @@ defmodule DotcomWeb.StopController do
325226

326227
@spec includes_predictions?(TransitNearMe.headsign_data()) :: boolean
327228
defp includes_predictions?(%{times: times}), do: Enum.any?(times, &(&1.prediction != nil))
328-
329-
defp assign_alerts(%{assigns: %{alerts: alerts}} = conn) do
330-
assign(conn, :all_alerts_count, length(alerts))
331-
end
332-
333-
defp assign_alerts(%{path_params: %{"id" => id}} = conn) do
334-
stop_id = URI.decode_www_form(id)
335-
alerts = @alerts_repo.by_stop_id(stop_id)
336-
337-
conn
338-
|> assign(:alerts, alerts)
339-
|> assign(:all_alerts_count, length(alerts))
340-
end
341-
342-
defp assign_alerts(conn) do
343-
assign(conn, :alerts, AlertsRepo.all(conn.assigns.date_time))
344-
end
345-
346-
@spec breadcrumbs(Stop.t(), [Route.t()]) :: [Util.Breadcrumb.t()]
347-
def breadcrumbs(%Stop{name: name}, []) do
348-
breadcrumbs_for_station_type(nil, name)
349-
end
350-
351-
def breadcrumbs(%Stop{station?: true, name: name}, routes) do
352-
routes
353-
|> Enum.min_by(& &1.type)
354-
|> Route.path_atom()
355-
|> breadcrumbs_for_station_type(name)
356-
end
357-
358-
def breadcrumbs(%Stop{name: name}, _routes) do
359-
breadcrumbs_for_station_type(nil, name)
360-
end
361-
362-
defp breadcrumbs_for_station_type(breadcrumb_tab, name)
363-
when breadcrumb_tab in ~w(subway commuter-rail ferry)a do
364-
[
365-
Breadcrumb.build(~t"Stations", stop_path(DotcomWeb.Endpoint, :show, breadcrumb_tab)),
366-
Breadcrumb.build(name)
367-
]
368-
end
369-
370-
defp breadcrumbs_for_station_type(_, name) do
371-
[Breadcrumb.build(name)]
372-
end
373-
374-
@spec meta_description(Conn.t(), Stop.t(), [Route.t()]) :: Conn.t()
375-
defp meta_description(conn, stop, routes),
376-
do:
377-
assign(
378-
conn,
379-
:meta_description,
380-
gettext(
381-
"Station serving MBTA %{lines} lines%{location}.",
382-
lines: lines(routes),
383-
location: location(stop)
384-
)
385-
)
386-
387-
@spec lines([Route.t()]) :: iolist
388-
defp lines(routes) do
389-
routes
390-
|> Enum.map(&(&1.type |> Route.type_atom() |> Route.type_name()))
391-
|> Enum.uniq()
392-
|> AndOr.join(:and)
393-
end
394-
395-
@spec location(Stop.t()) :: String.t()
396-
defp location(stop) do
397-
if stop.address && stop.address != "" do
398-
gettext(" at %{address}", address: stop.address)
399-
else
400-
""
401-
end
402-
end
403-
404-
# A stop is accessible if it is labeled as accessible in GTFS or it doesn't have a parent stop and it serves a bus route.
405-
def accessible?(stop) do
406-
routes = @routes_repo.by_stop(stop.id)
407-
408-
Enum.member?(stop.accessibility, "accessible") ||
409-
(is_nil(stop.parent_id) && Enum.any?(routes, &(&1.type === 3)))
410-
end
411229
end

‎lib/dotcom_web/endpoint.ex‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ defmodule DotcomWeb.Endpoint do
1010
secure: true
1111

1212
socket("/socket", DotcomWeb.UserSocket)
13-
socket("/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]])
13+
14+
socket("/live", Phoenix.LiveView.Socket,
15+
websocket: [connect_info: [:user_agent, session: @session_options]]
16+
)
1417

1518
# Serve at "/" the static files from "priv/static" directory.
1619
#

0 commit comments

Comments
 (0)