From df57323c4900af6432e4731521438584eba5d22a Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 28 Jan 2026 17:19:13 -0500 Subject: [PATCH 01/18] refactor: move start date before end date validation to utility module --- lib/arrow/disruptions/limit.ex | 19 +----------------- lib/arrow/disruptions/replacement_service.ex | 19 +----------------- lib/arrow/gtfs/calendar.ex | 13 +----------- lib/arrow/gtfs/feed_info.ex | 13 +----------- lib/arrow/hastus/service_date.ex | 19 +----------------- lib/arrow/util/util.ex | 21 ++++++++++++++++++++ 6 files changed, 26 insertions(+), 78 deletions(-) diff --git a/lib/arrow/disruptions/limit.ex b/lib/arrow/disruptions/limit.ex index 294979b20..0a67dfe19 100644 --- a/lib/arrow/disruptions/limit.ex +++ b/lib/arrow/disruptions/limit.ex @@ -44,7 +44,7 @@ defmodule Arrow.Disruptions.Limit do ]) |> put_change(:check_for_overlap, true) |> validate_required([:start_date, :end_date, :route_id, :start_stop_id, :end_stop_id]) - |> validate_start_date_before_end_date() + |> Arrow.Util.validate_start_date_before_end_date() |> cast_assoc_day_of_weeks() |> exclusion_constraint(:end_date, name: :no_overlap, @@ -73,23 +73,6 @@ defmodule Arrow.Disruptions.Limit do |> struct!(attrs) end - @spec validate_start_date_before_end_date(Ecto.Changeset.t(t())) :: Ecto.Changeset.t(t()) - defp validate_start_date_before_end_date(changeset) do - start_date = get_field(changeset, :start_date) - end_date = get_field(changeset, :end_date) - - cond do - is_nil(start_date) or is_nil(end_date) -> - changeset - - Date.compare(start_date, end_date) == :gt -> - add_error(changeset, :start_date, "start date should not be after end date") - - true -> - changeset - end - end - @spec cast_assoc_day_of_weeks(Ecto.Changeset.t(t())) :: Ecto.Changeset.t(t()) defp cast_assoc_day_of_weeks(changeset) do start_date = get_field(changeset, :start_date) diff --git a/lib/arrow/disruptions/replacement_service.ex b/lib/arrow/disruptions/replacement_service.ex index 2fd406191..5d59156f8 100644 --- a/lib/arrow/disruptions/replacement_service.ex +++ b/lib/arrow/disruptions/replacement_service.ex @@ -52,28 +52,11 @@ defmodule Arrow.Disruptions.ReplacementService do :disruption_id, :shuttle_id ]) - |> validate_start_date_before_end_date() + |> Arrow.Util.validate_start_date_before_end_date() |> assoc_constraint(:shuttle) |> assoc_constraint(:disruption) end - @spec validate_start_date_before_end_date(Ecto.Changeset.t(t())) :: Ecto.Changeset.t(t()) - defp validate_start_date_before_end_date(changeset) do - start_date = get_field(changeset, :start_date) - end_date = get_field(changeset, :end_date) - - cond do - is_nil(start_date) or is_nil(end_date) -> - changeset - - Date.compare(start_date, end_date) == :gt -> - add_error(changeset, :start_date, "start date should not be after end date") - - true -> - changeset - end - end - def add_timetable(%__MODULE__{} = replacement_service) do timetable = schedule_service_types() diff --git a/lib/arrow/gtfs/calendar.ex b/lib/arrow/gtfs/calendar.ex index d0ce45da8..e301fd98f 100644 --- a/lib/arrow/gtfs/calendar.ex +++ b/lib/arrow/gtfs/calendar.ex @@ -33,18 +33,7 @@ defmodule Arrow.Gtfs.Calendar do ~w[service_id monday tuesday wednesday thursday friday saturday sunday start_date end_date]a ) |> assoc_constraint(:service) - |> validate_start_date_not_after_end_date() - end - - defp validate_start_date_not_after_end_date(changeset) do - start_date = fetch_field!(changeset, :start_date) - end_date = fetch_field!(changeset, :end_date) - - if Date.compare(start_date, end_date) in [:lt, :eq] do - changeset - else - add_error(changeset, :dates, "start date should not be after end date") - end + |> Arrow.Util.validate_start_date_before_end_date() end @impl Arrow.Gtfs.Importable diff --git a/lib/arrow/gtfs/feed_info.ex b/lib/arrow/gtfs/feed_info.ex index 6f36e6974..30774a1e4 100644 --- a/lib/arrow/gtfs/feed_info.ex +++ b/lib/arrow/gtfs/feed_info.ex @@ -32,18 +32,7 @@ defmodule Arrow.Gtfs.FeedInfo do |> validate_required( ~w[id publisher_name publisher_url lang start_date end_date version contact_email]a ) - |> validate_start_date_before_end_date() - end - - defp validate_start_date_before_end_date(changeset) do - start_date = fetch_field!(changeset, :start_date) - end_date = fetch_field!(changeset, :end_date) - - if Date.compare(start_date, end_date) == :lt do - changeset - else - add_error(changeset, :dates, "start date should be before end date") - end + |> Arrow.Util.validate_start_date_before_end_date() end @impl Arrow.Gtfs.Importable diff --git a/lib/arrow/hastus/service_date.ex b/lib/arrow/hastus/service_date.ex index 2d11ab910..ceab51ef1 100644 --- a/lib/arrow/hastus/service_date.ex +++ b/lib/arrow/hastus/service_date.ex @@ -17,7 +17,7 @@ defmodule Arrow.Hastus.ServiceDate do service_date |> cast(attrs, [:start_date, :end_date, :service_id]) |> validate_required([:start_date, :end_date]) - |> validate_start_date_before_end_date() + |> Arrow.Util.validate_start_date_before_end_date() |> assoc_constraint(:service) end @@ -31,21 +31,4 @@ defmodule Arrow.Hastus.ServiceDate do |> Stream.take(7) |> MapSet.new(&Date.day_of_week/1) end - - @spec validate_start_date_before_end_date(Ecto.Changeset.t(t())) :: Ecto.Changeset.t(t()) - defp validate_start_date_before_end_date(changeset) do - start_date = get_field(changeset, :start_date) - end_date = get_field(changeset, :end_date) - - cond do - is_nil(start_date) or is_nil(end_date) -> - changeset - - Date.compare(start_date, end_date) == :gt -> - add_error(changeset, :start_date, "start date must be less than or equal to end date") - - true -> - changeset - end - end end diff --git a/lib/arrow/util/util.ex b/lib/arrow/util/util.ex index 29d2e4f62..41d40d45d 100644 --- a/lib/arrow/util/util.ex +++ b/lib/arrow/util/util.ex @@ -81,4 +81,25 @@ defmodule Arrow.Util do Phoenix.VerifiedRoutes.static_path(conn_or_socket, "/images/icon-bus-outline-small.svg") ) end + + @spec validate_start_date_before_end_date(Ecto.Changeset.t(any())) :: Ecto.Changeset.t(any()) + def validate_start_date_before_end_date(changeset) do + start_date = Ecto.Changeset.get_field(changeset, :start_date) + end_date = Ecto.Changeset.get_field(changeset, :end_date) + + cond do + is_nil(start_date) or is_nil(end_date) -> + changeset + + Date.compare(start_date, end_date) == :gt -> + Ecto.Changeset.add_error( + changeset, + :start_date, + "start date must be less than or equal to end date" + ) + + true -> + changeset + end + end end From 485a531ed21ae69315c8583a90a9caeac175ca1f Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 28 Jan 2026 17:19:48 -0500 Subject: [PATCH 02/18] feat(trainsformer): validate start date is before end date for trainsformer service date --- lib/arrow/trainsformer/service_date.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/arrow/trainsformer/service_date.ex b/lib/arrow/trainsformer/service_date.ex index 948db3b90..a2784366c 100644 --- a/lib/arrow/trainsformer/service_date.ex +++ b/lib/arrow/trainsformer/service_date.ex @@ -39,6 +39,7 @@ defmodule Arrow.Trainsformer.ServiceDate do |> cast(transformed_attrs, [:start_date, :end_date, :service_id]) |> cast_assoc(:service_date_days_of_week, with: &ServiceDateDayOfWeek.changeset/2) |> validate_required([:start_date, :end_date]) + |> Arrow.Util.validate_start_date_before_end_date() |> assoc_constraint(:service) end end From d4da2149a5011a31720e57b86d3aad6aef532ef0 Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 28 Jan 2026 17:20:40 -0500 Subject: [PATCH 03/18] polish: change the opacity on hover for trainsformer icons --- lib/arrow_web/components/disruption_components.ex | 4 ++-- lib/arrow_web/components/edit_trainsformer_export_form.ex | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/arrow_web/components/disruption_components.ex b/lib/arrow_web/components/disruption_components.ex index 11217d718..240240d3f 100644 --- a/lib/arrow_web/components/disruption_components.ex +++ b/lib/arrow_web/components/disruption_components.ex @@ -497,7 +497,7 @@ defmodule ArrowWeb.DisruptionComponents do class="btn-sm p-0" patch={~p"/disruptions/#{@disruption.id}/trainsformer_export/#{export.id}/edit"} > - <.icon name="hero-pencil-solid" class="bg-primary" /> + <.icon name="hero-pencil-solid" class="bg-primary hover:opacity-40" /> <.button :if={!@editing} @@ -508,7 +508,7 @@ defmodule ArrowWeb.DisruptionComponents do phx-value-export={export.id} data-confirm="Are you sure you want to delete this export?" > - <.icon name="hero-trash-solid" class="bg-primary" /> + <.icon name="hero-trash-solid" class="bg-primary hover:opacity-40" /> diff --git a/lib/arrow_web/components/edit_trainsformer_export_form.ex b/lib/arrow_web/components/edit_trainsformer_export_form.ex index fde05df77..b97ed9a8f 100644 --- a/lib/arrow_web/components/edit_trainsformer_export_form.ex +++ b/lib/arrow_web/components/edit_trainsformer_export_form.ex @@ -221,6 +221,7 @@ defmodule ArrowWeb.EditTrainsformerExportForm do phx-value-date_index={f_date.index} phx-target={@myself} > +