diff --git a/lib/towerops_web/helpers/time_helpers.ex b/lib/towerops_web/helpers/time_helpers.ex index cc4c7990..02d94f87 100644 --- a/lib/towerops_web/helpers/time_helpers.ex +++ b/lib/towerops_web/helpers/time_helpers.ex @@ -54,53 +54,93 @@ defmodule ToweropsWeb.TimeHelpers do end @doc """ - Formats a DateTime into a full date and time string. + Formats a DateTime into a full date and time string, converted to the given timezone. ## Examples iex> datetime = ~U[2026-01-15 14:34:00Z] - iex> ToweropsWeb.TimeHelpers.format_datetime(datetime) + iex> ToweropsWeb.TimeHelpers.format_datetime(datetime, "America/New_York") + "Jan 15, 2026 at 09:34 AM EST" + + iex> datetime = ~U[2026-01-15 14:34:00Z] + iex> ToweropsWeb.TimeHelpers.format_datetime(datetime, "UTC") "Jan 15, 2026 at 02:34 PM UTC" """ - @spec format_datetime(DateTime.t() | nil) :: String.t() - def format_datetime(nil), do: "Never" + @spec format_datetime(DateTime.t() | nil, String.t() | nil) :: String.t() + def format_datetime(nil, _timezone), do: "Never" - def format_datetime(datetime) do - Calendar.strftime(datetime, "%b %d, %Y at %I:%M %p %Z") + def format_datetime(datetime, nil), do: format_datetime(datetime, "UTC") + + def format_datetime(datetime, timezone) when is_binary(timezone) do + case DateTime.shift_zone(datetime, timezone) do + {:ok, shifted_dt} -> + Calendar.strftime(shifted_dt, "%b %d, %Y at %I:%M %p %Z") + + {:error, _reason} -> + # Fallback to UTC if timezone is invalid + Calendar.strftime(datetime, "%b %d, %Y at %I:%M %p UTC") + end end @doc """ - Formats a DateTime into a short date string. + Formats a DateTime into a short date string, converted to the given timezone. ## Examples iex> datetime = ~U[2026-01-15 14:34:00Z] - iex> ToweropsWeb.TimeHelpers.format_date(datetime) + iex> ToweropsWeb.TimeHelpers.format_date(datetime, "UTC") "Jan 15, 2026" - """ - @spec format_date(DateTime.t() | nil) :: String.t() - def format_date(nil), do: "Never" + iex> datetime = ~U[2026-01-15 02:00:00Z] + iex> ToweropsWeb.TimeHelpers.format_date(datetime, "America/Los_Angeles") + "Jan 14, 2026" - def format_date(datetime) do - Calendar.strftime(datetime, "%b %d, %Y") + """ + @spec format_date(DateTime.t() | nil, String.t() | nil) :: String.t() + def format_date(nil, _timezone), do: "Never" + + def format_date(datetime, nil), do: format_date(datetime, "UTC") + + def format_date(datetime, timezone) when is_binary(timezone) do + case DateTime.shift_zone(datetime, timezone) do + {:ok, shifted_dt} -> + Calendar.strftime(shifted_dt, "%b %d, %Y") + + {:error, _reason} -> + # Fallback to UTC if timezone is invalid + Calendar.strftime(datetime, "%b %d, %Y") + end end @doc """ - Formats a DateTime into ISO 8601 format with UTC timezone. + Formats a DateTime into ISO 8601 format, converted to the given timezone. ## Examples iex> datetime = ~U[2026-01-15 14:34:00Z] - iex> ToweropsWeb.TimeHelpers.format_iso8601(datetime) + iex> ToweropsWeb.TimeHelpers.format_iso8601(datetime, "UTC") "2026-01-15 14:34:00 UTC" - """ - @spec format_iso8601(DateTime.t() | nil) :: String.t() - def format_iso8601(nil), do: "Never" + iex> datetime = ~U[2026-01-15 14:34:00Z] + iex> ToweropsWeb.TimeHelpers.format_iso8601(datetime, "America/New_York") + "2026-01-15 09:34:00 EST" - def format_iso8601(datetime) do - Calendar.strftime(datetime, "%Y-%m-%d %H:%M:%S UTC") + """ + @spec format_iso8601(DateTime.t() | nil, String.t() | nil) :: String.t() + def format_iso8601(nil, _timezone), do: "Never" + + def format_iso8601(datetime, nil), do: format_iso8601(datetime, "UTC") + + def format_iso8601(datetime, timezone) when is_binary(timezone) do + case DateTime.shift_zone(datetime, timezone) do + {:ok, shifted_dt} -> + tz_abbr = shifted_dt.zone_abbr || timezone + Calendar.strftime(shifted_dt, "%Y-%m-%d %H:%M:%S #{tz_abbr}") + + {:error, _reason} -> + # Fallback to UTC if timezone is invalid + Calendar.strftime(datetime, "%Y-%m-%d %H:%M:%S UTC") + end end end diff --git a/lib/towerops_web/live/user_settings_live.ex b/lib/towerops_web/live/user_settings_live.ex index 75ebd6fb..a7eb3819 100644 --- a/lib/towerops_web/live/user_settings_live.ex +++ b/lib/towerops_web/live/user_settings_live.ex @@ -22,6 +22,7 @@ defmodule ToweropsWeb.UserSettingsLive do |> assign_api_tokens() |> assign_organizations() |> assign_default_organization() + |> assign(:show_add_token_modal, false) |> assign(:show_token_modal, false) |> assign(:created_token, nil) @@ -33,6 +34,16 @@ defmodule ToweropsWeb.UserSettingsLive do {:noreply, assign(socket, :active_tab, tab)} end + @impl true + def handle_event("show_add_token_modal", _params, socket) do + {:noreply, assign(socket, :show_add_token_modal, true)} + end + + @impl true + def handle_event("cancel_add_token", _params, socket) do + {:noreply, assign(socket, :show_add_token_modal, false)} + end + @impl true def handle_event("update_profile", %{"user" => user_params}, socket) do user = socket.assigns.current_scope.user @@ -132,7 +143,7 @@ defmodule ToweropsWeb.UserSettingsLive do end @impl true - def handle_event("create_api_token", %{"name" => name, "organization_id" => org_id}, socket) do + def handle_event("create_api_token", %{"token" => %{"name" => name, "organization_id" => org_id}}, socket) do user = socket.assigns.current_scope.user case Towerops.ApiTokens.create_api_token(%{ @@ -143,6 +154,7 @@ defmodule ToweropsWeb.UserSettingsLive do {:ok, {_token, raw_token}} -> socket = socket + |> assign(:show_add_token_modal, false) |> assign(:created_token, raw_token) |> assign(:show_token_modal, true) |> assign_api_tokens() @@ -633,7 +645,7 @@ defmodule ToweropsWeb.UserSettingsLive do
- - - """ end end