diff --git a/lib/atomic/accounts.ex b/lib/atomic/accounts.ex
index 0fd35f28e..dd2877564 100644
--- a/lib/atomic/accounts.ex
+++ b/lib/atomic/accounts.ex
@@ -115,6 +115,35 @@ defmodule Atomic.Accounts do
end
end
+ @doc """
+ Return the first and last name of a name.
+
+ ## Examples
+
+ iex> extract_first_last_name("John Doe")
+ "John Doe"
+
+ iex> extract_first_last_name("John")
+ "John"
+
+ iex> extract_first_last_name(nil)
+ ""
+
+ """
+ def extract_first_last_name(name) do
+ names =
+ name
+ |> String.split(" ")
+ |> Enum.filter(&String.match?(String.slice(&1, 0, 1), ~r/^\p{L}$/u))
+ |> Enum.map(&String.capitalize/1)
+
+ case length(names) do
+ 0 -> ""
+ 1 -> hd(names)
+ _ -> List.first(names) <> " " <> List.last(names)
+ end
+ end
+
@doc """
Returns an `%Ecto.Changeset{}` for tracking user changes.
diff --git a/lib/atomic_web.ex b/lib/atomic_web.ex
index c08964aab..06b431091 100644
--- a/lib/atomic_web.ex
+++ b/lib/atomic_web.ex
@@ -98,7 +98,7 @@ defmodule AtomicWeb do
import AtomicWeb.ErrorHelpers
import AtomicWeb.Gettext
- import AtomicWeb.UtilsView
+ import AtomicWeb.ViewUtils
alias AtomicWeb.Router.Helpers, as: Routes
alias Icons.{Heroicons, Ionicons}
diff --git a/lib/atomic_web/live/activity_live/index.ex b/lib/atomic_web/live/activity_live/index.ex
index f015369f8..8c5bc62b0 100644
--- a/lib/atomic_web/live/activity_live/index.ex
+++ b/lib/atomic_web/live/activity_live/index.ex
@@ -1,6 +1,7 @@
defmodule AtomicWeb.ActivityLive.Index do
use AtomicWeb, :live_view
+ alias Atomic.Accounts
alias Atomic.Activities
alias Atomic.Activities.Activity
diff --git a/lib/atomic_web/live/activity_live/index.html.heex b/lib/atomic_web/live/activity_live/index.html.heex
index eacafb63d..d5bd032e1 100644
--- a/lib/atomic_web/live/activity_live/index.html.heex
+++ b/lib/atomic_web/live/activity_live/index.html.heex
@@ -49,7 +49,7 @@
<%= if hd(activity.activity_sessions) do %>
- <%= display_date(hd(activity.activity_sessions).start) %>
+ <%= AtomicWeb.ViewUtils.display_date(hd(activity.activity_sessions).start) %>
<% end %>
<%= if not is_nil(hd(activity.activity_sessions).location) do %>
@@ -71,11 +71,11 @@
- <%= extract_initials(speaker.name) %>
+ <%= Accounts.extract_initials(speaker.name) %>
- <%= extract_first_last_name(speaker.name) %>
+ <%= Accounts.extract_first_last_name(speaker.name) %>
<% end %>
diff --git a/lib/atomic_web/live/activity_live/show.ex b/lib/atomic_web/live/activity_live/show.ex
index da92e20df..48c2e1b49 100644
--- a/lib/atomic_web/live/activity_live/show.ex
+++ b/lib/atomic_web/live/activity_live/show.ex
@@ -1,6 +1,7 @@
defmodule AtomicWeb.ActivityLive.Show do
use AtomicWeb, :live_view
+ alias Atomic.Accounts
alias Atomic.Activities
alias Atomic.Organizations
diff --git a/lib/atomic_web/live/activity_live/show.html.heex b/lib/atomic_web/live/activity_live/show.html.heex
index 573d61e2f..21647815a 100644
--- a/lib/atomic_web/live/activity_live/show.html.heex
+++ b/lib/atomic_web/live/activity_live/show.html.heex
@@ -79,7 +79,7 @@
- <%= display_date(session.start) %>
+ <%= AtomicWeb.ViewUtils.display_date(session.start) %>
@@ -125,11 +125,11 @@
- <%= extract_initials(speaker.name) %>
+ <%= Accounts.extract_initials(speaker.name) %>
<%= live_redirect to: Routes.speaker_show_path(@socket, :show, speaker), class: "text-md text-blue-500" do %>
- <%= extract_first_last_name(speaker.name) %>
+ <%= Accounts.extract_first_last_name(speaker.name) %>
<% end %>
diff --git a/lib/atomic_web/templates/layout/live.html.heex b/lib/atomic_web/templates/layout/live.html.heex
index 959330f84..e810c6c0e 100644
--- a/lib/atomic_web/templates/layout/live.html.heex
+++ b/lib/atomic_web/templates/layout/live.html.heex
@@ -18,7 +18,7 @@
- <%= extract_initials("CeSIUM") %>
+ <%= Atomic.Accounts.extract_initials("CeSIUM") %>
@@ -60,7 +60,7 @@
- <%= extract_initials("NECC") %>
+ <%= Atomic.Accounts.extract_initials("NECC") %>
@@ -178,7 +178,7 @@
<% end %>
<%= live_redirect to: Routes.user_settings_path(@socket, :edit), class: "bg-zinc-200 flex items-center gap-x-4 px-6 py-3 text-sm font-semibold leading-6 text-gray-900" do %>
- <%= extract_initials(@current_user.email) %>
+ <%= Atomic.Accounts.extract_initials(@current_user.email) %>
<%= @current_user.email %>
<% end %>
diff --git a/lib/atomic_web/views/helpers.ex b/lib/atomic_web/views/helpers.ex
index 475c2f5ff..e3db37af3 100644
--- a/lib/atomic_web/views/helpers.ex
+++ b/lib/atomic_web/views/helpers.ex
@@ -4,66 +4,145 @@ defmodule AtomicWeb.ViewUtils do
"""
use Phoenix.HTML
+ import AtomicWeb.Gettext
+
+ alias Timex.Format.DateTime.Formatters.Relative
+
require Timex.Translator
def frontend_url do
Application.fetch_env!(:atomic, AtomicWeb.Endpoint)[:frontend_url]
end
- @doc """
- Display a user's name
+ @doc ~S"""
+ Returns a relative datetime string for the given datetime.
+
## Examples
- iex> display_name(%{first_name: "John", last_name: "Doe"})
- "John Doe"
+
+ iex> relative_datetime(~N[2020-01-01 00:00:00])
+ "3 years ago"
+
+ iex> relative_datetime(~N[2023-01-01 00:00:00] |> Timex.shift(days: 1))
+ "6 months ago"
+
"""
- def display_name(user) do
- "#{user.first_name} #{user.last_name}"
+ def relative_datetime(nil), do: ""
+
+ def relative_datetime(""), do: ""
+
+ def relative_datetime(datetime) do
+ Relative.lformat!(datetime, "{relative}", Gettext.get_locale())
end
- @doc """
- Display a date in format "HH:MM"
+ @doc ~S"""
+ Returns a relative date string for the given date.
+
## Examples
- iex> display_time(~U[2018-01-01 00:00:00Z])
- "00:00"
- iex> display_time(~U[2018-01-01 12:00:00Z])
- "12:00"
- iex> display_time(~U[2018-01-01 23:59:00Z])
- "23:59"
+
+ iex> display_date(~D[2020-01-01])
+ "01-01-2020"
+
+ iex> display_date(~D[2023-01-01])
+ "01-01-2023"
+
"""
- def display_time(%DateTime{} = datetime) do
- hour = two_characters(datetime.hour)
- minute = two_characters(datetime.minute)
+ def display_date(nil), do: ""
+
+ def display_date(""), do: ""
+
+ def display_date(date) when is_binary(date) do
+ date
+ |> Timex.parse!("%FT%H:%M", :strftime)
+ |> Timex.format!("{0D}-{0M}-{YYYY}")
+ end
- "#{hour}:#{minute}"
+ def display_date(date) do
+ Timex.format!(date, "{0D}-{0M}-{YYYY}")
end
- @doc """
- Display a date in a given locale
+ @doc ~S"""
+ Returns a relative time string for the given time.
+
## Examples
- iex> display_date(~N[2021-03-10 02:27:07], "pt")
- "Quarta-feira, 10 de Março de 2021"
- iex> display_date(~N[2023-02-25 22:25:46], "en")
- "Saturday, February 25, 2023"
+
+ iex> display_time(~T[00:00:00])
+ "00:00"
+
+ iex> display_time(~T[23:59:59])
+ "23:59"
"""
- def display_date(datetime, locale \\ "pt")
+ def display_time(nil), do: ""
- def display_date(datetime, "pt" = locale) do
- Timex.Translator.with_locale locale do
- Timex.format!(datetime, "{WDfull}, {D} de {Mfull} de {YYYY}")
- end
+ def display_time(""), do: ""
+
+ def display_time(date) when is_binary(date) do
+ date
+ |> Timex.parse!("%FT%H:%M", :strftime)
+ |> Timex.format!("{0D}-{0M}-{YYYY}")
end
- def display_date(datetime, "en" = locale) do
- Timex.Translator.with_locale locale do
- Timex.format!(datetime, "{WDfull}, {Mfull} {D}, {YYYY}")
- end
+ def display_time(date) do
+ date
+ |> Timex.format!("{h24}:{m}")
end
- defp two_characters(number) do
- if number < 10 do
- "0#{number}"
+ @doc ~S"""
+ Returns a list of first element from tuples where the second element is true
+
+ ## Examples
+
+ iex> class_list([{"col-start-1", true}, {"col-start-2", false}, {"col-start-3", true}])
+ "col-start-1 col-start-3"
+
+ iex> class_list([{"Math", true}, {"Physics", false}, {"Chemistry", false}])
+ "Math"
+ """
+ def class_list(items) do
+ items
+ |> Enum.reject(&(elem(&1, 1) == false))
+ |> Enum.map_join(" ", &elem(&1, 0))
+ end
+
+ @doc ~S"""
+ Returns the class name for a given column
+
+ ## Examples
+
+ iex> col_start(1)
+ "col-start-1"
+
+ iex> col_start(2)
+ "col-start-2"
+
+ iex> col_start(0)
+ "col-start-0"
+
+ iex> col_start(8)
+ "col-start-0"
+ """
+ def col_start(col) do
+ if col in 1..7 do
+ "col-start-#{col}"
else
- number
+ "col-start-0"
end
end
+
+ @doc ~S"""
+ Returns an error message for a given error
+
+ ## Examples
+
+ iex> error_to_string(:too_large)
+ "Too large"
+
+ iex> error_to_string(:not_accepted)
+ "You have selected an unacceptable file type"
+
+ iex> error_to_string(:too_many_files)
+ "You have selected too many files"
+ """
+ def error_to_string(:too_large), do: gettext("Too large")
+ def error_to_string(:not_accepted), do: gettext("You have selected an unacceptable file type")
+ def error_to_string(:too_many_files), do: gettext("You have selected too many files")
end
diff --git a/lib/atomic_web/views/utils_view.ex b/lib/atomic_web/views/utils_view.ex
deleted file mode 100644
index 61111374f..000000000
--- a/lib/atomic_web/views/utils_view.ex
+++ /dev/null
@@ -1,112 +0,0 @@
-defmodule AtomicWeb.UtilsView do
- @moduledoc """
- Utility functions to be used on all views.
- """
- use Phoenix.HTML
-
- import AtomicWeb.Gettext
-
- alias Timex.Format.DateTime.Formatters.Relative
-
- @spec extract_initials(nil | String.t()) :: String.t()
- def extract_initials(nil), do: ""
-
- def extract_initials(name) do
- initials =
- name
- |> String.upcase()
- |> String.split(" ")
- |> Enum.map(&String.slice(&1, 0, 1))
- |> Enum.filter(&String.match?(&1, ~r/^\p{L}$/u))
-
- case length(initials) do
- 0 -> ""
- 1 -> hd(initials)
- _ -> List.first(initials) <> List.last(initials)
- end
- end
-
- @spec extract_first_last_name(nil | String.t()) :: String.t()
- def extract_first_last_name(nil), do: ""
-
- def extract_first_last_name(name) do
- names =
- name
- |> String.split(" ")
- |> Enum.filter(&String.match?(String.slice(&1, 0, 1), ~r/^\p{L}$/u))
- |> Enum.map(&String.capitalize/1)
-
- case length(names) do
- 0 -> ""
- 1 -> hd(names)
- _ -> List.first(names) <> " " <> List.last(names)
- end
- end
-
- def relative_datetime(nil), do: ""
-
- def relative_datetime(""), do: ""
-
- def relative_datetime(datetime) do
- Relative.lformat!(datetime, "{relative}", Gettext.get_locale())
- end
-
- def display_date(nil), do: ""
-
- def display_date(""), do: ""
-
- def display_date(date) when is_binary(date) do
- date
- |> Timex.parse!("%FT%H:%M", :strftime)
- |> Timex.format!("{0D}-{0M}-{YYYY}")
- end
-
- def display_date(date) do
- Timex.format!(date, "{0D}-{0M}-{YYYY}")
- end
-
- def display_time(nil), do: ""
-
- def display_time(""), do: ""
-
- def display_time(date) when is_binary(date) do
- date
- |> Timex.parse!("%FT%H:%M", :strftime)
- |> Timex.format!("{0D}-{0M}-{YYYY}")
- end
-
- def display_time(date) do
- date
- |> Timex.format!("{h24}:{m}")
- end
-
- def class_list(items) do
- items
- |> Enum.reject(&(elem(&1, 1) == false))
- |> Enum.map_join(" ", &elem(&1, 0))
- end
-
- def col_start(col) do
- case col do
- 1 -> "col-start-1"
- 2 -> "col-start-2"
- 3 -> "col-start-3"
- 4 -> "col-start-4"
- 5 -> "col-start-5"
- 6 -> "col-start-6"
- 7 -> "col-start-7"
- _ -> "col-start-0"
- end
- end
-
- def build_path(current_path, params) do
- current_path
- |> URI.parse()
- |> Map.put(:query, URI.encode_query(params))
- |> URI.to_string()
- end
-
- def error_to_string(:too_large), do: gettext("Too large")
- def error_to_string(:not_accepted), do: gettext("You have selected an unacceptable file type")
- def error_to_string(:too_many_files), do: gettext("You have selected too many files")
-end
diff --git a/test/atomic/utils_test.exs b/test/atomic/utils_test.exs
new file mode 100644
index 000000000..a6d1b0526
--- /dev/null
+++ b/test/atomic/utils_test.exs
@@ -0,0 +1,5 @@
+defmodule Atomic.UtilsTest do
+ use ExUnit.Case, async: true
+ import AtomicWeb.ViewUtils
+ doctest AtomicWeb.ViewUtils
+end