Packages

phoenix_kit

1.7.78
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules shop web import_show.ex
Raw

lib/modules/shop/web/import_show.ex

defmodule PhoenixKit.Modules.Shop.Web.ImportShow do
@moduledoc """
LiveView for displaying import details.
Shows:
- Import metadata (filename, user, dates)
- Statistics summary (imported/updated/skipped/errors)
- List of imported products with links to edit
- Error details (if any)
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Modules.Shop
alias PhoenixKit.Modules.Shop.Translations
alias PhoenixKit.Utils.Routes
@impl true
def mount(%{"uuid" => uuid}, _session, socket) do
case Shop.get_import_log(uuid, preload: [:user]) do
nil ->
{:ok,
socket
|> put_flash(:error, "Import not found")
|> push_navigate(to: Routes.path("/admin/shop/imports"))}
import_log ->
products = load_products(import_log.product_uuids || [])
socket =
socket
|> assign(:page_title, "Import: #{import_log.filename}")
|> assign(:import, import_log)
|> assign(:products, products)
{:ok, socket}
end
end
@impl true
def handle_params(_params, uri, socket) do
{:noreply, assign(socket, :url_path, URI.parse(uri).path)}
end
defp load_products([]), do: []
defp load_products(product_uuids) do
Shop.list_products_by_ids(product_uuids)
end
defp format_datetime(nil), do: "-"
defp format_datetime(datetime) do
Calendar.strftime(datetime, "%b %d, %Y %H:%M")
end
defp get_localized(nil), do: "-"
defp get_localized(value) when is_binary(value), do: value
defp get_localized(value) when is_map(value) do
lang = Translations.default_language()
Map.get(value, lang) || Map.get(value, "en") || Map.values(value) |> List.first() || "-"
end
defp format_price(nil), do: "-"
defp format_price(%Decimal{} = price), do: Decimal.to_string(price)
defp format_price(price) when is_number(price), do: to_string(price)
@impl true
def render(assigns) do
~H"""
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={@phoenix_kit_current_scope}
current_path={@url_path}
current_locale={@current_locale}
page_title={@page_title}
>
<div class="container mx-auto px-4 py-6 max-w-4xl">
<.admin_page_header back={Routes.path("/admin/shop/imports")} title="Import Details">
<:actions>
<span class={[
"badge",
case @import.status do
"pending" -> "badge-warning"
"processing" -> "badge-info"
"completed" -> "badge-success"
"failed" -> "badge-error"
_ -> "badge-ghost"
end
]}>
{@import.status}
</span>
</:actions>
</.admin_page_header>
<%!-- Metadata card --%>
<div class="card bg-base-100 shadow mb-6">
<div class="card-body">
<h2 class="card-title text-lg mb-4">
<.icon name="hero-document-text" class="w-5 h-5" /> Import Information
</h2>
<dl class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<dt class="text-sm text-base-content/60">Filename</dt>
<dd class="font-medium">{@import.filename}</dd>
</div>
<div>
<dt class="text-sm text-base-content/60">User</dt>
<dd class="font-medium">{if @import.user, do: @import.user.email, else: "-"}</dd>
</div>
<div>
<dt class="text-sm text-base-content/60">Started At</dt>
<dd class="font-medium">{format_datetime(@import.started_at)}</dd>
</div>
<div>
<dt class="text-sm text-base-content/60">Completed At</dt>
<dd class="font-medium">{format_datetime(@import.completed_at)}</dd>
</div>
</dl>
</div>
</div>
<%!-- Statistics --%>
<div class="stats shadow w-full mb-6">
<div class="stat">
<div class="stat-figure text-success">
<.icon name="hero-plus-circle" class="w-8 h-8" />
</div>
<div class="stat-title">Imported</div>
<div class="stat-value text-success">{@import.imported_count}</div>
<div class="stat-desc">new products</div>
</div>
<div class="stat">
<div class="stat-figure text-info">
<.icon name="hero-arrow-path" class="w-8 h-8" />
</div>
<div class="stat-title">Updated</div>
<div class="stat-value text-info">{@import.updated_count}</div>
<div class="stat-desc">existing products</div>
</div>
<div class="stat">
<div class="stat-figure text-warning">
<.icon name="hero-minus-circle" class="w-8 h-8" />
</div>
<div class="stat-title">Skipped</div>
<div class="stat-value text-warning">{@import.skipped_count}</div>
<div class="stat-desc">filtered out</div>
</div>
<div class="stat">
<div class="stat-figure text-error">
<.icon name="hero-exclamation-circle" class="w-8 h-8" />
</div>
<div class="stat-title">Errors</div>
<div class="stat-value text-error">{@import.error_count}</div>
<div class="stat-desc">failed rows</div>
</div>
</div>
<%!-- Products list --%>
<%= if @products != [] do %>
<div class="card bg-base-100 shadow mb-6">
<div class="card-body">
<h2 class="card-title text-lg mb-4">
<.icon name="hero-cube" class="w-5 h-5" /> Imported Products
<span class="badge badge-ghost">{length(@products)}</span>
</h2>
<div class="overflow-x-auto">
<table class="table table-zebra">
<thead>
<tr>
<th>Title</th>
<th>Slug</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<%= for product <- @products do %>
<tr class="hover">
<td class="font-medium max-w-[250px] truncate">
{get_localized(product.title)}
</td>
<td class="text-base-content/70 text-sm">
{get_localized(product.slug) || "-"}
</td>
<td>{format_price(product.price)}</td>
<td>
<div class="flex gap-1">
<.link
navigate={Routes.path("/admin/shop/products/#{product.uuid}")}
class="btn btn-xs btn-outline btn-info tooltip tooltip-bottom"
data-tip={gettext("View")}
>
<.icon name="hero-eye" class="w-4 h-4 hidden sm:inline" />
<span class="sm:hidden whitespace-nowrap">{gettext("View")}</span>
</.link>
<.link
navigate={Routes.path("/admin/shop/products/#{product.uuid}/edit")}
class="btn btn-xs btn-outline btn-info tooltip tooltip-bottom"
data-tip={gettext("Edit")}
>
<.icon name="hero-pencil" class="w-4 h-4 hidden sm:inline" />
<span class="sm:hidden whitespace-nowrap">{gettext("Edit")}</span>
</.link>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
<% else %>
<div class="alert mb-6">
<.icon name="hero-information-circle" class="w-6 h-6" />
<span>
No products tracked for this import. Product tracking was added in a later version.
</span>
</div>
<% end %>
<%!-- Errors section (if any) --%>
<%= if @import.error_count > 0 and @import.error_details != [] do %>
<div class="card bg-base-100 shadow">
<div class="card-body">
<h2 class="card-title text-lg text-error mb-4">
<.icon name="hero-exclamation-triangle" class="w-5 h-5" /> Errors
<span class="badge badge-error">{@import.error_count}</span>
</h2>
<div class="overflow-x-auto">
<table class="table table-sm">
<thead>
<tr>
<th>Handle</th>
<th>Error</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<%= for error <- Enum.take(@import.error_details, 50) do %>
<tr>
<td class="font-mono text-sm">{error["handle"]}</td>
<td class="text-error text-sm max-w-[300px] truncate">
{error["error"]}
</td>
<td class="text-xs text-base-content/60">
{error["timestamp"]}
</td>
</tr>
<% end %>
</tbody>
</table>
<%= if length(@import.error_details) > 50 do %>
<p class="text-sm text-base-content/60 mt-2">
Showing first 50 of {length(@import.error_details)} errors
</p>
<% end %>
</div>
</div>
</div>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>
"""
end
end