Updated UI

This commit is contained in:
2025-07-15 20:28:29 +02:00
parent 5c0bb602f5
commit 39006435b2
11 changed files with 139 additions and 63 deletions

View File

@@ -13,12 +13,12 @@
--text-xs--line-height: calc(1 / 0.75); --text-xs--line-height: calc(1 / 0.75);
--text-sm: 0.875rem; --text-sm: 0.875rem;
--text-sm--line-height: calc(1.25 / 0.875); --text-sm--line-height: calc(1.25 / 0.875);
--text-lg: 1.125rem;
--text-lg--line-height: calc(1.75 / 1.125);
--text-xl: 1.25rem; --text-xl: 1.25rem;
--text-xl--line-height: calc(1.75 / 1.25); --text-xl--line-height: calc(1.75 / 1.25);
--text-2xl: 1.5rem; --text-2xl: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5); --text-2xl--line-height: calc(2 / 1.5);
--text-3xl: 1.875rem;
--text-3xl--line-height: calc(2.25 / 1.875);
--font-weight-bold: 700; --font-weight-bold: 700;
--default-font-family: var(--font-sans); --default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono); --default-mono-font-family: var(--font-mono);
@@ -956,9 +956,18 @@
justify-content: flex-end; justify-content: flex-end;
gap: calc(0.25rem * 2); gap: calc(0.25rem * 2);
} }
.mt-2 {
margin-top: calc(var(--spacing) * 2);
}
.mt-3 { .mt-3 {
margin-top: calc(var(--spacing) * 3); margin-top: calc(var(--spacing) * 3);
} }
.mr-1 {
margin-right: calc(var(--spacing) * 1);
}
.mr-3 {
margin-right: calc(var(--spacing) * 3);
}
.fieldset-legend { .fieldset-legend {
margin-bottom: calc(0.25rem * -1); margin-bottom: calc(0.25rem * -1);
display: flex; display: flex;
@@ -1022,6 +1031,9 @@
.h-\[1\.2em\] { .h-\[1\.2em\] {
height: 1.2em; height: 1.2em;
} }
.h-\[92px\] {
height: 92px;
}
.h-full { .h-full {
height: 100%; height: 100%;
} }
@@ -1037,6 +1049,15 @@
.w-56 { .w-56 {
width: calc(var(--spacing) * 56); width: calc(var(--spacing) * 56);
} }
.w-\[1\.5em\] {
width: 1.5em;
}
.w-\[1\.6em\] {
width: 1.6em;
}
.w-\[1\.7em\] {
width: 1.7em;
}
.w-full { .w-full {
width: 100%; width: 100%;
} }
@@ -1068,6 +1089,12 @@
.flex-wrap { .flex-wrap {
flex-wrap: wrap; flex-wrap: wrap;
} }
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.gap-2 { .gap-2 {
gap: calc(var(--spacing) * 2); gap: calc(var(--spacing) * 2);
} }
@@ -1100,20 +1127,21 @@
.p-3 { .p-3 {
padding: calc(var(--spacing) * 3); padding: calc(var(--spacing) * 3);
} }
.p-5 {
padding: calc(var(--spacing) * 5);
}
.py-2 { .py-2 {
padding-block: calc(var(--spacing) * 2); padding-block: calc(var(--spacing) * 2);
} }
.py-10 {
padding-block: calc(var(--spacing) * 10);
}
.pt-3 { .pt-3 {
padding-top: calc(var(--spacing) * 3); padding-top: calc(var(--spacing) * 3);
} }
.pb-6 { .pb-6 {
padding-bottom: calc(var(--spacing) * 6); padding-bottom: calc(var(--spacing) * 6);
} }
.pl-10 { .text-2xl {
padding-left: calc(var(--spacing) * 10); font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
} }
.text-sm { .text-sm {
font-size: var(--text-sm); font-size: var(--text-sm);
@@ -1164,6 +1192,10 @@
--btn-color: var(--color-error); --btn-color: var(--color-error);
--btn-fg: var(--color-error-content); --btn-fg: var(--color-error-content);
} }
.btn-success {
--btn-color: var(--color-success);
--btn-fg: var(--color-success-content);
}
.hover\:cursor-pointer { .hover\:cursor-pointer {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {
@@ -1189,18 +1221,17 @@
} }
} }
} }
h1 {
font-size: var(--text-3xl);
line-height: var(--tw-leading, var(--text-3xl--line-height));
}
h2 { h2 {
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
h3 {
font-size: var(--text-xl); font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height)); line-height: var(--tw-leading, var(--text-xl--line-height));
} }
h3 {
font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height));
}
.desc {
opacity: 80%;
}
@layer base { @layer base {
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] {
color-scheme: light; color-scheme: light;

View File

@@ -2,4 +2,13 @@ mod eye;
mod search; mod search;
pub use eye::eye; pub use eye::eye;
use maud::{Markup, html};
pub use search::search; pub use search::search;
pub fn arrow_left() -> Markup {
html! {
svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 256 256" {
path fill="currentColor" d="M224 128a8 8 0 0 1-8 8H59.31l58.35 58.34a8 8 0 0 1-11.32 11.32l-72-72a8 8 0 0 1 0-11.32l72-72a8 8 0 0 1 11.32 11.32L59.31 120H216a8 8 0 0 1 8 8" {}
}
}
}

View File

@@ -1,5 +1,7 @@
use maud::{Markup, html}; use maud::{Markup, html};
use crate::icons;
use super::empty; use super::empty;
pub fn desktop_minimal(content: Markup, name: &str) -> Markup { pub fn desktop_minimal(content: Markup, name: &str) -> Markup {
@@ -8,7 +10,7 @@ pub fn desktop_minimal(content: Markup, name: &str) -> Markup {
div class="w-56" { div class="w-56" {
(sidebar()) (sidebar())
} }
div class="w-full overflow-y-auto" { div class="w-full h-full overflow-y-auto" {
(content) (content)
} }
} }
@@ -17,10 +19,13 @@ pub fn desktop_minimal(content: Markup, name: &str) -> Markup {
empty(content, name) empty(content, name)
} }
pub fn desktop(content: Markup, name: &str) -> Markup { pub fn desktop(content: Markup, name: &str, back_button: bool) -> Markup {
let content = html! { let content = html! {
div class="pl-10 py-10 w-full max-w-2xl" { div class="flex justify-center" {
(content) div class="p-5 w-full max-w-2xl" {
(header(name, back_button))
(content)
}
} }
}; };
@@ -47,3 +52,16 @@ fn sidebar() -> Markup {
} }
} }
} }
fn header(title: &str, back_button: bool) -> Markup {
html! {
div class="flex items-center mb-5" {
@if back_button {
button onclick="history.back()" class="w-[1.6em] mr-1" {
(icons::arrow_left())
}
}
h1 class="text-2xl" { (title) }
}
}
}

