feat(elixir): storage location system
This commit is contained in:
208
lib/components_elixir_web/live/storage_locations_live.ex
Normal file
208
lib/components_elixir_web/live/storage_locations_live.ex
Normal file
@@ -0,0 +1,208 @@
|
||||
defmodule ComponentsElixirWeb.StorageLocationsLive do
|
||||
@moduledoc """
|
||||
LiveView for managing storage locations and QR codes.
|
||||
"""
|
||||
use ComponentsElixirWeb, :live_view
|
||||
|
||||
alias ComponentsElixir.Inventory
|
||||
alias ComponentsElixir.Inventory.StorageLocation
|
||||
alias ComponentsElixir.QRCode
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
socket =
|
||||
socket
|
||||
|> assign(:storage_locations, list_storage_locations())
|
||||
|> assign(:form, to_form(%{}))
|
||||
|> assign(:show_form, false)
|
||||
|> assign(:edit_location, nil)
|
||||
|> assign(:qr_scanner_open, false)
|
||||
|> assign(:scanned_codes, [])
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_params(params, _url, socket) do
|
||||
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
|
||||
end
|
||||
|
||||
defp apply_action(socket, :index, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "Storage Locations")
|
||||
|> assign(:storage_location, %StorageLocation{})
|
||||
end
|
||||
|
||||
defp apply_action(socket, :new, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "New Storage Location")
|
||||
|> assign(:storage_location, %StorageLocation{})
|
||||
|> assign(:show_form, true)
|
||||
end
|
||||
|
||||
defp apply_action(socket, :edit, %{"id" => id}) do
|
||||
location = Inventory.get_storage_location!(id)
|
||||
|
||||
socket
|
||||
|> assign(:page_title, "Edit Storage Location")
|
||||
|> assign(:storage_location, location)
|
||||
|> assign(:edit_location, location)
|
||||
|> assign(:show_form, true)
|
||||
|> assign(:form, to_form(Inventory.change_storage_location(location)))
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("new", _params, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:show_form, true)
|
||||
|> assign(:storage_location, %StorageLocation{})
|
||||
|> assign(:edit_location, nil)
|
||||
|> assign(:form, to_form(Inventory.change_storage_location(%StorageLocation{})))}
|
||||
end
|
||||
|
||||
def handle_event("cancel", _params, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:show_form, false)
|
||||
|> assign(:edit_location, nil)
|
||||
|> push_patch(to: ~p"/storage_locations")}
|
||||
end
|
||||
|
||||
def handle_event("validate", %{"storage_location" => params}, socket) do
|
||||
# Normalize parent_id for validation too
|
||||
normalized_params =
|
||||
case Map.get(params, "parent_id") do
|
||||
"" -> Map.put(params, "parent_id", nil)
|
||||
value -> Map.put(params, "parent_id", value)
|
||||
end
|
||||
|
||||
changeset =
|
||||
case socket.assigns.edit_location do
|
||||
nil -> Inventory.change_storage_location(%StorageLocation{}, normalized_params)
|
||||
location -> Inventory.change_storage_location(location, normalized_params)
|
||||
end
|
||||
|
||||
{:noreply, assign(socket, :form, to_form(changeset, action: :validate))}
|
||||
end
|
||||
|
||||
def handle_event("save", %{"storage_location" => params}, socket) do
|
||||
# Normalize parent_id for consistency
|
||||
normalized_params =
|
||||
case Map.get(params, "parent_id") do
|
||||
"" -> Map.put(params, "parent_id", nil)
|
||||
value -> Map.put(params, "parent_id", value)
|
||||
end
|
||||
|
||||
case socket.assigns.edit_location do
|
||||
nil -> create_storage_location(socket, normalized_params)
|
||||
location -> update_storage_location(socket, location, normalized_params)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("delete", %{"id" => id}, socket) do
|
||||
location = Inventory.get_storage_location!(id)
|
||||
|
||||
case Inventory.delete_storage_location(location) do
|
||||
{:ok, _} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Storage location deleted successfully")
|
||||
|> assign(:storage_locations, list_storage_locations())}
|
||||
|
||||
{:error, _} ->
|
||||
{:noreply, put_flash(socket, :error, "Unable to delete storage location")}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("open_qr_scanner", _params, socket) do
|
||||
{:noreply, assign(socket, :qr_scanner_open, true)}
|
||||
end
|
||||
|
||||
def handle_event("close_qr_scanner", _params, socket) do
|
||||
{:noreply, assign(socket, :qr_scanner_open, false)}
|
||||
end
|
||||
|
||||
def handle_event("qr_scanned", %{"code" => code}, socket) do
|
||||
case QRCode.parse_qr_data(code) do
|
||||
{:ok, parsed} ->
|
||||
case Inventory.get_storage_location_by_qr_code(parsed.code) do
|
||||
nil ->
|
||||
{:noreply, put_flash(socket, :error, "Storage location not found for QR code: #{code}")}
|
||||
|
||||
location ->
|
||||
scanned_codes = [%{code: code, location: location} | socket.assigns.scanned_codes]
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:scanned_codes, scanned_codes)
|
||||
|> put_flash(:info, "Scanned: #{location.path}")}
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
{:noreply, put_flash(socket, :error, "Invalid QR code: #{reason}")}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("clear_scanned", _params, socket) do
|
||||
{:noreply, assign(socket, :scanned_codes, [])}
|
||||
end
|
||||
|
||||
defp create_storage_location(socket, params) do
|
||||
case Inventory.create_storage_location(params) do
|
||||
{:ok, _location} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Storage location created successfully")
|
||||
|> assign(:show_form, false)
|
||||
|> assign(:storage_locations, list_storage_locations())}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign(socket, :form, to_form(changeset))}
|
||||
end
|
||||
end
|
||||
|
||||
defp update_storage_location(socket, location, params) do
|
||||
case Inventory.update_storage_location(location, params) do
|
||||
{:ok, _location} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Storage location updated successfully")
|
||||
|> assign(:show_form, false)
|
||||
|> assign(:edit_location, nil)
|
||||
|> assign(:storage_locations, list_storage_locations())
|
||||
|> push_patch(to: ~p"/storage_locations")}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign(socket, :form, to_form(changeset))}
|
||||
end
|
||||
end
|
||||
|
||||
defp list_storage_locations do
|
||||
Inventory.list_storage_locations()
|
||||
end
|
||||
|
||||
defp format_level(level) do
|
||||
case level do
|
||||
0 -> "Shelf"
|
||||
1 -> "Drawer"
|
||||
2 -> "Box"
|
||||
n -> "Level #{n}"
|
||||
end
|
||||
end
|
||||
|
||||
# Function to get parent options for select dropdown
|
||||
defp parent_options(current_location) do
|
||||
locations = Inventory.list_storage_locations()
|
||||
|
||||
# Filter out the current location if provided (to prevent self-parent)
|
||||
filtered_locations = case current_location do
|
||||
nil -> locations
|
||||
%{id: current_id} -> Enum.filter(locations, fn loc -> loc.id != current_id end)
|
||||
_ -> locations
|
||||
end
|
||||
|
||||
filtered_locations
|
||||
|> Enum.map(fn location -> {"#{location.name} (#{location.level})", location.id} end)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user