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:
Jonatan Kłosko 2021-11-10 18:50:39 +01:00 committed by GitHub
parent 54511d5ffc
commit d81965ee99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 172 additions and 100 deletions

View file

@ -250,12 +250,14 @@ defmodule LivebookWeb.Helpers do
<.labeled_text label="Name" text="Sherlock Holmes" /> <.labeled_text label="Name" text="Sherlock Holmes" />
""" """
def labeled_text(assigns) do def labeled_text(assigns) do
assigns = assign_new(assigns, :one_line, fn -> false end)
~H""" ~H"""
<div class="flex flex-col space-y-1"> <div class="flex flex-col space-y-1">
<span class="text-xs text-gray-500"> <span class="text-xs text-gray-500">
<%= @label %> <%= @label %>
</span> </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 %> <%= @text %>
</span> </span>
</div> </div>

View file

@ -13,7 +13,7 @@ defmodule LivebookWeb.HomeLive do
Phoenix.PubSub.subscribe(Livebook.PubSub, "tracker_sessions") Phoenix.PubSub.subscribe(Livebook.PubSub, "tracker_sessions")
end end
sessions = sort_sessions(Sessions.list_sessions()) sessions = Sessions.list_sessions()
notebook_infos = Notebook.Explore.visible_notebook_infos() |> Enum.take(3) notebook_infos = Notebook.Explore.visible_notebook_infos() |> Enum.take(3)
{:ok, {:ok,
@ -87,7 +87,7 @@ defmodule LivebookWeb.HomeLive do
<div class="py-12"> <div class="py-12">
<div class="mb-4 flex justify-between items-center"> <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 Explore
</h2> </h2>
<%= live_redirect to: Routes.explore_path(@socket, :page), <%= live_redirect to: Routes.explore_path(@socket, :page),
@ -104,12 +104,10 @@ defmodule LivebookWeb.HomeLive do
<% end %> <% end %>
</div> </div>
</div> </div>
<div class="py-12"> <div class="py-12">
<h2 class="mb-4 text-xl font-semibold text-gray-800"> <.live_component module={LivebookWeb.HomeLive.SessionListComponent}
Running sessions id="session-list"
</h2> sessions={@sessions} />
<.sessions_list sessions={@sessions} socket={@socket} />
</div> </div>
</div> </div>
</div> </div>
@ -149,68 +147,6 @@ defmodule LivebookWeb.HomeLive do
end end
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 @impl true
def handle_params(%{"session_id" => session_id}, _url, socket) do def handle_params(%{"session_id" => session_id}, _url, socket) do
session = Enum.find(socket.assigns.sessions, &(&1.id == session_id)) 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 if session in socket.assigns.sessions do
{:noreply, socket} {:noreply, socket}
else else
sessions = sort_sessions([session | socket.assigns.sessions]) {:noreply, assign(socket, sessions: [session | socket.assigns.sessions])}
{:noreply, assign(socket, sessions: sessions)}
end end
end end
@ -349,10 +284,6 @@ defmodule LivebookWeb.HomeLive do
def handle_info(_message, socket), do: {:noreply, socket} 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 defp files(sessions) do
Enum.map(sessions, & &1.file) Enum.map(sessions, & &1.file)
end end
@ -390,11 +321,6 @@ defmodule LivebookWeb.HomeLive do
session.id session.id
end 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 defp import_content(socket, content, session_opts) do
{notebook, messages} = Livebook.LiveMarkdown.Import.notebook_from_markdown(content) {notebook, messages} = Livebook.LiveMarkdown.Import.notebook_from_markdown(content)

View 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

View file

@ -290,7 +290,7 @@ defmodule LivebookWeb.SessionLive do
defp sections_list(assigns) do defp sections_list(assigns) do
~H""" ~H"""
<div class="flex flex-col flex-grow"> <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 Sections
</h3> </h3>
<div class="flex flex-col mt-4 space-y-4"> <div class="flex flex-col mt-4 space-y-4">
@ -327,10 +327,10 @@ defmodule LivebookWeb.SessionLive do
~H""" ~H"""
<div class="flex flex-col flex-grow"> <div class="flex flex-col flex-grow">
<div class="flex items-center justify-between space-x-4"> <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 Users
</h3> </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 class="inline-flex w-3 h-3 bg-green-600 rounded-full"></span>
<span><%= length(@data_view.clients) %> connected</span> <span><%= length(@data_view.clients) %> connected</span>
</span> </span>
@ -373,31 +373,34 @@ defmodule LivebookWeb.SessionLive do
defp runtime_info(assigns) do defp runtime_info(assigns) do
~H""" ~H"""
<div class="flex flex-col flex-grow"> <div class="flex flex-col flex-grow">
<h3 class="text-lg font-semibold text-gray-800"> <div class="flex items-center justify-between">
Runtime <h3 class="uppercase text-sm font-semibold text-gray-500">
</h3> 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"> <div class="flex flex-col mt-4 space-y-4">
<%= if @data_view.runtime do %> <%= if @data_view.runtime do %>
<div class="flex flex-col space-y-3"> <div class="flex flex-col space-y-3">
<.labeled_text label="Type" text={runtime_type_label(@data_view.runtime)} /> <.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>
<div class="flex flex-col space-y-3"> <div class="flex flex-col space-y-3">
<div class="flex space-x-2"> <div class="flex space-x-2">
<button class="button button-outlined-blue w-full" phx-click="restart_runtime"> <button class="button button-blue" phx-click="restart_runtime">
Reconnect <.remix_icon icon="wireless-charging-line" class="align-middle mr-1" />
<span>Reconnect</span>
</button> </button>
<button class="button button-outlined-red w-full" <button class="button button-outlined-red"
type="button" type="button"
phx-click="disconnect_runtime"> phx-click="disconnect_runtime">
Disconnect Disconnect
</button> </button>
</div> </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> </div>
<% else %> <% else %>
<div class="flex flex-col space-y-3"> <div class="flex flex-col space-y-3">
@ -405,12 +408,13 @@ defmodule LivebookWeb.SessionLive do
</div> </div>
<div class="flex space-x-2"> <div class="flex space-x-2">
<button class="button button-blue" phx-click="connect_default_runtime"> <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> </button>
<%= live_patch to: Routes.session_path(@socket, :runtime_settings, @session.id), <%= 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 %> type: "button" do %>
<.remix_icon icon="settings-3-line" /> Configure
<% end %> <% end %>
</div> </div>
<% end %> <% end %>

View file

@ -33,7 +33,7 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do
~H""" ~H"""
<div class="p-6 pb-4 max-w-4xl flex flex-col space-y-5"> <div class="p-6 pb-4 max-w-4xl flex flex-col space-y-5">
<h3 class="text-2xl font-semibold text-gray-800"> <h3 class="text-2xl font-semibold text-gray-800">
Runtime Runtime settings
</h3> </h3>
<div class="w-full flex-col space-y-5"> <div class="w-full flex-col space-y-5">
<div class="flex space-x-4"> <div class="flex space-x-4">