Packages

phoenix_kit

1.7.62
1.7.210 1.7.209 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 phoenix_kit_web live users media.html.heex
Raw

lib/phoenix_kit_web/live/users/media.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="Media"
current_path={@url_path}
project_title={@project_title}
current_locale={@current_locale}
>
<div class="p-6">
<.admin_page_header
back={PhoenixKit.Utils.Routes.path("/admin")}
title="Media"
subtitle="Manage user media and uploads"
/>
<%!-- Upload Section (Hidden by default) --%>
<%= if @show_upload do %>
<div class="card bg-base-100 shadow-xl mb-8 animate-in fade-in duration-200">
<div class="card-body">
<div class="flex items-center gap-3 mb-4">
<h2 class="card-title text-xl">Upload Media</h2>
<button
phx-click="toggle_upload"
class="btn btn-sm btn-error gap-2"
>
<.icon name="hero-x-mark" class="w-4 h-4" /> Cancel
</button>
</div>
<%= if @has_buckets do %>
<p class="text-sm text-base-content/70 mb-4">
Upload images, videos, or PDFs. Files are automatically processed and optimized.
</p>
<.file_upload
upload={@uploads.media_files}
label="Upload Media Files"
icon="hero-cloud-arrow-up"
accept_description="JPG, PNG, WebP, MP4, WebM, PDF"
max_size_description="100MB per file"
/>
<% else %>
<div class="alert alert-warning">
<.icon name="hero-exclamation-triangle" class="stroke-current shrink-0 h-6 w-6" />
<div>
<h3 class="font-bold">No Storage Buckets Configured</h3>
<div class="text-sm mt-1">
You need to configure at least one storage bucket before you can upload files.
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/settings/media")}
class="link link-primary font-semibold"
>
Configure Storage Buckets
</.link>
</div>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
<%!-- Uploaded Files --%>
<%= if @total_count > 0 do %>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex justify-between items-center mb-4">
<div class="flex items-center gap-3 flex-wrap">
<h2 class="card-title text-xl">
<%= if @filter_orphaned do %>
Orphaned Files ({@total_count})
<% else %>
Uploaded Files ({@total_count})
<% end %>
</h2>
<%= unless @show_upload do %>
<button
phx-click="toggle_upload"
class="btn btn-sm btn-primary gap-2"
>
<.icon name="hero-plus" class="w-4 h-4" /> Add Media
</button>
<% end %>
<button
phx-click="toggle_orphan_filter"
class={[
"btn btn-sm gap-2",
if(@filter_orphaned, do: "btn-warning", else: "btn-ghost")
]}
>
<.icon name="hero-funnel" class="w-4 h-4" />
<%= if @filter_orphaned do %>
Showing orphaned ({@orphaned_count})
<% else %>
Find orphaned ({@orphaned_count})
<% end %>
</button>
<%= if @filter_orphaned && @total_count > 0 do %>
<button
phx-click="delete_all_orphaned"
data-confirm={"Delete all #{@total_count} orphaned files?"}
class="btn btn-sm btn-error gap-2"
>
<.icon name="hero-trash" class="w-4 h-4" /> Delete all orphaned
</button>
<% end %>
</div>
<.pagination_info
page={@current_page}
per_page={@per_page}
total_count={@total_count}
/>
</div>
<div class="grid grid-cols-3 md:grid-cols-5 lg:grid-cols-6 gap-3">
<%= for file <- @uploaded_files do %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/media/#{file.file_uuid}")}
class="group relative aspect-square bg-base-300 rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow"
>
<.thumbnail_url :let={url} file={file} size={:medium}>
<%= if url do %>
<img
src={url}
alt={file.filename}
class="w-full h-full object-cover pointer-events-none group-hover:opacity-75 transition-opacity"
onerror="this.parentElement.querySelector('.image-error-placeholder').style.display='grid';"
/>
<div class="image-error-placeholder hidden absolute inset-0 bg-base-300 grid place-items-center text-base-content/50">
<div class="flex flex-col items-center justify-center">
<.icon name="hero-photo" class="w-12 h-12 mb-2 text-error" />
<p class="text-xs font-semibold">Image Missing</p>
</div>
</div>
<% else %>
<div class="flex flex-col items-center justify-center w-full h-full text-base-content/50 group-hover:bg-base-200 transition-colors">
<.icon name={file_icon(file.file_type)} class="w-12 h-12 mb-2" />
<p class="text-sm font-semibold">{String.upcase(file.file_type)}</p>
</div>
<% end %>
</.thumbnail_url>
<%!-- Video indicator icon --%>
<%= if file.file_type == "video" do %>
<div class="absolute top-2 left-2 bg-black/60 text-white p-1.5 rounded pointer-events-none flex items-center justify-center">
<.icon name="hero-play-solid" class="w-4 h-4" />
</div>
<% end %>
<%!-- PDF indicator badge --%>
<%= if file.mime_type == "application/pdf" do %>
<div class="absolute top-2 left-2 bg-error/80 text-white px-2 py-0.5 rounded text-xs font-bold pointer-events-none">
PDF
</div>
<% end %>
<%!-- File size in corner --%>
<div class="absolute bottom-2 right-2 bg-black/60 text-white text-xs px-2 py-1 rounded pointer-events-none">
{format_file_size(file.size)}
</div>
</.link>
<% end %>
</div>
<%!-- Pagination Controls --%>
<%= if @total_count > @per_page do %>
<.pagination
current_page={@current_page}
total_pages={@total_pages}
base_path={@url_path}
/>
<% end %>
</div>
</div>
<% else %>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex items-center gap-3 mb-4">
<h2 class="card-title text-xl">Uploaded Files</h2>
<button
phx-click="toggle_upload"
class={["btn btn-sm gap-2", (@show_upload && "btn-error") || "btn-primary"]}
>
<%= if @show_upload do %>
<.icon name="hero-x-mark" class="w-4 h-4" /> Cancel
<% else %>
<.icon name="hero-plus" class="w-4 h-4" /> Add Media
<% end %>
</button>
</div>
<div class="bg-base-200 rounded-lg p-8 text-center">
<p class="text-base-content/70">
No media uploaded yet. Upload your first file above!
</p>
</div>
</div>
</div>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>