forked from shibao/cannery
		
	shot groups to shot records
This commit is contained in:
		@@ -1,10 +1,10 @@
 | 
			
		||||
defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
defmodule CanneryWeb.Components.AddShotRecordComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can create a ShotGroup
 | 
			
		||||
  Livecomponent that can create a ShotRecord
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo.Pack}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotRecord, Ammo.Pack}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.{JS, Socket}
 | 
			
		||||
 | 
			
		||||
@@ -19,8 +19,8 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{pack: pack, current_user: current_user} = assigns, socket) do
 | 
			
		||||
    changeset =
 | 
			
		||||
      %ShotGroup{date: Date.utc_today()}
 | 
			
		||||
      |> ShotGroup.create_changeset(current_user, pack, %{})
 | 
			
		||||
      %ShotRecord{date: Date.utc_today()}
 | 
			
		||||
      |> ShotRecord.create_changeset(current_user, pack, %{})
 | 
			
		||||
 | 
			
		||||
    {:ok, socket |> assign(assigns) |> assign(:changeset, changeset)}
 | 
			
		||||
  end
 | 
			
		||||
@@ -28,12 +28,12 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "validate",
 | 
			
		||||
        %{"shot_group" => shot_group_params},
 | 
			
		||||
        %{"shot_record" => shot_record_params},
 | 
			
		||||
        %{assigns: %{pack: pack, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    params = shot_group_params |> process_params(pack)
 | 
			
		||||
    params = shot_record_params |> process_params(pack)
 | 
			
		||||
 | 
			
		||||
    changeset = %ShotGroup{} |> ShotGroup.create_changeset(current_user, pack, params)
 | 
			
		||||
    changeset = %ShotRecord{} |> ShotRecord.create_changeset(current_user, pack, params)
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
      case changeset |> Changeset.apply_action(:validate) do
 | 
			
		||||
@@ -46,17 +46,17 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "save",
 | 
			
		||||
        %{"shot_group" => shot_group_params},
 | 
			
		||||
        %{"shot_record" => shot_record_params},
 | 
			
		||||
        %{
 | 
			
		||||
          assigns: %{pack: pack, current_user: current_user, return_to: return_to}
 | 
			
		||||
        } = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      shot_group_params
 | 
			
		||||
      shot_record_params
 | 
			
		||||
      |> process_params(pack)
 | 
			
		||||
      |> ActivityLog.create_shot_group(current_user, pack)
 | 
			
		||||
      |> ActivityLog.create_shot_record(current_user, pack)
 | 
			
		||||
      |> case do
 | 
			
		||||
        {:ok, _shot_group} ->
 | 
			
		||||
        {:ok, _shot_record} ->
 | 
			
		||||
          prompt = dgettext("prompts", "Shots recorded successfully")
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +69,7 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
 | 
			
		||||
  # calculate count from shots left
 | 
			
		||||
  defp process_params(params, %Pack{count: count}) do
 | 
			
		||||
    shot_group_count =
 | 
			
		||||
    shot_record_count =
 | 
			
		||||
      if params |> Map.get("ammo_left", "") == "" do
 | 
			
		||||
        nil
 | 
			
		||||
      else
 | 
			
		||||
@@ -77,6 +77,6 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
        count - new_count
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    params |> Map.put("count", shot_group_count)
 | 
			
		||||
    params |> Map.put("count", shot_record_count)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
defmodule CanneryWeb.Components.ShotRecordTableComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  A component that displays a list of shot groups
 | 
			
		||||
  A component that displays a list of shot records
 | 
			
		||||
  """
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Ammo, ComparableDate}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog.ShotRecord, Ammo, ComparableDate}
 | 
			
		||||
  alias Ecto.UUID
 | 
			
		||||
  alias Phoenix.LiveView.{Rendered, Socket}
 | 
			
		||||
 | 
			
		||||
@@ -12,26 +12,29 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
          %{
 | 
			
		||||
            required(:id) => UUID.t(),
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            optional(:shot_groups) => [ShotGroup.t()],
 | 
			
		||||
            optional(:shot_records) => [ShotRecord.t()],
 | 
			
		||||
            optional(:actions) => Rendered.t(),
 | 
			
		||||
            optional(any()) => any()
 | 
			
		||||
          },
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{id: _id, shot_groups: _shot_groups, current_user: _current_user} = assigns, socket) do
 | 
			
		||||
  def update(
 | 
			
		||||
        %{id: _id, shot_records: _shot_records, current_user: _current_user} = assigns,
 | 
			
		||||
        socket
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(assigns)
 | 
			
		||||
      |> assign_new(:actions, fn -> [] end)
 | 
			
		||||
      |> display_shot_groups()
 | 
			
		||||
      |> display_shot_records()
 | 
			
		||||
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_shot_groups(
 | 
			
		||||
  defp display_shot_records(
 | 
			
		||||
         %{
 | 
			
		||||
           assigns: %{
 | 
			
		||||
             shot_groups: shot_groups,
 | 
			
		||||
             shot_records: shot_records,
 | 
			
		||||
             current_user: current_user,
 | 
			
		||||
             actions: actions
 | 
			
		||||
           }
 | 
			
		||||
@@ -46,16 +49,16 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    packs =
 | 
			
		||||
      shot_groups
 | 
			
		||||
      shot_records
 | 
			
		||||
      |> Enum.map(fn %{pack_id: pack_id} -> pack_id end)
 | 
			
		||||
      |> Ammo.get_packs(current_user)
 | 
			
		||||
 | 
			
		||||
    extra_data = %{current_user: current_user, actions: actions, packs: packs}
 | 
			
		||||
 | 
			
		||||
    rows =
 | 
			
		||||
      shot_groups
 | 
			
		||||
      |> Enum.map(fn shot_group ->
 | 
			
		||||
        shot_group |> get_row_data_for_shot_group(columns, extra_data)
 | 
			
		||||
      shot_records
 | 
			
		||||
      |> Enum.map(fn shot_record ->
 | 
			
		||||
        shot_record |> get_row_data_for_shot_record(columns, extra_data)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
@@ -81,12 +84,12 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_row_data_for_shot_group(ShotGroup.t(), columns :: [map()], extra_data :: map()) ::
 | 
			
		||||
  @spec get_row_data_for_shot_record(ShotRecord.t(), columns :: [map()], extra_data :: map()) ::
 | 
			
		||||
          map()
 | 
			
		||||
  defp get_row_data_for_shot_group(shot_group, columns, extra_data) do
 | 
			
		||||
  defp get_row_data_for_shot_record(shot_record, columns, extra_data) do
 | 
			
		||||
    columns
 | 
			
		||||
    |> Map.new(fn %{key: key} ->
 | 
			
		||||
      {key, get_row_value(key, shot_group, extra_data)}
 | 
			
		||||
      {key, get_row_value(key, shot_record, extra_data)}
 | 
			
		||||
    end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -108,13 +111,13 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_row_value(:actions, shot_group, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, shot_group: shot_group}
 | 
			
		||||
  defp get_row_value(:actions, shot_record, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, shot_record: shot_record}
 | 
			
		||||
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <%= render_slot(@actions, @shot_group) %>
 | 
			
		||||
    <%= render_slot(@actions, @shot_record) %>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_row_value(key, shot_group, _extra_data), do: shot_group |> Map.get(key)
 | 
			
		||||
  defp get_row_value(key, shot_record, _extra_data), do: shot_record |> Map.get(key)
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ defmodule CanneryWeb.ExportController do
 | 
			
		||||
        })
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    shot_groups = ActivityLog.list_shot_groups(:all, current_user)
 | 
			
		||||
    shot_records = ActivityLog.list_shot_records(:all, current_user)
 | 
			
		||||
 | 
			
		||||
    containers =
 | 
			
		||||
      Containers.list_containers(current_user)
 | 
			
		||||
@@ -68,7 +68,7 @@ defmodule CanneryWeb.ExportController do
 | 
			
		||||
      user: current_user,
 | 
			
		||||
      ammo_types: ammo_types,
 | 
			
		||||
      packs: packs,
 | 
			
		||||
      shot_groups: shot_groups,
 | 
			
		||||
      shot_records: shot_records,
 | 
			
		||||
      containers: containers
 | 
			
		||||
    })
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ defmodule CanneryWeb.PackLive.Index do
 | 
			
		||||
 | 
			
		||||
  defp apply_action(
 | 
			
		||||
         %{assigns: %{current_user: current_user}} = socket,
 | 
			
		||||
         :add_shot_group,
 | 
			
		||||
         :add_shot_record,
 | 
			
		||||
         %{"id" => id}
 | 
			
		||||
       ) do
 | 
			
		||||
    socket
 | 
			
		||||
 
 | 
			
		||||
@@ -121,7 +121,7 @@
 | 
			
		||||
              </button>
 | 
			
		||||
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.pack_index_path(Endpoint, :add_shot_group, pack)}
 | 
			
		||||
                patch={Routes.pack_index_path(Endpoint, :add_shot_record, pack)}
 | 
			
		||||
                class="mx-2 my-1 text-sm btn btn-primary"
 | 
			
		||||
              >
 | 
			
		||||
                <%= dgettext("actions", "Record shots") %>
 | 
			
		||||
@@ -213,10 +213,10 @@
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :add_shot_group -> %>
 | 
			
		||||
  <% :add_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.pack_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.AddShotGroupComponent}
 | 
			
		||||
        module={CanneryWeb.Components.AddShotRecordComponent}
 | 
			
		||||
        id={:new}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotGroup}
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotRecord}
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.Pack}
 | 
			
		||||
  alias Cannery.{ComparableDate, Containers}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
@@ -15,15 +15,15 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(
 | 
			
		||||
        %{"id" => id, "shot_group_id" => shot_group_id},
 | 
			
		||||
        %{"id" => id, "shot_record_id" => shot_record_id},
 | 
			
		||||
        _url,
 | 
			
		||||
        %{assigns: %{live_action: live_action, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    shot_group = ActivityLog.get_shot_group!(shot_group_id, current_user)
 | 
			
		||||
    shot_record = ActivityLog.get_shot_record!(shot_record_id, current_user)
 | 
			
		||||
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(page_title: page_title(live_action), shot_group: shot_group)
 | 
			
		||||
      |> assign(page_title: page_title(live_action), shot_record: shot_record)
 | 
			
		||||
      |> display_pack(id)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
@@ -38,8 +38,8 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp page_title(:add_shot_group), do: gettext("Record Shots")
 | 
			
		||||
  defp page_title(:edit_shot_group), do: gettext("Edit Shot Records")
 | 
			
		||||
  defp page_title(:add_shot_record), do: gettext("Record Shots")
 | 
			
		||||
  defp page_title(:edit_shot_record), do: gettext("Edit Shot Records")
 | 
			
		||||
  defp page_title(:move), do: gettext("Move Ammo")
 | 
			
		||||
  defp page_title(:show), do: gettext("Show Ammo")
 | 
			
		||||
  defp page_title(:edit), do: gettext("Edit Ammo")
 | 
			
		||||
@@ -69,13 +69,13 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "delete_shot_group",
 | 
			
		||||
        "delete_shot_record",
 | 
			
		||||
        %{"id" => id},
 | 
			
		||||
        %{assigns: %{pack: %{id: pack_id}, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    {:ok, _} =
 | 
			
		||||
      ActivityLog.get_shot_group!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_group(current_user)
 | 
			
		||||
      ActivityLog.get_shot_record!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_record(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Shot records deleted succesfully")
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_pack(pack_id)}
 | 
			
		||||
@@ -93,12 +93,12 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
      %{label: gettext("Actions"), key: :actions, sortable: false}
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    shot_groups = ActivityLog.list_shot_groups_for_pack(pack, current_user)
 | 
			
		||||
    shot_records = ActivityLog.list_shot_records_for_pack(pack, current_user)
 | 
			
		||||
 | 
			
		||||
    rows =
 | 
			
		||||
      shot_groups
 | 
			
		||||
      |> Enum.map(fn shot_group ->
 | 
			
		||||
        pack |> get_table_row_for_shot_group(shot_group, columns)
 | 
			
		||||
      shot_records
 | 
			
		||||
      |> Enum.map(fn shot_record ->
 | 
			
		||||
        pack |> get_table_row_for_shot_record(shot_record, columns)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
@@ -107,7 +107,7 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
      original_count: Ammo.get_original_count(pack, current_user),
 | 
			
		||||
      percentage_remaining: Ammo.get_percentage_remaining(pack, current_user),
 | 
			
		||||
      container: container_id && Containers.get_container!(container_id, current_user),
 | 
			
		||||
      shot_groups: shot_groups,
 | 
			
		||||
      shot_records: shot_records,
 | 
			
		||||
      columns: columns,
 | 
			
		||||
      rows: rows
 | 
			
		||||
    )
 | 
			
		||||
@@ -119,9 +119,9 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
  @spec display_currency(float()) :: String.t()
 | 
			
		||||
  defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
 | 
			
		||||
 | 
			
		||||
  @spec get_table_row_for_shot_group(Pack.t(), ShotGroup.t(), [map()]) :: map()
 | 
			
		||||
  defp get_table_row_for_shot_group(pack, %{id: id, date: date} = shot_group, columns) do
 | 
			
		||||
    assigns = %{pack: pack, shot_group: shot_group}
 | 
			
		||||
  @spec get_table_row_for_shot_record(Pack.t(), ShotRecord.t(), [map()]) :: map()
 | 
			
		||||
  defp get_table_row_for_shot_record(pack, %{id: id, date: date} = shot_record, columns) do
 | 
			
		||||
    assigns = %{pack: pack, shot_record: shot_record}
 | 
			
		||||
 | 
			
		||||
    columns
 | 
			
		||||
    |> Map.new(fn %{key: key} ->
 | 
			
		||||
@@ -139,11 +139,11 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
            ~H"""
 | 
			
		||||
            <div class="px-4 py-2 space-x-4 flex justify-center items-center">
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.pack_show_path(Endpoint, :edit_shot_group, @pack, @shot_group)}
 | 
			
		||||
                patch={Routes.pack_show_path(Endpoint, :edit_shot_record, @pack, @shot_record)}
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "Edit shot group of %{shot_group_count} shots",
 | 
			
		||||
                    shot_group_count: @shot_group.count
 | 
			
		||||
                  dgettext("actions", "Edit shot record of %{shot_record_count} shots",
 | 
			
		||||
                    shot_record_count: @shot_record.count
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
@@ -153,12 +153,12 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
              <.link
 | 
			
		||||
                href="#"
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                phx-click="delete_shot_group"
 | 
			
		||||
                phx-value-id={@shot_group.id}
 | 
			
		||||
                phx-click="delete_shot_record"
 | 
			
		||||
                phx-value-id={@shot_record.id}
 | 
			
		||||
                data-confirm={dgettext("prompts", "Are you sure you want to delete this shot record?")}
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "Delete shot record of %{shot_group_count} shots",
 | 
			
		||||
                    shot_group_count: @shot_group.count
 | 
			
		||||
                  dgettext("actions", "Delete shot record of %{shot_record_count} shots",
 | 
			
		||||
                    shot_record_count: @shot_record.count
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
@@ -168,7 +168,7 @@ defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
          key ->
 | 
			
		||||
            shot_group |> Map.get(key)
 | 
			
		||||
            shot_record |> Map.get(key)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
      {key, value}
 | 
			
		||||
 
 | 
			
		||||
@@ -90,7 +90,7 @@
 | 
			
		||||
      </.link>
 | 
			
		||||
 | 
			
		||||
      <.link
 | 
			
		||||
        patch={Routes.pack_show_path(Endpoint, :add_shot_group, @pack)}
 | 
			
		||||
        patch={Routes.pack_show_path(Endpoint, :add_shot_record, @pack)}
 | 
			
		||||
        class="mx-4 my-2 btn btn-primary"
 | 
			
		||||
      >
 | 
			
		||||
        <%= dgettext("actions", "Record shots") %>
 | 
			
		||||
@@ -112,7 +112,7 @@
 | 
			
		||||
    <% end %>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <%= unless @shot_groups |> Enum.empty?() do %>
 | 
			
		||||
  <%= unless @shot_records |> Enum.empty?() do %>
 | 
			
		||||
    <hr class="mb-4 w-full" />
 | 
			
		||||
 | 
			
		||||
    <h1 class="mb-4 px-4 py-2 text-center rounded-lg title text-xl">
 | 
			
		||||
@@ -121,7 +121,7 @@
 | 
			
		||||
 | 
			
		||||
    <.live_component
 | 
			
		||||
      module={CanneryWeb.Components.TableComponent}
 | 
			
		||||
      id="pack_shot_groups_table"
 | 
			
		||||
      id="pack_shot_records_table"
 | 
			
		||||
      columns={@columns}
 | 
			
		||||
      rows={@rows}
 | 
			
		||||
    />
 | 
			
		||||
@@ -141,22 +141,22 @@
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :edit_shot_group -> %>
 | 
			
		||||
  <% :edit_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.pack_show_path(Endpoint, :show, @pack)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.RangeLive.FormComponent}
 | 
			
		||||
        id={@shot_group.id}
 | 
			
		||||
        id={@shot_record.id}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        shot_group={@shot_group}
 | 
			
		||||
        shot_record={@shot_record}
 | 
			
		||||
        return_to={Routes.pack_show_path(Endpoint, :show, @pack)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :add_shot_group -> %>
 | 
			
		||||
  <% :add_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.pack_show_path(Endpoint, :show, @pack)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.AddShotGroupComponent}
 | 
			
		||||
        module={CanneryWeb.Components.AddShotRecordComponent}
 | 
			
		||||
        id={:new}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
defmodule CanneryWeb.RangeLive.FormComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can update a ShotGroup
 | 
			
		||||
  Livecomponent that can update a ShotRecord
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo, Ammo.Pack}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotRecord, Ammo, Ammo.Pack}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ defmodule CanneryWeb.RangeLive.FormComponent do
 | 
			
		||||
  @impl true
 | 
			
		||||
  @spec update(
 | 
			
		||||
          %{
 | 
			
		||||
            required(:shot_group) => ShotGroup.t(),
 | 
			
		||||
            required(:shot_record) => ShotRecord.t(),
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            optional(:pack) => Pack.t(),
 | 
			
		||||
            optional(any()) => any()
 | 
			
		||||
@@ -23,7 +23,7 @@ defmodule CanneryWeb.RangeLive.FormComponent do
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(
 | 
			
		||||
        %{
 | 
			
		||||
          shot_group: %ShotGroup{pack_id: pack_id},
 | 
			
		||||
          shot_record: %ShotRecord{pack_id: pack_id},
 | 
			
		||||
          current_user: current_user
 | 
			
		||||
        } = assigns,
 | 
			
		||||
        socket
 | 
			
		||||
@@ -33,24 +33,24 @@ defmodule CanneryWeb.RangeLive.FormComponent do
 | 
			
		||||
    {:ok, socket |> assign(assigns) |> assign(:pack, pack) |> assign_changeset(%{})}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update(%{shot_group: %ShotGroup{}} = assigns, socket) do
 | 
			
		||||
  def update(%{shot_record: %ShotRecord{}} = assigns, socket) do
 | 
			
		||||
    {:ok, socket |> assign(assigns) |> assign_changeset(%{})}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("validate", %{"shot_group" => shot_group_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(shot_group_params, :validate)}
 | 
			
		||||
  def handle_event("validate", %{"shot_record" => shot_record_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(shot_record_params, :validate)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "save",
 | 
			
		||||
        %{"shot_group" => shot_group_params},
 | 
			
		||||
        %{assigns: %{shot_group: shot_group, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
        %{"shot_record" => shot_record_params},
 | 
			
		||||
        %{assigns: %{shot_record: shot_record, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
          socket
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      case ActivityLog.update_shot_group(shot_group, shot_group_params, current_user) do
 | 
			
		||||
        {:ok, _shot_group} ->
 | 
			
		||||
      case ActivityLog.update_shot_record(shot_record, shot_record_params, current_user) do
 | 
			
		||||
        {:ok, _shot_record} ->
 | 
			
		||||
          prompt = dgettext("prompts", "Shot records updated successfully")
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
@@ -67,22 +67,22 @@ defmodule CanneryWeb.RangeLive.FormComponent do
 | 
			
		||||
             action: live_action,
 | 
			
		||||
             current_user: user,
 | 
			
		||||
             pack: pack,
 | 
			
		||||
             shot_group: shot_group
 | 
			
		||||
             shot_record: shot_record
 | 
			
		||||
           }
 | 
			
		||||
         } = socket,
 | 
			
		||||
         shot_group_params,
 | 
			
		||||
         shot_record_params,
 | 
			
		||||
         action \\ nil
 | 
			
		||||
       ) do
 | 
			
		||||
    default_action =
 | 
			
		||||
      case live_action do
 | 
			
		||||
        :add_shot_group -> :insert
 | 
			
		||||
        editing when editing in [:edit, :edit_shot_group] -> :update
 | 
			
		||||
        :add_shot_record -> :insert
 | 
			
		||||
        editing when editing in [:edit, :edit_shot_record] -> :update
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
      case default_action do
 | 
			
		||||
        :insert -> shot_group |> ShotGroup.create_changeset(user, pack, shot_group_params)
 | 
			
		||||
        :update -> shot_group |> ShotGroup.update_changeset(user, shot_group_params)
 | 
			
		||||
        :insert -> shot_record |> ShotRecord.create_changeset(user, pack, shot_record_params)
 | 
			
		||||
        :update -> shot_record |> ShotRecord.update_changeset(user, shot_record_params)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
    <%= label(f, :count, gettext("Shots fired"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= number_input(f, :count,
 | 
			
		||||
      min: 1,
 | 
			
		||||
      max: @shot_group.count + @pack.count,
 | 
			
		||||
      max: @shot_record.count + @pack.count,
 | 
			
		||||
      class: "input input-primary col-span-2"
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :count, "col-span-3") %>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,17 +4,17 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo}
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotRecord, Ammo}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(%{"search" => search}, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(class: :all, search: search) |> display_shot_groups()}
 | 
			
		||||
    {:ok, socket |> assign(class: :all, search: search) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def mount(_params, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(class: :all, search: nil) |> display_shot_groups()}
 | 
			
		||||
    {:ok, socket |> assign(class: :all, search: nil) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
@@ -24,7 +24,7 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
 | 
			
		||||
  defp apply_action(
 | 
			
		||||
         %{assigns: %{current_user: current_user}} = socket,
 | 
			
		||||
         :add_shot_group,
 | 
			
		||||
         :add_shot_record,
 | 
			
		||||
         %{"id" => id}
 | 
			
		||||
       ) do
 | 
			
		||||
    socket
 | 
			
		||||
@@ -38,7 +38,7 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Edit Shot Records"),
 | 
			
		||||
      shot_group: ActivityLog.get_shot_group!(id, current_user)
 | 
			
		||||
      shot_record: ActivityLog.get_shot_record!(id, current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +46,7 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("New Shot Records"),
 | 
			
		||||
      shot_group: %ShotGroup{}
 | 
			
		||||
      shot_record: %ShotRecord{}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -55,9 +55,9 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Shot Records"),
 | 
			
		||||
      search: nil,
 | 
			
		||||
      shot_group: nil
 | 
			
		||||
      shot_record: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> display_shot_groups()
 | 
			
		||||
    |> display_shot_records()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :search, %{"search" => search}) do
 | 
			
		||||
@@ -65,19 +65,19 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Shot Records"),
 | 
			
		||||
      search: search,
 | 
			
		||||
      shot_group: nil
 | 
			
		||||
      shot_record: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> display_shot_groups()
 | 
			
		||||
    |> display_shot_records()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    {:ok, _} =
 | 
			
		||||
      ActivityLog.get_shot_group!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_group(current_user)
 | 
			
		||||
      ActivityLog.get_shot_record!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_record(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Shot records deleted succesfully")
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
@@ -90,7 +90,7 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    {:ok, _pack} = pack |> Ammo.update_pack(%{"staged" => !pack.staged}, current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Ammo unstaged succesfully")
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
 | 
			
		||||
@@ -102,28 +102,28 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"ammo_type" => %{"class" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :rifle) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> assign(:class, :rifle) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"ammo_type" => %{"class" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :shotgun) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> assign(:class, :shotgun) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"ammo_type" => %{"class" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :pistol) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> assign(:class, :pistol) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"ammo_type" => %{"class" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :all) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> assign(:class, :all) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec display_shot_groups(Socket.t()) :: Socket.t()
 | 
			
		||||
  defp display_shot_groups(
 | 
			
		||||
  @spec display_shot_records(Socket.t()) :: Socket.t()
 | 
			
		||||
  defp display_shot_records(
 | 
			
		||||
         %{assigns: %{class: class, search: search, current_user: current_user}} = socket
 | 
			
		||||
       ) do
 | 
			
		||||
    shot_groups = ActivityLog.list_shot_groups(search, class, current_user)
 | 
			
		||||
    shot_records = ActivityLog.list_shot_records(search, class, current_user)
 | 
			
		||||
    packs = Ammo.list_staged_packs(current_user)
 | 
			
		||||
    chart_data = shot_groups |> get_chart_data_for_shot_group()
 | 
			
		||||
    chart_data = shot_records |> get_chart_data_for_shot_record()
 | 
			
		||||
    original_counts = packs |> Ammo.get_original_counts(current_user)
 | 
			
		||||
    cprs = packs |> Ammo.get_cprs(current_user)
 | 
			
		||||
    last_used_dates = packs |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
@@ -136,14 +136,14 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
      cprs: cprs,
 | 
			
		||||
      last_used_dates: last_used_dates,
 | 
			
		||||
      chart_data: chart_data,
 | 
			
		||||
      shot_groups: shot_groups,
 | 
			
		||||
      shot_records: shot_records,
 | 
			
		||||
      shot_record_count: shot_record_count
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_chart_data_for_shot_group([ShotGroup.t()]) :: [map()]
 | 
			
		||||
  defp get_chart_data_for_shot_group(shot_groups) do
 | 
			
		||||
    shot_groups
 | 
			
		||||
  @spec get_chart_data_for_shot_record([ShotRecord.t()]) :: [map()]
 | 
			
		||||
  defp get_chart_data_for_shot_record(shot_records) do
 | 
			
		||||
    shot_records
 | 
			
		||||
    |> Enum.group_by(fn %{date: date} -> date end, fn %{count: count} -> count end)
 | 
			
		||||
    |> Enum.map(fn {date, rounds} ->
 | 
			
		||||
      sum = Enum.sum(rounds)
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
 | 
			
		||||
        <.link
 | 
			
		||||
          patch={Routes.range_index_path(Endpoint, :add_shot_group, pack)}
 | 
			
		||||
          patch={Routes.range_index_path(Endpoint, :add_shot_record, pack)}
 | 
			
		||||
          class="btn btn-primary"
 | 
			
		||||
        >
 | 
			
		||||
          <%= dgettext("actions", "Record shots") %>
 | 
			
		||||
@@ -117,26 +117,26 @@
 | 
			
		||||
      </.form>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <%= if @shot_groups |> Enum.empty?() do %>
 | 
			
		||||
    <%= if @shot_records |> Enum.empty?() do %>
 | 
			
		||||
      <h1 class="title text-xl text-primary-600">
 | 
			
		||||
        <%= gettext("No shots recorded") %>
 | 
			
		||||
        <%= display_emoji("😔") %>
 | 
			
		||||
      </h1>
 | 
			
		||||
    <% else %>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.ShotGroupTableComponent}
 | 
			
		||||
        id="shot_groups_index_table"
 | 
			
		||||
        shot_groups={@shot_groups}
 | 
			
		||||
        module={CanneryWeb.Components.ShotRecordTableComponent}
 | 
			
		||||
        id="shot_records_index_table"
 | 
			
		||||
        shot_records={@shot_records}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      >
 | 
			
		||||
        <:actions :let={shot_group}>
 | 
			
		||||
        <:actions :let={shot_record}>
 | 
			
		||||
          <div class="px-4 py-2 space-x-4 flex justify-center items-center">
 | 
			
		||||
            <.link
 | 
			
		||||
              patch={Routes.range_index_path(Endpoint, :edit, shot_group)}
 | 
			
		||||
              patch={Routes.range_index_path(Endpoint, :edit, shot_record)}
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "Edit shot record of %{shot_group_count} shots",
 | 
			
		||||
                  shot_group_count: shot_group.count
 | 
			
		||||
                dgettext("actions", "Edit shot record of %{shot_record_count} shots",
 | 
			
		||||
                  shot_record_count: shot_record.count
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
@@ -147,13 +147,13 @@
 | 
			
		||||
              href="#"
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              phx-click="delete"
 | 
			
		||||
              phx-value-id={shot_group.id}
 | 
			
		||||
              phx-value-id={shot_record.id}
 | 
			
		||||
              data-confirm={
 | 
			
		||||
                dgettext("prompts", "Are you sure you want to delete this shot record?")
 | 
			
		||||
              }
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "Delete shot record of %{shot_group_count} shots",
 | 
			
		||||
                  shot_group_count: shot_group.count
 | 
			
		||||
                dgettext("actions", "Delete shot record of %{shot_record_count} shots",
 | 
			
		||||
                  shot_record_count: shot_record.count
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
@@ -171,18 +171,18 @@
 | 
			
		||||
    <.modal return_to={Routes.range_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.RangeLive.FormComponent}
 | 
			
		||||
        id={@shot_group.id}
 | 
			
		||||
        id={@shot_record.id}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        shot_group={@shot_group}
 | 
			
		||||
        shot_record={@shot_record}
 | 
			
		||||
        return_to={Routes.range_index_path(Endpoint, :index)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :add_shot_group -> %>
 | 
			
		||||
  <% :add_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.range_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.AddShotGroupComponent}
 | 
			
		||||
        module={CanneryWeb.Components.AddShotRecordComponent}
 | 
			
		||||
        id={:new}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
 
 | 
			
		||||
@@ -93,19 +93,19 @@ defmodule CanneryWeb.Router do
 | 
			
		||||
    live "/ammo/new", PackLive.Index, :new
 | 
			
		||||
    live "/ammo/edit/:id", PackLive.Index, :edit
 | 
			
		||||
    live "/ammo/clone/:id", PackLive.Index, :clone
 | 
			
		||||
    live "/ammo/add_shot_group/:id", PackLive.Index, :add_shot_group
 | 
			
		||||
    live "/ammo/add_shot_record/:id", PackLive.Index, :add_shot_record
 | 
			
		||||
    live "/ammo/move/:id", PackLive.Index, :move
 | 
			
		||||
    live "/ammo/search/:search", PackLive.Index, :search
 | 
			
		||||
 | 
			
		||||
    live "/ammo/show/:id", PackLive.Show, :show
 | 
			
		||||
    live "/ammo/show/edit/:id", PackLive.Show, :edit
 | 
			
		||||
    live "/ammo/show/add_shot_group/:id", PackLive.Show, :add_shot_group
 | 
			
		||||
    live "/ammo/show/add_shot_record/:id", PackLive.Show, :add_shot_record
 | 
			
		||||
    live "/ammo/show/move/:id", PackLive.Show, :move
 | 
			
		||||
    live "/ammo/show/:id/edit/:shot_group_id", PackLive.Show, :edit_shot_group
 | 
			
		||||
    live "/ammo/show/:id/edit/:shot_record_id", PackLive.Show, :edit_shot_record
 | 
			
		||||
 | 
			
		||||
    live "/range", RangeLive.Index, :index
 | 
			
		||||
    live "/range/edit/:id", RangeLive.Index, :edit
 | 
			
		||||
    live "/range/add_shot_group/:id", RangeLive.Index, :add_shot_group
 | 
			
		||||
    live "/range/add_shot_record/:id", RangeLive.Index, :add_shot_record
 | 
			
		||||
    live "/range/search/:search", RangeLive.Index, :search
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user