diff --git a/assets/icon/android-chrome-192x192.png b/assets/icon/android-chrome-192x192.png new file mode 100644 index 0000000..8438ec2 Binary files /dev/null and b/assets/icon/android-chrome-192x192.png differ diff --git a/assets/icon/android-chrome-512x512.png b/assets/icon/android-chrome-512x512.png new file mode 100644 index 0000000..f4af23c Binary files /dev/null and b/assets/icon/android-chrome-512x512.png differ diff --git a/assets/icon/apple-touch-icon.png b/assets/icon/apple-touch-icon.png new file mode 100644 index 0000000..aa62bae Binary files /dev/null and b/assets/icon/apple-touch-icon.png differ diff --git a/assets/icon/favicon-16x16.png b/assets/icon/favicon-16x16.png new file mode 100644 index 0000000..6f2438d Binary files /dev/null and b/assets/icon/favicon-16x16.png differ diff --git a/assets/icon/favicon-32x32.png b/assets/icon/favicon-32x32.png new file mode 100644 index 0000000..3cdc25d Binary files /dev/null and b/assets/icon/favicon-32x32.png differ diff --git a/assets/icon/favicon.ico b/assets/icon/favicon.ico new file mode 100644 index 0000000..c4b4f4e Binary files /dev/null and b/assets/icon/favicon.ico differ diff --git a/assets/icon/site.webmanifest b/assets/icon/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/assets/icon/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/assets/style.css b/assets/style.css index bb0b1a8..6105bed 100644 --- a/assets/style.css +++ b/assets/style.css @@ -1007,6 +1007,16 @@ html { } @media (hover: hover) { + .btm-nav > *.disabled:hover, + .btm-nav > *[disabled]:hover { + pointer-events: none; + --tw-border-opacity: 0; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-bg-opacity: 0.1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; + } + .btn:hover { --tw-border-opacity: 1; border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); @@ -1278,6 +1288,16 @@ html { align-items: center; } +.btm-nav > *.disabled, + .btm-nav > *[disabled] { + pointer-events: none; + --tw-border-opacity: 0; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-bg-opacity: 0.1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; +} + .btm-nav > * .label { font-size: 1rem; line-height: 1.5rem; @@ -1304,6 +1324,14 @@ html { .btn-primary { --btn-color: var(--fallback-p); } + + .btn-secondary { + --btn-color: var(--fallback-s); + } + + .btn-error { + --btn-color: var(--fallback-er); + } } @supports (color: color-mix(in oklab, black, black)) { @@ -1359,6 +1387,26 @@ html { .btn-primary { --btn-color: var(--p); } + + .btn-secondary { + --btn-color: var(--s); + } + + .btn-error { + --btn-color: var(--er); + } +} + +.btn-secondary { + --tw-text-opacity: 1; + color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); + outline-color: var(--fallback-s,oklch(var(--s)/1)); +} + +.btn-error { + --tw-text-opacity: 1; + color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); + outline-color: var(--fallback-er,oklch(var(--er)/1)); } .btn.glass { @@ -1683,27 +1731,6 @@ html { outline-offset: 2px; } -.loading { - pointer-events: none; - display: inline-block; - aspect-ratio: 1 / 1; - width: 1.5rem; - background-color: currentColor; - -webkit-mask-size: 100%; - mask-size: 100%; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-position: center; - mask-position: center; - -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); -} - -.loading-spinner { - -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); -} - :where(.menu li:empty) { --tw-bg-opacity: 1; background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); @@ -1897,11 +1924,6 @@ html { } } -[type="checkbox"].checkbox-sm { - height: 1.25rem; - width: 1.25rem; -} - .menu-horizontal { display: inline-flex; flex-direction: row; @@ -1950,14 +1972,6 @@ html { margin-bottom: 2rem; } -.mr-2 { - margin-right: 0.5rem; -} - -.mt-2 { - margin-top: 0.5rem; -} - .mt-4 { margin-top: 1rem; } @@ -1986,10 +2000,22 @@ html { min-height: 100vh; } +.min-h-full { + min-height: 100%; +} + .w-full { width: 100%; } +.min-w-20 { + min-width: 5rem; +} + +.min-w-40 { + min-width: 10rem; +} + .max-w-2xl { max-width: 42rem; } @@ -2010,6 +2036,10 @@ html { flex: none; } +.flex-grow { + flex-grow: 1; +} + .cursor-pointer { cursor: pointer; } @@ -2030,12 +2060,20 @@ html { align-items: center; } +.justify-start { + justify-content: flex-start; +} + .justify-center { justify-content: center; } -.justify-between { - justify-content: space-between; +.gap-2 { + gap: 0.5rem; +} + +.gap-4 { + gap: 1rem; } .space-y-4 > :not([hidden]) ~ :not([hidden]) { @@ -2044,32 +2082,16 @@ html { margin-bottom: calc(1rem * var(--tw-space-y-reverse)); } -.space-y-6 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); -} - .space-y-8 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(2rem * var(--tw-space-y-reverse)); } -.rounded-t-none { - border-top-left-radius: 0px; - border-top-right-radius: 0px; -} - .border-transparent { border-color: transparent; } -.bg-base-100 { - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity, 1))); -} - .bg-base-200 { --tw-bg-opacity: 1; background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1))); @@ -2099,25 +2121,11 @@ html { background-clip: text; } -.p-2 { - padding: 0.5rem; -} - .px-1 { padding-left: 0.25rem; padding-right: 0.25rem; } -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.py-8 { - padding-top: 2rem; - padding-bottom: 2rem; -} - .text-center { text-align: center; } @@ -2156,11 +2164,6 @@ html { color: rgb(156 163 175 / var(--tw-text-opacity, 1)); } -.text-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity, 1))); -} - .text-transparent { color: transparent; } \ No newline at end of file diff --git a/flake.lock b/flake.lock index 35b3bc2..34e1454 100644 --- a/flake.lock +++ b/flake.lock @@ -511,11 +511,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1728492678, - "narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=", + "lastModified": 1735291276, + "narHash": "sha256-NYVcA06+blsLG6wpAbSPTCyLvxD/92Hy4vlY9WxFI1M=", "owner": "nixos", "repo": "nixpkgs", - "rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7", + "rev": "634fd46801442d760e09493a794c4f15db2d0cbb", "type": "github" }, "original": { diff --git a/src/server/routes/html/signin.rs b/src/server/routes/html/signin.rs index 12779b2..37de68f 100644 --- a/src/server/routes/html/signin.rs +++ b/src/server/routes/html/signin.rs @@ -1,7 +1,7 @@ use axum::{ extract::State, http::{StatusCode, Uri}, - response::{IntoResponse, Redirect}, + response::{Html, IntoResponse, Redirect}, Form, }; use axum_htmx::{HxBoosted, HxRedirect}; @@ -52,10 +52,18 @@ pub async fn authenticate_user( auth: AuthSession, Surreal>, Form(form): Form, ) -> Result { - let user = User::authenticate(form.email, form.password, &state.surreal_db_client).await?; + let user = match User::authenticate(form.email, form.password, &state.surreal_db_client).await { + Ok(user) => user, + Err(_) => { + return Ok(Html("

Invalid email or password.

").into_response()); + } + }; + auth.login_user(user.id); + if form.remember_me.is_some_and(|string| string == *"on") { auth.remember_user(true); } + Ok((HxRedirect::from(Uri::from_static("/")), StatusCode::OK).into_response()) } diff --git a/src/server/routes/html/signup.rs b/src/server/routes/html/signup.rs index 40cd9fa..5534886 100644 --- a/src/server/routes/html/signup.rs +++ b/src/server/routes/html/signup.rs @@ -1,9 +1,10 @@ use axum::{ extract::State, - response::{IntoResponse, Redirect}, + http::{StatusCode, Uri}, + response::{Html, IntoResponse, Redirect}, Form, }; -use axum_htmx::HxBoosted; +use axum_htmx::{HxBoosted, HxRedirect}; use axum_session_auth::AuthSession; use axum_session_surreal::SessionSurrealPool; use serde::{Deserialize, Serialize}; @@ -50,7 +51,14 @@ pub async fn process_signup_and_show_verification( auth: AuthSession, Surreal>, Form(form): Form, ) -> Result { - let user = User::create_new(form.email, form.password, &state.surreal_db_client).await?; + let user = match User::create_new(form.email, form.password, &state.surreal_db_client).await { + Ok(user) => user, + Err(_) => { + return Ok(Html("

User already exists

").into_response()); + } + }; + auth.login_user(user.id); - Ok(()) + + Ok((HxRedirect::from(Uri::from_static("/")), StatusCode::OK).into_response()) } diff --git a/templates/auth/account.html b/templates/auth/account.html index 1ce95cf..1d94127 100644 --- a/templates/auth/account.html +++ b/templates/auth/account.html @@ -1,49 +1,47 @@ {% extends "body_base.html" %} -{% block content %} +{% block main %} -
-
-

Account Settings

-
- - -
-
- - {% block api_key_section %} - {% if user.api_key %} - - {% else %} - - {% endif %} - {% endblock %} -
-
- -
-
- -
-
- -
-
+
+

Account Settings

+
+ +
+
+ + {% block api_key_section %} + {% if user.api_key %} + + {% else %} + + {% endif %} + {% endblock %} +
+
+ +
+
+ +
+
+ +
+
{% endblock %} \ No newline at end of file diff --git a/templates/auth/signin_form.html b/templates/auth/signin_form.html index d8cc02e..fcf20c0 100644 --- a/templates/auth/signin_form.html +++ b/templates/auth/signin_form.html @@ -5,41 +5,41 @@ opacity: 0.5; } -
-
-

Login to your account

-
-
- - -
-
- - -
-
- -
-
- -
-
-
-
OR
-
- Don't have an account? - Sign up + +
+

Login to your account

+ +
+
+ +
+
+ + +
+
+ +
+
+
+ +
+
+
OR
+
+ Don't have an account? + Sign up
{% endblock %} \ No newline at end of file diff --git a/templates/auth/signup_form.html b/templates/auth/signup_form.html index 0145ed4..091785c 100644 --- a/templates/auth/signup_form.html +++ b/templates/auth/signup_form.html @@ -6,41 +6,39 @@ opacity: 0.5; } -
-
-

Create your account

-
-
- - -
+
+

Create your account

-
- - -
- -
- -
- -
- - -
OR
- -
- Already have an account? - Sign in +
+
+ +
+ +
+ + +
+
+
+ +
+ +
+ +
OR
+ +
+ Already have an account? + Sign in
{% endblock %} \ No newline at end of file diff --git a/templates/body_base.html b/templates/body_base.html index da6d920..b60a27e 100644 --- a/templates/body_base.html +++ b/templates/body_base.html @@ -1,27 +1,30 @@ {% extends "head_base.html" %} - {% block body %} - - + +
+ + -
- {% block content %}{% endblock %} -
+ +
+ {% block main %}{% endblock %} +
+
{% endblock %} \ No newline at end of file diff --git a/templates/body_base.jinja b/templates/body_base.jinja new file mode 100644 index 0000000..da6d920 --- /dev/null +++ b/templates/body_base.jinja @@ -0,0 +1,27 @@ +{% extends "head_base.html" %} + +{% block body %} + + + + +
+ {% block content %}{% endblock %} +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/head_base.html b/templates/head_base.html index fc71f55..b55e1eb 100644 --- a/templates/head_base.html +++ b/templates/head_base.html @@ -11,7 +11,8 @@ - + + {% block head %}{% endblock %} diff --git a/templates/index.html b/templates/index.html index 5269fb7..34d3c22 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,5 +1,5 @@ {% extends "body_base.html" %} -{% block content %} +{% block main %}