feat: implement filter logic for boolean ustom fields
This commit is contained in:
parent
b701b84260
commit
da9ec06e8e
2 changed files with 558 additions and 321 deletions
|
|
@ -768,6 +768,14 @@ defmodule MvWeb.MemberLive.Index do
|
|||
socket.assigns.show_current_cycle
|
||||
)
|
||||
|
||||
# Apply boolean custom field filters if set
|
||||
members =
|
||||
apply_boolean_custom_field_filters(
|
||||
members,
|
||||
socket.assigns.boolean_custom_field_filters,
|
||||
socket.assigns.all_custom_fields
|
||||
)
|
||||
|
||||
# Sort in memory if needed (for custom fields)
|
||||
members =
|
||||
if sort_after_load do
|
||||
|
|
@ -1279,7 +1287,166 @@ defmodule MvWeb.MemberLive.Index do
|
|||
values when is_list(values) ->
|
||||
Enum.find(values, fn cfv ->
|
||||
cfv.custom_field_id == custom_field.id or
|
||||
(cfv.custom_field && cfv.custom_field.id == custom_field.id)
|
||||
(match?(%{custom_field: %{id: _}}, cfv) && cfv.custom_field.id == custom_field.id)
|
||||
end)
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Extracts the boolean value from a member's custom field value.
|
||||
#
|
||||
# Handles different value formats:
|
||||
# - `%Ash.Union{value: value, type: :boolean}` - Extracts value from union
|
||||
# - Map format with `"type"` and `"value"` keys - Extracts from map
|
||||
# - Map format with `"_union_type"` and `"_union_value"` keys - Extracts from map
|
||||
#
|
||||
# Returns:
|
||||
# - `true` if the custom field value is boolean true
|
||||
# - `false` if the custom field value is boolean false
|
||||
# - `nil` if no custom field value exists, value is nil, or value is not boolean
|
||||
#
|
||||
# Examples:
|
||||
# get_boolean_custom_field_value(member, boolean_field) -> true
|
||||
# get_boolean_custom_field_value(member, non_existent_field) -> nil
|
||||
def get_boolean_custom_field_value(member, custom_field) do
|
||||
case get_custom_field_value(member, custom_field) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
cfv ->
|
||||
extract_boolean_value(cfv.value)
|
||||
end
|
||||
end
|
||||
|
||||
# Extracts boolean value from custom field value, handling different formats.
|
||||
#
|
||||
# Handles:
|
||||
# - `%Ash.Union{value: value, type: :boolean}` - Union struct format
|
||||
# - Map with `"type"` and `"value"` keys - JSONB map format
|
||||
# - Map with `"_union_type"` and `"_union_value"` keys - Alternative map format
|
||||
# - Direct boolean value - Primitive boolean
|
||||
#
|
||||
# Returns `true`, `false`, or `nil`.
|
||||
defp extract_boolean_value(%Ash.Union{value: value, type: :boolean}) do
|
||||
extract_boolean_value(value)
|
||||
end
|
||||
|
||||
defp extract_boolean_value(value) when is_map(value) do
|
||||
# Handle map format from JSONB
|
||||
type = Map.get(value, "type") || Map.get(value, "_union_type")
|
||||
val = Map.get(value, "value") || Map.get(value, "_union_value")
|
||||
|
||||
if type == "boolean" or type == :boolean do
|
||||
extract_boolean_value(val)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_boolean_value(value) when is_boolean(value), do: value
|
||||
defp extract_boolean_value(nil), do: nil
|
||||
defp extract_boolean_value(_), do: nil
|
||||
|
||||
# Applies boolean custom field filters to a list of members.
|
||||
#
|
||||
# Filters members based on boolean custom field values. Only members that match
|
||||
# ALL active filters (AND logic) are returned.
|
||||
#
|
||||
# Parameters:
|
||||
# - `members` - List of Member resources with loaded custom_field_values
|
||||
# - `filters` - Map of `%{custom_field_id_string => true | false}`
|
||||
# - `all_custom_fields` - List of all CustomField resources (for validation)
|
||||
#
|
||||
# Returns:
|
||||
# - Filtered list of members that match all active filters
|
||||
# - All members if filters map is empty
|
||||
# - Filters with non-existent custom field IDs are ignored
|
||||
#
|
||||
# Examples:
|
||||
# apply_boolean_custom_field_filters(members, %{"uuid-123" => true}, all_custom_fields) -> [member1, ...]
|
||||
# apply_boolean_custom_field_filters(members, %{}, all_custom_fields) -> members
|
||||
def apply_boolean_custom_field_filters(members, filters, _all_custom_fields)
|
||||
when map_size(filters) == 0 do
|
||||
members
|
||||
end
|
||||
|
||||
def apply_boolean_custom_field_filters(members, filters, all_custom_fields) do
|
||||
# Build a map of valid boolean custom field IDs (as strings) for quick lookup
|
||||
valid_custom_field_ids =
|
||||
all_custom_fields
|
||||
|> Enum.filter(&(&1.value_type == :boolean))
|
||||
|> MapSet.new(fn cf -> to_string(cf.id) end)
|
||||
|
||||
# Filter out invalid custom field IDs from filters
|
||||
valid_filters =
|
||||
Enum.filter(filters, fn {custom_field_id_str, _value} ->
|
||||
MapSet.member?(valid_custom_field_ids, custom_field_id_str)
|
||||
end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
# If no valid filters remain, return all members
|
||||
if map_size(valid_filters) == 0 do
|
||||
members
|
||||
else
|
||||
Enum.filter(members, fn member ->
|
||||
matches_all_filters?(member, valid_filters)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if a member matches all active boolean filters.
|
||||
#
|
||||
# A member matches a filter if:
|
||||
# - The filter value is `true` and the member's custom field value is `true`
|
||||
# - The filter value is `false` and the member's custom field value is `false`
|
||||
#
|
||||
# Members without a custom field value or with `nil` value do not match any filter.
|
||||
#
|
||||
# Returns `true` if all filters match, `false` otherwise.
|
||||
defp matches_all_filters?(member, filters) do
|
||||
Enum.all?(filters, fn {custom_field_id_str, filter_value} ->
|
||||
matches_filter?(member, custom_field_id_str, filter_value)
|
||||
end)
|
||||
end
|
||||
|
||||
# Checks if a member matches a specific boolean filter.
|
||||
#
|
||||
# Finds the custom field value by ID and checks if the member's boolean value
|
||||
# matches the filter value.
|
||||
#
|
||||
# Returns:
|
||||
# - `true` if the member's boolean value matches the filter value
|
||||
# - `false` if no custom field value exists (member is filtered out)
|
||||
# - `false` if value is nil or values don't match
|
||||
defp matches_filter?(member, custom_field_id_str, filter_value) do
|
||||
case find_custom_field_value_by_id(member, custom_field_id_str) do
|
||||
nil ->
|
||||
false
|
||||
|
||||
cfv ->
|
||||
boolean_value = extract_boolean_value(cfv.value)
|
||||
boolean_value == filter_value
|
||||
end
|
||||
end
|
||||
|
||||
# Finds a custom field value by custom field ID string.
|
||||
#
|
||||
# Searches through the member's custom_field_values to find one matching
|
||||
# the given custom field ID.
|
||||
#
|
||||
# Returns the CustomFieldValue or nil.
|
||||
defp find_custom_field_value_by_id(member, custom_field_id_str) do
|
||||
case member.custom_field_values do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
values when is_list(values) ->
|
||||
Enum.find(values, fn cfv ->
|
||||
to_string(cfv.custom_field_id) == custom_field_id_str or
|
||||
(match?(%{custom_field: %{id: _}}, cfv) &&
|
||||
to_string(cfv.custom_field.id) == custom_field_id_str)
|
||||
end)
|
||||
|
||||
_ ->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue