Compare commits

...

4 Commits

24 changed files with 1479 additions and 38 deletions

View File

@ -1 +1,4 @@
/target /target
# Tailwind CSS CLI
tailwindcss

11
crates/app/README.md Normal file
View File

@ -0,0 +1,11 @@
## Pré-requis
- Récupérer le binaire TailwindCSS : https://tailwindcss.com/blog/standalone-cli
## Dev
- Lancer tailwindcss en mode watch dans un deuxième terminal :
```bash
./tailwindcss -i css/input.css -o assets/css/style.css --watch
```

File diff suppressed because it is too large Load Diff

3
crates/app/css/input.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -2,26 +2,27 @@ mod templates;
use std::path::Path; use std::path::Path;
use askama::Template;
use askama_axum::IntoResponse; use askama_axum::IntoResponse;
use templates::{hello::HelloResponse, index::GetIndexResponse}; use axum::http::{StatusCode, Uri};
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
async fn root() -> impl IntoResponse { async fn fallback(uri: Uri) -> (StatusCode, String) {
return GetIndexResponse {}.into_response(); (StatusCode::NOT_FOUND, format!("No route for {uri}"))
} }
async fn hello() -> impl IntoResponse { #[derive(Template)]
return HelloResponse { #[template(path = "index.html")]
name: "Theo".to_string(), pub struct GetIndexResponse;
}
.into_response(); async fn root() -> impl IntoResponse {
GetIndexResponse {}.into_response()
} }
pub fn get_router(assets_path: &Path) -> axum::Router { pub fn get_router(assets_path: &Path) -> axum::Router {
let 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))
.route("/hello", axum::routing::get(hello)); .merge(templates::get_routes())
.fallback(fallback)
router
} }

View File

@ -9,6 +9,7 @@ async fn main() {
let router = get_router(assets_path.as_path()); let router = get_router(assets_path.as_path());
// TODO: select port based on available port (or ask in CLI) // TODO: select port based on available port (or ask in CLI)
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); let listener = tokio::net::TcpListener::bind("localhost:3000").await.unwrap();
println!("Listening on: http://{}", listener.local_addr().unwrap());
axum::serve(listener, router).await.unwrap(); axum::serve(listener, router).await.unwrap();
} }

View File

@ -1,7 +1,20 @@
use askama::Template; use askama::Template;
use askama_axum::IntoResponse;
use axum::{routing, Router};
#[derive(Template)] #[derive(Template)]
#[template(path = "hello.html")] #[template(path = "hello.html")]
pub struct HelloResponse { struct HelloResponse {
pub name: String, pub name: String,
} }
async fn hello() -> impl IntoResponse {
HelloResponse {
name: "Theo".to_string(),
}.into_response()
}
pub fn get_routes() -> Router {
Router::new()
.route("/", routing::get(hello))
}

View File

@ -1,5 +0,0 @@
use askama::Template;
#[derive(Template)]
#[template(path = "index.html")]
pub struct GetIndexResponse;

View File

@ -1,2 +1,8 @@
pub mod hello; use axum::Router;
pub mod index;
mod hello;
pub fn get_routes() -> Router {
Router::new()
.nest("/hello", hello::get_routes())
}

View File

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./templates/**/*.html',
'./css/**/*.css',
],
theme: {
extend: {},
},
plugins: [],
}

View File

@ -1,13 +1,21 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="fr" class="h-full">
<head> <head>
<title>{% block title %}{{ title }}{% endblock %}</title> <title>{% block title %}{{ title }}{% endblock %}</title>
<script src="/assets/js/htmx.min.js"></script> <script src="/assets/js/htmx.min.js"></script>
<script src="//unpkg.com/alpinejs" defer></script>
<link href="/assets/css/style.css" rel="stylesheet">
{% block head %}{% endblock %} {% block head %}{% endblock %}
</head> </head>
<body> <body class="h-full">
{% block body %}{% endblock %} <div class="min-h-full">
{% block nav %}
{% include "layout/nav.html" %}
{% endblock %}
{% block body %}{% endblock %}
</div>
</body> </body>
</html> </html>

