defmodule BerrypodWeb.Admin.GSC do @moduledoc """ Google Search Console dashboard. Shows connection status, top queries, and top pages from GSC data. """ use BerrypodWeb, :live_view alias Berrypod.GSC.{Cache, Client, OAuth} alias Berrypod.Settings @impl true def mount(_params, _session, socket) do # Demo mode: set GSC_DEMO=1 to see the dashboard with sample data demo_mode = System.get_env("GSC_DEMO") == "1" connected = demo_mode or OAuth.connected?() site_url = if demo_mode, do: "https://example.com", else: Settings.get_setting("gsc_site_url") socket = socket |> assign(:page_title, "Search Console") |> assign(:connected, connected) |> assign(:site_url, site_url) |> assign(:sites, []) |> assign(:loading, false) |> assign(:error, nil) |> assign(:data, nil) |> assign(:demo_mode, demo_mode) socket = cond do demo_mode -> assign(socket, :data, demo_data()) connected && site_url -> load_data(socket) true -> socket end {:ok, socket} end defp demo_data do %{ top_queries: [ %{ keys: %{"query" => "wildflower tote bag"}, clicks: 145, impressions: 2340, ctr: 6.2, position: 3.2 }, %{ keys: %{"query" => "custom art prints"}, clicks: 98, impressions: 1890, ctr: 5.2, position: 4.1 }, %{ keys: %{"query" => "botanical poster"}, clicks: 76, impressions: 1456, ctr: 5.2, position: 5.8 }, %{ keys: %{"query" => "nature wall art"}, clicks: 54, impressions: 980, ctr: 5.5, position: 7.2 }, %{ keys: %{"query" => "meadow illustration"}, clicks: 32, impressions: 654, ctr: 4.9, position: 8.4 } ], top_pages: [ %{ keys: %{"page" => "https://example.com/products/wildflower-tote"}, clicks: 234, impressions: 4500, ctr: 5.2, position: 4.1 }, %{ keys: %{"page" => "https://example.com/collections/art-prints"}, clicks: 187, impressions: 3200, ctr: 5.8, position: 3.8 }, %{ keys: %{"page" => "https://example.com/"}, clicks: 156, impressions: 2800, ctr: 5.6, position: 2.1 }, %{ keys: %{"page" => "https://example.com/products/botanical-poster"}, clicks: 98, impressions: 1900, ctr: 5.2, position: 5.4 }, %{ keys: %{"page" => "https://example.com/about"}, clicks: 45, impressions: 890, ctr: 5.1, position: 6.2 } ], updated_at: DateTime.utc_now() } end @impl true def handle_event("select_site", %{"site_url" => site_url}, socket) do Settings.put_setting("gsc_site_url", site_url, "string") Cache.invalidate() socket = socket |> assign(:site_url, site_url) |> load_data() {:noreply, socket} end def handle_event("refresh_data", _params, socket) do Cache.invalidate() {:noreply, load_data(socket)} end def handle_event("load_sites", _params, socket) do case Client.list_sites() do {:ok, sites} -> {:noreply, assign(socket, :sites, sites)} {:error, reason} -> {:noreply, assign(socket, :error, "Failed to load sites: #{inspect(reason)}")} end end defp load_data(socket) do site_url = socket.assigns.site_url case Cache.get_all() do {:ok, data} -> assign(socket, :data, data) :miss -> fetch_fresh_data(socket, site_url) end end defp fetch_fresh_data(socket, site_url) do with {:ok, queries} <- Client.top_queries(site_url, row_limit: 25), {:ok, pages} <- Client.top_pages(site_url, row_limit: 25) do Cache.put_top_queries(queries) Cache.put_top_pages(pages) data = %{ top_queries: queries, top_pages: pages, updated_at: DateTime.utc_now() } assign(socket, :data, data) else {:error, :not_connected} -> assign(socket, :connected, false) {:error, reason} -> assign(socket, :error, "Failed to fetch data: #{inspect(reason)}") end end @impl true def render(assigns) do ~H""" <.header>Search Console
See how your site performs in Google search results
See your search performance data, top queries, and page rankings directly in your admin dashboard.
<%= if @configured do %> Connect with Google <% else %>
Set GSC_CLIENT_ID
and GSC_CLIENT_SECRET
environment variables to enable this feature.
What people search for to find your site
<.queries_table queries={@data.top_queries} />Your best performing pages in search
<.pages_table pages={@data.top_pages} />| Query | Clicks | Impr. | CTR | Pos. |
|---|---|---|---|---|
| No data yet | ||||
| {row.keys["query"]} | {row.clicks} | {format_number(row.impressions)} | {row.ctr}% | {row.position} |
| Page | Clicks | Impr. | CTR | Pos. |
|---|---|---|---|---|
| No data yet | ||||
| {format_page_url(row.keys["page"])} | {row.clicks} | {format_number(row.impressions)} | {row.ctr}% | {row.position} |
{@error}
<% else %>Loading data...
<% end %>Select a site to view its search performance data.