defmodule AiDebugWeb.DashboardLive do use AiDebugWeb, :live_view alias AiDebug.{SessionManager, BrowserPool} alias Phoenix.PubSub @impl true def render(assigns) do ~H"""

AI Debug Dashboard

<%= if @error do %>
<%= @error %>
<% end %> <%= if @success do %>
<%= @success %>
<% end %>

Quick Actions

<.link navigate="/validate" class="text-center px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"> URL Validator <.link navigate="/tests" class="text-center px-4 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700"> Test Runner <.link navigate="/debug_demo" class="text-center px-4 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700"> Debug Demo <.link navigate="/dev/dashboard" class="text-center px-4 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700"> Live Dashboard

Create New Debug Session

<.form for={@form} phx-submit="create_session" id="new-session-form">
<.input field={@form[:url]} type="text" placeholder="http://localhost:3000" class="flex-1" /> <.button type="submit">Start Session

Active Sessions

<%= for session <- filter_sessions(@sessions, @search) do %>
<.link navigate={"/session/#{session.id}"} class="font-medium text-blue-600 hover:text-blue-500"> <%= session.id %>

<%= session.url %>

Started <%= format_time(session.started_at) %>

<%= session.event_count %> events <%= if session.type == "debug" do %> Debug Mode <% end %>
<% end %>

Browser Pool

Available Browsers
<%= @browser_stats.available %>
Active Browsers
<%= @browser_stats.active %>

Performance Metrics

Session Creation Time
<%= @metrics.session_creation_time %> ms
Browser Checkout Time
<%= @metrics.browser_checkout_time %> ms
Active Workers
<%= @metrics.active_workers %>
<%= if @running_tests != [] do %>

Running Tests

<%= for test <- @running_tests do %>

<%= test.name %>

<%= test.completed %> / <%= test.total %> tests completed

Running
<% end %>
<% end %>
""" end @impl true def mount(_params, _session, socket) do if connected?(socket) do # Subscribe to updates PubSub.subscribe(AiDebug.PubSub, "sessions") PubSub.subscribe(AiDebug.PubSub, "browser_pool") PubSub.subscribe(AiDebug.PubSub, "test_runner") # Schedule periodic updates :timer.send_interval(5000, self(), :update_metrics) end {:ok, socket |> assign(:form, to_form(%{"url" => ""})) |> assign(:sessions, list_sessions()) |> assign(:browser_stats, get_browser_stats()) |> assign(:metrics, get_metrics()) |> assign(:running_tests, []) |> assign(:search, "") |> assign(:error, nil) |> assign(:success, nil)} end @impl true def handle_event("create_session", %{"url" => url}, socket) do case SessionManager.create_session(url) do {:ok, _session_info} -> {:noreply, socket |> assign(:success, "Session created successfully") |> assign(:sessions, list_sessions()) |> clear_alerts()} {:error, reason} -> {:noreply, socket |> assign(:error, "Failed to create session: #{inspect(reason)}") |> clear_alerts()} end end @impl true def handle_event("stop_session", %{"id" => session_id}, socket) do SessionManager.close_session(session_id) {:noreply, assign(socket, :sessions, list_sessions())} end @impl true def handle_event("search_sessions", %{"search" => search}, socket) do {:noreply, assign(socket, :search, search)} end @impl true def handle_info({:session_created, session}, socket) do {:noreply, assign(socket, :sessions, [session | socket.assigns.sessions])} end @impl true def handle_info({:session_stopped, session_id}, socket) do sessions = Enum.reject(socket.assigns.sessions, &(&1.id == session_id)) {:noreply, assign(socket, :sessions, sessions)} end @impl true def handle_info({:browser_pool_update, stats}, socket) do {:noreply, assign(socket, :browser_stats, stats)} end @impl true def handle_info({:test_update, test_info}, socket) do running_tests = update_test_list(socket.assigns.running_tests, test_info) {:noreply, assign(socket, :running_tests, running_tests)} end @impl true def handle_info(:update_metrics, socket) do {:noreply, assign(socket, :metrics, get_metrics())} end @impl true def handle_info({:error, message}, socket) do {:noreply, socket |> assign(:error, message) |> clear_alerts()} end @impl true def handle_info(:clear_alerts, socket) do {:noreply, socket |> assign(:error, nil) |> assign(:success, nil)} end defp list_sessions do SessionManager.list_sessions() |> Enum.map(fn session_info -> case session_info do session_id when is_binary(session_id) -> {:ok, session} = SessionManager.get_session(session_id) session session_map when is_map(session_map) -> session_map end end) end defp filter_sessions(sessions, ""), do: sessions defp filter_sessions(sessions, search) do search_lower = String.downcase(search) Enum.filter(sessions, fn session -> String.contains?(String.downcase(session.url), search_lower) || String.contains?(String.downcase(session.id), search_lower) end) end defp get_browser_stats do %{ available: BrowserPool.available_count(), active: BrowserPool.active_count() } end defp get_metrics do %{ session_creation_time: 8, # Mock for now browser_checkout_time: 45, # Mock for now active_workers: 4 # Mock for now } end defp update_test_list(tests, test_info) do # Update or add test info case Enum.find_index(tests, &(&1.id == test_info.id)) do nil -> [test_info | tests] index -> List.replace_at(tests, index, test_info) end |> Enum.reject(&(&1.status == :completed)) end defp format_time(datetime) do # Simple time formatting "#{datetime.hour}:#{String.pad_leading(to_string(datetime.minute), 2, "0")}" end defp clear_alerts(socket) do Process.send_after(self(), :clear_alerts, 5000) socket end end