View File

@ -3,20 +3,24 @@
{% block title %}Pharma Libre{% endblock %} {% block title %}Pharma Libre{% endblock %}
{% block body %} {% block body %}
<div class="py-10">
<div>
<header> <header>
<h1>Pharma Libre</h1> <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>
</div>
</header> </header>
<main> <main>
<div <div class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
id="hello" <!-- Your content -->
hx-get="/hello" <div
hx-target="this" id="hello"
hx-trigger="load" hx-get="/hello"
hx-swap="outerHTML" hx-target="this"
> hx-trigger="load"
Loading... hx-swap="outerHTML"
>
Loading...
</div>
</div> </div>
</main> </main>
</div> </div>

View File

@ -0,0 +1,41 @@
<nav
class="border-b border-gray-200 bg-white"
x-data="{ menuOpen: false }"
>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="flex h-16 justify-between">
<div class="flex">
{% include "layout/nav/logo.html" %}
<div class="hidden sm:-my-px sm:ml-6 sm:flex sm:space-x-8">
{% include "layout/nav/desktop/menu-items.html" %}
</div>
</div>
<div class="hidden sm:ml-6 sm:flex sm:items-center">
{% include "layout/nav/desktop/notifications-button.html" %}
{% include "layout/nav/desktop/profile.html" %}
</div>
<div class="-mr-2 flex items-center sm:hidden">
{% include "layout/nav/mobile/menu-button.html" %}
</div>
</div>
</div>
<!-- Mobile menu, show/hide based on menu state. -->
<div
class="sm:hidden" id="mobile-menu"
x-show="menuOpen"
x-cloak
>
<div class="space-y-1 pb-3 pt-2">
{% include "layout/nav/mobile/menu-items.html" %}
</div>
<div class="border-t border-gray-200 pb-3 pt-4">
<div class="flex items-center px-4">
{% include "layout/nav/mobile/profile.html" %}
{% include "layout/nav/mobile/notifications-button.html" %}
</div>
<div class="mt-3 space-y-1">
{% include "layout/nav/mobile/profile-items.html" %}
</div>
</div>
</div>
</nav>

View File

@ -0,0 +1,22 @@
<!-- Current: "border-indigo-500 text-gray-900", Default: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700" -->
<a
href="#"
class="inline-flex items-center border-b-2 border-indigo-500 px-1 pt-1 text-sm font-medium text-gray-900"
aria-current="page"
>Dashboard</a
>
<a
href="#"
class="inline-flex items-center border-b-2 border-transparent px-1 pt-1 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700"
>Team</a
>
<a
href="#"
class="inline-flex items-center border-b-2 border-transparent px-1 pt-1 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700"
>Projects</a
>
<a
href="#"
class="inline-flex items-center border-b-2 border-transparent px-1 pt-1 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700"
>Calendar</a
>

View File

@ -0,0 +1,6 @@
<button
type="button"
class="relative rounded-full bg-white p-1 text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
{% include "layout/nav/notifications-icon.html" %}
</button>

View File

@ -0,0 +1,38 @@
<div
class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
role="menu"
id="profile-dropdown"
aria-orientation="vertical"
aria-labelledby="user-menu-button"
tabindex="-1"
x-show="profileOpen"
x-on:click.outside="profileOpen = false"
x-cloak
x-transition
>
<!-- Active: "bg-gray-100", Not Active: "" -->
<a
href="#"
class="block px-4 py-2 text-sm text-gray-700"
role="menuitem"
tabindex="-1"
id="user-menu-item-0"
>Your Profile</a
>
<a
href="#"
class="block px-4 py-2 text-sm text-gray-700"
role="menuitem"
tabindex="-1"
id="user-menu-item-1"
>Settings</a
>
<a
href="#"
class="block px-4 py-2 text-sm text-gray-700"
role="menuitem"
tabindex="-1"
id="user-menu-item-2"
>Sign out</a
>
</div>

