From c3c0d940cb5b015a2c872b4b9551258e4f502938 Mon Sep 17 00:00:00 2001 From: Tin Geber <tin@greenhost.nl> Date: Thu, 19 Oct 2023 12:57:12 +0200 Subject: [PATCH] styling done --- backend/web/README.md | 5 + backend/web/static/base.js | 55 +++++++- backend/web/static/css/main.css | 225 ++++++++++++++++++------------- backend/web/static/favicon.ico | Bin 0 -> 15086 bytes backend/web/static/src/input.css | 6 + backend/web/templates/base.html | 92 +++++++------ backend/web/templates/login.html | 177 ++++++++++++------------ 7 files changed, 322 insertions(+), 238 deletions(-) create mode 100644 backend/web/README.md create mode 100644 backend/web/static/favicon.ico diff --git a/backend/web/README.md b/backend/web/README.md new file mode 100644 index 00000000..663e3a5a --- /dev/null +++ b/backend/web/README.md @@ -0,0 +1,5 @@ +This `web` directory is responsible for authentication frontend components. + +It uses Tailwind for CSS; when making UI changes open a terminal in the `web` directory and run + +`npx tailwindcss -i ./static/src/input.css -o ./static/css/main.css --watch` diff --git a/backend/web/static/base.js b/backend/web/static/base.js index bf79733c..e09a07bf 100644 --- a/backend/web/static/base.js +++ b/backend/web/static/base.js @@ -57,7 +57,7 @@ function check_flow_auth() { // Set a custom cookie so the settings page knows we're in // recovery context and can open the right tab. Cookies.set("stackspin_context", "recovery"); - window.location.href = api_url + '/self-service/settings/browser'; + window.location.href = api_url + "/self-service/settings/browser"; return; } @@ -215,8 +215,8 @@ function flow_settings() { // If we are in recovery context, switch to the password tab. if (context == "recovery") { - $('#pills-password-tab').tab('show'); - Cookies.set('stackspin_context', ''); + $("#pills-password-tab").tab("show"); + Cookies.set("stackspin_context", ""); } }, complete: function (obj) { @@ -235,7 +235,7 @@ function flow_settings() { // Redirect so user can enter 2FA. window.location.href = response.redirect_browser_to; return; - } + } } // There was another error, one we don't specifically prepared for. $("#contentProfileSaveFailed").show(); @@ -643,3 +643,50 @@ $.urlParam = function (name) { } return decodeURI(results[1]) || 0; }; + +////////////////////////////////////// +// sign up form for the demo instance +////////////////////////////////////// + +function submitSignup() { + let result = document.querySelector("#signup-result"); + let email = document.querySelector("#signup-email"); + let xhr = new XMLHttpRequest(); + xhr.responseType = "json"; + let url = "/web/demo-user"; + xhr.open("POST", url, true); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // In the success case, we get a plain (json) string; in the error + // case, we get an object with `errorMessage` property. + if (typeof this.response == "object" && "errorMessage" in this.response) { + window.console.log("Error in sign-up request."); + result.classList.remove("alert-success"); + result.classList.add("alert-danger"); + if ( + this.response.errorMessage == + "[KratosError] Unable to insert or update resource because a resource with that value exists already" + ) { + result.innerHTML = "A user with that email address already exists."; + } else if ( + this.response.errorMessage == + "[KratosError] The request was malformed or contained invalid parameters" + ) { + result.innerHTML = "That doesn't appear to be a valid email address."; + } else { + result.innerHTML = this.response.errorMessage; + } + } else { + result.classList.add("alert-success"); + result.classList.remove("alert-danger"); + result.innerHTML = this.response; + } + result.style.visibility = "visible"; + } + }; + // Converting JSON data to string + var data = JSON.stringify({ email: email.value }); + // Sending data with the request + xhr.send(data); +} diff --git a/backend/web/static/css/main.css b/backend/web/static/css/main.css index 5821f614..698b3ea1 100644 --- a/backend/web/static/css/main.css +++ b/backend/web/static/css/main.css @@ -1,5 +1,5 @@ /* -! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com +! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com */ /* @@ -191,6 +191,10 @@ select, textarea { font-family: inherit; /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ font-size: 100%; /* 1 */ font-weight: inherit; @@ -341,6 +345,14 @@ menu { padding: 0; } +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + /* Prevent resizing textareas horizontally by default. */ @@ -422,6 +434,16 @@ video { display: none; } +label { + margin-bottom: 0.25rem; + display: block; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + *, ::before, ::after { --tw-border-spacing-x: 0; --tw-border-spacing-y: 0; @@ -573,14 +595,18 @@ video { margin-top: 0px; } -.mt-10 { - margin-top: 2.5rem; -} - .block { display: block; } +.inline-block { + display: inline-block; +} + +.inline { + display: inline; +} + .flex { display: flex; } @@ -621,6 +647,14 @@ video { flex-direction: column; } +.place-content-end { + place-content: end; +} + +.place-items-center { + place-items: center; +} + .items-center { align-items: center; } @@ -633,12 +667,35 @@ video { justify-content: center; } +.justify-between { + justify-content: space-between; +} + +.justify-items-end { + justify-items: end; +} + +.gap-4 { + gap: 1rem; +} + .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)); } +.divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + +.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-divide-opacity)); +} + .rounded-lg { border-radius: 0.5rem; } @@ -659,6 +716,20 @@ video { border-top-width: 2px; } +.border-t-\[1px\] { + border-top-width: 1px; +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + +.border-primary-400 { + --tw-border-opacity: 1; + border-color: rgb(85 198 204 / var(--tw-border-opacity)); +} + .border-transparent { border-color: transparent; } @@ -688,6 +759,10 @@ video { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } +.p-4 { + padding: 1rem; +} + .p-8 { padding: 2rem; } @@ -731,21 +806,25 @@ video { padding-bottom: 1.25rem; } +.pt-0 { + padding-top: 0px; +} + .pt-2 { padding-top: 0.5rem; } -.text-center { - text-align: center; +.pt-4 { + padding-top: 1rem; } .text-right { text-align: right; } -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; } .text-sm { @@ -753,6 +832,11 @@ video { line-height: 1.25rem; } +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + .font-bold { font-weight: 700; } @@ -761,12 +845,8 @@ video { font-weight: 500; } -.leading-9 { - line-height: 2.25rem; -} - -.tracking-tight { - letter-spacing: -0.025em; +.leading-6 { + line-height: 1.5rem; } .text-gray-400 { @@ -774,6 +854,11 @@ video { color: rgb(156 163 175 / var(--tw-text-opacity)); } +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + .text-gray-900 { --tw-text-opacity: 1; color: rgb(17 24 39 / var(--tw-text-opacity)); @@ -801,6 +886,33 @@ video { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } +.outline { + outline-style: solid; +} + +.outline-1 { + outline-width: 1px; +} + +.outline-primary-300 { + outline-color: #7AE5EA; +} + +.ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.ring-inset { + --tw-ring-inset: inset; +} + +.ring-gray-300 { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); +} + .hover\:bg-primary-700:hover { --tw-bg-opacity: 1; background-color: rgb(21 121 131 / var(--tw-bg-opacity)); @@ -836,104 +948,23 @@ video { --tw-ring-offset-width: 2px; } -@media (min-width: 640px) { - .sm\:mx-auto { - margin-left: auto; - margin-right: auto; - } - - .sm\:w-full { - width: 100%; - } -} - @media (min-width: 768px) { .md\:mb-0 { margin-bottom: 0px; } - .md\:min-h-full { - min-height: 100%; - } - - .md\:w-20 { - width: 5rem; - } - - .md\:w-1\/6 { - width: 16.666667%; - } - - .md\:w-2\/6 { - width: 33.333333%; - } - - .md\:w-1\/4 { - width: 25%; - } - - .md\:w-1\/2 { - width: 50%; - } - - .md\:w-1\/3 { - width: 33.333333%; - } - - .md\:w-80 { - width: 20rem; - } - - .md\:w-96 { - width: 24rem; - } - .md\:w-2\/3 { width: 66.666667%; } - .md\:max-w-md { - max-width: 28rem; - } - - .md\:max-w-sm { - max-width: 24rem; - } - .md\:max-w-lg { max-width: 32rem; } - - .md\:flex-col { - flex-direction: column; - } - - .md\:justify-center { - justify-content: center; - } } @media (min-width: 1024px) { - .lg\:w-1\/4 { - width: 25%; - } - - .lg\:w-1\/3 { - width: 33.333333%; - } - .lg\:px-8 { padding-left: 2rem; padding-right: 2rem; } } - -@media (min-width: 1280px) { - .xl\:w-1\/3 { - width: 33.333333%; - } - - .xl\:max-w-md { - max-width: 28rem; - } -} diff --git a/backend/web/static/favicon.ico b/backend/web/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..863fd60c8b337f1e043f9d7b466e4e8c0961768f GIT binary patch literal 15086 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 literal 0 HcmV?d00001 diff --git a/backend/web/static/src/input.css b/backend/web/static/src/input.css index b0f89e7f..5081208a 100644 --- a/backend/web/static/src/input.css +++ b/backend/web/static/src/input.css @@ -2,6 +2,12 @@ @tailwind components; @tailwind utilities; +@layer base { + label { + @apply block text-sm font-medium text-gray-700 mb-1; + } +} + @layer components { #pills-tab .nav-link.active { @apply bg-transparent text-gray-800 border-b-2 text-left border-b-primary-700 hover:bg-primary-700 py-2 px-4 rounded-none; diff --git a/backend/web/templates/base.html b/backend/web/templates/base.html index 8cadf738..d5221bfc 100644 --- a/backend/web/templates/base.html +++ b/backend/web/templates/base.html @@ -1,49 +1,55 @@ -<!doctype html> +<!DOCTYPE html> <html class="h-full bg-white"> -<link rel="stylesheet" href="static/style.css"> + <link rel="stylesheet" href="static/style.css" /> + <link rel="icon" type="image/x-icon" href="/static/favicon.ico" /> -<link rel="stylesheet" href="static/css/bootstrap.min.css"> -<link href="static/css/main.css" rel="stylesheet"> -<script src="static/js/jquery-3.6.0.min.js"></script> -<script src="static/js/bootstrap.bundle.min.js"></script> -<script src="static/js/js.cookie.min.js"></script> -<script src="static/base.js"></script> - -<title>Your Stackspin Account</title> + <link rel="stylesheet" href="static/css/bootstrap.min.css" /> + <link href="static/css/main.css" rel="stylesheet" /> + <script src="static/js/jquery-3.6.0.min.js"></script> + <script src="static/js/bootstrap.bundle.min.js"></script> + <script src="static/js/js.cookie.min.js"></script> + <script src="static/base.js"></script> + <title>Your Stackspin Account</title> </html> - - <body class="h-full bg-gray-50"> - - - <script> - var api_url = '{{ api_url }}'; - - // Actions - $(document).ready(function () { - check_flow_expired(); - }); - - </script> - - - - <div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8"> - <div class="mx-auto w-full h-full md:w-2/3 md:max-w-lg bg-white shadow rounded-lg space-y-8 mb-4 md:mb-0 p-8"> - - <div id="contentFlowExpired" class='alert alert-warning' style='display:none'>Your request has expired. - Please resubmit your request.</div> - - - <a href="{{ dashboard_url }}"><img class="mx-auto h-10 w-auto mb-4" src='static/logo.svg' /></a> - <!-- <h2 class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">Your Stackspin - account</h2> --> - - - <div id='messages'></div> - - {% block content %}{% endblock %} - </div> - </div> \ No newline at end of file + <script> + var api_url = "{{ api_url }}"; + + // Actions + $(document).ready(function () { + check_flow_expired(); + }); + </script> + + <div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8"> + <div + class="mx-auto w-full h-full md:w-2/3 md:max-w-lg bg-white shadow rounded-lg space-y-8 mb-4 md:mb-0 p-8 pt-0" + > + <div + id="contentFlowExpired" + class="alert alert-warning" + style="display: none" + > + Your request has expired. Please resubmit your request. + </div> + + <div class="login-header flex flex-col place-items-center"> + <a href="{{ dashboard_url }}" class="block" + ><img class="mx-auto h-10 w-auto" src="static/logo.svg" /> + </a> + {% if demo %} + <span + class="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-500 ring-1 ring-inset ring-gray-300" + > + Demo Instance + </span> + {% endif %} + </div> + <div id="messages"></div> + + {% block content %}{% endblock %} + </div> + </div> +</body> diff --git a/backend/web/templates/login.html b/backend/web/templates/login.html index e29bee76..9a6c891b 100644 --- a/backend/web/templates/login.html +++ b/backend/web/templates/login.html @@ -1,106 +1,95 @@ -{% extends 'base.html' %} - -{% block content %} +{% extends 'base.html' %} {% block content %} <script> - var api_url = '{{ api_url }}'; + var api_url = '{{ api_url }}'; + + // Actions + $(document).ready(function () { + flow_login(); + }); - // Actions - $(document).ready(function () { - flow_login(); - }); - // Render messages - {% for message in messages %} - renderMessage('{{ message['id'] }}', '{{ message['message'] }}', '{{ message['type'] }}'); - {% endfor %} + {% for message in messages %} + renderMessage('{{ message['id'] }}', '{{ message['message'] }}', '{{ message['type'] }}'); + {% endfor %} + + // On demo instance, submit the sign-up form via ajax + // so we can show the result on the same page. + {% if demo %} + $('#signup-form').submit((event) => { + event.preventDefault(); + window.console.log("signup submit"); + submitSignup(); + }); + {% endif %} </script> -<div id="contentMessages"></div> -<div id="contentLogin_password"></div> -<div id="contentLogin_totp"></div> -<div id="contentHelp" class="flex flex-col font-medium text-primary-600 mt-0 pt-2 border-t-gray-50 border-t-2"> - <a href='recovery' class="block w-full text-right hover:text-primary-700 mb-2">Set new - password</a><a href='https://stackspin.net' - class="block text-right w-full text-sm text-gray-400 hover:text-primary-500">What - is - Stackspin?</a> -</div> {% if demo %} -<br> -<script> - function submitSignup() { - let result = document.querySelector('#signup-result'); - let email = document.querySelector('#signup-email'); - let xhr = new XMLHttpRequest(); - xhr.responseType = 'json'; - let url = "/web/demo-user"; - xhr.open("POST", url, true); - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - // In the success case, we get a plain (json) string; in the error - // case, we get an object with `errorMessage` property. - if (typeof (this.response) == 'object' && 'errorMessage' in this.response) { - window.console.log("Error in sign-up request."); - result.classList.remove('alert-success'); - result.classList.add('alert-danger'); - if (this.response.errorMessage == '[KratosError] Unable to insert or update resource because a resource with that value exists already') { - result.innerHTML = "A user with that email address already exists."; - } else if (this.response.errorMessage == '[KratosError] The request was malformed or contained invalid parameters') { - result.innerHTML = "That doesn't appear to be a valid email address."; - } else { - result.innerHTML = this.response.errorMessage; - } - } else { - result.classList.add('alert-success'); - result.classList.remove('alert-danger'); - result.innerHTML = this.response; - } - result.style.visibility = 'visible'; - } - }; - // Converting JSON data to string - var data = JSON.stringify({ "email": email.value }); - // Sending data with the request - xhr.send(data); - } - $(() => { - // Submit the sign-up form via ajax so we can show the result on the same - // page. - $('#signup-form').submit((event) => { - event.preventDefault(); - window.console.log("signup submit"); - submitSignup(); - }); - }) -</script> -<h4>Sign up for this demo instance</h4> -Enter your email address here to create an account on this Stackspin -instance. -<div class="alert alert-warning" style="margin-top: 1em;"> - Warning: this is a demo instance! That means that: - <ul> - <li>Anyone can create an account on this same instance, like yourself, - and will share the same set of users and data. So any data you create - or upload, including the email address you enter here, becomes - essentially public information.</li> - <li>Every night (Europe/Amsterdam time), this instance gets automatically - reset to an empty state, so any data you create or upload will be - destroyed.</li> - </ul> + +<div + class="rounded-md bg-gray-50 border border-gray-300 outline outline-1 outline-primary-300 p-4 flex flex-col gap-4 text-xs text-gray-500" +> + <p>Please keep in mind this is a demo instance:</p> + <ul class="divide-y divide-gray-200"> + <li class="py-2">Anyone can create an account, just like yourself.</li> + <li class="py-2"> + Any data you upload, including your email address, can be accessible by + other users. + </li> + <li class="py-2"> + The demo is automatically reset every night (Europe/Amsterdam time), + removing all users and their data. + </li> + </ul> </div> +<h1 class="text-lg leading-6 font-bold text-gray-900"> + Create your Stackspin demo account: +</h1> <form id="signup-form"> - <div class="form-group"> - <label for="signup-email">Email address</label> - <input type="email" class="form-control" id="signup-email" name="signup-email" - placeholder="Your email address to sign up with."> - </div> - <div class="form-group"> - <input type="submit" class="btn btn-primary" value="Sign up"> - <div id="signup-result" class="alert" style="visibility: hidden; margin-top: 1em;"></div> - </div> + <label for="signup-email">E-mail address</label> + <div class="form-group flex justify-between gap-4"> + <input + type="email" + class="form-control" + id="signup-email" + name="signup-email" + placeholder="Please provide your e-mail address" + /> + <button + type="submit" + class="inline-flex pointer h-10 items-center px-2.5 py-1.5 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" + > + Create + </button> + </div> + <div + id="signup-result" + class="form-group rounded-md bg-gray-50 border border-gray-300 outline outline-1 outline-primary-300 p-4 flex flex-col gap-4 text-xs text-gray-500" + style="visibility: hidden" + ></div> </form> + +<h1 + class="text-lg mt-0 leading-6 font-bold text-gray-900 pt-4 border-t-[1px] border-primary-400" +> + If you already have an account, log in: +</h1> + {% endif %} -{% endblock %} \ No newline at end of file +<div id="contentMessages"></div> +<div id="contentLogin_password"></div> +<div id="contentLogin_totp"></div> +<footer + class="font-medium text-primary-600 mt-0 pt-2 border-t-gray-50 border-t-2 flex justify-end" +> + <div id="contentHelp" class="flex flex-col text-right"> + <a href="recovery" class="hover:text-primary-700 mb-2">Set new password</a + ><a + href="https://stackspin.net" + class="text-sm text-gray-400 hover:text-primary-500" + >What is Stackspin?</a + > + </div> + <footer>{% endblock %}</footer> +</footer> -- GitLab