0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-01 11:59:17 -05:00

🎉 Add user feedback module.

This commit is contained in:
Andrey Antukh 2021-02-08 22:39:11 +01:00 committed by Hirunatan
parent 1cb18ad7cb
commit c1a139fc51
20 changed files with 431 additions and 59 deletions

View file

@ -1,5 +1,6 @@
{:lint-as {potok.core/reify clojure.core/reify {:lint-as {potok.core/reify clojure.core/reify
promesa.core/let clojure.core/let promesa.core/let clojure.core/let
rumext.alpha/defc clojure.core/defn
app.db/with-atomic clojure.core/with-open} app.db/with-atomic clojure.core/with-open}
:output :output
{:exclude-files ["data_readers.clj"]} {:exclude-files ["data_readers.clj"]}

View file

@ -0,0 +1 @@
[FEEDBACK]: From {{ profile.email }}

View file

@ -0,0 +1,7 @@
Feedback from: {{profile.fullname}} <{{profile.email}}>
Profile ID: {{profile.id}}
Subject: {{subject}}
{{content}}

View file

@ -40,6 +40,9 @@
:storage-s3-region :eu-central-1 :storage-s3-region :eu-central-1
:storage-s3-bucket "penpot-devenv-assets-pre" :storage-s3-bucket "penpot-devenv-assets-pre"
:feedback-destination "info@example.com"
:feedback-enabled false
:assets-path "/internal/assets/" :assets-path "/internal/assets/"
:rlimits-password 10 :rlimits-password 10
@ -93,7 +96,11 @@
(s/def ::media-directory ::us/string) (s/def ::media-directory ::us/string)
(s/def ::asserts-enabled ::us/boolean) (s/def ::asserts-enabled ::us/boolean)
(s/def ::feedback-enabled ::us/boolean)
(s/def ::feedback-destination ::us/string)
(s/def ::error-report-webhook ::us/string) (s/def ::error-report-webhook ::us/string)
(s/def ::smtp-enabled ::us/boolean) (s/def ::smtp-enabled ::us/boolean)
(s/def ::smtp-default-reply-to ::us/string) (s/def ::smtp-default-reply-to ::us/string)
(s/def ::smtp-default-from ::us/string) (s/def ::smtp-default-from ::us/string)
@ -156,6 +163,8 @@
::database-username ::database-username
::default-blob-version ::default-blob-version
::error-report-webhook ::error-report-webhook
::feedback-enabled
::feedback-destination
::github-client-id ::github-client-id
::github-client-secret ::github-client-secret
::gitlab-base-uri ::gitlab-base-uri

View file

@ -43,6 +43,16 @@
;; --- Emails ;; --- Emails
(s/def ::subject ::us/string)
(s/def ::content ::us/string)
(s/def ::feedback
(s/keys :req-un [::subject ::content]))
(def feedback
"A profile feedback email."
(emails/template-factory ::feedback default-context))
(s/def ::name ::us/string) (s/def ::name ::us/string)
(s/def ::register (s/def ::register
(s/keys :req-un [::name])) (s/keys :req-un [::name]))

View file

@ -126,6 +126,7 @@
'app.rpc.mutations.projects 'app.rpc.mutations.projects
'app.rpc.mutations.viewer 'app.rpc.mutations.viewer
'app.rpc.mutations.teams 'app.rpc.mutations.teams
'app.rpc.mutations.feedback
'app.rpc.mutations.verify-token) 'app.rpc.mutations.verify-token)
(map (partial process-method cfg)) (map (partial process-method cfg))
(into {})))) (into {}))))

View file

@ -0,0 +1,41 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2021 UXBOX Labs SL
(ns app.rpc.mutations.feedback
(:require
[app.common.exceptions :as ex]
[app.common.spec :as us]
[app.config :as cfg]
[app.db :as db]
[app.emails :as emails]
[app.rpc.queries.profile :as profile]
[app.util.services :as sv]
[clojure.spec.alpha :as s]))
(s/def ::subject ::us/string)
(s/def ::content ::us/string)
(s/def ::send-profile-feedback
(s/keys :req-un [::profile-id ::subject ::content]))
(sv/defmethod ::send-profile-feedback
[{:keys [pool] :as cfg} {:keys [profile-id subject content] :as params}]
(when-not (:feedback-enabled cfg/config)
(ex/raise :type :validation
:code :feedback-disabled
:hint "feedback module is disabled"))
(db/with-atomic [conn pool]
(let [profile (profile/retrieve-profile-data conn profile-id)]
(emails/send! conn emails/feedback
{:to (:feedback-destination cfg/config)
:profile profile
:subject subject
:content content})
nil)))

