feat: add page system behing navbar items and skeletons for loading

This commit is contained in:
Florian Briand 2024-08-06 00:18:09 +02:00 committed by theo
parent 37ef6e2c15
commit 581bd2455e
Signed by: theo
SSH Key Fingerprint: SHA256:IbehMhSwpXrGUj7vj9iVvfdwe3g09IL9KLUz0zFzcXU
15 changed files with 392 additions and 16 deletions

View File

@ -615,6 +615,30 @@ video {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
.mb-4 {
margin-bottom: 1rem;
}
.mt-4 {
margin-top: 1rem;
}
.mt-6 {
margin-top: 1.5rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.mb-2\.5 {
margin-bottom: 0.625rem;
}
.me-3 {
margin-inline-end: 0.75rem;
}
.block { .block {
display: block; display: block;
} }
@ -627,6 +651,10 @@ video {
display: inline-flex; display: inline-flex;
} }
.grid {
display: grid;
}
.hidden { .hidden {
display: none; display: none;
} }
@ -651,6 +679,38 @@ video {
height: 100%; height: 100%;
} }
.h-32 {
height: 8rem;
}
.h-48 {
height: 12rem;
}
.h-96 {
height: 24rem;
}
.h-2\.5 {
height: 0.625rem;
}
.h-9 {
height: 2.25rem;
}
.h-7 {
height: 1.75rem;
}
.h-2 {
height: 0.5rem;
}
.h-4 {
height: 1rem;
}
.min-h-full { .min-h-full {
min-height: 100%; min-height: 100%;
} }
@ -675,6 +735,22 @@ video {
width: auto; width: auto;
} }
.w-32 {
width: 8rem;
}
.w-20 {
width: 5rem;
}
.w-24 {
width: 6rem;
}
.w-28 {
width: 7rem;
}
.max-w-7xl { .max-w-7xl {
max-width: 80rem; max-width: 80rem;
} }
@ -683,6 +759,10 @@ video {
max-width: 20rem; max-width: 20rem;
} }
.max-w-sm {
max-width: 24rem;
}
.flex-shrink-0 { .flex-shrink-0 {
flex-shrink: 0; flex-shrink: 0;
} }
@ -691,6 +771,24 @@ video {
transform-origin: top right; transform-origin: top right;
} }
@keyframes pulse {
50% {
opacity: .5;
}
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.items-center { .items-center {
align-items: center; align-items: center;
} }
@ -703,6 +801,10 @@ video {
justify-content: space-between; justify-content: space-between;
} }
.gap-4 {
gap: 1rem;
}
.space-y-1 > :not([hidden]) ~ :not([hidden]) { .space-y-1 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0; --tw-space-y-reverse: 0;
margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
@ -717,6 +819,22 @@ video {
border-radius: 0.375rem; border-radius: 0.375rem;
} }
.rounded-lg {
border-radius: 0.5rem;
}
.rounded {
border-radius: 0.25rem;
}
.border-2 {
border-width: 2px;
}
.border {
border-width: 1px;
}
.border-b { .border-b {
border-bottom-width: 1px; border-bottom-width: 1px;
} }
@ -725,16 +843,35 @@ video {
border-top-width: 1px; border-top-width: 1px;
} }
.border-dashed {
border-style: dashed;
}
.border-gray-200 { .border-gray-200 {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(229 231 235 / var(--tw-border-opacity)); border-color: rgb(229 231 235 / var(--tw-border-opacity));
} }
.border-gray-300 {
--tw-border-opacity: 1;
border-color: rgb(209 213 219 / var(--tw-border-opacity));
}
.bg-white { .bg-white {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
} }
.bg-gray-200 {
--tw-bg-opacity: 1;
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
}
.bg-gray-300 {
--tw-bg-opacity: 1;
background-color: rgb(209 213 219 / var(--tw-bg-opacity));
}
.p-1 { .p-1 {
padding: 0.25rem; padding: 0.25rem;
} }
@ -743,6 +880,10 @@ video {
padding: 0.5rem; padding: 0.5rem;
} }
.p-4 {
padding: 1rem;
}
.px-4 { .px-4 {
padding-left: 1rem; padding-left: 1rem;
padding-right: 1rem; padding-right: 1rem;
@ -826,12 +967,27 @@ video {
color: rgb(17 24 39 / var(--tw-text-opacity)); color: rgb(17 24 39 / var(--tw-text-opacity));
} }
.text-gray-200 {
--tw-text-opacity: 1;
color: rgb(229 231 235 / var(--tw-text-opacity));
}
.opacity-0 {
opacity: 0;
}
.shadow-lg { .shadow-lg {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
.shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.ring-1 { .ring-1 {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
@ -895,6 +1051,10 @@ video {
display: none; display: none;
} }
.sm\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.sm\:items-center { .sm\:items-center {
align-items: center; align-items: center;
} }
@ -911,6 +1071,20 @@ video {
} }
} }
@media (min-width: 768px) {
.md\:h-64 {
height: 16rem;
}
.md\:h-72 {
height: 18rem;
}
.md\:p-6 {
padding: 1.5rem;
}
}
@media (min-width: 1024px) { @media (min-width: 1024px) {
.lg\:block { .lg\:block {
display: block; display: block;
@ -920,8 +1094,39 @@ video {
display: none; display: none;
} }
.lg\:grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.lg\:px-8 { .lg\:px-8 {
padding-left: 2rem; padding-left: 2rem;
padding-right: 2rem; padding-right: 2rem;
} }
} }
@media (prefers-color-scheme: dark) {
.dark\:border-gray-600 {
--tw-border-opacity: 1;
border-color: rgb(75 85 99 / var(--tw-border-opacity));
}
.dark\:border-gray-700 {
--tw-border-opacity: 1;
border-color: rgb(55 65 81 / var(--tw-border-opacity));
}
.dark\:bg-gray-700 {
--tw-bg-opacity: 1;
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
}
.dark\:text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity));
}
.dark\:text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity));
}
}

