better login page

This commit is contained in:
2026-05-29 10:55:14 -07:00
parent 3441ae63b4
commit 6814cf1b15
6 changed files with 209 additions and 182 deletions

View File

@@ -1,12 +1,11 @@
(ns auto-ap.ssr.auth
(:require
[auto-ap.session-version :as session-version]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]]
[buddy.sign.jwt :as jwt]
[config.core :refer [env]]
[hiccup2.core :as hiccup]
[hiccup.util :as hu]))
(defn logout [request]
@@ -37,69 +36,73 @@
"scope" "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"}
next (assoc "state" (hu/url-encode next))))))))
(defn- login-page [contents]
{:status 200
:headers {"Content-Type" "text/html"}
:body (str "<!DOCTYPE html>"
(hiccup/html
[:html
[:head
[:meta {:charset "utf-8"}]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1"}]
[:title "Integreat · Sign In"]
[:link {:rel "icon" :type "image/png" :href "/favicon.png"}]
[:link {:rel "stylesheet" :href "/output.css"}]
[:script {:defer true :src "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"}]
[:style
"body{background:linear-gradient(160deg,#79b52e 0%,#009cea 100%);min-height:100vh}"]]
[:body contents]]))})
(defn- page-contents [request]
[:div#app {"@notification.document" "notificationDetails=event.detail.value; showNotification=true"
[:div
{:x-data (hx/json {:showError false
:errorDetails ""})
"@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"}
:x-data (hx/json {:showError false
:errorDetails ""
:showNotification false
:notificationDetails ""})
"@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"}
[:div#app-contents.flex.overflow-hidden
[:div#main-content {:class "relative w-full h-full overflow-y-auto px-4 bg-gray-100 dark:bg-gray-900 min-h-content "}
[:div#notification-holder
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg {:x-show "showNotification"}
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-blue-400
{"@click" "showNotification=false"}
svg/filled-x]]
[:div.fixed.top-0.left-0.right-0.z-50.mx-auto.max-w-md.w-full.px-4.pt-6
{:x-show "showError"
"x-transition:enter" "transition duration-200 ease-out"
"x-transition:enter-start" "opacity-0 -translate-y-3"
"x-transition:enter-end" "opacity-100 translate-y-0"}
[:div.relative.bg-white.rounded-xl.shadow-xl.border.border-red-200.p-4
[:button.absolute.right-3.top-3.p-1.text-red-400.hover:text-red-600
{"@click" "showError=false"}
svg/filled-x]
[:div.flex.items-start.gap-3
[:div.flex-shrink-0.w-5.h-5.text-red-500 svg/alert]
[:div.flex-1.min-w-0
[:p.text-sm.font-medium.text-gray-900 "Something went wrong"]
[:p.text-xs.text-gray-500.mt-0.5
"Our team has been notified. Please try again."
[:span {:x-data (hx/json {"e" false})}
" "
[:a.text-xs.underline.cursor-pointer.text-gray-500.hover:text-gray-700
{"@click" "e=true"}
"Details"]
[:pre.text-xs.mt-1.font-mono.text-red-600.bg-red-50.p-2.rounded {:x-show "e" :x-text "errorDetails"}]]]]]]]
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-blue-800.bg-blue-50.dark:bg-gray-800.dark:text-blue-400.border-blue-300.rounded-lg.border.max-h-96
{:x-show "showNotification"
"x-transition:enter" "transition duration-300 transform ease-in-out"
"x-transition:enter-start" "opacity-0 translate-y-full"
"x-transition:enter-end" "opacity-100 translate-y-0"
"x-transition:leave" "transition duration-300 transform ease-in-out"
"x-transition:leave-start" "opacity-100 translate-y-0"
"x-transition:leave-end" "opacity-0 translate-y-full"}
[:div.flex.items-center.justify-center.min-h-screen.px-4
[:div.w-full.max-w-lg
[:div.flex.flex-col.items-center.mb-10
[:img {:src "/img/logo-big.png" :alt "Integreat" :class "h-16 brightness-0 invert"}]]
[:div {:class "p-4 text-lg w-full" :role "alert"}
[:div.text-sm
[:pre#notification-details.text-xs {:x-html "notificationDetails"}]]]]]]
[:div {:x-show "showError"
:x-init ""}
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-red-600
{"@click" "showError=false"}
svg/filled-x]]
[:div.bg-white.rounded-2xl.shadow-2xl.p-10
{:style "animation: slideUp 0.4s ease-out forwards; opacity: 0;"}
[:div.flex.flex-col.items-center.gap-8
[:div.text-center
[:h1.text-2xl.font-bold.text-gray-900 "Sign in to Integreat"]
[:p.mt-2.text-base.text-gray-500 "Use your Google account to continue"]]
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-red-800.bg-red-50.dark:bg-gray-800.dark:text-red-400.border-red-300.rounded-lg.border.max-h-96
{:x-show "showError"
"x-transition:enter" "transition duration-300"
"x-transition:enter-start" "opacity-0"
"x-transition:enter-end" "opacity-100"}
[:a {:href (login-url (get (:query-params request) "redirect-to"))
:class "w-full max-w-xs flex items-center justify-center gap-3 px-6 py-3.5 text-base font-semibold rounded-xl border-2 border-gray-200 text-gray-700 bg-white hover:bg-gray-50 hover:border-gray-300 shadow-md hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400 transition-all duration-150"}
svg/google
"Sign in with Google"]]
[:div {:class "p-4 mb-4 text-lg w-full" :role "alert"}
[:div.inline-block.w-8.h-8.mr-2 svg/alert]
[:span.font-medium "Oh, drat! An unexpected error has occurred."]
[:div.text-sm {:x-data (hx/json {"expandError" false})}
[:p "Integreat staff have been notified and are looking into it. "]
[:p "To see error details, " [:a.underline.cursor-pointer {"@click" "expandError=true"} "click here"] "."]
[:pre#error-details.text-xs {:x-show "expandError" :x-text "errorDetails"}]]]]]]
[:div.p-4.flex.flex-row.justify-center.items-center.h-screen
(com/card {:class "animate-slideUp w-full max-w-md"}
[:div.p-8
[:div.flex.justify-center.mb-6
[:img {:src "/img/logo-big.png" :class "max-w-[200px]"}]]
[:div
[:a {:href (login-url (get (:query-params request) "redirect-to"))
:class "inline-flex items-center justify-center w-full px-8 py-3 text-base font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"}
"Login with Google"]]])]]]])
[:p.mt-2.text-center.text-xs.text-gray-400
"By signing in, you agree to our "
[:a.underline.hover:text-gray-600 {:href "/terms"} "Terms of Service"]
" and "
[:a.underline.hover:text-gray-600 {:href "/privacy"} "Privacy Policy"]]]]]])
(defn login [request]
(base-page
request
(page-contents request)
"Dashboard"))
(login-page (page-contents request)))