0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-26 06:31:26 -05:00

Merge pull request #3873 from penpot/alotor-login-ui

New UI for Login
This commit is contained in:
Eva Marco 2023-12-01 13:03:15 +01:00 committed by GitHub
commit 990f63a136
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1432 additions and 378 deletions

View file

@ -13,6 +13,7 @@
"A common flags that affects both: backend and frontend."
[:enable-registration
:enable-login-with-password
:enable-login-illustration
:enable-feature-styles-v2])
(defn parse

View file

@ -29,12 +29,13 @@ paths.resources = "./resources/";
paths.output = "./resources/public/";
paths.dist = "./target/dist/";
const touchSourceOnStyleChange = false;
/***********************************************
* Marked Extensions
***********************************************/
// Name of Penpot's top level package
const ROOT_NAME = "app";
const renderer = {
link(href, title, text) {
return `<a href="${href}" target="_blank">${text}</a>`;
@ -223,7 +224,18 @@ gulp.task("scss:modules", function () {
.pipe(
gulpPostcss([
modules({
generateScopedName: "[folder]_[name]_[local]_[hash:base64:5]",
getJSON: function (cssFileName, json, outputFileName) {
// We do nothing because we don't want the generated JSON files
},
// Calculates the whole css-module selector name.
// Should be the same as the one in the file `/src/app/main/style.clj`
generateScopedName: function (selector, filename, css) {
const dir = path.dirname(filename);
const name = path.basename(filename, ".css");
const parts = dir.split("/");
const rootIdx = parts.findIndex(s => s === ROOT_NAME);
return parts.slice(rootIdx + 1).join("_") + "_" + name + "__" + selector;
},
}),
autoprefixer(),
]),
@ -350,13 +362,6 @@ gulp.task("dev:dirs", async function (next) {
gulp.task("watch:main", function () {
const watchTask = gulp.watch("src/**/**.scss", gulp.series("scss"));
if (touchSourceOnStyleChange) {
watchTask.on("change", function (path) {
// Replace ".scss" for ".cljs" to refresh the file
gulp.src(path.replace(".scss", ".cljs")).pipe(touch());
});
}
gulp.watch(paths.resources + "styles/**/**.scss", gulp.series("scss"));
gulp.watch(paths.resources + "images/**/*", gulp.series("copy:assets:images"));

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

@ -4,6 +4,8 @@
//
// Copyright (c) KALEIDOS INC
@use "sass:color" as color;
:root {
// DARK
--dark-gray-1: #1d1f20;
@ -46,4 +48,21 @@
//GENERIC
--color-canvas: #e8e9ea;
// SOCIAL LOGIN BUTTONS
--google-login-background: #4285f4;
--google-login-background-hover: #{color.adjust(#4285f4, $lightness: -15%)};
--google-login-foreground: var(--white);
--github-login-background: #4c4c4c;
--github-login-background-hover: #{color.adjust(#4c4c4c, $lightness: -15%)};
--github-login-foreground: var(--white);
--oidc-login-background: #b3b3b3;
--oidc-login-background-hover: #{color.adjust(#b3b3b3, $lightness: -15%)};
--oidc-login-foreground: var(--white);
--gitlab-login-background: #fc6d26;
--gitlab-login-background-hover: #{color.adjust(#fc6d26, $lightness: -15%)};
--gitlab-login-foreground: var(--white);
}

View file

@ -151,6 +151,7 @@
--input-foreground-color-disabled: var(--color-foreground-secondary);
--input-border-color-disabled: var(--color-background-quaternary);
--input-border-color-error: var(--error-color);
--input-border-color-success: var(--color-accent-primary);
--input-details-color: var(--color-background-primary);
--input-checkbox-background-color-rest: var(--color-background-quaternary);
--input-checkbox-border-color-active: var(--color-background-quaternary);

View file

@ -145,6 +145,7 @@ $s-580: #{0.25 * 145}rem;
$s-612: #{0.25 * 153}rem;
$s-640: #{0.25 * 160}rem;
$s-664: #{0.25 * 166}rem;
$s-688: #{0.25 * 172}rem;
$s-712: #{0.25 * 178}rem;
$s-736: #{0.25 * 184}rem;
$s-800: #{0.25 * 200}rem;

View file

@ -4,6 +4,8 @@
//
// Copyright (c) KALEIDOS INC
@use "sass:meta";
:root {
--color-background-primary: var(--dark-gray-1);
--color-background-secondary: var(--dark-gray-2);
@ -25,4 +27,6 @@
--pending-color: var(--dark-pending-color);
--error-color: var(--dark-error-color);
--canvas-color: var(--color-canvas);
@include meta.load-css("hljs-dark-theme");
}

View file

@ -0,0 +1,98 @@
/**
* Panda Syntax Theme for Highlight.js
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
*/
.hljs {
color: #e6e6e6;
background: #2a2c2d;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
.hljs-comment,
.hljs-quote {
color: #bbbbbb;
font-style: italic;
}
.hljs-params {
color: #bbbbbb;
}
.hljs-punctuation,
.hljs-attr {
color: #e6e6e6;
}
.hljs-selector-tag,
.hljs-name,
.hljs-meta {
color: #ff4b82;
}
.hljs-operator,
.hljs-char.escape_ {
color: #b084eb;
}
.hljs-keyword,
.hljs-deletion {
color: #ff75b5;
}
.hljs-regexp,
.hljs-selector-pseudo,
.hljs-selector-attr,
.hljs-variable.language_ {
color: #ff9ac1;
}
.hljs-subst,
.hljs-property,
.hljs-code,
.hljs-formula,
.hljs-section,
.hljs-title.function_ {
color: #45a9f9;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition,
.hljs-selector-class,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-meta .hljs-string {
color: #19f9d8;
}
.hljs-variable,
.hljs-template-variable,
.hljs-number,
.hljs-literal,
.hljs-type,
.hljs-link,
.hljs-built_in,
.hljs-title,
.hljs-selector-id,
.hljs-tag,
.hljs-doctag,
.hljs-attribute,
.hljs-template-tag,
.hljs-meta .hljs-keyword,
.hljs-punctuation {
color: #ffb86c;
}

View file

@ -0,0 +1,94 @@
/**
* Panda Syntax Theme for Highlight.js
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
*/
.hljs {
color: #2a2c2d;
background: #e6e6e6;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
.hljs-comment,
.hljs-quote {
color: #676b79;
font-style: italic;
}
.hljs-params {
color: #676b79;
}
.hljs-punctuation,
.hljs-attr {
color: #2a2c2d;
}
.hljs-selector-tag,
.hljs-name,
.hljs-meta,
.hljs-operator,
.hljs-char.escape_ {
color: #c56200;
}
.hljs-keyword,
.hljs-deletion {
color: #d92792;
}
.hljs-regexp,
.hljs-selector-pseudo,
.hljs-selector-attr,
.hljs-variable.language_ {
color: #cc5e91;
}
.hljs-subst,
.hljs-property,
.hljs-code,
.hljs-formula,
.hljs-section,
.hljs-title.function_ {
color: #3787c7;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition,
.hljs-selector-class,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-meta .hljs-string {
color: #0d7d6c;
}
.hljs-variable,
.hljs-template-variable,
.hljs-number,
.hljs-literal,
.hljs-type,
.hljs-link,
.hljs-built_in,
.hljs-title,
.hljs-selector-id,
.hljs-tag,
.hljs-doctag,
.hljs-attribute,
.hljs-template-tag,
.hljs-meta .hljs-keyword {
color: #7641bb;
}

View file

@ -4,6 +4,8 @@
//
// Copyright (c) KALEIDOS INC
@use "sass:meta";
.light {
--color-background-primary: var(--light-gray-1);
--color-background-secondary: var(--light-gray-2);
@ -24,4 +26,6 @@
--pending-color: var(--light-pending-color);
--error-color: var(--light-error-color);
--canvas-color: var(--color-canvas);
@include meta.load-css("hljs-light-theme");
}

View file

@ -100,8 +100,8 @@
(def browser (parse-browser))
(def platform (parse-platform))
(def terms-of-service-uri (obj/get global "penpotTermsOfServiceURI" nil))
(def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI" nil))
(def terms-of-service-uri (obj/get global "penpotTermsOfServiceURI" "https://penpot.app/terms"))
(def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI" "https://penpot.app/privacy"))
(defn- normalize-uri
[uri-str]

View file

@ -11,9 +11,25 @@
[clojure.core :as c]
[clojure.data.json :as json]
[clojure.java.io :as io]
[cuerdas.core :as str]
[rumext.v2.util :as mfu]))
(def ^:dynamic *css-data* nil)
;; Should match with the `ROOT_NAME` constant in gulpfile.js
(def ROOT-NAME "app")
(def ^:dynamic *css-prefix* nil)
(defn get-prefix
;; Calculates the css-modules prefix given the filename
;; should be the same as the calculation inside the `gulpfile.js`
[fname]
(let [file (io/file fname)
parts
(->> (str/split (.getParent file) #"/")
(drop-while #(not= % ROOT-NAME))
(rest)
(str/join "_"))]
(str parts "_" (subs (.getName file) 0 (- (count (.getName file)) 5)) "__")))
(def ^:private xform-css
(keep (fn [k]
@ -22,9 +38,8 @@
(let [knm (name k)
kns (namespace k)]
(case kns
"global" knm
"old-css" (if (nil? *css-data*) knm "")
(or (get *css-data* (keyword knm)) (str "_not_found_" knm))))
"global" knm
(str *css-prefix* knm)))
(string? k)
k))))
@ -49,14 +64,13 @@
all classes with space in the same way as `css*`."
[& selectors]
(let [fname (-> *ns* meta :file)
path (str (subs fname 0 (- (count fname) 4)) "css.json")
data (read-json-file path)]
prefix (get-prefix fname)]
(if (symbol? (first selectors))
`(if ~(with-meta (first selectors) {:tag 'boolean})
(css* ~@(binding [*css-data* data]
(css* ~@(binding [*css-prefix* prefix]
(into [] xform-css (rest selectors))))
(css* ~@(rest selectors)))
`(css* ~@(binding [*css-data* data]
`(css* ~@(binding [*css-prefix* prefix]
(into [] xform-css selectors))))))
(defmacro styles
@ -76,8 +90,7 @@
kns (namespace k)]
(case kns
"global" knm
"old-css" (if (nil? *css-data*) knm "")
(or (get *css-data* (keyword knm)) knm)))
(str *css-prefix* knm)))
(string? k)
k)]
@ -95,7 +108,6 @@
;;
;; (stl/css-case new-css-system
;; :left-settings-bar true
;; :old-css/settings-bar true
;; :global/two-row (<= size 300))
;;
;; The first argument to the `css-case` macro is optional an if you don't
@ -118,17 +130,16 @@
(defmacro css-case
[& params]
(let [fname (-> *ns* meta :file)
path (str (subs fname 0 (- (count fname) 4)) "css.json")
data (read-json-file path)]
prefix (get-prefix fname)]
(if (symbol? (first params))
`(if ~(with-meta (first params) {:tag 'boolean})
~(binding [*css-data* data]
~(binding [*css-prefix* prefix]
(-> (into [] xform-css-case (rest params))
(mfu/compile-concat :safe? false)))
~(-> (into [] xform-css-case (rest params))
(mfu/compile-concat :safe? false)))
`~(binding [*css-data* data]
`~(binding [*css-prefix* prefix]
(-> (into [] xform-css-case params)
(mfu/compile-concat :safe? false))))))

View file

@ -5,6 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.auth
(:require-macros [app.main.style :as stl])
(:require
[app.config :as cf]
[app.main.ui.auth.login :refer [login-page]]
@ -19,57 +20,99 @@
(mf/defc terms-login
[]
(let [show-all? (and cf/terms-of-service-uri cf/privacy-policy-uri)
(let [new-css-system (mf/use-ctx ctx/new-css-system)
show-all? (and cf/terms-of-service-uri cf/privacy-policy-uri)
show-terms? (some? cf/terms-of-service-uri)
show-privacy? (some? cf/privacy-policy-uri)]
(when show-all?
[:div.terms-login
(when show-terms?
[:a {:href cf/terms-of-service-uri :target "_blank"} (tr "auth.terms-of-service")])
(if new-css-system
(when show-all?
[:div {:class (stl/css :terms-login)}
(when show-terms?
[:a {:href cf/terms-of-service-uri :target "_blank"} (tr "auth.terms-of-service")])
(when show-all?
[:span (tr "labels.and")])
(when show-all?
[:span (tr "labels.and")])
(when show-privacy?
[:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])])))
(when show-privacy?
[:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])])
(when show-all?
[:div.terms-login
(when show-terms?
[:a {:href cf/terms-of-service-uri :target "_blank"} (tr "auth.terms-of-service")])
(when show-all?
[:span (tr "labels.and")])
(when show-privacy?
[:a {:href cf/privacy-policy-uri :target "_blank"} (tr "auth.privacy-policy")])]))))
(mf/defc auth
[{:keys [route] :as props}]
(let [section (get-in route [:data :name])
params (:query-params route)]
(let [new-css-system (mf/use-ctx ctx/new-css-system)
section (get-in route [:data :name])
params (:query-params route)
show-illustration? (contains? cf/flags :login-illustration)]
(mf/use-effect
#(dom/set-html-title (tr "title.default")))
;; FIXME: Temporary disabled new css system until we redesign the login with the new styles
[:& (mf/provider ctx/new-css-system) {:value false}
[:main.auth
[:section.auth-sidebar
[:a.logo {:href "#/"}
[:span {:aria-hidden true} i/logo]
[:span.hidden-name "Home"]]
[:span.tagline (tr "auth.sidebar-tagline")]]
(if new-css-system
[:main {:class (stl/css-case :auth-section true
:no-illustration (not show-illustration?))}
(when show-illustration?
[:div {:class (stl/css :login-illustration)}
i/login-illustration])
[:section {:class (stl/css :auth-content)}
(case section
:auth-register
[:& register-page {:params params}]
[:section.auth-content
(case section
:auth-register
[:& register-page {:params params}]
:auth-register-validate
[:& register-validate-page {:params params}]
:auth-register-validate
[:& register-validate-page {:params params}]
:auth-register-success
[:& register-success-page {:params params}]
:auth-register-success
[:& register-success-page {:params params}]
:auth-login
[:& login-page {:params params}]
:auth-login
[:& login-page {:params params}]
:auth-recovery-request
[:& recovery-request-page]
:auth-recovery-request
[:& recovery-request-page]
:auth-recovery
[:& recovery-page {:params params}])
:auth-recovery
[:& recovery-page {:params params}])
(when (contains? #{:auth-login :auth-register} section)
[:& terms-login])]]
[:& terms-login {}]]]]))
;; OLD
[:main.auth
[:section.auth-sidebar
[:a.logo {:href "#/"}
[:span {:aria-hidden true} i/logo]
[:span.hidden-name "Home"]]
[:span.tagline (tr "auth.sidebar-tagline")]]
[:section.auth-content
(case section
:auth-register
[:& register-page {:params params}]
:auth-register-validate
[:& register-validate-page {:params params}]
:auth-register-success
[:& register-success-page {:params params}]
:auth-login
[:& login-page {:params params}]
:auth-recovery-request
[:& recovery-request-page]
:auth-recovery
[:& recovery-page {:params params}])
[:& terms-login {}]]])))

View file

@ -0,0 +1,74 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-refactor.scss" as *;
.auth-section {
align-items: center;
background: $db-primary;
display: grid;
gap: $s-32;
grid-template-columns: repeat(3, 1fr);
height: 100%;
padding: $s-32;
width: 100%;
&.no-illustration {
display: flex;
justify-content: center;
}
@media (max-width: 992px) {
display: flex;
justify-content: center;
}
.login-illustration {
display: flex;
justify-content: center;
grid-column: 1 / 3;
svg {
width: $s-688;
fill: $df-primary;
height: auto;
}
@media (max-width: 992px) {
display: none;
}
}
.auth-content {
align-items: center;
display: flex;
height: fit-content;
justify-content: center;
max-width: $s-412;
padding-bottom: $s-8;
position: relative;
width: 100%;
}
}
.terms-login {
font-size: $fs-11;
position: absolute;
bottom: 0;
width: 100%;
display: flex;
gap: $s-4;
justify-content: center;
a {
font-weight: $fw700;
color: $df-secondary;
}
span {
border-bottom: $s-1 solid transparent;
color: $df-secondary;
}
}

View file

@ -0,0 +1,154 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-refactor.scss" as *;
.auth-form {
width: 100%;
padding-bottom: $s-16;
form {
display: flex;
flex-direction: column;
gap: $s-12;
margin-bottom: $s-24;
}
}
.separator {
border-color: $db-cuaternary;
margin: $s-24 0;
}
.auth-title {
@include bigTitleTipography;
color: $df-primary;
}
.auth-subtitle {
margin-top: $s-24;
font-size: $fs-14;
color: $df-secondary;
}
.form-field {
--input-width: 100%;
--input-height: #{$s-40};
--input-min-width: 100%;
}
.buttons-stack {
display: flex;
flex-direction: column;
gap: $s-8;
button,
:global(.btn-primary) {
@extend .button-primary;
font-size: $fs-11;
height: $s-40;
text-transform: uppercase;
width: 100%;
}
}
.link-entry {
display: flex;
flex-direction: column;
gap: $s-12;
padding: $s-24 0;
border-top: $s-1 solid $db-cuaternary;
span {
text-align: center;
font-size: $fs-14;
color: $df-secondary;
}
a {
@extend .button-secondary;
height: $s-40;
text-transform: uppercase;
font-size: $fs-11;
}
}
.forgot-password {
display: flex;
justify-content: flex-end;
a {
font-size: $fs-14;
color: $df-secondary;
font-weight: $fw400;
}
}
.submit-btn,
.register-btn,
.recover-btn {
@extend .button-primary;
font-size: $fs-11;
height: $s-40;
text-transform: uppercase;
width: 100%;
}
.login-btn {
border-radius: $br-8;
font-size: $fs-14;
display: flex;
align-items: center;
gap: $s-6;
width: 100%;
span {
padding-top: $s-2;
}
&:hover {
color: var(--white);
}
}
.auth-buttons {
display: flex;
gap: $s-8;
}
.btn-google-auth {
color: var(--google-login-foreground);
background-color: var(--google-login-background);
&:hover {
background: var(--google-login-background-hover);
}
}
.btn-github-auth {
color: var(--github-login-foreground);
background: var(--github-login-background);
&:hover {
background: var(--github-login-background-hover);
}
}
.btn-oidc-auth {
color: var(--oidc-login-foreground);
background: var(--oidc-login-background);
&:hover {
background: var(--oidc-login-background-hover);
}
}
.btn-gitlab-auth {
color: var(--gitlab-login-foreground);
background: var(--gitlab-login-background);
&:hover {
background: var(--gitlab-login-background-hover);
}
}
.banner {
margin: $s-16 0;
}

View file

@ -5,6 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.auth.login
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.logging :as log]
@ -17,6 +18,7 @@
[app.main.ui.components.button-link :as bl]
[app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.util.dom :as dom]
@ -92,7 +94,8 @@
(mf/defc login-form
[{:keys [params on-success-callback] :as props}]
(let [initial (mf/use-memo (mf/deps params) (constantly params))
(let [new-css-system (mf/use-ctx ctx/new-css-system)
initial (mf/use-memo (mf/deps params) (constantly params))
error (mf/use-state false)
form (fm/use-form :spec ::login-form
@ -150,137 +153,269 @@
(login-with-ldap event (with-meta params
{:on-error on-error
:on-success on-success})))))]
[:*
(when-let [message @error]
[:& msgs/inline-banner
{:type :warning
:content message
:on-close #(reset! error nil)
:data-test "login-banner"
:role "alert"}])
(if new-css-system
[:*
(when-let [message @error]
[:& msgs/inline-banner
{:type :warning
:content message
:on-close #(reset! error nil)
:data-test "login-banner"
:role "alert"}])
[:& fm/form {:on-submit on-submit :form form}
[:div.fields-row
[:& fm/input
{:name :email
:type "email"
:help-icon i/at
:label (tr "auth.email")}]]
[:& fm/form {:on-submit on-submit :form form}
[:div {:class (stl/css :fields-row)}
[:& fm/input
{:name :email
:type "email"
:label (tr "auth.email")
:class (stl/css :form-field)}]]
[:div.fields-row
[:& fm/input
{:type "password"
:name :password
:help-icon i/eye
:label (tr "auth.password")}]]
[:div {:class (stl/css :fields-row)}
[:& fm/input
{:type "password"
:name :password
:label (tr "auth.password")
:class (stl/css :form-field)}]]
[:div.buttons-stack
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password))
[:> fm/submit-button*
{:label (tr "auth.login-submit")
:data-test "login-submit"}])
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password))
[:div {:class (stl/css :fields-row :forgot-password)}
[:& lk/link {:action #(st/emit! (rt/nav :auth-recovery-request))
:data-test "forgot-password"}
(tr "auth.forgot-password")]])
(when (contains? cf/flags :login-with-ldap)
[:> fm/submit-button*
{:label (tr "auth.login-with-ldap-submit")
:on-click on-submit-ldap}])]]]))
[:div {:class (stl/css :buttons-stack)}
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password))
[:> fm/submit-button*
{:label (tr "auth.login-submit")
:data-test "login-submit"
:class (stl/css :login-button)}])
(when (contains? cf/flags :login-with-ldap)
[:> fm/submit-button*
{:label (tr "auth.login-with-ldap-submit")
:on-click on-submit-ldap}])]]]
;; OLD
[:*
(when-let [message @error]
[:& msgs/inline-banner
{:type :warning
:content message
:on-close #(reset! error nil)
:data-test "login-banner"
:role "alert"}])
[:& fm/form {:on-submit on-submit :form form}
[:div.fields-row
[:& fm/input
{:name :email
:type "email"
:help-icon i/at
:label (tr "auth.email")
:class (stl/css :form-field)}]]
[:div.fields-row
[:& fm/input
{:type "password"
:name :password
:help-icon i/eye
:label (tr "auth.password")
:class (stl/css :form-field)}]]
[:div.buttons-stack
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password))
[:> fm/submit-button*
{:label (tr "auth.login-submit")
:data-test "login-submit"}])
(when (contains? cf/flags :login-with-ldap)
[:> fm/submit-button*
{:label (tr "auth.login-with-ldap-submit")
:on-click on-submit-ldap}])]]])))
(mf/defc login-buttons
[{:keys [params] :as props}]
(let [login-with-google (mf/use-fn (mf/deps params) #(login-with-oidc % :google params))
(let [new-css-system (mf/use-ctx ctx/new-css-system)
login-with-google (mf/use-fn (mf/deps params) #(login-with-oidc % :google params))
login-with-github (mf/use-fn (mf/deps params) #(login-with-oidc % :github params))
login-with-gitlab (mf/use-fn (mf/deps params) #(login-with-oidc % :gitlab params))
login-with-oidc (mf/use-fn (mf/deps params) #(login-with-oidc % :oidc params))]
[:div.auth-buttons
(when (contains? cf/flags :login-with-google)
[:& bl/button-link {:on-click login-with-google
:icon i/brand-google
:label (tr "auth.login-with-google-submit")
:class "btn-google-auth"}])
(if new-css-system
[:div {:class (stl/css :auth-buttons)}
(when (contains? cf/flags :login-with-google)
[:& bl/button-link {:on-click login-with-google
:icon i/brand-google
:label (tr "auth.login-with-google-submit")
:class (stl/css :login-btn :btn-google-auth)}])
(when (contains? cf/flags :login-with-github)
[:& bl/button-link {:on-click login-with-github
:icon i/brand-github
:label (tr "auth.login-with-github-submit")
:class "btn-github-auth"}])
(when (contains? cf/flags :login-with-github)
[:& bl/button-link {:on-click login-with-github
:icon i/brand-github
:label (tr "auth.login-with-github-submit")
:class (stl/css :login-btn :btn-github-auth)}])
(when (contains? cf/flags :login-with-gitlab)
[:& bl/button-link {:on-click login-with-gitlab
:icon i/brand-gitlab
:label (tr "auth.login-with-gitlab-submit")
:class "btn-gitlab-auth"}])
(when (contains? cf/flags :login-with-gitlab)
[:& bl/button-link {:on-click login-with-gitlab
:icon i/brand-gitlab
:label (tr "auth.login-with-gitlab-submit")
:class (stl/css :login-btn :btn-gitlab-auth)}])
(when (contains? cf/flags :login-with-oidc)
[:& bl/button-link {:on-click login-with-oidc
:icon i/brand-openid
:label (tr "auth.login-with-oidc-submit")
:class "btn-github-auth"}])]))
(when (contains? cf/flags :login-with-oidc)
[:& bl/button-link {:on-click login-with-oidc
:icon i/brand-openid
:label (tr "auth.login-with-oidc-submit")
:class (stl/css :login-btn :btn-oidc-auth)}])]
[:div.auth-buttons
(when (contains? cf/flags :login-with-google)
[:& bl/button-link {:on-click login-with-google
:icon i/brand-google
:label (tr "auth.login-with-google-submit")
:class "btn-google-auth"}])
(when (contains? cf/flags :login-with-github)
[:& bl/button-link {:on-click login-with-github
:icon i/brand-github
:label (tr "auth.login-with-github-submit")
:class "btn-github-auth"}])
(when (contains? cf/flags :login-with-gitlab)
[:& bl/button-link {:on-click login-with-gitlab
:icon i/brand-gitlab
:label (tr "auth.login-with-gitlab-submit")
:class "btn-gitlab-auth"}])
(when (contains? cf/flags :login-with-oidc)
[:& bl/button-link {:on-click login-with-oidc
:icon i/brand-openid
:label (tr "auth.login-with-oidc-submit")
:class "btn-github-auth"}])])))
(mf/defc login-button-oidc
[{:keys [params] :as props}]
(when (contains? cf/flags :login-with-oidc)
[:div.link-entry.link-oidc
[:a {:tab-index "0"
:on-key-down (fn [event]
(when (k/enter? event)
(login-with-oidc event :oidc params)))
:on-click #(login-with-oidc % :oidc params)}
(tr "auth.login-with-oidc-submit")]]))
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
(when (contains? cf/flags :login-with-oidc)
[:div {:class (stl/css :link-entry :link-oidc)}
[:a {:tab-index "0"
:on-key-down (fn [event]
(when (k/enter? event)
(login-with-oidc event :oidc params)))
:on-click #(login-with-oidc % :oidc params)}
(tr "auth.login-with-oidc-submit")]])
(when (contains? cf/flags :login-with-oidc)
[:div.link-entry.link-oidc
[:a {:tab-index "0"
:on-key-down (fn [event]
(when (k/enter? event)
(login-with-oidc event :oidc params)))
:on-click #(login-with-oidc % :oidc params)}
(tr "auth.login-with-oidc-submit")]]))))
(mf/defc login-methods
[{:keys [params on-success-callback] :as props}]
[:*
(when show-alt-login-buttons?
[:*
[:span.separator
[:span.line]
[:span.text (tr "labels.continue-with")]
[:span.line]]
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:*
(when show-alt-login-buttons?
[:*
[:& login-buttons {:params params}]
[:& login-buttons {:params params}]
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password)
(contains? cf/flags :login-with-ldap))
[:hr {:class (stl/css :separator)}])])
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password)
(contains? cf/flags :login-with-ldap))
[:span.separator
[:span.line]
[:span.text (tr "labels.or")]
[:span.line]])])
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password)
(contains? cf/flags :login-with-ldap))
[:& login-form {:params params :on-success-callback on-success-callback}])]
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password)
(contains? cf/flags :login-with-ldap))
[:& login-form {:params params :on-success-callback on-success-callback}])])
;; OLD
[:*
(when show-alt-login-buttons?
[:*
[:span.separator
[:span.line]
[:span.text (tr "labels.continue-with")]
[:span.line]]
[:& login-buttons {:params params}]
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password)
(contains? cf/flags :login-with-ldap))
[:span.separator
[:span.line]
[:span.text (tr "labels.or")]
[:span.line]])])
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password)
(contains? cf/flags :login-with-ldap))
[:& login-form {:params params :on-success-callback on-success-callback}])])))
(mf/defc login-page
[{:keys [params] :as props}]
[:div.generic-form.login-form
[:div.form-container
[:h1 {:data-test "login-title"} (tr "auth.login-title")]
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:div {:class (stl/css :auth-form)}
[:h1 {:class (stl/css :auth-title)
:data-test "login-title"} (tr "auth.login-title")]
[:& login-methods {:params params}]
[:hr {:class (stl/css :separator)}]
[:div.links
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password))
[:div.link-entry
[:& lk/link {:action #(st/emit! (rt/nav :auth-recovery-request))
:data-test "forgot-password"}
(tr "auth.forgot-password")]])
[:& login-methods {:params params}]
(when (contains? cf/flags :registration)
[:div.link-entry
[:span (tr "auth.register") " "]
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} params))
:data-test "register-submit"}
(tr "auth.register-submit")]])]
[:div {:class (stl/css :links)}
(when (contains? cf/flags :registration)
[:div {:class (stl/css :link-entry :register)}
[:span (tr "auth.register") " "]
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} params))
:data-test "register-submit"}
(tr "auth.register-submit")]])]
(when (contains? cf/flags :demo-users)
[:div {:class (stl/css :link-entry :demo-account)}
[:span (tr "auth.create-demo-profile") " "]
[:& lk/link {:action #(st/emit! (du/create-demo-profile))
:data-test "demo-account-link"}
(tr "auth.create-demo-account")]])]
;; OLD
[:div.generic-form.login-form
[:div.form-container
[:h1 {:data-test "login-title"} (tr "auth.login-title")]
[:& login-methods {:params params}]
[:div.links
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-password))
[:div.link-entry
[:& lk/link {:action #(st/emit! (rt/nav :auth-recovery-request))
:data-test "forgot-password"}
(tr "auth.forgot-password")]])
(when (contains? cf/flags :registration)
[:div.link-entry
[:span (tr "auth.register") " "]
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} params))
:data-test "register-submit"}
(tr "auth.register-submit")]])]
(when (contains? cf/flags :demo-users)
[:div.links.demo
[:div.link-entry
[:span (tr "auth.create-demo-profile") " "]
[:& lk/link {:action #(st/emit! (du/create-demo-profile))
:data-test "demo-account-link"}
(tr "auth.create-demo-account")]]])]])))
(when (contains? cf/flags :demo-users)
[:div.links.demo
[:div.link-entry
[:span (tr "auth.create-demo-profile") " "]
[:& lk/link {:action #(st/emit! (du/create-demo-profile))
:data-test "demo-account-link"}
(tr "auth.create-demo-account")]]])]])

