refactor(elixir): remove unused is_active field
from storage location
This commit is contained in:
@@ -97,6 +97,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
|
||||
def handle_event("category_filter", %{"category_id" => category_id}, socket) do
|
||||
category_id = String.to_integer(category_id)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:selected_category, category_id)
|
||||
@@ -119,20 +120,26 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
case Inventory.increment_component_count(component) do
|
||||
{:ok, _updated_component} ->
|
||||
# Only apply sort freeze for dynamic sorting criteria
|
||||
should_freeze = socket.assigns.sort_criteria in ["count_asc", "count_desc", "updated_at_asc", "updated_at_desc"]
|
||||
|
||||
should_freeze =
|
||||
socket.assigns.sort_criteria in [
|
||||
"count_asc",
|
||||
"count_desc",
|
||||
"updated_at_asc",
|
||||
"updated_at_desc"
|
||||
]
|
||||
|
||||
if should_freeze do
|
||||
# Cancel any existing timer
|
||||
if socket.assigns.sort_freeze_timer do
|
||||
Process.cancel_timer(socket.assigns.sort_freeze_timer)
|
||||
end
|
||||
|
||||
|
||||
# Set sort freeze for 3 seconds and mark component as interacting
|
||||
freeze_until = DateTime.add(DateTime.utc_now(), 3, :second)
|
||||
|
||||
|
||||
# Set new timer to clear interaction state
|
||||
timer_ref = Process.send_after(self(), {:clear_interaction, id}, 3000)
|
||||
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Count updated")
|
||||
@@ -160,20 +167,26 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
case Inventory.decrement_component_count(component) do
|
||||
{:ok, _updated_component} ->
|
||||
# Only apply sort freeze for dynamic sorting criteria
|
||||
should_freeze = socket.assigns.sort_criteria in ["count_asc", "count_desc", "updated_at_asc", "updated_at_desc"]
|
||||
|
||||
should_freeze =
|
||||
socket.assigns.sort_criteria in [
|
||||
"count_asc",
|
||||
"count_desc",
|
||||
"updated_at_asc",
|
||||
"updated_at_desc"
|
||||
]
|
||||
|
||||
if should_freeze do
|
||||
# Cancel any existing timer
|
||||
if socket.assigns.sort_freeze_timer do
|
||||
Process.cancel_timer(socket.assigns.sort_freeze_timer)
|
||||
end
|
||||
|
||||
|
||||
# Set sort freeze for 3 seconds and mark component as interacting
|
||||
freeze_until = DateTime.add(DateTime.utc_now(), 3, :second)
|
||||
|
||||
|
||||
# Set new timer to clear interaction state
|
||||
timer_ref = Process.send_after(self(), {:clear_interaction, id}, 3000)
|
||||
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Count updated")
|
||||
@@ -269,9 +282,11 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
|
||||
new_focused_id =
|
||||
if socket.assigns.focused_component_id == component_id do
|
||||
nil # Unfocus if clicking on the same component
|
||||
# Unfocus if clicking on the same component
|
||||
nil
|
||||
else
|
||||
component_id # Focus on the new component
|
||||
# Focus on the new component
|
||||
component_id
|
||||
end
|
||||
|
||||
{:noreply, assign(socket, :focused_component_id, new_focused_id)}
|
||||
@@ -349,23 +364,26 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
|
||||
# Check if sorting should be frozen
|
||||
now = DateTime.utc_now()
|
||||
should_reload = is_nil(socket.assigns.sort_freeze_until) ||
|
||||
DateTime.compare(now, socket.assigns.sort_freeze_until) != :lt
|
||||
|
||||
should_reload =
|
||||
is_nil(socket.assigns.sort_freeze_until) ||
|
||||
DateTime.compare(now, socket.assigns.sort_freeze_until) != :lt
|
||||
|
||||
if should_reload do
|
||||
# Normal loading - query database with current sort criteria
|
||||
filters = [
|
||||
search: socket.assigns.search,
|
||||
sort_criteria: socket.assigns.sort_criteria,
|
||||
category_id: socket.assigns.selected_category,
|
||||
limit: @items_per_page,
|
||||
offset: socket.assigns.offset
|
||||
]
|
||||
|> Enum.reject(fn
|
||||
{_, v} when is_nil(v) -> true
|
||||
{:search, v} when v == "" -> true
|
||||
{_, _} -> false
|
||||
end)
|
||||
filters =
|
||||
[
|
||||
search: socket.assigns.search,
|
||||
sort_criteria: socket.assigns.sort_criteria,
|
||||
category_id: socket.assigns.selected_category,
|
||||
limit: @items_per_page,
|
||||
offset: socket.assigns.offset
|
||||
]
|
||||
|> Enum.reject(fn
|
||||
{_, v} when is_nil(v) -> true
|
||||
{:search, v} when v == "" -> true
|
||||
{_, _} -> false
|
||||
end)
|
||||
|
||||
%{components: new_components, has_more: has_more} =
|
||||
Inventory.paginate_components(filters)
|
||||
@@ -383,7 +401,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
else
|
||||
# Frozen - just update the specific component in place without reordering
|
||||
if socket.assigns.interacting_with do
|
||||
updated_components =
|
||||
updated_components =
|
||||
Enum.map(socket.assigns.components, fn component ->
|
||||
if to_string(component.id) == socket.assigns.interacting_with do
|
||||
# Reload this specific component to get updated count
|
||||
@@ -392,7 +410,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
component
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
assign(socket, :components, updated_components)
|
||||
else
|
||||
socket
|
||||
@@ -485,8 +503,8 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="flex flex-col sm:flex-row gap-4">
|
||||
<div class="flex-1">
|
||||
@@ -553,7 +571,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
<%= if @sort_frozen do %>
|
||||
<div class="absolute -bottom-5 left-0 text-xs text-yellow-600 flex items-center transition-opacity duration-200 pointer-events-none">
|
||||
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2L3 7v11h14V7l-7-5z"/>
|
||||
<path d="M10 2L3 7v11h14V7l-7-5z" />
|
||||
</svg>
|
||||
Sort temporarily frozen
|
||||
</div>
|
||||
@@ -562,7 +580,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Add Component Modal -->
|
||||
<%= if @show_add_form do %>
|
||||
<div class="fixed inset-0 bg-base-content/50 overflow-y-auto h-full w-full z-50">
|
||||
@@ -578,7 +596,13 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<.form for={@form} phx-submit="save_component" phx-change="validate" multipart={true} class="space-y-4">
|
||||
<.form
|
||||
for={@form}
|
||||
phx-submit="save_component"
|
||||
phx-change="validate"
|
||||
multipart={true}
|
||||
class="space-y-4"
|
||||
>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-base-content">Name</label>
|
||||
<.input field={@form[:name]} type="text" required />
|
||||
@@ -605,7 +629,9 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
<.input field={@form[:keywords]} type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-base-content">Storage Location</label>
|
||||
<label class="block text-sm font-medium text-base-content">
|
||||
Storage Location
|
||||
</label>
|
||||
<.input
|
||||
field={@form[:storage_location_id]}
|
||||
type="select"
|
||||
@@ -628,14 +654,17 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-base-content">Component Image</label>
|
||||
<div class="mt-1">
|
||||
<.live_file_input upload={@uploads.image} class="block w-full text-sm text-base-content file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary/10 file:text-primary hover:file:bg-primary/20" />
|
||||
<.live_file_input
|
||||
upload={@uploads.image}
|
||||
class="block w-full text-sm text-base-content file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary/10 file:text-primary hover:file:bg-primary/20"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-base-content/60">
|
||||
JPG, PNG, GIF up to 5MB
|
||||
</p>
|
||||
|
||||
<%= for err <- upload_errors(@uploads.image) do %>
|
||||
<p class="text-error text-sm mt-1"><%= Phoenix.Naming.humanize(err) %></p>
|
||||
<p class="text-error text-sm mt-1">{Phoenix.Naming.humanize(err)}</p>
|
||||
<% end %>
|
||||
|
||||
<%= for entry <- @uploads.image.entries do %>
|
||||
@@ -645,17 +674,23 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
<.live_img_preview entry={entry} class="h-10 w-10 rounded-lg object-cover" />
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium text-base-content"><%= entry.client_name %></p>
|
||||
<p class="text-sm text-base-content/60"><%= entry.progress %>%</p>
|
||||
<p class="text-sm font-medium text-base-content">{entry.client_name}</p>
|
||||
<p class="text-sm text-base-content/60">{entry.progress}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" phx-click="cancel-upload" phx-value-ref={entry.ref} aria-label="cancel" class="text-error hover:text-error/80">
|
||||
<button
|
||||
type="button"
|
||||
phx-click="cancel-upload"
|
||||
phx-value-ref={entry.ref}
|
||||
aria-label="cancel"
|
||||
class="text-error hover:text-error/80"
|
||||
>
|
||||
<.icon name="hero-x-mark" class="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= for err <- upload_errors(@uploads.image) do %>
|
||||
<p class="mt-1 text-sm text-error"><%= upload_error_to_string(err) %></p>
|
||||
<p class="mt-1 text-sm text-error">{upload_error_to_string(err)}</p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -679,7 +714,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
<!-- Edit Component Modal -->
|
||||
<%= if @show_edit_form do %>
|
||||
<div class="fixed inset-0 bg-base-content/50 overflow-y-auto h-full w-full z-50">
|
||||
@@ -695,7 +730,13 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<.form for={@form} phx-submit="save_edit" phx-change="validate" multipart={true} class="space-y-4">
|
||||
<.form
|
||||
for={@form}
|
||||
phx-submit="save_edit"
|
||||
phx-change="validate"
|
||||
multipart={true}
|
||||
class="space-y-4"
|
||||
>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-base-content">Name</label>
|
||||
<.input field={@form[:name]} type="text" required />
|
||||
@@ -722,7 +763,9 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
<.input field={@form[:keywords]} type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-base-content">Storage Location</label>
|
||||
<label class="block text-sm font-medium text-base-content">
|
||||
Storage Location
|
||||
</label>
|
||||
<.input
|
||||
field={@form[:storage_location_id]}
|
||||
type="select"
|
||||
@@ -747,18 +790,25 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
<%= if @editing_component && @editing_component.image_filename do %>
|
||||
<div class="mt-1 mb-2">
|
||||
<p class="text-sm text-base-content/70">Current image:</p>
|
||||
<img src={"/uploads/images/#{@editing_component.image_filename}"} alt="Current component" class="h-20 w-20 object-cover rounded-lg" />
|
||||
<img
|
||||
src={"/uploads/images/#{@editing_component.image_filename}"}
|
||||
alt="Current component"
|
||||
class="h-20 w-20 object-cover rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="mt-1">
|
||||
<.live_file_input upload={@uploads.image} class="block w-full text-sm text-base-content file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary/10 file:text-primary hover:file:bg-primary/20" />
|
||||
<.live_file_input
|
||||
upload={@uploads.image}
|
||||
class="block w-full text-sm text-base-content file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary/10 file:text-primary hover:file:bg-primary/20"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-base-content/60">
|
||||
JPG, PNG, GIF up to 5MB (leave empty to keep current image)
|
||||
</p>
|
||||
|
||||
<%= for err <- upload_errors(@uploads.image) do %>
|
||||
<p class="text-error text-sm mt-1"><%= Phoenix.Naming.humanize(err) %></p>
|
||||
<p class="text-error text-sm mt-1">{Phoenix.Naming.humanize(err)}</p>
|
||||
<% end %>
|
||||
|
||||
<%= for entry <- @uploads.image.entries do %>
|
||||
@@ -768,17 +818,23 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
<.live_img_preview entry={entry} class="h-10 w-10 rounded-lg object-cover" />
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium text-base-content"><%= entry.client_name %></p>
|
||||
<p class="text-sm text-base-content/60"><%= entry.progress %>%</p>
|
||||
<p class="text-sm font-medium text-base-content">{entry.client_name}</p>
|
||||
<p class="text-sm text-base-content/60">{entry.progress}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" phx-click="cancel-upload" phx-value-ref={entry.ref} aria-label="cancel" class="text-error hover:text-error/80">
|
||||
<button
|
||||
type="button"
|
||||
phx-click="cancel-upload"
|
||||
phx-value-ref={entry.ref}
|
||||
aria-label="cancel"
|
||||
class="text-error hover:text-error/80"
|
||||
>
|
||||
<.icon name="hero-x-mark" class="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= for err <- upload_errors(@uploads.image) do %>
|
||||
<p class="mt-1 text-sm text-error"><%= upload_error_to_string(err) %></p>
|
||||
<p class="mt-1 text-sm text-error">{upload_error_to_string(err)}</p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -802,17 +858,28 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
<!-- Components List -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-6">
|
||||
<div class="bg-base-100 shadow overflow-hidden sm:rounded-md">
|
||||
<ul class="divide-y divide-base-300" id="components-list" phx-update="replace">
|
||||
<%= for component <- @components do %>
|
||||
<li id={"component-#{component.id}"} class={[
|
||||
"px-6 py-6 hover:bg-base-200 transition-all duration-200",
|
||||
if(@focused_component_id == component.id, do: "bg-base-50 border-l-4 border-primary", else: "cursor-pointer"),
|
||||
if(@interacting_with == to_string(component.id), do: "ring-2 ring-yellow-400 ring-opacity-50 bg-yellow-50", else: "")
|
||||
]} phx-click={if @focused_component_id != component.id, do: "toggle_focus", else: nil} phx-value-id={component.id}>
|
||||
<li
|
||||
id={"component-#{component.id}"}
|
||||
class={[
|
||||
"px-6 py-6 hover:bg-base-200 transition-all duration-200",
|
||||
if(@focused_component_id == component.id,
|
||||
do: "bg-base-50 border-l-4 border-primary",
|
||||
else: "cursor-pointer"
|
||||
),
|
||||
if(@interacting_with == to_string(component.id),
|
||||
do: "ring-2 ring-yellow-400 ring-opacity-50 bg-yellow-50",
|
||||
else: ""
|
||||
)
|
||||
]}
|
||||
phx-click={if @focused_component_id != component.id, do: "toggle_focus", else: nil}
|
||||
phx-value-id={component.id}
|
||||
>
|
||||
<%= if @focused_component_id == component.id do %>
|
||||
<!-- Expanded/Focused View -->
|
||||
<div class="space-y-6">
|
||||
@@ -851,8 +918,8 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content area with image and details -->
|
||||
|
||||
<!-- Content area with image and details -->
|
||||
<div class="flex gap-6">
|
||||
<!-- Large Image -->
|
||||
<div class="flex-shrink-0">
|
||||
@@ -870,12 +937,15 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</button>
|
||||
<% else %>
|
||||
<div class="h-48 w-48 rounded-lg bg-base-200 flex items-center justify-center border border-base-300">
|
||||
<.icon name="hero-cube-transparent" class="h-20 w-20 text-base-content/50" />
|
||||
<.icon
|
||||
name="hero-cube-transparent"
|
||||
class="h-20 w-20 text-base-content/50"
|
||||
/>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Details -->
|
||||
|
||||
<!-- Details -->
|
||||
<div class="flex-1 space-y-4 select-text">
|
||||
<!-- Full Description -->
|
||||
<%= if component.description do %>
|
||||
@@ -886,15 +956,20 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Metadata Grid -->
|
||||
|
||||
<!-- Metadata Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
||||
<%= if component.storage_location do %>
|
||||
<div class="flex items-start">
|
||||
<.icon name="hero-map-pin" class="w-4 h-4 mr-2 text-base-content/50 mt-0.5 flex-shrink-0" />
|
||||
<.icon
|
||||
name="hero-map-pin"
|
||||
class="w-4 h-4 mr-2 text-base-content/50 mt-0.5 flex-shrink-0"
|
||||
/>
|
||||
<div>
|
||||
<span class="font-medium text-base-content">Location:</span>
|
||||
<div class="text-base-content/70">{storage_location_display_name(component.storage_location)}</div>
|
||||
<div class="text-base-content/70">
|
||||
{storage_location_display_name(component.storage_location)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -906,10 +981,15 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</div>
|
||||
|
||||
<div class="flex items-start">
|
||||
<.icon name="hero-calendar" class="w-4 h-4 mr-2 text-base-content/50 mt-0.5 flex-shrink-0" />
|
||||
<.icon
|
||||
name="hero-calendar"
|
||||
class="w-4 h-4 mr-2 text-base-content/50 mt-0.5 flex-shrink-0"
|
||||
/>
|
||||
<div>
|
||||
<span class="font-medium text-base-content">Entry Date:</span>
|
||||
<div class="text-base-content/70">{Calendar.strftime(component.inserted_at, "%B %d, %Y")}</div>
|
||||
<div class="text-base-content/70">
|
||||
{Calendar.strftime(component.inserted_at, "%B %d, %Y")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -933,32 +1013,29 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex justify-end items-center space-x-2 pt-4 border-t border-base-300">
|
||||
<button
|
||||
phx-click="increment_count"
|
||||
phx-value-id={component.id}
|
||||
class="inline-flex items-center px-3 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
|
||||
>
|
||||
<.icon name="hero-plus" class="w-4 h-4 mr-1" />
|
||||
Add
|
||||
<.icon name="hero-plus" class="w-4 h-4 mr-1" /> Add
|
||||
</button>
|
||||
<button
|
||||
phx-click="decrement_count"
|
||||
phx-value-id={component.id}
|
||||
class="inline-flex items-center px-3 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-yellow-600 hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500"
|
||||
>
|
||||
<.icon name="hero-minus" class="w-4 h-4 mr-1" />
|
||||
Remove
|
||||
<.icon name="hero-minus" class="w-4 h-4 mr-1" /> Remove
|
||||
</button>
|
||||
<button
|
||||
phx-click="show_edit_form"
|
||||
phx-value-id={component.id}
|
||||
class="inline-flex items-center px-3 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
<.icon name="hero-pencil" class="w-4 h-4 mr-1" />
|
||||
Edit
|
||||
<.icon name="hero-pencil" class="w-4 h-4 mr-1" /> Edit
|
||||
</button>
|
||||
<button
|
||||
phx-click="delete_component"
|
||||
@@ -966,8 +1043,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
data-confirm="Are you sure you want to delete this component?"
|
||||
class="inline-flex items-center px-3 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
|
||||
>
|
||||
<.icon name="hero-trash" class="w-4 h-4 mr-1" />
|
||||
Delete
|
||||
<.icon name="hero-trash" class="w-4 h-4 mr-1" /> Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -982,7 +1058,11 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
phx-value-url={"/user_generated/uploads/images/#{component.image_filename}"}
|
||||
class="hover:opacity-75 transition-opacity block"
|
||||
>
|
||||
<img src={"/user_generated/uploads/images/#{component.image_filename}"} alt={component.name} class="max-h-20 max-w-20 rounded-md object-contain cursor-pointer block" />
|
||||
<img
|
||||
src={"/user_generated/uploads/images/#{component.image_filename}"}
|
||||
alt={component.name}
|
||||
class="max-h-20 max-w-20 rounded-md object-contain cursor-pointer block"
|
||||
/>
|
||||
</button>
|
||||
<% else %>
|
||||
<div class="h-20 w-20 rounded-md bg-base-200 flex items-center justify-center">
|
||||
@@ -1018,8 +1098,8 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Middle row: Description -->
|
||||
|
||||
<!-- Middle row: Description -->
|
||||
<%= if component.description do %>
|
||||
<div class="mt-1">
|
||||
<p class="text-sm text-base-content/70 line-clamp-2">
|
||||
@@ -1027,14 +1107,19 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Bottom row: Metadata -->
|
||||
|
||||
<!-- Bottom row: Metadata -->
|
||||
<div class="mt-2 grid grid-cols-1 sm:grid-cols-3 gap-x-4 gap-y-1 text-sm text-base-content/60">
|
||||
<%= if component.storage_location do %>
|
||||
<div class="flex items-center min-w-0">
|
||||
<.icon name="hero-map-pin" class="w-4 h-4 mr-1 text-base-content/50 flex-shrink-0" />
|
||||
<.icon
|
||||
name="hero-map-pin"
|
||||
class="w-4 h-4 mr-1 text-base-content/50 flex-shrink-0"
|
||||
/>
|
||||
<span class="font-medium">Location:</span>
|
||||
<span class="ml-1 truncate">{storage_location_display_name(component.storage_location)}</span>
|
||||
<span class="ml-1 truncate">
|
||||
{storage_location_display_name(component.storage_location)}
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="flex items-center">
|
||||
@@ -1050,8 +1135,8 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Keywords row -->
|
||||
|
||||
<!-- Keywords row -->
|
||||
<%= if component.keywords do %>
|
||||
<div class="mt-2">
|
||||
<p class="text-xs text-base-content/50">
|
||||
@@ -1127,12 +1212,18 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
|
||||
<!-- Image Modal -->
|
||||
<%= if @show_image_modal do %>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center p-4" phx-click="close_image_modal">
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
phx-click="close_image_modal"
|
||||
>
|
||||
<!-- Background overlay -->
|
||||
<div class="absolute inset-0 bg-black bg-opacity-75"></div>
|
||||
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-base-100 rounded-lg shadow-xl max-w-4xl w-full max-h-full overflow-auto" phx-click="prevent_close">
|
||||
|
||||
<!-- Modal content -->
|
||||
<div
|
||||
class="relative bg-base-100 rounded-lg shadow-xl max-w-4xl w-full max-h-full overflow-auto"
|
||||
phx-click="prevent_close"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center p-4 border-b border-base-300 bg-base-100 rounded-t-lg">
|
||||
<h3 class="text-lg font-semibold text-base-content">Component Image</h3>
|
||||
@@ -1145,8 +1236,8 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
|
||||
<!-- Content -->
|
||||
<div class="p-6 bg-base-100 rounded-b-lg">
|
||||
<div class="text-center">
|
||||
<%= if @modal_image_url do %>
|
||||
@@ -1192,6 +1283,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
:ok ->
|
||||
IO.puts("=== DEBUG: File copy successful ===")
|
||||
{:ok, filename}
|
||||
|
||||
{:error, reason} ->
|
||||
IO.puts("=== DEBUG: File copy failed: #{inspect(reason)} ===")
|
||||
{:postpone, {:error, reason}}
|
||||
@@ -1200,18 +1292,21 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|
||||
|
||||
IO.inspect(uploaded_files, label: "Uploaded files result")
|
||||
|
||||
result = case uploaded_files do
|
||||
[filename] when is_binary(filename) ->
|
||||
IO.puts("=== DEBUG: Adding filename to params: #{filename} ===")
|
||||
Map.put(component_params, "image_filename", filename)
|
||||
[] ->
|
||||
IO.puts("=== DEBUG: No files uploaded ===")
|
||||
component_params
|
||||
_error ->
|
||||
IO.puts("=== DEBUG: Upload error ===")
|
||||
IO.inspect(uploaded_files, label: "Unexpected upload result")
|
||||
component_params
|
||||
end
|
||||
result =
|
||||
case uploaded_files do
|
||||
[filename] when is_binary(filename) ->
|
||||
IO.puts("=== DEBUG: Adding filename to params: #{filename} ===")
|
||||
Map.put(component_params, "image_filename", filename)
|
||||
|
||||
[] ->
|
||||
IO.puts("=== DEBUG: No files uploaded ===")
|
||||
component_params
|
||||
|
||||
_error ->
|
||||
IO.puts("=== DEBUG: Upload error ===")
|
||||
IO.inspect(uploaded_files, label: "Unexpected upload result")
|
||||
component_params
|
||||
end
|
||||
|
||||
IO.inspect(result, label: "Final component_params")
|
||||
IO.puts("=== DEBUG: End save_uploaded_image ===")
|
||||
|
||||
Reference in New Issue
Block a user