defmodule CanneryWeb.Components.AmmoGroupTableComponent do @moduledoc """ A component that displays a list of ammo groups """ use CanneryWeb, :live_component alias Cannery.{Accounts.User, Ammo.AmmoGroup, ComparableDate} alias Cannery.{ActivityLog, Ammo, Containers} alias Ecto.UUID alias Phoenix.LiveView.{Rendered, Socket} @impl true @spec update( %{ required(:id) => UUID.t(), required(:current_user) => User.t(), required(:ammo_groups) => [AmmoGroup.t()], required(:show_used) => boolean(), optional(:ammo_type) => Rendered.t(), optional(:range) => Rendered.t(), optional(:container) => Rendered.t(), optional(:actions) => Rendered.t(), optional(any()) => any() }, Socket.t() ) :: {:ok, Socket.t()} def update( %{id: _id, ammo_groups: _ammo_group, current_user: _current_user, show_used: _show_used} = assigns, socket ) do socket = socket |> assign(assigns) |> assign_new(:ammo_type, fn -> [] end) |> assign_new(:range, fn -> [] end) |> assign_new(:container, fn -> [] end) |> assign_new(:actions, fn -> [] end) |> display_ammo_groups() {:ok, socket} end defp display_ammo_groups( %{ assigns: %{ ammo_groups: ammo_groups, current_user: current_user, ammo_type: ammo_type, range: range, container: container, actions: actions, show_used: show_used } } = socket ) do columns = if actions == [] do [] else [%{label: gettext("Actions"), key: :actions, sortable: false}] end columns = [ %{label: gettext("Purchased on"), key: :purchased_on, type: ComparableDate}, %{label: gettext("Last used on"), key: :used_up_on, type: ComparableDate} | columns ] columns = if container == [] do columns else [%{label: gettext("Container"), key: :container} | columns] end columns = if range == [] do columns else [%{label: gettext("Range"), key: :range} | columns] end columns = [ %{label: gettext("Price paid"), key: :price_paid}, %{label: gettext("CPR"), key: :cpr} | columns ] columns = if show_used do [ %{label: gettext("Original Count"), key: :original_count}, %{label: gettext("% left"), key: :remaining} | columns ] else columns end columns = [ %{label: if(show_used, do: gettext("Current Count"), else: gettext("Count")), key: :count} | columns ] columns = if ammo_type == [] do columns else [%{label: gettext("Ammo type"), key: :ammo_type} | columns] end extra_data = %{ current_user: current_user, ammo_type: ammo_type, columns: columns, container: container, original_counts: Ammo.get_original_counts(ammo_groups, current_user), cprs: Ammo.get_cprs(ammo_groups, current_user), last_used_dates: ActivityLog.get_last_used_dates(ammo_groups, current_user), actions: actions, range: range } rows = ammo_groups |> Enum.map(fn ammo_group -> ammo_group |> get_row_data_for_ammo_group(extra_data) end) socket |> assign(columns: columns, rows: rows) end @impl true def render(assigns) do ~H"""
<.live_component module={CanneryWeb.Components.TableComponent} id={"table-#{@id}"} columns={@columns} rows={@rows} />
""" end @spec get_row_data_for_ammo_group(AmmoGroup.t(), additional_data :: map()) :: map() defp get_row_data_for_ammo_group(ammo_group, %{columns: columns} = additional_data) do columns |> Map.new(fn %{key: key} -> {key, get_value_for_key(key, ammo_group, additional_data)} end) end @spec get_value_for_key(atom(), AmmoGroup.t(), additional_data :: map()) :: any() | {any(), Rendered.t()} defp get_value_for_key( :ammo_type, %{ammo_type: %{name: ammo_type_name} = ammo_type}, %{ammo_type: ammo_type_block} ) do assigns = %{ammo_type: ammo_type, ammo_type_block: ammo_type_block} {ammo_type_name, ~H""" <%= render_slot(@ammo_type_block, @ammo_type) %> """} end defp get_value_for_key(:price_paid, %{price_paid: nil}, _additional_data), do: {0, gettext("No cost information")} defp get_value_for_key(:price_paid, %{price_paid: price_paid}, _additional_data), do: {price_paid, gettext("$%{amount}", amount: display_currency(price_paid))} defp get_value_for_key(:purchased_on, %{purchased_on: purchased_on} = assigns, _additional_data) do {purchased_on, ~H""" <.date id={"#{@id}-purchased-on"} date={@purchased_on} /> """} end defp get_value_for_key(:used_up_on, %{id: ammo_group_id}, %{last_used_dates: last_used_dates}) do last_used_date = last_used_dates |> Map.get(ammo_group_id) assigns = %{id: ammo_group_id, last_used_date: last_used_date} {last_used_date, ~H""" <%= if @last_used_date do %> <.date id={"#{@id}-last-used-date"} date={@last_used_date} /> <% else %> <%= gettext("Never used") %> <% end %> """} end defp get_value_for_key(:range, %{staged: staged} = ammo_group, %{range: range}) do assigns = %{range: range, ammo_group: ammo_group} {staged, ~H""" <%= render_slot(@range, @ammo_group) %> """} end defp get_value_for_key(:remaining, ammo_group, %{current_user: current_user}) do percentage = ammo_group |> Ammo.get_percentage_remaining(current_user) {percentage, gettext("%{percentage}%", percentage: percentage)} end defp get_value_for_key(:actions, ammo_group, %{actions: actions}) do assigns = %{actions: actions, ammo_group: ammo_group} ~H""" <%= render_slot(@actions, @ammo_group) %> """ end defp get_value_for_key(:container, %{container: nil}, _additional_data), do: {nil, nil} defp get_value_for_key( :container, %{container_id: container_id} = ammo_group, %{container: container, current_user: current_user} ) do assigns = %{ container: %{name: container_name} = container_id |> Containers.get_container!(current_user), container_block: container, ammo_group: ammo_group } {container_name, ~H""" <%= render_slot(@container_block, {@ammo_group, @container}) %> """} end defp get_value_for_key( :original_count, %{id: ammo_group_id}, %{original_counts: original_counts} ) do Map.fetch!(original_counts, ammo_group_id) end defp get_value_for_key(:cpr, %{price_paid: nil}, _additional_data), do: {0, gettext("No cost information")} defp get_value_for_key(:cpr, %{id: ammo_group_id}, %{cprs: cprs}) do amount = Map.fetch!(cprs, ammo_group_id) {amount, gettext("$%{amount}", amount: display_currency(amount))} end defp get_value_for_key(:count, %{count: count}, _additional_data), do: if(count == 0, do: {0, gettext("Empty")}, else: count) defp get_value_for_key(key, ammo_group, _additional_data), do: ammo_group |> Map.get(key) @spec display_currency(float()) :: String.t() defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2) end