Packages

phoenix_kit

1.7.69
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 broadcasts.html.heex
Raw

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

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="Broadcasts"
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")}
title="Broadcasts"
subtitle="Create and manage email broadcasts"
/>
<%!-- Actions Bar --%>
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-6">
<div>
<select
name="status"
class="select select-sm"
phx-change="filter_status"
>
<option value="" selected={@status_filter == ""}>All Statuses</option>
<option value="draft" selected={@status_filter == "draft"}>Draft</option>
<option value="scheduled" selected={@status_filter == "scheduled"}>Scheduled</option>
<option value="sending" selected={@status_filter == "sending"}>Sending</option>
<option value="sent" selected={@status_filter == "sent"}>Sent</option>
<option value="cancelled" selected={@status_filter == "cancelled"}>Cancelled</option>
</select>
</div>
<.pk_link navigate="/admin/newsletters/broadcasts/new" class="btn btn-primary btn-sm">
<.icon name="hero-plus" class="w-4 h-4" /> New Broadcast
</.pk_link>
</div>
<%!-- Broadcasts Table (desktop) --%>
<%= if @broadcasts == [] do %>
<div class="text-center py-12 text-base-content/60">
<.icon name="hero-megaphone" class="w-12 h-12 mx-auto mb-4 opacity-40" />
<p class="text-lg font-medium">No broadcasts found</p>
<p class="text-sm mt-1">Create your first broadcast to get started.</p>
</div>
<% else %>
<div class="hidden md:block">
<.table_default variant="zebra" size="sm" class="w-full">
<.table_default_header>
<.table_default_row>
<.table_default_header_cell>Subject</.table_default_header_cell>
<.table_default_header_cell>List</.table_default_header_cell>
<.table_default_header_cell>Status</.table_default_header_cell>
<.table_default_header_cell class="text-right">Sent</.table_default_header_cell>
<.table_default_header_cell class="text-right">
Delivered
</.table_default_header_cell>
<.table_default_header_cell class="text-right">Opened</.table_default_header_cell>
<.table_default_header_cell class="text-right">Bounced</.table_default_header_cell>
<.table_default_header_cell>Sent At</.table_default_header_cell>
</.table_default_row>
</.table_default_header>
<.table_default_body>
<.table_default_row
:for={broadcast <- @broadcasts}
class="hover cursor-pointer"
phx-click="view_broadcast"
phx-value-uuid={broadcast.uuid}
>
<.table_default_cell class="font-medium max-w-xs truncate">
{broadcast.subject}
</.table_default_cell>
<.table_default_cell>
<%= if broadcast.list do %>
{broadcast.list.name}
<% else %>
<span class="text-base-content/40">-</span>
<% end %>
</.table_default_cell>
<.table_default_cell>
<span class={"badge badge-sm #{status_badge_class(broadcast.status)}"}>
{broadcast.status}
</span>
</.table_default_cell>
<.table_default_cell class="text-right">{broadcast.sent_count}</.table_default_cell>
<.table_default_cell class="text-right">
{broadcast.delivered_count}
</.table_default_cell>
<.table_default_cell class="text-right">
{broadcast.opened_count}
</.table_default_cell>
<.table_default_cell class="text-right">
{broadcast.bounced_count}
</.table_default_cell>
<.table_default_cell class="text-sm text-base-content/70">
{format_datetime(broadcast.sent_at)}
</.table_default_cell>
</.table_default_row>
</.table_default_body>
</.table_default>
</div>
<%!-- Broadcasts Cards (mobile) --%>
<div class="md:hidden mb-6">
<div class="space-y-3">
<%= for broadcast <- @broadcasts do %>
<div
class="card card-border card-sm bg-base-100 cursor-pointer hover:bg-base-200/50"
phx-click="view_broadcast"
phx-value-uuid={broadcast.uuid}
>
<div class="card-body gap-3">
<%!-- Header: subject + status --%>
<div class="flex items-start justify-between gap-2">
<span class="font-medium text-sm truncate">{broadcast.subject}</span>
<span class={"badge badge-sm #{status_badge_class(broadcast.status)}"}>
{broadcast.status}
</span>
</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">List</div>
<div>
{if broadcast.list, do: broadcast.list.name, else: "-"}
</div>
<div class="text-base-content/60">Sent</div>
<div>{broadcast.sent_count}</div>
<div class="text-base-content/60">Delivered</div>
<div>{broadcast.delivered_count}</div>
<div class="text-base-content/60">Opened</div>
<div>{broadcast.opened_count}</div>
<div class="text-base-content/60">Bounced</div>
<div>{broadcast.bounced_count}</div>
<div class="text-base-content/60">Sent At</div>
<div class="text-base-content/70">{format_datetime(broadcast.sent_at)}</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>