mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-11 07:24:30 +08:00
Redesign runtime panel and sessions list (#697)
* Redesign runtimes panel * Redesign session list * Add session sort * Move session list to a separate component * Up
This commit is contained in:
parent
54511d5ffc
commit
d81965ee99
5 changed files with 172 additions and 100 deletions
|
@ -250,12 +250,14 @@ defmodule LivebookWeb.Helpers do
|
|||
<.labeled_text label="Name" text="Sherlock Holmes" />
|
||||
"""
|
||||
def labeled_text(assigns) do
|
||||
assigns = assign_new(assigns, :one_line, fn -> false end)
|
||||
|
||||
~H"""
|
||||
<div class="flex flex-col space-y-1">
|
||||
<span class="text-xs text-gray-500">
|
||||
<%= @label %>
|
||||
</span>
|
||||
<span class="text-gray-800 text-sm font-semibold">
|
||||
<span class={"text-gray-800 text-sm font-semibold #{if @one_line, do: "whitespace-nowrap overflow-auto tiny-scrollbar"}"}>
|
||||
<%= @text %>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,7 @@ defmodule LivebookWeb.HomeLive do
|
|||
Phoenix.PubSub.subscribe(Livebook.PubSub, "tracker_sessions")
|
||||
end
|
||||
|
||||
sessions = sort_sessions(Sessions.list_sessions())
|
||||
sessions = Sessions.list_sessions()
|
||||
notebook_infos = Notebook.Explore.visible_notebook_infos() |> Enum.take(3)
|
||||
|
||||
{:ok,
|
||||
|
@ -87,7 +87,7 @@ defmodule LivebookWeb.HomeLive do
|
|||
|
||||
<div class="py-12">
|
||||
<div class="mb-4 flex justify-between items-center">
|
||||
<h2 class="text-xl font-semibold text-gray-800">
|
||||
<h2 class="uppercase font-semibold text-gray-500">
|
||||
Explore
|
||||
</h2>
|
||||
<%= live_redirect to: Routes.explore_path(@socket, :page),
|
||||
|
@ -104,12 +104,10 @@ defmodule LivebookWeb.HomeLive do
|
|||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="py-12">
|
||||
<h2 class="mb-4 text-xl font-semibold text-gray-800">
|
||||
Running sessions
|
||||
</h2>
|
||||
<.sessions_list sessions={@sessions} socket={@socket} />
|
||||
<.live_component module={LivebookWeb.HomeLive.SessionListComponent}
|
||||
id="session-list"
|
||||
sessions={@sessions} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -149,68 +147,6 @@ defmodule LivebookWeb.HomeLive do
|
|||
end
|
||||
end
|
||||
|
||||
defp sessions_list(%{sessions: []} = assigns) do
|
||||
~H"""
|
||||
<div class="p-5 flex space-x-4 items-center border border-gray-200 rounded-lg">
|
||||
<div>
|
||||
<.remix_icon icon="windy-line" class="text-gray-400 text-xl" />
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
You do not have any running sessions.
|
||||
<br>
|
||||
Please create a new one by clicking <span class="font-semibold">“New notebook”</span>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp sessions_list(assigns) do
|
||||
~H"""
|
||||
<div class="flex flex-col space-y-4">
|
||||
<%= for session <- @sessions do %>
|
||||
<div class="p-5 flex items-center border border-gray-200 rounded-lg"
|
||||
data-test-session-id={session.id}>
|
||||
<div class="flex-grow flex flex-col space-y-1">
|
||||
<%= live_redirect session.notebook_name,
|
||||
to: Routes.session_path(@socket, :page, session.id),
|
||||
class: "font-semibold text-gray-800 hover:text-gray-900" %>
|
||||
<div class="text-gray-600 text-sm">
|
||||
<%= if session.file, do: session.file.path, else: "No file" %>
|
||||
</div>
|
||||
<div class="text-gray-600 text-sm">
|
||||
Created <%= format_creation_date(session.created_at) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative" id={"session-#{session.id}-menu"} phx-hook="Menu" data-element="menu">
|
||||
<button class="icon-button" data-toggle>
|
||||
<.remix_icon icon="more-2-fill" class="text-xl" />
|
||||
</button>
|
||||
<div class="menu" data-content>
|
||||
<button class="menu__item text-gray-500"
|
||||
phx-click="fork_session"
|
||||
phx-value-id={session.id}>
|
||||
<.remix_icon icon="git-branch-line" />
|
||||
<span class="font-medium">Fork</span>
|
||||
</button>
|
||||
<a class="menu__item text-gray-500"
|
||||
href={live_dashboard_process_path(@socket, session.pid)}
|
||||
target="_blank">
|
||||
<.remix_icon icon="dashboard-2-line" />
|
||||
<span class="font-medium">See on Dashboard</span>
|
||||
</a>
|
||||
<%= live_patch to: Routes.home_path(@socket, :close_session, session.id),
|
||||
class: "menu__item text-red-600" do %>
|
||||
<.remix_icon icon="close-circle-line" />
|
||||
<span class="font-medium">Close</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_params(%{"session_id" => session_id}, _url, socket) do
|
||||
session = Enum.find(socket.assigns.sessions, &(&1.id == session_id))
|
||||
|
@ -323,8 +259,7 @@ defmodule LivebookWeb.HomeLive do
|
|||
if session in socket.assigns.sessions do
|
||||
{:noreply, socket}
|
||||
else
|
||||
sessions = sort_sessions([session | socket.assigns.sessions])
|
||||
{:noreply, assign(socket, sessions: sessions)}
|
||||
{:noreply, assign(socket, sessions: [session | socket.assigns.sessions])}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -349,10 +284,6 @@ defmodule LivebookWeb.HomeLive do
|
|||
|
||||
def handle_info(_message, socket), do: {:noreply, socket}
|
||||
|
||||
defp sort_sessions(sessions) do
|
||||
Enum.sort_by(sessions, & &1.created_at, {:desc, DateTime})
|
||||
end
|
||||
|
||||
defp files(sessions) do
|
||||
Enum.map(sessions, & &1.file)
|
||||
end
|
||||
|
@ -390,11 +321,6 @@ defmodule LivebookWeb.HomeLive do
|
|||
session.id
|
||||
end
|
||||
|
||||
def format_creation_date(created_at) do
|
||||
time_words = created_at |> DateTime.to_naive() |> Livebook.Utils.Time.time_ago_in_words()
|
||||
time_words <> " ago"
|
||||
end
|
||||
|
||||
defp import_content(socket, content, session_opts) do
|
||||
{notebook, messages} = Livebook.LiveMarkdown.Import.notebook_from_markdown(content)
|
||||
|
||||
|
|
140
lib/livebook_web/live/home_live/session_list_component.ex
Normal file
140
lib/livebook_web/live/home_live/session_list_component.ex
Normal file
|
@ -0,0 +1,140 @@
|
|||
defmodule LivebookWeb.HomeLive.SessionListComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
@impl true
|
||||
def mount(socket) do
|
||||
{:ok, assign(socket, order_by: "date")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(assigns, socket) do
|
||||
{sessions, assigns} = Map.pop!(assigns, :sessions)
|
||||
|
||||
sessions = sort_sessions(sessions, socket.assigns.order_by)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign(:sessions, sessions)
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="mb-4 uppercase font-semibold text-gray-500">
|
||||
Running sessions (<%= length(@sessions) %>)
|
||||
</h2>
|
||||
<div class="relative" id={"sessions-order-menu"} phx-hook="Menu" data-element="menu">
|
||||
<button class="button button-outlined-gray px-4 py-1" data-toggle>
|
||||
<span><%= order_by_label(@order_by) %></span>
|
||||
<.remix_icon icon="arrow-down-s-line" class="text-lg leading-none align-middle ml-1" />
|
||||
</button>
|
||||
<div class="menu" data-content>
|
||||
<%= for order_by <- ["date", "title"] do %>
|
||||
<button class={"menu__item #{if order_by == @order_by, do: "text-gray-900", else: "text-gray-500"}"}
|
||||
phx-click={JS.push("set_order", value: %{order_by: order_by}, target: @myself)}>
|
||||
<.remix_icon icon={order_by_icon(order_by)} />
|
||||
<span class="font-medium"><%= order_by_label(order_by) %></span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<.session_list sessions={@sessions} socket={@socket} />
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp session_list(%{sessions: []} = assigns) do
|
||||
~H"""
|
||||
<div class="p-5 flex space-x-4 items-center border border-gray-200 rounded-lg">
|
||||
<div>
|
||||
<.remix_icon icon="windy-line" class="text-gray-400 text-xl" />
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
You do not have any running sessions.
|
||||
<br>
|
||||
Please create a new one by clicking <span class="font-semibold">“New notebook”</span>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp session_list(assigns) do
|
||||
~H"""
|
||||
<div class="flex flex-col">
|
||||
<%= for session <- @sessions do %>
|
||||
<div class="py-4 flex items-center border-b border-gray-300"
|
||||
data-test-session-id={session.id}>
|
||||
<div class="flex-grow flex flex-col items-start">
|
||||
<%= live_redirect session.notebook_name,
|
||||
to: Routes.session_path(@socket, :page, session.id),
|
||||
class: "font-semibold text-gray-800 hover:text-gray-900" %>
|
||||
<div class="text-gray-600 text-sm">
|
||||
<%= if session.file, do: session.file.path, else: "No file" %>
|
||||
</div>
|
||||
<div class="mt-2 text-gray-600 text-sm">
|
||||
Created <%= format_creation_date(session.created_at) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative" id={"session-#{session.id}-menu"} phx-hook="Menu" data-element="menu">
|
||||
<button class="icon-button" data-toggle>
|
||||
<.remix_icon icon="more-2-fill" class="text-xl" />
|
||||
</button>
|
||||
<div class="menu" data-content>
|
||||
<button class="menu__item text-gray-500"
|
||||
phx-click="fork_session"
|
||||
phx-value-id={session.id}>
|
||||
<.remix_icon icon="git-branch-line" />
|
||||
<span class="font-medium">Fork</span>
|
||||
</button>
|
||||
<a class="menu__item text-gray-500"
|
||||
href={live_dashboard_process_path(@socket, session.pid)}
|
||||
target="_blank">
|
||||
<.remix_icon icon="dashboard-2-line" />
|
||||
<span class="font-medium">See on Dashboard</span>
|
||||
</a>
|
||||
<%= live_patch to: Routes.home_path(@socket, :close_session, session.id),
|
||||
class: "menu__item text-red-600" do %>
|
||||
<.remix_icon icon="close-circle-line" />
|
||||
<span class="font-medium">Close</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("set_order", %{"order_by" => order_by}, socket) do
|
||||
sessions = sort_sessions(socket.assigns.sessions, order_by)
|
||||
{:noreply, assign(socket, sessions: sessions, order_by: order_by)}
|
||||
end
|
||||
|
||||
def format_creation_date(created_at) do
|
||||
time_words = created_at |> DateTime.to_naive() |> Livebook.Utils.Time.time_ago_in_words()
|
||||
time_words <> " ago"
|
||||
end
|
||||
|
||||
defp order_by_label("date"), do: "Date"
|
||||
defp order_by_label("title"), do: "Title"
|
||||
|
||||
defp order_by_icon("date"), do: "calendar-2-line"
|
||||
defp order_by_icon("title"), do: "text"
|
||||
|
||||
defp sort_sessions(sessions, "date") do
|
||||
Enum.sort_by(sessions, & &1.created_at, {:desc, DateTime})
|
||||
end
|
||||
|
||||
defp sort_sessions(sessions, "title") do
|
||||
Enum.sort_by(sessions, fn session ->
|
||||
{session.notebook_name, -DateTime.to_unix(session.created_at)}
|
||||
end)
|
||||
end
|
||||
end
|
|
@ -290,7 +290,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
defp sections_list(assigns) do
|
||||
~H"""
|
||||
<div class="flex flex-col flex-grow">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
<h3 class="uppercase text-sm font-semibold text-gray-500">
|
||||
Sections
|
||||
</h3>
|
||||
<div class="flex flex-col mt-4 space-y-4">
|
||||
|
@ -327,10 +327,10 @@ defmodule LivebookWeb.SessionLive do
|
|||
~H"""
|
||||
<div class="flex flex-col flex-grow">
|
||||
<div class="flex items-center justify-between space-x-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800 flex-lg">
|
||||
<h3 class="uppercase text-sm font-semibold text-gray-500">
|
||||
Users
|
||||
</h3>
|
||||
<span class="flex items-center p-2 space-x-2 text-sm bg-gray-200 rounded-lg">
|
||||
<span class="flex items-center px-2 py-1 space-x-2 text-sm bg-gray-200 rounded-lg">
|
||||
<span class="inline-flex w-3 h-3 bg-green-600 rounded-full"></span>
|
||||
<span><%= length(@data_view.clients) %> connected</span>
|
||||
</span>
|
||||
|
@ -373,31 +373,34 @@ defmodule LivebookWeb.SessionLive do
|
|||
defp runtime_info(assigns) do
|
||||
~H"""
|
||||
<div class="flex flex-col flex-grow">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
Runtime
|
||||
</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="uppercase text-sm font-semibold text-gray-500">
|
||||
Runtime
|
||||
</h3>
|
||||
<%= live_patch to: Routes.session_path(@socket, :runtime_settings, @session.id),
|
||||
class: "icon-button",
|
||||
type: "button" do %>
|
||||
<.remix_icon icon="settings-3-line text-xl" />
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="flex flex-col mt-4 space-y-4">
|
||||
<%= if @data_view.runtime do %>
|
||||
<div class="flex flex-col space-y-3">
|
||||
<.labeled_text label="Type" text={runtime_type_label(@data_view.runtime)} />
|
||||
<.labeled_text label="Node name" text={@data_view.runtime.node} />
|
||||
<.labeled_text label="Node name" text={@data_view.runtime.node} one_line={true} />
|
||||
</div>
|
||||
<div class="flex flex-col space-y-3">
|
||||
<div class="flex space-x-2">
|
||||
<button class="button button-outlined-blue w-full" phx-click="restart_runtime">
|
||||
Reconnect
|
||||
<button class="button button-blue" phx-click="restart_runtime">
|
||||
<.remix_icon icon="wireless-charging-line" class="align-middle mr-1" />
|
||||
<span>Reconnect</span>
|
||||
</button>
|
||||
<button class="button button-outlined-red w-full"
|
||||
<button class="button button-outlined-red"
|
||||
type="button"
|
||||
phx-click="disconnect_runtime">
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
<%= live_patch to: Routes.session_path(@socket, :runtime_settings, @session.id),
|
||||
class: "button button-gray button-square-icon",
|
||||
type: "button" do %>
|
||||
<.remix_icon icon="settings-3-line" />
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="flex flex-col space-y-3">
|
||||
|
@ -405,12 +408,13 @@ defmodule LivebookWeb.SessionLive do
|
|||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<button class="button button-blue" phx-click="connect_default_runtime">
|
||||
Connect
|
||||
<.remix_icon icon="wireless-charging-line" class="align-middle mr-1" />
|
||||
<span>Connect</span>
|
||||
</button>
|
||||
<%= live_patch to: Routes.session_path(@socket, :runtime_settings, @session.id),
|
||||
class: "button button-gray button-square-icon",
|
||||
class: "button button-outlined-gray bg-transparent",
|
||||
type: "button" do %>
|
||||
<.remix_icon icon="settings-3-line" />
|
||||
Configure
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -33,7 +33,7 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do
|
|||
~H"""
|
||||
<div class="p-6 pb-4 max-w-4xl flex flex-col space-y-5">
|
||||
<h3 class="text-2xl font-semibold text-gray-800">
|
||||
Runtime
|
||||
Runtime settings
|
||||
</h3>
|
||||
<div class="w-full flex-col space-y-5">
|
||||
<div class="flex space-x-4">
|
||||
|
|
Loading…
Add table
Reference in a new issue