diff --git a/assets/css/_autocomplete-theme.scss b/assets/css/_autocomplete-theme.scss index c624d72584..8aaa7331fa 100644 --- a/assets/css/_autocomplete-theme.scss +++ b/assets/css/_autocomplete-theme.scss @@ -179,7 +179,8 @@ #error-page, #proposed-sales-locations, #sales-locations, -#transit-near-me-locations { +#transit-near-me-locations, +#vote { --aa-search-input-height: 2.5rem; .aa-Form { diff --git a/assets/ts/ui/autocomplete/__tests__/helpers-test.ts b/assets/ts/ui/autocomplete/__tests__/helpers-test.ts index 0769613360..0c691646f0 100644 --- a/assets/ts/ui/autocomplete/__tests__/helpers-test.ts +++ b/assets/ts/ui/autocomplete/__tests__/helpers-test.ts @@ -53,7 +53,8 @@ test("itemWithUrl gets a requested URL", () => { urls: { "transit-near-me": "/transit-near-me/logan", "retail-sales-locations": "/retail-locations-somewhere", - "proposed-sales-locations": "/proposed-locations-near-logan" + "proposed-sales-locations": "/proposed-locations-near-logan", + vote: "/vote" } } as WithUrls; const item2 = itemWithUrl(item, "retail-sales-locations"); diff --git a/assets/ts/ui/autocomplete/config.ts b/assets/ts/ui/autocomplete/config.ts index aec61227c8..7a70843064 100644 --- a/assets/ts/ui/autocomplete/config.ts +++ b/assets/ts/ui/autocomplete/config.ts @@ -157,6 +157,23 @@ const PROPOSED_RETAIL: Partial> = { } }; +/** + * This configuration is used for finding polling locations + */ +const VOTE: Partial> = { + ...baseOptions, + initialState: { + query: getLikelyQueryParams() + }, + getSources({ query }): AutocompleteSource[] { + if (!query) return debounced([]); + return debounced([locationSource(query, 5, "vote")]); + }, + onReset: (): void => { + window.location.assign(`/vote`); + } +}; + /** * This configuration is intended for use within a form, and will update form * values or internal LiveView state instead of navigating to any URL. Further @@ -229,6 +246,7 @@ const ALL: Record ConfigurationOptions> = { "transit-near-me": () => TNM, "retail-locations": () => RETAIL, "proposed-locations": () => PROPOSED_RETAIL, + vote: () => VOTE, "trip-planner": TRIP_PLANNER }; diff --git a/assets/ts/ui/autocomplete/helpers.ts b/assets/ts/ui/autocomplete/helpers.ts index aa0785cd33..ffa1f47b6b 100644 --- a/assets/ts/ui/autocomplete/helpers.ts +++ b/assets/ts/ui/autocomplete/helpers.ts @@ -51,7 +51,8 @@ export const getTitleAttribute = (item: Item): string[] => { export type UrlType = | "transit-near-me" | "retail-sales-locations" - | "proposed-sales-locations"; + | "proposed-sales-locations" + | "vote"; export type WithUrls = T & { urls: Record }; diff --git a/config/runtime.exs b/config/runtime.exs index 45f914ea13..b24515cfc5 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -187,6 +187,8 @@ config :dotcom, DotcomWeb.ViewHelpers, google_tag_manager_auth: System.get_env("GOOGLE_TAG_MANAGER_AUTH"), google_tag_manager_preview: System.get_env("GOOGLE_TAG_MANAGER_PREVIEW") +config :dotcom, google_api_key: System.get_env("GOOGLE_API_KEY") + config :recaptcha, public_key: System.get_env("RECAPTCHA_PUBLIC_KEY"), secret: System.get_env("RECAPTCHA_PRIVATE_KEY", "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe") diff --git a/lib/dotcom_web/controllers/places_controller.ex b/lib/dotcom_web/controllers/places_controller.ex index 674d1f356b..e997284c7c 100644 --- a/lib/dotcom_web/controllers/places_controller.ex +++ b/lib/dotcom_web/controllers/places_controller.ex @@ -155,6 +155,17 @@ defmodule DotcomWeb.PlacesController do Map.take(map, [:latitude, :longitude]) end + vote_params = + case map do + %{formatted: formatted} -> + map + |> Map.take([:latitude, :longitude]) + |> Map.put(:address, formatted) + + _ -> + %{} + end + map |> Map.put_new(:urls, %{ "retail-sales-locations" => @@ -165,7 +176,8 @@ defmodule DotcomWeb.PlacesController do :show_transformation, params ), - "transit-near-me" => transit_near_me_path(DotcomWeb.Endpoint, :index, params) + "transit-near-me" => transit_near_me_path(DotcomWeb.Endpoint, :index, params), + "vote" => vote_path(DotcomWeb.Endpoint, :show, vote_params) }) end diff --git a/lib/dotcom_web/controllers/vote_controller.ex b/lib/dotcom_web/controllers/vote_controller.ex new file mode 100644 index 0000000000..c16b493ba0 --- /dev/null +++ b/lib/dotcom_web/controllers/vote_controller.ex @@ -0,0 +1,83 @@ +defmodule DotcomWeb.VoteController do + @moduledoc """ + Handles rendering the vote widget + """ + + use DotcomWeb, :controller + + import DotcomWeb.ViewHelpers, only: [cms_static_page_path: 2] + + plug(:meta_description) + plug(:clear_polling_results) + + def show( + conn, + %{"address" => address, "latitude" => latitude, "longitude" => longitude} = _params + ) do + google_api_key = Application.get_env(:dotcom, :google_api_key) + + response = + Req.get("https://www.googleapis.com/civicinfo/v2/voterinfo", + params: [ + key: google_api_key, + electionId: "9000", + address: address + ] + ) + + conn = + case response do + {:ok, %{body: %{"pollingLocations" => [polling_location | _]}}} -> + polling_location_name = Recase.to_title(polling_location["address"]["locationName"]) + + params = %{ + "plan" => %{ + "from_latitude" => latitude, + "from_longitude" => longitude, + "from" => address, + "to_latitude" => polling_location["latitude"], + "to_longitude" => polling_location["longitude"], + "to" => polling_location_name + } + } + + conn + |> assign(:polling_location, polling_location) + |> assign(:polling_location_name, polling_location_name) + |> assign(:trip_plan_path, trip_plan_path(DotcomWeb.Endpoint, :index, params)) + + _ -> + conn |> assign(:polling_error, true) + end + + conn + |> assign(:should_scroll, true) + |> assign(:breadcrumbs, [ + Breadcrumb.build("Take the T to Vote", cms_static_page_path(conn, "/vote")) + ]) + |> render("show.html") + end + + def show(conn, _params) do + conn + |> assign(:breadcrumbs, [ + Breadcrumb.build("Vote", cms_static_page_path(conn, "/vote")) + ]) + |> render("show.html") + end + + defp meta_description(conn, _) do + conn + |> assign( + :meta_description, + "Tuesday, November 5 is the last day to vote in the 2024 general election. Use the T to get to your polling location." + ) + end + + defp clear_polling_results(conn, _) do + conn + |> assign(:polling_location, nil) + |> assign(:polling_error, false) + |> assign(:should_scroll, false) + end +end diff --git a/lib/dotcom_web/router.ex b/lib/dotcom_web/router.ex index c9aa822586..d70e4a1b5e 100644 --- a/lib/dotcom_web/router.ex +++ b/lib/dotcom_web/router.ex @@ -220,6 +220,8 @@ defmodule DotcomWeb.Router do for static_page <- StaticPage.static_pages() do get("/#{StaticPage.convert_path(static_page)}", StaticPageController, static_page) end + + get("/vote", VoteController, :show) end scope "/", DotcomWeb do diff --git a/lib/dotcom_web/templates/vote/show.html.heex b/lib/dotcom_web/templates/vote/show.html.heex new file mode 100644 index 0000000000..1438ce03b4 --- /dev/null +++ b/lib/dotcom_web/templates/vote/show.html.heex @@ -0,0 +1,93 @@ +
+
+
+

