feat: Rewrite routes, pages and components to be more HATEOAS

This commit is contained in:
Florian Briand 2024-08-23 18:45:43 +02:00 committed by florian_briand
parent 7487b34a17
commit 8ce18e53d5
13 changed files with 201 additions and 57 deletions

6
crates/app/askama.toml Normal file
View File

@ -0,0 +1,6 @@
[general]
# Directories to search for templates, relative to the crate root.
dirs = [
"src/pages",
"src/components",
]

View File

@ -1,34 +0,0 @@
{% extends "base.html" %}
{% block title %}Pharma Libre{% endblock %}
{% block body %}
<div class="py-10">
<header>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<h1
id="page-title"
class="text-3xl font-bold leading-tight tracking-tight text-gray-900"
>
{% include "skeletons/page-title.html" %}
</h1>
</div>
</header>
<main>
<div
id="main-container"
class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8"
>
<!-- Your content -->
<div
hx-get="/pages/home"
hx-target="this"
hx-trigger="load"
hx-swap="outerHTML"
>
{% include "skeletons/card.html" %}
</div>
</div>
</main>
</div>
{% endblock %}

View File

@ -9,14 +9,10 @@
<link href="/assets/css/flowbite@2.5.1.min.css" rel="stylesheet" /> <link href="/assets/css/flowbite@2.5.1.min.css" rel="stylesheet" />
<script src="/assets/js/flowbite@2.5.1.min.js"></script> <script src="/assets/js/flowbite@2.5.1.min.js"></script>
{% block head %}{% endblock %} {% block headx %}{% endblock %}
</head> </head>
<body class="h-full"> <body class="h-full">
<div class="min-h-full"> <div class="min-h-full">
{% block nav %}
{% include "layout/nav.html" %}
{% endblock %}
{% block body %}{% endblock %} {% block body %}{% endblock %}
</div> </div>
</body> </body>

View File

@ -0,0 +1,14 @@
{% set selected = item.id == current %}
<li>
<a
href="{{ item.href }}"
{% if selected -%}
class="block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 md:dark:text-blue-500"
aria-current="page"
{% else -%}
class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
{% endif -%}
>
{{ item.label }}
</a>
</li>

View File

@ -0,0 +1,50 @@
{% macro navbar(current) %}
{% let items=crate::menu::get_menu_items() %}
<nav class="bg-white border-gray-200 dark:bg-gray-900">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="/" class="flex items-center space-x-3 rtl:space-x-reverse">
<img src="https://flowbite.com/docs/images/logo.svg" class="h-8" alt="Flowbite Logo" />
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">Krys4lide</span>
</a>
<div class="flex items-center md:order-2 space-x-3 md:space-x-0 rtl:space-x-reverse">
<button type="button" class="flex text-sm bg-gray-800 rounded-full md:me-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600" id="user-menu-button" aria-expanded="false" data-dropdown-toggle="user-dropdown" data-dropdown-placement="bottom">
<span class="sr-only">Open user menu</span>
<img class="w-8 h-8 rounded-full" src="https://flowbite.com/docs/images/people/profile-picture-3.jpg" alt="user photo">
</button>
<!-- Dropdown menu -->
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600" id="user-dropdown">
<div class="px-4 py-3">
<span class="block text-sm text-gray-900 dark:text-white">Bonnie Green</span>
<span class="block text-sm text-gray-500 truncate dark:text-gray-400">name@flowbite.com</span>
</div>
<ul class="py-2" aria-labelledby="user-menu-button">
<li>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Profile</a>
</li>
<li>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Settings</a>
</li>
<li>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Sign out</a>
</li>
</ul>
</div>
<button data-collapse-toggle="navbar-user" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="navbar-user" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
</svg>
</button>
</div>
<div class="items-center justify-between hidden w-full md:flex md:w-auto md:order-1" id="navbar-user">
<ul class="flex flex-col font-medium p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:space-x-8 rtl:space-x-reverse md:flex-row md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700">
{% for item in items %}
{% include "navbar/menu-item.html" %}
{% endfor %}
</ul>
</div>
</div>
</nav>
{% endmacro %}

View File

@ -4,6 +4,9 @@ use askama_axum::Template;
use axum::http::{StatusCode, Uri}; use axum::http::{StatusCode, Uri};
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
mod menu;
mod pages;
async fn fallback(uri: Uri) -> (StatusCode, String) { async fn fallback(uri: Uri) -> (StatusCode, String) {
(StatusCode::NOT_FOUND, format!("No route for {uri}")) (StatusCode::NOT_FOUND, format!("No route for {uri}"))
} }
@ -20,7 +23,6 @@ pub fn get_router(assets_path: &Path) -> axum::Router {
axum::Router::new() axum::Router::new()
.nest_service("/assets", ServeDir::new(assets_path)) .nest_service("/assets", ServeDir::new(assets_path))
.route("/", axum::routing::get(root)) .route("/", axum::routing::get(root))
// .nest("/pages", old_pages::get_routes()) .merge(pages::get_routes())
// .merge(old_templates::get_routes())
.fallback(fallback) .fallback(fallback)
} }

View File

