feat(overview): rework filter builder with editable chips and native date ranges

Editable filter chips, uniformly-sized value controls, working field-picker
search, and native date-range inputs with relative presets in place of the
vendored calendar — native inputs give year-jump, manual entry and keyboard
accessibility for free.
This commit is contained in:
Simon 2026-07-10 16:27:15 +02:00
parent c0d1550401
commit 8303a39041
23 changed files with 1905 additions and 565 deletions

View file

@ -63,6 +63,8 @@
date_filters={@date_filters}
payment_filter={@payment_filter}
payment_period={@payment_period}
suspended={@suspended}
stichtag={@stichtag}
/>
</div>
@ -471,7 +473,7 @@
/>
<span
:if={MvWeb.MemberLive.Index.payment_period_active?(@payment_period)}
class="badge badge-soft badge-info badge-sm shrink-0"
class="badge badge-soft badge-primary badge-sm font-normal shrink-0"
title={PaymentAging.period_tooltip(@payment_period)}
aria-label={PaymentAging.period_tooltip(@payment_period)}
data-testid="payment-period-badge"
@ -485,20 +487,28 @@
<% 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"}
<% anchor = "--pay-#{member.id}" %>
<%!-- The badge is a real navigation link (semantic, keyboard- and
right-click-friendly) into the member's payment history. Its hover/focus
tooltip is a native Popover-API element in the top layer, placed via CSS
anchor positioning (see the `payment-tip` class + the HoverPopover hook),
so it escapes the members table's `overflow` clipping without any custom
coordinate math. No tooltip renders when the member is fully paid. --%>
<.link
navigate={~p"/members/#{member.id}?tab=membership_fees"}
id={"payment-badge-#{member.id}"}
data-tooltip-id={count > 0 && tip_id}
phx-click={JS.navigate(~p"/members/#{member.id}?tab=membership_fees")}
class={["badge badge-soft badge-md", badge.color_class]}
style={count > 0 && "anchor-name: #{anchor}"}
data-variant={badge.variant}
phx-hook={count > 0 && "HoverPopover"}
data-popover-target={count > 0 && tip_id}
aria-details={count > 0 && tip_id}
aria-label={gettext("%{label} — open payment history", label: badge.label)}
data-testid="payment-badge"
>
<.icon name={badge.icon} class="size-4" />
{badge.label}
</button>
</.link>
<%= if count > 0 do %>
<% cycles = PaymentAging.open_cycles(member, @payment_period) %>
<% {shown, overflow} = PaymentAging.tooltip_cycles(cycles) %>
@ -506,7 +516,8 @@
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"
style={"position-anchor: #{anchor}"}
class="payment-tip 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">
@ -521,7 +532,8 @@
</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)}
<.icon name="hero-plus-circle" class="size-3.5" />
{gettext("%{count} more", count: overflow)}
</span>
</li>
</ul>