View File

@@ -21,17 +21,17 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
.await?; .await?;
let content = html! { let content = html! {
h1 { "Exercises" }
a href="/exercises/new" { "new exercise +" } a href="/exercises/new" { "new exercise +" }
ul class="list" {
div class="list" {
@for exercise in exercises { @for exercise in exercises {
li hx-get={ "/exercises/" (exercise.exercise_id) } hx-target="body" hx-push-url="true" class="list-row" { a href={"/exercises/" (exercise.exercise_id)} class="list-row" {
div {} div {}
div { div {
div class="font-bold" { (exercise.name) } div class="font-bold" { (exercise.name) }
div class="text-xs" { (exercise.description) } div class="text-xs" { (exercise.description) }
} }
a class="btn btn-square btn-ghost" { div class="btn btn-square btn-ghost" {
div class="size-[1.6em]" { div class="size-[1.6em]" {
(icons::eye()) (icons::eye())
} }
@@ -41,5 +41,5 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
} }
}; };
Ok(layouts::desktop(content, "Exercises")) Ok(layouts::desktop(content, "Browse Exercises", false))
} }

View File

@@ -1,4 +1,4 @@
use crate::{AppError, layouts, util::AppState}; use crate::{AppError, layouts, models::exercises::ExerciseFull, util::AppState};
use axum::{ use axum::{
Form, Router, Form, Router,
extract::{Path, State}, extract::{Path, State},
@@ -26,17 +26,16 @@ async fn page(State(state): State<AppState>, Path(id): Path<Uuid>) -> Result<Mar
.await?; .await?;
let content = html! { let content = html! {
div { h2 { (exercise.name) }
div class="flex w-full" { p class="desc" { (exercise.description) }
h1 { (exercise.name) }
a href={"/exercises/" (exercise.exercise_id) "/edit" } class="ml-auto btn" { "Edit" } a href={"/exercises/" (id) "/edit"} class="ml-auto btn" { "Edit" }
}
p { (exercise.description) } p { (exercise.description) }
button hx-delete={ "/exercises/" (exercise.exercise_id) } hx-confirm="Are you sure that you want to delete this exercise?" class="btn btn-error" { "Delete Exercise" } button hx-delete={"/exercises/" (id)} hx-confirm="Are you sure that you want to delete this exercise?" class="btn btn-error" { "Delete Exercise" }
}
}; };
Ok(layouts::desktop(content, "Exercises")) Ok(layouts::desktop(content, "View Exercise", true))
} }
async fn delete( async fn delete(
@@ -70,8 +69,6 @@ async fn edit(
.await?; .await?;
let content = html! { let content = html! {
h1 class="mb-5" { "Edit Exercise" }
form hx-put={"/exercises/" (exercise.exercise_id)} class="space-y-1" { form hx-put={"/exercises/" (exercise.exercise_id)} class="space-y-1" {
fieldset class="fieldset" { fieldset class="fieldset" {
legend class="fieldset-legend" { "Name" } legend class="fieldset-legend" { "Name" }
@@ -87,7 +84,7 @@ async fn edit(
} }
}; };
Ok(layouts::desktop(content, "Edit Exercises")) Ok(layouts::desktop(content, "Edit", true))
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

View File

@@ -28,8 +28,6 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
.collect(); .collect();
let content = html! { let content = html! {
h1 class="mb-5" { "New Exercise" }
form hx-post="/exercises/new" class="space-y-1" { form hx-post="/exercises/new" class="space-y-1" {
fieldset class="fieldset" { fieldset class="fieldset" {
legend class="fieldset-legend" { "Name" } legend class="fieldset-legend" { "Name" }
@@ -49,7 +47,7 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
} }
}; };
Ok(layouts::desktop(content, "New Exercise")) Ok(layouts::desktop(content, "New Exercise", true))
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

View File

@@ -9,5 +9,5 @@ pub fn routes() -> Router<AppState> {
async fn page() -> Markup { async fn page() -> Markup {
let content = html! {}; let content = html! {};
layouts::desktop(content, "Home") layouts::desktop(content, "Home", false)
} }

View File

@@ -21,7 +21,6 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
.await?; .await?;
let content = html! { let content = html! {
h1 { "Workouts" }
a href="/workouts/new" { "new workout +" } a href="/workouts/new" { "new workout +" }
div class="list" { div class="list" {
@for workout in workouts { @for workout in workouts {
@@ -41,5 +40,5 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
} }
}; };
Ok(layouts::desktop(content, "Workouts")) Ok(layouts::desktop(content, "Browse Workouts", false))
} }

View File

@@ -45,25 +45,51 @@ async fn page(State(state): State<AppState>, Path(id): Path<Uuid>) -> Result<Mar
let content = html! { let content = html! {
div { div {
div class="flex w-full" { div class="mb-5" {
h1 { (workout.name) } h2 { (workout.name) }
a href={"/workouts/" (workout.workout_id) "/edit" } class="ml-auto btn" { "Edit" } p class="desc" { (workout.description) }
} }
p { (workout.description) }
h2 { "Exercises" } h3 { "Exercises" }
div class="flex flex-col gap-y-3" { div class="flex flex-col gap-y-3" {
@for exercise in exercises { @for exercise in exercises {
div class="bg-base-200 p-3 rounded" { (display_exercise(exercise))
h3 { (exercise.name) }
p class="text-sm opacity-50" { (exercise.description) }
}
} }
} }
a href={"/workouts/" (id) "/start"} class="btn btn-success" { "Start Workout" }
} }
}; };
Ok(layouts::desktop(content, "Exercises")) Ok(layouts::desktop(content, "View Workout", true))
}
fn display_exercise(exercise: WorkoutExerciseFull) -> Markup {
html! {
div class="bg-base-200 p-3 rounded flex" {
div {
h3 { (exercise.name) }
p class="text-sm opacity-50" { (exercise.description) }
}
div class="ml-auto flex items-center" {
@match exercise.exercise_type {
ExerciseVariant::Time => {
div { (exercise.time.unwrap_or(-1))"s" }
},
ExerciseVariant::Number => {
div {
div { (exercise.sets.unwrap_or(-1)) " sets" }
div { (exercise.reps.unwrap_or(-1)) " reps" }
}
},
ExerciseVariant::Failure => {
"Failure"
},
}
}
}
}
} }
async fn delete( async fn delete(
@@ -114,7 +140,7 @@ async fn edit(
} }
}; };
Ok(layouts::desktop(content, "Edit Exercises")) Ok(layouts::desktop(content, "Edit Exercises", true))
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

View File

@@ -29,8 +29,6 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
.await?; .await?;
let content = html! { let content = html! {
h1 class="mb-5" { "New Workout" }
div x-data="{ exercises: [] }" { div x-data="{ exercises: [] }" {
form hx-post="/workouts/new" class="space-y-1" { form hx-post="/workouts/new" class="space-y-1" {
fieldset class="fieldset" { fieldset class="fieldset" {
@@ -132,7 +130,7 @@ async fn page(State(state): State<AppState>) -> Result<Markup, AppError> {
}; };
Ok(layouts::desktop(content, "New Exercise")) Ok(layouts::desktop(content, "New Workout", true))
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

View File

@@ -39,14 +39,14 @@
--noise: 0; --noise: 0;
} }
h1 {
@apply text-3xl
}
h2 { h2 {
@apply text-2xl @apply text-xl
} }
h3 { h3 {
@apply text-xl @apply text-lg
}
.desc {
@apply opacity-80
} }