View file

@ -0,0 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "./common.scss";

View file

@ -5,12 +5,14 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.auth.recovery
(:require-macros [app.main.style :as stl])
(:require
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.users :as du]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
[app.main.ui.context :as ctx]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[cljs.spec.alpha :as s]
@ -55,38 +57,73 @@
(mf/defc recovery-form
[{:keys [params] :as props}]
(let [form (fm/use-form :spec ::recovery-form
(let [new-css-system (mf/use-ctx ctx/new-css-system)
form (fm/use-form :spec ::recovery-form
:validators [password-equality
(fm/validate-not-empty :password-1 (tr "auth.password-not-empty"))
(fm/validate-not-empty :password-2 (tr "auth.password-not-empty"))]
:initial params)]
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:type "password"
:name :password-1
:label (tr "auth.new-password")}]]
(if new-css-system
[:& fm/form {:on-submit on-submit :form form}
[:div {:class (stl/css :fields-row)}
[:& fm/input {:type "password"
:name :password-1
:show-success? true
:label (tr "auth.new-password")
:class (stl/css :form-field)}]]
[:div.fields-row
[:& fm/input {:type "password"
:name :password-2
:label (tr "auth.confirm-password")}]]
[:div {:class (stl/css :fields-row)}
[:& fm/input {:type "password"
:name :password-2
:show-success? true
:label (tr "auth.confirm-password")
:class (stl/css :form-field)}]]
[:> fm/submit-button*
{:label (tr "auth.recovery-submit")}]]))
[:> fm/submit-button*
{:label (tr "auth.recovery-submit")
:class (stl/css :submit-btn)}]]
;; OLD
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:type "password"
:name :password-1
:label (tr "auth.new-password")}]]
[:div.fields-row
[:& fm/input {:type "password"
:name :password-2
:label (tr "auth.confirm-password")}]]
[:> fm/submit-button*
{:label (tr "auth.recovery-submit")}]])))
;; --- Recovery Request Page
(mf/defc recovery-page
[{:keys [params] :as props}]
[:section.generic-form
[:div.form-container
[:h1 "Forgot your password?"]
[:div.subtitle "Please enter your new password"]
[:& recovery-form {:params params}]
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:div {:class (stl/css :auth-form)}
[:h1 {:class (stl/css :auth-title)} "Forgot your password?"]
[:div {:class (stl/css :auth-subtitle)} "Please enter your new password"]
[:hr {:class (stl/css :separator)}]
[:& recovery-form {:params params}]
[:div.links
[:div.link-entry
[:a {:on-click #(st/emit! (rt/nav :auth-login))}
(tr "profile.recovery.go-to-login")]]]]])
[:div {:class (stl/css :links)}
[:div {:class (stl/css :link-entry)}
[:a {:on-click #(st/emit! (rt/nav :auth-login))}
(tr "profile.recovery.go-to-login")]]]]
;; TODO
[:section.generic-form
[:div.form-container
[:h1 "Forgot your password?"]
[:div.subtitle "Please enter your new password"]
[:& recovery-form {:params params}]
[:div.links
[:div.link-entry
[:a {:on-click #(st/emit! (rt/nav :auth-login))}
(tr "profile.recovery.go-to-login")]]]]])))