View File

@ -1,3 +1,4 @@
mod pages;
mod templates; mod templates;
use std::path::Path; use std::path::Path;
@ -23,6 +24,7 @@ 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", pages::get_routes())
.merge(templates::get_routes()) .merge(templates::get_routes())
.fallback(fallback) .fallback(fallback)
} }

View File

@ -0,0 +1,10 @@
use askama::Template;
use askama_axum::IntoResponse;
#[derive(Template)]
#[template(path = "pages/cps.html")]
struct CpsResponse;
pub async fn cps() -> impl IntoResponse {
CpsResponse.into_response()
}

View File

@ -0,0 +1,10 @@
use askama::Template;
use askama_axum::IntoResponse;
#[derive(Template)]
#[template(path = "pages/home.html")]
struct HomeResponse;
pub async fn home() -> impl IntoResponse {
HomeResponse.into_response()
}

View File

@ -0,0 +1,10 @@
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

@ -5,7 +5,7 @@ use serde::Deserialize;
struct MenuItem { struct MenuItem {
label: String, label: String,
id: String, href: String,
current: bool, current: bool,
} }
@ -42,17 +42,12 @@ async fn menu(Query(params): Query<MenuParameters>) -> impl IntoResponse {
items: vec![ items: vec![
MenuItem { MenuItem {
label: "Accueil".to_string(), label: "Accueil".to_string(),
id: "home".to_string(), href: "/pages/home".to_string(),
current: true, current: true,
}, },
MenuItem { MenuItem {
label: "À propos".to_string(), label: "CPS".to_string(),
id: "about".to_string(), href: "/pages/cps".to_string(),
current: false,
},
MenuItem {
label: "Contact".to_string(),
id: "contact".to_string(),
current: false, current: false,
}, },
], ],

View File

@ -6,6 +6,8 @@
<script src="/assets/js/htmx.min.js"></script> <script src="/assets/js/htmx.min.js"></script>
<script src="//unpkg.com/alpinejs" defer></script> <script src="//unpkg.com/alpinejs" defer></script>
<link href="/assets/css/style.css" rel="stylesheet"> <link href="/assets/css/style.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/flowbite@2.5.1/dist/flowbite.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/flowbite@2.5.1/dist/flowbite.min.js"></script>
{% block head %}{% endblock %} {% block head %}{% endblock %}
</head> </head>

View File

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

View File

@ -5,5 +5,5 @@
hx-trigger="load" hx-trigger="load"
hx-swap="outerHTML" hx-swap="outerHTML"
> >
Chargement ... {% include "skeletons/menu-items.html" %}
</div> </div>

View File

@ -1,6 +1,10 @@
{% for item in items %} {% for item in items %}
<a <a
href="#{{ item.id }}" href=""
hx-get="{{ item.href }}"
hx-trigger="click"
hx-target="#main-container"
hx-swap="innerHTML"
class="{{ Self::get_classes(self, item.current) }}" class="{{ Self::get_classes(self, item.current) }}"
aria-current="{% if item.current %}page{% endif %}" aria-current="{% if item.current %}page{% endif %}"
> >

View File

@ -0,0 +1,52 @@
<h3 id="page-title" hx-swap-oob="textContent">
CPS
</h3>
<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"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
></div>
</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
></div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
></div>
<div class="grid grid-cols-2 gap-4">
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
</div>

View File

@ -0,0 +1,52 @@
<h3 id="page-title" hx-swap-oob="textContent">
Accueil
</h3>
<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"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
></div>
</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
></div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
</div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
></div>
<div class="grid grid-cols-2 gap-4">
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
<div
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
></div>
</div>

View File

@ -0,0 +1,22 @@
<div role="status" class="animate-pulse max-w-sm p-4 border border-gray-200 rounded shadow md:p-6 dark:border-gray-700">
<div class="flex items-center justify-center h-48 mb-4 bg-gray-300 rounded dark:bg-gray-700">
<svg class="w-10 h-10 text-gray-200 dark:text-gray-600" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 20">
<path d="M14.066 0H7v5a2 2 0 0 1-2 2H0v11a1.97 1.97 0 0 0 1.934 2h12.132A1.97 1.97 0 0 0 16 18V2a1.97 1.97 0 0 0-1.934-2ZM10.5 6a1.5 1.5 0 1 1 0 2.999A1.5 1.5 0 0 1 10.5 6Zm2.221 10.515a1 1 0 0 1-.858.485h-8a1 1 0 0 1-.9-1.43L5.6 10.039a.978.978 0 0 1 .936-.57 1 1 0 0 1 .9.632l1.181 2.981.541-1a.945.945 0 0 1 .883-.522 1 1 0 0 1 .879.529l1.832 3.438a1 1 0 0 1-.031.988Z"/>
<path d="M5 5V.13a2.96 2.96 0 0 0-1.293.749L.879 3.707A2.98 2.98 0 0 0 .13 5H5Z"/>
</svg>
</div>
<div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4"></div>
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5"></div>
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5"></div>
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
<div class="flex items-center mt-4">
<svg class="w-10 h-10 me-3 text-gray-200 dark:text-gray-700" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/>
</svg>
<div>
<div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-32 mb-2"></div>
<div class="w-48 h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
</div>
</div>
<span class="sr-only">Loading...</span>
</div>

View File

@ -0,0 +1,4 @@
<div role="status" class="animate-pulse flex items-center justify-center h-full">
<div class="w-32 h-4 bg-gray-200 rounded-full dark:bg-gray-700 me-3"></div>
<div class="w-32 h-4 bg-gray-200 rounded-full dark:bg-gray-700"></div>
</div>

View File

@ -0,0 +1 @@
<div role="status" class="animate-pulse h-7 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mt-3"></div>