style: format codebase

This commit is contained in:
Schuwi
2025-09-20 11:52:43 +02:00
parent aaf278f7f9
commit c6c218970c
20 changed files with 722 additions and 385 deletions

View File

@@ -54,12 +54,13 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
def handle_event("show_edit_form", %{"id" => id}, socket) do
location = Inventory.get_storage_location!(id)
# Create a changeset with current values forced into changes for proper form display
changeset = Inventory.change_storage_location(location, %{
name: location.name,
description: location.description,
parent_id: location.parent_id
})
|> Ecto.Changeset.force_change(:parent_id, location.parent_id)
changeset =
Inventory.change_storage_location(location, %{
name: location.name,
description: location.description,
parent_id: location.parent_id
})
|> Ecto.Changeset.force_change(:parent_id, location.parent_id)
form = to_form(changeset)
@@ -82,29 +83,31 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
def handle_event("save_location", %{"storage_location" => location_params}, socket) do
# Process AprilTag assignment based on mode
processed_params = case socket.assigns.apriltag_mode do
"none" ->
# Remove any apriltag_id from params to ensure it's nil
Map.delete(location_params, "apriltag_id")
processed_params =
case socket.assigns.apriltag_mode do
"none" ->
# Remove any apriltag_id from params to ensure it's nil
Map.delete(location_params, "apriltag_id")
"auto" ->
# Auto-assign next available AprilTag ID
case AprilTag.next_available_apriltag_id() do
nil ->
# No available IDs, proceed without AprilTag
Map.delete(location_params, "apriltag_id")
apriltag_id ->
Map.put(location_params, "apriltag_id", apriltag_id)
end
"auto" ->
# Auto-assign next available AprilTag ID
case AprilTag.next_available_apriltag_id() do
nil ->
# No available IDs, proceed without AprilTag
Map.delete(location_params, "apriltag_id")
"manual" ->
# Use the manually entered apriltag_id (validation will be handled by changeset)
location_params
apriltag_id ->
Map.put(location_params, "apriltag_id", apriltag_id)
end
_ ->
# Fallback: remove apriltag_id
Map.delete(location_params, "apriltag_id")
end
"manual" ->
# Use the manually entered apriltag_id (validation will be handled by changeset)
location_params
_ ->
# Fallback: remove apriltag_id
Map.delete(location_params, "apriltag_id")
end
case Inventory.create_storage_location(processed_params) do
{:ok, _location} ->
@@ -147,7 +150,12 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
|> reload_storage_locations()}
{:error, _changeset} ->
{:noreply, put_flash(socket, :error, "Cannot delete storage location - it may have components assigned or child locations")}
{:noreply,
put_flash(
socket,
:error,
"Cannot delete storage location - it may have components assigned or child locations"
)}
end
end
@@ -164,10 +172,17 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
{apriltag_id, ""} when apriltag_id >= 0 and apriltag_id <= 586 ->
case Inventory.get_storage_location_by_apriltag_id(apriltag_id) do
nil ->
{:noreply, put_flash(socket, :error, "Storage location not found for AprilTag ID: #{apriltag_id}")}
{:noreply,
put_flash(
socket,
:error,
"Storage location not found for AprilTag ID: #{apriltag_id}"
)}
location ->
scanned_tags = [%{apriltag_id: apriltag_id, location: location} | socket.assigns.scanned_tags]
scanned_tags = [
%{apriltag_id: apriltag_id, location: location} | socket.assigns.scanned_tags
]
{:noreply,
socket
@@ -188,11 +203,12 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
location_id = String.to_integer(id)
expanded_locations = socket.assigns.expanded_locations
new_expanded = if MapSet.member?(expanded_locations, location_id) do
MapSet.delete(expanded_locations, location_id)
else
MapSet.put(expanded_locations, location_id)
end
new_expanded =
if MapSet.member?(expanded_locations, location_id) do
MapSet.delete(expanded_locations, location_id)
else
MapSet.put(expanded_locations, location_id)
end
{:noreply, assign(socket, :expanded_locations, new_expanded)}
end
@@ -203,19 +219,26 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
def handle_event("set_edit_apriltag_mode", %{"mode" => mode}, socket) do
# Clear the apriltag_id field when switching modes
form = case mode do
"remove" ->
socket.assigns.form
|> Phoenix.Component.to_form()
|> Map.put(:params, Map.put(socket.assigns.form.params || %{}, "apriltag_id", nil))
"keep" ->
current_id = socket.assigns.editing_location.apriltag_id
socket.assigns.form
|> Phoenix.Component.to_form()
|> Map.put(:params, Map.put(socket.assigns.form.params || %{}, "apriltag_id", current_id))
_ ->
socket.assigns.form
end
form =
case mode do
"remove" ->
socket.assigns.form
|> Phoenix.Component.to_form()
|> Map.put(:params, Map.put(socket.assigns.form.params || %{}, "apriltag_id", nil))
"keep" ->
current_id = socket.assigns.editing_location.apriltag_id
socket.assigns.form
|> Phoenix.Component.to_form()
|> Map.put(
:params,
Map.put(socket.assigns.form.params || %{}, "apriltag_id", current_id)
)
_ ->
socket.assigns.form
end
{:noreply,
socket
@@ -234,7 +257,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
{:noreply, put_flash(socket, :error, "Failed to get AprilTag URL")}
apriltag_url ->
filename = "#{location.name |> String.replace(" ", "_")}_AprilTag_#{location.apriltag_id}.svg"
filename =
"#{location.name |> String.replace(" ", "_")}_AprilTag_#{location.apriltag_id}.svg"
# Send file download to browser
{:noreply,
@@ -257,7 +281,7 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
Hierarchical.parent_select_options(
storage_locations,
editing_location_id,
&(&1.parent),
& &1.parent,
"No parent (Root location)"
)
end
@@ -267,11 +291,11 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
end
defp root_storage_locations(storage_locations) do
Hierarchical.root_entities(storage_locations, &(&1.parent_id))
Hierarchical.root_entities(storage_locations, & &1.parent_id)
end
defp child_storage_locations(storage_locations, parent_id) do
Hierarchical.child_entities(storage_locations, parent_id, &(&1.parent_id))
Hierarchical.child_entities(storage_locations, parent_id, & &1.parent_id)
end
defp count_components_in_location(location_id) do
@@ -291,46 +315,53 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
border_class = if assigns.depth > 0, do: "border-l-2 border-base-300 pl-6", else: ""
# Icon size and button size based on depth
{icon_size, button_size, text_size, title_tag} = case assigns.depth do
0 -> {"w-5 h-5", "p-2", "text-lg", "h3"}
1 -> {"w-4 h-4", "p-1.5", "text-base", "h4"}
_ -> {"w-3 h-3", "p-1", "text-sm", "h5"}
end
{icon_size, button_size, text_size, title_tag} =
case assigns.depth do
0 -> {"w-5 h-5", "p-2", "text-lg", "h3"}
1 -> {"w-4 h-4", "p-1.5", "text-base", "h4"}
_ -> {"w-3 h-3", "p-1", "text-sm", "h5"}
end
# Different icons based on level - QR code is always present for storage locations
icon_name = case assigns.depth do
0 -> "hero-building-office" # Shelf/Room
1 -> "hero-archive-box" # Drawer/Cabinet
_ -> "hero-cube" # Box/Container
end
icon_name =
case assigns.depth do
# Shelf/Room
0 -> "hero-building-office"
# Drawer/Cabinet
1 -> "hero-archive-box"
# Box/Container
_ -> "hero-cube"
end
children = child_storage_locations(assigns.storage_locations, assigns.location.id)
has_children = !Enum.empty?(children)
is_expanded = MapSet.member?(assigns.expanded_locations, assigns.location.id)
# Calculate component counts including descendants
{self_count, children_count, _total_count} = Hierarchical.count_with_descendants(
assigns.location.id,
assigns.storage_locations,
&(&1.parent_id),
&count_components_in_location/1
)
{self_count, children_count, _total_count} =
Hierarchical.count_with_descendants(
assigns.location.id,
assigns.storage_locations,
& &1.parent_id,
&count_components_in_location/1
)
# Format count display
count_display = Hierarchical.format_count_display(self_count, children_count, is_expanded)
assigns = assigns
|> assign(:margin_left, margin_left)
|> assign(:border_class, border_class)
|> assign(:icon_size, icon_size)
|> assign(:button_size, button_size)
|> assign(:text_size, text_size)
|> assign(:title_tag, title_tag)
|> assign(:icon_name, icon_name)
|> assign(:children, children)
|> assign(:has_children, has_children)
|> assign(:is_expanded, is_expanded)
|> assign(:count_display, count_display)
assigns =
assigns
|> assign(:margin_left, margin_left)
|> assign(:border_class, border_class)
|> assign(:icon_size, icon_size)
|> assign(:button_size, button_size)
|> assign(:text_size, text_size)
|> assign(:title_tag, title_tag)
|> assign(:icon_name, icon_name)
|> assign(:children, children)
|> assign(:has_children, has_children)
|> assign(:is_expanded, is_expanded)
|> assign(:count_display, count_display)
~H"""
<div class={[@border_class, @depth > 0 && "mt-4"]} style={"margin-left: #{@margin_left}px"}>
@@ -350,12 +381,16 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<% end %>
</button>
<% else %>
<div class="w-6"></div> <!-- Spacer for alignment -->
<div class="w-6"></div>
<!-- Spacer for alignment -->
<% end %>
<.icon name={@icon_name} class={"#{@icon_size} #{if @depth == 0, do: "text-primary", else: "text-base-content/60"} mt-0.5"} />
<!-- Content area - always starts at same vertical position -->
<.icon
name={@icon_name}
class={"#{@icon_size} #{if @depth == 0, do: "text-primary", else: "text-base-content/60"} mt-0.5"}
/>
<!-- Content area - always starts at same vertical position -->
<div class="flex-1">
<!-- Minimized view (default) -->
<%= unless @is_expanded do %>
@@ -408,8 +443,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div>
</div>
<% end %>
<!-- Expanded view -->
<!-- Expanded view -->
<%= if @is_expanded do %>
<div class="flex items-start justify-between">
<div class="flex-1">
@@ -468,8 +503,7 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
class="inline-flex items-center px-3 py-1.5 border border-base-300 rounded-md shadow-sm text-sm font-medium text-base-content bg-base-100 hover:bg-base-200 flex-shrink-0"
title="Download AprilTag"
>
<.icon name="hero-arrow-down-tray" class="w-4 h-4 mr-1.5" />
Download
<.icon name="hero-arrow-down-tray" class="w-4 h-4 mr-1.5" /> Download
</button>
<% end %>
<div class="flex items-center space-x-2">
@@ -495,11 +529,16 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div>
</div>
</div>
<!-- Render children recursively (only when expanded) -->
<!-- Render children recursively (only when expanded) -->
<%= if @is_expanded do %>
<%= for child <- @children do %>
<.location_item location={child} storage_locations={@storage_locations} expanded_locations={@expanded_locations} depth={@depth + 1} />
<.location_item
location={child}
storage_locations={@storage_locations}
expanded_locations={@expanded_locations}
depth={@depth + 1}
/>
<% end %>
<% end %>
</div>
@@ -552,8 +591,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div>
</div>
</div>
<!-- Add Location Modal -->
<!-- Add Location Modal -->
<%= if @show_add_form do %>
<div class="fixed inset-0 bg-base-content/50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border border-base-300 w-11/12 md:w-1/2 shadow-lg rounded-md bg-base-100">
@@ -589,7 +628,9 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div>
<div>
<label class="block text-sm font-medium text-base-content">AprilTag ID (Optional)</label>
<label class="block text-sm font-medium text-base-content">
AprilTag ID (Optional)
</label>
<div class="space-y-2">
<div class="flex items-center space-x-2">
<input
@@ -647,10 +688,12 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
class="w-32"
/>
<div class="text-xs text-base-content/60">
Available IDs: <%= length(@available_apriltag_ids) %> of 587
Available IDs: {length(@available_apriltag_ids)} of 587
<%= if length(@available_apriltag_ids) < 20 do %>
<br/>Next available: <%= @available_apriltag_ids |> Enum.take(10) |> Enum.join(", ") %>
<%= if length(@available_apriltag_ids) > 10, do: "..." %>
<br />Next available: {@available_apriltag_ids
|> Enum.take(10)
|> Enum.join(", ")}
{if length(@available_apriltag_ids) > 10, do: "..."}
<% end %>
</div>
</div>
@@ -678,8 +721,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div>
</div>
<% end %>
<!-- Edit Location Modal -->
<!-- Edit Location Modal -->
<%= if @show_edit_form do %>
<div class="fixed inset-0 bg-base-content/50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border border-base-300 w-11/12 md:w-1/2 shadow-lg rounded-md bg-base-100">
@@ -773,12 +816,14 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
class="w-32"
/>
<div class="text-xs text-base-content/60">
Available IDs: <%= length(@available_apriltag_ids) %> of 587
Available IDs: {length(@available_apriltag_ids)} of 587
</div>
</div>
<% end %>
<p class="text-xs text-base-content/60 mt-1">
Current: <%= if @editing_location.apriltag_id, do: "ID #{@editing_location.apriltag_id}", else: "None" %>
Current: {if @editing_location.apriltag_id,
do: "ID #{@editing_location.apriltag_id}",
else: "None"}
</p>
</div>
</div>
@@ -803,8 +848,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div>
</div>
<% end %>
<!-- AprilTag Scanner Modal -->
<!-- AprilTag Scanner Modal -->
<%= if @apriltag_scanner_open do %>
<div class="fixed inset-0 bg-base-content/30 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-10 mx-auto p-5 border border-base-300 w-11/12 md:w-1/2 shadow-lg rounded-md bg-base-100">
@@ -818,16 +863,20 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<.icon name="hero-x-mark" class="w-6 h-6" />
</button>
</div>
<!-- AprilTag Scanner Interface -->
<!-- AprilTag Scanner Interface -->
<div class="border-2 border-dashed border-base-300 rounded-lg p-6 text-center">
<.icon name="hero-qr-code" class="mx-auto h-12 w-12 text-base-content/50" />
<p class="mt-2 text-sm text-base-content/70">Camera AprilTag scanner would go here</p>
<p class="text-xs text-base-content/60 mt-1">In a real implementation, this would use JavaScript AprilTag detection</p>
<!-- Test buttons for demo -->
<p class="text-xs text-base-content/60 mt-1">
In a real implementation, this would use JavaScript AprilTag detection
</p>
<!-- Test buttons for demo -->
<div class="mt-4 space-y-2">
<p class="text-sm font-medium text-base-content/80">Test with sample AprilTag IDs:</p>
<p class="text-sm font-medium text-base-content/80">
Test with sample AprilTag IDs:
</p>
<button
phx-click="apriltag_scanned"
phx-value-apriltag_id="0"
@@ -848,8 +897,8 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</div>
</div>
<% end %>
<!-- Scanned Tags Display -->
<!-- Scanned Tags Display -->
<%= if length(@scanned_tags) > 0 do %>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div class="bg-green-50 border border-green-200 rounded-lg p-4">
@@ -863,26 +912,35 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
</button>
</div>
<div class="space-y-2">
<div :for={scan <- @scanned_tags} class="flex items-center justify-between bg-base-100 p-2 rounded border border-base-300">
<div
:for={scan <- @scanned_tags}
class="flex items-center justify-between bg-base-100 p-2 rounded border border-base-300"
>
<div>
<span class="font-medium text-base-content">{location_display_name(scan.location)}</span>
<span class="text-sm text-base-content/70 ml-2">(AprilTag ID {scan.apriltag_id})</span>
<span class="font-medium text-base-content">
{location_display_name(scan.location)}
</span>
<span class="text-sm text-base-content/70 ml-2">
(AprilTag ID {scan.apriltag_id})
</span>
</div>
<span class="text-xs text-green-600 bg-green-100 px-2 py-1 rounded">
Level <%= Hierarchical.compute_level(scan.location, &(&1.parent)) %>
Level {Hierarchical.compute_level(scan.location, & &1.parent)}
</span>
</div>
</div>
</div>
</div>
<% end %>
<!-- Storage Locations List -->
<!-- Storage Locations List -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="bg-base-100 shadow overflow-hidden sm:rounded-md">
<div class="px-6 py-4 border-b border-base-300">
<h2 class="text-lg font-medium text-base-content">Storage Location Hierarchy</h2>
<p class="text-sm text-base-content/60 mt-1">Manage your physical storage locations and AprilTags</p>
<p class="text-sm text-base-content/60 mt-1">
Manage your physical storage locations and AprilTags
</p>
</div>
<%= if Enum.empty?(@storage_locations) do %>
@@ -897,8 +955,7 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
phx-click="show_add_form"
class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700"
>
<.icon name="hero-plus" class="w-4 h-4 mr-2" />
Add Location
<.icon name="hero-plus" class="w-4 h-4 mr-2" /> Add Location
</button>
</div>
</div>
@@ -907,7 +964,12 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<!-- Recursive Storage Location Tree -->
<%= for location <- root_storage_locations(@storage_locations) do %>
<div class="px-6 py-4">
<.location_item location={location} storage_locations={@storage_locations} expanded_locations={@expanded_locations} depth={0} />
<.location_item
location={location}
storage_locations={@storage_locations}
expanded_locations={@expanded_locations}
depth={0}
/>
</div>
<% end %>
</div>