View file

@ -0,0 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-refactor.scss" as *;
@use "./common.scss";
.submit-btn {
margin-top: $s-16;
}

View file

@ -5,6 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.auth.recovery-request
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.spec :as us]
@ -13,6 +14,7 @@
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
@ -32,7 +34,8 @@
(mf/defc recovery-form
[{:keys [on-success-callback] :as props}]
(let [form (fm/use-form :spec ::recovery-request-form
(let [new-css-system (mf/use-ctx ctx/new-css-system)
form (fm/use-form :spec ::recovery-request-form
:validators [handle-error-messages]
:initial {})
submitted (mf/use-state false)
@ -74,32 +77,62 @@
(reset! form nil)
(st/emit! (du/request-profile-recovery params)))))]
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:name :email
:label (tr "auth.email")
:help-icon i/at
:type "text"}]]
(if new-css-system
[:& fm/form {:on-submit on-submit
:form form}
[:div {:class (stl/css :fields-row)}
[:& fm/input {:name :email
:label (tr "auth.email")
:type "text"
:class (stl/css :form-field)}]]
[:> fm/submit-button*
{:label (tr "auth.recovery-request-submit")
:data-test "recovery-resquest-submit"}]]))
[:> fm/submit-button*
{:label (tr "auth.recovery-request-submit")
:data-test "recovery-resquest-submit"
:class (stl/css :recover-btn)}]]
;; OLD
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:name :email
:label (tr "auth.email")
:help-icon i/at
:type "text"}]]
[:> fm/submit-button*
{:label (tr "auth.recovery-request-submit")
:data-test "recovery-resquest-submit"}]])))
;; --- Recovery Request Page
(mf/defc recovery-request-page
[{:keys [params on-success-callback go-back-callback] :as props}]
(let [default-go-back #(st/emit! (rt/nav :auth-login))
(let [new-css-system (mf/use-ctx ctx/new-css-system)
default-go-back #(st/emit! (rt/nav :auth-login))
go-back (or go-back-callback default-go-back)]
[:section.generic-form
[:div.form-container
[:h1 (tr "auth.recovery-request-title")]
[:div.subtitle (tr "auth.recovery-request-subtitle")]
[:& recovery-form {:params params :on-success-callback on-success-callback}]
[:div.links
[:div.link-entry
(if new-css-system
[:div {:class (stl/css :auth-form)}
[:h1 {:class (stl/css :auth-title)} (tr "auth.recovery-request-title")]
[:div {:class (stl/css :auth-subtitle)} (tr "auth.recovery-request-subtitle")]
[:hr {:class (stl/css :separator)}]
[:& recovery-form {:params params :on-success-callback on-success-callback}]
[:div {:class (stl/css :link-entry)}
[:& lk/link {:action go-back
:data-test "go-back-link"}
(tr "labels.go-back")]]]]]))
(tr "labels.go-back")]]]
;; old
[:section.generic-form
[:div.form-container
[:h1 (tr "auth.recovery-request-title")]
[:div.subtitle (tr "auth.recovery-request-subtitle")]
[:& recovery-form {:params params :on-success-callback on-success-callback}]
[:div.links
[:div.link-entry
[:& lk/link {:action go-back
:data-test "go-back-link"}
(tr "labels.go-back")]]]]])))

