feat: category filter includes subcategories
This commit is contained in:
@@ -203,6 +203,27 @@ defmodule ComponentsElixir.Inventory do
|
|||||||
Category.changeset(category, attrs)
|
Category.changeset(category, attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets all category IDs that are descendants of the given category ID, including the category itself.
|
||||||
|
This is used for filtering components by category and all its subcategories.
|
||||||
|
Returns an empty list if the category doesn't exist.
|
||||||
|
|
||||||
|
Note: This implementation loads all categories into memory for traversal, which is efficient
|
||||||
|
for typical category tree sizes (hundreds of categories). For very large category trees,
|
||||||
|
a recursive CTE query could be used instead.
|
||||||
|
"""
|
||||||
|
def get_category_and_descendant_ids(category_id) when is_integer(category_id) do
|
||||||
|
categories = list_categories()
|
||||||
|
|
||||||
|
# Verify the category exists before getting descendants
|
||||||
|
case Enum.find(categories, &(&1.id == category_id)) do
|
||||||
|
nil -> []
|
||||||
|
_category -> ComponentsElixir.Inventory.Hierarchical.descendant_ids(categories, category_id, &(&1.parent_id))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_category_and_descendant_ids(_), do: []
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@@ -219,7 +240,9 @@ defmodule ComponentsElixir.Inventory do
|
|||||||
defp apply_component_filters(query, opts) do
|
defp apply_component_filters(query, opts) do
|
||||||
Enum.reduce(opts, query, fn
|
Enum.reduce(opts, query, fn
|
||||||
{:category_id, category_id}, query when not is_nil(category_id) ->
|
{:category_id, category_id}, query when not is_nil(category_id) ->
|
||||||
where(query, [c], c.category_id == ^category_id)
|
# Get the category and all its descendant category IDs
|
||||||
|
category_ids = get_category_and_descendant_ids(category_id)
|
||||||
|
where(query, [c], c.category_id in ^category_ids)
|
||||||
|
|
||||||
{:storage_location_id, storage_location_id}, query when not is_nil(storage_location_id) ->
|
{:storage_location_id, storage_location_id}, query when not is_nil(storage_location_id) ->
|
||||||
where(query, [c], c.storage_location_id == ^storage_location_id)
|
where(query, [c], c.storage_location_id == ^storage_location_id)
|
||||||
|
|||||||
@@ -143,6 +143,32 @@ defmodule ComponentsElixir.Inventory.Hierarchical do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets all descendant IDs for a given entity ID, including the entity itself.
|
||||||
|
This recursively finds all children, grandchildren, etc.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
iex> categories = [
|
||||||
|
...> %{id: 1, parent_id: nil},
|
||||||
|
...> %{id: 2, parent_id: 1},
|
||||||
|
...> %{id: 3, parent_id: 2},
|
||||||
|
...> %{id: 4, parent_id: 1}
|
||||||
|
...> ]
|
||||||
|
iex> Hierarchical.descendant_ids(categories, 1, &(&1.parent_id))
|
||||||
|
[1, 2, 3, 4]
|
||||||
|
"""
|
||||||
|
def descendant_ids(entities, entity_id, parent_id_accessor_fn) do
|
||||||
|
[entity_id | get_descendant_ids_recursive(entities, entity_id, parent_id_accessor_fn)]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_descendant_ids_recursive(entities, parent_id, parent_id_accessor_fn) do
|
||||||
|
children = child_entities(entities, parent_id, parent_id_accessor_fn)
|
||||||
|
|
||||||
|
Enum.flat_map(children, fn child ->
|
||||||
|
[child.id | get_descendant_ids_recursive(entities, child.id, parent_id_accessor_fn)]
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Generates display name for entity including parent context.
|
Generates display name for entity including parent context.
|
||||||
For dropdown displays: "Parent > Child"
|
For dropdown displays: "Parent > Child"
|
||||||
|
|||||||
Reference in New Issue
Block a user