Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@
@custom-variant phx-submit-loading (.phx-submit-loading&, .phx-submit-loading &);
@custom-variant phx-change-loading (.phx-change-loading&, .phx-change-loading &);
@custom-variant phx-no-feedback (.phx-no-feedback&, .phx-no-feedback &);
/* LiveView sets `phx-drop-target-active` on a `phx-drop-target` element while a
file is being dragged over it, which is what reveals a drop overlay. */
@custom-variant phx-drop-target-active (
.phx-drop-target-active&,
.phx-drop-target-active &
);

@utility shadow-filter-slider {
box-shadow: var(--shadow-filter-slider);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,107 +1,110 @@
<NervesHubWeb.Layouts.sidebar flash={@flash} current_scope={@current_scope} sidebar_tab={@sidebar_tab}>
<div class="h-0 flex-1 overflow-y-auto" phx-drop-target={@uploads.firmware.ref}>
<div class="border-base-700 h-header flex items-center gap-4 border-b px-6 py-7 text-sm font-medium">
<h1 class="text-base-50 text-xl leading-[30px] font-semibold">All Firmware</h1>
<div class="bg-base-800 text-base-300 mr-auto rounded-sm px-1.5 py-0.5 text-xs">
{@pager_meta.total_count}
</div>

<div :for={entry <- @uploads.firmware.entries} class="flex w-2/5 flex-col items-center pr-2 pl-4">
<div class="mb-1 flex justify-between">
<span class="text-primary text-sm dark:text-white">Uploading firmware... </span>
<span class="text-primary text-sm dark:text-white">{entry.progress}%</span>
</div>
<div class="h-2.5 w-full rounded-full bg-gray-200 dark:bg-gray-700">
<div class="bg-primary h-2.5 animate-pulse rounded-full" style={"width: #{entry.progress}%"}></div>
<%!-- The drop target wraps the scrolling list instead of being it, so the
overlay can cover the whole area without scrolling along with the rows. --%>
<div class="relative flex h-0 flex-1 flex-col" phx-drop-target={@uploads.firmware.ref}>
<div class="h-0 flex-1 overflow-y-auto">
<div class="border-base-700 h-header flex items-center gap-4 border-b px-6 py-7 text-sm font-medium">
<h1 class="text-base-50 text-xl leading-[30px] font-semibold">All Firmware</h1>
<div class="bg-base-800 text-base-300 mr-auto rounded-sm px-1.5 py-0.5 text-xs">
{@pager_meta.total_count}
</div>
</div>

<form id="firmware-form" phx-change="validate-firmware" class={[Enum.any?(@uploads.firmware.entries) && "hidden"]}>
<div class="bg-base-800 border-base-600 flex gap-2 rounded border px-3 py-1.5 hover:cursor-pointer">
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">
<path
d="M4.1665 10.0001H9.99984M15.8332 10.0001H9.99984M9.99984 10.0001V4.16675M9.99984 10.0001V15.8334"
stroke="#A1A1AA"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<label for={@uploads.firmware.ref} class="text-base-300 text-sm font-medium hover:cursor-pointer">Upload Firmware</label>
<.live_file_input upload={@uploads.firmware} class="hidden" />
<div :for={entry <- @uploads.firmware.entries} class="flex w-2/5 flex-col items-center pr-2 pl-4">
<div class="mb-1 flex justify-between">
<span class="text-primary text-sm dark:text-white">Uploading firmware... </span>
<span class="text-primary text-sm dark:text-white">{entry.progress}%</span>
</div>
<div class="h-2.5 w-full rounded-full bg-gray-200 dark:bg-gray-700">
<div class="bg-primary h-2.5 animate-pulse rounded-full" style={"width: #{entry.progress}%"}></div>
</div>
</div>
</form>
</div>
<%= if Enum.any?(@firmware) do %>
<div class="listing">
<table>
<thead>
<tr>
<th>UUID</th>
<th phx-click="sort" phx-value-sort="version" class="cursor-pointer">
<Sorting.sort_icon text="Version" field="version" selected_field={@current_sort} selected_direction={@sort_direction} />
</th>
<th>Tool</th>
<th>Platform</th>
<th>Architecture</th>
<th phx-click="sort" phx-value-sort="install_count" class="cursor-pointer">
<Sorting.sort_icon text="Install count" field="install_count" selected_field={@current_sort} selected_direction={@sort_direction} />
</th>
<th>Signing Key</th>
<th phx-click="sort" phx-value-sort="inserted_at" class="cursor-pointer">
<Sorting.sort_icon text={"Uploaded on (#{zone_abbr(@time_zone)})"} field="inserted_at" selected_field={@current_sort} selected_direction={@sort_direction} />
</th>
</tr>
</thead>
<tbody>
<tr :for={firmware <- @firmware} class="border-base-800 border-b">
<td>
<div class="flex items-center gap-2">
<.link patch={~p"/org/#{@current_scope.org}/#{@product}/firmware/#{firmware}"}>
{firmware.uuid}
</.link>
</div>
</td>

<td>
<div class="flex items-center gap-2">
{firmware.version}
</div>
</td>
<form id="firmware-form" phx-change="validate-firmware" class={[Enum.any?(@uploads.firmware.entries) && "hidden"]}>
<div class="bg-base-800 border-base-600 flex gap-2 rounded border px-3 py-1.5 hover:cursor-pointer">
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">
<path
d="M4.1665 10.0001H9.99984M15.8332 10.0001H9.99984M9.99984 10.0001V4.16675M9.99984 10.0001V15.8334"
stroke="#A1A1AA"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<label for={@uploads.firmware.ref} class="text-base-300 text-sm font-medium hover:cursor-pointer">Upload Firmware</label>
<.live_file_input upload={@uploads.firmware} class="hidden" />
</div>
</form>
</div>
<%= if Enum.any?(@firmware) do %>
<div class="listing">
<table>
<thead>
<tr>
<th>UUID</th>
<th phx-click="sort" phx-value-sort="version" class="cursor-pointer">
<Sorting.sort_icon text="Version" field="version" selected_field={@current_sort} selected_direction={@sort_direction} />
</th>
<th>Tool</th>
<th>Platform</th>
<th>Architecture</th>
<th phx-click="sort" phx-value-sort="install_count" class="cursor-pointer">
<Sorting.sort_icon text="Install count" field="install_count" selected_field={@current_sort} selected_direction={@sort_direction} />
</th>
<th>Signing Key</th>
<th phx-click="sort" phx-value-sort="inserted_at" class="cursor-pointer">
<Sorting.sort_icon text={"Uploaded on (#{zone_abbr(@time_zone)})"} field="inserted_at" selected_field={@current_sort} selected_direction={@sort_direction} />
</th>
</tr>
</thead>
<tbody>
<tr :for={firmware <- @firmware} class="border-base-800 border-b">
<td>
<div class="flex items-center gap-2">
<.link patch={~p"/org/#{@current_scope.org}/#{@product}/firmware/#{firmware}"}>
{firmware.uuid}
</.link>
</div>
</td>

<td>
<span class="bg-base-800 border-base-700 inline-flex gap-1 rounded-full border py-0.5 pr-3 pl-2.5">
<code class="text-base-300 text-xs">{firmware.tool}</code>
</span>
</td>
<td>
<div class="flex items-center gap-2">
{firmware.version}
</div>
</td>

<td>
{firmware.platform}
</td>
<td>
<span class="bg-base-800 border-base-700 inline-flex gap-1 rounded-full border py-0.5 pr-3 pl-2.5">
<code class="text-base-300 text-xs">{firmware.tool}</code>
</span>
</td>

<td>
{firmware.architecture}
</td>
<td>
{firmware.platform}
</td>

<td>
{firmware.install_count || 0}
</td>
<td>
{firmware.architecture}
</td>

<td>
<span class="bg-base-800 border-base-700 inline-flex gap-1 rounded-full border py-0.5 pr-3 pl-2.5">
<code class="text-base-300 text-xs">{format_signed(firmware, @org_keys)}</code>
</span>
</td>
<td>
{firmware.install_count || 0}
</td>

<td>
<%= if is_nil(firmware.inserted_at) do %>
Never
<% else %>
<.local_datetime at={firmware.inserted_at} time_zone={@time_zone} zone_label={false} />
<% end %>
</td>
<td>
<span class="bg-base-800 border-base-700 inline-flex gap-1 rounded-full border py-0.5 pr-3 pl-2.5">
<code class="text-base-300 text-xs">{format_signed(firmware, @org_keys)}</code>
</span>
</td>

<%!-- <td class="actions">
<td>
<%= if is_nil(firmware.inserted_at) do %>
Never
<% else %>
<.local_datetime at={firmware.inserted_at} time_zone={@time_zone} zone_label={false} />
<% end %>
</td>

<%!-- <td class="actions">
<div class="">
<a class="" data-target="#" id={"actions-#{device.id}"} data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" phx-click={show_menu("actions-menu-#{device.id}")}>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
Expand Down Expand Up @@ -139,15 +142,29 @@
</div>
</div>
</td> --%>
</tr>
</tbody>
</table>
</div>
<% else %>
<div class="flex h-full items-center justify-center pb-16">
<span class="text-base-50 text-xl font-medium">{@product.name} doesn’t have any firmware yet.</span>
</tr>
</tbody>
</table>
</div>
<% else %>
<div class="flex h-full items-center justify-center pb-16">
<span class="text-base-50 text-xl font-medium">{@product.name} doesn’t have any firmware yet.</span>
</div>
<% end %>
</div>

<%!-- Only shown while a file is being dragged over the page. `pointer-events-none`
keeps the drop itself landing on the target underneath. --%>
<div
aria-hidden="true"
class="bg-surface/70 phx-drop-target-active:opacity-100 pointer-events-none absolute inset-0 z-40 flex items-center justify-center p-6 opacity-0 backdrop-blur-xs transition-opacity duration-150"
>
<div class="bg-primary/5 border-primary flex size-full flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed">
<span class="lucide-upload--light text-primary size-10"></span>
<span class="text-base-50 text-lg font-medium">Drop to upload firmware</span>
<span class="text-base-400 text-sm">Accepts .fw, .bin, .raucb and .avm files</span>
</div>
<% end %>
</div>
</div>

<Pager.render_with_page_sizes pager={@pager_meta} page_sizes={[25, 50, 100]} />
Expand Down
Loading