Track open rate for new post notifications (user clicked the button in the link or not). No data about the user is collected during the process, it only uses an ID inserted by the issue delivery worker.
265 lines
16 KiB
HTML
265 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Dashboard{% endblock %}
|
|
{% block content %}
|
|
<div class="min-w-6/12 mx-auto p-4 sm:p-6">
|
|
<div class="mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-900">Dashboard</h1>
|
|
<p class="mt-2 text-gray-600">
|
|
Connected as <span class="font-bold">{{ username }}</span>
|
|
</p>
|
|
<button hx-post="/admin/logout"
|
|
type="submit"
|
|
class="flex items-center text-sm text-gray-500 hover:text-red-600 transition-colors cursor-pointer gap-1 mt-2">
|
|
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
|
</svg>
|
|
<span>Logout</span>
|
|
</button>
|
|
</div>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
<div class="bg-white rounded-lg shadow-md p-6 border border-gray-200">
|
|
<div class="flex items-center">
|
|
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center shrink-0">
|
|
<svg class="w-6 h-6 text-blue-600"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round">
|
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
<circle cx="9" cy="7" r="4" />
|
|
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
|
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm font-medium text-gray-500">Subscribers</p>
|
|
<p class="text-2xl font-semibold text-gray-900">{{ stats.subscribers }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow-md p-6 border border-gray-200">
|
|
<div class="flex items-center">
|
|
<div class="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center shrink-0">
|
|
<svg class="w-6 h-6 text-purple-600 mr-2"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 20h9M12 4h9M5 4h.01M5 20h.01M5 12h.01M9 16h6M9 8h6" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm font-medium text-gray-500">Posts</p>
|
|
<p class="text-2xl font-semibold text-gray-900">{{ stats.posts }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow-md p-6 border border-gray-200">
|
|
<div class="flex items-center">
|
|
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center shrink-0">
|
|
<svg class="w-6 h-6 text-green-600"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm font-medium text-gray-500">Notifications</p>
|
|
<p class="text-2xl font-semibold text-gray-900">{{ stats.notifications_sent }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow-md p-6 border border-gray-200">
|
|
<div class="flex items-center">
|
|
<div class="w-12 h-12 bg-orange-100 rounded-lg flex items-center justify-center shrink-0">
|
|
<svg class="w-6 h-6 text-orange-600"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
|
</svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm font-medium text-gray-500">Open rate</p>
|
|
<p class="text-2xl font-semibold text-gray-900">{{ stats.formatted_rate() }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<div class="bg-white rounded-lg shadow-md border border-gray-200">
|
|
<div class="p-6 border-b border-gray-200">
|
|
<h2 class="text-xl font-semibold text-gray-900 flex items-center">
|
|
<svg class="w-5 h-5 text-purple-600 mr-2"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 20h9M12 4h9M5 4h.01M5 20h.01M5 12h.01M9 16h6M9 8h6" />
|
|
</svg>
|
|
Write a new post
|
|
</h2>
|
|
<p class="text-sm text-gray-600 mt-1">Publish a new post online. Subscribers will be notified.</p>
|
|
</div>
|
|
<div class="p-6">
|
|
<form hx-post="/admin/posts"
|
|
hx-target="#post-messages"
|
|
hx-swap="innerHTML"
|
|
class="space-y-4">
|
|
<input type="hidden" name="idempotency_key" value="{{ idempotency_key_1 }}" />
|
|
<div>
|
|
<label for="post-title" class="block text-sm font-medium text-gray-700 mb-2">Title</label>
|
|
<input type="text"
|
|
id="post-title"
|
|
name="title"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
|
|
</div>
|
|
<div>
|
|
<label for="post-content"
|
|
class="block text-sm font-medium text-gray-700 mb-2">Markdown content</label>
|
|
<textarea id="post-content"
|
|
name="content"
|
|
rows="6"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"></textarea>
|
|
</div>
|
|
<button type="submit"
|
|
class="w-full bg-purple-600 text-white hover:bg-purple-700 font-medium py-3 px-4 rounded-md transition-colors flex items-center justify-center">
|
|
<svg class="w-4 h-4 mr-2"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 20h9M12 4h9M5 4h.01M5 20h.01M5 12h.01M9 16h6M9 8h6" />
|
|
</svg>
|
|
Publish
|
|
</button>
|
|
<div id="post-messages" class="mt-4"></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow-md border border-gray-200">
|
|
<div class="p-6 border-b border-gray-200">
|
|
<h2 class="text-xl font-semibold text-gray-900 flex items-center">
|
|
<svg class="w-5 h-5 text-blue-600 mr-2"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
|
|
</svg>
|
|
Send an email
|
|
</h2>
|
|
<p class="text-sm text-gray-600 mt-1">Contact your subscribers directly.</p>
|
|
</div>
|
|
<div class="p-6">
|
|
<form hx-post="/admin/newsletters"
|
|
hx-target="#newsletter-messages"
|
|
hx-swap="innerHTML"
|
|
class="space-y-4">
|
|
<input type="hidden" name="idempotency_key" value="{{ idempotency_key_2 }}" />
|
|
<div>
|
|
<label for="newsletter-title"
|
|
class="block text-sm font-medium text-gray-700 mb-2">Subject</label>
|
|
<input type="text"
|
|
id="newsletter-title"
|
|
name="title"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
|
|
</div>
|
|
<div>
|
|
<label for="newsletter-html"
|
|
class="block text-sm font-medium text-gray-700 mb-2">HTML content</label>
|
|
<textarea id="newsletter-html"
|
|
name="html"
|
|
rows="6"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"></textarea>
|
|
</div>
|
|
<div>
|
|
<label for="newsletter-text"
|
|
class="block text-sm font-medium text-gray-700 mb-2">Text content</label>
|
|
<textarea id="newsletter-text"
|
|
name="text"
|
|
rows="6"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"></textarea>
|
|
</div>
|
|
<button type="submit"
|
|
class="w-full bg-blue-600 text-white hover:bg-blue-700 font-medium py-3 px-4 rounded-md transition-colors flex items-center justify-center">
|
|
<svg class="w-4 h-4 mr-2"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
|
</svg>
|
|
Send
|
|
</button>
|
|
<div id="newsletter-messages" class="mt-4"></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow-md border border-gray-200 lg:col-span-2">
|
|
<div class="p-6 border-b border-gray-200">
|
|
<h2 class="text-xl font-semibold text-gray-900 flex items-center">
|
|
<svg class="w-5 h-5 text-green-600 mr-2"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
</svg>
|
|
Change your password
|
|
</h2>
|
|
<p class="text-sm text-gray-600 mt-1">Set a new password for your account.</p>
|
|
</div>
|
|
<div class="p-6">
|
|
<form hx-post="/admin/password"
|
|
hx-target="#password-messages"
|
|
hx-swap="innerHTML"
|
|
class="space-y-4">
|
|
<div>
|
|
<label for="current_password"
|
|
class="block text-sm font-medium text-gray-700 mb-2">Current password</label>
|
|
<input type="password"
|
|
id="current_password"
|
|
name="current_password"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500" />
|
|
</div>
|
|
<div>
|
|
<label for="new_password"
|
|
class="block text-sm font-medium text-gray-700 mb-2">New password</label>
|
|
<input type="password"
|
|
id="new_password"
|
|
name="new_password"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500" />
|
|
</div>
|
|
<div>
|
|
<label for="new_password_check"
|
|
class="block text-sm font-medium text-gray-700 mb-2">Confirm new password</label>
|
|
<input type="password"
|
|
id="new_password_check"
|
|
name="new_password_check"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500" />
|
|
</div>
|
|
<button type="submit"
|
|
class="w-full bg-green-600 text-white hover:bg-green-700 font-medium py-3 px-4 rounded-md transition-colors flex items-center justify-center">
|
|
<svg class="w-4 h-4 mr-2"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
</svg>
|
|
Update password
|
|
</button>
|
|
<div id="password-messages" class="mt-4"></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|