Templates refactoring
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,5 +1,7 @@
|
|||||||
mod new_subscriber;
|
mod new_subscriber;
|
||||||
|
mod post;
|
||||||
mod subscriber_email;
|
mod subscriber_email;
|
||||||
|
|
||||||
pub use new_subscriber::NewSubscriber;
|
pub use new_subscriber::NewSubscriber;
|
||||||
|
pub use post::PostEntry;
|
||||||
pub use subscriber_email::SubscriberEmail;
|
pub use subscriber_email::SubscriberEmail;
|
||||||
|
|||||||
17
src/domain/post.rs
Normal file
17
src/domain/post.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub struct PostEntry {
|
||||||
|
pub post_id: Uuid,
|
||||||
|
pub author: Option<String>,
|
||||||
|
pub title: String,
|
||||||
|
pub content: String,
|
||||||
|
pub published_at: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PostEntry {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn formatted_date(&self) -> String {
|
||||||
|
self.published_at.format("%B %d, %Y").to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ pub use subscriptions_confirm::*;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
authentication::AuthError,
|
authentication::AuthError,
|
||||||
templates::{InternalErrorTemplate, MessageTemplate},
|
templates::{InternalErrorTemplate, MessageTemplate, NotFoundTemplate},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(thiserror::Error)]
|
#[derive(thiserror::Error)]
|
||||||
@@ -130,10 +130,6 @@ impl From<AuthError> for AppError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "../templates/404.html")]
|
|
||||||
struct NotFoundTemplate;
|
|
||||||
|
|
||||||
pub async fn not_found() -> Response {
|
pub async fn not_found() -> Response {
|
||||||
(
|
(
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::authentication::AuthenticatedUser;
|
use crate::{authentication::AuthenticatedUser, templates::DashboardTemplate};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use axum::{
|
use axum::{
|
||||||
Extension,
|
Extension,
|
||||||
@@ -6,14 +6,6 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "../templates/dashboard.html")]
|
|
||||||
struct DashboardTemplate {
|
|
||||||
username: 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 {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
use askama::Template;
|
use askama::Template;
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
|
|
||||||
#[derive(Template)]
|
use crate::templates::HomeTemplate;
|
||||||
#[template(path = "../templates/home.html")]
|
|
||||||
struct HomeTemplate;
|
|
||||||
|
|
||||||
pub async fn home() -> Html<String> {
|
pub async fn home() -> Html<String> {
|
||||||
Html(HomeTemplate.render().unwrap())
|
Html(HomeTemplate.render().unwrap())
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::{
|
|||||||
routes::AppError,
|
routes::AppError,
|
||||||
session_state::TypedSession,
|
session_state::TypedSession,
|
||||||
startup::AppState,
|
startup::AppState,
|
||||||
|
templates::LoginTemplate,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
@@ -15,10 +16,6 @@ use axum::{
|
|||||||
use axum::{http::StatusCode, response::Redirect};
|
use axum::{http::StatusCode, response::Redirect};
|
||||||
use secrecy::SecretString;
|
use secrecy::SecretString;
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "../templates/login.html")]
|
|
||||||
struct LoginTemplate;
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct LoginFormData {
|
pub struct LoginFormData {
|
||||||
username: String,
|
username: String,
|
||||||
|
|||||||
@@ -1,41 +1,18 @@
|
|||||||
use crate::{routes::AppError, startup::AppState};
|
use crate::{
|
||||||
|
domain::PostEntry,
|
||||||
|
routes::AppError,
|
||||||
|
startup::AppState,
|
||||||
|
templates::{PostTemplate, PostsTemplate},
|
||||||
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
struct PostEntry {
|
|
||||||
post_id: Uuid,
|
|
||||||
author: Option<String>,
|
|
||||||
title: String,
|
|
||||||
content: String,
|
|
||||||
published_at: DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PostEntry {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn formatted_date(&self) -> String {
|
|
||||||
self.published_at.format("%B %d, %Y").to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "../templates/posts.html")]
|
|
||||||
struct PostsTemplate {
|
|
||||||
posts: Vec<PostEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "../templates/post.html")]
|
|
||||||
struct PostTemplate {
|
|
||||||
post: PostEntry,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn list_posts(
|
pub async fn list_posts(
|
||||||
State(AppState {
|
State(AppState {
|
||||||
connection_pool, ..
|
connection_pool, ..
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::startup::AppState;
|
use crate::{startup::AppState, templates::ConfirmTemplate};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Query, State},
|
extract::{Query, State},
|
||||||
@@ -9,10 +9,6 @@ use serde::Deserialize;
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "../templates/confirm.html")]
|
|
||||||
struct ConfirmTemplate;
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "Confirming new subscriber", skip(params))]
|
#[tracing::instrument(name = "Confirming new subscriber", skip(params))]
|
||||||
pub async fn confirm(
|
pub async fn confirm(
|
||||||
State(AppState {
|
State(AppState {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
|
||||||
|
use crate::domain::PostEntry;
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
pub enum MessageTemplate {
|
pub enum MessageTemplate {
|
||||||
#[template(path = "../templates/success.html")]
|
#[template(path = "../templates/success.html")]
|
||||||
@@ -11,3 +13,39 @@ pub enum MessageTemplate {
|
|||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "../templates/500.html")]
|
#[template(path = "../templates/500.html")]
|
||||||
pub struct InternalErrorTemplate;
|
pub struct InternalErrorTemplate;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "../templates/login.html")]
|
||||||
|
pub struct LoginTemplate;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "../templates/dashboard.html")]
|
||||||
|
pub struct DashboardTemplate {
|
||||||
|
pub username: String,
|
||||||
|
pub idempotency_key_1: String,
|
||||||
|
pub idempotency_key_2: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "../templates/home.html")]
|
||||||
|
pub struct HomeTemplate;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "../templates/posts.html")]
|
||||||
|
pub struct PostsTemplate {
|
||||||
|
pub posts: Vec<PostEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "../templates/post.html")]
|
||||||
|
pub struct PostTemplate {
|
||||||
|
pub post: PostEntry,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "../templates/confirm.html")]
|
||||||
|
pub struct ConfirmTemplate;
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "../templates/404.html")]
|
||||||
|
pub struct NotFoundTemplate;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<h1 class="text-4xl font-semibold text-gray-700 mb-4">404</h1>
|
<h1 class="text-4xl font-semibold text-gray-700 mb-4">404</h1>
|
||||||
<h2 class="text-2xl font-semibold text-gray-500 mb-6">Not Found</h2>
|
<h2 class="text-2xl font-semibold text-gray-500 mb-6">Not Found</h2>
|
||||||
<p class="text-gray-600 mb-8 max-w-2xl mx-auto">
|
<p class="text-gray-600 mb-8 max-w-2xl mx-auto">
|
||||||
Sorry, we couldn't find the page you're looking for. The page may have been moved, deleted, or the URL might be incorrect.
|
Sorry, I couldn't find the page you're looking for. The page may have been moved, deleted, or the URL might be incorrect.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||||
<a href="/"
|
<a href="/"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<h1 class="text-4xl font-semibold text-red-600 mb-4">500</h1>
|
<h1 class="text-4xl font-semibold text-red-600 mb-4">500</h1>
|
||||||
<h2 class="text-2xl font-semibold text-gray-500 mb-6">Internal Server Error</h2>
|
<h2 class="text-2xl font-semibold text-gray-500 mb-6">Internal Server Error</h2>
|
||||||
<p class="text-gray-600 mb-8 max-w-2xl mx-auto">
|
<p class="text-gray-600 mb-8 max-w-2xl mx-auto">
|
||||||
Something went wrong on the server. Please try again in a few minutes or contact me if the problem persists.
|
Something went wrong. Please try again in a few minutes or contact me if the problem persists.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||||
<a href="/"
|
<a href="/"
|
||||||
|
|||||||
@@ -16,27 +16,26 @@
|
|||||||
<header class="bg-white shadow-sm border-b border-gray-200 top-0 z-40">
|
<header class="bg-white shadow-sm border-b border-gray-200 top-0 z-40">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex justify-between items-center h-16">
|
<div class="flex justify-between items-center h-16">
|
||||||
<div class="flex items-center space-x-6">
|
<div class="flex items-center space-x-4 text-blue-600">
|
||||||
<a href="/"
|
<div class="flex space-x-2 items-center">
|
||||||
class="flex items-center space-x-2 text-blue-600 hover:text-blue-700 transition-colors">
|
<div class="w-6 h-6 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-lg flex items-center justify-center">
|
||||||
<div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-lg flex items-center justify-center">
|
<svg class="w-4 h-4 text-white"
|
||||||
<svg class="w-5 h-5 text-white"
|
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke="currentColor">
|
stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-xl font-bold">zero2prod</span>
|
<span class="text-sm font-bold">zero2prod</span>
|
||||||
</a>
|
</div>
|
||||||
<nav class="flex items-center space-x-2">
|
<nav class="flex items-center space-x-2">
|
||||||
<a href="/"
|
<a href="/"
|
||||||
class="flex items-center text-gray-700 hover:text-blue-600 hover:bg-blue-50 px-3 py-2 rounded-md text-sm font-medium transition-colors">
|
class="flex items-center text-gray-700 hover:underline underline-offset-2 decoration-1 decoration-blue-600 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium transition-colors">
|
||||||
Home
|
home
|
||||||
</a>
|
</a>
|
||||||
<a href="/posts"
|
<a href="/posts"
|
||||||
class="flex items-center text-gray-700 hover:text-blue-600 hover:bg-blue-50 px-3 py-2 rounded-md text-sm font-medium transition-colors">
|
class="flex items-center text-gray-700 hover:underline underline-offset-2 decoration-1 decoration-blue-600 hover:text-blue-600 px-3 py-2 rounded-md text-sm font-medium transition-colors">
|
||||||
Posts
|
posts
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,7 +43,7 @@
|
|||||||
<a href="/admin/dashboard"
|
<a href="/admin/dashboard"
|
||||||
hx-boost="true"
|
hx-boost="true"
|
||||||
class="bg-blue-600 text-white hover:bg-blue-700 px-4 py-2 rounded-md text-sm font-medium transition-colors">
|
class="bg-blue-600 text-white hover:bg-blue-700 px-4 py-2 rounded-md text-sm font-medium transition-colors">
|
||||||
Dashboard
|
dashboard
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Home{% endblock %}
|
{% block title %}Home{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="flex-1 flex items-center justify-center">
|
|
||||||
<div class="max-w-4xl mx-auto">
|
<div class="max-w-4xl mx-auto">
|
||||||
<div class="bg-gradient-to-r from-blue-600 to-indigo-700 rounded-lg shadow-lg text-white p-8 mb-8">
|
<div class="bg-gradient-to-r from-blue-600 to-indigo-700 rounded-lg shadow-lg text-white p-8 mb-8">
|
||||||
<div class="max-w-3xl">
|
<div class="max-w-3xl">
|
||||||
@@ -80,5 +79,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Login - zero2prod{% endblock %}
|
{% block title %}Login - zero2prod{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="min-h-[60vh] flex items-center justify-center">
|
<div class="flex-1 flex items-center justify-center">
|
||||||
<div class="max-w-md w-full space-y-8">
|
<div class="max-w-md w-full space-y-8">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h2 class="text-3xl font-bold text-gray-900">Login</h2>
|
<h2 class="text-3xl font-bold text-gray-900">Login</h2>
|
||||||
|
|||||||
Reference in New Issue
Block a user