@ -1,4 +1,4 @@
use std::path::Path; use std::path::{Path, PathBuf};
use std::{env, io}; use std::{env, io};
use axum::body::Body; use axum::body::Body;
@ -48,12 +48,14 @@ async fn get_tcp_listener() -> Result<TcpListener, io::Error> {
} }
fn get_livereload_layer( fn get_livereload_layer(
templates_path: &Path, templates_paths: Vec<PathBuf>,
) -> Result<LiveReloadLayer<NotHtmxPredicate>, notify::Error> { ) -> Result<LiveReloadLayer<NotHtmxPredicate>, notify::Error> {
let livereload = LiveReloadLayer::new(); let livereload = LiveReloadLayer::new();
let reloader = livereload.reloader(); let reloader = livereload.reloader();
let mut watcher = notify::recommended_watcher(move |_| reloader.reload())?; let mut watcher = notify::recommended_watcher(move |_| reloader.reload())?;
watcher.watch(templates_path, notify::RecursiveMode::Recursive)?; for templates_path in templates_paths {
watcher.watch(templates_path.as_path(), notify::RecursiveMode::Recursive)?;
}
Ok(livereload.request_predicate::<Body, NotHtmxPredicate>(NotHtmxPredicate)) Ok(livereload.request_predicate::<Body, NotHtmxPredicate>(NotHtmxPredicate))
} }
@ -63,10 +65,13 @@ async fn main() -> Result<(), AppError> {
var: "CARGO_MANIFEST_DIR", var: "CARGO_MANIFEST_DIR",
})?; })?;
let assets_path = Path::new(&manifest_dir).join("assets"); let assets_path = Path::new(&manifest_dir).join("assets");
let templates_path = Path::new(&manifest_dir).join("templates"); let templates_paths = vec![
Path::new(&manifest_dir).join("src/pages"),
Path::new(&manifest_dir).join("src/components"),
];
let livereload_layer = let livereload_layer =
get_livereload_layer(&templates_path).map_err(AppError::NotifyWatcher)?; get_livereload_layer(templates_paths).map_err(AppError::NotifyWatcher)?;
let router = get_router(assets_path.as_path()).layer(livereload_layer); let router = get_router(assets_path.as_path()).layer(livereload_layer);
let listener: TcpListener = get_tcp_listener().await.map_err(AppError::TCPListener)?; let listener: TcpListener = get_tcp_listener().await.map_err(AppError::TCPListener)?;

20
crates/app/src/menu.rs Normal file
View File

@ -0,0 +1,20 @@
pub struct MenuItem {
pub id: String,
pub label: String,
pub href: String,
}
pub fn get_menu_items() -> Vec<MenuItem> {
vec![
MenuItem {
id: "home".to_string(),
label: "Accueil".to_string(),
href: "/".to_string(),
},
MenuItem {
id: "cps".to_string(),
label: "CPS".to_string(),
href: "/cps".to_string(),
},
]
}

View File

@ -1,10 +0,0 @@
use axum::{routing, Router};
mod cps;
mod home;
pub fn get_routes() -> Router {
Router::new()
.route("/home", routing::get(home::home))
.route("/cps", routing::get(cps::cps))
}

View File

@ -0,0 +1,44 @@
{% extends "base.html" %}
{% import "navbar/navbar.html" as navbar -%}
{% block title %}Pharma Libre - CPS{% endblock %}
{% block body %}
{% call navbar::navbar(current="cps") %}
<div class="py-10">
<header>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<h1
id="page-title"
class="text-3xl font-bold leading-tight tracking-tight text-gray-900"
>
CPS
</h1>
</div>
</header>
<main>
<div
id="main-container"
class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8"
>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
<div
class="border-2 border-dashed border-gray-300 rounded-lg dark:border-gray-600 h-32 md:h-64"
>A</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
>B</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
>C</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
>D</div>
</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
>E</div>
</div>
</main>
</div>
{% endblock %}

View File

@ -6,7 +6,7 @@ use axum::{
}; };
#[derive(Template)] #[derive(Template)]
#[template(path = "pages/cps.html")] #[template(path = "cps.html")]
pub struct CpsTemplate; pub struct CpsTemplate;
pub struct ExtractHxRequest(bool); pub struct ExtractHxRequest(bool);

View File

@ -0,0 +1,44 @@
{% extends "base.html" %}
{% import "navbar/navbar.html" as navbar -%}
{% block title %}Pharma Libre - Accueil{% endblock %}
{% block body %}
{% call navbar::navbar(current="home") %}
<div class="py-10">
<header>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<h1
id="page-title"
class="text-3xl font-bold leading-tight tracking-tight text-gray-900"
>
Accueil
</h1>
</div>
</header>
<main>
<div
id="main-container"
class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8"
>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
<div
class="border-2 border-dashed border-gray-300 rounded-lg dark:border-gray-600 h-32 md:h-64"
>A</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
>B</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
>C</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
>D</div>
</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
>E</div>
</div>
</main>
</div>
{% endblock %}

View File

@ -0,0 +1,7 @@
use axum::{routing, Router};
mod cps;
pub fn get_routes() -> Router {
Router::new().route("/cps", routing::get(cps::cps))
}