5.2 KiB · text History 6280797
defmodule GitGudWeb.ImportLive.Show do
@moduledoc """
Live progress for one import batch — a row per repository, updated over
PubSub as the clone workers report in.
"""
use GitGudWeb, :live_view
alias GitGud.Imports
alias GitGud.Repositories.Storage
@impl true
def mount(%{"id" => id}, _session, socket) do
user = socket.assigns.current_scope.user
import_row = Imports.get_import!(user, id)
if connected?(socket),
do: Phoenix.PubSub.subscribe(GitGud.PubSub, Imports.topic(import_row.id))
{:ok, assign_import(socket, import_row)}
end
defp assign_import(socket, import_row) do
socket
|> assign(:import, import_row)
|> assign(:page_title, "Import from #{import_row.remote_owner}")
|> assign(:counts, Imports.progress(import_row))
|> assign(:destination_handle, destination_handle(import_row))
end
defp destination_handle(import_row) do
case Imports.destination(import_row) do
nil -> nil
owner -> Storage.owner_handle(owner)
end
end
@impl true
def handle_info({:import_item_updated, _id}, socket), do: {:noreply, reload(socket)}
def handle_info({:import_updated, _id}, socket), do: {:noreply, reload(socket)}
def handle_info(_msg, socket), do: {:noreply, socket}
defp reload(socket) do
user = socket.assigns.current_scope.user
assign_import(socket, Imports.get_import!(user, socket.assigns.import.id))
end
@impl true
def handle_event("cancel", _params, socket) do
{:ok, _} = Imports.cancel(socket.assigns.import)
{:noreply,
socket
|> put_flash(:info, "Import canceled. Repositories already cloning will finish.")
|> assign_import(
Imports.get_import!(socket.assigns.current_scope.user, socket.assigns.import.id)
)}
end
defp count(counts, status), do: Map.get(counts, status, 0)
defp in_flight?(import_row), do: import_row.status in ~w(pending listing ready running)
defp status_badge("imported"), do: "badge-success"
defp status_badge("failed"), do: "badge-error"
defp status_badge("skipped"), do: "badge-warning"
defp status_badge("cloning"), do: "badge-info"
defp status_badge(_), do: "badge-ghost"
defp source_label(%{provider: "git"} = import_row), do: import_row.remote_owner
defp source_label(import_row),
do: "#{String.capitalize(import_row.provider)} · #{import_row.remote_owner}"
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope}>
<div class="space-y-6 max-w-4xl">
<div class="flex items-start justify-between gap-3 flex-wrap">
<div>
<h1 class="text-2xl font-semibold break-all">{source_label(@import)}</h1>
<p class="text-sm opacity-70 mt-1">
Importing into <span class="font-medium">{@destination_handle}</span>
· <span class="capitalize">{@import.status}</span>
</p>
</div>
<div class="flex gap-2">
<.link navigate={~p"/imports"} class="btn btn-sm btn-ghost">All imports</.link>
<button
:if={in_flight?(@import)}
phx-click="cancel"
class="btn btn-sm btn-outline btn-warning"
>
Cancel
</button>
</div>
</div>
<div :if={@import.error} class="alert alert-error text-sm">
<.icon name="hero-exclamation-triangle" class="w-5 h-5" />
<span>{@import.error}</span>
</div>
<div class="grid grid-cols-2 sm:grid-cols-5 gap-3">
<div
:for={
{label, key} <- [
{"Queued", "pending"},
{"Cloning", "cloning"},
{"Imported", "imported"},
{"Skipped", "skipped"},
{"Failed", "failed"}
]
}
class="border border-base-300 rounded-lg px-3 py-2"
>
<div class="text-xs opacity-60">{label}</div>
<div class="text-xl font-semibold tabular-nums">{count(@counts, key)}</div>
</div>
</div>
<div class="overflow-x-auto border border-base-300 rounded-lg">
<table class="table table-sm">
<thead>
<tr>
<th>Source</th>
<th>Repository</th>
<th class="w-28">Status</th>
</tr>
</thead>
<tbody>
<tr :for={item <- @import.items}>
<td class="break-all">{item.remote_full_name}</td>
<td>
<.link
:if={item.repository_id && @destination_handle}
navigate={~p"/r/#{@destination_handle}/#{item.target_name}"}
class="link link-hover font-medium"
>
{item.target_name}
</.link>
<span :if={is_nil(item.repository_id)} class="opacity-70">{item.target_name}</span>
<div :if={item.error} class="text-xs opacity-60 break-all">{item.error}</div>
</td>
<td>
<span class={"badge badge-sm #{status_badge(item.status)}"}>{item.status}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</Layouts.app>
"""
end
end