View File

@ -0,0 +1,27 @@
<!-- Profile dropdown -->
<div
class="relative ml-3"
x-data="{ profileOpen: false }"
>
<div>
<button
type="button"
class="relative flex max-w-xs items-center rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
id="user-menu-button"
aria-controls="profile-dropdown"
aria-expanded="false"
aria-haspopup="menu"
x-on:click="profileOpen = ! profileOpen"
>
<span class="absolute -inset-1.5"></span>
<span class="sr-only">Open user menu</span>
<img
class="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt=""
/>
</button>
</div>
{% include "layout/nav/desktop/profile-dropdown.html" %}
</div>

View File

@ -0,0 +1,4 @@
<div class="flex flex-shrink-0 items-center">
<img class="block h-8 w-auto lg:hidden" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company">
<img class="hidden h-8 w-auto lg:block" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company">
</div>

View File

@ -0,0 +1,43 @@
<!-- Mobile menu button -->
<button
type="button"
class="relative inline-flex items-center justify-center rounded-md bg-white p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
aria-controls="mobile-menu"
x-on:click="menuOpen = ! menuOpen"
x-bind:aria-expanded="menuOpen"
>
<span class="absolute -inset-0.5"></span>
<span class="sr-only">Open main menu</span>
<!-- Menu open: "hidden", Menu closed: "block" -->
<svg
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
x-bind:class="menuOpen ? 'hidden' : 'block'"
x-bind:aria-hidden="menuOpen"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
/>
</svg>
<!-- Menu open: "block", Menu closed: "hidden" -->
<svg
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
x-bind:class="menuOpen ? 'block' : 'hidden'"
x-bind:aria-hidden="! menuOpen"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>

View File

@ -0,0 +1,22 @@
<!-- Current: "border-indigo-500 bg-indigo-50 text-indigo-700", Default: "border-transparent text-gray-600 hover:border-gray-300 hover:bg-gray-50 hover:text-gray-800" -->
<a
href="#"
class="block border-l-4 border-indigo-500 bg-indigo-50 py-2 pl-3 pr-4 text-base font-medium text-indigo-700"
aria-current="page"
>Dashboard</a
>
<a
href="#"
class="block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-gray-600 hover:border-gray-300 hover:bg-gray-50 hover:text-gray-800"
>Team</a
>
<a
href="#"
class="block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-gray-600 hover:border-gray-300 hover:bg-gray-50 hover:text-gray-800"
>Projects</a
>
<a
href="#"
class="block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-gray-600 hover:border-gray-300 hover:bg-gray-50 hover:text-gray-800"
>Calendar</a
>

View File

@ -0,0 +1,6 @@
<button
type="button"
class="relative ml-auto flex-shrink-0 rounded-full bg-white p-1 text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
{% include "layout/nav/notifications-icon.html" %}
</button>

View File

@ -0,0 +1,15 @@
<a
href="#"
class="block px-4 py-2 text-base font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-800"
>Your Profile</a
>
<a
href="#"
class="block px-4 py-2 text-base font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-800"
>Settings</a
>
<a
href="#"
class="block px-4 py-2 text-base font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-800"
>Sign out</a
>

View File

@ -0,0 +1,11 @@
<div class="flex-shrink-0">
<img
class="h-10 w-10 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt=""
/>
</div>
<div class="ml-3">
<div class="text-base font-medium text-gray-800">Tom Cook</div>
<div class="text-sm font-medium text-gray-500">tom@example.com</div>
</div>

View File

@ -0,0 +1,16 @@
<span class="absolute -inset-1.5"></span>
<span class="sr-only">View notifications</span>
<svg
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"
/>
</svg>