polling
All checks were successful
Build and Deploy / Build & Push Image (push) Successful in 36s

This commit is contained in:
2026-03-16 19:51:21 -06:00
parent 34ee245543
commit cbeecb1021

View File

@@ -3,6 +3,8 @@ defmodule CobblemonUiWeb.DashboardLive do
@impl true @impl true
def mount(_params, _session, socket) do def mount(_params, _session, socket) do
if connected?(socket), do: :timer.send_interval(1000, self(), :tick)
players = players =
case CobblemonUi.CobblemonFS.list_players() do case CobblemonUi.CobblemonFS.list_players() do
{:ok, list} -> list {:ok, list} -> list
@@ -86,6 +88,15 @@ defmodule CobblemonUiWeb.DashboardLive do
end end
def handle_event("refresh", _params, socket) do def handle_event("refresh", _params, socket) do
{:noreply, do_refresh(socket)}
end
@impl true
def handle_info(:tick, socket) do
{:noreply, do_refresh(socket)}
end
defp do_refresh(socket) do
players = players =
case CobblemonUi.CobblemonFS.list_players() do case CobblemonUi.CobblemonFS.list_players() do
{:ok, list} -> list {:ok, list} -> list
@@ -103,7 +114,7 @@ defmodule CobblemonUiWeb.DashboardLive do
socket socket
end end
{:noreply, assign(socket, players: players)} assign(socket, players: players)
end end
@impl true @impl true
@@ -111,8 +122,10 @@ defmodule CobblemonUiWeb.DashboardLive do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash}>
<div class="min-h-screen -mx-4 -my-20 sm:-mx-6 lg:-mx-8"> <div class="min-h-screen -mx-4 -my-20 sm:-mx-6 lg:-mx-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<%!-- Header --%>
<%!-- Player picker --%>
<div :if={is_nil(@selected_player)}>
<div class="flex items-center justify-between mb-8"> <div class="flex items-center justify-between mb-8">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-primary/20 flex items-center justify-center"> <div class="w-10 h-10 rounded-xl bg-primary/20 flex items-center justify-center">
@@ -120,10 +133,9 @@ defmodule CobblemonUiWeb.DashboardLive do
</div> </div>
<div> <div>
<h1 class="text-2xl font-bold tracking-tight text-base-content">Cobblemon</h1> <h1 class="text-2xl font-bold tracking-tight text-base-content">Cobblemon</h1>
<p class="text-sm text-base-content/50">Player Data Explorer</p> <p class="text-sm text-base-content/50">Who are you?</p>
</div> </div>
</div> </div>
<div class="flex items-center gap-2">
<button <button
id="refresh-btn" id="refresh-btn"
phx-click="refresh" phx-click="refresh"
@@ -132,54 +144,62 @@ defmodule CobblemonUiWeb.DashboardLive do
<.icon name="hero-arrow-path" class="size-4" /> Refresh <.icon name="hero-arrow-path" class="size-4" /> Refresh
</button> </button>
</div> </div>
</div>
<div class="flex flex-col lg:flex-row gap-6">
<%!-- Sidebar: Player List --%>
<aside class="w-full lg:w-72 shrink-0">
<div class="rounded-xl border border-base-300/50 bg-base-200/30 backdrop-blur-sm">
<div class="px-4 py-3 border-b border-base-300/30">
<h2 class="text-sm font-semibold text-base-content/70 uppercase tracking-wider">
Players
</h2>
<p class="text-xs text-base-content/40 mt-0.5">
{length(@players)} found
</p>
</div>
<div class="p-2 max-h-[60vh] overflow-y-auto">
<div <div
:if={@players == []} :if={@players == []}
class="px-3 py-8 text-center text-sm text-base-content/40" class="rounded-xl border border-base-300/30 bg-base-200/20 px-8 py-20 text-center"
> >
<.icon name="hero-user-group" class="size-8 mx-auto mb-2 opacity-30" /> <.icon name="hero-user-group" class="size-10 mx-auto mb-4 text-base-content/20" />
<p>No players found</p> <p class="text-base-content/40 text-lg">No players found</p>
<p class="text-xs mt-1">Check data directory</p> <p class="text-base-content/25 text-sm mt-1">Check the data directory</p>
</div> </div>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-3">
<.link <.link
:for={player <- @players} :for={player <- @players}
patch={~p"/player/#{player.uuid}"} patch={~p"/player/#{player.uuid}"}
class={[ id={"player-#{player.uuid}"}
"block px-3 py-2.5 rounded-lg transition-all duration-150 mb-1", class="group flex items-center gap-3 rounded-xl border border-base-300/40 bg-base-200/20 px-5 py-4 hover:bg-base-200/50 hover:border-primary/30 hover:shadow-md hover:shadow-primary/5 transition-all duration-150"
if(@selected_player == player.uuid,
do:
"bg-primary/15 text-primary border border-primary/20 shadow-sm shadow-primary/5",
else: "text-base-content/60 hover:bg-base-300/40 hover:text-base-content/80"
)
]}
> >
<span class="text-sm font-medium block truncate"> <div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0 group-hover:bg-primary/20 transition-colors">
<.icon name="hero-user" class="size-5 text-primary/70" />
</div>
<span class="text-base font-semibold text-base-content/80 group-hover:text-base-content truncate">
{player.name || "Unknown"} {player.name || "Unknown"}
</span> </span>
<span class="text-[10px] font-mono text-base-content/30 block truncate"> <.icon name="hero-chevron-right" class="size-4 text-base-content/20 ml-auto shrink-0 group-hover:text-primary/40 transition-colors" />
{player.uuid}
</span>
</.link> </.link>
</div> </div>
</div> </div>
</aside>
<%!-- Main Content --%> <%!-- Player view --%>
<main class="flex-1 min-w-0"> <div :if={@selected_player}>
<%!-- Header --%>
<div class="flex items-center justify-between mb-8">
<div class="flex items-center gap-3">
<.link
patch={~p"/"}
id="back-to-players"
class="w-10 h-10 rounded-xl bg-base-200/50 hover:bg-base-200 flex items-center justify-center transition-colors"
>
<.icon name="hero-arrow-left" class="size-5 text-base-content/50" />
</.link>
<div>
<h1 class="text-2xl font-bold tracking-tight text-base-content">
{player_name(@players, @selected_player)}
</h1>
<p class="text-sm text-base-content/50">Player Data Explorer</p>
</div>
</div>
<button
id="refresh-btn"
phx-click="refresh"
class="btn btn-ghost btn-sm gap-2 hover:bg-base-300/50 transition-colors"
>
<.icon name="hero-arrow-path" class="size-4" /> Refresh
</button>
</div>
<div <div
:if={@error} :if={@error}
class="rounded-xl border border-error/30 bg-error/10 px-5 py-4 mb-6" class="rounded-xl border border-error/30 bg-error/10 px-5 py-4 mb-6"
@@ -190,38 +210,9 @@ defmodule CobblemonUiWeb.DashboardLive do
</div> </div>
</div> </div>
<%!-- Empty state --%>
<div
:if={is_nil(@selected_player)}
class="rounded-xl border border-base-300/30 bg-base-200/20 px-8 py-20 text-center"
>
<.icon
name="hero-arrow-left"
class="size-10 mx-auto mb-4 text-base-content/20"
/>
<p class="text-base-content/40 text-lg">Select a player to explore</p>
<p class="text-base-content/25 text-sm mt-1">
Choose from the sidebar to view their Pokémon
</p>
</div>
<%!-- Player data --%>
<div :if={@player_data}> <div :if={@player_data}>
<%!-- Player header --%> <%!-- Stats bar --%>
<div class="rounded-xl border border-base-300/40 bg-base-200/30 px-5 py-4 mb-6"> <div class="flex items-center gap-4 text-sm text-base-content/50 mb-6">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
<.icon name="hero-user" class="size-5 text-primary/70" />
</div>
<div class="min-w-0">
<p class="text-base font-semibold text-base-content/90">
{player_name(@players, @player_data.uuid)}
</p>
<p class="text-xs font-mono text-base-content/40 truncate">
{@player_data.uuid}
</p>
</div>
<div class="ml-auto flex items-center gap-3 text-sm text-base-content/50">
<div class="flex items-center gap-1.5"> <div class="flex items-center gap-1.5">
<div class="w-2 h-2 rounded-full bg-success/60" /> <div class="w-2 h-2 rounded-full bg-success/60" />
<span>{party_count(@player_data)} in party</span> <span>{party_count(@player_data)} in party</span>
@@ -231,8 +222,6 @@ defmodule CobblemonUiWeb.DashboardLive do
<span>{pc_count(@player_data)} in PC</span> <span>{pc_count(@player_data)} in PC</span>
</div> </div>
</div> </div>
</div>
</div>
<%!-- Active battle --%> <%!-- Active battle --%>
<.battle_panel :if={@battle} battle={@battle} player_id={@selected_player} /> <.battle_panel :if={@battle} battle={@battle} player_id={@selected_player} />
@@ -305,8 +294,8 @@ defmodule CobblemonUiWeb.DashboardLive do
<%!-- Pokémon detail panel --%> <%!-- Pokémon detail panel --%>
<.pokemon_detail :if={@selected_pokemon} pokemon={@selected_pokemon} /> <.pokemon_detail :if={@selected_pokemon} pokemon={@selected_pokemon} />
</div> </div>
</main>
</div> </div>
</div> </div>
</div> </div>
</Layouts.app> </Layouts.app>
@@ -559,7 +548,6 @@ defmodule CobblemonUiWeb.DashboardLive do
<div class="px-5 py-3 border-b border-error/20 flex items-center gap-2"> <div class="px-5 py-3 border-b border-error/20 flex items-center gap-2">
<span class="w-2 h-2 rounded-full bg-error animate-pulse inline-block"></span> <span class="w-2 h-2 rounded-full bg-error animate-pulse inline-block"></span>
<span class="text-sm font-semibold text-error">Active Battle</span> <span class="text-sm font-semibold text-error">Active Battle</span>
<span class="ml-auto text-[10px] font-mono text-base-content/30">{@battle.battle_id}</span>
</div> </div>
<div class="p-4 grid grid-cols-1 md:grid-cols-2 gap-3 relative"> <div class="p-4 grid grid-cols-1 md:grid-cols-2 gap-3 relative">
<div class="hidden md:flex absolute inset-y-0 left-1/2 -translate-x-1/2 items-center justify-center z-10 pointer-events-none"> <div class="hidden md:flex absolute inset-y-0 left-1/2 -translate-x-1/2 items-center justify-center z-10 pointer-events-none">