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.
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.
Counts each member's unpaid billing cycles that fall inside the selected
period. Cycle boundaries are member-relative — derived from each member's
fee-type interval — so "the current cycle" spans different calendar dates
per member.
Extract the cookie parser, export-payload builder and fee-status helper
into their own modules and remove the filter/sort/load helpers the
database pushdown made dead, so the overview LiveView stays a thin
coordinator over focused units.
The View dropdown drives row density and whether the Member and Address
fields render as composite cells or split into their underlying columns;
the column manager toggles visibility and resets to the curated default.
All choices persist per browser through the URL/session/cookie/global
chain, so a saved layout survives reloads without a per-account store.
Push the boolean and date custom-field predicates and custom-field
sorting down to JSONB expressions so the overview stops classifying
custom-field values in memory. A GIN index on custom_field_values.value
keeps the boolean membership predicates index-served.
Integrate current main (CSV import, GDPR join-form description, dependency and
tooling bumps) into the bulk-actions-dropdown feature. Gettext catalogs were
reconciled with mix gettext.extract --merge; the CHANGELOG Unreleased entries
of both sides were combined.
The growing row of bulk-action buttons above the member overview is replaced
by one "Aktionen" dropdown holding all four actions (open in email program,
copy addresses, export CSV, export PDF). With no selection the actions operate
on all — or the currently filtered — members; the email-program action is
disabled past a recipient cap, because the browser cannot reliably hand a very
long mailto over to the mail client. The trigger shows the active scope as a
badge: an emphasized count when members are selected, a muted "alle"/"gefiltert"
otherwise.
Dropdown openers were visually indistinguishable from ordinary buttons. A
trailing chevron now marks every dropdown trigger — both the shared
dropdown_menu component and the bespoke member-filter trigger — and an
optional badge slot lets a trigger show a status indicator beside its label.
LiveSession.assign_new_resources/2 is typed to return a Phoenix.Socket, which made the on_mount redirect type-incompatible. The authenticated-routes live_session already assigns current_user, so the guard reads it from socket.assigns directly. Also assign the locale into the socket actually used by the no-user redirect instead of discarding it.
Date.from_iso8601/1 returns {:error, reason}, so the with else clause matching a bare :error never fired and an invalid date raised a WithClauseError. Match the real date/calendar error reasons so the user sees the validation message.