Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store pollutant and map coordinates and zoom in URL #122

Merged
merged 6 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/components/day_tab_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
data: {
date: @forecast.date.to_s,
day: @day,
"tab-target": "dayPrediction",
action: "click->tab#changeTab",
"forecast-target": "dayPrediction",
action: "click->forecast#changeDay",
} do
%>
<div class="day" data-tab-target="day">
<div class="day">
<%= @forecast.date == Date.today ? 'Today' : @forecast.date.strftime('%A') %>
</div>
<div class="date">
Expand All @@ -20,7 +20,7 @@
<div class="daqi-label">
<%= @forecast.air_pollution.label.capitalize %>
</div>
<div class="daqi-value" data-tab-target="daqiValue">
<div class="daqi-value">
Index <%= @forecast.air_pollution.value %>/10
</div>
<% end %>
4 changes: 2 additions & 2 deletions app/components/prediction_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div class="prediction <%= name_for_class %> <%= daqi_level_for_class %>" data-controller="prediction">
<div class="prediction <%= name_for_class %> <%= daqi_level_for_class %>">
<div class="summary flex flex-row">
<dt class="name w-1/2"> <%= name_for_label %></dt>
<dd class="daqi-label w-1/2"><%= daqi_level_for_label %></dd>
</div>
<div id="<%= name_for_class %>-guidance" class="guidance <%= guidance_visible? ? "visible" : "hidden" %> <%= guidance_panel_colour %>" data-prediction-target="guidance">
<div id="<%= name_for_class %>-guidance" class="guidance <%= guidance_visible? ? "visible" : "hidden" %> <%= guidance_panel_colour %>">
<%= guidance%>
</div>
</div>
16 changes: 15 additions & 1 deletion app/controllers/forecasts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,27 @@ 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)
end

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"
Expand Down
39 changes: 39 additions & 0 deletions app/javascript/controllers/forecast_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Controller } from "@hotwired/stimulus";

export default class ForecastController extends Controller {
static targets = ["zoneSelector", "daySelector", "pollutantSelector"];

changeZone() {
this.updateUrl({ zone: this.zoneSelectorTarget.value });
this.reloadPrediction();
}

changeDay(event) {
const selectedDate = event.currentTarget.dataset.date;

this.updateUrl({ date: selectedDate });

// Update contents of daySelector
this.daySelectorTarget.value = selectedDate;

this.reloadPrediction();
}

changePollutant() {
this.updateUrl({ pollutant: this.pollutantSelectorTarget.value });
this.reloadPrediction();
}

reloadPrediction() {
// Submit the form to reload the turbo frame
this.zoneSelectorTarget.form.requestSubmit();
}

updateUrl(newParams) {
const url = new URL(window.location.href);
for (const [key, value] of Object.entries(newParams)) {
url.searchParams.set(key, value);
}
window.history.pushState({}, "", url);
}
}
7 changes: 2 additions & 5 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@ window.Stimulus = application;
import MapController from "./map_controller";
application.register("map", MapController);

import PredictionController from "./prediction_controller";
application.register("prediction", PredictionController);

import TabController from "./tab_controller";
application.register("tab", TabController);
import ForecastController from "./forecast_controller";
application.register("forecast", ForecastController);

