feat(elixir): complete storage location integration
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 """
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user