Packages

phoenix_kit

1.7.70
1.7.211 1.7.210 1.7.209 1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules newsletters web list_members.html.heex
Raw

lib/modules/newsletters/web/list_members.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={@page_title}
current_path={@url_path}
project_title={@project_title}
>
<div class="container flex-col mx-auto px-4 py-6">
<.admin_page_header
back={Routes.path("/admin/newsletters/lists")}
title={@list.name}
subtitle={"#{@list.subscriber_count} active subscribers"}
/>
<%!-- Confirm Modal --%>
<%= if @show_confirm_modal do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg mb-4">{@confirm_title}</h3>
<p class="mb-4">{@confirm_message}</p>
<div class="modal-action">
<button phx-click="hide_confirm" class="btn btn-outline">Cancel</button>
<button phx-click="confirm_action" class="btn btn-error">{@confirm_title}</button>
</div>
</div>
</div>
<% end %>
<%!-- Add Members --%>
<div class="card bg-base-100 shadow-sm mb-6">
<div class="card-body">
<div class="flex items-center justify-between mb-3">
<h3 class="card-title text-base">Add Members</h3>
<button
phx-click="show_confirm"
phx-value-action="add_all_users"
class="btn btn-sm btn-outline"
>
<.icon name="hero-user-group" class="w-4 h-4" /> Add All Users
</button>
</div>
<fieldset class="fieldset">
<input
type="text"
name="query"
value={@search_query}
placeholder="Search users by email or name..."
class="input w-full"
phx-change="search_users"
phx-debounce="300"
/>
</fieldset>
<%= if length(@search_results) > 0 do %>
<div class="mt-2 border border-base-300 rounded-lg divide-y divide-base-300">
<%= for user <- @search_results do %>
<div class="flex items-center justify-between px-3 py-2">
<div>
<span class="font-medium text-sm">{user.email}</span>
<%= if user.username do %>
<span class="text-xs text-base-content/60 ml-2">@{user.username}</span>
<% end %>
</div>
<button
phx-click="add_member"
phx-value-user-uuid={user.uuid}
class="btn btn-xs btn-primary"
>
<.icon name="hero-plus" class="w-3 h-3" /> Add
</button>
</div>
<% end %>
</div>
<% end %>
<%= if @search_query != "" and length(@search_results) == 0 do %>
<p class="text-sm text-base-content/60 mt-2">
No users found matching "{@search_query}"
</p>
<% end %>
</div>
</div>
<%!-- Filters --%>
<div class="bg-base-200 rounded-lg p-4 mb-6">
<div class="flex gap-2 items-center">
<span class="text-sm font-medium">Filter by status:</span>
<select
phx-change="filter_status"
name="status"
class="select select-sm"
>
<option value="" selected={@status_filter == ""}>All</option>
<option value="active" selected={@status_filter == "active"}>Active</option>
<option value="unsubscribed" selected={@status_filter == "unsubscribed"}>
Unsubscribed
</option>
</select>
</div>
</div>
<%!-- Members Table (desktop) --%>
<%= if length(@members) > 0 do %>
<div class="hidden md:block">
<.table_default variant="zebra" size="sm" class="w-full">
<.table_default_header>
<.table_default_row>
<.table_default_header_cell>Email</.table_default_header_cell>
<.table_default_header_cell>Status</.table_default_header_cell>
<.table_default_header_cell>Subscribed At</.table_default_header_cell>
<.table_default_header_cell>Actions</.table_default_header_cell>
</.table_default_row>
</.table_default_header>
<.table_default_body>
<%= for member <- @members do %>
<.table_default_row>
<.table_default_cell class="font-mono text-xs">
<%= if member.user do %>
{member.user.email}
<% else %>
<span class="text-base-content/50">(unknown)</span>
<% end %>
</.table_default_cell>
<.table_default_cell>
<%= if member.status == "active" do %>
<span class="badge badge-sm badge-success">Active</span>
<% else %>
<span class="badge badge-sm badge-ghost">Unsubscribed</span>
<% end %>
</.table_default_cell>
<.table_default_cell class="text-xs">
<%= if member.subscribed_at do %>
{Calendar.strftime(member.subscribed_at, "%Y-%m-%d %H:%M")}
<% end %>
</.table_default_cell>
<.table_default_cell>
<div class="flex gap-1">
<%= if member.status == "active" do %>
<button
phx-click="show_confirm"
phx-value-action="unsubscribe"
phx-value-uuid={member.uuid}
class="btn btn-xs btn-outline btn-warning"
title="Unsubscribe"
>
<.icon
name="hero-arrow-left-on-rectangle"
class="w-3 h-3 hidden sm:inline"
/>
<span class="sm:hidden whitespace-nowrap">Unsubscribe</span>
</button>
<% end %>
<button
phx-click="show_confirm"
phx-value-action="remove"
phx-value-uuid={member.uuid}
class="btn btn-xs btn-outline btn-error"
title="Remove"
>
<.icon name="hero-trash" class="w-3 h-3 hidden sm:inline" />
<span class="sm:hidden whitespace-nowrap">Remove</span>
</button>
</div>
</.table_default_cell>
</.table_default_row>
<% end %>
</.table_default_body>
</.table_default>
</div>
<%!-- Members Cards (mobile) --%>
<div class="md:hidden mb-6">
<div class="space-y-3">
<%= for member <- @members do %>
<div class="card card-border card-sm bg-base-100">
<div class="card-body gap-3">
<%!-- Header: email + status --%>
<div class="flex items-start justify-between gap-2">
<span class="font-medium text-sm truncate">
<%= if member.user do %>
{member.user.email}
<% else %>
<span class="text-base-content/50">(unknown)</span>
<% end %>
</span>
<%= if member.status == "active" do %>
<span class="badge badge-sm badge-success">Active</span>
<% else %>
<span class="badge badge-sm badge-ghost">Unsubscribed</span>
<% end %>
</div>
<%!-- Body: key/value grid --%>
<div class="grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
<div class="text-base-content/60">Subscribed At</div>
<div>
<%= if member.subscribed_at do %>
{Calendar.strftime(member.subscribed_at, "%Y-%m-%d %H:%M")}
<% else %>
-
<% end %>
</div>
</div>
<%!-- Footer: actions --%>
<div class="card-actions justify-end pt-1 border-t border-base-200">
<%= if member.status == "active" do %>
<button
phx-click="show_confirm"
phx-value-action="unsubscribe"
phx-value-uuid={member.uuid}
class="btn btn-xs btn-outline btn-warning"
>
<.icon name="hero-arrow-left-on-rectangle" class="w-3 h-3" /> Unsubscribe
</button>
<% end %>
<button
phx-click="show_confirm"
phx-value-action="remove"
phx-value-uuid={member.uuid}
class="btn btn-xs btn-outline btn-error"
>
<.icon name="hero-trash" class="w-3 h-3" /> Remove
</button>
</div>
</div>
</div>
<% end %>
</div>
</div>
<% else %>
<div class="text-center py-12">
<.icon name="hero-users" class="w-16 h-16 mx-auto mb-4 opacity-30" />
<h3 class="text-lg font-medium text-base-content mb-2">No members found</h3>
<p class="text-base-content/70">
<%= if @status_filter != "" do %>
Try changing the filter above.
<% else %>
This list has no subscribers yet.
<% end %>
</p>
</div>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>