Current section

Files

Jump to
phoenix_kit_emails lib phoenix_kit modules emails web metrics.html.heex
Raw

lib/phoenix_kit/modules/emails/web/metrics.html.heex

<div class="container flex-col mx-auto px-4 py-6">
<.admin_page_header
back={Routes.path("/admin")}
title={gettext("Email Dashboard")}
subtitle={gettext("Comprehensive email performance metrics and insights")}
/>
<%!-- Controls Section --%>
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between mb-6 gap-4">
<%!-- Period Selection --%>
<div class="flex flex-wrap gap-2">
<button
class={[
"btn btn-sm",
(@period == :last_24_hours && "btn-primary") || "btn-outline"
]}
phx-click="change_period"
phx-value-period="last_24_hours"
>
{gettext("24 Hours")}
</button>
<button
class={[
"btn btn-sm",
(@period == :last_7_days && "btn-primary") || "btn-outline"
]}
phx-click="change_period"
phx-value-period="last_7_days"
>
{gettext("7 Days")}
</button>
<button
class={[
"btn btn-sm",
(@period == :last_30_days && "btn-primary") || "btn-outline"
]}
phx-click="change_period"
phx-value-period="last_30_days"
>
{gettext("30 Days")}
</button>
<button
class={[
"btn btn-sm",
(@period == :last_90_days && "btn-primary") || "btn-outline"
]}
phx-click="change_period"
phx-value-period="last_90_days"
>
{gettext("90 Days")}
</button>
<button
class="btn btn-sm btn-outline"
phx-click="toggle_custom_range"
>
{gettext("Custom Range")}
</button>
</div>
<%!-- Action Buttons --%>
<div class="flex gap-2">
<button
class="btn btn-sm btn-outline"
phx-click="refresh"
disabled={@loading}
>
<%= if @loading do %>
<span class="loading loading-spinner loading-xs"></span>
<% else %>
<PhoenixKitWeb.Components.Core.Icons.icon_refresh class="w-4 h-4" />
<% end %>
{gettext("Refresh")}
</button>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-sm btn-outline">
<PhoenixKitWeb.Components.Core.Icons.icon_download class="w-4 h-4" /> {gettext(
"Export"
)}
</div>
<ul
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52"
>
<li>
<a phx-click="export_metrics" phx-value-format="csv">
{gettext("Export as CSV")}
</a>
</li>
<li>
<a phx-click="export_metrics" phx-value-format="json">
{gettext("Export as JSON")}
</a>
</li>
</ul>
</div>
</div>
</div>
<%!-- Custom Date Range Modal --%>
<%= if @custom_range do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg mb-4">{gettext("Select Custom Date Range")}</h3>
<form phx-submit="apply_custom_range">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="label">
<span class="label-text">{gettext("Start Date")}</span>
</label>
<input
type="date"
name="start_date"
class="input input-bordered w-full"
max={Date.utc_today() |> Date.to_iso8601()}
required
/>
</div>
<div>
<label class="label">
<span class="label-text">{gettext("End Date")}</span>
</label>
<input
type="date"
name="end_date"
class="input input-bordered w-full"
max={Date.utc_today() |> Date.to_iso8601()}
required
/>
</div>
</div>
<div class="modal-action">
<button type="submit" class="btn btn-primary">{gettext("Apply")}</button>
<button type="button" class="btn" phx-click="toggle_custom_range">
{gettext("Cancel")}
</button>
</div>
</form>
</div>
</div>
<% end %>
<%!-- Loading State --%>
<%= if @loading do %>
<div class="flex justify-center items-center h-32">
<span class="loading loading-spinner loading-lg"></span>
<span class="ml-3 text-lg">{gettext("Loading analytics data...")}</span>
</div>
<% else %>
<%!-- KPI Cards --%>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<%!-- Total Sent --%>
<div class="card bg-primary text-primary-content shadow-sm">
<div class="card-body p-4">
<div class="flex items-center justify-between">
<div>
<p class="text-primary-content/70 text-sm">{gettext("Total Sent")}</p>
<p class="text-2xl font-bold">{UtilsNumber.format(@metrics.total_sent || 0)}</p>
</div>
<div class="text-primary-content/70">
<PhoenixKitWeb.Components.Core.Icons.icon_email class="w-8 h-8" />
</div>
</div>
</div>
</div>
<%!-- Delivery Rate --%>
<div class="card bg-success text-success-content shadow-sm">
<div class="card-body p-4">
<div class="flex items-center justify-between">
<div>
<p class="text-success-content/70 text-sm">{gettext("Delivery Rate")}</p>
<p class="text-2xl font-bold">
{UtilsNumber.format_percentage(@metrics.delivery_rate || 0)}
</p>
</div>
<div class="text-success-content/70">
<PhoenixKitWeb.Components.Core.Icons.icon_check class="w-8 h-8" />
</div>
</div>
</div>
</div>
<%!-- Bounce Rate --%>
<div class="card bg-warning text-warning-content shadow-sm">
<div class="card-body p-4">
<div class="flex items-center justify-between">
<div>
<p class="text-warning-content/70 text-sm">{gettext("Bounce Rate")}</p>
<p class="text-2xl font-bold">
{UtilsNumber.format_percentage(@metrics.bounce_rate || 0)}
</p>
</div>
<div class="text-warning-content/70">
<PhoenixKitWeb.Components.Core.Icons.icon_warning_filled class="w-8 h-8" />
</div>
</div>
</div>
</div>
<%!-- Open Rate --%>
<div class="card bg-info text-info-content shadow-sm">
<div class="card-body p-4">
<div class="flex items-center justify-between">
<div>
<p class="text-info-content/70 text-sm">{gettext("Open Rate")}</p>
<p class="text-2xl font-bold">
{UtilsNumber.format_percentage(@metrics.open_rate || 0)}
</p>
</div>
<div class="text-info-content/70">
<PhoenixKitWeb.Components.Core.Icons.icon_eye class="w-8 h-8" />
</div>
</div>
</div>
</div>
</div>
<%!-- Charts Section --%>
<div
class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"
id="email-charts-container"
phx-hook="EmailCharts"
>
<%!-- Delivery Trends Chart --%>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title text-lg mb-4">{gettext("Delivery Trends")}</h2>
<div
class="w-full h-64"
id="delivery-chart-container"
data-chart-data={Jason.encode!(Map.get(@charts_data, :delivery_trend, %{}))}
>
<canvas id="delivery-trend-chart" width="400" height="200" phx-update="ignore">
</canvas>
</div>
</div>
</div>
<%!-- Engagement Chart --%>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title text-lg mb-4">{gettext("Engagement Metrics")}</h2>
<div
class="w-full h-64"
id="engagement-chart-container"
data-chart-data={Jason.encode!(Map.get(@charts_data, :engagement, %{}))}
>
<canvas id="engagement-chart" width="400" height="200" phx-update="ignore"></canvas>
</div>
</div>
</div>
</div>
<%!-- Additional Analytics --%>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<%!-- Provider Performance --%>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title text-lg mb-4">{gettext("Provider Performance")}</h2>
<%= if @metrics.by_provider && length(Map.keys(@metrics.by_provider)) > 0 do %>
<div class="overflow-x-auto">
<.table_default size="sm" class="w-full">
<.table_default_header>
<.table_default_row>
<.table_default_header_cell>
{gettext("Provider")}
</.table_default_header_cell>
<.table_default_header_cell>{gettext("Sent")}</.table_default_header_cell>
<.table_default_header_cell>
{gettext("Delivery Rate")}
</.table_default_header_cell>
<.table_default_header_cell>
{gettext("Bounce Rate")}
</.table_default_header_cell>
</.table_default_row>
</.table_default_header>
<.table_default_body>
<%= for {provider, stats} <- @metrics.by_provider do %>
<.table_default_row>
<.table_default_cell class="font-medium">{provider}</.table_default_cell>
<.table_default_cell>
{UtilsNumber.format(stats.total_sent || 0)}
</.table_default_cell>
<.table_default_cell>
<span class={[
"badge badge-sm h-auto",
((stats.delivery_rate || 0) >= 95 && "badge-success") ||
((stats.delivery_rate || 0) >= 85 && "badge-warning") ||
"badge-error"
]}>
{UtilsNumber.format_percentage(stats.delivery_rate || 0)}
</span>
</.table_default_cell>
<.table_default_cell>
{UtilsNumber.format_percentage(stats.bounce_rate || 0)}
</.table_default_cell>
</.table_default_row>
<% end %>
</.table_default_body>
</.table_default>
</div>
<% else %>
<div class="text-center text-base-content/50 py-8">
{gettext("No provider data available")}
</div>
<% end %>
</div>
</div>
<%!-- Recent Activity --%>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title text-lg mb-4">{gettext("System Status")}</h2>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-sm">{gettext("Email")}</span>
<span class="badge badge-success">{gettext("Active")}</span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">{gettext("Last Updated")}</span>
<span class="text-sm text-base-content/70">
{UtilsDate.format_datetime_with_user_format(@last_updated)}
</span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">{gettext("Total Emails Today")}</span>
<span class="text-sm font-medium">
{UtilsNumber.format(@metrics.today_count || 0)}
</span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">{gettext("Data Retention")}</span>
<span class="text-sm text-base-content/70">
{gettext("%{days} days", days: PhoenixKit.Modules.Emails.get_retention_days())}
</span>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>