forked from shibao/cannery
		
	add container index table
This commit is contained in:
		@@ -47,7 +47,7 @@ defmodule CanneryWeb.Components.TableComponent do
 | 
			
		||||
      if assigns |> Map.has_key?(:initial_key) do
 | 
			
		||||
        assigns.initial_key
 | 
			
		||||
      else
 | 
			
		||||
        columns |> List.first() |> Map.get(:key)
 | 
			
		||||
        columns |> List.first(%{}) |> Map.get(:key)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    initial_sort_mode =
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,11 @@ defmodule CanneryWeb.ContainerLive.Index do
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  import CanneryWeb.Components.ContainerCard
 | 
			
		||||
  alias Cannery.{Containers, Containers.Container, Repo}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
  alias CanneryWeb.{Components.TagCard, Endpoint}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(_params, _session, socket), do: {:ok, socket}
 | 
			
		||||
  def mount(_params, _session, socket), do: {:ok, socket |> assign(view_table: false)}
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do
 | 
			
		||||
@@ -33,8 +33,20 @@ defmodule CanneryWeb.ContainerLive.Index do
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :index, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(:page_title, gettext("Containers"))
 | 
			
		||||
    |> assign(:container, nil)
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Containers"),
 | 
			
		||||
      container: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> display_containers()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :table, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Containers"),
 | 
			
		||||
      container: nil,
 | 
			
		||||
      view_table: true
 | 
			
		||||
    )
 | 
			
		||||
    |> display_containers()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -83,10 +95,127 @@ defmodule CanneryWeb.ContainerLive.Index do
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do
 | 
			
		||||
    new_path =
 | 
			
		||||
      if view_table,
 | 
			
		||||
        do: Routes.container_index_path(Endpoint, :index),
 | 
			
		||||
        else: Routes.container_index_path(Endpoint, :table)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket |> assign(view_table: !view_table) |> push_patch(to: new_path)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_containers(%{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    containers =
 | 
			
		||||
      Containers.list_containers(current_user) |> Repo.preload([:tags, :ammo_groups], force: true)
 | 
			
		||||
 | 
			
		||||
    socket |> assign(containers: containers)
 | 
			
		||||
    columns =
 | 
			
		||||
      [
 | 
			
		||||
        %{label: gettext("Name"), key: :name, type: :string},
 | 
			
		||||
        %{label: gettext("Description"), key: :desc, type: :string},
 | 
			
		||||
        %{label: gettext("Location"), key: :location, type: :string},
 | 
			
		||||
        %{label: gettext("Type"), key: :type, type: :string},
 | 
			
		||||
        %{label: gettext("Packs"), key: :packs, type: :integer},
 | 
			
		||||
        %{label: gettext("Rounds"), key: :rounds, type: :string},
 | 
			
		||||
        %{label: gettext("Tags"), key: :tags, type: :tags},
 | 
			
		||||
        %{label: nil, key: :actions, sortable: false, type: :actions}
 | 
			
		||||
      ]
 | 
			
		||||
      |> Enum.filter(fn %{key: key, type: type} ->
 | 
			
		||||
        # remove columns if all values match defaults
 | 
			
		||||
        default_value =
 | 
			
		||||
          case type do
 | 
			
		||||
            :boolean -> false
 | 
			
		||||
            _other_type -> nil
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
        containers
 | 
			
		||||
        |> Enum.any?(fn container ->
 | 
			
		||||
          type in [:tags, :actions] or not (container |> Map.get(key) == default_value)
 | 
			
		||||
        end)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    rows =
 | 
			
		||||
      containers
 | 
			
		||||
      |> Enum.map(fn container -> container |> get_row_data_for_container(columns) end)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      containers: containers,
 | 
			
		||||
      columns: columns,
 | 
			
		||||
      rows: rows
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_row_data_for_container(Container.t(), [map()]) :: [map()]
 | 
			
		||||
  defp get_row_data_for_container(container, columns) do
 | 
			
		||||
    container = container |> Repo.preload([:ammo_groups, :tags])
 | 
			
		||||
 | 
			
		||||
    columns
 | 
			
		||||
    |> Enum.into(%{}, fn %{key: key} -> {key, get_value_for_key(key, container)} end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_value_for_key(atom(), Container.t()) :: any()
 | 
			
		||||
  defp get_value_for_key(:packs, container) do
 | 
			
		||||
    container |> Containers.get_container_ammo_group_count!()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:rounds, container) do
 | 
			
		||||
    container |> Containers.get_container_rounds!()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:tags, container) do
 | 
			
		||||
    assigns = %{container: container}
 | 
			
		||||
 | 
			
		||||
    {container.tags |> Enum.map(fn %{name: name} -> name end),
 | 
			
		||||
     ~H"""
 | 
			
		||||
     <div class="flex flex-wrap justify-center items-center">
 | 
			
		||||
       <%= unless @container.tags |> Enum.empty?() do %>
 | 
			
		||||
         <%= for tag <- @container.tags do %>
 | 
			
		||||
           <TagCard.simple_tag_card tag={tag} />
 | 
			
		||||
         <% end %>
 | 
			
		||||
       <% end %>
 | 
			
		||||
 | 
			
		||||
       <div class="mx-4 my-2">
 | 
			
		||||
         <.link
 | 
			
		||||
           patch={Routes.container_index_path(Endpoint, :edit_tags, @container)}
 | 
			
		||||
           class="text-primary-600 link"
 | 
			
		||||
         >
 | 
			
		||||
           <i class="fa-fw fa-lg fas fa-tags"></i>
 | 
			
		||||
         </.link>
 | 
			
		||||
       </div>
 | 
			
		||||
     </div>
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:actions, container) do
 | 
			
		||||
    assigns = %{container: container}
 | 
			
		||||
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <.link
 | 
			
		||||
      patch={Routes.container_index_path(Endpoint, :edit, @container)}
 | 
			
		||||
      class="text-primary-600 link"
 | 
			
		||||
      data-qa={"edit-#{@container.id}"}
 | 
			
		||||
    >
 | 
			
		||||
      <i class="fa-fw fa-lg fas fa-edit"></i>
 | 
			
		||||
    </.link>
 | 
			
		||||
 | 
			
		||||
    <.link
 | 
			
		||||
      href="#"
 | 
			
		||||
      class="text-primary-600 link"
 | 
			
		||||
      phx-click="delete"
 | 
			
		||||
      phx-value-id={@container.id}
 | 
			
		||||
      data-confirm={
 | 
			
		||||
        dgettext("prompts", "Are you sure you want to delete %{name}?", name: @container.name)
 | 
			
		||||
      }
 | 
			
		||||
      data-qa={"delete-#{@container.id}"}
 | 
			
		||||
    >
 | 
			
		||||
      <i class="fa-fw fa-lg fas fa-trash"></i>
 | 
			
		||||
    </.link>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(key, container), do: container |> Map.get(key)
 | 
			
		||||
 | 
			
		||||
  def return_to(true = _view_table), do: Routes.container_index_path(Endpoint, :table)
 | 
			
		||||
  def return_to(false = _view_table), do: Routes.container_index_path(Endpoint, :index)
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -16,62 +16,80 @@
 | 
			
		||||
    <.link patch={Routes.container_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
      <%= dgettext("actions", "New Container") %>
 | 
			
		||||
    </.link>
 | 
			
		||||
 | 
			
		||||
    <div class="flex flex-col justify-center items-center">
 | 
			
		||||
      <.toggle_button action="toggle_table" value={@view_table}>
 | 
			
		||||
        <span class="title text-lg text-primary-600">
 | 
			
		||||
          <%= gettext("View as table") %>
 | 
			
		||||
        </span>
 | 
			
		||||
      </.toggle_button>
 | 
			
		||||
    </div>
 | 
			
		||||
  <% end %>
 | 
			
		||||
 | 
			
		||||
  <div class="max-w-full flex flex-row flex-wrap justify-center items-center">
 | 
			
		||||
    <%= for container <- @containers do %>
 | 
			
		||||
      <.container_card container={container}>
 | 
			
		||||
        <:tag_actions>
 | 
			
		||||
          <div class="mx-4 my-2">
 | 
			
		||||
            <.link
 | 
			
		||||
              patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
            >
 | 
			
		||||
              <i class="fa-fw fa-lg fas fa-tags"></i>
 | 
			
		||||
            </.link>
 | 
			
		||||
          </div>
 | 
			
		||||
        </:tag_actions>
 | 
			
		||||
        <.link
 | 
			
		||||
          patch={Routes.container_index_path(Endpoint, :edit, container)}
 | 
			
		||||
          class="text-primary-600 link"
 | 
			
		||||
          data-qa={"edit-#{container.id}"}
 | 
			
		||||
        >
 | 
			
		||||
          <i class="fa-fw fa-lg fas fa-edit"></i>
 | 
			
		||||
        </.link>
 | 
			
		||||
    <%= if @view_table do %>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.TableComponent}
 | 
			
		||||
        id="containers_index_table"
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        columns={@columns}
 | 
			
		||||
        rows={@rows}
 | 
			
		||||
      />
 | 
			
		||||
    <% else %>
 | 
			
		||||
      <%= for container <- @containers do %>
 | 
			
		||||
        <.container_card container={container}>
 | 
			
		||||
          <:tag_actions>
 | 
			
		||||
            <div class="mx-4 my-2">
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
              >
 | 
			
		||||
                <i class="fa-fw fa-lg fas fa-tags"></i>
 | 
			
		||||
              </.link>
 | 
			
		||||
            </div>
 | 
			
		||||
          </:tag_actions>
 | 
			
		||||
          <.link
 | 
			
		||||
            patch={Routes.container_index_path(Endpoint, :edit, container)}
 | 
			
		||||
            class="text-primary-600 link"
 | 
			
		||||
            data-qa={"edit-#{container.id}"}
 | 
			
		||||
          >
 | 
			
		||||
            <i class="fa-fw fa-lg fas fa-edit"></i>
 | 
			
		||||
          </.link>
 | 
			
		||||
 | 
			
		||||
        <.link
 | 
			
		||||
          href="#"
 | 
			
		||||
          class="text-primary-600 link"
 | 
			
		||||
          phx-click="delete"
 | 
			
		||||
          phx-value-id={container.id}
 | 
			
		||||
          data-confirm={
 | 
			
		||||
            dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
 | 
			
		||||
          }
 | 
			
		||||
          data-qa={"delete-#{container.id}"}
 | 
			
		||||
        >
 | 
			
		||||
          <i class="fa-fw fa-lg fas fa-trash"></i>
 | 
			
		||||
        </.link>
 | 
			
		||||
      </.container_card>
 | 
			
		||||
          <.link
 | 
			
		||||
            href="#"
 | 
			
		||||
            class="text-primary-600 link"
 | 
			
		||||
            phx-click="delete"
 | 
			
		||||
            phx-value-id={container.id}
 | 
			
		||||
            data-confirm={
 | 
			
		||||
              dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
 | 
			
		||||
            }
 | 
			
		||||
            data-qa={"delete-#{container.id}"}
 | 
			
		||||
          >
 | 
			
		||||
            <i class="fa-fw fa-lg fas fa-trash"></i>
 | 
			
		||||
          </.link>
 | 
			
		||||
        </.container_card>
 | 
			
		||||
      <% end %>
 | 
			
		||||
    <% end %>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<%= if @live_action in [:new, :edit] do %>
 | 
			
		||||
  <.modal return_to={Routes.container_index_path(Endpoint, :index)}>
 | 
			
		||||
  <.modal return_to={return_to(@view_table)}>
 | 
			
		||||
    <.live_component
 | 
			
		||||
      module={CanneryWeb.ContainerLive.FormComponent}
 | 
			
		||||
      id={@container.id || :new}
 | 
			
		||||
      title={@page_title}
 | 
			
		||||
      action={@live_action}
 | 
			
		||||
      container={@container}
 | 
			
		||||
      return_to={Routes.container_index_path(Endpoint, :index)}
 | 
			
		||||
      return_to={return_to(@view_table)}
 | 
			
		||||
      current_user={@current_user}
 | 
			
		||||
    />
 | 
			
		||||
  </.modal>
 | 
			
		||||
<% end %>
 | 
			
		||||
 | 
			
		||||
<%= if @live_action == :edit_tags do %>
 | 
			
		||||
  <.modal return_to={Routes.container_index_path(Endpoint, :index)}>
 | 
			
		||||
  <.modal return_to={return_to(@view_table)}>
 | 
			
		||||
    <.live_component
 | 
			
		||||
      module={CanneryWeb.ContainerLive.EditTagsComponent}
 | 
			
		||||
      id={@container.id}
 | 
			
		||||
 
 | 
			
		||||
@@ -74,6 +74,7 @@ defmodule CanneryWeb.Router do
 | 
			
		||||
    live "/catalog/:id/show/edit", AmmoTypeLive.Show, :edit
 | 
			
		||||
 | 
			
		||||
    live "/containers", ContainerLive.Index, :index
 | 
			
		||||
    live "/containers/table", ContainerLive.Index, :table
 | 
			
		||||
    live "/containers/new", ContainerLive.Index, :new
 | 
			
		||||
    live "/containers/:id/edit", ContainerLive.Index, :edit
 | 
			
		||||
    live "/containers/:id/edit_tags", ContainerLive.Index, :edit_tags
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user