View file

@ -0,0 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-refactor.scss" as *;
@use "./common.scss";
.fields-row {
margin-bottom: $s-8;
}

View file

@ -5,6 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.auth.register
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.spec :as us]
@ -16,9 +17,10 @@
[app.main.ui.auth.login :as login]
[app.main.ui.components.forms :as fm]
[app.main.ui.components.link :as lk]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.util.i18n :refer [tr]]
[app.util.i18n :refer [tr tr-html]]
[app.util.router :as rt]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
@ -26,9 +28,10 @@
(mf/defc demo-warning
[_]
[:& msgs/inline-banner
{:type :warning
:content (tr "auth.demo-warning")}])
[:div {:class (stl/css :banner)}
[:& msgs/inline-banner
{:type :warning
:content (tr "auth.demo-warning")}]])
;; --- PAGE: Register
@ -85,7 +88,8 @@
(mf/defc register-form
[{:keys [params on-success-callback] :as props}]
(let [initial (mf/use-memo (mf/deps params) (constantly params))
(let [new-css-system (mf/use-ctx ctx/new-css-system)
initial (mf/use-memo (mf/deps params) (constantly params))
form (fm/use-form :spec ::register-form
:validators [validate
(fm/validate-not-empty :password (tr "auth.password-not-empty"))]
@ -110,72 +114,133 @@
(partial handle-prepare-register-error form))))))]
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:type "email"
:name :email
:help-icon i/at
:label (tr "auth.email")
:data-test "email-input"}]]
[:div.fields-row
[:& fm/input {:name :password
:hint (tr "auth.password-length-hint")
:label (tr "auth.password")
:type "password"}]]
(if new-css-system
[:& fm/form {:on-submit on-submit :form form}
[:div {:class (stl/css :fields-row)}
[:& fm/input {:type "email"
:name :email
:label (tr "auth.email")
:data-test "email-input"
:show-success? true
:class (stl/css :form-field)}]]
[:div {:class (stl/css :fields-row)}
[:& fm/input {:name :password
:hint (tr "auth.password-length-hint")
:label (tr "auth.password")
:show-success? true
:type "password"
:class (stl/css :form-field)}]]
[:> fm/submit-button*
{:label (tr "auth.register-submit")
:disabled @submitted?
:data-test "register-form-submit"}]]))
[:> fm/submit-button*
{:label (tr "auth.register-submit")
:disabled @submitted?
:data-test "register-form-submit"
:class (stl/css :register-btn)}]]
;; OLD
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:type "email"
:name :email
:help-icon i/at
:label (tr "auth.email")
:data-test "email-input"}]]
[:div.fields-row
[:& fm/input {:name :password
:hint (tr "auth.password-length-hint")
:label (tr "auth.password")
:type "password"}]]
[:> fm/submit-button*
{:label (tr "auth.register-submit")
:disabled @submitted?
:data-test "register-form-submit"}]])))
(mf/defc register-methods
[{:keys [params on-success-callback] :as props}]
[:*
(when login/show-alt-login-buttons?
[:*
[:span.separator
[:span.line]
[:span.text (tr "labels.continue-with")]
[:span.line]]
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:*
(when login/show-alt-login-buttons?
[:*
[:hr {:class (stl/css :separator)}]
[:& login/login-buttons {:params params}]])
[:hr {:class (stl/css :separator)}]
[:& register-form {:params params :on-success-callback on-success-callback}]]
[:& login/login-buttons {:params params}]
;; OLD
[:*
(when login/show-alt-login-buttons?
[:*
[:span.separator
[:span.line]
[:span.text (tr "labels.continue-with")]
[:span.line]]
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-ldap))
[:span.separator
[:span.line]
[:span.text (tr "labels.or")]
[:span.line]])])
[:& login/login-buttons {:params params}]
[:& register-form {:params params :on-success-callback on-success-callback}]])
(when (or (contains? cf/flags :login)
(contains? cf/flags :login-with-ldap))
[:span.separator
[:span.line]
[:span.text (tr "labels.or")]
[:span.line]])])
[:& register-form {:params params :on-success-callback on-success-callback}]])))
(mf/defc register-page
[{:keys [params] :as props}]
[:div.form-container
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:div {:class (stl/css :auth-form)}
[:h1 {:class (stl/css :auth-title)
:data-test "registration-title"} (tr "auth.register-title")]
[:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")]
[:h1 {:data-test "registration-title"} (tr "auth.register-title")]
[:div.subtitle (tr "auth.register-subtitle")]
(when (contains? cf/flags :demo-warning)
[:& demo-warning])
(when (contains? cf/flags :demo-warning)
[:& demo-warning])
[:& register-methods {:params params}]
[:& register-methods {:params params}]
[:div {:class (stl/css :links)}
[:div {:class (stl/css :link-entry :account)}
[:span (tr "auth.already-have-account") " "]
[:div.links
[:div.link-entry
[:span (tr "auth.already-have-account") " "]
[:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params))
:data-test "login-here-link"}
(tr "auth.login-here")]]
[:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params))
:data-test "login-here-link"}
(tr "auth.login-here")]]
(when (contains? cf/flags :demo-users)
[:div {:class (stl/css :link-entry :demo-users)}
[:span (tr "auth.create-demo-profile") " "]
[:& lk/link {:action #(st/emit! (du/create-demo-profile))}
(tr "auth.create-demo-account")]])]]
(when (contains? cf/flags :demo-users)
[:div.link-entry
[:span (tr "auth.create-demo-profile") " "]
[:& lk/link {:action #(st/emit! (du/create-demo-profile))}
(tr "auth.create-demo-account")]])]])
;; OLD
[:div.form-container
[:h1 {:data-test "registration-title"} (tr "auth.register-title")]
[:div.subtitle (tr "auth.register-subtitle")]
(when (contains? cf/flags :demo-warning)
[:& demo-warning])
[:& register-methods {:params params}]
[:div.links
[:div.link-entry
[:span (tr "auth.already-have-account") " "]
[:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params))
:data-test "login-here-link"}
(tr "auth.login-here")]]
(when (contains? cf/flags :demo-users)
[:div.link-entry
[:span (tr "auth.create-demo-profile") " "]
[:& lk/link {:action #(st/emit! (du/create-demo-profile))}
(tr "auth.create-demo-account")]])]])))
;; --- PAGE: register validation
@ -219,7 +284,8 @@
(mf/defc register-validate-form
[{:keys [params on-success-callback] :as props}]
(let [form (fm/use-form :spec ::register-validate-form
(let [new-css-system (mf/use-ctx ctx/new-css-system)
form (fm/use-form :spec ::register-validate-form
:validators [(fm/validate-not-empty :fullname (tr "auth.name.not-all-space"))
(fm/validate-length :fullname fm/max-length-allowed (tr "auth.name.too-long"))]
:initial params)
@ -240,48 +306,103 @@
(rx/subs on-success
(partial handle-register-error form))))))]
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:name :fullname
:label (tr "auth.fullname")
:type "text"}]]
(if new-css-system
[:& fm/form {:on-submit on-submit :form form}
[:div {:class (stl/css :fields-row)}
[:& fm/input {:name :fullname
:label (tr "auth.fullname")
:type "text"
:show-success? true
:class (stl/css :form-field)}]]
(when (contains? cf/flags :terms-and-privacy-checkbox)
[:div.fields-row.input-visible.accept-terms-and-privacy-wrapper
[:& fm/input {:name :accept-terms-and-privacy
:class "check-primary"
:type "checkbox"}
[:span
(tr "auth.terms-privacy-agreement")]]
[:div.auth-links
[:a {:href "https://penpot.app/terms" :target "_blank"} (tr "auth.terms-of-service")]
[:span ",\u00A0"]
[:a {:href "https://penpot.app/privacy" :target "_blank"} (tr "auth.privacy-policy")]]])
(when (contains? cf/flags :terms-and-privacy-checkbox)
(let [terms-label
(mf/html
[:& tr-html
{:tag-name "div"
:label "auth.terms-privacy-agreement-md"
:params [cf/terms-of-service-uri cf/privacy-policy-uri]}])]
[:div {:class (stl/css :fields-row :input-visible :accept-terms-and-privacy-wrapper)}
[:& fm/input {:name :accept-terms-and-privacy
:class "check-primary"
:type "checkbox"
:label terms-label}]]))
[:> fm/submit-button*
{:label (tr "auth.register-submit")
:disabled @submitted?}]]))
[:> fm/submit-button*
{:label (tr "auth.register-submit")
:disabled @submitted?
:class (stl/css :register-btn)}]]
;; OLD
[:& fm/form {:on-submit on-submit
:form form}
[:div.fields-row
[:& fm/input {:name :fullname
:label (tr "auth.fullname")
:type "text"}]]
(when (contains? cf/flags :terms-and-privacy-checkbox)
[:div.fields-row.input-visible.accept-terms-and-privacy-wrapper
[:& fm/input {:name :accept-terms-and-privacy
:class "check-primary"
:type "checkbox"}
[:span
(tr "auth.terms-privacy-agreement")]]
[:div.auth-links
[:a {:href "https://penpot.app/terms" :target "_blank"} (tr "auth.terms-of-service")]
[:span ",\u00A0"]
[:a {:href "https://penpot.app/privacy" :target "_blank"} (tr "auth.privacy-policy")]]])
[:> fm/submit-button*
{:label (tr "auth.register-submit")
:disabled @submitted?}]])))
(mf/defc register-validate-page
[{:keys [params] :as props}]
[:div.form-container
[:h1 {:data-test "register-title"} (tr "auth.register-title")]
[:div.subtitle (tr "auth.register-subtitle")]
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:div {:class (stl/css :auth-form)}
[:h1 {:class (stl/css :auth-title)
:data-test "register-title"} (tr "auth.register-title")]
[:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")]
[:& register-validate-form {:params params}]
[:hr {:class (stl/css :separator)}]
[:div.links
[:div.link-entry
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))}
(tr "labels.go-back")]]]])
[:& register-validate-form {:params params}]
[:div {:class (stl/css :links)}
[:div {:class (stl/css :link-entry :go-back)}
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))}
(tr "labels.go-back")]]]]
;; OLD
[:div.form-container
[:h1 {:data-test "register-title"} (tr "auth.register-title")]
[:div.subtitle (tr "auth.register-subtitle")]
[:& register-validate-form {:params params}]
[:div.links
[:div.link-entry
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))}
(tr "labels.go-back")]]]])))
(mf/defc register-success-page
[{:keys [params] :as props}]
[:div.form-container
[:div.notification-icon i/icon-verify]
[:div.notification-text (tr "auth.verification-email-sent")]
[:div.notification-text-email (:email params "")]
[:div.notification-text (tr "auth.check-your-email")]])
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:div {:class (stl/css :auth-form :register-success)}
[:div {:class (stl/css :notification-icon)} i/icon-verify]
[:div {:class (stl/css :notification-text)} (tr "auth.verification-email-sent")]
[:div {:class (stl/css :notification-text-email)} (:email params "")]
[:div {:class (stl/css :notification-text)} (tr "auth.check-your-email")]]
;; OLD
[:div.form-container
[:div.notification-icon i/icon-verify]
[:div.notification-text (tr "auth.verification-email-sent")]
[:div.notification-text-email (:email params "")]
[:div.notification-text (tr "auth.check-your-email")]])))

