From ee0f425f77bafdd33e97ea921f293b082fff99bf Mon Sep 17 00:00:00 2001 From: "joseph@dxw.com" Date: Wed, 20 Nov 2024 17:48:41 +0000 Subject: [PATCH] Use date rather than day in the URL Currently we store the day ('today', 'tomorrow', etc.) in the URL. If the user shares the tomorrow URL on Monday and the recipient clicks on Tuesday, they will see the forecast for Wednesday. This change means we store the date ('2024-11-20') instead. If the date is outside of the bounds of the three day forecast, we fall back to the nearest day (so today for past dates and day after tomorrow for future dates). The former URLs with day will still work, but date takes precedence. --- app/controllers/forecasts_controller.rb | 15 +++++- .../controllers/forecast_controller.js | 6 +-- app/javascript/controllers/map_controller.js | 2 +- app/views/forecasts/_forecast_tabs.html.erb | 6 +-- .../forecasts/_location_selector.html.erb | 2 +- spec/controllers/forecasts_controller_spec.rb | 49 ++++++++++++++++++- 6 files changed, 68 insertions(+), 12 deletions(-) diff --git a/app/controllers/forecasts_controller.rb b/app/controllers/forecasts_controller.rb index 095e2ad7..7879696c 100644 --- a/app/controllers/forecasts_controller.rb +++ b/app/controllers/forecasts_controller.rb @@ -2,7 +2,8 @@ class ForecastsController < ApplicationController def show @maptiler_api_key = ENV.fetch("MAPTILER_API_KEY") @zone = zone - @day = params.fetch("day", "today") + @date = Date.parse(params.fetch("date")) if params[:date] + @day = @date ? day_from_date(@date) : params.fetch("day", "today") @pollutant = params.fetch("pollutant", "Total") @forecasts = CercForecastService.latest_forecasts_for(zone).data @day_forecast = forecast_for_day(@day, @forecasts) @@ -10,6 +11,18 @@ def show private + def day_from_date(date) + if date <= Date.today + "today" + elsif date == Date.tomorrow + "tomorrow" + elsif date >= Date.tomorrow + "day_after_tomorrow" + else + "today" + end + end + def forecast_for_day(day, forecasts) case day when "today" diff --git a/app/javascript/controllers/forecast_controller.js b/app/javascript/controllers/forecast_controller.js index d31a8a20..a7f2d09f 100644 --- a/app/javascript/controllers/forecast_controller.js +++ b/app/javascript/controllers/forecast_controller.js @@ -9,12 +9,12 @@ export default class ForecastController extends Controller { } changeDay(event) { - const selectedDay = event.currentTarget.dataset.day; + const selectedDate = event.currentTarget.dataset.date; - this.updateUrl({ day: selectedDay }); + this.updateUrl({ date: selectedDate }); // Update contents of daySelector - this.daySelectorTarget.value = selectedDay; + this.daySelectorTarget.value = selectedDate; this.reloadPrediction(); } diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js index 65443a3d..fee019dc 100644 --- a/app/javascript/controllers/map_controller.js +++ b/app/javascript/controllers/map_controller.js @@ -26,7 +26,7 @@ export default class MapController extends Controller { updateSettings() { const pollutant = this.pollutantSelectorTarget.value; - const date = this.daySelectorTarget.querySelector(".active").dataset.date; + const date = this.daySelectorTarget.value; const url = new URL(window.location.href); const lat = parseFloat(url.searchParams.get("lat")); const lng = parseFloat(url.searchParams.get("lng")); diff --git a/app/views/forecasts/_forecast_tabs.html.erb b/app/views/forecasts/_forecast_tabs.html.erb index 0e3db44d..47646e71 100644 --- a/app/views/forecasts/_forecast_tabs.html.erb +++ b/app/views/forecasts/_forecast_tabs.html.erb @@ -1,9 +1,5 @@

Air pollution for <%= @zone.name %>

-
+
<%= render(DayTabComponent.new(forecast: @forecasts.first, day: 'today', active: @day == 'today')) %> <%= render(DayTabComponent.new(forecast: @forecasts.second, day: 'tomorrow', active: @day == 'tomorrow')) %> <%= render(DayTabComponent.new(forecast: @forecasts.third, day: 'day_after_tomorrow', active: @day == 'day_after_tomorrow')) %> diff --git a/app/views/forecasts/_location_selector.html.erb b/app/views/forecasts/_location_selector.html.erb index 8df08b64..27b685ec 100644 --- a/app/views/forecasts/_location_selector.html.erb +++ b/app/views/forecasts/_location_selector.html.erb @@ -3,5 +3,5 @@ options_for_select(Zone.all.order(:name).map(&:name), @zone.name), data: { "forecast-target": "zoneSelector", action: "change->forecast#changeZone" } %> - <%= hidden_field_tag :day, @day, data: { "forecast-target": "daySelector" } %> + <%= hidden_field_tag :date, @date, data: { "forecast-target": "daySelector", "map-target": "daySelector" } %> <% end %> diff --git a/spec/controllers/forecasts_controller_spec.rb b/spec/controllers/forecasts_controller_spec.rb index 6c1f025f..bb5a00e0 100644 --- a/spec/controllers/forecasts_controller_spec.rb +++ b/spec/controllers/forecasts_controller_spec.rb @@ -41,7 +41,7 @@ end context "when a recognised _day_ parameter is received" do - it "renders the turbo update template" do + it "renders the _show_ template" do allow(CercForecastService).to receive(:latest_forecasts_for).and_return(forecasts) get :show, params: {day: :today} @@ -60,5 +60,52 @@ }.to raise_error(ArgumentError, "Invalid day: yesterday") end end + + context "when a _date_ parameter is received" do + before do + allow(CercForecastService).to receive(:latest_forecasts_for).and_return(forecasts) + get :show, params: {date: date.to_s} + end + + context "when the date is in the past" do + let(:date) { 5.day.ago.to_date } + + it "shows the forecast for today" do + expect(assigns(:day_forecast)).to eq(forecasts.data.first) + end + end + + context "when the date is today" do + let(:date) { Date.today } + + it "shows the forecast for today" do + expect(assigns(:day_forecast)).to eq(forecasts.data.first) + end + end + + context "when the date is tomorrow" do + let(:date) { Date.tomorrow } + + it "shows the forecast for tomorrow" do + expect(assigns(:day_forecast)).to eq(forecasts.data.second) + end + end + + context "when the date is the day after tomorrow" do + let(:date) { 2.days.from_now.to_date } + + it "shows the forecast for the day after tomorrow" do + expect(assigns(:day_forecast)).to eq(forecasts.data.third) + end + end + + context "when the date is in the future" do + let(:date) { 5.days.from_now.to_date } + + it "shows the forecast for the day after tomorrow" do + expect(assigns(:day_forecast)).to eq(forecasts.data.third) + end + end + end end end