View file

@ -9,6 +9,7 @@
(ns app.util.emails (ns app.util.emails
(:require (:require
[app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.spec :as us] [app.common.spec :as us]
[app.util.template :as tmpl] [app.util.template :as tmpl]
@ -196,15 +197,17 @@
text (render-email-template-part :txt id context) text (render-email-template-part :txt id context)
html (render-email-template-part :html id context)] html (render-email-template-part :html id context)]
(when (or (not subj) (when (or (not subj)
(not text) (and (not text)
(not html)) (not html)))
(ex/raise :type :internal (ex/raise :type :internal
:code :missing-email-templates)) :code :missing-email-templates))
{:subject subj {:subject subj
:body [{:type "text/plain" :body (d/concat
:content text} [{:type "text/plain"
{:type "text/html" :content text}]
:content html}]})) (when html
[{:type "text/html"
:content html}]))}))
(s/def ::priority #{:high :low}) (s/def ::priority #{:high :low})
(s/def ::to (s/or :sigle ::us/email (s/def ::to (s/or :sigle ::us/email

View file

@ -974,6 +974,78 @@
"es" : "La contraseña anterior no es correcta" "es" : "La contraseña anterior no es correcta"
} }
}, },
"feedback.chat-start" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:112" ],
"translations" : {
"en" : "Join the chat",
"es" : "Unirse al chat"
}
},
"feedback.chat-subtitle" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:109" ],
"translations" : {
"en" : "Feeling like talking? Chat with us at Gitter",
"es" : "¿Deseas conversar? Entra al nuestro chat de la comunidad en Gitter"
}
},
"feedback.description" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:88" ],
"translations" : {
"en" : "Description",
"es" : "Descripción"
}
},
"feedback.discussions-go-to" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:104" ],
"translations" : {
"en" : "Go to discussions",
"es" : "Ir a las discussiones"
}
},
"feedback.discussions-subtitle1" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:99" ],
"translations" : {
"en" : "Join Penpot team collaborative communication forum.",
"es" : "Entra al foro colaborativo de Penpot"
}
},
"feedback.discussions-subtitle2" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:100" ],
"translations" : {
"en" : "You can ask and answer questions, have open-ended conversations, and follow along on decisions affecting the project.",
"es" : ""
}
},
"feedback.discussions-title" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:98" ],
"translations" : {
"en" : "Team discussions",
"es" : ""
}
},
"feedback.subject" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:84" ],
"translations" : {
"en" : "Subject",
"es" : "Asunto"
}
},
"feedback.subtitle" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:81" ],
"translations" : {
"en" : "Please describe the reason of your email, specifying if is an issue, an idea or a doubt. A member of our team will respond as soon as possible.",
"es" : ""
}
},
"feedback.title" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:80" ],
"translations" : {
"en" : "Email",
"fr" : "Adresse email",
"ru" : "Email",
"es" : "Correo electrónico"
}
},
"generic.error" : { "generic.error" : {
"used-in" : [ "src/app/main/ui/settings/password.cljs:31" ], "used-in" : [ "src/app/main/ui/settings/password.cljs:31" ],
"translations" : { "translations" : {
@ -1605,7 +1677,7 @@
"es" : "Correo electrónico" "es" : "Correo electrónico"
} }
}, },
"labels.feedback" : { "labels.give-feedback" : {
"used-in" : [ "src/app/main/ui/workspace/header.cljs:231", "src/app/main/ui/dashboard/sidebar.cljs:471" ], "used-in" : [ "src/app/main/ui/workspace/header.cljs:231", "src/app/main/ui/dashboard/sidebar.cljs:471" ],
"translations" : { "translations" : {
"en" : "Give feedback", "en" : "Give feedback",
@ -1823,6 +1895,37 @@
"es" : "Cargo" "es" : "Cargo"
} }
}, },
"labels.send" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:93" ],
"translations" : {
"en" : "Send",
"es" : "Enviar"
}
},
"labels.sending" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs:93" ],
"translations" : {
"en" : "Sending...",
"es" : "Enviando..."
}
},
"labels.feedback-disabled" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs" ],
"translations" : {
"en" : "Feedback disabled",
"es" : "El modulo de recepción de opiniones esta deshabilitado."
}
},
"labels.feedback-sent" : {
"used-in" : [ "src/app/main/ui/settings/feedback.cljs" ],
"translations" : {
"en" : "Feedback sent",
"es" : "Opinión enviada"
}
},
"labels.service-unavailable.desc-message" : { "labels.service-unavailable.desc-message" : {
"used-in" : [ "src/app/main/ui/static.cljs:75" ], "used-in" : [ "src/app/main/ui/static.cljs:75" ],
"translations" : { "translations" : {

View file

@ -20,6 +20,7 @@
min-width: 25px; min-width: 25px;
padding: 0 1rem; padding: 0 1rem;
transition: all .4s; transition: all .4s;
text-decoration: none !important;
svg { svg {
height: 15px; height: 15px;
width: 15px; width: 15px;

View file

@ -102,6 +102,14 @@ textarea {
text-decoration: underline; text-decoration: underline;
} }
p {
color: $color-gray-60;
}
hr {
border-color: $color-gray-20;
}
.links { .links {
display: flex; display: flex;
font-size: $fs14; font-size: $fs14;
@ -131,7 +139,8 @@ textarea {
flex-direction: column; flex-direction: column;
position: relative; position: relative;
input { input,
textarea {
background-color: $color-white; background-color: $color-white;
border-radius: 2px; border-radius: 2px;
border: 1px solid $color-gray-20; border: 1px solid $color-gray-20;
@ -143,6 +152,13 @@ textarea {
width: 100%; width: 100%;
} }
textarea {
height: auto;
font-size: $fs14;
font-family: "worksans", sans-serif;
padding-top: 20px;
}
// Makes the background for autocomplete white // Makes the background for autocomplete white
input:-webkit-autofill, input:-webkit-autofill,
input:-webkit-autofill:hover, input:-webkit-autofill:hover,

View file

@ -67,6 +67,7 @@
(def default-language "en") (def default-language "en")
(def demo-warning (obj/get global "penpotDemoWarning" false)) (def demo-warning (obj/get global "penpotDemoWarning" false))
(def feedback-enabled (obj/get global "penpotFeedbackEnabled" false))
(def allow-demo-users (obj/get global "penpotAllowDemoUsers" true)) (def allow-demo-users (obj/get global "penpotAllowDemoUsers" true))
(def google-client-id (obj/get global "penpotGoogleClientID" nil)) (def google-client-id (obj/get global "penpotGoogleClientID" nil))
(def gitlab-client-id (obj/get global "penpotGitlabClientID" nil)) (def gitlab-client-id (obj/get global "penpotGitlabClientID" nil))

View file

@ -59,17 +59,18 @@
(def routes (def routes
[["/auth" [["/auth"
["/login" :auth-login] ["/login" :auth-login]
["/register" :auth-register] ["/register" :auth-register]
["/register/success" :auth-register-success] ["/register/success" :auth-register-success]
["/recovery/request" :auth-recovery-request] ["/recovery/request" :auth-recovery-request]
["/recovery" :auth-recovery] ["/recovery" :auth-recovery]
["/verify-token" :auth-verify-token]] ["/verify-token" :auth-verify-token]]
["/settings" ["/settings"
["/profile" :settings-profile] ["/profile" :settings-profile]
["/password" :settings-password] ["/password" :settings-password]
["/options" :settings-options]] ["/feedback" :settings-feedback]
["/options" :settings-options]]
["/view/:file-id/:page-id" ["/view/:file-id/:page-id"
{:name :viewer {:name :viewer
@ -89,11 +90,11 @@
["/render-object/:file-id/:page-id/:object-id" :render-object] ["/render-object/:file-id/:page-id/:object-id" :render-object]
["/dashboard/team/:team-id" ["/dashboard/team/:team-id"
["/members" :dashboard-team-members] ["/members" :dashboard-team-members]
["/settings" :dashboard-team-settings] ["/settings" :dashboard-team-settings]
["/projects" :dashboard-projects] ["/projects" :dashboard-projects]
["/search" :dashboard-search] ["/search" :dashboard-search]
["/libraries" :dashboard-libraries] ["/libraries" :dashboard-libraries]
["/projects/:project-id" :dashboard-files]] ["/projects/:project-id" :dashboard-files]]
["/workspace/:project-id/:file-id" :workspace]]) ["/workspace/:project-id/:file-id" :workspace]])
@ -121,7 +122,8 @@
(:settings-profile (:settings-profile
:settings-password :settings-password
:settings-options) :settings-options
:settings-feedback)
[:& settings/settings {:route route}] [:& settings/settings {:route route}]
:debug-icons-preview :debug-icons-preview

View file

@ -15,7 +15,7 @@
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.forms :as fm] [app.util.forms :as fm]
[app.util.i18n :as i18n :refer [t]] [app.util.i18n :as i18n :refer [t tr]]
["react" :as react] ["react" :as react]
[app.util.dom :as dom])) [app.util.dom :as dom]))
@ -28,7 +28,6 @@
type' (mf/use-state type) type' (mf/use-state type)
focus? (mf/use-state false) focus? (mf/use-state false)
locale (mf/deref i18n/locale)
touched? (get-in @form [:touched name]) touched? (get-in @form [:touched name])
error (get-in @form [:errors name]) error (get-in @form [:errors name])
@ -94,7 +93,59 @@
help-icon']) help-icon'])
(cond (cond
(and touched? (:message error)) (and touched? (:message error))
[:span.error (t locale (:message error))] [:span.error (tr (:message error))]
(string? hint)
[:span.hint hint])]]))
(mf/defc textarea
[{:keys [label disabled name form hint trim] :as props}]
(let [form (or form (mf/use-ctx form-ctx))
type' (mf/use-state type)
focus? (mf/use-state false)
touched? (get-in @form [:touched name])
error (get-in @form [:errors name])
value (get-in @form [:data name] "")
klass (dom/classnames
:focus @focus?
:valid (and touched? (not error))
:invalid (and touched? error)
:disabled disabled
;; :empty (str/empty? value)
)
on-focus #(reset! focus? true)
on-change (fm/on-input-change form name trim)
on-blur
(fn [event]
(reset! focus? false)
(when-not (get-in @form [:touched name])
(swap! form assoc-in [:touched name] true)))
props (-> props
(dissoc :help-icon :form :trim)
(assoc :value value
:on-focus on-focus
:on-blur on-blur
;; :placeholder label
:on-change on-change
:type @type')
(obj/clj->props))]
[:div.custom-input
{:class klass}
[:*
[:label label]
[:> :textarea props]
(cond
(and touched? (:message error))
[:span.error (tr (:message error))]
(string? hint) (string? hint)
[:span.hint hint])]])) [:span.hint hint])]]))