View file

@ -0,0 +1,38 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-refactor.scss" as *;
@use "./common.scss";
.accept-terms-and-privacy-wrapper {
margin: $s-16 0;
:global(a) {
color: $df-secondary;
font-weight: $fw700;
}
}
.register-success {
padding-bottom: $s-32;
}
.notification-icon {
fill: $df-primary;
display: flex;
justify-content: center;
margin-bottom: $s-32;
svg {
width: $s-92;
height: $s-92;
}
}
.notification-text-email,
.notification-text {
font-size: $fs-16;
color: $df-secondary;
margin-bottom: $s-16;
}

View file

@ -5,11 +5,13 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.auth.verify-token
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.messages :as dm]
[app.main.data.users :as du]
[app.main.repo :as rp]
[app.main.store :as st]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i]
[app.main.ui.static :as static]
[app.util.dom :as dom]
@ -60,7 +62,8 @@
(mf/defc verify-token
[{:keys [route] :as props}]
(let [token (get-in route [:query-params :token])
(let [new-css-system (mf/use-ctx ctx/new-css-system)
token (get-in route [:query-params :token])
bad-token (mf/use-state false)]
(mf/with-effect []
@ -92,9 +95,7 @@
(st/emit! (rt/nav :auth-login))))))))
(if @bad-token
[:> static/static-header {}
[:div.image i/unchain]
[:div.main-message (tr "errors.invite-invalid")]
[:div.desc-message (tr "errors.invite-invalid.info")]]
[:div.verify-token
[:> static/invalid-token {}]
[:div {:class (stl/css-case :verify-token new-css-system
:global/verify-token (not new-css-system))}
i/loader-pencil])))

