mirror of
https://github.com/penpot/penpot.git
synced 2025-05-23 08:16:11 +02:00
✨ Update onboarding modals
This commit is contained in:
parent
55ce9bef49
commit
fda6deaa4f
85 changed files with 938 additions and 1482 deletions
|
@ -491,6 +491,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [props {:onboarding-questions-answered true
|
||||
:onboarding-viewed true
|
||||
:onboarding-questions onboarding-questions}]
|
||||
(->> (rp/cmd! :update-profile-props {:props props})
|
||||
(rx/map (constantly (fetch-profile))))))))
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
[app.main.ui.frame-preview :as frame-preview]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.messages :as msgs]
|
||||
[app.main.ui.onboarding :refer [onboarding-modal]]
|
||||
[app.main.ui.onboarding.newsletter :refer [onboarding-newsletter]]
|
||||
[app.main.ui.onboarding.questions :refer [questions-modal]]
|
||||
[app.main.ui.onboarding.team-choice :refer [onboarding-team-modal]]
|
||||
[app.main.ui.releases :refer [release-notes-modal]]
|
||||
[app.main.ui.static :as static]
|
||||
[app.util.dom :as dom]
|
||||
|
@ -96,19 +98,37 @@
|
|||
#_[:& app.main.ui.onboarding/onboarding-modal]
|
||||
#_[:& app.main.ui.onboarding.team-choice/onboarding-team-modal]
|
||||
(when-let [props (get profile :props)]
|
||||
(cond
|
||||
(and (not (:onboarding-viewed props))
|
||||
(contains? cf/flags :onboarding))
|
||||
[:& onboarding-modal {}]
|
||||
(let [show-question-modal? (and (not (:onboarding-viewed props))
|
||||
(contains? cf/flags :onboarding)
|
||||
(not (:onboarding-questions-answered props))
|
||||
(contains? cf/flags :onboarding-questions))
|
||||
|
||||
(and (contains? cf/flags :onboarding)
|
||||
(:onboarding-viewed props)
|
||||
(not= (:release-notes-viewed props) (:main cf/version))
|
||||
(not= "0.0" (:main cf/version)))
|
||||
[:& release-notes-modal {:version (:main cf/version)}]))
|
||||
show-newsletter-modal? (and (not (:onboarding-viewed props))
|
||||
(contains? cf/flags :onboarding)
|
||||
(contains? cf/flags :onboarding-newsletter))
|
||||
|
||||
show-team-modal? (and (not (:onboarding-viewed props))
|
||||
(contains? cf/flags :onboarding)
|
||||
(contains? cf/flags :onboarding-team))
|
||||
|
||||
show-release-modal? (and (contains? cf/flags :onboarding)
|
||||
(:onboarding-viewed props)
|
||||
(not= (:release-notes-viewed props) (:main cf/version))
|
||||
(not= "0.0" (:main cf/version)))]
|
||||
(cond
|
||||
show-question-modal?
|
||||
[:& questions-modal]
|
||||
|
||||
show-newsletter-modal?
|
||||
[:& onboarding-newsletter]
|
||||
|
||||
show-team-modal?
|
||||
[:& onboarding-team-modal]
|
||||
|
||||
show-release-modal?
|
||||
[:& release-notes-modal {:version (:main cf/version)}])))
|
||||
|
||||
[:& dashboard-page {:route route :profile profile}]]
|
||||
|
||||
:viewer
|
||||
(let [{:keys [query-params path-params]} route
|
||||
{:keys [index share-id section page-id interactions-mode frame-id]
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
{::mf/props :obj}
|
||||
[{:keys [route]}]
|
||||
(let [section (dm/get-in route [:data :name])
|
||||
show-login-icon (and
|
||||
(not= section :auth-register-validate)
|
||||
(not= section :auth-register-success))
|
||||
params (:query-params route)
|
||||
error (:error params)]
|
||||
|
||||
|
@ -55,8 +58,9 @@
|
|||
(st/emit! (du/show-redirect-error error))))
|
||||
|
||||
[:main {:class (stl/css :auth-section)}
|
||||
[:h1 {:class (stl/css :logo-container)}
|
||||
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]]
|
||||
(when show-login-icon
|
||||
[:h1 {:class (stl/css :logo-container)}
|
||||
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]])
|
||||
[:div {:class (stl/css :login-illustration)}
|
||||
i/login-illustration]
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: $s-120;
|
||||
height: $s-96;
|
||||
margin-block-end: $s-52;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,14 +10,22 @@
|
|||
width: 100%;
|
||||
padding-block-end: 0;
|
||||
display: grid;
|
||||
gap: $s-24;
|
||||
gap: $s-12;
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-12;
|
||||
margin-top: $s-12;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-title-wrapper {
|
||||
width: 100%;
|
||||
padding-block-end: 0;
|
||||
display: grid;
|
||||
gap: $s-8;
|
||||
}
|
||||
|
||||
.separator {
|
||||
border-color: var(--modal-separator-backogrund-color);
|
||||
margin: 0;
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
[:& fm/input
|
||||
{:name :email
|
||||
:type "email"
|
||||
:label (tr "auth.email")
|
||||
:label (tr "auth.work-email")
|
||||
:class (stl/css :form-field)}]]
|
||||
|
||||
[:div {:class (stl/css :fields-row)}
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
:form form}
|
||||
[:div {:class (stl/css :fields-row)}
|
||||
[:& fm/input {:name :email
|
||||
:label (tr "auth.email")
|
||||
:label (tr "auth.work-email")
|
||||
:type "text"
|
||||
:class (stl/css :form-field)}]]
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
[:div {:class (stl/css :fields-row)}
|
||||
[:& fm/input {:type "text"
|
||||
:name :email
|
||||
:label (tr "auth.email")
|
||||
:label (tr "auth.work-email")
|
||||
:data-test "email-input"
|
||||
:show-success? true
|
||||
:class (stl/css :form-field)}]]
|
||||
|
@ -134,11 +134,11 @@
|
|||
(mf/defc register-page
|
||||
{::mf/props :obj}
|
||||
[{:keys [params]}]
|
||||
[:div {:class (stl/css :auth-form-wrapper)}
|
||||
[:div {:class (stl/css :auth-form-wrapper :register-form)}
|
||||
[:h1 {:class (stl/css :auth-title)
|
||||
:data-test "registration-title"} (tr "auth.register-title")]
|
||||
[:p {:class (stl/css :auth-tagline)}
|
||||
(tr "auth.login-tagline")]
|
||||
(tr "auth.register-tagline")]
|
||||
|
||||
(when (contains? cf/flags :demo-warning)
|
||||
[:& login/demo-warning])
|
||||
|
@ -229,11 +229,11 @@
|
|||
(mf/html
|
||||
[:& tr-html
|
||||
{:tag-name "div"
|
||||
:label "auth.terms-privacy-agreement-md"
|
||||
:label "auth.terms-and-privacy-agreement"
|
||||
: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"
|
||||
:class (stl/css :checkbox-terms-and-privacy)
|
||||
:type "checkbox"
|
||||
:default-checked false
|
||||
:label terms-label}]]))
|
||||
|
@ -247,11 +247,12 @@
|
|||
(mf/defc register-validate-page
|
||||
[{:keys [params]}]
|
||||
[:div {:class (stl/css :auth-form-wrapper)}
|
||||
[:h1 {:class (stl/css :auth-title)
|
||||
:data-test "register-title"} (tr "auth.register-title")]
|
||||
[:div {:class (stl/css :auth-subtitle)} (tr "auth.register-subtitle")]
|
||||
|
||||
[:hr {:class (stl/css :separator)}]
|
||||
[:h1 {:class (stl/css :logo-container)}
|
||||
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]]
|
||||
[:div {:class (stl/css :auth-title-wrapper)}
|
||||
[:h2 {:class (stl/css :auth-title)
|
||||
:data-test "register-title"} (tr "auth.register-account-title")]
|
||||
[:div {:class (stl/css :auth-subtitle)} (tr "auth.register-account-tagline")]]
|
||||
|
||||
[:& register-validate-form {:params params}]
|
||||
|
||||
|
@ -264,7 +265,11 @@
|
|||
(mf/defc register-success-page
|
||||
[{:keys [params]}]
|
||||
[:div {:class (stl/css :auth-form-wrapper :register-success)}
|
||||
[:div {:class (stl/css :notification-icon)} i/icon-verify]
|
||||
[:div {:class (stl/css :notification-text)} (tr "auth.verification-email-sent")]
|
||||
[:h1 {:class (stl/css :logo-container)}
|
||||
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} i/logo]]
|
||||
[:div {:class (stl/css :auth-title-wrapper)}
|
||||
[:h2 {:class (stl/css :auth-title)}
|
||||
(tr "auth.check-mail")]
|
||||
[: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")]])
|
||||
|
|
|
@ -8,15 +8,24 @@
|
|||
@use "./common.scss";
|
||||
|
||||
.accept-terms-and-privacy-wrapper {
|
||||
margin: $s-16 0;
|
||||
:global(a) {
|
||||
color: $df-secondary;
|
||||
font-weight: $fw700;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-terms-and-privacy {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.register-form {
|
||||
gap: $s-24;
|
||||
}
|
||||
|
||||
.register-success {
|
||||
padding-bottom: $s-32;
|
||||
gap: $s-24;
|
||||
.auth-title {
|
||||
@include medTitleTipography;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-icon {
|
||||
|
@ -30,9 +39,30 @@
|
|||
}
|
||||
}
|
||||
|
||||
.notification-text-email,
|
||||
.notification-text {
|
||||
font-size: $fs-16;
|
||||
color: var(--notification-foreground-color-default);
|
||||
margin-bottom: $s-16;
|
||||
@include bodyMediumTypography;
|
||||
color: var(--title-foreground-color);
|
||||
}
|
||||
|
||||
.notification-text-email {
|
||||
@include medTitleTipography;
|
||||
font-size: $fs-20;
|
||||
color: var(--register-confirmation-color);
|
||||
margin-inline: $s-36;
|
||||
}
|
||||
|
||||
.logo-btn {
|
||||
height: $s-40;
|
||||
svg {
|
||||
width: $s-120;
|
||||
height: $s-40;
|
||||
fill: var(--main-icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: $s-120;
|
||||
margin-block-end: $s-24;
|
||||
}
|
||||
|
|
|
@ -236,6 +236,7 @@
|
|||
.reply-form {
|
||||
textarea {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
line-height: 1.45;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
[:*
|
||||
(cond
|
||||
(some? label)
|
||||
[:label {:class (stl/css-case :input-with-label (not is-checkbox?)
|
||||
[:label {:class (stl/css-case :input-with-label-form (not is-checkbox?)
|
||||
:input-label is-text?
|
||||
:radio-label is-radio?
|
||||
:checkbox-label is-checkbox?)
|
||||
|
@ -214,7 +214,7 @@
|
|||
[:span {:class (stl/css :hint)} hint])]))
|
||||
|
||||
(mf/defc select
|
||||
[{:keys [options disabled form default dropdown-class] :as props
|
||||
[{:keys [options disabled form default dropdown-class select-class] :as props
|
||||
:or {default ""}}]
|
||||
(let [input-name (get props :name)
|
||||
form (or form (mf/use-ctx form-ctx))
|
||||
|
@ -230,6 +230,7 @@
|
|||
{:default-value value
|
||||
:disabled disabled
|
||||
:options options
|
||||
:class select-class
|
||||
:dropdown-class dropdown-class
|
||||
:on-change handle-change}]]))
|
||||
|
||||
|
@ -297,6 +298,70 @@
|
|||
:value value'
|
||||
:checked checked?}]]))]))
|
||||
|
||||
|
||||
(mf/defc image-radio-buttons
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [form (or (unchecked-get props "form")
|
||||
(mf/use-ctx form-ctx))
|
||||
name (unchecked-get props "name")
|
||||
image (unchecked-get props "image")
|
||||
img-height (unchecked-get props "img-height")
|
||||
img-width (unchecked-get props "img-width")
|
||||
current-value (or (dm/get-in @form [:data name] "")
|
||||
(unchecked-get props "value"))
|
||||
on-change (unchecked-get props "on-change")
|
||||
options (unchecked-get props "options")
|
||||
trim? (unchecked-get props "trim")
|
||||
class (unchecked-get props "class")
|
||||
encode-fn (d/nilv (unchecked-get props "encode-fn") identity)
|
||||
decode-fn (d/nilv (unchecked-get props "decode-fn") identity)
|
||||
|
||||
on-change'
|
||||
(mf/use-fn
|
||||
(mf/deps on-change form name)
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/get-value decode-fn)]
|
||||
(when (some? form)
|
||||
(swap! form assoc-in [:touched name] true)
|
||||
(fm/on-input-change form name value trim?))
|
||||
|
||||
(when (fn? on-change)
|
||||
(on-change name value)))))]
|
||||
[:div {:class (if image
|
||||
class
|
||||
(dm/str class " " (stl/css :custom-radio)))}
|
||||
(for [{:keys [image icon value label area]} options]
|
||||
(let [icon? (some? icon)
|
||||
value' (encode-fn value)
|
||||
checked? (= value current-value)
|
||||
key (str/ffmt "%-%" (d/name name) (d/name value'))]
|
||||
[:label {:for key
|
||||
:key key
|
||||
:style {:grid-area area}
|
||||
:class (stl/css-case :radio-label-image true
|
||||
:global/checked checked?)}
|
||||
(cond
|
||||
icon?
|
||||
[:span {:class (stl/css :icon-inside)
|
||||
:style {:height img-height
|
||||
:width img-width}} icon]
|
||||
|
||||
:else
|
||||
[:span {:style {:background-image (str/ffmt "url(%)" image)
|
||||
:height img-height
|
||||
:width img-width}
|
||||
:class (stl/css :image-inside)}])
|
||||
|
||||
[:span {:class (stl/css :image-text)} label]
|
||||
[:input {:on-change on-change'
|
||||
:type "radio"
|
||||
:class (stl/css :radio-input)
|
||||
:id key
|
||||
:name name
|
||||
:value value'
|
||||
:checked checked?}]]))]))
|
||||
|
||||
(mf/defc submit-button*
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [on-click children label form class name disabled] :as props}]
|
||||
|
|
|
@ -38,10 +38,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
.input-with-label {
|
||||
.input-with-label-form {
|
||||
@include flexColumn;
|
||||
gap: $s-8;
|
||||
@include bodySmallTypography;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
height: 100%;
|
||||
|
@ -55,6 +54,7 @@
|
|||
color: var(--input-foreground-color-active);
|
||||
margin-top: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 $s-8;
|
||||
|
||||
|
@ -64,6 +64,7 @@
|
|||
border-radius: $br-8;
|
||||
}
|
||||
}
|
||||
|
||||
// Input autofill
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
|
@ -169,6 +170,10 @@
|
|||
border-color: var(--input-checkbox-border-color-hover);
|
||||
}
|
||||
}
|
||||
a {
|
||||
// Need for terms and conditions links on register checkbox
|
||||
color: var(--link-foreground-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,7 +372,7 @@
|
|||
height: fit-content;
|
||||
border-radius: $br-8;
|
||||
padding: $s-8;
|
||||
color: var(--input-foreground-color);
|
||||
color: var(--input-foreground-color-rest);
|
||||
border: $s-1 solid transparent;
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
|
@ -393,14 +398,12 @@
|
|||
border-radius: $br-circle;
|
||||
}
|
||||
|
||||
.radio-label.with-image {
|
||||
.radio-label-image {
|
||||
@include smallTitleTipography;
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 0px;
|
||||
justify-items: center;
|
||||
gap: 0;
|
||||
height: $s-116;
|
||||
width: $s-92;
|
||||
border-radius: $br-8;
|
||||
margin: 0;
|
||||
border: 1px solid var(--color-background-tertiary);
|
||||
|
@ -413,22 +416,29 @@
|
|||
outline: none;
|
||||
border: $s-1 solid var(--input-border-color-active);
|
||||
}
|
||||
.image-text {
|
||||
color: var(--input-foreground-color-rest);
|
||||
display: grid;
|
||||
align-self: center;
|
||||
margin-bottom: $s-16;
|
||||
padding-inline: $s-8;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.image-inside {
|
||||
width: $s-60;
|
||||
height: $s-48;
|
||||
background-size: $s-48;
|
||||
margin: $s-16;
|
||||
background-size: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.icon-inside {
|
||||
width: $s-60;
|
||||
height: $s-48;
|
||||
margin: $s-16;
|
||||
@include flexCenter;
|
||||
svg {
|
||||
width: $s-60;
|
||||
height: $s-48;
|
||||
width: 40px;
|
||||
height: 60px;
|
||||
stroke: var(--icon-foreground);
|
||||
fill: none;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
.input-wrapper {
|
||||
@extend .input-with-label;
|
||||
@include bodySmallTypography;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
}
|
||||
.file-name-edit {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.file-name-label {
|
||||
|
|
|
@ -540,5 +540,6 @@
|
|||
|
||||
.email-input {
|
||||
@extend .input-base;
|
||||
@include bodySmallTypography;
|
||||
height: auto;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
.group-name-input {
|
||||
@extend .input-element-label;
|
||||
@include bodySmallTypography;
|
||||
margin-bottom: $s-8;
|
||||
label {
|
||||
@include flexColumn;
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
}
|
||||
.input-wrapper {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
;; 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
|
||||
|
||||
(ns app.main.ui.onboarding
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.config :as cf]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.onboarding.newsletter]
|
||||
[app.main.ui.onboarding.questions]
|
||||
[app.main.ui.onboarding.team-choice]
|
||||
[app.main.ui.onboarding.templates]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.timers :as tm]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
;; --- ONBOARDING LIGHTBOX
|
||||
|
||||
(defn send-event
|
||||
[event-name]
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name event-name
|
||||
::ev/origin "dashboard"})))
|
||||
|
||||
|
||||
(mf/defc onboarding-welcome
|
||||
[{:keys [next] :as props}]
|
||||
(let [go-next
|
||||
(fn []
|
||||
(send-event "onboarding-step1-continue")
|
||||
(next))]
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-left)}
|
||||
[:img {:src "images/welcomeilustration.svg"
|
||||
:border "0"
|
||||
:alt (tr "onboarding.welcome.alt")}]]
|
||||
[:div {:class (stl/css :modal-right)}
|
||||
[:div {:class (stl/css :release)}
|
||||
"Version " (:main cf/version)]
|
||||
[:h1 {:class (stl/css :modal-title)
|
||||
:data-test "onboarding-welcome"}
|
||||
(tr "onboarding-v2.welcome.title")]
|
||||
[:p {:class (stl/css :modal-text)}
|
||||
(tr "onboarding-v2.welcome.desc1")]
|
||||
|
||||
[:div {:class (stl/css :text-wrapper)}
|
||||
[:div {:class (stl/css :property-title)}
|
||||
[:a {:href "https://community.penpot.app/"
|
||||
:target "_blank"
|
||||
:on-click #(send-event "onboarding-community-link")}
|
||||
(tr "onboarding-v2.welcome.desc2.title")]]
|
||||
[:div {:class (stl/css :property-description)}
|
||||
(tr "onboarding-v2.welcome.desc2")]]
|
||||
|
||||
[:div {:class (stl/css :text-wrapper)}
|
||||
[:div {:class (stl/css :property-title)}
|
||||
[:a {:href "https://help.penpot.app/contributing-guide/"
|
||||
:target "_blank" :on-click #(send-event "onboarding-contributing-link")}
|
||||
(tr "onboarding-v2.welcome.desc3.title")]]
|
||||
[:div {:class (stl/css :property-description)}
|
||||
(tr "onboarding-v2.welcome.desc3")]]
|
||||
|
||||
[:button {:on-click go-next
|
||||
:class (stl/css :accept-btn)
|
||||
:data-test "onboarding-next-btn"}
|
||||
(tr "labels.continue")]]]))
|
||||
|
||||
(mf/defc onboarding-before-start
|
||||
[{:keys [next] :as props}]
|
||||
(let [go-next
|
||||
(fn []
|
||||
(send-event "onboarding-step2-continue")
|
||||
(next))]
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-left)}
|
||||
[:img {:src "images/beforeyoustartilustration.svg"
|
||||
:border "0"
|
||||
:alt (tr "onboarding.welcome.alt")}]]
|
||||
[:div {:class (stl/css :modal-right)}
|
||||
[:div {:class (stl/css :release)}
|
||||
"Version " (:main cf/version)]
|
||||
|
||||
[:h1 {:class (stl/css :modal-title)
|
||||
:data-test "onboarding-welcome"}
|
||||
(tr "onboarding-v2.before-start.title")]
|
||||
[:p {:class (stl/css :modal-text)}
|
||||
(tr "onboarding-v2.before-start.desc1")]
|
||||
|
||||
[:div {:class (stl/css :text-wrapper)}
|
||||
[:div {:class (stl/css :property-title)}
|
||||
[:a {:class (stl/css :modal-link)
|
||||
:href "https://help.penpot.app/user-guide/"
|
||||
:target "_blank"
|
||||
:on-click #(send-event "onboarding-user-guide-link")}
|
||||
(tr "onboarding-v2.before-start.desc2.title")]]
|
||||
[:div {:class (stl/css :property-description)}
|
||||
(tr "onboarding-v2.before-start.desc2")]]
|
||||
|
||||
[:div {:class (stl/css :text-wrapper)}
|
||||
[:div {:class (stl/css :property-title)}
|
||||
[:a {:class (stl/css :modal-link)
|
||||
:href "https://www.youtube.com/c/Penpot"
|
||||
:target "_blank"
|
||||
:on-click #(send-event "onboarding-video-tutorials-link")}
|
||||
(tr "onboarding-v2.before-start.desc3.title")]]
|
||||
[:div {:class (stl/css :property-description)}
|
||||
(tr "onboarding-v2.before-start.desc3")]]
|
||||
|
||||
|
||||
[:button {:on-click go-next
|
||||
:class (stl/css :accept-btn)
|
||||
:data-test "onboarding-next-btn"}
|
||||
(tr "labels.continue")]]]))
|
||||
|
||||
(mf/defc onboarding-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :onboarding}
|
||||
[_]
|
||||
(let [slide (mf/use-state :start)
|
||||
klass (mf/use-state "fadeInDown")
|
||||
|
||||
navigate
|
||||
(mf/use-fn #(reset! slide %))
|
||||
|
||||
skip
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(st/emit! (modal/hide)
|
||||
(du/mark-onboarding-as-viewed))
|
||||
(cond
|
||||
(contains? cf/flags :onboarding-questions)
|
||||
(modal/show! {:type :onboarding-questions})
|
||||
|
||||
(contains? cf/flags :onboarding-newsletter)
|
||||
(modal/show! {:type :onboarding-newsletter})
|
||||
|
||||
(contains? cf/flags :onboarding-team)
|
||||
(modal/show! {:type :onboarding-team}))))
|
||||
|
||||
onboarding-a-b-test? (cf/external-feature-flag "signup-background" "test")]
|
||||
|
||||
(mf/with-effect [@slide]
|
||||
(when (not= :start @slide)
|
||||
(reset! klass "fadeIn"))
|
||||
(let [sem (tm/schedule 300 #(reset! klass nil))]
|
||||
(fn []
|
||||
(reset! klass nil)
|
||||
(tm/dispose! sem))))
|
||||
[:div {:class (stl/css-case :modal-overlay true
|
||||
:onboarding-a-b-test onboarding-a-b-test?)}
|
||||
[:div.animated {:class (dm/str @klass " " (stl/css :animated))}
|
||||
(case @slide
|
||||
:start [:& onboarding-welcome {:next #(navigate :opensource)}]
|
||||
:opensource [:& onboarding-before-start {:next skip}])]]))
|
|
@ -1,86 +0,0 @@
|
|||
// 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
|
||||
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.modal-overlay {
|
||||
@extend .modal-overlay-base;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
@extend .modal-container-base;
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
gap: $s-32;
|
||||
padding-inline: $s-100;
|
||||
padding-block-start: $s-100;
|
||||
padding-block-end: $s-72;
|
||||
margin: 0;
|
||||
width: $s-960;
|
||||
height: $s-632;
|
||||
max-width: $s-960;
|
||||
max-height: $s-632;
|
||||
}
|
||||
|
||||
.modal-left {
|
||||
width: $s-240;
|
||||
margin-block-end: $s-64;
|
||||
img {
|
||||
width: $s-240;
|
||||
height: 100%;
|
||||
border-radius: $br-8 0 0 $br-8;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-right {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: $s-40 auto auto auto $s-32;
|
||||
gap: $s-24;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.release {
|
||||
@include bodySmallTypography;
|
||||
position: absolute;
|
||||
top: calc(-1 * $s-28);
|
||||
right: 0;
|
||||
padding: $s-8;
|
||||
color: var(--modal-text-foreground-color);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include bigTitleTipography;
|
||||
color: var(--modal-title-foreground-color);
|
||||
}
|
||||
|
||||
.modal-text,
|
||||
.property-description {
|
||||
@include bodyLargeTypography;
|
||||
margin: 0;
|
||||
color: var(--modal-text-foreground-color);
|
||||
}
|
||||
|
||||
.modal-link {
|
||||
@include bodyLargeTypography;
|
||||
color: var(--modal-link-foreground-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.text-wrapper {
|
||||
@include flexColumn;
|
||||
}
|
||||
|
||||
.property-title a {
|
||||
@include medTitleTipography;
|
||||
color: var(--modal-title-foreground-color);
|
||||
}
|
||||
|
||||
.accept-btn {
|
||||
@extend .modal-accept-btn;
|
||||
justify-self: flex-end;
|
||||
}
|
|
@ -25,7 +25,7 @@
|
|||
[{:keys [form step on-next on-prev children class] :as props}]
|
||||
|
||||
[:& fm/form {:form form :on-submit on-next :class (dm/str class " " (stl/css :form-wrapper))}
|
||||
[:div {:class (stl/css :paginator)} (str/ffmt "%/4" step)]
|
||||
[:div {:class (stl/css :paginator)} (str/ffmt "%/5" step)]
|
||||
|
||||
children
|
||||
|
||||
|
@ -36,107 +36,86 @@
|
|||
:on-click on-prev} (tr "questions.previous")])
|
||||
|
||||
[:> fm/submit-button*
|
||||
{:label (if (< step 4) (tr "questions.next") (tr "questions.start"))
|
||||
{:label (if (< step 5) (tr "questions.next") (tr "questions.start"))
|
||||
:class (stl/css :next-button)}]]])
|
||||
|
||||
(s/def ::questions-form-step-1
|
||||
(s/keys :req-un [::planning]))
|
||||
|
||||
(mf/defc step-1
|
||||
[{:keys [on-next form] :as props}]
|
||||
[:& step-container {:form form :step 1 :on-next on-next :class (stl/css :step-1)}
|
||||
[:img {:class (stl/css :header-image)
|
||||
:src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}]
|
||||
[:h1 {:class (stl/css :modal-title)} (tr "questions.lets-get-started")]
|
||||
[:p {:class (stl/css :modal-text)} (tr "questions.your-feedback-will-help-us")]
|
||||
|
||||
[:div {:class (stl/css :modal-question)}
|
||||
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.questions-how-are-you-planning-to-use-penpot")]
|
||||
[:& fm/select
|
||||
{:options [{:label (tr "questions.select-option")
|
||||
:value "" :key "questions-how-are-you-planning-to-use-penpot"
|
||||
:disabled true}
|
||||
{:label (tr "questions.discover-more-about-penpot")
|
||||
:value "discover-more-about-penpot"
|
||||
:key "discover-more-about-penpot"}
|
||||
{:label (tr "questions.test-penpot-to-see-if-its-a-fit-for-team")
|
||||
:value "test-penpot-to-see-if-its-a-fit-for-team"
|
||||
:key "test-penpot-to-see-if-its-a-fit-for-team"}
|
||||
{:label (tr "questions.start-to-work-on-my-project")
|
||||
:value "start-to-work-on-my-project"
|
||||
:key "start-to-work-on-my-project"}
|
||||
{:label (tr "questions.get-the-code-from-my-team-project")
|
||||
:value "get-the-code-from-my-team-project"
|
||||
:key "get-the-code-from-my-team-project"}
|
||||
{:label (tr "questions.leave-feedback-for-my-team-project")
|
||||
:value "leave-feedback-for-my-team-project"
|
||||
:key "leave-feedback-for-my-team-project"}
|
||||
{:label (tr "questions.work-in-concept-ideas")
|
||||
:value "work-in-concept-ideas"
|
||||
:key "work-in-concept-ideas"}
|
||||
{:label (tr "questions.try-out-before-using-penpot-on-premise")
|
||||
:value "try-out-before-using-penpot-on-premise"
|
||||
:key "try-out-before-using-penpot-on-premise"}]
|
||||
:default ""
|
||||
:name :planning
|
||||
:dropdown-class (stl/css :question-dropdown)}]]])
|
||||
|
||||
(s/def ::questions-form-step-2
|
||||
(s/keys :req-un [::experience-branding-illustrations-marketing-pieces
|
||||
::experience-interface-design-visual-assets-design-systems
|
||||
::experience-interface-wireframes-user-journeys-flows-navigation-trees]))
|
||||
|
||||
(mf/defc step-2
|
||||
[{:keys [on-next on-prev form] :as props}]
|
||||
[:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev :class (stl/css :step-2)}
|
||||
[:h1 {:class (stl/css :modal-title)}
|
||||
(tr "questions.describe-your-experience-working-on")]
|
||||
|
||||
[:div {:class (stl/css-case :modal-question true
|
||||
:question-centered true)}
|
||||
[:div {:class (stl/css-case :modal-subtitle true
|
||||
:centered true)}
|
||||
(tr "branding-illustrations-marketing-pieces")]
|
||||
[:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"}
|
||||
{:label (tr "questions.some") :value "some"}
|
||||
{:label (tr "questions.a-lot") :value "a-lot"}]
|
||||
:name :experience-branding-illustrations-marketing-pieces
|
||||
:class (stl/css :radio-btns)}]]
|
||||
|
||||
[:div {:class (stl/css-case :modal-question true
|
||||
:question-centered true)}
|
||||
[:div {:class (stl/css-case :modal-subtitle true
|
||||
:centered true)}
|
||||
(tr "questions.interface-design-visual-assets-design-systems")]
|
||||
[:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"}
|
||||
{:label (tr "questions.some") :value "some"}
|
||||
{:label (tr "questions.a-lot") :value "a-lot"}]
|
||||
:name :experience-interface-design-visual-assets-design-systems
|
||||
:class (stl/css :radio-btns)}]]
|
||||
|
||||
[:div {:class (stl/css-case :modal-question true
|
||||
:question-centered true)}
|
||||
[:div {:class (stl/css-case :modal-subtitle true
|
||||
:centered true)}
|
||||
(tr "questions.wireframes-user-journeys-flows-navigation-trees")]
|
||||
[:& fm/radio-buttons {:options [{:label (tr "questions.none") :value "none"}
|
||||
{:label (tr "questions.some") :value "some"}
|
||||
{:label (tr "questions.a-lot") :value "a-lot"}]
|
||||
:name :experience-interface-wireframes-user-journeys-flows-navigation-trees
|
||||
:class (stl/css :radio-btns)}]]])
|
||||
|
||||
(s/def ::questions-form-step-3
|
||||
(s/keys :req-un [::experience-design-tool]
|
||||
:opt-un [::experience-design-tool-other]))
|
||||
(s/keys :req-un [::planning
|
||||
::penpot-use]
|
||||
:opt-un [::planning-other]))
|
||||
|
||||
(defn- step-1-form-validator
|
||||
[errors data]
|
||||
(let [planning (-> (:planning data) (str/trim))]
|
||||
(let [planning (-> (:planning data) (str/trim))
|
||||
planning-other (-> (:planning-other data) str/trim)]
|
||||
|
||||
(cond-> errors
|
||||
(and (= planning-other "other") (= 0 (count planning-other)))
|
||||
(assoc :planning-other {:code "missing"})
|
||||
|
||||
(= planning "")
|
||||
(assoc :planning {:code "missing"}))))
|
||||
|
||||
(defn- step-3-form-validator
|
||||
(mf/defc step-1
|
||||
[{:keys [on-next form] :as props}]
|
||||
(let [use-ops-randomized (mf/with-memo [] (shuffle [{:label (tr "questions.use-work") :value "use-work"}
|
||||
{:label (tr "questions.use-education") :value "use-education"}
|
||||
{:label (tr "questions.use-personal") :value "use-personal"}]))
|
||||
|
||||
planning-ops (mf/with-memo [] (shuffle [{:label (tr "questions.select-option")
|
||||
:value "" :key "questions-what-brings-you-here"
|
||||
:disabled true}
|
||||
{:label (tr "questions.reasons.exploring")
|
||||
:value "discover-more-about-penpot"
|
||||
:key "discover-more-about-penpot"}
|
||||
{:label (tr "questions.reasons.fit")
|
||||
:value "test-penpot-to-see-if-its-a-fit-for-team"
|
||||
:key "test-penpot-to-see-if-its-a-fit-for-team"}
|
||||
{:label (tr "questions.reasons.alternative")
|
||||
:value "alternative-to-figma"
|
||||
:key "alternative-to-figma"}
|
||||
{:label (tr "questions.reasons.testing")
|
||||
:value "try-out-before-using-penpot-on-premise"
|
||||
:key "try-out-before-using-penpot-on-premise"}]))
|
||||
|
||||
planning-ops-randomized (conj planning-ops {:label (tr "questions.other-short") :value "other"})
|
||||
|
||||
planning (dm/get-in @form [:data :planning])]
|
||||
|
||||
|
||||
[:& step-container {:form form :step 1 :on-next on-next :class (stl/css :step-1)}
|
||||
[:img {:class (stl/css :header-image)
|
||||
:src "images/form/use-for-1.png"
|
||||
:alt (tr "questions.lets-get-started")}]
|
||||
[:h1 {:class (stl/css :modal-title)}
|
||||
(tr "questions.step1-title")]
|
||||
[:p {:class (stl/css :modal-text)}
|
||||
(tr "questions.step1-subtitle")]
|
||||
|
||||
[:div {:class (stl/css :modal-question)}
|
||||
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.step1-question1")]
|
||||
[:& fm/radio-buttons {:options use-ops-randomized
|
||||
:name :penpot-use
|
||||
:class (stl/css :radio-btns)}]
|
||||
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.step1-question2")]
|
||||
[:& fm/select
|
||||
{:options planning-ops-randomized
|
||||
:select-class (stl/css :select-class)
|
||||
:default ""
|
||||
:name :planning
|
||||
:dropdown-class (stl/css :question-dropdown)}]
|
||||
|
||||
(when (= planning "other")
|
||||
[:& fm/input {:name :planning-other
|
||||
:class (stl/css :input-spacing)
|
||||
:placeholder (tr "questions.other")
|
||||
:label ""}])]]))
|
||||
|
||||
(s/def ::questions-form-step-2
|
||||
(s/keys :req-un [::experience-design-tool]
|
||||
:opt-un [::experience-design-tool-other]))
|
||||
|
||||
(defn- step-2-form-validator
|
||||
[errors data]
|
||||
(let [experience-design-tool (:experience-design-tool data)
|
||||
experience-design-tool-other (-> (:experience-design-tool-other data) str/trim)]
|
||||
|
@ -144,9 +123,17 @@
|
|||
(and (= experience-design-tool "other") (= 0 (count experience-design-tool-other)))
|
||||
(assoc :experience-design-tool-other {:code "missing"}))))
|
||||
|
||||
(mf/defc step-3
|
||||
(mf/defc step-2
|
||||
[{:keys [on-next on-prev form] :as props}]
|
||||
(let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])
|
||||
(let [design-tool-options (mf/with-memo [] (shuffle [{:label (tr "questions.figma") :img-width "48px" :img-height "60px" :value "figma" :image "images/form/figma.png"}
|
||||
{:label (tr "questions.sketch") :img-width "48px" :img-height "60px" :value "sketch" :image "images/form/sketch.png"}
|
||||
{:label (tr "questions.adobe-xd") :img-width "48px" :img-height "60px" :value "adobe-xd" :image "images/form/adobe-xd.png"}
|
||||
{:label (tr "questions.canva") :img-width "48px" :img-height "60px" :value "canva" :image "images/form/canva.png"}
|
||||
{:label (tr "questions.invision") :img-width "48px" :img-height "60px" :value "invision" :image "images/form/invision.png"}]))
|
||||
|
||||
design-tool-options-randomized (conj design-tool-options {:label (tr "questions.other-short") :value "other" :icon i/curve})
|
||||
|
||||
experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])
|
||||
on-design-tool-change
|
||||
(fn [_ _]
|
||||
(let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])]
|
||||
|
@ -155,71 +142,98 @@
|
|||
(swap! form d/dissoc-in [:data :experience-design-tool-other])
|
||||
(swap! form d/dissoc-in [:errors :experience-design-tool-other])))))]
|
||||
|
||||
[:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev :class (stl/css :step-3)}
|
||||
[:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev :class (stl/css :step-2)}
|
||||
[:h1 {:class (stl/css :modal-title)}
|
||||
(tr "question.design-tool-more-experienced-with")]
|
||||
(tr "question.design-tool-more-used")]
|
||||
[:div {:class (stl/css :radio-wrapper)}
|
||||
[:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png" :area "image1"}
|
||||
{:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png" :area "image2"}
|
||||
{:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png" :area "image3"}
|
||||
{:label (tr "questions.canva") :value "canva" :image "images/form/canva.png" :area "image4"}
|
||||
{:label (tr "questions.invision") :value "invision" :image "images/form/invision.png" :area "image5"}
|
||||
{:label (tr "questions.never-used-one") :area "image6" :value "never-used-a-tool" :icon i/curve}
|
||||
{:label (tr "questions.other") :value "other" :area "other"}]
|
||||
:name :experience-design-tool
|
||||
:image true
|
||||
:class (stl/css :image-radio)
|
||||
:on-change on-design-tool-change}]
|
||||
[:& fm/image-radio-buttons {:options design-tool-options-randomized
|
||||
:img-width "48px"
|
||||
:img-height "60px"
|
||||
:name :experience-design-tool
|
||||
:image true
|
||||
:class (stl/css :image-radio)
|
||||
:on-change on-design-tool-change}]
|
||||
|
||||
[:& fm/input {:name :experience-design-tool-other
|
||||
:class (stl/css :input-spacing)
|
||||
:placeholder (tr "questions.other")
|
||||
:label ""
|
||||
:disabled (not= experience-design-tool "other")}]]]))
|
||||
(when (= experience-design-tool "other")
|
||||
[:& fm/input {:name :experience-design-tool-other
|
||||
:class (stl/css :input-spacing)
|
||||
:placeholder (tr "questions.other")
|
||||
:label ""}])]]))
|
||||
|
||||
(s/def ::questions-form-step-4
|
||||
(s/keys :req-un [::team-size ::role]
|
||||
:opt-un [::role-other]))
|
||||
(s/def ::questions-form-step-3
|
||||
(s/keys :req-un [::team-size ::role ::responsability]
|
||||
:opt-un [::role-other ::responsability-other]))
|
||||
|
||||
(defn- step-4-form-validator
|
||||
(defn- step-3-form-validator
|
||||
[errors data]
|
||||
(let [role (:role data)
|
||||
role-other (-> (:role-other data) str/trim)]
|
||||
role-other (-> (:role-other data) str/trim)
|
||||
|
||||
responsability (:responsability data)
|
||||
responsability-other (-> (:responsability-other data) str/trim)]
|
||||
(cond-> errors
|
||||
(and (= role "other") (= 0 (count role-other)))
|
||||
(assoc :role-other {:code "missing"}))))
|
||||
(assoc :role-other {:code "missing"})
|
||||
|
||||
(mf/defc step-4
|
||||
(and (= responsability "other") (= 0 (count responsability-other)))
|
||||
(assoc :responsability-other {:code "missing"}))))
|
||||
|
||||
(mf/defc step-3
|
||||
[{:keys [on-next on-prev form] :as props}]
|
||||
(let [role (dm/get-in @form [:data :role])
|
||||
on-role-change
|
||||
(fn [_ _]
|
||||
(let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])]
|
||||
(when (not= experience-design-tool "other")
|
||||
(do
|
||||
(swap! form d/dissoc-in [:data :role-other])
|
||||
(swap! form d/dissoc-in [:errors :role-other])))))]
|
||||
(let [role-ops (mf/with-memo [] (shuffle [{:label (tr "questions.select-option") :value "" :key "role" :disabled true}
|
||||
{:label (tr "questions.work-type.ux") :value "designer" :key "designer"}
|
||||
{:label (tr "questions.work-type.dev") :value "developer" :key "developer"}
|
||||
{:label (tr "questions.work-type.student") :value "student-teacher" :key "student"}
|
||||
{:label (tr "questions.work-type.graphic") :value "graphic-design" :key "design"}
|
||||
{:label (tr "questions.work-type.marketing") :value "marketing" :key "marketing"}
|
||||
{:label (tr "questions.work-type.product") :value "manager" :key "manager"}]))
|
||||
role-ops-randomized (conj role-ops {:label (tr "questions.other-short") :value "other"})
|
||||
|
||||
[:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev :class (stl/css :step-4)}
|
||||
[:h1 {:class (stl/css :modal-title)} (tr "questions.role")]
|
||||
[:div {:class (stl/css :radio-wrapper)}
|
||||
[:& fm/radio-buttons {:options [{:label (tr "questions.designer") :value "designer"}
|
||||
{:label (tr "questions.developer") :value "developer"}
|
||||
{:label (tr "questions.manager") :value "manager"}
|
||||
{:label (tr "questions.founder") :value "founder"}
|
||||
{:label (tr "questions.marketing") :value "marketing"}
|
||||
{:label (tr "questions.student-teacher") :value "student-teacher"}
|
||||
{:label (tr "questions.other") :value "other"}]
|
||||
:name :role
|
||||
:on-change on-role-change}]
|
||||
[:& fm/input {:name :role-other
|
||||
:class (stl/css :input-spacing)
|
||||
:label ""
|
||||
:placeholder (tr "questions.other")
|
||||
:disabled (not= role "other")}]]
|
||||
responsability-options (mf/with-memo [] (shuffle [{:label (tr "questions.select-option") :value "" :key "responsability" :disabled true}
|
||||
{:label (tr "questions.role.team-leader") :value "team-leader"}
|
||||
{:label (tr "questions.role.team-member") :value "team-member"}
|
||||
{:label (tr "questions.role.freelancer") :value "freelancer"}
|
||||
{:label (tr "questions.role.founder") :value "ceo-founder"}
|
||||
{:label (tr "questions.role.director") :value "director"}
|
||||
{:label (tr "questions.student-teacher") :value "student-teacher"}]))
|
||||
|
||||
responsability-options-randomized (conj responsability-options {:label (tr "questions.other-short") :value "other"})
|
||||
|
||||
|
||||
role (dm/get-in @form [:data :role])
|
||||
|
||||
responsability (dm/get-in @form [:data :responsability])]
|
||||
|
||||
[:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev :class (stl/css :step-3)}
|
||||
[:h1 {:class (stl/css :modal-title)} (tr "questions.step3-title")]
|
||||
[:div {:class (stl/css :modal-question)}
|
||||
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.step3.question1")]
|
||||
[:& fm/select {:options role-ops-randomized
|
||||
:select-class (stl/css :select-class)
|
||||
:default ""
|
||||
:name :role}]
|
||||
|
||||
(when (= role "other")
|
||||
[:& fm/input {:name :role-other
|
||||
:class (stl/css :input-spacing)
|
||||
:placeholder (tr "questions.other")
|
||||
:label ""}])]
|
||||
|
||||
[:div {:class (stl/css :modal-question)}
|
||||
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.team-size")]
|
||||
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.step3.question2")]
|
||||
[:& fm/select {:options responsability-options-randomized
|
||||
:select-class (stl/css :select-class)
|
||||
:default ""
|
||||
:name :responsability}]
|
||||
|
||||
(when (= responsability "other")
|
||||
[:& fm/input {:name :responsability-other
|
||||
:class (stl/css :input-spacing)
|
||||
:placeholder (tr "questions.other")
|
||||
:label ""}])]
|
||||
|
||||
[:div {:class (stl/css :modal-question)}
|
||||
[:h3 {:class (stl/css :modal-subtitle)} (tr "questions.company-size")]
|
||||
[:& fm/select {:options [{:label (tr "questions.select-option") :value "" :key "team-size" :disabled true}
|
||||
{:label (tr "questions.more-than-50") :value "more-than-50" :key "more-than-50"}
|
||||
{:label (tr "questions.31-50") :value "31-50" :key "31-50"}
|
||||
|
@ -228,10 +242,102 @@
|
|||
{:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"}
|
||||
{:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}]
|
||||
:default ""
|
||||
:select-class (stl/css :select-class)
|
||||
:name :team-size}]]]))
|
||||
|
||||
;; NOTE: we don't register it on registry modal because we reference
|
||||
;; this modal directly on the ui namespace.
|
||||
(s/def ::questions-form-step-4
|
||||
(s/keys :req-un [::start]
|
||||
:opt-un [::start-other]))
|
||||
|
||||
(defn- step-4-form-validator
|
||||
[errors data]
|
||||
(let [start (:start data)
|
||||
start-other (-> (:start-other data) str/trim)]
|
||||
(cond-> errors
|
||||
(and (= start "other") (= 0 (count start-other)))
|
||||
(assoc :start-other {:code "missing"}))))
|
||||
|
||||
(mf/defc step-4
|
||||
[{:keys [on-next on-prev form] :as props}]
|
||||
(let [start-options (mf/with-memo [] (shuffle [{:label (tr "questions.starting-ui") :value "ui" :image "images/form/Design.png"}
|
||||
{:label (tr "questions.starting-wireframing") :value "wireframing" :image "images/form/templates.png"}
|
||||
{:label (tr "questions.starting-prototyping") :value "prototyping" :image "images/form/Prototype.png"}
|
||||
{:label (tr "questions.starting-ds") :value "ds" :image "images/form/components.png"}
|
||||
{:label (tr "questions.starting-code") :value "code" :image "images/form/design-and-dev.png"}]))
|
||||
|
||||
start-options-randomized (conj start-options {:label (tr "questions.other-short") :value "other" :icon i/curve})
|
||||
|
||||
|
||||
start (dm/get-in @form [:data :start])
|
||||
|
||||
on-start-change
|
||||
(fn [_ _]
|
||||
(let [start (dm/get-in @form [:clean-data :start])]
|
||||
(when (not= start "other")
|
||||
(do
|
||||
(swap! form d/dissoc-in [:data :start-other])
|
||||
(swap! form d/dissoc-in [:errors :start-other])))))]
|
||||
|
||||
[:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev :class (stl/css :step-4)}
|
||||
[:h1 {:class (stl/css :modal-title)} (tr "questions.step4-title")]
|
||||
[:div {:class (stl/css :radio-wrapper)}
|
||||
[:& fm/image-radio-buttons {:options start-options-randomized
|
||||
:img-width "159px"
|
||||
:img-height "120px"
|
||||
:class (stl/css :image-radio)
|
||||
:name :start
|
||||
:on-change on-start-change}]
|
||||
|
||||
(when (= start "other")
|
||||
[:& fm/input {:name :start-other
|
||||
:class (stl/css :input-spacing)
|
||||
:label ""
|
||||
:placeholder (tr "questions.other")
|
||||
:disabled (not= start "other")}])]]))
|
||||
|
||||
(s/def ::questions-form-step-5
|
||||
(s/keys :req-un [::knowledge]
|
||||
:opt-un [::knowledge-other]))
|
||||
|
||||
(defn- step-5-form-validator
|
||||
[errors data]
|
||||
(let [knowledge (:knowledge data)
|
||||
knowledge-other (-> (:knowledge-other data) str/trim)]
|
||||
(cond-> errors
|
||||
(and (= knowledge "other") (= 0 (count knowledge-other)))
|
||||
(assoc :knowledge-other {:code "missing"}))))
|
||||
|
||||
(mf/defc step-5
|
||||
[{:keys [on-next on-prev form] :as props}]
|
||||
(let [knowledge-options (mf/with-memo [] (shuffle [{:label (tr "questions.knowledge.youtube") :value "Youtube"}
|
||||
{:label (tr "questions.knowledge.event") :value "event"}
|
||||
{:label (tr "questions.knowledge.search") :value "search"}
|
||||
{:label (tr "questions.knowledge.social") :value "social"}
|
||||
{:label (tr "questions.knowledge.article") :value "article"}]))
|
||||
knowledge-options-randomized (conj knowledge-options {:label (tr "questions.other-short") :value "other"})
|
||||
|
||||
knowledge (dm/get-in @form [:data :knowledge])
|
||||
on-knowledge-change
|
||||
(fn [_ _]
|
||||
(let [experience-design-tool (dm/get-in @form [:clean-data :experience-design-tool])]
|
||||
(when (not= experience-design-tool "other")
|
||||
(do
|
||||
(swap! form d/dissoc-in [:data :knowledge-other])
|
||||
(swap! form d/dissoc-in [:errors :knowledge-other])))))]
|
||||
|
||||
[:& step-container {:form form :step 5 :on-next on-next :on-prev on-prev :class (stl/css :step-5)}
|
||||
[:h1 {:class (stl/css :modal-title)} (tr "questions.step5-title")]
|
||||
[:div {:class (stl/css :radio-wrapper)}
|
||||
[:& fm/radio-buttons {:options knowledge-options-randomized
|
||||
:class (stl/css :radio-btns)
|
||||
:name :knowledge
|
||||
:on-change on-knowledge-change}]
|
||||
(when (= knowledge "other")
|
||||
[:& fm/input {:name :knowledge-other
|
||||
:class (stl/css :input-spacing)
|
||||
:label ""
|
||||
:placeholder (tr "questions.other")
|
||||
:disabled (not= knowledge "other")}])]]))
|
||||
|
||||
(mf/defc questions-modal
|
||||
{::mf/register modal/components
|
||||
|
@ -247,9 +353,12 @@
|
|||
:initial {}
|
||||
:validators [step-1-form-validator]
|
||||
:spec ::questions-form-step-1)
|
||||
|
||||
step-2-form (fm/use-form
|
||||
:initial {}
|
||||
:validators [step-2-form-validator]
|
||||
:spec ::questions-form-step-2)
|
||||
|
||||
step-3-form (fm/use-form
|
||||
:initial {}
|
||||
:validators [step-3-form-validator]
|
||||
|
@ -260,6 +369,11 @@
|
|||
:validators [step-4-form-validator]
|
||||
:spec ::questions-form-step-4)
|
||||
|
||||
step-5-form (fm/use-form
|
||||
:initial {}
|
||||
:validators [step-5-form-validator]
|
||||
:spec ::questions-form-step-5)
|
||||
|
||||
on-next
|
||||
(mf/use-fn
|
||||
(fn [form]
|
||||
|
@ -298,4 +412,5 @@
|
|||
1 [:& step-1 {:on-next on-next :on-prev on-prev :form step-1-form}]
|
||||
2 [:& step-2 {:on-next on-next :on-prev on-prev :form step-2-form}]
|
||||
3 [:& step-3 {:on-next on-next :on-prev on-prev :form step-3-form}]
|
||||
4 [:& step-4 {:on-next on-submit :on-prev on-prev :form step-4-form}])]]))
|
||||
4 [:& step-4 {:on-next on-next :on-prev on-prev :form step-4-form}]
|
||||
5 [:& step-5 {:on-next on-submit :on-prev on-prev :form step-5-form}])]]))
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
}
|
||||
|
||||
.modal-container {
|
||||
max-width: $s-744;
|
||||
max-width: $s-960;
|
||||
max-height: fit-content;
|
||||
width: $s-744;
|
||||
width: fit-content;
|
||||
padding-inline: $s-100;
|
||||
padding-block-start: $s-40;
|
||||
padding-block-end: $s-72;
|
||||
|
@ -47,15 +47,15 @@
|
|||
@extend .modal-cancel-btn;
|
||||
}
|
||||
|
||||
.radio-btns label,
|
||||
.select-class span {
|
||||
@include bodyMediumTypography;
|
||||
}
|
||||
|
||||
// STEP 1
|
||||
|
||||
// .step-1 {
|
||||
// max-height: $s-468;
|
||||
// height: $s-468;
|
||||
// }
|
||||
|
||||
.header-image {
|
||||
height: $s-112;
|
||||
height: $s-60;
|
||||
width: auto;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
@ -81,9 +81,15 @@
|
|||
}
|
||||
|
||||
// STEP-2
|
||||
|
||||
.step-1,
|
||||
.step-2,
|
||||
.step-3,
|
||||
.step-5 {
|
||||
max-width: $s-540;
|
||||
width: $s-540;
|
||||
}
|
||||
.step-2 {
|
||||
grid-template-rows: $s-20 auto auto auto auto $s-32;
|
||||
grid-template-rows: $s-20 auto auto $s-32;
|
||||
}
|
||||
|
||||
.modal-question {
|
||||
|
@ -103,36 +109,36 @@
|
|||
.radio-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: $s-8;
|
||||
gap: $s-16;
|
||||
}
|
||||
|
||||
// STEP-3
|
||||
.step-3 {
|
||||
grid-template-rows: $s-20 auto auto $s-32;
|
||||
grid-template-rows: $s-20 auto auto auto auto $s-32;
|
||||
}
|
||||
|
||||
.image-radio {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr $s-32;
|
||||
grid-template-columns: $s-88 $s-92 $s-92 $s-92 $s-88;
|
||||
grid-template-areas:
|
||||
". image1 image2 image3 ."
|
||||
". image4 image5 image6 ."
|
||||
"other other other other other";
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-template-columns: $s-92 $s-92 $s-92;
|
||||
row-gap: $s-16;
|
||||
column-gap: $s-24;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.input-spacing {
|
||||
height: $s-32;
|
||||
width: calc(100% - $s-24);
|
||||
margin-inline-start: $s-24;
|
||||
width: 100%;
|
||||
margin-block-end: $s-8;
|
||||
}
|
||||
|
||||
.input-spacing input {
|
||||
@include bodyMediumTypography;
|
||||
}
|
||||
|
||||
// STEP-4
|
||||
|
||||
.step-4 {
|
||||
grid-template-rows: $s-20 auto auto auto $s-32;
|
||||
grid-template-rows: $s-20 auto auto $s-32;
|
||||
row-gap: $s-16;
|
||||
}
|
||||
|
|
|
@ -160,6 +160,7 @@
|
|||
|
||||
.custom-input-token {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
margin: 0;
|
||||
flex-grow: 1;
|
||||
&:focus {
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
|
||||
.suffix-input {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
|
||||
.input-text {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
color: var(--input-foreground-color-active);
|
||||
padding-left: $s-8;
|
||||
margin: 0;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
@include flexRow;
|
||||
.input-wrapper {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-84;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
@ -26,6 +27,7 @@
|
|||
@include flexRow;
|
||||
.input-wrapper {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-84;
|
||||
&.hex {
|
||||
width: $s-172;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
.input-wrapper {
|
||||
@extend .input-with-label;
|
||||
@include bodySmallTypography;
|
||||
label {
|
||||
text-transform: none;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
}
|
||||
.input-wrapper {
|
||||
@extend .input-with-label;
|
||||
@include bodySmallTypography;
|
||||
margin-bottom: $s-8;
|
||||
}
|
||||
.action-buttons {
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
|
||||
.second-row {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-92;
|
||||
.label {
|
||||
padding-left: $s-8;
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
.suffix-input {
|
||||
grid-column: span 3;
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
}
|
||||
|
||||
.export-btn {
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
border-radius: 0 $br-8 $br-8 0;
|
||||
.numeric-input {
|
||||
@extend .input-base;
|
||||
@include bodySmallTypography;
|
||||
}
|
||||
}
|
||||
.editable-select-wrapper {
|
||||
|
@ -93,6 +94,7 @@
|
|||
border: $s-1 solid var(--input-border-color);
|
||||
.numeric-input {
|
||||
@extend .input-base;
|
||||
@include bodySmallTypography;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -196,6 +198,7 @@
|
|||
}
|
||||
.height {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
.icon-text {
|
||||
padding-top: $s-1;
|
||||
|
@ -204,6 +207,7 @@
|
|||
.gutter,
|
||||
.margin {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
.icon {
|
||||
&.rotated svg {
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
.area-input {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: 100%;
|
||||
padding: $s-8;
|
||||
}
|
||||
|
@ -51,6 +52,7 @@
|
|||
|
||||
.coord-input {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
border-radius: 0 $br-8 $br-8 0;
|
||||
border-left: $s-1 solid var(--panel-background-color);
|
||||
}
|
||||
|
|
|
@ -143,6 +143,7 @@
|
|||
}
|
||||
.input-element-wrapper {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
grid-area: content;
|
||||
}
|
||||
.buttons-wrapper {
|
||||
|
@ -319,6 +320,7 @@
|
|||
|
||||
.flow-input {
|
||||
@extend .input-base;
|
||||
@include bodySmallTypography;
|
||||
background-color: transparent;
|
||||
height: $s-28;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
}
|
||||
.input {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-60;
|
||||
}
|
||||
.actions {
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
gap: $s-4;
|
||||
.column-gap {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
&.disabled {
|
||||
@extend .disabled-input;
|
||||
|
@ -91,6 +92,7 @@
|
|||
}
|
||||
.row-gap {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
&.disabled {
|
||||
@extend .disabled-input;
|
||||
|
@ -113,6 +115,7 @@
|
|||
|
||||
.padding-simple {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
max-width: $s-108;
|
||||
}
|
||||
}
|
||||
|
@ -124,6 +127,7 @@
|
|||
|
||||
.padding-multiple {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
max-width: $s-108;
|
||||
}
|
||||
}
|
||||
|
@ -239,6 +243,7 @@
|
|||
|
||||
.track-info-value {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
border-radius: 0;
|
||||
border-right: $s-1 solid var(--panel-background-color);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
|
||||
.z-index-wrapper {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-60;
|
||||
}
|
||||
|
||||
|
@ -94,6 +95,7 @@
|
|||
.vertical-margin,
|
||||
.horizontal-margin {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +111,7 @@
|
|||
.left-margin,
|
||||
.right-margin {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
}
|
||||
|
||||
|
@ -127,6 +130,7 @@
|
|||
.layout-item-max-w,
|
||||
.layout-item-max-h {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
.icon-text {
|
||||
justify-content: flex-start;
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
.height,
|
||||
.width {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
.icon-text {
|
||||
padding-top: $s-1;
|
||||
|
@ -145,6 +146,7 @@
|
|||
.x-position,
|
||||
.y-position {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
.icon-text {
|
||||
padding-top: $s-1;
|
||||
|
@ -163,6 +165,7 @@
|
|||
|
||||
.rotation {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
.icon-text {
|
||||
padding-top: $s-1;
|
||||
|
@ -181,6 +184,7 @@
|
|||
|
||||
.radius-1 {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-108;
|
||||
}
|
||||
|
||||
|
@ -190,6 +194,7 @@
|
|||
gap: $s-4;
|
||||
.small-input {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-52;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
.spread-input,
|
||||
.offset-y-input {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-60;
|
||||
min-width: $s-60;
|
||||
align-items: baseline;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
.attr-input {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-124;
|
||||
}
|
||||
|
||||
|
|
|
@ -308,6 +308,7 @@
|
|||
.line-height,
|
||||
.letter-spacing {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
.icon {
|
||||
@include flexCenter;
|
||||
width: $s-28;
|
||||
|
@ -339,6 +340,7 @@
|
|||
padding: $s-8;
|
||||
.numeric-input {
|
||||
@extend .input-base;
|
||||
@include bodySmallTypography;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
.color-name-wrapper {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
|
@ -166,6 +167,7 @@
|
|||
|
||||
.opacity-element-wrapper {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-60;
|
||||
border-radius: 0 $br-8 $br-8 0;
|
||||
.opacity-input {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
@include flexRow;
|
||||
.stroke-width-input-element {
|
||||
@extend .input-element;
|
||||
@include bodySmallTypography;
|
||||
width: $s-60;
|
||||
}
|
||||
.select-wrapper {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue