feat(elixir): complete storage location integration

This commit is contained in:
Schuwi
2025-09-14 15:32:16 +02:00
parent 9d090859e8
commit ad12ae2ec7
4 changed files with 34 additions and 8 deletions

View File

@@ -140,13 +140,13 @@ The application uses a simple password-based authentication system:
## ✅ Recently Implemented Features ## ✅ Recently Implemented Features
### Storage Location System Foundation 🚧 **PARTIALLY IMPLEMENTED** ### Storage Location System Foundation **COMPLETED**
- **Database Schema** ✅ Complete - Hierarchical storage locations with parent-child relationships - **Database Schema** ✅ Complete - Hierarchical storage locations with parent-child relationships
- **Storage Location CRUD** ✅ Complete - Full create, read, update, delete operations via web interface - **Storage Location CRUD** ✅ Complete - Full create, read, update, delete operations via web interface
- **QR Code Data Generation** ✅ Complete - Text-based QR codes with format `SL:{level}:{code}:{parent}` - **QR Code Data Generation** ✅ Complete - Text-based QR codes with format `SL:{level}:{code}:{parent}`
- **Hierarchical Organization** ✅ Complete - Unlimited nesting (shelf → drawer → box) - **Hierarchical Organization** ✅ Complete - Unlimited nesting (shelf → drawer → box)
- **Web Interface** ✅ Complete - Storage locations management page with navigation - **Web Interface** ✅ Complete - Storage locations management page with navigation
- **Component-Storage Integration** ❌ Missing - Linking components to storage locations not yet implemented correctly - **Component-Storage Integration** ✅ Complete - Components can now be assigned to storage locations via dropdown interface
### QR Code System - Still Needed 🚧 **NOT IMPLEMENTED** ### QR Code System - Still Needed 🚧 **NOT IMPLEMENTED**
- **Visual QR Code Generation** ❌ Missing - No actual QR code images are generated - **Visual QR Code Generation** ❌ Missing - No actual QR code images are generated

View File

@@ -170,6 +170,14 @@ defmodule ComponentsElixir.Inventory do
end end
end end
@doc """
Computes the path for a storage location (for display purposes).
"""
def compute_storage_location_path(nil), do: nil
def compute_storage_location_path(%StorageLocation{} = location) do
compute_path_for_single(location)
end
# Convert string keys to atoms for consistency # Convert string keys to atoms for consistency
defp normalize_string_keys(attrs) when is_map(attrs) do defp normalize_string_keys(attrs) when is_map(attrs) do
Enum.reduce(attrs, %{}, fn Enum.reduce(attrs, %{}, fn

View File

@@ -38,6 +38,7 @@ defmodule ComponentsElixir.Inventory.Component do
|> validate_number(:count, greater_than_or_equal_to: 0) |> validate_number(:count, greater_than_or_equal_to: 0)
|> validate_url(:datasheet_url) |> validate_url(:datasheet_url)
|> foreign_key_constraint(:category_id) |> foreign_key_constraint(:category_id)
|> foreign_key_constraint(:storage_location_id)
end end
@doc """ @doc """

View File

@@ -13,12 +13,14 @@ defmodule ComponentsElixirWeb.ComponentsLive do
{:ok, socket |> push_navigate(to: ~p"/login")} {:ok, socket |> push_navigate(to: ~p"/login")}
else else
categories = Inventory.list_categories() categories = Inventory.list_categories()
storage_locations = Inventory.list_storage_locations()
stats = Inventory.component_stats() stats = Inventory.component_stats()
{:ok, {:ok,
socket socket
|> assign(:session, session) |> assign(:session, session)
|> assign(:categories, categories) |> assign(:categories, categories)
|> assign(:storage_locations, storage_locations)
|> assign(:stats, stats) |> assign(:stats, stats)
|> assign(:search, "") |> assign(:search, "")
|> assign(:sort_criteria, "all_not_id") |> assign(:sort_criteria, "all_not_id")
@@ -293,6 +295,13 @@ defmodule ComponentsElixirWeb.ComponentsLive do
end) end)
end end
defp storage_location_options(storage_locations) do
[{"No storage location", nil}] ++
Enum.map(storage_locations, fn location ->
{location.path, location.id}
end)
end
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
@@ -439,8 +448,12 @@ defmodule ComponentsElixirWeb.ComponentsLive do
<.input field={@form[:keywords]} type="text" /> <.input field={@form[:keywords]} type="text" />
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-700">Position</label> <label class="block text-sm font-medium text-gray-700">Storage Location</label>
<.input field={@form[:position]} type="text" /> <.input
field={@form[:storage_location_id]}
type="select"
options={storage_location_options(@storage_locations)}
/>
</div> </div>
</div> </div>
@@ -552,8 +565,12 @@ defmodule ComponentsElixirWeb.ComponentsLive do
<.input field={@form[:keywords]} type="text" /> <.input field={@form[:keywords]} type="text" />
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-700">Position</label> <label class="block text-sm font-medium text-gray-700">Storage Location</label>
<.input field={@form[:position]} type="text" /> <.input
field={@form[:storage_location_id]}
type="select"
options={storage_location_options(@storage_locations)}
/>
</div> </div>
</div> </div>
@@ -684,9 +701,9 @@ defmodule ComponentsElixirWeb.ComponentsLive do
</p> </p>
</div> </div>
<div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0"> <div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0">
<%= if component.position do %> <%= if component.storage_location do %>
<p class="mr-6"> <p class="mr-6">
<span class="font-medium">Position:</span> {component.position} <span class="font-medium">Location:</span> {Inventory.compute_storage_location_path(component.storage_location)}
</p> </p>
<% end %> <% end %>
<p class="mr-6"> <p class="mr-6">