Packages
Alpha: Phoenix LiveView components for interactive Selecto query building and data exploration
Current section
Files
Jump to
Current section
Files
lib/selecto_components/views/graph/drill_down.ex
defmodule SelectoComponents.Views.Graph.DrillDown do
@moduledoc false
alias SelectoComponents.Form.ParamsState
alias SelectoComponents.SafeAtom
@drill_down_error_message "Could not drill down for that chart value. Try a different slice or bar."
def apply(socket, params) do
label = Map.get(params, "label")
graph_config = socket.assigns.view_config.views[:graph] || %{}
field_name = extract_chart_filter_field(socket, graph_config)
field_type = lookup_column_type(socket, field_name)
{filter_value, comp} = build_chart_filter_value(label, field_type)
if is_nil(filter_value) do
{:error, @drill_down_error_message}
else
current_view_mode = socket.assigns.view_config.view_mode
new_filter = build_filter_tuple(field_name, filter_value, comp)
updated_filters = socket.assigns.view_config.filters ++ [new_filter]
new_view_mode = determine_drill_down_view(socket, current_view_mode)
filters_map = build_filters_map(updated_filters)
view_mode_param = normalize_view_mode_param(new_view_mode)
current_params = socket.assigns[:used_params] || %{}
view_params =
current_params
|> Map.put("view_mode", view_mode_param)
|> Map.put("filters", filters_map)
|> Map.put_new("aggregate", %{})
|> Map.put_new("detail", %{})
|> Map.put_new("graph", %{})
updated_socket =
socket
|> Phoenix.Component.assign(
:view_config,
%{socket.assigns.view_config | view_mode: view_mode_param, filters: updated_filters}
)
updated_socket = ParamsState.view_from_params(view_params, updated_socket)
{:ok, updated_socket, view_params}
end
end
defp determine_drill_down_view(socket, current_view_mode) do
selected_view = SafeAtom.to_view_mode(current_view_mode)
{_, _, _, opt} =
Enum.find(socket.assigns.views, fn {id, _, _, _} -> id == selected_view end) ||
{:detail, nil, nil, %{}}
Map.get(opt, :drill_down, :detail)
end
defp normalize_view_mode_param(view_mode) when is_atom(view_mode), do: Atom.to_string(view_mode)
defp normalize_view_mode_param(view_mode) when is_binary(view_mode), do: view_mode
defp normalize_view_mode_param(_view_mode), do: "detail"
defp build_filter_tuple(field_name, filter_value, comp) do
new_filter_id = UUID.uuid4()
new_filter_map = %{
"filter" => field_name,
"value" => filter_value,
"comp" => comp,
"section" => "filters"
}
{new_filter_id, "filters", new_filter_map}
end
defp build_filters_map(updated_filters) do
Enum.reduce(updated_filters, %{}, fn
{id, "filters", filter_map}, acc ->
filter_with_defaults =
filter_map
|> Map.put_new("section", "filters")
|> Map.put_new("comp", "=")
Map.put(acc, id, filter_with_defaults)
_, acc ->
acc
end)
end
defp extract_chart_filter_field(socket, graph_config) do
case get_in(socket.assigns, [:selecto, :set, :x_axis_groups]) || [] do
[{_, {:field, field, _alias}} | _] when is_binary(field) -> field
[{_, {:field, field}} | _] when is_binary(field) -> field
[{_, field, _} | _] when is_binary(field) -> field
_ -> extract_graph_config_field(graph_config)
end
end
defp extract_graph_config_field(graph_config) do
case graph_config[:x_axis] || [] do
[{_id, field, _config} | _] when is_binary(field) -> field
[{_id, field, _config} | _] -> inspect(field)
_ -> "id"
end
end
defp lookup_column_type(socket, field_name) do
Enum.find_value(socket.assigns.columns, fn
{^field_name, _label, type} -> type
_ -> nil
end)
end
defp build_chart_filter_value(label, {:array, _}) do
value =
cond do
is_binary(label) and label != "" -> label
is_list(label) and Enum.all?(label, &is_binary/1) and label != [] -> hd(label)
true -> nil
end
{value, "contains"}
end
defp build_chart_filter_value(label, _field_type) do
{normalize_chart_label(label), "="}
end
defp normalize_chart_label(label) when is_binary(label), do: label
defp normalize_chart_label(label) when is_number(label), do: to_string(label)
defp normalize_chart_label(label) when is_atom(label), do: Atom.to_string(label)
defp normalize_chart_label(label) when is_list(label) do
cond do
label == [] -> ""
Enum.all?(label, &is_binary/1) -> Enum.join(label, ", ")
true -> inspect(label)
end
end
defp normalize_chart_label(label), do: inspect(label)
end