Search scrobble rules, improved layout

This commit is contained in:
Claudio Ortolina
2026-03-16 07:44:45 +00:00
parent c4b2f00996
commit 43b054b34a
3 changed files with 82 additions and 68 deletions
+42 -45
View File
@@ -14,6 +14,7 @@ defmodule MusicLibrary.ScrobbleRules do
@type list_opts :: [
type: atom(),
enabled: boolean(),
query: String.t(),
offset: non_neg_integer(),
limit: non_neg_integer()
]
@@ -21,28 +22,8 @@ defmodule MusicLibrary.ScrobbleRules do
@spec list_scrobble_rules(list_opts()) :: [ScrobbleRule.t()]
def list_scrobble_rules(opts \\ []) do
query =
from r in ScrobbleRule,
order_by: [desc: r.inserted_at]
query =
case Keyword.get(opts, :type) do
nil ->
query
type ->
from r in query,
where: r.type == ^type
end
query =
case Keyword.get(opts, :enabled) do
nil ->
query
enabled ->
from r in query,
where: r.enabled == ^enabled
end
from(r in ScrobbleRule, order_by: [desc: r.inserted_at])
|> filter_scrobble_rules(opts)
query =
case Keyword.get(opts, :offset) do
@@ -69,29 +50,9 @@ defmodule MusicLibrary.ScrobbleRules do
@spec count_scrobble_rules(list_opts()) :: non_neg_integer()
def count_scrobble_rules(opts \\ []) do
query = from(r in ScrobbleRule)
query =
case Keyword.get(opts, :type) do
nil ->
query
type ->
from r in query,
where: r.type == ^type
end
query =
case Keyword.get(opts, :enabled) do
nil ->
query
enabled ->
from r in query,
where: r.enabled == ^enabled
end
Repo.aggregate(query, :count)
from(r in ScrobbleRule)
|> filter_scrobble_rules(opts)
|> Repo.aggregate(:count)
end
@spec get_scrobble_rule!(integer()) :: ScrobbleRule.t()
@@ -497,6 +458,42 @@ defmodule MusicLibrary.ScrobbleRules do
end)
end
defp filter_scrobble_rules(query, opts) do
query =
case Keyword.get(opts, :type) do
nil ->
query
type ->
from r in query,
where: r.type == ^type
end
query =
case Keyword.get(opts, :enabled) do
nil ->
query
enabled ->
from r in query,
where: r.enabled == ^enabled
end
case Keyword.get(opts, :query) do
q when q in [nil, ""] ->
query
q ->
like = "%#{q}%"
from r in query,
where:
like(r.match_value, ^like) or
like(r.target_musicbrainz_id, ^like) or
like(r.description, ^like)
end
end
# column and json_path are hardcoded string literals from internal callers,
# never user input. All user-derived values use parameterized ? placeholders.
# sobelow_skip ["SQL.Query"]
@@ -9,7 +9,7 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
@default_list_params %{
page: 1,
page_size: 20,
page_size: 50,
query: "",
order: :inserted_at
}
@@ -48,6 +48,8 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
</div>
</header>
<.search_form query={@list_params.query} />
<div class="mt-6 space-y-4">
<ul phx-update="stream" id="scrobble-rules-list" class="space-y-4">
<li
@@ -63,31 +65,32 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
<li
:for={{dom_id, scrobble_rule} <- @streams.scrobble_rules}
id={dom_id}
class="flex justify-between"
class="flex items-center gap-2"
>
<div class="flex items-center">
<.type_badge type={scrobble_rule.type} />
</div>
<div class="grow p-2">
<p class="text-sm text-zinc-900 dark:text-zinc-100">
{scrobble_rule.match_value}
</p>
<p class="text-xs font-mono text-zinc-500 dark:text-zinc-400 mt-2">
{scrobble_rule.target_musicbrainz_id}
</p>
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-2">
<div class="grow min-w-0">
<div class="lg:flex lg:items-center lg:gap-2">
<p class="text-sm text-zinc-900 dark:text-zinc-100">
{scrobble_rule.match_value}
</p>
<p class="text-xs font-mono text-zinc-500 dark:text-zinc-400 mt-1 lg:mt-0 truncate">
{scrobble_rule.target_musicbrainz_id}
</p>
</div>
<p
:if={scrobble_rule.description}
class="text-xs text-zinc-500 dark:text-zinc-400 mt-1 truncate"
>
{scrobble_rule.description}
</p>
</div>
<div class="flex flex-col justify-center text-right">
<span>
<.status_badge enabled={scrobble_rule.enabled} />
</span>
<span class="text-xs text-zinc-500 dark:text-zinc-400 text-nowrap mt-2">
<div class="flex flex-col lg:flex-row items-end lg:items-center gap-1 lg:gap-2 shrink-0">
<.type_badge type={scrobble_rule.type} />
<.status_badge enabled={scrobble_rule.enabled} />
<span class="text-xs text-zinc-500 dark:text-zinc-400 text-nowrap">
{Calendar.strftime(scrobble_rule.inserted_at, "%Y-%m-%d")}
</span>
</div>
<div class="flex items-center p-2">
<div class="flex items-center shrink-0">
<.dropdown id={"actions-#{scrobble_rule.id}"} placement="bottom-end">
<:toggle>
<.button variant="ghost">
@@ -109,7 +112,7 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
<.dropdown_link
id={"actions-#{scrobble_rule.id}-edit"}
patch={
~p"/scrobble-rules/#{scrobble_rule}/edit?#{Map.take(@list_params, [:page, :page_size])}"
~p"/scrobble-rules/#{scrobble_rule}/edit?#{@list_params |> Map.take([:page, :page_size, :query]) |> Enum.filter(fn {_, v} -> v not in ["", nil] end)}"
}
>
{gettext("Edit")}
@@ -176,10 +179,13 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
end
defp apply_action(socket, :index, params) do
total_rules = ScrobbleRules.count_scrobble_rules()
query = params["query"]
total_rules = ScrobbleRules.count_scrobble_rules(query: query)
list_params =
@default_list_params
|> merge_query(query)
|> merge_pagination(params, total_rules)
load_and_assign_rules(socket, list_params)
@@ -190,6 +196,7 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
rules =
ScrobbleRules.list_scrobble_rules(
query: list_params.query,
offset: offset,
limit: list_params.page_size
)
@@ -204,7 +211,8 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
def back_path(list_params) do
qs =
list_params
|> Map.take([:page, :page_size])
|> Map.take([:page, :page_size, :query])
|> Enum.filter(fn {_, v} -> v not in ["", nil] end)
~p"/scrobble-rules?#{qs}"
end
@@ -283,6 +291,15 @@ defmodule MusicLibraryWeb.ScrobbleRulesLive.Index do
{:noreply, put_toast(socket, :info, message)}
end
def handle_event("search", %{"query" => query}, socket) do
qs =
@default_list_params
|> Map.put(:query, query)
|> Map.take([:query, :page, :page_size])
{:noreply, push_patch(socket, to: ~p"/scrobble-rules?#{qs}")}
end
attr :type, :atom, required: true, values: [:album, :artist]
defp type_badge(assigns) do
@@ -64,7 +64,7 @@ defmodule MusicLibraryWeb.ScrobbleRulesLiveTest do
|> render_click() =~
"Edit Scrobble Rule"
assert_patch(index_live, ~p"/scrobble-rules/#{scrobble_rule}/edit?page=1&page_size=20")
assert_patch(index_live, ~p"/scrobble-rules/#{scrobble_rule}/edit?page=1&page_size=50")
assert index_live
|> form("#scrobble_rule-form", scrobble_rule: @invalid_attrs)