diff --git a/lib/music_library_web/components/search_components.ex b/lib/music_library_web/components/search_components.ex
index 3dcbc90d..e1b80a53 100644
--- a/lib/music_library_web/components/search_components.ex
+++ b/lib/music_library_web/components/search_components.ex
@@ -159,6 +159,45 @@ defmodule MusicLibraryWeb.SearchComponents do
"""
end
+ @doc """
+ Renders a search result item for navigation links.
+
+ ## Examples
+
+ <.search_result_navigation label="Collection" icon="hero-circle-stack" />
+ """
+ attr :label, :string, required: true
+ attr :icon, :string, required: true
+ attr :rest, :global, include: ~w(phx-click phx-value-path)
+
+ def search_result_navigation(assigns) do
+ ~H"""
+
+
+
+ <.icon name={@icon} class="h-4 w-4 text-zinc-500 dark:text-zinc-400" />
+
+
+
+ {gettext("Go to →")}
+
+
+
+ """
+ end
+
@doc """
Renders a search result group with a title and items.
diff --git a/lib/music_library_web/live/universal_search_live/index.ex b/lib/music_library_web/live/universal_search_live/index.ex
index e25bef60..41d2f859 100644
--- a/lib/music_library_web/live/universal_search_live/index.ex
+++ b/lib/music_library_web/live/universal_search_live/index.ex
@@ -39,6 +39,20 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
<.no_results :if={@search_query != "" and @total_results == 0} query={@search_query} />
0} class="max-h-148 md:max-h-164 overflow-y-auto mt-4">
+ <.search_result_group
+ :if={length(@navigation_links_results) > 0}
+ title={gettext("Go to")}
+ count={length(@navigation_links_results)}
+ >
+ <.search_result_navigation
+ :for={link <- @navigation_links_results}
+ label={link.label}
+ icon={link.icon}
+ phx-click="navigate_to_link"
+ phx-value-path={link.path}
+ phx-target={@myself}
+ />
+
<.search_result_group
:if={length(@search_results.artists) > 0}
title="Artists"
@@ -206,9 +220,11 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
query ->
search_results = Search.universal_search(query, limit: 5)
search_counts = Search.search_counts(query)
+ navigation_links_results = filter_navigation_links(query)
total_results =
- length(search_results.collection) +
+ length(navigation_links_results) +
+ length(search_results.collection) +
length(search_results.wishlist) +
length(search_results.artists) +
length(search_results.record_sets)
@@ -218,6 +234,7 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
|> assign(:search_query, query)
|> assign(:search_results, search_results)
|> assign(:search_counts, search_counts)
+ |> assign(:navigation_links_results, navigation_links_results)
|> assign(:total_results, total_results)}
end
end
@@ -293,6 +310,14 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
|> push_navigate(to: ~p"/record-sets?query=#{query}")}
end
+ @impl true
+ def handle_event("navigate_to_link", %{"path" => path}, socket) do
+ {:noreply,
+ socket
+ |> assign(:show_modal, false)
+ |> push_navigate(to: path)}
+ end
+
defp reset(socket) do
socket
|> assign(:search_query, "")
@@ -303,6 +328,87 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
artists_count: 0,
record_sets_count: 0
})
+ |> assign(:navigation_links_results, [])
|> assign(:total_results, 0)
end
+
+ defp filter_navigation_links(query) do
+ downcased = String.downcase(query)
+
+ Enum.filter(navigation_links(), fn link ->
+ String.contains?(String.downcase(link.label), downcased) or
+ Enum.any?(link.keywords, &String.contains?(&1, downcased))
+ end)
+ end
+
+ defp navigation_links do
+ [
+ %{
+ label: "Stats",
+ path: ~p"/",
+ icon: "hero-chart-pie",
+ keywords: ["stats", "dashboard", "home"]
+ },
+ %{
+ label: "Collection",
+ path: ~p"/collection",
+ icon: "hero-circle-stack",
+ keywords: ["collection", "collected", "records"]
+ },
+ %{
+ label: "Wishlist",
+ path: ~p"/wishlist",
+ icon: "hero-star",
+ keywords: ["wishlist", "wish", "want"]
+ },
+ %{
+ label: "Sets",
+ path: ~p"/record-sets",
+ icon: "hero-rectangle-stack",
+ keywords: ["sets", "record sets", "groups"]
+ },
+ %{
+ label: "Scrobble Anything",
+ path: ~p"/scrobble",
+ icon: "hero-play",
+ keywords: ["scrobble", "play"]
+ },
+ %{
+ label: "Scrobbled Tracks",
+ path: ~p"/scrobbled-tracks",
+ icon: "hero-musical-note",
+ keywords: ["scrobbled", "tracks", "history", "listening"]
+ },
+ %{
+ label: "Scrobble Rules",
+ path: ~p"/scrobble-rules",
+ icon: "hero-adjustments-horizontal",
+ keywords: ["scrobble rules", "rules", "remap"]
+ },
+ %{
+ label: "Online Store Templates",
+ path: ~p"/online-store-templates",
+ icon: "hero-building-storefront",
+ keywords: ["store", "templates", "online", "buy"]
+ },
+ %{
+ label: "Live Dashboard",
+ path: ~p"/dev/dashboard",
+ icon: "hero-chart-bar",
+ keywords: ["live dashboard", "telemetry", "metrics"]
+ },
+ %{
+ label: "Oban",
+ path: ~p"/dev/oban",
+ icon: "hero-cog",
+ keywords: ["oban", "jobs", "workers", "queue"]
+ },
+ %{
+ label: "Maintenance",
+ path: ~p"/dev/maintenance",
+ icon: "hero-wrench-screwdriver",
+ keywords: ["maintenance", "admin", "vacuum"]
+ }
+ ]
+ end
end
diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot
index 640e3859..be53a25b 100644
--- a/priv/gettext/default.pot
+++ b/priv/gettext/default.pot
@@ -2021,3 +2021,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Wikipedia"
msgstr ""
+
+#: lib/music_library_web/live/universal_search_live/index.ex
+#, elixir-autogen, elixir-format
+msgid "Go to"
+msgstr ""
+
+#: lib/music_library_web/components/search_components.ex
+#, elixir-autogen, elixir-format
+msgid "Go to →"
+msgstr ""
diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po
index 6aa1b64d..6639b769 100644
--- a/priv/gettext/en/LC_MESSAGES/default.po
+++ b/priv/gettext/en/LC_MESSAGES/default.po
@@ -2021,3 +2021,13 @@ msgstr ""
#, elixir-autogen, elixir-format, fuzzy
msgid "Wikipedia"
msgstr ""
+
+#: lib/music_library_web/live/universal_search_live/index.ex
+#, elixir-autogen, elixir-format
+msgid "Go to"
+msgstr ""
+
+#: lib/music_library_web/components/search_components.ex
+#, elixir-autogen, elixir-format
+msgid "Go to →"
+msgstr ""