# LiveView Index Page Template Copy-pasteable template for a new list/index page following TowerOps conventions. Replace all `__RESOURCE__` placeholders with your resource name (e.g., `Schedule`, `Device`). Replace `__resource__` with the lowercase/snake_case form (e.g., `schedule`, `device`). Replace `__resources__` with the plural form (e.g., `schedules`, `devices`). Replace `__active_page__` with the sidebar page key (e.g., `"schedules"`). --- ## `lib/towerops_web/live/__resource___live/index.ex` ```elixir defmodule ToweropsWeb.__RESOURCE__Live.Index do @moduledoc false use ToweropsWeb, :live_view alias Towerops.__CONTEXT__ @impl true def mount(_params, _session, socket) do organization = socket.assigns.current_scope.organization {:ok, socket |> assign(:page_title, t("__RESOURCES_TITLE__")) |> assign(:filter, "all") |> assign(:timezone, socket.assigns.current_scope.timezone) |> load___resources__(organization.id)} end @impl true def handle_params(params, _url, socket) do filter = Map.get(params, "filter", "all") {:noreply, socket |> assign(:filter, filter) |> load___resources__(socket.assigns.current_scope.organization.id)} end @impl true def handle_event("delete", %{"id" => id}, socket) do organization = socket.assigns.current_scope.organization __resource__ = __CONTEXT__.get___resource__!(id) case __CONTEXT__.delete___resource__(__resource__) do {:ok, _} -> {:noreply, socket |> put_flash(:info, t("__RESOURCE_TITLE__ deleted")) |> load___resources__(organization.id)} {:error, _changeset} -> {:noreply, put_flash(socket, :error, t("Unable to delete __resource__"))} end end defp load___resources__(socket, org_id) do filter_atom = case socket.assigns.filter do "active" -> :active "archived" -> :archived _ -> nil end opts = if filter_atom, do: [filter: filter_atom], else: [] __resources__ = __CONTEXT__.list___resources__(org_id, opts) assign(socket, :__resources__, __resources__) end end ``` --- ## `lib/towerops_web/live/__resource___live/index.html.heex` ```heex <%!-- Page header --%>

{t("__RESOURCES_TITLE__")}

<.link navigate={~p"/__resources__/new"} class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-500 transition-colors" > <.icon name="hero-plus" class="h-4 w-4" /> {t("New __RESOURCE_TITLE__")}

{t("Description of what this page shows.")}

<%!-- Filter tabs --%>
<.link patch={~p"/__resources__?filter=all"} class={[ "inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium transition-colors", @filter == "all" && "bg-gray-900 text-white dark:bg-white dark:text-gray-900", @filter != "all" && "bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700" ]} > {t("All")} <.link patch={~p"/__resources__?filter=active"} class={[ "inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium transition-colors", @filter == "active" && "bg-gray-900 text-white dark:bg-white dark:text-gray-900", @filter != "active" && "bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700" ]} > {t("Active")} <.link patch={~p"/__resources__?filter=archived"} class={[ "inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium transition-colors", @filter == "archived" && "bg-gray-900 text-white dark:bg-white dark:text-gray-900", @filter != "archived" && "bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700" ]} > {t("Archived")}
<%!-- Empty state --%> <%= if Enum.empty?(@__resources__) do %>
<.icon name="hero-rectangle-stack" class="h-12 w-12 text-gray-300 dark:text-gray-600 mx-auto mb-4" />

{t("No __resources__ found")}

{t("Get started by creating your first __resource__.")}

<% else %> <%!-- Data table --%>
<%= for item <- @__resources__ do %> <% end %>
{t("Name")} {t("Status")} {t("Created")} {t("Actions")}
{item.name} Active {Calendar.strftime(item.inserted_at, "%b %d, %Y")}
<.link navigate={~p"/__resources__/#{item.id}/edit"} class="text-xs px-2 py-1 rounded bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 transition-colors" > {t("Edit")}
<% end %>
``` --- ## Router entry ```elixir # In lib/towerops_web/router.ex, inside the authenticated scope: live "/__resources__", __RESOURCE__Live.Index, :index ```