View file

@ -466,10 +466,12 @@
[:li {:on-click (partial on-click (da/logout))} [:li {:on-click (partial on-click (da/logout))}
[:span.icon i/exit] [:span.icon i/exit]
[:span.text (t locale "labels.logout")]] [:span.text (t locale "labels.logout")]]
[:li.feedback {:on-click #(.open js/window "https://github.com/penpot/penpot/discussions" "_blank")}
[:span.icon i/msg-info] (when cfg/feedback-enabled
[:span.text (t locale "labels.feedback")] [:li.feedback {:on-click (partial on-click :settings-feedback)}
[:span.primary-badge "ALPHA"]]]]] [:span.icon i/msg-info]
[:span.text (t locale "labels.give-feedback")]
[:span.primary-badge "ALPHA"]])]]]
(when (and team profile) (when (and team profile)
[:& comments-section {:profile profile [:& comments-section {:profile profile

View file

@ -5,30 +5,31 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0. ;; defined by the Mozilla Public License, v. 2.0.
;; ;;
;; Copyright (c) 2020 UXBOX Labs SL ;; Copyright (c) 2020-2021 UXBOX Labs SL
(ns app.main.ui.settings (ns app.main.ui.settings
(:require (:require
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.settings.options :refer [options-page]] [app.main.ui.settings.options :refer [options-page]]
[app.main.ui.settings.feedback :refer [feedback-page]]
[app.main.ui.settings.password :refer [password-page]] [app.main.ui.settings.password :refer [password-page]]
[app.main.ui.settings.profile :refer [profile-page]] [app.main.ui.settings.profile :refer [profile-page]]
[app.main.ui.settings.sidebar :refer [sidebar]] [app.main.ui.settings.sidebar :refer [sidebar]]
[app.main.ui.settings.change-email] [app.main.ui.settings.change-email]
[app.main.ui.settings.delete-account] [app.main.ui.settings.delete-account]
[app.util.i18n :as i18n :refer [t]] [app.util.i18n :as i18n :refer [tr]]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(mf/defc header (mf/defc header
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [locale] :as props}] []
(let [logout (constantly nil)] (let [logout (constantly nil)]
[:header.dashboard-header [:header.dashboard-header
[:div.dashboard-title [:div.dashboard-title
[:h1 (t locale "dashboard.your-account-title")]] [:h1 (tr "dashboard.your-account-title")]]
[:a.btn-secondary.btn-small {:on-click logout} [:a.btn-secondary.btn-small {:on-click logout}
(t locale "labels.logout")]])) (tr "labels.logout")]]))
(mf/defc settings (mf/defc settings
[{:keys [route] :as props}] [{:keys [route] :as props}]
@ -41,12 +42,15 @@
:section section}] :section section}]
[:div.dashboard-content [:div.dashboard-content
[:& header {:locale locale}] [:& header]
[:section.dashboard-container [:section.dashboard-container
(case section (case section
:settings-profile :settings-profile
[:& profile-page {:locale locale}] [:& profile-page {:locale locale}]
:settings-feedback
[:& feedback-page]
:settings-password :settings-password
[:& password-page {:locale locale}] [:& password-page {:locale locale}]

View file

@ -0,0 +1,120 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.settings.feedback
"Feedback form."
(:require
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.users :as du]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.forms :as fm]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[beicon.core :as rx]
[app.main.repo :as rp]
[cljs.spec.alpha :as s]
[rumext.alpha :as mf]))
(s/def ::content ::us/not-empty-string)
(s/def ::subject ::us/not-empty-string)
(s/def ::feedback-form
(s/keys :req-un [::subject ::content]))
(defn- on-error
[form error]
(st/emit! (dm/error (tr "errors.generic"))))
(defn- on-success
[form]
(st/emit! (dm/success (tr "notifications.profile-saved"))))
(mf/defc options-form
[]
(let [profile (mf/deref refs/profile)
form (fm/use-form :spec ::feedback-form)
loading (mf/use-state false)
on-succes
(mf/use-callback
(mf/deps profile)
(fn [event]
(st/emit! (dm/success (tr "labels.feedback-sent")))
(swap! form assoc :data {} :touched {} :errors {})))
on-error
(mf/use-callback
(mf/deps profile)
(fn [{:keys [code] :as error}]
(reset! loading false)
(if (= code :feedbck-disabled)
(st/emit! (dm/error (tr "labels.feedback-disabled")))
(st/emit! (dm/error (tr "errors.generic"))))))
on-submit
(mf/use-callback
(mf/deps profile)
(fn [form event]
(reset! loading true)
(let [data (:clean-data @form)]
(prn "on-submit" data)
(->> (rp/mutation! :send-profile-feedback data)
(rx/subs on-succes on-error #(reset! loading false))))))]
[:& fm/form {:class "feedback-form"
:on-submit on-submit
:form form}
;; --- Feedback section
[:h2 (tr "feedback.title")]
[:p (tr "feedback.subtitle")]
[:div.fields-row
[:& fm/input {:label (tr "feedback.subject")
:name :subject}]]
[:div.fields-row
[:& fm/textarea
{:label (tr "feedback.description")
:name :content
:rows 5}]]
[:& fm/submit-button
{:label (if @loading (tr "labels.sending") (tr "labels.send"))
:disabled @loading}]
[:hr]
[:h2 (tr "feedback.discussions-title")]
[:p (tr "feedback.discussions-subtitle1")]
[:p (tr "feedback.discussions-subtitle2")]
[:a.btn-secondary.btn-large
{:href "https://github.com/penpot/penpot/discussions" :target "_blank"}
(tr "feedback.discussions-go-to")]
[:hr]
[:h2 "Gitter"]
[:p (tr "feedback.chat-subtitle")]
[:a.btn-secondary.btn-large
{:href "https://gitter.im/penpot/community" :target "_blank"}
(tr "feedback.chat-start")]
]))
(mf/defc feedback-page
[]
[:div.dashboard-settings
[:div.form-container
[:& options-form]]])

View file

@ -9,31 +9,20 @@
(ns app.main.ui.settings.sidebar (ns app.main.ui.settings.sidebar
(:require (:require
[app.common.spec :as us] [app.config :as cfg]
[app.main.data.auth :as da]
[app.main.data.messages :as dm]
[app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.components.forms :as fm]
[app.main.ui.dashboard.sidebar :refer [profile-section]] [app.main.ui.dashboard.sidebar :refer [profile-section]]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [t tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.object :as obj]
[app.util.router :as rt] [app.util.router :as rt]
[app.util.time :as dt]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[goog.functions :as f]
[okulary.core :as l]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(mf/defc sidebar-content (mf/defc sidebar-content
[{:keys [locale profile section] :as props}] [{:keys [profile section] :as props}]
(let [profile? (= section :settings-profile) (let [profile? (= section :settings-profile)
password? (= section :settings-password) password? (= section :settings-password)
options? (= section :settings-options) options? (= section :settings-options)
feedback? (= section :settings-feedback)
go-dashboard go-dashboard
(mf/use-callback (mf/use-callback
@ -45,6 +34,11 @@
(mf/deps profile) (mf/deps profile)
(st/emitf (rt/nav :settings-profile))) (st/emitf (rt/nav :settings-profile)))
go-settings-feedback
(mf/use-callback
(mf/deps profile)
(st/emitf (rt/nav :settings-feedback)))
go-settings-password go-settings-password
(mf/use-callback (mf/use-callback
(mf/deps profile) (mf/deps profile)
@ -59,7 +53,7 @@
[:div.sidebar-content-section [:div.sidebar-content-section
[:div.back-to-dashboard {:on-click go-dashboard} [:div.back-to-dashboard {:on-click go-dashboard}
[:span.icon i/arrow-down] [:span.icon i/arrow-down]
[:span.text (t locale "labels.dashboard")]]] [:span.text (tr "labels.dashboard")]]]
[:hr] [:hr]
[:div.sidebar-content-section [:div.sidebar-content-section
@ -67,25 +61,30 @@
[:li {:class (when profile? "current") [:li {:class (when profile? "current")
:on-click go-settings-profile} :on-click go-settings-profile}
i/user i/user
[:span.element-title (t locale "labels.profile")]] [:span.element-title (tr "labels.profile")]]
[:li {:class (when password? "current") [:li {:class (when password? "current")
:on-click go-settings-password} :on-click go-settings-password}
i/lock i/lock
[:span.element-title (t locale "labels.password")]] [:span.element-title (tr "labels.password")]]
[:li {:class (when options? "current") [:li {:class (when options? "current")
:on-click go-settings-options} :on-click go-settings-options}
i/tree i/tree
[:span.element-title (t locale "labels.settings")]]]]])) [:span.element-title (tr "labels.settings")]]
(when cfg/feedback-enabled
[:li {:class (when feedback? "current")
:on-click go-settings-feedback}
i/msg-info
[:span.element-title (tr "labels.give-feedback")]])]]]))
(mf/defc sidebar (mf/defc sidebar
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [profile locale section]}] [{:keys [profile locale section]}]
[:div.dashboard-sidebar.settings [:div.dashboard-sidebar.settings
[:div.sidebar-inside [:div.sidebar-inside
[:& sidebar-content {:locale locale [:& sidebar-content {:profile profile
:profile profile
:section section}] :section section}]
[:& profile-section {:profile profile [:& profile-section {:profile profile
:locale locale}]]]) :locale locale}]]])

View file

@ -5,7 +5,7 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0. ;; defined by the Mozilla Public License, v. 2.0.
;; ;;
;; Copyright (c) 2020 UXBOX Labs SL ;; Copyright (c) 2020-2021 UXBOX Labs SL
(ns app.main.ui.workspace.header (ns app.main.ui.workspace.header
(:require (:require
@ -227,9 +227,10 @@
[:li {:on-click on-add-shared} [:li {:on-click on-add-shared}
[:span (tr "dashboard.add-shared")]]) [:span (tr "dashboard.add-shared")]])
[:li.feedback {:on-click #(.open js/window "https://github.com/penpot/penpot/discussions" "_blank")} (when cfg/feedback-enabled
[:span (tr "labels.feedback")] [:li.feedback {:on-click (st/emitf (rt/nav :settings-feedback))}
[:span.primary-badge "ALPHA"]] [:span (tr "labels.give-feedback")]
[:span.primary-badge "ALPHA"]])
]]])) ]]]))
;; --- Header Component ;; --- Header Component

View file

@ -57,7 +57,6 @@
form)) form))
(defn- wrap-update-fn (defn- wrap-update-fn
[f {:keys [spec validators]}] [f {:keys [spec validators]}]
(fn [& args] (fn [& args]