Brought back newsletter form on admin page
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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 {
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user