feat: sticky memberstable header
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
49fd2181a7
commit
e5a6003ace
8 changed files with 411 additions and 324 deletions
|
|
@ -90,302 +90,311 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<.table
|
||||
id="members"
|
||||
rows={@members}
|
||||
row_id={fn member -> "row-#{member.id}" end}
|
||||
row_click={fn member -> 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}
|
||||
sort_order={@sort_order}
|
||||
<%!-- On desktop (lg:), only the table area scrolls; header and filters stay visible. On mobile, normal flow. --%>
|
||||
<div
|
||||
class="lg:max-h-[calc(100vh-14rem)] lg:overflow-auto min-h-0"
|
||||
data-testid="members-table-scroll"
|
||||
role="region"
|
||||
aria-label={gettext("Members table")}
|
||||
>
|
||||
|
||||
<.table
|
||||
id="members"
|
||||
rows={@members}
|
||||
sticky_header={true}
|
||||
row_id={fn member -> "row-#{member.id}" end}
|
||||
row_click={fn member -> 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}
|
||||
sort_order={@sort_order}
|
||||
>
|
||||
|
||||
<!-- <:col :let={member} label="Id">{member.id}</:col> -->
|
||||
<:col
|
||||
:let={member}
|
||||
col_click={&MvWeb.MemberLive.Index.checkbox_column_click/1}
|
||||
label={
|
||||
~H"""
|
||||
<:col
|
||||
:let={member}
|
||||
col_click={&MvWeb.MemberLive.Index.checkbox_column_click/1}
|
||||
label={
|
||||
~H"""
|
||||
<.input
|
||||
type="checkbox"
|
||||
name="select_all"
|
||||
phx-click="select_all"
|
||||
checked={MapSet.equal?(@selected_members, @members |> Enum.map(& &1.id) |> MapSet.new())}
|
||||
aria-label={gettext("Select all members")}
|
||||
role="checkbox"
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
<.input
|
||||
type="checkbox"
|
||||
name="select_all"
|
||||
phx-click="select_all"
|
||||
checked={MapSet.equal?(@selected_members, @members |> Enum.map(& &1.id) |> MapSet.new())}
|
||||
aria-label={gettext("Select all members")}
|
||||
name={member.id}
|
||||
checked={MapSet.member?(@selected_members, member.id)}
|
||||
aria-label={gettext("Select member")}
|
||||
role="checkbox"
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
<.input
|
||||
type="checkbox"
|
||||
name={member.id}
|
||||
checked={MapSet.member?(@selected_members, member.id)}
|
||||
aria-label={gettext("Select member")}
|
||||
role="checkbox"
|
||||
/>
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:first_name in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_first_name}
|
||||
field={:first_name}
|
||||
label={gettext("First name")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.first_name}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:last_name in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_last_name}
|
||||
field={:last_name}
|
||||
label={gettext("Last name")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.last_name}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:email in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_email}
|
||||
field={:email}
|
||||
label={gettext("Email")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.email}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:join_date in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_join_date}
|
||||
field={:join_date}
|
||||
label={gettext("Join Date")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{MvWeb.MemberLive.Index.format_date(member.join_date)}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:exit_date in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_exit_date}
|
||||
field={:exit_date}
|
||||
label={gettext("Exit Date")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{MvWeb.MemberLive.Index.format_date(member.exit_date)}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:notes in @member_fields_visible}
|
||||
label={gettext("Notes")}
|
||||
>
|
||||
{member.notes}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:city in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_city}
|
||||
field={:city}
|
||||
label={gettext("City")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.city}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:street in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_street}
|
||||
field={:street}
|
||||
label={gettext("Street")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.street}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:house_number in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_house_number}
|
||||
field={:house_number}
|
||||
label={gettext("House Number")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.house_number}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:postal_code in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_postal_code}
|
||||
field={:postal_code}
|
||||
label={gettext("Postal Code")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.postal_code}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:membership_fee_start_date in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_membership_fee_start_date}
|
||||
field={:membership_fee_start_date}
|
||||
label={gettext("Membership Fee Start Date")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{MvWeb.MemberLive.Index.format_date(member.membership_fee_start_date)}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:membership_fee_type in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_membership_fee_type}
|
||||
field={:membership_fee_type}
|
||||
label={gettext("Fee Type")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
<%= if member.membership_fee_type do %>
|
||||
{member.membership_fee_type.name}
|
||||
<% else %>
|
||||
<span class="text-base-content/50">—</span>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:membership_fee_status in @member_fields_visible}
|
||||
label={gettext("Membership Fee Status")}
|
||||
>
|
||||
<%= if badge = MvWeb.MemberLive.Index.MembershipFeeStatus.format_cycle_status_badge(
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:first_name in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_first_name}
|
||||
field={:first_name}
|
||||
label={gettext("First name")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.first_name}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:last_name in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_last_name}
|
||||
field={:last_name}
|
||||
label={gettext("Last name")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.last_name}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:email in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_email}
|
||||
field={:email}
|
||||
label={gettext("Email")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.email}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:join_date in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_join_date}
|
||||
field={:join_date}
|
||||
label={gettext("Join Date")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{MvWeb.MemberLive.Index.format_date(member.join_date)}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:exit_date in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_exit_date}
|
||||
field={:exit_date}
|
||||
label={gettext("Exit Date")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{MvWeb.MemberLive.Index.format_date(member.exit_date)}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:notes in @member_fields_visible}
|
||||
label={gettext("Notes")}
|
||||
>
|
||||
{member.notes}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:city in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_city}
|
||||
field={:city}
|
||||
label={gettext("City")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.city}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:street in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_street}
|
||||
field={:street}
|
||||
label={gettext("Street")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.street}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:house_number in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_house_number}
|
||||
field={:house_number}
|
||||
label={gettext("House Number")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.house_number}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:postal_code in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_postal_code}
|
||||
field={:postal_code}
|
||||
label={gettext("Postal Code")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{member.postal_code}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:membership_fee_start_date in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_membership_fee_start_date}
|
||||
field={:membership_fee_start_date}
|
||||
label={gettext("Membership Fee Start Date")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
{MvWeb.MemberLive.Index.format_date(member.membership_fee_start_date)}
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:membership_fee_type in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_membership_fee_type}
|
||||
field={:membership_fee_type}
|
||||
label={gettext("Fee Type")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
<%= if member.membership_fee_type do %>
|
||||
{member.membership_fee_type.name}
|
||||
<% else %>
|
||||
<span class="text-base-content/50">—</span>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:membership_fee_status in @member_fields_visible}
|
||||
label={gettext("Membership Fee Status")}
|
||||
>
|
||||
<%= if badge = MvWeb.MemberLive.Index.MembershipFeeStatus.format_cycle_status_badge(
|
||||
MvWeb.MemberLive.Index.MembershipFeeStatus.get_cycle_status_for_member(member, @show_current_cycle)
|
||||
) do %>
|
||||
<span class={["badge", badge.color]}>
|
||||
<.icon name={badge.icon} class="size-4" />
|
||||
{badge.label}
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="badge badge-ghost">{gettext("No cycle")}</span>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:groups in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_groups}
|
||||
field={:groups}
|
||||
label={gettext("Groups")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
<%= for group <- (member.groups || []) do %>
|
||||
<span
|
||||
class="badge badge-outline badge-primary"
|
||||
aria-label={gettext("Member of group %{name}", name: group.name)}
|
||||
>
|
||||
{group.name}
|
||||
</span>
|
||||
<% end %>
|
||||
<%= if (member.groups || []) == [] do %>
|
||||
<span class="text-base-content/50">—</span>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:action :let={member}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/members/#{member}"} data-testid="member-show-link">
|
||||
{gettext("Show")}
|
||||
</.link>
|
||||
</div>
|
||||
</:action>
|
||||
</.table>
|
||||
<span class={["badge", badge.color]}>
|
||||
<.icon name={badge.icon} class="size-4" />
|
||||
{badge.label}
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="badge badge-ghost">{gettext("No cycle")}</span>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:col
|
||||
:let={member}
|
||||
:if={:groups in @member_fields_visible}
|
||||
label={
|
||||
~H"""
|
||||
<.live_component
|
||||
module={MvWeb.Components.SortHeaderComponent}
|
||||
id={:sort_groups}
|
||||
field={:groups}
|
||||
label={gettext("Groups")}
|
||||
sort_field={@sort_field}
|
||||
sort_order={@sort_order}
|
||||
/>
|
||||
"""
|
||||
}
|
||||
>
|
||||
<%= for group <- (member.groups || []) do %>
|
||||
<span
|
||||
class="badge badge-outline badge-primary"
|
||||
aria-label={gettext("Member of group %{name}", name: group.name)}
|
||||
>
|
||||
{group.name}
|
||||
</span>
|
||||
<% end %>
|
||||
<%= if (member.groups || []) == [] do %>
|
||||
<span class="text-base-content/50">—</span>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:action :let={member}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/members/#{member}"} data-testid="member-show-link">
|
||||
{gettext("Show")}
|
||||
</.link>
|
||||
</div>
|
||||
</:action>
|
||||
</.table>
|
||||
</div>
|
||||
</Layouts.app>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue