From c1a139fc51d0585ec8379435afb9ca50b4feb085 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 8 Feb 2021 22:39:11 +0100 Subject: [PATCH] :tada: Add user feedback module. --- .clj-kondo/config.edn | 1 + backend/resources/emails/feedback/en.subj | 1 + backend/resources/emails/feedback/en.txt | 7 + backend/src/app/config.clj | 9 ++ backend/src/app/emails.clj | 10 ++ backend/src/app/rpc.clj | 1 + backend/src/app/rpc/mutations/feedback.clj | 41 ++++++ backend/src/app/util/emails.clj | 15 ++- frontend/resources/locales.json | 105 ++++++++++++++- .../resources/styles/common/framework.scss | 1 + .../resources/styles/main/partials/forms.scss | 18 ++- frontend/src/app/config.cljs | 1 + frontend/src/app/main/ui.cljs | 26 ++-- .../src/app/main/ui/components/forms.cljs | 57 ++++++++- .../src/app/main/ui/dashboard/sidebar.cljs | 10 +- frontend/src/app/main/ui/settings.cljs | 16 ++- .../src/app/main/ui/settings/feedback.cljs | 120 ++++++++++++++++++ .../src/app/main/ui/settings/sidebar.cljs | 41 +++--- .../src/app/main/ui/workspace/header.cljs | 9 +- frontend/src/app/util/forms.cljs | 1 - 20 files changed, 431 insertions(+), 59 deletions(-) create mode 100644 backend/resources/emails/feedback/en.subj create mode 100644 backend/resources/emails/feedback/en.txt create mode 100644 backend/src/app/rpc/mutations/feedback.clj create mode 100644 frontend/src/app/main/ui/settings/feedback.cljs diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index 94fce53eb..becc05752 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -1,5 +1,6 @@ {:lint-as {potok.core/reify clojure.core/reify promesa.core/let clojure.core/let + rumext.alpha/defc clojure.core/defn app.db/with-atomic clojure.core/with-open} :output {:exclude-files ["data_readers.clj"]} diff --git a/backend/resources/emails/feedback/en.subj b/backend/resources/emails/feedback/en.subj new file mode 100644 index 000000000..2ecd8c0c4 --- /dev/null +++ b/backend/resources/emails/feedback/en.subj @@ -0,0 +1 @@ +[FEEDBACK]: From {{ profile.email }} diff --git a/backend/resources/emails/feedback/en.txt b/backend/resources/emails/feedback/en.txt new file mode 100644 index 000000000..f6e602a19 --- /dev/null +++ b/backend/resources/emails/feedback/en.txt @@ -0,0 +1,7 @@ +Feedback from: {{profile.fullname}} <{{profile.email}}> + +Profile ID: {{profile.id}} + +Subject: {{subject}} + +{{content}} diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index 239fc5984..71de67169 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -40,6 +40,9 @@ :storage-s3-region :eu-central-1 :storage-s3-bucket "penpot-devenv-assets-pre" + :feedback-destination "info@example.com" + :feedback-enabled false + :assets-path "/internal/assets/" :rlimits-password 10 @@ -93,7 +96,11 @@ (s/def ::media-directory ::us/string) (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 ::smtp-enabled ::us/boolean) (s/def ::smtp-default-reply-to ::us/string) (s/def ::smtp-default-from ::us/string) @@ -156,6 +163,8 @@ ::database-username ::default-blob-version ::error-report-webhook + ::feedback-enabled + ::feedback-destination ::github-client-id ::github-client-secret ::gitlab-base-uri diff --git a/backend/src/app/emails.clj b/backend/src/app/emails.clj index d6408b8e4..68441d821 100644 --- a/backend/src/app/emails.clj +++ b/backend/src/app/emails.clj @@ -43,6 +43,16 @@ ;; --- 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 ::register (s/keys :req-un [::name])) diff --git a/backend/src/app/rpc.clj b/backend/src/app/rpc.clj index b99987a0b..28f9e2f9e 100644 --- a/backend/src/app/rpc.clj +++ b/backend/src/app/rpc.clj @@ -126,6 +126,7 @@ 'app.rpc.mutations.projects 'app.rpc.mutations.viewer 'app.rpc.mutations.teams + 'app.rpc.mutations.feedback 'app.rpc.mutations.verify-token) (map (partial process-method cfg)) (into {})))) diff --git a/backend/src/app/rpc/mutations/feedback.clj b/backend/src/app/rpc/mutations/feedback.clj new file mode 100644 index 000000000..875a93985 --- /dev/null +++ b/backend/src/app/rpc/mutations/feedback.clj @@ -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))) diff --git a/backend/src/app/util/emails.clj b/backend/src/app/util/emails.clj index b5e744f50..813cf4367 100644 --- a/backend/src/app/util/emails.clj +++ b/backend/src/app/util/emails.clj @@ -9,6 +9,7 @@ (ns app.util.emails (:require + [app.common.data :as d] [app.common.exceptions :as ex] [app.common.spec :as us] [app.util.template :as tmpl] @@ -196,15 +197,17 @@ text (render-email-template-part :txt id context) html (render-email-template-part :html id context)] (when (or (not subj) - (not text) - (not html)) + (and (not text) + (not html))) (ex/raise :type :internal :code :missing-email-templates)) {:subject subj - :body [{:type "text/plain" - :content text} - {:type "text/html" - :content html}]})) + :body (d/concat + [{:type "text/plain" + :content text}] + (when html + [{:type "text/html" + :content html}]))})) (s/def ::priority #{:high :low}) (s/def ::to (s/or :sigle ::us/email diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index 74d10c33d..712a139a9 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -974,6 +974,78 @@ "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" : { "used-in" : [ "src/app/main/ui/settings/password.cljs:31" ], "translations" : { @@ -1605,7 +1677,7 @@ "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" ], "translations" : { "en" : "Give feedback", @@ -1823,6 +1895,37 @@ "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" : { "used-in" : [ "src/app/main/ui/static.cljs:75" ], "translations" : { diff --git a/frontend/resources/styles/common/framework.scss b/frontend/resources/styles/common/framework.scss index 6c1923b92..55089a604 100644 --- a/frontend/resources/styles/common/framework.scss +++ b/frontend/resources/styles/common/framework.scss @@ -20,6 +20,7 @@ min-width: 25px; padding: 0 1rem; transition: all .4s; + text-decoration: none !important; svg { height: 15px; width: 15px; diff --git a/frontend/resources/styles/main/partials/forms.scss b/frontend/resources/styles/main/partials/forms.scss index 03d520195..9e2d68ac0 100644 --- a/frontend/resources/styles/main/partials/forms.scss +++ b/frontend/resources/styles/main/partials/forms.scss @@ -102,6 +102,14 @@ textarea { text-decoration: underline; } + p { + color: $color-gray-60; + } + + hr { + border-color: $color-gray-20; + } + .links { display: flex; font-size: $fs14; @@ -131,7 +139,8 @@ textarea { flex-direction: column; position: relative; - input { + input, + textarea { background-color: $color-white; border-radius: 2px; border: 1px solid $color-gray-20; @@ -143,6 +152,13 @@ textarea { width: 100%; } + textarea { + height: auto; + font-size: $fs14; + font-family: "worksans", sans-serif; + padding-top: 20px; + } + // Makes the background for autocomplete white input:-webkit-autofill, input:-webkit-autofill:hover, diff --git a/frontend/src/app/config.cljs b/frontend/src/app/config.cljs index 482bed0d8..70e808686 100644 --- a/frontend/src/app/config.cljs +++ b/frontend/src/app/config.cljs @@ -67,6 +67,7 @@ (def default-language "en") (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 google-client-id (obj/get global "penpotGoogleClientID" nil)) (def gitlab-client-id (obj/get global "penpotGitlabClientID" nil)) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index b2f5e67e9..81ac15d54 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -59,17 +59,18 @@ (def routes [["/auth" - ["/login" :auth-login] - ["/register" :auth-register] + ["/login" :auth-login] + ["/register" :auth-register] ["/register/success" :auth-register-success] ["/recovery/request" :auth-recovery-request] - ["/recovery" :auth-recovery] - ["/verify-token" :auth-verify-token]] + ["/recovery" :auth-recovery] + ["/verify-token" :auth-verify-token]] ["/settings" - ["/profile" :settings-profile] + ["/profile" :settings-profile] ["/password" :settings-password] - ["/options" :settings-options]] + ["/feedback" :settings-feedback] + ["/options" :settings-options]] ["/view/:file-id/:page-id" {:name :viewer @@ -89,11 +90,11 @@ ["/render-object/:file-id/:page-id/:object-id" :render-object] ["/dashboard/team/:team-id" - ["/members" :dashboard-team-members] - ["/settings" :dashboard-team-settings] - ["/projects" :dashboard-projects] - ["/search" :dashboard-search] - ["/libraries" :dashboard-libraries] + ["/members" :dashboard-team-members] + ["/settings" :dashboard-team-settings] + ["/projects" :dashboard-projects] + ["/search" :dashboard-search] + ["/libraries" :dashboard-libraries] ["/projects/:project-id" :dashboard-files]] ["/workspace/:project-id/:file-id" :workspace]]) @@ -121,7 +122,8 @@ (:settings-profile :settings-password - :settings-options) + :settings-options + :settings-feedback) [:& settings/settings {:route route}] :debug-icons-preview diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 913dab1d1..37b08f6c9 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -15,7 +15,7 @@ [app.main.ui.icons :as i] [app.util.object :as obj] [app.util.forms :as fm] - [app.util.i18n :as i18n :refer [t]] + [app.util.i18n :as i18n :refer [t tr]] ["react" :as react] [app.util.dom :as dom])) @@ -28,7 +28,6 @@ type' (mf/use-state type) focus? (mf/use-state false) - locale (mf/deref i18n/locale) touched? (get-in @form [:touched name]) error (get-in @form [:errors name]) @@ -94,7 +93,59 @@ help-icon']) (cond (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) [:span.hint hint])]])) diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 7804168de..2d4fb98a3 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -466,10 +466,12 @@ [:li {:on-click (partial on-click (da/logout))} [:span.icon i/exit] [: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] - [:span.text (t locale "labels.feedback")] - [:span.primary-badge "ALPHA"]]]]] + + (when cfg/feedback-enabled + [:li.feedback {:on-click (partial on-click :settings-feedback)} + [:span.icon i/msg-info] + [:span.text (t locale "labels.give-feedback")] + [:span.primary-badge "ALPHA"]])]]] (when (and team profile) [:& comments-section {:profile profile diff --git a/frontend/src/app/main/ui/settings.cljs b/frontend/src/app/main/ui/settings.cljs index 6994f243a..05ec590a2 100644 --- a/frontend/src/app/main/ui/settings.cljs +++ b/frontend/src/app/main/ui/settings.cljs @@ -5,30 +5,31 @@ ;; 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 +;; Copyright (c) 2020-2021 UXBOX Labs SL (ns app.main.ui.settings (:require [app.main.refs :as refs] [app.main.store :as st] [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.profile :refer [profile-page]] [app.main.ui.settings.sidebar :refer [sidebar]] [app.main.ui.settings.change-email] [app.main.ui.settings.delete-account] - [app.util.i18n :as i18n :refer [t]] + [app.util.i18n :as i18n :refer [tr]] [rumext.alpha :as mf])) (mf/defc header {::mf/wrap [mf/memo]} - [{:keys [locale] :as props}] + [] (let [logout (constantly nil)] [:header.dashboard-header [:div.dashboard-title - [:h1 (t locale "dashboard.your-account-title")]] + [:h1 (tr "dashboard.your-account-title")]] [:a.btn-secondary.btn-small {:on-click logout} - (t locale "labels.logout")]])) + (tr "labels.logout")]])) (mf/defc settings [{:keys [route] :as props}] @@ -41,12 +42,15 @@ :section section}] [:div.dashboard-content - [:& header {:locale locale}] + [:& header] [:section.dashboard-container (case section :settings-profile [:& profile-page {:locale locale}] + :settings-feedback + [:& feedback-page] + :settings-password [:& password-page {:locale locale}] diff --git a/frontend/src/app/main/ui/settings/feedback.cljs b/frontend/src/app/main/ui/settings/feedback.cljs new file mode 100644 index 000000000..125303a23 --- /dev/null +++ b/frontend/src/app/main/ui/settings/feedback.cljs @@ -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]]]) diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 744778388..dd7a49d05 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -9,31 +9,20 @@ (ns app.main.ui.settings.sidebar (:require - [app.common.spec :as us] - [app.main.data.auth :as da] - [app.main.data.messages :as dm] - [app.main.refs :as refs] + [app.config :as cfg] [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.icons :as i] - [app.util.i18n :as i18n :refer [t tr]] - [app.util.object :as obj] + [app.util.i18n :as i18n :refer [tr]] [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])) (mf/defc sidebar-content - [{:keys [locale profile section] :as props}] + [{:keys [profile section] :as props}] (let [profile? (= section :settings-profile) password? (= section :settings-password) options? (= section :settings-options) + feedback? (= section :settings-feedback) go-dashboard (mf/use-callback @@ -45,6 +34,11 @@ (mf/deps 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 (mf/use-callback (mf/deps profile) @@ -59,7 +53,7 @@ [:div.sidebar-content-section [:div.back-to-dashboard {:on-click go-dashboard} [:span.icon i/arrow-down] - [:span.text (t locale "labels.dashboard")]]] + [:span.text (tr "labels.dashboard")]]] [:hr] [:div.sidebar-content-section @@ -67,25 +61,30 @@ [:li {:class (when profile? "current") :on-click go-settings-profile} i/user - [:span.element-title (t locale "labels.profile")]] + [:span.element-title (tr "labels.profile")]] [:li {:class (when password? "current") :on-click go-settings-password} i/lock - [:span.element-title (t locale "labels.password")]] + [:span.element-title (tr "labels.password")]] [:li {:class (when options? "current") :on-click go-settings-options} 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/wrap [mf/memo]} [{:keys [profile locale section]}] [:div.dashboard-sidebar.settings [:div.sidebar-inside - [:& sidebar-content {:locale locale - :profile profile + [:& sidebar-content {:profile profile :section section}] [:& profile-section {:profile profile :locale locale}]]]) diff --git a/frontend/src/app/main/ui/workspace/header.cljs b/frontend/src/app/main/ui/workspace/header.cljs index 158e07c58..e4507a5ec 100644 --- a/frontend/src/app/main/ui/workspace/header.cljs +++ b/frontend/src/app/main/ui/workspace/header.cljs @@ -5,7 +5,7 @@ ;; 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 +;; Copyright (c) 2020-2021 UXBOX Labs SL (ns app.main.ui.workspace.header (:require @@ -227,9 +227,10 @@ [:li {:on-click on-add-shared} [:span (tr "dashboard.add-shared")]]) - [:li.feedback {:on-click #(.open js/window "https://github.com/penpot/penpot/discussions" "_blank")} - [:span (tr "labels.feedback")] - [:span.primary-badge "ALPHA"]] + (when cfg/feedback-enabled + [:li.feedback {:on-click (st/emitf (rt/nav :settings-feedback))} + [:span (tr "labels.give-feedback")] + [:span.primary-badge "ALPHA"]]) ]]])) ;; --- Header Component diff --git a/frontend/src/app/util/forms.cljs b/frontend/src/app/util/forms.cljs index 17e80068d..94e1a385e 100644 --- a/frontend/src/app/util/forms.cljs +++ b/frontend/src/app/util/forms.cljs @@ -57,7 +57,6 @@ form)) - (defn- wrap-update-fn [f {:keys [spec validators]}] (fn [& args]