🎉 Move survey to local resources

This commit is contained in:
Alejandro Alonso 2023-05-16 10:46:50 +02:00 committed by Andrey Antukh
parent b6d60773e3
commit a1c09057c1
10 changed files with 704 additions and 385 deletions

View file

@ -87,7 +87,6 @@
(def worker-uri (obj/get global "penpotWorkerURI" "/js/worker.js"))
(def translations (obj/get global "penpotTranslations"))
(def themes (obj/get global "penpotThemes"))
(def onboarding-form-id (obj/get global "penpotOnboardingQuestionsFormId"))
(def build-date (parse-build-date global))
(def flags (atom (parse-flags global)))

View file

@ -384,7 +384,7 @@
(rx/map (constantly (fetch-profile)))))))))
(defn mark-questions-as-answered
[]
[onboarding-questions]
(ptk/reify ::mark-questions-as-answered
ptk/UpdateEvent
(update [_ state]
@ -392,7 +392,8 @@
ptk/WatchEvent
(watch [_ _ _]
(let [props {:onboarding-questions-answered true}]
(let [props {:onboarding-questions-answered true
:onboarding-questions onboarding-questions}]
(->> (rp/cmd! :update-profile-props {:props props})
(rx/map (constantly (fetch-profile))))))))

View file

@ -90,12 +90,9 @@
#_[:& app.main.ui.onboarding/onboarding-team-modal]]
(when-let [props (some-> profile (get :props {}))]
(cond
(and cf/onboarding-form-id
(not (:onboarding-questions-answered props false))
(and (not (:onboarding-questions-answered props false))
(not (:onboarding-viewed props false)))
[:& app.main.ui.onboarding.questions/questions
{:profile profile
:form-id cf/onboarding-form-id}]
[:& app.main.ui.onboarding.questions/questions]
(not (:onboarding-viewed props))
[:& app.main.ui.onboarding/onboarding-modal {}]

View file

@ -230,6 +230,30 @@
[:div.icon
i/arrow-slide]]]))
(mf/defc radio-buttons
[{:keys [name options form trim] :as props}]
(let [form (or form (mf/use-ctx form-ctx))
value (get-in @form [:data name] "")
on-change (fn [event]
(let [value (-> event dom/get-target dom/get-value)]
(swap! form assoc-in [:touched name] true)
(fm/on-input-change form name value trim)))]
[:div.custom-radio
(for [item options]
(let [id (str/ffmt "%-%" name (:value item))
image (:image item)]
[:div.input-radio {:key id :class (when image "with-image")}
[:input {:on-change on-change
:type "radio"
:id id
:name name
:value (:value item)
:checked (= value (:value item))}]
[:label {:for id
:style {:background-image (when image (str/ffmt "url(%)" image))}
:class (when image "with-image")}
(:label item)]]))]))
(mf/defc submit-button
[{:keys [label form on-click disabled data-test] :as props}]
(let [form (or form (mf/use-ctx form-ctx))]

View file

@ -7,69 +7,175 @@
(ns app.main.ui.onboarding.questions
"External form for onboarding questions."
(:require
[app.main.data.events :as ev]
[app.main.data.users :as du]
[app.main.store :as st]
[app.util.dom :as dom]
[goog.events :as gev]
[potok.core :as ptk]
[promesa.core :as p]
[app.main.ui.components.forms :as fm]
[app.util.i18n :as i18n :refer [tr]]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(defn load-arengu-sdk
[container-ref email form-id]
(letfn [(on-arengu-loaded [resolve reject]
(let [container (mf/ref-val container-ref)]
(-> (.embed js/ArenguForms form-id container)
(p/then (fn [form]
(.setHiddenField ^js form "email" email)
(st/emit! (ptk/event ::ev/event {::ev/name "arengu-form-load-success"
::ev/origin "onboarding-questions"
::ev/type "fact"}))
(resolve)))
(p/catch reject))))
(mf/defc step-container
[{:keys [form step on-next on-prev children] :as props}]
[:& fm/form {:form form :on-submit on-next}
[:div.step-header
[:div.step-number (str/ffmt "%/4" step)]]
(mark-as-answered []
(st/emit! (du/mark-questions-as-answered)))
children
(initialize [cleaners resolve reject]
(let [script (dom/create-element "script")
head (unchecked-get js/document "head")
lkey1 (gev/listen js/document "af-submitForm-success" mark-as-answered)
lkey2 (gev/listen js/document "af-getForm-error" reject)]
[:div.step-next
[:& fm/submit-button
{:label (if (< step 4) (tr "questions.next") (tr "questions.start"))
:class "step-next"}]]
(unchecked-set script "src" "https://sdk.arengu.com/forms.js")
(unchecked-set script "onload" (partial on-arengu-loaded resolve reject))
(dom/append-child! head script)
(when on-prev
[:div.step-prev
[:button {:on-click on-prev} (tr "questions.previous")]])])
(swap! cleaners conj
#(do (gev/unlistenByKey lkey1)
(gev/unlistenByKey lkey2)))
(s/def ::questions-form-step-1
(s/keys :req-un [::planning]))
(swap! cleaners conj
#(dom/remove-child! head script))))
(mf/defc step-1
[{:keys [on-next] :as props}]
(on-error [_]
(st/emit! (ptk/event ::ev/event {::ev/name "arengu-form-load-error"
::ev/origin "onboarding-questions"
::ev/type "fact"}))
(mark-as-answered))]
(let [form (fm/use-form
:initial {}
:spec ::questions-form-step-1)]
[:& step-container {:form form :step 1 :on-next on-next}
[:img.header-image {:src "images/form/use-for-1.png" :alt (tr "questions.lets-get-started")}]
[:h1 (tr "questions.lets-get-started")]
[:p.intro (tr "questions.your-feedback-will-help-us")]
[:h3 (tr "questions-how-are-you-planning-to-use-penpot")]
[:& fm/select {:options [{: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"}]
:label (tr "questions.select-option")
:default nil
:name :planning}]]))
(let [cleaners (atom #{})]
(-> (p/create (partial initialize cleaners))
(p/timeout 5000)
(p/catch on-error))
(fn []
(run! (fn [clean-fn] (clean-fn)) @cleaners)))))
(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] :as props}]
(let [form (fm/use-form
:initial {}
:spec ::questions-form-step-2)]
[:& step-container {:form form :step 2 :on-next on-next :on-prev on-prev}
[:h3 (tr "questions.describe-your-experience-working-on")]
[:div.section (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}]
[:div.section (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}]
[:div.section (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}]]))
(s/def ::questions-form-step-3
(s/keys :req-un [::experience-design-tool]
:opt-un[::experience-design-tool-other]))
(mf/defc step-3
[{:keys [on-next on-prev] :as props}]
(let [form (fm/use-form
:initial {}
:spec ::questions-form-step-3)]
[:& step-container {:form form :step 3 :on-next on-next :on-prev on-prev}
[:h3 (tr "question.design-tool-more-experienced-with")]
[:& fm/radio-buttons {:options [{:label (tr "questions.figma") :value "figma" :image "images/form/figma.png"}
{:label (tr "questions.sketch") :value "sketch" :image "images/form/sketch.png"}
{:label (tr "questions.adobe-xd") :value "adobe-xd" :image "images/form/adobe-xd.png"}
{:label (tr "questions.canva") :value "canva" :image "images/form/canva.png"}
{:label (tr "questions.invision") :value "invision" :image "images/form/invision.png"}
{:label (tr "questions.never-used-a-tool") :value "never-used-a-tool" :image "images/form/never-used.png"}
{:label (tr "questions.other") :value "other"}]
:name :experience-design-tool}]
[:div.other
[:label (tr "questions.other")]
[:& fm/input {:name :experience-design-tool-other :label (tr "questions.other")}]]]))
(s/def ::questions-form-step-4
(s/keys :req-un [::team-size ::role]
:opt-un [::role-other]))
(mf/defc step-4
[{:keys [on-next on-prev] :as props}]
(let [form (fm/use-form
:initial {}
:spec ::questions-form-step-4)]
[:& step-container {:form form :step 4 :on-next on-next :on-prev on-prev}
[:h3 (tr "questions.role")]
[:& 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}]
[:div.other
[:label (tr "questions.other")]
[:& fm/input {:name :role-other :label (tr "questions.other")}]]
[:h3 (tr "questions.team-size")]
[:& fm/select {:options [{: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"}
{:label (tr "questions.11-30") :value "11-30" :key "11-30"}
{:label (tr "questions.2-10") :value "2-10" :key "2-10"}
{:label (tr "questions.freelancer") :value "freelancer" :key "freelancer"}
{:label (tr "questions.personal-project") :value "personal-project" :key "personal-project"}]
:label (tr "questions.select-option")
:default nil
:name :team-size}]]))
(mf/defc questions
[{:keys [profile form-id]}]
(let [container (mf/use-ref)]
(mf/use-effect (partial load-arengu-sdk container (:email profile) form-id))
[{:keys []}]
(let [container (mf/use-ref)
step (mf/use-state 1)
clean-data (mf/use-state {})
on-next
(mf/use-fn
(fn [form]
(swap! step inc)
(swap! clean-data merge (:clean-data @form))))
on-prev
(mf/use-fn
(fn []
(swap! step dec)))
on-submit
(mf/use-fn
(mf/deps @clean-data)
(fn [form]
(let [questionnaire (merge @clean-data (:clean-data @form))]
(reset! clean-data questionnaire)
(st/emit! (du/mark-questions-as-answered questionnaire)))))]
[:div.modal-wrapper.questions-form
[:div.modal-overlay
[:div.modal-container.onboarding.onboarding-v2 {:ref container}
[:img.deco.left {:src "images/deco-left.png" :border 0}]
[:img.deco.right {:src "images/deco-right.png" :border 0}]]]]))
[:img.deco.right {:src "images/deco-right.png" :border 0}]
[:div.signup-questions
(case @step
1 [:& step-1 {:on-next on-next :on-prev on-prev}]
2 [:& step-2 {:on-next on-next :on-prev on-prev}]
3 [:& step-3 {:on-next on-next :on-prev on-prev}]
4 [:& step-4 {:on-next on-submit :on-prev on-prev}])]]]]))