View file

@ -0,0 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "./common.scss";

View file

@ -10,16 +10,20 @@
["highlight.js" :as hljs]
[app.common.data.macros :as dm]
[app.main.ui.context :as ctx]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(mf/defc code-block
{::mf/wrap-props false}
[{:keys [code type]}]
(let [new-css-system (mf/use-ctx ctx/new-css-system)
block-ref (mf/use-ref)]
block-ref (mf/use-ref)
code (str/trim code)]
(mf/with-effect [code type]
(when-let [node (mf/ref-val block-ref)]
(hljs/highlightElement node)))
[:pre {:class (dm/str type " " (stl/css new-css-system :code-display)) :ref block-ref} code]))
(if new-css-system
[:pre {:class (dm/str type " " (stl/css :code-display)) :ref block-ref} code]
[:pre {:class (dm/str type " " "code-display") :ref block-ref} code])))

View file

@ -7,8 +7,10 @@
@import "refactor/common-refactor.scss";
.code-display {
user-select: text;
border-radius: $br-8;
margin-top: $s-8;
padding: $s-12;
background-color: var(--menu-background-color);
overflow: auto;
}

View file

@ -27,8 +27,9 @@
(def use-form fm/use-form)
(mf/defc input
[{:keys [label help-icon disabled form hint trim children data-test on-change-value placeholder] :as props}]
[{:keys [label help-icon disabled form hint trim children data-test on-change-value placeholder show-success?] :as props}]
(let [new-css-system (mf/use-ctx ctx/new-css-system)
input-type (get props :type "text")
input-name (get props :name)
more-classes (get props :class)
@ -54,36 +55,17 @@
help-icon' (cond
(and (= input-type "password")
(= @type' "password"))
i/eye
i/shown-refactor
(and (= input-type "password")
(= @type' "text"))
i/eye-closed
i/hide-refactor
:else
help-icon)
on-change-value (or on-change-value (constantly nil))
klass (str more-classes " "
(dom/classnames
:focus @focus?
:valid (and touched? (not error))
:invalid (and touched? error)
:disabled disabled
:empty (and is-text? (str/empty? value))
:with-icon (not (nil? help-icon'))
:custom-input is-text?
:input-radio is-radio?
:input-checkbox is-checkbox?))
new-classes (dm/str more-classes " "
(stl/css-case
:input-wrapper true
:global/invalid (and touched? error)
:checkbox is-checkbox?
:global/disabled disabled))
swap-text-password
(fn []
(swap! type' (fn [input-type]
@ -100,9 +82,7 @@
on-blur
(fn [_]
(reset! focus? false)
(when-not (get-in @form [:touched input-name])
(swap! form assoc-in [:touched input-name] true)))
(reset! focus? false))
on-click
(fn [_]
@ -126,10 +106,19 @@
(cond-> (and value is-checkbox?) (assoc :default-checked value))
(cond-> (and touched? (:message error)) (assoc "aria-invalid" "true"
"aria-describedby" (dm/str "error-" input-name)))
(obj/clj->props))]
(obj/clj->props))
show-valid? (and show-success? touched? (not error))
show-invalid? (and touched? error)]
(if new-css-system
[:div {:class new-classes}
[:div {:class (dm/str more-classes " "
(stl/css-case
:input-wrapper true
:valid show-valid?
:invalid show-invalid?
:checkbox is-checkbox?
:disabled disabled))}
[:*
(cond
(some? label)
@ -137,23 +126,38 @@
:input-label is-text?
:radio-label is-radio?
:checkbox-label is-checkbox?)
:tab-index "0"
:tab-index "-1"
:for (name input-name)} label
(when is-checkbox?
[:span {:class (stl/css-case :global/checked value)} i/status-tick-refactor])
[:> :input props]]
(if is-checkbox?
[:> :input props]
[:div {:class (stl/css :input-and-icon)}
[:> :input props]
(when help-icon'
[:span {:class (stl/css :help-icon)
:on-click (when (= "password" input-type)
swap-text-password)}
help-icon'])
(when show-valid?
[:span {:class (stl/css :valid-icon)}
i/tick-refactor])
(when show-invalid?
[:span {:class (stl/css :invalid-icon)}
i/close-refactor])])]
(some? children)
[:label {:for (name input-name)}
[:> :input props]
children])
(when help-icon'
[:span {:class (stl/css :help-icon)
:on-click (when (= "password" input-type)
swap-text-password)}
help-icon'])
(cond
(and touched? (:message error))
[:div {:id (dm/str "error-" input-name)
@ -164,9 +168,19 @@
(string? hint)
[:div {:class (stl/css :hint)} hint])]]
;;OLD
[:div
{:class klass}
{:class (str more-classes " "
(dom/classnames
:focus @focus?
:valid (and touched? (not error))
:invalid (and touched? error)
:disabled disabled
:empty (and is-text? (str/empty? value))
:with-icon (not (nil? help-icon'))
:custom-input is-text?
:input-radio is-radio?
:input-checkbox is-checkbox?))}
[:*
[:> :input props]
(cond
@ -387,7 +401,7 @@
(true? (unchecked-get props "disabled")))
klass (dm/str class " " (if disabled? "btn-disabled" ""))
new-klass (if disabled? (stl/css :btn-disabled) class)
new-klass (dm/str class " " (if disabled? (stl/css :btn-disabled) ""))
on-key-down
(mf/use-fn

View file

@ -10,8 +10,8 @@
.input-wrapper {
display: flex;
flex-direction: column;
gap: $s-6;
align-items: center;
position: relative;
.input-with-label {
@include flexColumn;
gap: $s-8;
@ -24,11 +24,13 @@
cursor: pointer;
color: var(--modal-title-foreground-color);
text-transform: uppercase;
input {
@extend .input-element;
color: var(--input-foreground-color-active);
width: calc(100% - $s-1);
margin-top: 0;
width: 100%;
height: 100%;
&:focus {
outline: none;
@ -41,28 +43,101 @@
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-text-fill-color: var(--input-foreground-color-active) !important;
-webkit-box-shadow: 0 0 0 28px var(--input-background-color) inset !important;
-webkit-text-fill-color: var(--input-foreground-color-active);
-webkit-box-shadow: inset 0 0 20px 20px var(--input-background-color);
border: $s-1 solid var(--input-background-color);
-webkit-background-clip: text;
transition: background-color 5000s ease-in-out 0s;
caret-color: var(--input-foreground-color-active);
}
}
&:global(.invalid) {
&.valid {
input {
border: $s-1 solid var(--input-border-color-success);
@extend .disabled-input;
&:hover,
&:focus {
border: $s-1 solid var(--input-border-color-success);
}
}
}
&.invalid {
input {
border: $s-1 solid var(--input-border-color-error);
@extend .disabled-input;
&:hover,
&:focus {
border: $s-1 solid var(--input-border-color-error);
}
}
}
&.valid .help-icon,
&.invalid .help-icon {
right: $s-40;
}
}
.input-and-icon {
position: relative;
width: var(--input-width, calc(100% - $s-1));
min-width: var(--input-min-width);
height: var(--input-height, $s-32);
}
.help-icon {
cursor: pointer;
position: absolute;
right: $s-16;
top: calc(50% - $s-8);
svg {
@extend .button-icon-small;
stroke: var(--input-details-color);
stroke: $df-secondary;
width: $s-16;
height: $s-16;
}
}
.invalid-icon {
width: $s-16;
height: $s-16;
background: var(--input-border-color-error);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: $s-16;
top: calc(50% - $s-8);
svg {
width: $s-12;
height: $s-12;
stroke: var(--input-background-color);
}
}
.valid-icon {
width: $s-16;
height: $s-16;
background: var(--input-border-color-success);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: $s-16;
top: calc(50% - $s-8);
svg {
width: $s-12;
height: $s-12;
fill: var(--input-border-color-success);
stroke: var(--input-background-color);
}
}
.error {
color: var(--input-border-color-error);
width: 100%;
font-size: $fs-14;
}
.hint {
@ -181,9 +256,6 @@
// SUBMIT-BUTTON
.btn-disabled {
@extend .button-disabled;
height: $s-32;
padding: $s-8 $s-24;
border-radius: $br-8;
}
// MULTI INPUT

View file

@ -139,8 +139,9 @@
(if new-css-system
[:*
[:li {:tab-index "0"
:class (if selected? (stl/css :current)
(when (:dragging? local) (stl/css :dragging)))
:class (stl/css-case :project-element true
:current selected?
:dragging (:dragging? local))
:on-click on-click
:on-key-down on-key-down
:on-double-click on-edit-open
@ -1255,3 +1256,4 @@
[:& profile-section
{:profile profile
:team team}]])))

View file

@ -356,8 +356,13 @@
cursor: pointer;
display: flex;
flex-shrink: 0;
padding: $s-8 $s-8 $s-8 $s-24;
&.project-element {
padding: $s-8 $s-8 $s-8 $s-24;
}
a {
padding: $s-8 $s-8 $s-8 $s-24;
font-weight: $fw400;
width: 100%;
&:hover {

View file

@ -504,10 +504,9 @@
border-radius: 50%;
color: $df-primary;
z-index: $z-index-modal;
background-color: $da-primary;
height: $s-120;
width: $s-120;
height: 100%;
width: 100%;
svg {
fill: $db-primary;
@ -517,8 +516,6 @@
&:hover {
.update-overlay {
opacity: 1;
width: $s-72;
height: $s-72;
top: $s-16;
left: $s-16;
}

View file

@ -172,6 +172,7 @@
(def logo-icon (icon-xref :penpot-logo-icon))
(def logo-error-screen (icon-xref :logo-error-screen))
(def logout (icon-xref :logout))
(def login-illustration (icon-xref :login-illustration))
(def lowercase (icon-xref :lowercase))
(def mail (icon-xref :mail))
(def mask (icon-xref :mask))

View file

@ -80,7 +80,7 @@
[:div {:ref wrapper-ref
:class (stl/css-case
:modal-wrapper new-css-system
:old-css/modal-wrapper (not new-css-system))}
:global/modal-wrapper (not new-css-system))}
(mf/element component (:props data))])))
(def modal-ref

View file

@ -126,6 +126,7 @@
:name :name
:disabled @created?
:label (tr "modals.create-access-token.name.label")
:show-success? true
:placeholder (tr "modals.create-access-token.name.placeholder")}]]
[:div {:class (stl/css :fields-row)}

View file

@ -122,6 +122,7 @@
:name :email-1
:label (tr "modals.change-email.new-email")
:trim true
:show-success? true
:on-change-value on-email-change}]]
[:div {:class (stl/css :fields-row)}
@ -129,6 +130,7 @@
:name :email-2
:label (tr "modals.change-email.confirm-email")
:trim true
:show-success? true
:on-change-value on-email-change}]]]
[:div {:class (stl/css :modal-footer)}

View file

@ -73,7 +73,8 @@
[:div {:class (stl/css :fields-row)}
[:& fm/input {:label (tr "feedback.subject")
:name :subject}]]
:name :subject
:show-success? true}]]
[:div {:class (stl/css :fields-row :description)}
[:& fm/textarea
{:label (tr "feedback.description")

View file

@ -95,17 +95,20 @@
[:& fm/input
{:type "password"
:name :password-1
:show-success? true
:label (t locale "labels.new-password")}]]
[:div {:class (stl/css :fields-row)}
[:& fm/input
{:type "password"
:name :password-2
:show-success? true
:label (t locale "labels.confirm-password")}]]
[:> fm/submit-button*
{:label (t locale "dashboard.update-settings")
:data-test "submit-password"}]]
:data-test "submit-password"
:class (stl/css :update-btn)}]]
;; OLD
[:& fm/form {:class "password-form"
@ -153,4 +156,3 @@
[:section.dashboard-settings.form-container
[:div.form-container
[:& password-form {:locale locale}]]])))

View file

@ -4,4 +4,11 @@
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-refactor.scss" as *;
@use "./profile" as *;
.update-btn {
margin-top: $s-16;
@extend .button-primary;
height: $s-36;
}

View file

@ -75,7 +75,6 @@
{:type "email"
:name :email
:disabled true
:help-icon i/at
:label (tr "dashboard.your-email")}]
[:div {:class (stl/css :options)}

View file

@ -48,6 +48,7 @@
}
.fields-row {
--input-height: $s-40;
margin-bottom: $s-20;
flex-direction: column;
@ -57,10 +58,6 @@
font-size: $fs-14;
margin-top: $s-12;
}
:global(input) {
height: $s-40;
}
}
.field {

View file

@ -25,7 +25,7 @@
on-click (mf/use-callback #(set! (.-href globals/location) "/"))]
(if new-css-system
[:section {:class (stl/css :exception-layout)}
[:div
[:button
{:class (stl/css :exception-header)
:on-click on-click}
i/logo-icon]
@ -42,6 +42,19 @@
[:div.exception-content
[:div.container children]]])))
(mf/defc invalid-token
[]
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
(if new-css-system
[:> static-header {}
[:div {:class (stl/css :main-message)} (tr "errors.invite-invalid")]
[:div {:class (stl/css :desc-message)} (tr "errors.invite-invalid.info")]]
[:> static-header {}
[:div.image i/unchain]
[:div.main-message (tr "errors.invite-invalid")]
[:div.desc-message (tr "errors.invite-invalid.info")]])))
(mf/defc not-found
[]
(let [new-css-system (mf/use-ctx ctx/new-css-system)]

View file

@ -44,6 +44,9 @@
.exception-header {
padding: $s-24 $s-32;
position: fixed;
background: none;
border: none;
cursor: pointer;
svg {
fill: $df-primary;
width: $s-48;
@ -89,3 +92,9 @@
font-size: $fs-11;
}
}
.image {
svg {
fill: $df-primary;
}
}

View file

@ -196,8 +196,8 @@
(obj/set! "on-expand" handle-expand))]
[:aside {:class (stl/css-case new-css-system
:old-css/settings-bar true
:old-css/settings-bar-right true
:global/settings-bar (not new-css-system)
:global/settings-bar-right (not new-css-system)
:right-settings-bar true
:not-expand (not can-be-expanded?)
:expanded (> size 276))

View file

@ -156,7 +156,7 @@
[:div.shadow-option {:class (stl/css-case new-css-system
:old-css/shadow-option true
:global/shadow-option true
:shadow-element true
:dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot))

View file

@ -7,6 +7,7 @@
(ns app.util.i18n
"A i18n foundation."
(:require
[app.common.data :as d]
[app.common.logging :as log]
[app.config :as cfg]
[app.util.dom :as dom]
@ -175,8 +176,10 @@
{::mf/wrap-props false}
[props]
(let [label (obj/get props "label")
tag-name (obj/get props "tag-name" "p")]
[:> tag-name {:dangerouslySetInnerHTML #js {:__html (tr label)}}]))
tag-name (obj/get props "tag-name" "p")
params (obj/get props "params" [])
html (apply tr (d/concat-vec [label] params))]
[:> tag-name {:dangerouslySetInnerHTML #js {:__html html}}]))
;; DEPRECATED
(defn use-locale

View file

@ -168,10 +168,14 @@ msgid "auth.terms-of-service"
msgstr "Terms of service"
#: src/app/main/ui/auth/register.cljs
#, markdown
msgid "auth.terms-privacy-agreement-md"
msgstr ""
"When creating a new account, you agree to our [terms of service](%s) and [privacy policy](%s)."
msgid "auth.terms-privacy-agreement"
msgstr ""
"When creating a new account, you agree to our terms of service and privacy "
"policy."
"When creating a new account, you agree to ourf terms of service and privacy policy."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"

View file

@ -173,10 +173,14 @@ msgid "auth.terms-of-service"
msgstr "Términos de servicio"
#: src/app/main/ui/auth/register.cljs
#, markdown
msgid "auth.terms-privacy-agreement-md"
msgstr ""
"Al crear una nueva cuenta, aceptas nuestros [términos de servicio](%s) y [política de privacidad](%s)."
msgid "auth.terms-privacy-agreement"
msgstr ""
"Al crear una nueva cuenta, aceptas nuestros términos de servicio y política "
"de privacidad."
"Al crear una nueva cuenta, aceptas nuestros [términos de servicio](%s) y [política de privacidad](%s)."
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"