Skip to content

Commit

Permalink
Polished date picker implementation (#2195)
Browse files Browse the repository at this point in the history
* date picker working

* polish almost entirely done

* bump metro version and small feedback

* update metro version and reconcile merge conflicts

* remove tz

* handle string datetime_type

* remove tests until we are done with the form

* move defaults

* update to latest mbta metro

* merge conflicts

* latest metro
  • Loading branch information
anthonyshull authored Oct 23, 2024
1 parent 6636e7b commit 88751c7
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 114 deletions.
6 changes: 6 additions & 0 deletions assets/css/_trip-plan-form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,12 @@
}
}

#date-picker-calendar {
.form-control[readonly] {
background-color: $body-bg;
}
}

#trip-planner-inputs {
margin: .75rem 0;
width: 100%;
Expand Down
18 changes: 10 additions & 8 deletions assets/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"leaflet": "^1.4.0",
"leaflet-rotatedmarker": "^0.2.0",
"lodash": "^4.17.21",
"mbta_metro": "^0.0.23",
"mbta_metro": "^0.0.49",
"mobile-detect": "^1.4.5",
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
Expand Down
4 changes: 4 additions & 0 deletions assets/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ module.exports = {
},
plugins: [
...plugins(),
require("@tailwindcss/forms")({
// don't make global styles since they conflict with ours
strategy: "class"
}),
// Allows prefixing tailwind classes with LiveView classes to add rules
// only when LiveView classes are applied, for example:
//
Expand Down
10 changes: 3 additions & 7 deletions lib/dotcom/trip_plan/input_form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ defmodule Dotcom.TripPlan.InputForm do

alias OpenTripPlannerClient.PlanParams

@time_types ~W(now leave_at arrive_by)a

@error_messages %{
from: "Please specify an origin location.",
to: "Please add a destination.",
Expand All @@ -25,13 +23,11 @@ defmodule Dotcom.TripPlan.InputForm do
embeds_one(:from, __MODULE__.Location)
embeds_one(:to, __MODULE__.Location)
embeds_one(:modes, __MODULE__.Modes)
field(:datetime_type, Ecto.Enum, values: @time_types)
field(:datetime_type, :string)
field(:datetime, :naive_datetime)
field(:wheelchair, :boolean, default: true)
end

def time_types, do: @time_types

def initial_modes do
__MODULE__.Modes.fields()
|> Enum.map(&{Atom.to_string(&1), "true"})
Expand All @@ -49,7 +45,7 @@ defmodule Dotcom.TripPlan.InputForm do
%{
fromPlace: PlanParams.to_place_param(from),
toPlace: PlanParams.to_place_param(to),
arriveBy: datetime_type == :arrive_by,
arriveBy: datetime_type == "arrive_by",
date: PlanParams.to_date_param(datetime),
time: PlanParams.to_time_param(datetime),
transportModes: __MODULE__.Modes.selected_mode_keys(modes) |> PlanParams.to_modes_param(),
Expand Down Expand Up @@ -114,7 +110,7 @@ defmodule Dotcom.TripPlan.InputForm do

defp validate_chosen_datetime(changeset) do
case get_field(changeset, :datetime_type) do
:now ->
"now" ->
force_change(changeset, :datetime, Util.now())

_ ->
Expand Down
152 changes: 82 additions & 70 deletions lib/dotcom_web/components/live_components/trip_planner_form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,28 @@ defmodule DotcomWeb.Components.LiveComponents.TripPlannerForm do
use DotcomWeb, :live_component

import DotcomWeb.ViewHelpers, only: [svg: 1]
import MbtaMetro.Components.Feedback
import MbtaMetro.Components.InputGroup
import Phoenix.HTML.Form, only: [input_name: 2, input_value: 2, input_id: 2]
import MbtaMetro.Components.{Feedback, InputGroup}
import Phoenix.HTML.Form, only: [input_value: 2]

alias Dotcom.TripPlan.{InputForm, InputForm.Modes}

@form_defaults %{
"datetime_type" => :now,
"datetime" => NaiveDateTime.local_now(),
"modes" => InputForm.initial_modes(),
"wheelchair" => true
}
alias MbtaMetro.Live.DatePicker

@impl true
def mount(socket) do
form =
%InputForm{}
|> InputForm.changeset(@form_defaults)
|> to_form()

{:ok,
assign(socket, %{
form: form,
location_keys: InputForm.Location.fields(),
show_datepicker: input_value(form, :datetime_type) != :now
})}
form_defaults = %{
"datetime_type" => "now",
"datetime" => Timex.now("America/New_York"),
"modes" => InputForm.initial_modes(),
"wheelchair" => true
}

defaults = %{
form: %InputForm{} |> InputForm.changeset(form_defaults) |> to_form(),
location_keys: InputForm.Location.fields(),
show_datepicker: false
}

{:ok, assign(socket, defaults)}
end

@impl true
Expand All @@ -43,11 +39,11 @@ defmodule DotcomWeb.Components.LiveComponents.TripPlannerForm do
id={@id}
for={@form}
method="get"
phx-change="validate"
phx-submit="save_form"
phx-change="validate"
phx-target={@myself}
>
<div :for={field <- [:from, :to]} class="mb-1">
<div :for={field <- [:from, :to]} class="mb-1" id="trip-planner-locations" phx-update="ignore">
<.algolia_autocomplete
config_type="trip-planner"
placeholder="Enter a location"
Expand All @@ -68,65 +64,43 @@ defmodule DotcomWeb.Components.LiveComponents.TripPlannerForm do
</.feedback>
</.algolia_autocomplete>
</div>
<.fieldset legend="When">
<ul class="m-0 p-0 flex flex-col sm:flex-row list-none">
<li
:for={type <- Ecto.Enum.values(InputForm, :datetime_type)}
class={[
"py-0 px-4",
"border border-solid border-slate-300 bg-white",
"has-[:checked]:bg-blue-50 has-[:checked]:border-blue-600",
"first:max-sm:rounded-t-lg last:max-sm:rounded-b-lg",
"sm:first:rounded-l-lg sm:last:rounded-r-lg"
]}
>
<.input
id={input_id(@form, :datetime_type) <> "_#{type}"}
type="radio"
field={f[:datetime_type]}
value={type}
checked={input_value(@form, :datetime_type) == type}
phx-click="toggle_datepicker"
phx-target={@myself}
/>
</li>
</ul>
<.feedback
:for={{msg, _} <- f[:datetime_type].errors}
:if={used_input?(f[:datetime_type])}
kind={:error}
>
<%= msg %>
</.feedback>
<.label :if={@show_datepicker} for="timepick">
<input
id="timepick"
type="datetime-local"
step="any"
name={input_name(@form, :datetime)}
value={input_value(@form, :datetime)}
/>
<span class="sr-only">Date and time to leave at or arrive by</span>
</.label>
<div>
<.input_group
legend="When"
form={f}
field={:datetime_type}
id="datetime_type"
options={[{"Now", "now"}, {"Leave at", "leave_at"}, {"Arrive by", "arrive_by"}]}
type="radio-button"
class="mb-0"
phx-change="toggle_datepicker"
phx-update="ignore"
/>
<.feedback
:for={{msg, _} <- f[:datetime_type].errors}
:if={used_input?(f[:datetime_type])}
kind={:error}
>
<%= msg %>
</.feedback>
<.live_component
:if={@show_datepicker}
module={DatePicker}
config={datepicker_config()}
field={f[:datetime]}
id={:datepicker}
/>
<.feedback
:for={{msg, _} <- f[:datetime].errors}
:if={used_input?(f[:datetime])}
kind={:error}
>
<%= msg %>
</.feedback>
</.fieldset>
</div>
<div>
<.fieldset legend="Modes">
<.accordion id="input_modes">
<.fieldset id="modes" legend="Modes">
<.accordion id="accordion">
<:heading>
<%= Modes.selected_modes(input_value(f, :modes)) %>
</:heading>
Expand Down Expand Up @@ -157,7 +131,7 @@ defmodule DotcomWeb.Components.LiveComponents.TripPlannerForm do
</div>
</div>
<div class="col-start-2 justify-self-end">
<.button color="green" type="submit" phx-disable-with="Planning your trip...">
<.button type="submit" phx-disable-with="Planning your trip...">
Get trip suggestions
</.button>
</div>
Expand All @@ -167,8 +141,28 @@ defmodule DotcomWeb.Components.LiveComponents.TripPlannerForm do
end

@impl true
def handle_event("toggle_datepicker", %{"value" => datetime_value}, socket) do
{:noreply, assign(socket, :show_datepicker, datetime_value !== "now")}
@doc """
If the user selects "now" for the date and time, hide the datepicker.
This will destroy the flatpickr instance.
If the user selects arrive by or leave at, then we show the datepicker and set the time to the nearest 5 minutes.
"""
def handle_event("toggle_datepicker", %{"input_form" => %{"datetime_type" => "now"}}, socket) do
new_socket =
socket
|> assign(show_datepicker: false)
|> push_event("set-datetime", %{datetime: nearest_5_minutes()})

{:noreply, new_socket}
end

def handle_event("toggle_datepicker", _, socket) do
new_socket =
socket
|> assign(show_datepicker: true)
|> push_event("set-datetime", %{datetime: nearest_5_minutes()})

{:noreply, new_socket}
end

def handle_event("validate", %{"input_form" => params}, socket) do
Expand Down Expand Up @@ -197,4 +191,22 @@ defmodule DotcomWeb.Components.LiveComponents.TripPlannerForm do
{:noreply, assign(socket, %{form: form})}
end
end

defp datepicker_config do
%{
default_date: Timex.now("America/New_York"),
enable_time: true,
max_date: Schedules.Repo.end_of_rating(),
min_date: Timex.today("America/New_York")
}
end

defp nearest_5_minutes do
datetime = Timex.now("America/New_York")
minutes = datetime.minute
rounded_minutes = Float.ceil(minutes / 5) * 5
added_minutes = Kernel.trunc(rounded_minutes - minutes)

Timex.shift(datetime, minutes: added_minutes)
end
end
2 changes: 1 addition & 1 deletion lib/dotcom_web/components/trip_planner/itinerary_group.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defmodule DotcomWeb.Components.TripPlanner.ItineraryGroup do
<div class="mb-3 p-2 border border-2 border-slate-200">
<% [first | rest] = @group %>
<div class="text-slate-800 font-bold">Group with <%= Enum.count(@group) %> options</div>
<.accordion id="itinerary_group">
<.accordion id="itinerary-group">
<:heading>
<%= format_datetime_full(first.departure) %> — <%= format_datetime_full(first.arrival) %>
</:heading>
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ defmodule DotCom.Mixfile do
{:jason, "1.4.4", override: true},
{:logster, "1.1.1"},
{:mail, "0.3.1"},
{:mbta_metro, "0.0.16"},
{:mbta_metro, "0.0.49"},
{:mock, "0.3.8", [only: :test]},
{:mox, "1.1.0", [only: :test]},
{:nebulex, "2.6.3"},
Expand Down
Loading

0 comments on commit 88751c7

Please sign in to comment.