fix: look for filter back to old look
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -97,14 +97,38 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("toggle_sidebar_filters", _params, socket) do
|
def handle_event("toggle_sidebar_filters", _params, socket) do
|
||||||
view_id = Atom.to_string(socket.assigns.workbench.active_view)
|
socket =
|
||||||
|
put_sidebar_filter_panel_state(socket, fn state ->
|
||||||
sidebar_filter_panels =
|
if state.visible do
|
||||||
Map.update(socket.assigns.sidebar_filter_panels, view_id, false, ¬ &1)
|
%{state | visible: false}
|
||||||
|
else
|
||||||
|
%{default_sidebar_filter_panel_state() | visible: true}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|> assign(:sidebar_filter_panels, sidebar_filter_panels)
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("toggle_sidebar_archive", _params, socket) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_sidebar_filter_panel_state(fn state -> %{state | archive_collapsed: not state.archive_collapsed} end)
|
||||||
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("toggle_sidebar_tags", _params, socket) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_sidebar_filter_panel_state(fn state -> %{state | tags_collapsed: not state.tags_collapsed} end)
|
||||||
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("toggle_sidebar_categories", _params, socket) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_sidebar_filter_panel_state(fn state -> %{state | categories_collapsed: not state.categories_collapsed} end)
|
||||||
|> reload_shell(socket.assigns.workbench)}
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -122,6 +146,20 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
|> reload_shell(socket.assigns.workbench)}
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("clear_sidebar_tags", _params, socket) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_sidebar_filters(fn filters -> Map.put(filters, :tags, []) end)
|
||||||
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("clear_sidebar_categories", _params, socket) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_sidebar_filters(fn filters -> Map.put(filters, :categories, []) end)
|
||||||
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("toggle_sidebar_tag", %{"tag" => tag}, socket) do
|
def handle_event("toggle_sidebar_tag", %{"tag" => tag}, socket) do
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
@@ -136,9 +174,31 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
|> reload_shell(socket.assigns.workbench)}
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("select_sidebar_year", %{"year" => year}, socket) do
|
||||||
|
parsed_year = parse_optional_integer(year)
|
||||||
|
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_sidebar_filter_panel_state(fn state ->
|
||||||
|
%{state |
|
||||||
|
archive_collapsed: false,
|
||||||
|
expanded_year: if(state.expanded_year == parsed_year, do: nil, else: parsed_year)
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|> put_sidebar_filters(fn filters ->
|
||||||
|
filters
|
||||||
|
|> Map.put(:year, parsed_year)
|
||||||
|
|> Map.put(:month, nil)
|
||||||
|
end)
|
||||||
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("select_sidebar_month", %{"year" => year, "month" => month}, socket) do
|
def handle_event("select_sidebar_month", %{"year" => year, "month" => month}, socket) do
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|
|> put_sidebar_filter_panel_state(fn state ->
|
||||||
|
%{state | archive_collapsed: false, expanded_year: parse_optional_integer(year)}
|
||||||
|
end)
|
||||||
|> put_sidebar_filters(fn filters ->
|
|> put_sidebar_filters(fn filters ->
|
||||||
filters
|
filters
|
||||||
|> Map.put(:year, parse_optional_integer(year))
|
|> Map.put(:year, parse_optional_integer(year))
|
||||||
@@ -150,6 +210,7 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
def handle_event("clear_sidebar_month", _params, socket) do
|
def handle_event("clear_sidebar_month", _params, socket) do
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|
|> put_sidebar_filter_panel_state(fn state -> %{state | archive_collapsed: false} end)
|
||||||
|> put_sidebar_filters(fn filters -> filters |> Map.put(:year, nil) |> Map.put(:month, nil) end)
|
|> put_sidebar_filters(fn filters -> filters |> Map.put(:year, nil) |> Map.put(:month, nil) end)
|
||||||
|> reload_shell(socket.assigns.workbench)}
|
|> reload_shell(socket.assigns.workbench)}
|
||||||
end
|
end
|
||||||
@@ -346,34 +407,29 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
assigns
|
assigns
|
||||||
|> assign(:sidebar_filters_config, filters)
|
|> assign(:sidebar_filters_config, filters)
|
||||||
|> assign(:selected_filters, selected)
|
|> assign(:selected_filters, selected)
|
||||||
|> assign(:filter_panel_visible, Map.get(filters, :filter_panel_visible, true))
|
|> assign(:filter_panel_visible, Map.get(filters, :filter_panel_visible, false))
|
||||||
|
|> assign(:archive_collapsed, Map.get(filters, :archive_collapsed, true))
|
||||||
|
|> assign(:tags_collapsed, Map.get(filters, :tags_collapsed, true))
|
||||||
|
|> assign(:categories_collapsed, Map.get(filters, :categories_collapsed, true))
|
||||||
|
|> assign(:expanded_year, Map.get(filters, :expanded_year))
|
||||||
|
|> assign(:year_groups, group_year_month_counts(Map.get(filters, :year_month_counts, [])))
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<form class="search-box" data-testid="sidebar-search-form" phx-change="update_sidebar_search">
|
<form class="search-box" data-testid="sidebar-search-form" phx-change="update_sidebar_search" phx-submit="update_sidebar_search">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="sidebar_filters[search]"
|
name="sidebar_filters[search]"
|
||||||
value={Map.get(@selected_filters, :search) || ""}
|
value={Map.get(@selected_filters, :search) || ""}
|
||||||
placeholder={translated(@sidebar_filters_config.search_placeholder)}
|
placeholder={translated(@sidebar_filters_config.search_placeholder)}
|
||||||
/>
|
/>
|
||||||
|
<button type="submit" title={translated("sidebar.search")}>
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M15.7 14.3l-4.2-4.2c-.2-.2-.5-.3-.8-.3.9-1.1 1.5-2.5 1.5-4C12.2 2.6 9.6 0 6.4 0S.6 2.6.6 5.8s2.6 5.8 5.8 5.8c1.5 0 2.9-.5 4-1.4 0 .3.1.6.3.8l4.2 4.2c.2.2.5.3.7.3s.5-.1.7-.3c.4-.4.4-1 0-1.4zm-9.3-4c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<%= if Map.get(@selected_filters, :search) do %>
|
<%= if Map.get(@selected_filters, :search) do %>
|
||||||
<button class="clear-search" data-testid="sidebar-clear-search" type="button" phx-click="clear_sidebar_search">×</button>
|
<button class="clear-search" data-testid="sidebar-clear-search" type="button" phx-click="clear_sidebar_search">×</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="sidebar-actions">
|
|
||||||
<button
|
|
||||||
class={[
|
|
||||||
"sidebar-action",
|
|
||||||
if(@filter_panel_visible, do: "active")
|
|
||||||
]}
|
|
||||||
data-testid="sidebar-filter-toggle"
|
|
||||||
type="button"
|
|
||||||
phx-click="toggle_sidebar_filters"
|
|
||||||
aria-label={translated(@sidebar_filters_config.toggle_filters_label)}
|
|
||||||
title={translated(@sidebar_filters_config.toggle_filters_label)}
|
|
||||||
>
|
|
||||||
≡
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<%= if Map.get(@sidebar_filters_config, :has_active_filters) do %>
|
<%= if Map.get(@sidebar_filters_config, :has_active_filters) do %>
|
||||||
@@ -388,79 +444,144 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @filter_panel_visible do %>
|
<%= if @filter_panel_visible do %>
|
||||||
<%= if Enum.any?(Map.get(@sidebar_filters_config, :year_month_counts, [])) do %>
|
<%= if Enum.any?(@year_groups) do %>
|
||||||
<div class="calendar-view">
|
<div class="calendar-view">
|
||||||
<div class="calendar-header">
|
<div
|
||||||
|
class={[
|
||||||
|
"calendar-header",
|
||||||
|
"collapsible-header",
|
||||||
|
if(@archive_collapsed, do: "collapsed", else: "expanded")
|
||||||
|
]}
|
||||||
|
data-testid="sidebar-filter-archive-header"
|
||||||
|
phx-click="toggle_sidebar_archive"
|
||||||
|
>
|
||||||
|
<span class="collapse-icon"><%= if @archive_collapsed, do: "▶", else: "▼" %></span>
|
||||||
<span><%= translated(@sidebar_filters_config.archive_label) %></span>
|
<span><%= translated(@sidebar_filters_config.archive_label) %></span>
|
||||||
<%= if Map.get(@selected_filters, :year) do %>
|
<%= if Map.get(@selected_filters, :year) do %>
|
||||||
<button class="clear-filter" type="button" phx-click="clear_sidebar_month">×</button>
|
<button class="clear-filter" type="button" phx-click="clear_sidebar_month" phx-stop-propagation>✕</button>
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div class="calendar-years">
|
|
||||||
<%= for entry <- Map.get(@sidebar_filters_config, :year_month_counts, []) do %>
|
|
||||||
<button
|
|
||||||
class={[
|
|
||||||
"calendar-month",
|
|
||||||
if(Map.get(@selected_filters, :year) == entry.year and Map.get(@selected_filters, :month) == entry.month, do: "selected")
|
|
||||||
]}
|
|
||||||
data-testid="sidebar-filter-month"
|
|
||||||
type="button"
|
|
||||||
phx-click="select_sidebar_month"
|
|
||||||
phx-value-year={entry.year}
|
|
||||||
phx-value-month={entry.month}
|
|
||||||
>
|
|
||||||
<span class="month-label"><%= ShellData.format_dashboard_month(entry.year, entry.month) %> <%= entry.year %></span>
|
|
||||||
<span class="month-count"><%= entry.count %></span>
|
|
||||||
</button>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
<%= unless @archive_collapsed do %>
|
||||||
|
<div class="calendar-years">
|
||||||
|
<%= for year_group <- @year_groups do %>
|
||||||
|
<div class="calendar-year">
|
||||||
|
<div
|
||||||
|
class={[
|
||||||
|
"calendar-year-header",
|
||||||
|
if(Map.get(@selected_filters, :year) == year_group.year and is_nil(Map.get(@selected_filters, :month)), do: "selected")
|
||||||
|
]}
|
||||||
|
phx-click="select_sidebar_year"
|
||||||
|
phx-value-year={year_group.year}
|
||||||
|
>
|
||||||
|
<span class="expand-icon"><%= if @expanded_year == year_group.year, do: "▼", else: "▶" %></span>
|
||||||
|
<span class="year-label"><%= year_group.year %></span>
|
||||||
|
<span class="year-count"><%= year_group.count %></span>
|
||||||
|
</div>
|
||||||
|
<%= if @expanded_year == year_group.year do %>
|
||||||
|
<div class="calendar-months">
|
||||||
|
<%= for month_entry <- year_group.months do %>
|
||||||
|
<button
|
||||||
|
class={[
|
||||||
|
"calendar-month",
|
||||||
|
if(Map.get(@selected_filters, :year) == year_group.year and Map.get(@selected_filters, :month) == month_entry.month, do: "selected")
|
||||||
|
]}
|
||||||
|
data-testid="sidebar-filter-month"
|
||||||
|
type="button"
|
||||||
|
phx-click="select_sidebar_month"
|
||||||
|
phx-value-year={year_group.year}
|
||||||
|
phx-value-month={month_entry.month}
|
||||||
|
>
|
||||||
|
<span class="month-label"><%= ShellData.format_dashboard_month(year_group.year, month_entry.month) %></span>
|
||||||
|
<span class="month-count"><%= month_entry.count %></span>
|
||||||
|
</button>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="filter-panel">
|
<div class="filter-panel">
|
||||||
<%= if Enum.any?(Map.get(@sidebar_filters_config, :available_tags, [])) do %>
|
<%= if Enum.any?(Map.get(@sidebar_filters_config, :available_tags, [])) do %>
|
||||||
<section class="filter-section">
|
<section class="filter-section">
|
||||||
<div class="filter-header"><%= translated(@sidebar_filters_config.tags_label) %></div>
|
<div
|
||||||
<div class="filter-chips">
|
class={[
|
||||||
<%= for tag <- Map.get(@sidebar_filters_config, :available_tags, []) do %>
|
"filter-header",
|
||||||
<button
|
"collapsible-header",
|
||||||
class={[
|
if(@tags_collapsed, do: "collapsed", else: "expanded")
|
||||||
"filter-chip",
|
]}
|
||||||
if(tag in Map.get(@selected_filters, :tags, []), do: "active")
|
data-testid="sidebar-filter-tags-header"
|
||||||
]}
|
phx-click="toggle_sidebar_tags"
|
||||||
data-testid="sidebar-filter-tag"
|
>
|
||||||
data-filter-tag={tag}
|
<span class="collapse-icon"><%= if @tags_collapsed, do: "▶", else: "▼" %></span>
|
||||||
type="button"
|
<span><%= translated(@sidebar_filters_config.tags_label) %></span>
|
||||||
phx-click="toggle_sidebar_tag"
|
<%= if Enum.any?(Map.get(@selected_filters, :tags, [])) do %>
|
||||||
phx-value-tag={tag}
|
<button class="clear-filter" type="button" phx-click="clear_sidebar_tags" phx-stop-propagation title={translated(@sidebar_filters_config.clear_tags_label)}>✕</button>
|
||||||
>
|
|
||||||
<%= tag %>
|
|
||||||
</button>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
<%= unless @tags_collapsed do %>
|
||||||
|
<div class="filter-chips">
|
||||||
|
<%= for tag <- Map.get(@sidebar_filters_config, :available_tags, []) do %>
|
||||||
|
<button
|
||||||
|
class={[
|
||||||
|
"filter-chip",
|
||||||
|
if(tag in Map.get(@selected_filters, :tags, []), do: "active"),
|
||||||
|
if(sidebar_filter_tag_color(@sidebar_filters_config, tag), do: "has-color")
|
||||||
|
]}
|
||||||
|
style={sidebar_filter_chip_style(@sidebar_filters_config, tag)}
|
||||||
|
data-testid="sidebar-filter-tag"
|
||||||
|
data-filter-tag={tag}
|
||||||
|
type="button"
|
||||||
|
phx-click="toggle_sidebar_tag"
|
||||||
|
phx-value-tag={tag}
|
||||||
|
>
|
||||||
|
<%= tag %>
|
||||||
|
</button>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</section>
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if Enum.any?(Map.get(@sidebar_filters_config, :available_categories, [])) do %>
|
<%= if Enum.any?(Map.get(@sidebar_filters_config, :available_categories, [])) do %>
|
||||||
<section class="filter-section">
|
<section class="filter-section">
|
||||||
<div class="filter-header"><%= translated(@sidebar_filters_config.categories_label) %></div>
|
<div
|
||||||
<div class="filter-chips">
|
class={[
|
||||||
<%= for category <- Map.get(@sidebar_filters_config, :available_categories, []) do %>
|
"filter-header",
|
||||||
<button
|
"collapsible-header",
|
||||||
class={[
|
if(@categories_collapsed, do: "collapsed", else: "expanded")
|
||||||
"filter-chip",
|
]}
|
||||||
if(category in Map.get(@selected_filters, :categories, []), do: "active")
|
data-testid="sidebar-filter-categories-header"
|
||||||
]}
|
phx-click="toggle_sidebar_categories"
|
||||||
data-testid="sidebar-filter-category"
|
>
|
||||||
data-filter-category={category}
|
<span class="collapse-icon"><%= if @categories_collapsed, do: "▶", else: "▼" %></span>
|
||||||
type="button"
|
<span><%= translated(@sidebar_filters_config.categories_label) %></span>
|
||||||
phx-click="toggle_sidebar_category"
|
<%= if Enum.any?(Map.get(@selected_filters, :categories, [])) do %>
|
||||||
phx-value-category={category}
|
<button class="clear-filter" type="button" phx-click="clear_sidebar_categories" phx-stop-propagation title={translated(@sidebar_filters_config.clear_categories_label)}>✕</button>
|
||||||
>
|
|
||||||
<%= category %>
|
|
||||||
</button>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
<%= unless @categories_collapsed do %>
|
||||||
|
<div class="filter-chips">
|
||||||
|
<%= for category <- Map.get(@sidebar_filters_config, :available_categories, []) do %>
|
||||||
|
<button
|
||||||
|
class={[
|
||||||
|
"filter-chip",
|
||||||
|
if(category in Map.get(@selected_filters, :categories, []), do: "active")
|
||||||
|
]}
|
||||||
|
data-testid="sidebar-filter-category"
|
||||||
|
data-filter-category={category}
|
||||||
|
type="button"
|
||||||
|
phx-click="toggle_sidebar_category"
|
||||||
|
phx-value-category={category}
|
||||||
|
>
|
||||||
|
<%= category %>
|
||||||
|
</button>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</section>
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@@ -925,13 +1046,46 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
filters = Map.get(sidebar_data, :filters)
|
filters = Map.get(sidebar_data, :filters)
|
||||||
|
|
||||||
if is_map(filters) and Map.get(filters, :enabled) do
|
if is_map(filters) and Map.get(filters, :enabled) do
|
||||||
filter_panel_visible = Map.get(socket.assigns.sidebar_filter_panels, view_id, true)
|
panel_state = sidebar_filter_panel_state(socket, view_id)
|
||||||
Map.put(sidebar_data, :filters, Map.put(filters, :filter_panel_visible, filter_panel_visible))
|
|
||||||
|
Map.put(sidebar_data, :filters, Map.merge(filters, %{
|
||||||
|
filter_panel_visible: panel_state.visible,
|
||||||
|
archive_collapsed: panel_state.archive_collapsed,
|
||||||
|
tags_collapsed: panel_state.tags_collapsed,
|
||||||
|
categories_collapsed: panel_state.categories_collapsed,
|
||||||
|
expanded_year: panel_state.expanded_year
|
||||||
|
}))
|
||||||
else
|
else
|
||||||
sidebar_data
|
sidebar_data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp sidebar_filter_panel_state(socket, view_id) do
|
||||||
|
default_state = default_sidebar_filter_panel_state()
|
||||||
|
|
||||||
|
case Map.get(socket.assigns.sidebar_filter_panels, view_id) do
|
||||||
|
state when is_map(state) -> Map.merge(default_state, state)
|
||||||
|
visible when is_boolean(visible) -> Map.put(default_state, :visible, visible)
|
||||||
|
_other -> default_state
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_sidebar_filter_panel_state(socket, updater) do
|
||||||
|
view_id = Atom.to_string(socket.assigns.workbench.active_view)
|
||||||
|
state = socket |> sidebar_filter_panel_state(view_id) |> updater.()
|
||||||
|
assign(socket, :sidebar_filter_panels, Map.put(socket.assigns.sidebar_filter_panels, view_id, state))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp default_sidebar_filter_panel_state do
|
||||||
|
%{
|
||||||
|
visible: false,
|
||||||
|
archive_collapsed: true,
|
||||||
|
tags_collapsed: true,
|
||||||
|
categories_collapsed: true,
|
||||||
|
expanded_year: nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp current_sidebar_filters(socket, view_id) do
|
defp current_sidebar_filters(socket, view_id) do
|
||||||
socket.assigns.sidebar_filters_by_view
|
socket.assigns.sidebar_filters_by_view
|
||||||
|> Map.get(view_id, %{})
|
|> Map.get(view_id, %{})
|
||||||
@@ -970,6 +1124,65 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
Map.put(filters, key, next_values)
|
Map.put(filters, key, next_values)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp group_year_month_counts(entries) do
|
||||||
|
entries
|
||||||
|
|> Enum.group_by(& &1.year)
|
||||||
|
|> Enum.map(fn {year, months} ->
|
||||||
|
%{
|
||||||
|
year: year,
|
||||||
|
count: Enum.reduce(months, 0, fn entry, acc -> acc + (entry.count || 0) end),
|
||||||
|
months: Enum.sort_by(months, &-&1.month)
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|> Enum.sort_by(&-&1.year)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sidebar_filter_tag_color(filters_config, tag) do
|
||||||
|
filters_config
|
||||||
|
|> Map.get(:available_tag_colors, %{})
|
||||||
|
|> Map.get(tag)
|
||||||
|
|> normalize_sidebar_filter_color()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sidebar_filter_chip_style(filters_config, tag) do
|
||||||
|
case sidebar_filter_tag_color(filters_config, tag) do
|
||||||
|
nil -> nil
|
||||||
|
color -> "background-color: #{color}; color: #{sidebar_filter_contrast_color(color)}; border-color: #{color};"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_sidebar_filter_color(nil), do: nil
|
||||||
|
defp normalize_sidebar_filter_color(""), do: nil
|
||||||
|
|
||||||
|
defp normalize_sidebar_filter_color("#" <> rest = color) when byte_size(rest) == 6 do
|
||||||
|
if String.match?(rest, ~r/\A[0-9a-fA-F]{6}\z/), do: color, else: nil
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_sidebar_filter_color(_color), do: nil
|
||||||
|
|
||||||
|
defp sidebar_filter_contrast_color("#" <> rgb) do
|
||||||
|
<<r::binary-size(2), g::binary-size(2), b::binary-size(2)>> = rgb
|
||||||
|
{red, _} = Integer.parse(r, 16)
|
||||||
|
{green, _} = Integer.parse(g, 16)
|
||||||
|
{blue, _} = Integer.parse(b, 16)
|
||||||
|
luminance = (red * 299 + green * 587 + blue * 114) / 1000
|
||||||
|
if luminance > 150, do: "#1e1e1e", else: "#ffffff"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sidebar_filter_contrast_color(_color), do: "#ffffff"
|
||||||
|
|
||||||
|
defp sidebar_filters_enabled?(sidebar_data) do
|
||||||
|
sidebar_data
|
||||||
|
|> Map.get(:filters)
|
||||||
|
|> then(&(is_map(&1) and Map.get(&1, :enabled, false)))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sidebar_filters_visible?(sidebar_data) do
|
||||||
|
sidebar_data
|
||||||
|
|> Map.get(:filters, %{})
|
||||||
|
|> Map.get(:filter_panel_visible, false)
|
||||||
|
end
|
||||||
|
|
||||||
defp normalize_filter_string(nil), do: nil
|
defp normalize_filter_string(nil), do: nil
|
||||||
|
|
||||||
defp normalize_filter_string(value) do
|
defp normalize_filter_string(value) do
|
||||||
|
|||||||
@@ -97,6 +97,25 @@
|
|||||||
<div class="sidebar-section">
|
<div class="sidebar-section">
|
||||||
<div class="sidebar-section-header">
|
<div class="sidebar-section-header">
|
||||||
<span><%= String.upcase(sidebar_header_label(@sidebar_header)) %></span>
|
<span><%= String.upcase(sidebar_header_label(@sidebar_header)) %></span>
|
||||||
|
<%= if sidebar_filters_enabled?(@sidebar_data) do %>
|
||||||
|
<div class="sidebar-actions">
|
||||||
|
<button
|
||||||
|
class={[
|
||||||
|
"sidebar-action",
|
||||||
|
if(sidebar_filters_visible?(@sidebar_data), do: "active")
|
||||||
|
]}
|
||||||
|
data-testid="sidebar-filter-toggle"
|
||||||
|
type="button"
|
||||||
|
phx-click="toggle_sidebar_filters"
|
||||||
|
aria-label={translated(Map.get(@sidebar_data.filters, :toggle_filters_label))}
|
||||||
|
title={translated(Map.get(@sidebar_data.filters, :toggle_filters_label))}
|
||||||
|
>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M6 12v-1h4v1H6zM4 8v-1h8v1H4zm-2-4v-1h12v1H2z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%= render_sidebar_filters(assigns) %>
|
<%= render_sidebar_filters(assigns) %>
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ defmodule BDS.UI.Sidebar do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp empty_view("posts"), do: posts_view_data([], [], %{}, false, empty_filter_params())
|
defp empty_view("posts"), do: posts_view_data([], [], %{}, false, empty_filter_params(), %{})
|
||||||
defp empty_view("pages"), do: posts_view_data([], [], %{}, true, empty_filter_params())
|
defp empty_view("pages"), do: posts_view_data([], [], %{}, true, empty_filter_params(), %{})
|
||||||
defp empty_view("media"), do: media_view_data([], [], empty_filter_params())
|
defp empty_view("media"), do: media_view_data([], [], empty_filter_params(), %{})
|
||||||
defp empty_view("scripts"), do: entity_list_view("Scripts", "Automation helpers", "scripts", [])
|
defp empty_view("scripts"), do: entity_list_view("Scripts", "Automation helpers", "scripts", [])
|
||||||
defp empty_view("templates"), do: entity_list_view("Templates", "Site rendering", "templates", [])
|
defp empty_view("templates"), do: entity_list_view("Templates", "Site rendering", "templates", [])
|
||||||
defp empty_view("tags"), do: tags_nav_view([])
|
defp empty_view("tags"), do: tags_nav_view([])
|
||||||
@@ -84,16 +84,19 @@ defmodule BDS.UI.Sidebar do
|
|||||||
defp posts_view(project_id, params, pages?) do
|
defp posts_view(project_id, params, pages?) do
|
||||||
posts = list_posts(project_id)
|
posts = list_posts(project_id)
|
||||||
translation_counts = translation_counts(project_id)
|
translation_counts = translation_counts(project_id)
|
||||||
|
tag_colors = tag_color_map(project_id)
|
||||||
filters = normalize_filter_params(params)
|
filters = normalize_filter_params(params)
|
||||||
base_posts = Enum.filter(posts, &(page_post?(&1) == pages?))
|
base_posts = Enum.filter(posts, &(page_post?(&1) == pages?))
|
||||||
filtered_posts = apply_post_filters(base_posts, filters)
|
filtered_posts = apply_post_filters(base_posts, filters)
|
||||||
|
|
||||||
posts_view_data(base_posts, filtered_posts, translation_counts, pages?, filters)
|
posts_view_data(base_posts, filtered_posts, translation_counts, pages?, filters, tag_colors)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp posts_view_data(base_posts, filtered_posts, translation_counts, pages?, filters) do
|
defp posts_view_data(base_posts, filtered_posts, translation_counts, pages?, filters, tag_colors) do
|
||||||
limited_posts = Enum.take(filtered_posts, filters.display_limit)
|
limited_posts = Enum.take(filtered_posts, filters.display_limit)
|
||||||
grouped_posts = group_posts(limited_posts)
|
grouped_posts = group_posts(limited_posts)
|
||||||
|
available_tags = available_tags(base_posts, & &1.tags)
|
||||||
|
available_categories = available_categories(base_posts, pages?)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
title: if(pages?, do: "Pages", else: "Posts"),
|
title: if(pages?, do: "Pages", else: "Posts"),
|
||||||
@@ -114,8 +117,9 @@ defmodule BDS.UI.Sidebar do
|
|||||||
results_for_label: "sidebar.resultsFor",
|
results_for_label: "sidebar.resultsFor",
|
||||||
no_results_label: "sidebar.noMatchingPosts",
|
no_results_label: "sidebar.noMatchingPosts",
|
||||||
year_month_counts: year_month_counts(base_posts, &post_filter_timestamp/1),
|
year_month_counts: year_month_counts(base_posts, &post_filter_timestamp/1),
|
||||||
available_tags: available_tags(base_posts, & &1.tags),
|
available_tags: available_tags,
|
||||||
available_categories: available_categories(base_posts, pages?),
|
available_tag_colors: Map.take(tag_colors, available_tags),
|
||||||
|
available_categories: available_categories,
|
||||||
max_items: @default_page_size,
|
max_items: @default_page_size,
|
||||||
display_limit: filters.display_limit,
|
display_limit: filters.display_limit,
|
||||||
loaded_count: length(limited_posts),
|
loaded_count: length(limited_posts),
|
||||||
@@ -140,14 +144,16 @@ defmodule BDS.UI.Sidebar do
|
|||||||
|
|
||||||
defp media_view(project_id, params) do
|
defp media_view(project_id, params) do
|
||||||
media_items = list_media(project_id)
|
media_items = list_media(project_id)
|
||||||
|
tag_colors = tag_color_map(project_id)
|
||||||
filters = normalize_filter_params(params)
|
filters = normalize_filter_params(params)
|
||||||
filtered_media = apply_media_filters(media_items, filters)
|
filtered_media = apply_media_filters(media_items, filters)
|
||||||
|
|
||||||
media_view_data(media_items, filtered_media, filters)
|
media_view_data(media_items, filtered_media, filters, tag_colors)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp media_view_data(base_media, filtered_media, filters) do
|
defp media_view_data(base_media, filtered_media, filters, tag_colors) do
|
||||||
limited_media = Enum.take(filtered_media, filters.display_limit)
|
limited_media = Enum.take(filtered_media, filters.display_limit)
|
||||||
|
available_tags = available_tags(base_media, & &1.tags)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
title: "Media",
|
title: "Media",
|
||||||
@@ -166,7 +172,8 @@ defmodule BDS.UI.Sidebar do
|
|||||||
results_for_label: "sidebar.resultsFor",
|
results_for_label: "sidebar.resultsFor",
|
||||||
no_results_label: "sidebar.noMediaFiles",
|
no_results_label: "sidebar.noMediaFiles",
|
||||||
year_month_counts: year_month_counts(base_media, &Map.get(&1, :updated_at)),
|
year_month_counts: year_month_counts(base_media, &Map.get(&1, :updated_at)),
|
||||||
available_tags: available_tags(base_media, & &1.tags),
|
available_tags: available_tags,
|
||||||
|
available_tag_colors: Map.take(tag_colors, available_tags),
|
||||||
available_categories: [],
|
available_categories: [],
|
||||||
max_items: @default_page_size,
|
max_items: @default_page_size,
|
||||||
display_limit: filters.display_limit,
|
display_limit: filters.display_limit,
|
||||||
@@ -493,6 +500,16 @@ defmodule BDS.UI.Sidebar do
|
|||||||
|> Enum.sort_by(&String.downcase/1)
|
|> Enum.sort_by(&String.downcase/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp tag_color_map(project_id) do
|
||||||
|
Repo.all(from tag in Tag, where: tag.project_id == ^project_id, select: {tag.name, tag.color})
|
||||||
|
|> Enum.reduce(%{}, fn {name, color}, acc ->
|
||||||
|
case String.trim(to_string(color || "")) do
|
||||||
|
"" -> acc
|
||||||
|
trimmed -> Map.put(acc, to_string(name), trimmed)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp filtered_categories(categories) do
|
defp filtered_categories(categories) do
|
||||||
Enum.reject(categories || [], &(normalize_term(&1) == @page_category))
|
Enum.reject(categories || [], &(normalize_term(&1) == @page_category))
|
||||||
end
|
end
|
||||||
|
|||||||
119
priv/ui/app.css
119
priv/ui/app.css
@@ -2460,18 +2460,6 @@ button {
|
|||||||
border-color: var(--vscode-focusBorder);
|
border-color: var(--vscode-focusBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box button,
|
|
||||||
.clear-filter,
|
|
||||||
.filter-status button,
|
|
||||||
.load-more-button,
|
|
||||||
.calendar-year-header,
|
|
||||||
.calendar-month,
|
|
||||||
.filter-header,
|
|
||||||
.filter-chip {
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box button[type="submit"] {
|
.search-box button[type="submit"] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 40px;
|
right: 40px;
|
||||||
@@ -2485,7 +2473,6 @@ button {
|
|||||||
|
|
||||||
.search-box button[type="submit"]:hover {
|
.search-box button[type="submit"]:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box .clear-search {
|
.search-box .clear-search {
|
||||||
@@ -2502,7 +2489,6 @@ button {
|
|||||||
|
|
||||||
.search-box .clear-search:hover {
|
.search-box .clear-search:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-view {
|
.calendar-view {
|
||||||
@@ -2522,42 +2508,29 @@ button {
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsible-header {
|
.calendar-header.collapsible-header {
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-header.collapsible-header,
|
|
||||||
.filter-header.collapsible-header {
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
margin: 0 -6px 8px -6px;
|
margin: 0 -6px 8px -6px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-header.collapsible-header:hover,
|
.calendar-header.collapsible-header:hover {
|
||||||
.filter-header.collapsible-header:hover {
|
|
||||||
background-color: var(--vscode-list-hoverBackground);
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-header.collapsible-header.collapsed,
|
.calendar-header.collapsible-header.collapsed {
|
||||||
.filter-header.collapsible-header.collapsed {
|
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapse-icon {
|
.calendar-header .collapse-icon {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
color: var(--vscode-descriptionForeground);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-header .clear-filter,
|
.calendar-header .clear-filter {
|
||||||
.filter-header .clear-filter {
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--vscode-descriptionForeground);
|
color: var(--vscode-descriptionForeground);
|
||||||
@@ -2565,11 +2538,9 @@ button {
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
margin-left: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-header .clear-filter:hover,
|
.calendar-header .clear-filter:hover {
|
||||||
.filter-header .clear-filter:hover {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2592,13 +2563,11 @@ button {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-year-header:hover,
|
.calendar-year-header:hover {
|
||||||
.calendar-month:hover {
|
|
||||||
background-color: var(--vscode-list-hoverBackground);
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-year-header.selected,
|
.calendar-year-header.selected {
|
||||||
.calendar-month.selected {
|
|
||||||
background-color: var(--vscode-list-activeSelectionBackground);
|
background-color: var(--vscode-list-activeSelectionBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2608,8 +2577,7 @@ button {
|
|||||||
width: 10px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.year-label,
|
.calendar-year-header .year-label {
|
||||||
.month-label {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2642,6 +2610,26 @@ button {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calendar-month:hover {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month.selected {
|
||||||
|
background-color: var(--vscode-list-activeSelectionBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month .month-count {
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-empty {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.month-count,
|
.month-count,
|
||||||
.sidebar-section-count {
|
.sidebar-section-count {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
@@ -2672,6 +2660,43 @@ button {
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-header.collapsible-header {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px 6px;
|
||||||
|
margin: 0 -6px 6px -6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header.collapsible-header:hover {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header.collapsible-header.collapsed {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header .collapse-icon {
|
||||||
|
font-size: 9px;
|
||||||
|
margin-right: 4px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header .clear-filter {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin-left: auto;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header .clear-filter:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-chips {
|
.filter-chips {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -2698,6 +2723,18 @@ button {
|
|||||||
color: var(--vscode-button-foreground);
|
color: var(--vscode-button-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-chip.has-color {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-chip.has-color:hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-chip.has-color.active {
|
||||||
|
box-shadow: 0 0 0 2px var(--vscode-focusBorder, #007fd4);
|
||||||
|
}
|
||||||
|
|
||||||
.filter-status {
|
.filter-status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
alias BDS.Posts.Post
|
alias BDS.Posts.Post
|
||||||
alias BDS.Projects
|
alias BDS.Projects
|
||||||
alias BDS.Repo
|
alias BDS.Repo
|
||||||
|
alias BDS.Tags
|
||||||
|
|
||||||
@endpoint BDS.Desktop.Endpoint
|
@endpoint BDS.Desktop.Endpoint
|
||||||
|
|
||||||
@@ -218,15 +219,37 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
test "sidebar filters and load more are server-driven", %{project: project} do
|
test "sidebar filters and load more are server-driven", %{project: project} do
|
||||||
seed_sidebar_posts(project.id)
|
seed_sidebar_posts(project.id)
|
||||||
|
|
||||||
|
assert {:ok, _tag} = Tags.create_tag(%{project_id: project.id, name: "tech", color: "#112233"})
|
||||||
|
|
||||||
{:ok, view, html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
{:ok, view, html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
assert html =~ ~s(data-testid="sidebar-search-form")
|
assert html =~ ~s(data-testid="sidebar-search-form")
|
||||||
assert html =~ ~s(data-testid="sidebar-filter-toggle")
|
assert html =~ ~s(data-testid="sidebar-filter-toggle")
|
||||||
assert html =~ ~s(data-testid="sidebar-filter-tag")
|
assert html =~ ~s(class="sidebar-section-header")
|
||||||
|
assert html =~ ~s(class="sidebar-actions")
|
||||||
assert html =~ ~s(data-testid="sidebar-load-more")
|
assert html =~ ~s(data-testid="sidebar-load-more")
|
||||||
|
refute html =~ ~s(data-testid="sidebar-filter-tag")
|
||||||
assert html =~ "Alpha Post"
|
assert html =~ "Alpha Post"
|
||||||
refute html =~ "Overflow Post"
|
refute html =~ "Overflow Post"
|
||||||
|
|
||||||
|
html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='sidebar-filter-toggle']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert html =~ ~s(class="calendar-header collapsible-header collapsed")
|
||||||
|
assert html =~ ~s(class="filter-header collapsible-header collapsed")
|
||||||
|
refute html =~ ~s(class="calendar-year-header")
|
||||||
|
refute html =~ ~s(data-testid="sidebar-filter-tag")
|
||||||
|
|
||||||
|
html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='sidebar-filter-tags-header']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert html =~ ~s(class="filter-chip has-color")
|
||||||
|
assert html =~ ~s(data-testid="sidebar-filter-tag")
|
||||||
|
|
||||||
html =
|
html =
|
||||||
view
|
view
|
||||||
|> form("[data-testid='sidebar-search-form']", %{sidebar_filters: %{search: "Alpha"}})
|
|> form("[data-testid='sidebar-search-form']", %{sidebar_filters: %{search: "Alpha"}})
|
||||||
@@ -354,8 +377,8 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
|> render_click()
|
|> render_click()
|
||||||
|
|
||||||
refute html =~ ~s(class="panel-shell is-hidden")
|
refute html =~ ~s(class="panel-shell is-hidden")
|
||||||
assert html =~ ~s(class="panel-tab active")
|
assert html =~ ~s(<button class="panel-tab active" type="button" phx-click="select_panel_tab" phx-value-tab="tasks">)
|
||||||
assert html =~ "No background tasks running"
|
assert html =~ ~s(class="task-list") or html =~ "No background tasks running"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp seed_sidebar_posts(project_id) do
|
defp seed_sidebar_posts(project_id) do
|
||||||
|
|||||||
Reference in New Issue
Block a user