-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(disruptions): Create new disruption page/form component for Arro…
…w V2 (#1073) Adds a new disruption form for the Arrow V2 epic based on the mockup here: https://miro.com/app/board/uXjVKVLTOI0=/?moveToWidget=3458764585920181114&cot=14
- Loading branch information
Showing
10 changed files
with
617 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
defmodule Arrow.Disruptions do | ||
@moduledoc """ | ||
The Disruptions context. | ||
""" | ||
|
||
import Ecto.Query, warn: false | ||
alias Arrow.Repo | ||
|
||
alias Arrow.Disruptions.DisruptionV2 | ||
|
||
@doc """ | ||
Returns the list of disruptionsv2. | ||
## Examples | ||
iex> list_disruptionsv2() | ||
[%DisruptionV2{}, ...] | ||
""" | ||
def list_disruptionsv2 do | ||
Repo.all(DisruptionV2) | ||
end | ||
|
||
@doc """ | ||
Gets a single disruption_v2. | ||
Raises `Ecto.NoResultsError` if the Disruption v2 does not exist. | ||
## Examples | ||
iex> get_disruption_v2!(123) | ||
%DisruptionV2{} | ||
iex> get_disruption_v2!(456) | ||
** (Ecto.NoResultsError) | ||
""" | ||
def get_disruption_v2!(id), do: Repo.get!(DisruptionV2, id) | ||
|
||
@doc """ | ||
Creates a disruption_v2. | ||
## Examples | ||
iex> create_disruption_v2(%{field: value}) | ||
{:ok, %DisruptionV2{}} | ||
iex> create_disruption_v2(%{field: bad_value}) | ||
{:error, %Ecto.Changeset{}} | ||
""" | ||
def create_disruption_v2(attrs \\ %{}) do | ||
%DisruptionV2{} | ||
|> DisruptionV2.changeset(attrs) | ||
|> Repo.insert() | ||
end | ||
|
||
@doc """ | ||
Updates a disruption_v2. | ||
## Examples | ||
iex> update_disruption_v2(disruption_v2, %{field: new_value}) | ||
{:ok, %DisruptionV2{}} | ||
iex> update_disruption_v2(disruption_v2, %{field: bad_value}) | ||
{:error, %Ecto.Changeset{}} | ||
""" | ||
def update_disruption_v2(%DisruptionV2{} = disruption_v2, attrs) do | ||
disruption_v2 | ||
|> DisruptionV2.changeset(attrs) | ||
|> Repo.update() | ||
end | ||
|
||
@doc """ | ||
Deletes a disruption_v2. | ||
## Examples | ||
iex> delete_disruption_v2(disruption_v2) | ||
{:ok, %DisruptionV2{}} | ||
iex> delete_disruption_v2(disruption_v2) | ||
{:error, %Ecto.Changeset{}} | ||
""" | ||
def delete_disruption_v2(%DisruptionV2{} = disruption_v2) do | ||
Repo.delete(disruption_v2) | ||
end | ||
|
||
@doc """ | ||
Returns an `%Ecto.Changeset{}` for tracking disruption_v2 changes. | ||
## Examples | ||
iex> change_disruption_v2(disruption_v2) | ||
%Ecto.Changeset{data: %DisruptionV2{}} | ||
""" | ||
def change_disruption_v2(%DisruptionV2{} = disruption_v2, attrs \\ %{}) do | ||
DisruptionV2.changeset(disruption_v2, attrs) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
defmodule Arrow.Disruptions.DisruptionV2 do | ||
@moduledoc """ | ||
Represents a change to scheduled service for one of our transportions modes. | ||
See: https://github.com/mbta/gtfs_creator/blob/ab5aac52561027aa13888e4c4067a8de177659f6/gtfs_creator2/disruptions/disruption.py | ||
""" | ||
use Ecto.Schema | ||
import Ecto.Changeset | ||
|
||
schema "disruptionsv2" do | ||
field :title, :string | ||
field :mode, Ecto.Enum, values: [:subway, :commuter_rail, :silver_line, :bus] | ||
field :is_active, :boolean | ||
field :description, :string | ||
|
||
timestamps(type: :utc_datetime) | ||
end | ||
|
||
@doc false | ||
def changeset(disruption_v2, attrs) do | ||
disruption_v2 | ||
|> cast(attrs, [:title, :is_active, :description]) | ||
|> cast(attrs, [:mode], force_changes: true) | ||
|> validate_required([:title, :mode, :is_active]) | ||
end | ||
end |
231 changes: 231 additions & 0 deletions
231
lib/arrow_web/live/disruption_v2_live/disruption_v2_view_live.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
defmodule ArrowWeb.DisruptionV2ViewLive do | ||
use ArrowWeb, :live_view | ||
|
||
alias Arrow.Adjustment | ||
alias Arrow.Disruptions | ||
alias Arrow.Disruptions.DisruptionV2 | ||
|
||
@spec disruption_status_labels :: map() | ||
def disruption_status_labels, do: %{Approved: true, Pending: false} | ||
|
||
@spec mode_labels :: map() | ||
def mode_labels, | ||
do: %{ | ||
Subway: :subway, | ||
"Commuter Rail": :commuter_rail, | ||
Bus: :bus, | ||
"Silver Line": :silver_line | ||
} | ||
|
||
def disruption_form(assigns) do | ||
~H""" | ||
<div class="w-75"> | ||
<.simple_form for={@form} id="disruption_v2-form" phx-submit="save" phx-change="validate"> | ||
<div class="flex flex-row"> | ||
<fieldset class="w-50"> | ||
<legend>Title</legend> | ||
<.input field={@form[:title]} type="text" placeholder="Add text" /> | ||
</fieldset> | ||
<fieldset class="w-50 ml-20"> | ||
<legend>Approval Status</legend> | ||
<div :for={{{status, value}, idx} <- Enum.with_index(disruption_status_labels())} %> | ||
<label class="form-check form-check-label"> | ||
<input | ||
name={@form[:is_active].name} | ||
id={"#{@form[:is_active].id}-#{idx}"} | ||
class="form-check-input" | ||
type="radio" | ||
checked={to_string(@form[:is_active].value) == to_string(value)} | ||
value={to_string(value)} | ||
/> | ||
{status} | ||
</label> | ||
</div> | ||
</fieldset> | ||
</div> | ||
<fieldset> | ||
<legend>Mode</legend> | ||
<div :for={{{mode, value}, idx} <- Enum.with_index(mode_labels())}> | ||
<label class="form-check form-check-label"> | ||
<input | ||
name={@form[:mode].name} | ||
id={"#{@form[:mode].id}-#{idx}"} | ||
class="form-check-input" | ||
type="radio" | ||
checked={to_string(@form[:mode].value) == to_string(value) |> IO.inspect()} | ||
value={to_string(value)} | ||
/> | ||
<span | ||
class="m-icon m-icon-sm mr-1" | ||
style={"background-image: url('#{Map.get(@icon_paths, value)}');"} | ||
} | ||
> | ||
</span> | ||
{mode} | ||
</label> | ||
</div> | ||
</fieldset> | ||
<fieldset> | ||
<legend>Description</legend> | ||
<.input | ||
type="textarea" | ||
class="form-control" | ||
cols={30} | ||
field={@form[:description]} | ||
aria-describedby="descriptionHelp" | ||
aria-label="description" | ||
/> | ||
<small id="descriptionHelp" class="form-text"> | ||
please include: types of disruption, place, and reason | ||
</small> | ||
</fieldset> | ||
<:actions> | ||
<div class="w-25 mr-2"> | ||
<.button disabled={not Enum.empty?(@form.source.errors)} class="btn btn-primary w-100"> | ||
Save Disruption | ||
</.button> | ||
</div> | ||
<div class="w-25 mr-2"> | ||
<.link_button | ||
href={~p"/"} | ||
class="btn-outline-primary w-100" | ||
data-confirm="Are you sure you want to cancel? All changes will be lost!" | ||
> | ||
Cancel | ||
</.link_button> | ||
</div> | ||
</:actions> | ||
</.simple_form> | ||
</div> | ||
""" | ||
end | ||
|
||
@impl true | ||
def mount(%{"id" => disruption_id}, _session, socket) do | ||
disruption = Disruptions.get_disruption_v2!(disruption_id) | ||
|
||
socket = | ||
socket | ||
|> assign(:form_action, :edit) | ||
|> assign(:title, "edit disruption") | ||
|> assign(:form, Disruptions.change_disruption_v2(disruption) |> to_form) | ||
|> assign(:errors, %{}) | ||
|> assign(:icon_paths, icon_paths(socket)) | ||
|> assign(:disruption_v2, disruption) | ||
|
||
{:ok, socket} | ||
end | ||
|
||
@impl true | ||
def mount(%{} = _params, _session, socket) do | ||
socket = | ||
socket | ||
|> assign(:form_action, :create) | ||
|> assign(:http_action, ~p"/disruptionsv2/new") | ||
|> assign(:title, "create new disruption") | ||
|> assign(:form, Disruptions.change_disruption_v2(%DisruptionV2{}) |> to_form) | ||
|> assign(:errors, %{}) | ||
|> assign(:icon_paths, icon_paths(socket)) | ||
|> assign(:disruption_v2, %DisruptionV2{}) | ||
|
||
{:ok, socket} | ||
end | ||
|
||
@impl true | ||
def handle_params(params, _url, socket) do | ||
{:noreply, apply_action(socket, socket.assigns.live_action, params)} | ||
end | ||
|
||
@impl true | ||
def handle_event( | ||
"validate", | ||
%{"disruption_v2" => disruption_v2_params}, | ||
%Phoenix.LiveView.Socket{} = socket | ||
) do | ||
form = | ||
socket.assigns.disruption_v2 | ||
|> Disruptions.change_disruption_v2(disruption_v2_params) | ||
|> to_form(action: :validate) | ||
|
||
{:noreply, assign(socket, form: form)} | ||
end | ||
|
||
@impl true | ||
def handle_event("save", %{"disruption_v2" => disruption_v2_params}, socket) do | ||
save_disruption_v2(socket, socket.assigns.form_action, disruption_v2_params) | ||
end | ||
|
||
defp save_disruption_v2(socket, action, disruption_v2_params) do | ||
save_result = | ||
case action do | ||
:create -> | ||
Disruptions.create_disruption_v2(disruption_v2_params) | ||
|
||
:edit -> | ||
Disruptions.update_disruption_v2(socket.assigns.disruption_v2, disruption_v2_params) | ||
|
||
_ -> | ||
raise "Unknown action for disruption form: #{action}" | ||
end | ||
|
||
case save_result do | ||
{:ok, _} -> | ||
{:noreply, | ||
socket | ||
|> put_flash(:info, "Disruption saved successfully")} | ||
|
||
{:error, %Ecto.Changeset{} = changeset} -> | ||
{:noreply, | ||
socket | ||
|> assign(form: to_form(changeset)) | ||
|> put_flash(:error, "Error when saving disruption!")} | ||
end | ||
end | ||
|
||
@adjustment_kind_icon_names %{ | ||
blue_line: "blue-line", | ||
bus: "mode-bus", | ||
commuter_rail: "mode-commuter-rail", | ||
green_line: "green-line", | ||
green_line_b: "green-line-b", | ||
green_line_c: "green-line-c", | ||
green_line_d: "green-line-d", | ||
green_line_e: "green-line-e", | ||
mattapan_line: "mattapan-line", | ||
orange_line: "orange-line", | ||
red_line: "red-line", | ||
silver_line: "silver-line" | ||
} | ||
|
||
defp adjustment_kind_icon_path(socket, kind) do | ||
Phoenix.VerifiedRoutes.static_path( | ||
socket, | ||
"/images/icon-#{@adjustment_kind_icon_names[kind]}-small.svg" | ||
) | ||
end | ||
|
||
defp icon_paths(socket) do | ||
Adjustment.kinds() | ||
|> Enum.map(&{&1, adjustment_kind_icon_path(socket, &1)}) | ||
|> Enum.into(%{}) | ||
|> Map.put( | ||
:subway, | ||
Phoenix.VerifiedRoutes.static_path(socket, "/images/icon-mode-subway-small.svg") | ||
) | ||
end | ||
|
||
defp apply_action(socket, :edit, %{"id" => id}) do | ||
socket | ||
|> assign(:page_title, "Edit Disruption v2") | ||
|> assign(:disruption_v2, Disruptions.get_disruption_v2!(id)) | ||
end | ||
|
||
defp apply_action(socket, :new, _params) do | ||
socket | ||
|> assign(:page_title, "New Disruption v2") | ||
|> assign(:disruption_v2, %DisruptionV2{}) | ||
end | ||
end |
16 changes: 16 additions & 0 deletions
16
lib/arrow_web/live/disruption_v2_live/disruption_v2_view_live.html.heex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<hr /> | ||
|
||
<.header> | ||
{@title} | ||
</.header> | ||
|
||
<hr /> | ||
|
||
<h2>Disruption</h2> | ||
|
||
<.disruption_form | ||
id="disruption_v2_form" | ||
form={@form} | ||
disruption_v2={@disruption_v2} | ||
icon_paths={@icon_paths} | ||
/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.