feat(overview): replace filter panel with add-filter builder and aging column
Swaps the unbounded vertical filter panel for a Polaris-style add-filter builder (searchable grouped field picker, type-aware value control, applied chips) and colour-codes the fees column with each member's period-scoped unpaid-cycle count.
This commit is contained in:
parent
7d1a71b1fd
commit
c547ca19af
35 changed files with 2817 additions and 3149 deletions
|
|
@ -21,17 +21,38 @@
|
|||
</:actions>
|
||||
</.header>
|
||||
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<%!-- Two-row overview toolbar (Option C): row 1 is search + column/view
|
||||
controls; row 2 is the dedicated filter row (add-filter + applied chips). --%>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<.live_component
|
||||
module={MvWeb.Components.SearchBarComponent}
|
||||
id="search-bar"
|
||||
query={@query}
|
||||
placeholder={gettext("Search...")}
|
||||
/>
|
||||
<div class="ml-auto flex items-center gap-4">
|
||||
<.live_component
|
||||
module={MvWeb.Components.FieldVisibilityDropdownComponent}
|
||||
id="field-visibility-dropdown"
|
||||
all_fields={@all_available_fields}
|
||||
custom_fields={@all_custom_fields}
|
||||
selected_fields={@user_field_selection}
|
||||
/>
|
||||
<.live_component
|
||||
module={MvWeb.Components.ViewSettingsDropdownComponent}
|
||||
id="view-settings-dropdown"
|
||||
density={@density}
|
||||
compact_member={@compact_member}
|
||||
member_include_email={@member_include_email}
|
||||
compact_address={@compact_address}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<.live_component
|
||||
module={MvWeb.Components.SearchBarComponent}
|
||||
id="search-bar"
|
||||
query={@query}
|
||||
placeholder={gettext("Search...")}
|
||||
/>
|
||||
<.live_component
|
||||
module={MvWeb.Components.MemberFilterComponent}
|
||||
module={MvWeb.Components.AddFilterBuilderComponent}
|
||||
id="member-filter"
|
||||
cycle_status_filter={@cycle_status_filter}
|
||||
groups={@groups}
|
||||
group_filters={@group_filters}
|
||||
fee_types={@fee_types}
|
||||
|
|
@ -40,57 +61,9 @@
|
|||
boolean_filters={@boolean_custom_field_filters}
|
||||
date_custom_fields={@date_custom_fields}
|
||||
date_filters={@date_filters}
|
||||
member_count={@total_count}
|
||||
payment_filter={@payment_filter}
|
||||
payment_period={@payment_period}
|
||||
/>
|
||||
<.tooltip
|
||||
content={
|
||||
gettext(
|
||||
"Sets whether the payment status filter and the membership fee status column use the last completed or the current payment cycle."
|
||||
)
|
||||
}
|
||||
position="top"
|
||||
>
|
||||
<.button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
class="gap-2"
|
||||
active={@show_current_cycle}
|
||||
aria-pressed={to_string(@show_current_cycle)}
|
||||
phx-click="toggle_cycle_view"
|
||||
data-testid="toggle-cycle-view"
|
||||
aria-label={
|
||||
if(@show_current_cycle,
|
||||
do: gettext("Current payment cycle"),
|
||||
else: gettext("Last payment cycle")
|
||||
)
|
||||
}
|
||||
>
|
||||
<.icon name="hero-arrow-path" class="h-5 w-5" />
|
||||
<span class="hidden sm:inline">
|
||||
{if(@show_current_cycle,
|
||||
do: gettext("Current payment cycle"),
|
||||
else: gettext("Last payment cycle")
|
||||
)}
|
||||
</span>
|
||||
</.button>
|
||||
</.tooltip>
|
||||
<div class="ml-auto flex items-center gap-4">
|
||||
<.live_component
|
||||
module={MvWeb.Components.FieldVisibilityDropdownComponent}
|
||||
id="field-visibility-dropdown"
|
||||
all_fields={@all_available_fields}
|
||||
custom_fields={@all_custom_fields}
|
||||
selected_fields={@user_field_selection}
|
||||
/>
|
||||
<.live_component
|
||||
module={MvWeb.Components.ViewSettingsDropdownComponent}
|
||||
id="view-settings-dropdown"
|
||||
density={@density}
|
||||
compact_member={@compact_member}
|
||||
member_include_email={@member_include_email}
|
||||
compact_address={@compact_address}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%!-- Polite live region: present on first render (before it is filled) so
|
||||
|
|
@ -131,7 +104,6 @@
|
|||
JS.push("select_row_and_navigate", value: %{id: member.id})
|
||||
end
|
||||
}
|
||||
row_tooltip={gettext("Click for member details")}
|
||||
row_selected?={fn member -> MapSet.member?(@selected_members, member.id) end}
|
||||
dynamic_cols={@dynamic_cols}
|
||||
sort_field={@sort_field}
|
||||
|
|
@ -484,17 +456,52 @@
|
|||
<:col
|
||||
:let={member}
|
||||
:if={:membership_fee_status in @member_fields_visible}
|
||||
label={gettext("Membership Fee Status")}
|
||||
label={MvWeb.MemberLive.Index.payment_column_label(@payment_period)}
|
||||
>
|
||||
<%= if badge = MembershipFeeStatus.format_cycle_status_badge(
|
||||
MembershipFeeStatus.get_cycle_status_for_member(member, @show_current_cycle)
|
||||
) do %>
|
||||
<.badge variant={badge.variant}>
|
||||
<.icon name={badge.icon} class="size-4" />
|
||||
{badge.label}
|
||||
</.badge>
|
||||
<% else %>
|
||||
<.empty_cell sr_text={gettext("No cycle")} />
|
||||
<% count = member.unpaid_cycle_count || 0 %>
|
||||
<% badge = PaymentAging.badge(count) %>
|
||||
<% tip_id = "payment-tip-#{member.id}" %>
|
||||
<button
|
||||
type="button"
|
||||
class={["badge badge-soft badge-md cursor-pointer", badge.color_class]}
|
||||
data-variant={badge.variant}
|
||||
phx-hook={count > 0 && "SortTooltip"}
|
||||
id={"payment-badge-#{member.id}"}
|
||||
data-tooltip-id={count > 0 && tip_id}
|
||||
phx-click={JS.navigate(~p"/members/#{member.id}?tab=membership_fees")}
|
||||
aria-label={gettext("%{label} — open payment history", label: badge.label)}
|
||||
data-testid="payment-badge"
|
||||
>
|
||||
<.icon name={badge.icon} class="size-4" />
|
||||
{badge.label}
|
||||
</button>
|
||||
<%= if count > 0 do %>
|
||||
<% cycles = PaymentAging.open_cycles(member, @payment_period) %>
|
||||
<% {shown, overflow} = PaymentAging.tooltip_cycles(cycles) %>
|
||||
<div
|
||||
id={tip_id}
|
||||
popover="manual"
|
||||
role="tooltip"
|
||||
class="m-0 w-fit rounded-box border border-base-300 bg-base-100 p-2 text-sm shadow-xl"
|
||||
data-testid="payment-tooltip"
|
||||
>
|
||||
<ul class="m-0 flex list-none flex-col items-stretch gap-1 p-0">
|
||||
<li :for={cycle <- shown} class="flex">
|
||||
<span class={[
|
||||
"badge badge-soft badge-sm w-full justify-start",
|
||||
PaymentAging.cycle_color_class(cycle.status)
|
||||
]}>
|
||||
<.icon name={PaymentAging.cycle_icon(cycle.status)} class="size-3.5" />
|
||||
{PaymentAging.short_period(cycle)}
|
||||
</span>
|
||||
</li>
|
||||
<li :if={overflow > 0} class="flex">
|
||||
<span class="badge badge-soft badge-sm badge-ghost w-full justify-start">
|
||||
{gettext("+%{count} more", count: overflow)}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:col
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue