diff --git a/Cargo.lock b/Cargo.lock index b80a2a9..197525e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,6 +69,7 @@ dependencies = [ "askama", "askama_axum", "axum", + "serde", "tokio", "tower-http", ] diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 786b7b6..23c7568 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" askama = "0.12.1" askama_axum = "0.4.0" axum = "0.7.5" +serde = { version = "1.0.204", features = ["derive"] } tokio = { version = "1.39.1", features = ["macros", "rt-multi-thread"] } tower-http = { version = "0.5.2", features = ["fs"] } diff --git a/crates/app/assets/css/style.css b/crates/app/assets/css/style.css index ad1cf1d..40ca251 100644 --- a/crates/app/assets/css/style.css +++ b/crates/app/assets/css/style.css @@ -566,10 +566,6 @@ video { border-width: 0; } -.visible { - visibility: visible; -} - .absolute { position: absolute; } @@ -586,15 +582,6 @@ video { inset: -0.375rem; } -.inset-y-0 { - top: 0px; - bottom: 0px; -} - -.left-0 { - left: 0px; -} - .right-0 { right: 0px; } @@ -608,22 +595,22 @@ video { margin-right: auto; } -.ml-3 { - margin-left: 0.75rem; -} - -.mt-2 { - margin-top: 0.5rem; -} - .-mr-2 { margin-right: -0.5rem; } +.ml-3 { + margin-left: 0.75rem; +} + .ml-auto { margin-left: auto; } +.mt-2 { + margin-top: 0.5rem; +} + .mt-3 { margin-top: 0.75rem; } @@ -644,6 +631,10 @@ video { display: none; } +.h-10 { + height: 2.5rem; +} + .h-16 { height: 4rem; } @@ -660,14 +651,14 @@ video { height: 100%; } -.h-10 { - height: 2.5rem; -} - .min-h-full { min-height: 100%; } +.w-10 { + width: 2.5rem; +} + .w-48 { width: 12rem; } @@ -684,10 +675,6 @@ video { width: auto; } -.w-10 { - width: 2.5rem; -} - .max-w-7xl { max-width: 80rem; } @@ -696,10 +683,6 @@ video { max-width: 20rem; } -.flex-1 { - flex: 1 1 0%; -} - .flex-shrink-0 { flex-shrink: 0; } @@ -708,32 +691,6 @@ video { transform-origin: top right; } -.-translate-y-full { - --tw-translate-y: -100%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.translate-y-0 { - --tw-translate-y: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.scale-100 { - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.scale-95 { - --tw-scale-x: .95; - --tw-scale-y: .95; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - .items-center { align-items: center; } @@ -760,6 +717,10 @@ video { border-radius: 0.375rem; } +.border-b { + border-bottom-width: 1px; +} + .border-b-2 { border-bottom-width: 2px; } @@ -768,14 +729,15 @@ video { border-left-width: 4px; } -.border-b { - border-bottom-width: 1px; -} - .border-t { border-top-width: 1px; } +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); +} + .border-indigo-500 { --tw-border-opacity: 1; border-color: rgb(99 102 241 / var(--tw-border-opacity)); @@ -785,11 +747,6 @@ video { border-color: transparent; } -.border-gray-200 { - --tw-border-opacity: 1; - border-color: rgb(229 231 235 / var(--tw-border-opacity)); -} - .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgb(243 244 246 / var(--tw-bg-opacity)); @@ -818,11 +775,6 @@ video { padding-right: 0.25rem; } -.px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - .px-4 { padding-left: 1rem; padding-right: 1rem; @@ -848,18 +800,14 @@ video { padding-bottom: 2rem; } -.pb-4 { - padding-bottom: 1rem; +.pb-3 { + padding-bottom: 0.75rem; } .pl-3 { padding-left: 0.75rem; } -.pr-2 { - padding-right: 0.5rem; -} - .pr-4 { padding-right: 1rem; } @@ -872,10 +820,6 @@ video { padding-top: 0.5rem; } -.pb-3 { - padding-bottom: 0.75rem; -} - .pt-4 { padding-top: 1rem; } @@ -921,11 +865,21 @@ video { color: rgb(107 114 128 / var(--tw-text-opacity)); } +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + .text-gray-700 { --tw-text-opacity: 1; color: rgb(55 65 81 / var(--tw-text-opacity)); } +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + .text-gray-900 { --tw-text-opacity: 1; color: rgb(17 24 39 / var(--tw-text-opacity)); @@ -936,30 +890,6 @@ video { color: rgb(67 56 202 / var(--tw-text-opacity)); } -.text-gray-600 { - --tw-text-opacity: 1; - color: rgb(75 85 99 / var(--tw-text-opacity)); -} - -.text-gray-800 { - --tw-text-opacity: 1; - color: rgb(31 41 55 / var(--tw-text-opacity)); -} - -.opacity-0 { - opacity: 0; -} - -.opacity-100 { - opacity: 1; -} - -.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); -} - .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-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); @@ -981,34 +911,6 @@ video { --tw-ring-opacity: 0.05; } -.transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.duration-200 { - transition-duration: 200ms; -} - -.duration-75 { - transition-duration: 75ms; -} - -.duration-300 { - transition-duration: 300ms; -} - -.ease-in { - transition-timing-function: cubic-bezier(0.4, 0, 1, 1); -} - -.ease-out { - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); -} - .hover\:border-gray-300:hover { --tw-border-opacity: 1; border-color: rgb(209 213 219 / var(--tw-border-opacity)); @@ -1050,10 +952,6 @@ video { box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); } -.focus\:ring-inset:focus { - --tw-ring-inset: inset; -} - .focus\:ring-indigo-500:focus { --tw-ring-opacity: 1; --tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity)); @@ -1064,14 +962,6 @@ video { } @media (min-width: 640px) { - .sm\:static { - position: static; - } - - .sm\:inset-auto { - inset: auto; - } - .sm\:-my-px { margin-top: -1px; margin-bottom: -1px; @@ -1093,14 +983,6 @@ video { align-items: center; } - .sm\:items-stretch { - align-items: stretch; - } - - .sm\:justify-start { - justify-content: flex-start; - } - .sm\:space-x-8 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(2rem * var(--tw-space-x-reverse)); @@ -1111,10 +993,6 @@ video { padding-left: 1.5rem; padding-right: 1.5rem; } - - .sm\:pr-0 { - padding-right: 0px; - } } @media (min-width: 1024px) { diff --git a/crates/app/src/templates/mod.rs b/crates/app/src/templates/mod.rs index 11b9607..c0b91c0 100644 --- a/crates/app/src/templates/mod.rs +++ b/crates/app/src/templates/mod.rs @@ -1,8 +1,12 @@ use axum::Router; mod hello; +mod nav; +mod profile; pub fn get_routes() -> Router { Router::new() .nest("/hello", hello::get_routes()) + .nest("/nav", nav::get_routes()) + .nest("/profile", profile::get_routes()) } diff --git a/crates/app/src/templates/nav.rs b/crates/app/src/templates/nav.rs new file mode 100644 index 0000000..5675ec7 --- /dev/null +++ b/crates/app/src/templates/nav.rs @@ -0,0 +1,65 @@ +use askama::Template; +use askama_axum::IntoResponse; +use axum::{extract::Query, routing, Router}; +use serde::Deserialize; + +struct MenuItem { + label: String, + id: String, + current: bool, +} + +#[derive(Deserialize)] +struct MenuParameters { + mobile: bool, +} + +#[derive(Template)] +#[template(path = "layout/nav/nav-menu-items.html")] +struct MenuResponse { + mobile: bool, + items: Vec, +} + +impl MenuResponse { + fn get_classes(&self, is_current_item: &bool) -> String { + let common_classes = match self.mobile { + true => "block border-l-4 py-2 pl-3 pr-4 text-base font-medium".to_string(), + false => "inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium".to_string(), + }; + match (self.mobile, is_current_item) { + (true, true) => common_classes + " border-indigo-500 bg-indigo-50 text-indigo-700", + (true, false) => common_classes + " border-transparent text-gray-600 hover:border-gray-300 hover:bg-gray-50 hover:text-gray-800", + (false, true) => common_classes + " border-indigo-500 text-gray-900", + (false, false) => common_classes + " border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700", + } + } +} + +async fn menu(Query(params): Query) -> impl IntoResponse { + MenuResponse { + mobile: params.mobile, + items: vec![ + MenuItem { + label: "Accueil".to_string(), + id: "home".to_string(), + current: true, + }, + MenuItem { + label: "À propos".to_string(), + id: "about".to_string(), + current: false, + }, + MenuItem { + label: "Contact".to_string(), + id: "contact".to_string(), + current: false, + }, + ], + }.into_response() +} + +pub fn get_routes() -> Router { + Router::new() + .route("/menu", routing::get(menu)) +} diff --git a/crates/app/src/templates/profile.rs b/crates/app/src/templates/profile.rs new file mode 100644 index 0000000..4b1d186 --- /dev/null +++ b/crates/app/src/templates/profile.rs @@ -0,0 +1,65 @@ +use askama::Template; +use askama_axum::IntoResponse; +use axum::{extract::Query, routing, Router}; +use serde::Deserialize; + +struct MenuItem { + label: String, + id: String, + current: bool, +} + +#[derive(Deserialize)] +struct MenuParameters { + mobile: bool, +} + +#[derive(Template)] +#[template(path = "layout/nav/profile-menu-items.html")] +struct MenuResponse { + mobile: bool, + items: Vec, +} + +impl MenuResponse { + fn get_classes(&self, is_current_item: &bool) -> String { + let common_classes = match self.mobile { + true => "block px-4 py-2 text-base font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-800".to_string(), + false => "block px-4 py-2 text-sm text-gray-700".to_string(), + }; + match (self.mobile, is_current_item) { + (true, true) => common_classes + "", // ??? + (true, false) => common_classes + "", + (false, true) => common_classes + " bg-gray-100", + (false, false) => common_classes + "", + } + } +} + +async fn menu(Query(params): Query) -> impl IntoResponse { + MenuResponse { + mobile: params.mobile, + items: vec![ + MenuItem { + label: "Votre profil".to_string(), + id: "profile".to_string(), + current: false, + }, + MenuItem { + label: "Paramètres".to_string(), + id: "settings".to_string(), + current: false, + }, + MenuItem { + label: "Déconnexion".to_string(), + id: "logout".to_string(), + current: false, + }, + ], + }.into_response() +} + +pub fn get_routes() -> Router { + Router::new() + .route("/menu", routing::get(menu)) +} \ No newline at end of file diff --git a/crates/app/templates/index.html b/crates/app/templates/index.html index 3881b0d..427de19 100644 --- a/crates/app/templates/index.html +++ b/crates/app/templates/index.html @@ -19,7 +19,7 @@ hx-trigger="load" hx-swap="outerHTML" > - Loading... + Chargement ... diff --git a/crates/app/templates/layout/nav/desktop/menu-items.html b/crates/app/templates/layout/nav/desktop/menu-items.html index 43190ea..6fb9e25 100644 --- a/crates/app/templates/layout/nav/desktop/menu-items.html +++ b/crates/app/templates/layout/nav/desktop/menu-items.html @@ -1,22 +1,9 @@ - -Dashboard -Team -Projects -Calendar + Chargement ... + diff --git a/crates/app/templates/layout/nav/desktop/profile-dropdown.html b/crates/app/templates/layout/nav/desktop/profile-dropdown.html index 4c2f44e..73667f7 100644 --- a/crates/app/templates/layout/nav/desktop/profile-dropdown.html +++ b/crates/app/templates/layout/nav/desktop/profile-dropdown.html @@ -10,29 +10,13 @@ x-cloak x-transition > - - Your Profile - Settings - Sign out + Chargement ... + diff --git a/crates/app/templates/layout/nav/mobile/menu-items.html b/crates/app/templates/layout/nav/mobile/menu-items.html index b1e5088..1bece64 100644 --- a/crates/app/templates/layout/nav/mobile/menu-items.html +++ b/crates/app/templates/layout/nav/mobile/menu-items.html @@ -1,22 +1,9 @@ - -Dashboard -Team -Projects -Calendar + Chargement ... + diff --git a/crates/app/templates/layout/nav/mobile/profile-items.html b/crates/app/templates/layout/nav/mobile/profile-items.html index 0bc27c8..9f84346 100644 --- a/crates/app/templates/layout/nav/mobile/profile-items.html +++ b/crates/app/templates/layout/nav/mobile/profile-items.html @@ -1,15 +1,9 @@ -Your Profile -Settings -Sign out + Chargement ... + diff --git a/crates/app/templates/layout/nav/nav-menu-items.html b/crates/app/templates/layout/nav/nav-menu-items.html new file mode 100644 index 0000000..92bb706 --- /dev/null +++ b/crates/app/templates/layout/nav/nav-menu-items.html @@ -0,0 +1,9 @@ +{% for item in items %} + + {{ item.label }} + +{% endfor %} \ No newline at end of file diff --git a/crates/app/templates/layout/nav/profile-menu-items.html b/crates/app/templates/layout/nav/profile-menu-items.html new file mode 100644 index 0000000..95b0064 --- /dev/null +++ b/crates/app/templates/layout/nav/profile-menu-items.html @@ -0,0 +1,11 @@ +{% for item in items %} + + {{ item.label }} + +{% endfor %}