- <.category_item category={category} categories={@categories} expanded_categories={@expanded_categories} depth={0} />
+ <.category_item
+ category={category}
+ categories={@categories}
+ expanded_categories={@expanded_categories}
+ depth={0}
+ />
<% end %>
diff --git a/lib/components_elixir_web/live/components_live.ex b/lib/components_elixir_web/live/components_live.ex
index f18bd5f..67d604f 100644
--- a/lib/components_elixir_web/live/components_live.ex
+++ b/lib/components_elixir_web/live/components_live.ex
@@ -139,7 +139,11 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|> push_patch(to: path)}
end
- def handle_event("storage_location_filter", %{"storage_location_id" => storage_location_id}, socket) do
+ def handle_event(
+ "storage_location_filter",
+ %{"storage_location_id" => storage_location_id},
+ socket
+ ) do
storage_location_id = String.to_integer(storage_location_id)
query_string = build_query_params_with_storage_location(socket, storage_location_id)
path = if query_string == "", do: "/", else: "/?" <> query_string
@@ -387,7 +391,10 @@ defmodule ComponentsElixirWeb.ComponentsLive do
|> save_uploaded_image(component_params)
|> save_uploaded_datasheet(socket)
- case Inventory.update_component_with_datasheet(socket.assigns.editing_component, updated_params) do
+ case Inventory.update_component_with_datasheet(
+ socket.assigns.editing_component,
+ updated_params
+ ) do
{:ok, _component} ->
{:noreply,
socket
@@ -496,7 +503,8 @@ defmodule ComponentsElixirWeb.ComponentsLive do
search: Map.get(overrides, :search, socket.assigns.search),
criteria: Map.get(overrides, :criteria, socket.assigns.sort_criteria),
category_id: Map.get(overrides, :category_id, socket.assigns.selected_category),
- storage_location_id: Map.get(overrides, :storage_location_id, socket.assigns.selected_storage_location)
+ storage_location_id:
+ Map.get(overrides, :storage_location_id, socket.assigns.selected_storage_location)
}
params
@@ -506,12 +514,14 @@ defmodule ComponentsElixirWeb.ComponentsLive do
defp parse_filter_id(nil), do: nil
defp parse_filter_id(""), do: nil
+
defp parse_filter_id(id) when is_binary(id) do
case Integer.parse(id) do
{int_id, ""} -> int_id
_ -> nil
end
end
+
defp parse_filter_id(id) when is_integer(id), do: id
defp build_query_params_with_category(socket, category_id) do
@@ -553,7 +563,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
end
defp category_options(categories) do
- Hierarchical.select_options(categories, &(&1.parent), "Select a category")
+ Hierarchical.select_options(categories, & &1.parent, "Select a category")
end
defp storage_location_display_name(location) do
@@ -561,7 +571,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
end
defp storage_location_options(storage_locations) do
- Hierarchical.select_options(storage_locations, &(&1.parent), "No storage location")
+ Hierarchical.select_options(storage_locations, & &1.parent, "No storage location")
end
@impl true
@@ -610,7 +620,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
-
+
@@ -1029,8 +1043,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
target="_blank"
class="inline-flex items-center text-primary hover:text-primary/80"
>
- <.icon name="hero-document-text" class="w-4 h-4 mr-1" />
- View PDF
+ <.icon name="hero-document-text" class="w-4 h-4 mr-1" /> View PDF
<% end %>
@@ -1097,7 +1110,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
<% end %>
-
+
@@ -1192,7 +1205,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
<% end %>
-
+
<%= if component.storage_location do %>
@@ -1427,7 +1448,7 @@ defmodule ComponentsElixirWeb.ComponentsLive do
<% end %>
-
+
<%= if component.keywords do %>
@@ -818,16 +863,20 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<.icon name="hero-x-mark" class="w-6 h-6" />
-
-
+
+
<.icon name="hero-qr-code" class="mx-auto h-12 w-12 text-base-content/50" />
Camera AprilTag scanner would go here
-
In a real implementation, this would use JavaScript AprilTag detection
-
-
+
+ In a real implementation, this would use JavaScript AprilTag detection
+
+
+
-
Test with sample AprilTag IDs:
+
+ Test with sample AprilTag IDs:
+
<% end %>
-
-
+
+
<%= if length(@scanned_tags) > 0 do %>
@@ -863,26 +912,35 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
-
+
- {location_display_name(scan.location)}
- (AprilTag ID {scan.apriltag_id})
+
+ {location_display_name(scan.location)}
+
+
+ (AprilTag ID {scan.apriltag_id})
+
- Level <%= Hierarchical.compute_level(scan.location, &(&1.parent)) %>
+ Level {Hierarchical.compute_level(scan.location, & &1.parent)}
<% end %>
-
-
+
+
Storage Location Hierarchy
-
Manage your physical storage locations and AprilTags
+
+ Manage your physical storage locations and AprilTags
+
<%= 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
@@ -907,7 +964,12 @@ defmodule ComponentsElixirWeb.StorageLocationsLive do
<%= for location <- root_storage_locations(@storage_locations) do %>
- <.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}
+ />
<% end %>
diff --git a/lib/mix/tasks/apriltag.generate_all.ex b/lib/mix/tasks/apriltag.generate_all.ex
index e69a7ae..5f0b886 100644
--- a/lib/mix/tasks/apriltag.generate_all.ex
+++ b/lib/mix/tasks/apriltag.generate_all.ex
@@ -25,9 +25,8 @@ defmodule Mix.Tasks.Apriltag.GenerateAll do
start_time = System.monotonic_time(:millisecond)
- result = ComponentsElixir.AprilTag.generate_all_apriltag_svgs(
- force_regenerate: force_regenerate
- )
+ result =
+ ComponentsElixir.AprilTag.generate_all_apriltag_svgs(force_regenerate: force_regenerate)
end_time = System.monotonic_time(:millisecond)
duration = end_time - start_time
@@ -39,6 +38,7 @@ defmodule Mix.Tasks.Apriltag.GenerateAll do
if result.errors > 0 do
IO.puts("\nErrors encountered:")
+
result.results
|> Enum.filter(&match?({:error, _, _}, &1))
|> Enum.each(fn {:error, id, reason} ->
diff --git a/priv/repo/migrations/20250914160354_migrate_qr_to_apriltag.exs b/priv/repo/migrations/20250914160354_migrate_qr_to_apriltag.exs
index 3a55848..baa1b2e 100644
--- a/priv/repo/migrations/20250914160354_migrate_qr_to_apriltag.exs
+++ b/priv/repo/migrations/20250914160354_migrate_qr_to_apriltag.exs
@@ -14,7 +14,9 @@ defmodule ComponentsElixir.Repo.Migrations.MigrateQrToApriltag do
create unique_index(:storage_locations, [:apriltag_id])
# Add constraint to ensure apriltag_id is in valid range (0-586 for tag36h11)
- create constraint(:storage_locations, :apriltag_id_range, check: "apriltag_id >= 0 AND apriltag_id <= 586")
+ create constraint(:storage_locations, :apriltag_id_range,
+ check: "apriltag_id >= 0 AND apriltag_id <= 586"
+ )
# Note: We keep qr_code_old for now in case we need to rollback
# It can be removed in a future migration after confirming everything works
diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
index 81a75a5..269e4c4 100644
--- a/priv/repo/seeds.exs
+++ b/priv/repo/seeds.exs
@@ -25,102 +25,208 @@ Repo.delete_all(Category)
Repo.delete_all(StorageLocation)
# Create categories
-{:ok, resistors} = Inventory.create_category(%{name: "Resistors", description: "Various types of resistors"})
-{:ok, capacitors} = Inventory.create_category(%{name: "Capacitors", description: "Electrolytic, ceramic, and film capacitors"})
-{:ok, semiconductors} = Inventory.create_category(%{name: "Semiconductors", description: "ICs, transistors, diodes"})
-{:ok, connectors} = Inventory.create_category(%{name: "Connectors", description: "Headers, terminals, plugs"})
+{:ok, resistors} =
+ Inventory.create_category(%{name: "Resistors", description: "Various types of resistors"})
+
+{:ok, capacitors} =
+ Inventory.create_category(%{
+ name: "Capacitors",
+ description: "Electrolytic, ceramic, and film capacitors"
+ })
+
+{:ok, semiconductors} =
+ Inventory.create_category(%{name: "Semiconductors", description: "ICs, transistors, diodes"})
+
+{:ok, connectors} =
+ Inventory.create_category(%{name: "Connectors", description: "Headers, terminals, plugs"})
# Create subcategories
-{:ok, _through_hole_resistors} = Inventory.create_category(%{
- name: "Through-hole",
- description: "Traditional leaded resistors",
- parent_id: resistors.id
-})
+{:ok, _through_hole_resistors} =
+ Inventory.create_category(%{
+ name: "Through-hole",
+ description: "Traditional leaded resistors",
+ parent_id: resistors.id
+ })
-{:ok, _smd_resistors} = Inventory.create_category(%{
- name: "SMD/SMT",
- description: "Surface mount resistors",
- parent_id: resistors.id
-})
+{:ok, _smd_resistors} =
+ Inventory.create_category(%{
+ name: "SMD/SMT",
+ description: "Surface mount resistors",
+ parent_id: resistors.id
+ })
-{:ok, _ceramic_caps} = Inventory.create_category(%{
- name: "Ceramic",
- description: "Ceramic disc and multilayer capacitors",
- parent_id: capacitors.id
-})
+{:ok, _ceramic_caps} =
+ Inventory.create_category(%{
+ name: "Ceramic",
+ description: "Ceramic disc and multilayer capacitors",
+ parent_id: capacitors.id
+ })
-{:ok, _electrolytic_caps} = Inventory.create_category(%{
- name: "Electrolytic",
- description: "Polarized electrolytic capacitors",
- parent_id: capacitors.id
-})
+{:ok, _electrolytic_caps} =
+ Inventory.create_category(%{
+ name: "Electrolytic",
+ description: "Polarized electrolytic capacitors",
+ parent_id: capacitors.id
+ })
# Create a DEEP category hierarchy to test fallback path (7+ levels)
-{:ok, deep_cat_1} = Inventory.create_category(%{name: "Level 1", description: "Deep hierarchy test", parent_id: resistors.id})
-{:ok, deep_cat_2} = Inventory.create_category(%{name: "Level 2", description: "Deep hierarchy test", parent_id: deep_cat_1.id})
-{:ok, deep_cat_3} = Inventory.create_category(%{name: "Level 3", description: "Deep hierarchy test", parent_id: deep_cat_2.id})
-{:ok, deep_cat_4} = Inventory.create_category(%{name: "Level 4", description: "Deep hierarchy test", parent_id: deep_cat_3.id})
-{:ok, deep_cat_5} = Inventory.create_category(%{name: "Level 5", description: "Deep hierarchy test", parent_id: deep_cat_4.id})
-{:ok, deep_cat_6} = Inventory.create_category(%{name: "Level 6", description: "Deep hierarchy test", parent_id: deep_cat_5.id})
-{:ok, deep_cat_7} = Inventory.create_category(%{name: "Level 7", description: "Deep hierarchy test - triggers fallback", parent_id: deep_cat_6.id})
+{:ok, deep_cat_1} =
+ Inventory.create_category(%{
+ name: "Level 1",
+ description: "Deep hierarchy test",
+ parent_id: resistors.id
+ })
+
+{:ok, deep_cat_2} =
+ Inventory.create_category(%{
+ name: "Level 2",
+ description: "Deep hierarchy test",
+ parent_id: deep_cat_1.id
+ })
+
+{:ok, deep_cat_3} =
+ Inventory.create_category(%{
+ name: "Level 3",
+ description: "Deep hierarchy test",
+ parent_id: deep_cat_2.id
+ })
+
+{:ok, deep_cat_4} =
+ Inventory.create_category(%{
+ name: "Level 4",
+ description: "Deep hierarchy test",
+ parent_id: deep_cat_3.id
+ })
+
+{:ok, deep_cat_5} =
+ Inventory.create_category(%{
+ name: "Level 5",
+ description: "Deep hierarchy test",
+ parent_id: deep_cat_4.id
+ })
+
+{:ok, deep_cat_6} =
+ Inventory.create_category(%{
+ name: "Level 6",
+ description: "Deep hierarchy test",
+ parent_id: deep_cat_5.id
+ })
+
+{:ok, deep_cat_7} =
+ Inventory.create_category(%{
+ name: "Level 7",
+ description: "Deep hierarchy test - triggers fallback",
+ parent_id: deep_cat_6.id
+ })
# Create storage locations
-{:ok, shelf_a} = Inventory.create_storage_location(%{name: "Shelf A", description: "Main electronics shelf"})
-{:ok, _shelf_b} = Inventory.create_storage_location(%{name: "Shelf B", description: "Components overflow shelf"})
+{:ok, shelf_a} =
+ Inventory.create_storage_location(%{name: "Shelf A", description: "Main electronics shelf"})
+
+{:ok, _shelf_b} =
+ Inventory.create_storage_location(%{name: "Shelf B", description: "Components overflow shelf"})
# Create drawers on Shelf A
-{:ok, drawer_a1} = Inventory.create_storage_location(%{
- name: "Drawer 1",
- description: "Resistors and capacitors",
- parent_id: shelf_a.id
-})
+{:ok, drawer_a1} =
+ Inventory.create_storage_location(%{
+ name: "Drawer 1",
+ description: "Resistors and capacitors",
+ parent_id: shelf_a.id
+ })
-{:ok, drawer_a2} = Inventory.create_storage_location(%{
- name: "Drawer 2",
- description: "Semiconductors and ICs",
- parent_id: shelf_a.id
-})
+{:ok, drawer_a2} =
+ Inventory.create_storage_location(%{
+ name: "Drawer 2",
+ description: "Semiconductors and ICs",
+ parent_id: shelf_a.id
+ })
# Create boxes in Drawer A1
-{:ok, box_a1_1} = Inventory.create_storage_location(%{
- name: "Box 1",
- description: "Through-hole resistors",
- parent_id: drawer_a1.id
-})
+{:ok, box_a1_1} =
+ Inventory.create_storage_location(%{
+ name: "Box 1",
+ description: "Through-hole resistors",
+ parent_id: drawer_a1.id
+ })
-{:ok, _box_a1_2} = Inventory.create_storage_location(%{
- name: "Box 2",
- description: "SMD resistors",
- parent_id: drawer_a1.id
-})
+{:ok, _box_a1_2} =
+ Inventory.create_storage_location(%{
+ name: "Box 2",
+ description: "SMD resistors",
+ parent_id: drawer_a1.id
+ })
-{:ok, box_a1_3} = Inventory.create_storage_location(%{
- name: "Box 3",
- description: "Ceramic capacitors",
- parent_id: drawer_a1.id
-})
+{:ok, box_a1_3} =
+ Inventory.create_storage_location(%{
+ name: "Box 3",
+ description: "Ceramic capacitors",
+ parent_id: drawer_a1.id
+ })
# Create boxes in Drawer A2
-{:ok, box_a2_1} = Inventory.create_storage_location(%{
- name: "Box 1",
- description: "Microcontrollers",
- parent_id: drawer_a2.id
-})
+{:ok, box_a2_1} =
+ Inventory.create_storage_location(%{
+ name: "Box 1",
+ description: "Microcontrollers",
+ parent_id: drawer_a2.id
+ })
-{:ok, _box_a2_2} = Inventory.create_storage_location(%{
- name: "Box 2",
- description: "Transistors and diodes",
- parent_id: drawer_a2.id
-})
+{:ok, _box_a2_2} =
+ Inventory.create_storage_location(%{
+ name: "Box 2",
+ description: "Transistors and diodes",
+ parent_id: drawer_a2.id
+ })
# Create a DEEP storage location hierarchy to test fallback path (7+ levels)
-{:ok, deep_loc_1} = Inventory.create_storage_location(%{name: "Deep Level 1", description: "Deep hierarchy test", parent_id: box_a1_3.id})
-{:ok, deep_loc_2} = Inventory.create_storage_location(%{name: "Deep Level 2", description: "Deep hierarchy test", parent_id: deep_loc_1.id})
-{:ok, deep_loc_3} = Inventory.create_storage_location(%{name: "Deep Level 3", description: "Deep hierarchy test", parent_id: deep_loc_2.id})
-{:ok, deep_loc_4} = Inventory.create_storage_location(%{name: "Deep Level 4", description: "Deep hierarchy test", parent_id: deep_loc_3.id})
-{:ok, deep_loc_5} = Inventory.create_storage_location(%{name: "Deep Level 5", description: "Deep hierarchy test", parent_id: deep_loc_4.id})
-{:ok, deep_loc_6} = Inventory.create_storage_location(%{name: "Deep Level 6", description: "Deep hierarchy test", parent_id: deep_loc_5.id})
-{:ok, deep_loc_7} = Inventory.create_storage_location(%{name: "Deep Level 7", description: "Deep hierarchy test - triggers fallback", parent_id: deep_loc_6.id})
+{:ok, deep_loc_1} =
+ Inventory.create_storage_location(%{
+ name: "Deep Level 1",
+ description: "Deep hierarchy test",
+ parent_id: box_a1_3.id
+ })
+
+{:ok, deep_loc_2} =
+ Inventory.create_storage_location(%{
+ name: "Deep Level 2",
+ description: "Deep hierarchy test",
+ parent_id: deep_loc_1.id
+ })
+
+{:ok, deep_loc_3} =
+ Inventory.create_storage_location(%{
+ name: "Deep Level 3",
+ description: "Deep hierarchy test",
+ parent_id: deep_loc_2.id
+ })
+
+{:ok, deep_loc_4} =
+ Inventory.create_storage_location(%{
+ name: "Deep Level 4",
+ description: "Deep hierarchy test",
+ parent_id: deep_loc_3.id
+ })
+
+{:ok, deep_loc_5} =
+ Inventory.create_storage_location(%{
+ name: "Deep Level 5",
+ description: "Deep hierarchy test",
+ parent_id: deep_loc_4.id
+ })
+
+{:ok, deep_loc_6} =
+ Inventory.create_storage_location(%{
+ name: "Deep Level 6",
+ description: "Deep hierarchy test",
+ parent_id: deep_loc_5.id
+ })
+
+{:ok, deep_loc_7} =
+ Inventory.create_storage_location(%{
+ name: "Deep Level 7",
+ description: "Deep hierarchy test - triggers fallback",
+ parent_id: deep_loc_6.id
+ })
# Create sample components
sample_components = [
@@ -162,7 +268,8 @@ sample_components = [
keywords: "microcontroller avr atmega328 arduino",
storage_location_id: box_a2_1.id,
count: 10,
- datasheet_url: "https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf",
+ datasheet_url:
+ "https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf",
category_id: semiconductors.id
},
%{
@@ -264,7 +371,11 @@ IO.puts("")
IO.puts("🎉 Database seeded successfully!")
IO.puts("📊 Summary:")
IO.puts(" Categories: #{length(Inventory.list_categories())}")
-IO.puts(" Storage Locations: #{length(Inventory.list_storage_locations())} (with auto-assigned AprilTags)")
+
+IO.puts(
+ " Storage Locations: #{length(Inventory.list_storage_locations())} (with auto-assigned AprilTags)"
+)
+
IO.puts(" Components: #{length(Inventory.list_components())}")
IO.puts("")
IO.puts("🏷️ AprilTag System:")
diff --git a/test/components_elixir_web/controllers/error_html_test.exs b/test/components_elixir_web/controllers/error_html_test.exs
index f98a9d0..44a8c86 100644
--- a/test/components_elixir_web/controllers/error_html_test.exs
+++ b/test/components_elixir_web/controllers/error_html_test.exs
@@ -9,6 +9,7 @@ defmodule ComponentsElixirWeb.ErrorHTMLTest do
end
test "renders 500.html" do
- assert render_to_string(ComponentsElixirWeb.ErrorHTML, "500", "html", []) == "Internal Server Error"
+ assert render_to_string(ComponentsElixirWeb.ErrorHTML, "500", "html", []) ==
+ "Internal Server Error"
end
end
diff --git a/test/components_elixir_web/controllers/error_json_test.exs b/test/components_elixir_web/controllers/error_json_test.exs
index ca3abdb..deb11b5 100644
--- a/test/components_elixir_web/controllers/error_json_test.exs
+++ b/test/components_elixir_web/controllers/error_json_test.exs
@@ -2,7 +2,9 @@ defmodule ComponentsElixirWeb.ErrorJSONTest do
use ComponentsElixirWeb.ConnCase, async: true
test "renders 404" do
- assert ComponentsElixirWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
+ assert ComponentsElixirWeb.ErrorJSON.render("404.json", %{}) == %{
+ errors: %{detail: "Not Found"}
+ }
end
test "renders 500" do