feat: category filter includes subcategories

This commit is contained in:
Schuwi
2025-09-19 20:28:12 +02:00
parent a0348c7df9
commit b68f8d92f7
2 changed files with 50 additions and 1 deletions

View File

@@ -203,6 +203,27 @@ defmodule ComponentsElixir.Inventory do
Category.changeset(category, attrs)
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
@doc """
@@ -219,7 +240,9 @@ defmodule ComponentsElixir.Inventory do
defp apply_component_filters(query, opts) do
Enum.reduce(opts, query, fn
{: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) ->
where(query, [c], c.storage_location_id == ^storage_location_id)

View File

@@ -143,6 +143,32 @@ defmodule ComponentsElixir.Inventory.Hierarchical do
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 """
Generates display name for entity including parent context.
For dropdown displays: "Parent > Child"