Brought back newsletter form on admin page

This commit is contained in:
Alphonse Paix
2025-09-18 18:40:03 +02:00
parent 54218f92a9
commit 08d5f611b5
5 changed files with 85 additions and 16 deletions

File diff suppressed because one or more lines are too long

View File

@@ -58,7 +58,8 @@ impl IntoResponse for AdminError {
AdminError::NotAuthenticated => { AdminError::NotAuthenticated => {
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert("HX-Redirect", "/login".parse().unwrap()); headers.insert("HX-Redirect", "/login".parse().unwrap());
(StatusCode::OK, headers).into_response() headers.insert("Location", "/login".parse().unwrap());
(StatusCode::SEE_OTHER, headers).into_response()
} }
AdminError::ChangePassword(e) => { AdminError::ChangePassword(e) => {
let template = ErrorTemplate { let template = ErrorTemplate {

View File

@@ -10,16 +10,19 @@ use uuid::Uuid;
#[template(path = "../templates/dashboard.html")] #[template(path = "../templates/dashboard.html")]
struct DashboardTemplate { struct DashboardTemplate {
username: String, username: String,
idempotency_key: String, idempotency_key_1: String,
idempotency_key_2: String,
} }
pub async fn admin_dashboard( pub async fn admin_dashboard(
Extension(AuthenticatedUser { username, .. }): Extension<AuthenticatedUser>, Extension(AuthenticatedUser { username, .. }): Extension<AuthenticatedUser>,
) -> Response { ) -> Response {
let idempotency_key = Uuid::new_v4().to_string(); let idempotency_key_1 = Uuid::new_v4().to_string();
let idempotency_key_2 = Uuid::new_v4().to_string();
let template = DashboardTemplate { let template = DashboardTemplate {
username, username,
idempotency_key, idempotency_key_1,
idempotency_key_2,
}; };
Html(template.render().unwrap()).into_response() Html(template.render().unwrap()).into_response()
} }

View File

@@ -10,5 +10,6 @@ pub async fn logout(session: TypedSession) -> Result<Response, AdminError> {
session.clear().await; session.clear().await;
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert("HX-Redirect", "/login".parse().unwrap()); headers.insert("HX-Redirect", "/login".parse().unwrap());
Ok((StatusCode::OK, headers).into_response()) headers.insert("Location", "/login".parse().unwrap());
Ok((StatusCode::SEE_OTHER, headers).into_response())
} }

View File

@@ -93,6 +93,56 @@
</div> </div>
</div> </div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> <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.</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">HTML 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="bg-white rounded-lg shadow-md border border-gray-200">
<div class="p-6 border-b border-gray-200"> <div class="p-6 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-900 flex items-center"> <h2 class="text-xl font-semibold text-gray-900 flex items-center">
@@ -102,29 +152,43 @@
stroke="currentColor"> 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" /> <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> </svg>
Write a new post Contact your subscribers
</h2> </h2>
<p class="text-sm text-gray-600 mt-1">Create a new post and notify your subscribers.</p> <p class="text-sm text-gray-600 mt-1">Contact your subscribers directly.</p>
</div> </div>
<div class="p-6"> <div class="p-6">
<form hx-post="/admin/posts" <form hx-post="/admin/newsletters"
hx-target="#newsletter-messages" hx-target="#newsletter-messages"
hx-swap="innerHTML" hx-swap="innerHTML"
class="space-y-4"> class="space-y-4">
<input type="hidden" name="idempotency_key" value="{{ idempotency_key }}" /> <input type="hidden" name="idempotency_key" value="{{ idempotency_key_2 }}" />
<div> <div>
<label for="title" class="block text-sm font-medium text-gray-700 mb-2">Title</label> <label for="newsletter-title"
class="block text-sm font-medium text-gray-700 mb-2">Subject</label>
<input type="text" <input type="text"
id="title" id="newsletter-title"
name="title" name="title"
placeholder="Subject"
required 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" /> 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>
<div> <div>
<label for="content" class="block text-sm font-medium text-gray-700 mb-2">HTML content</label> <label for="newsletter-html"
<textarea id="content" class="block text-sm font-medium text-gray-700 mb-2">HTML content</label>
name="content" <textarea id="newsletter-html"
name="html"
rows="6" rows="6"
placeholder="HTML version"
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"
placeholder="Plain text version"
required 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> 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>
@@ -136,7 +200,7 @@
stroke="currentColor"> stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg> </svg>
Create Send
</button> </button>
<div id="newsletter-messages" class="mt-4"></div> <div id="newsletter-messages" class="mt-4"></div>
</form> </form>