Templates refactoring
Some checks failed
Rust / Test (push) Has been cancelled
Rust / Rustfmt (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Rust / Code coverage (push) Has been cancelled

This commit is contained in:
Alphonse Paix
2025-09-20 04:43:55 +02:00
parent b52b676dc0
commit 40dfe1aed8
15 changed files with 156 additions and 146 deletions

File diff suppressed because one or more lines are too long

View File

@@ -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
View 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()
}
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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())

View File

@@ -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,

View File

@@ -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, ..

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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="/"

View File

@@ -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="/"

View File

@@ -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>

View File

@@ -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 %}

View File

@@ -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>