import NavigationController from "./navigation_controller";
application.register("navigation", NavigationController);
30 changes: 27 additions & 3 deletions app/javascript/controllers/map_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,21 @@ export default class MapController extends Controller {

updateSettings() {
const pollutant = this.pollutantSelectorTarget.value;
const date = this.daySelectorTarget.querySelector(".active").dataset.date;
const newSettings = { pollutant: pollutant, date: 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"));
const center = lat && lng ? [lat, lng] : null;
const zoom = parseInt(url.searchParams.get("zoom"));

console.log({ pollutant, date, center, zoom });

const newSettings = {
pollutant: pollutant,
date: date,
center: center || this.defaultMapSettings.center,
zoom: zoom || this.defaultMapSettings.zoom,
};
this.settings = Object.assign({}, this.defaultMapSettings, newSettings);
}

Expand All @@ -52,6 +65,18 @@ export default class MapController extends Controller {

this.map.createPane("zones");
this.addZonesLayer();

this.map.on("zoomend moveend", () => {
this.updateUrl();
});
}

updateUrl() {
const url = new URL(window.location.href);
url.searchParams.set("lat", this.map.getCenter().lat.toFixed(6));
url.searchParams.set("lng", this.map.getCenter().lng.toFixed(6));
url.searchParams.set("zoom", this.map.getZoom());
window.history.replaceState({}, "", url);
}

addSearchControl() {
Expand Down Expand Up @@ -195,7 +220,6 @@ export default class MapController extends Controller {
}

updatePollutionLayer() {
this.map.removeControl(this.controls.pollution);
this.map.removeLayer(this.layers.pollution);
this.addPollutionLayer();
}
Expand Down
23 changes: 0 additions & 23 deletions app/javascript/controllers/prediction_controller.js

This file was deleted.

23 changes: 0 additions & 23 deletions app/javascript/controllers/tab_controller.js

This file was deleted.

6 changes: 1 addition & 5 deletions app/views/forecasts/_forecast_tabs.html.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
<h3 class="text-xl font-bold mt-4 mb-2"> Air pollution for <%= @zone.name %></h3>
<div
class="tabs"
data-turbo-prefetch="false"
data-map-target="daySelector"
>
<div class="tabs">
<%= 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')) %>
Expand Down
6 changes: 3 additions & 3 deletions app/views/forecasts/_location_selector.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<%= form_tag(false, method: :get) do %>
<%= form_tag(false, method: :get, id: "forecast-form") do %>
<%= select_tag :zone,
options_for_select(Zone.all.order(:name).map(&:name), @zone.name),
data: { controller: "prediction", "prediction-target": "zoneSelector", action: "change->prediction#changeZone" }
data: { "forecast-target": "zoneSelector", action: "change->forecast#changeZone" }
%>
<%= hidden_field_tag :day, @day, data: { "tab-target": "daySelector" } %>
<%= hidden_field_tag :date, @date, data: { "forecast-target": "daySelector", "map-target": "daySelector" } %>
<% end %>
25 changes: 12 additions & 13 deletions app/views/forecasts/_pollutant_selector.html.erb
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<%= form_tag do %>
<%= select_tag :pollutant,
options_for_select({
"All pollutants" => 'Total',
"Nitrogen Dioxide" => 'NO2',
"Particles < 2.5µm" => 'PM25',
"Particles < 10µm" => 'PM10',
"Ozone" => 'O3',
}),
class: "mt-5",
data: { action: "map#updateMap", "map-target": "pollutantSelector" }
%>
<% end %>
<%= select_tag :pollutant,
options_for_select({
"All pollutants" => 'Total',
"Nitrogen Dioxide" => 'NO2',
"Particles < 2.5µm" => 'PM25',
"Particles < 10µm" => 'PM10',
"Ozone" => 'O3',
}, @pollutant),
class: "mt-5",
data: { action: "forecast#changePollutant", "map-target": "pollutantSelector", "forecast-target": "pollutantSelector" },
form: "forecast-form"
%>
2 changes: 1 addition & 1 deletion app/views/forecasts/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<h2 class="text-xl font-bold mb-2">Air quality forecast</h2>
<p>The air quality forecast displays levels of air pollution, ultraviolet rays, pollen, and temperature over the next three days for your area of interest.</p>

<turbo-frame id="forecasts-frame" data-controller="map tab">
<turbo-frame id="forecasts-frame" data-controller="map forecast">
<%= render "location_selector" %>
<%= render "forecast_tabs" %>
<div class="tab-contents">
Expand Down
49 changes: 48 additions & 1 deletion spec/controllers/forecasts_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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