Take the T to Vote

+ An exterior shot of the Downtown Crossing Station entrance. US Flags line the street, and skyscrapers are visible in the background. Many pedestrians are walking by + +

+ Tuesday, November 5 is the last day to vote in the 2024 + general election. Use the T to get to your polling location. +

+
+ +
"ScrollIntoView"}, else: %{}} + > +

Find Your Polling Place

+ +

Home Address

+ <.algolia_autocomplete + id="vote" + config_type="vote" + placeholder="Enter your registered address" + /> +
+ +
+

Your Polling Place

+ +
+
+ <%= @polling_location_name %> +
+
+ <%= @polling_location["address"]["line1"] %> +
+ <%= @polling_location["address"]["city"] %>, <%= @polling_location["address"]["state"] %> + <%= @polling_location["address"]["zip"] %> +
+ + Plan your trip + +
+
+ +
+

+ We weren't able to find a polling place for your address. You + can also visit the + + Secretary of State's official site + + to find your polling place, and then use the + + MBTA Trip Planner + + to plan your trip. +

+
+
+ +
+
+

More Guides

+
+
+ + Clickable graphic for User Guides + +
+
+ +

+ Our user guides can help you learn how to navigate the system, + get to local events, use accessibility features, and more. +

+ <%= link("View all guides", + to: cms_static_page_path(@conn, "/guides"), + target: "_blank", + class: "c-call-to-action" + ) %> +
+
+
diff --git a/lib/dotcom_web/views/vote_view.ex b/lib/dotcom_web/views/vote_view.ex new file mode 100644 index 0000000000..e05f122a07 --- /dev/null +++ b/lib/dotcom_web/views/vote_view.ex @@ -0,0 +1,6 @@ +defmodule DotcomWeb.VoteView do + @moduledoc """ + View for the vote widget + """ + use DotcomWeb, :view +end