mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
✨ Add proper backend integration of for new feedback form
This commit is contained in:
@@ -8,38 +8,41 @@
|
||||
<body>
|
||||
<p>
|
||||
<strong>Feedback from:</strong><br />
|
||||
{% if profile %}
|
||||
<span>
|
||||
<span>Name: </span>
|
||||
<span><code>{{profile.fullname|abbreviate:25}}</code></span>
|
||||
</span>
|
||||
<br />
|
||||
|
||||
<span>
|
||||
<span>Email: </span>
|
||||
<span>{{profile.email}}</span>
|
||||
</span>
|
||||
<br />
|
||||
|
||||
<span>
|
||||
<span>ID: </span>
|
||||
<span><code>{{profile.id}}</code></span>
|
||||
</span>
|
||||
{% else %}
|
||||
<span>
|
||||
<span>Email: </span>
|
||||
<span>{{profile.email}}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span>
|
||||
<span>Name: </span>
|
||||
<span><code>{{profile.fullname|abbreviate:25}}</code></span>
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
<span>Email: </span>
|
||||
<span>{{profile.email}}</span>
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
<span>ID: </span>
|
||||
<span><code>{{profile.id}}</code></span>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Subject:</strong><br />
|
||||
<span>{{subject|abbreviate:300}}</span>
|
||||
<span>{{feedback-subject|abbreviate:300}}</span>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Type:</strong><br />
|
||||
<span>{{feedback-type|abbreviate:300}}</span>
|
||||
</p>
|
||||
|
||||
{% if feedback-error-href %}
|
||||
<p>
|
||||
<strong>Error HREF:</strong><br />
|
||||
<span>{{feedback-error-href|abbreviate:500}}</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
<strong>Message:</strong><br />
|
||||
{{content|linebreaks-br|safe}}
|
||||
{{feedback-content|linebreaks-br}}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1 +1 @@
|
||||
[PENPOT FEEDBACK]: {{subject}}
|
||||
[PENPOT FEEDBACK]: {{feedback-subject}}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{% if profile %}
|
||||
Feedback profile: {{profile.fullname}} <{{profile.email}}> / {{profile.id}}
|
||||
{% else %}
|
||||
Feedback from: {{email}}
|
||||
{% endif %}
|
||||
From: {{profile.fullname}} <{{profile.email}}> / {{profile.id}}
|
||||
Subject: {{feedback-subject}}
|
||||
Type: {{feedback-type}}
|
||||
{%- if feedback-error-href %}
|
||||
HREF: {{feedback-error-href}}
|
||||
{% endif -%}
|
||||
|
||||
Subject: {{subject}}
|
||||
Message:
|
||||
|
||||
{{content}}
|
||||
{{feedback-content}}
|
||||
|
||||
@@ -359,8 +359,10 @@
|
||||
|
||||
(def ^:private schema:feedback
|
||||
[:map
|
||||
[:subject ::sm/text]
|
||||
[:content ::sm/text]])
|
||||
[:feedback-subject ::sm/text]
|
||||
[:feedback-type ::sm/text]
|
||||
[:feedback-content ::sm/text]
|
||||
[:profile :map]])
|
||||
|
||||
(def user-feedback
|
||||
"A profile feedback email."
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
(ns app.rpc.commands.feedback
|
||||
"A general purpose feedback module."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.schema :as sm]
|
||||
[app.config :as cf]
|
||||
@@ -21,8 +22,11 @@
|
||||
|
||||
(def ^:private schema:send-user-feedback
|
||||
[:map {:title "send-user-feedback"}
|
||||
[:subject [:string {:max 400}]]
|
||||
[:content [:string {:max 2500}]]])
|
||||
[:subject [:string {:max 500}]]
|
||||
[:content [:string {:max 2500}]]
|
||||
[:type {:optional true} :string]
|
||||
[:error-href {:optional true} [:string {:max 2500}]]
|
||||
[:error-report {:optional true} :string]])
|
||||
|
||||
(sv/defmethod ::send-user-feedback
|
||||
{::doc/added "1.18"
|
||||
@@ -39,16 +43,26 @@
|
||||
|
||||
(defn- send-user-feedback!
|
||||
[pool profile params]
|
||||
(let [dest (or (cf/get :user-feedback-destination)
|
||||
;; LEGACY
|
||||
(cf/get :feedback-destination))]
|
||||
(let [destination
|
||||
(or (cf/get :user-feedback-destination)
|
||||
;; LEGACY
|
||||
(cf/get :feedback-destination))
|
||||
|
||||
attachments
|
||||
(d/without-nils
|
||||
{"error-report.txt" (:error-report params)})]
|
||||
|
||||
(eml/send! {::eml/conn pool
|
||||
::eml/factory eml/user-feedback
|
||||
:from dest
|
||||
:to dest
|
||||
:profile profile
|
||||
:from (cf/get :smtp-default-from)
|
||||
:to destination
|
||||
:reply-to (:email profile)
|
||||
:email (:email profile)
|
||||
:subject (:subject params)
|
||||
:content (:content params)})
|
||||
:attachments attachments
|
||||
|
||||
:feedback-subject (:subject params)
|
||||
:feedback-type (:type params "not-specified")
|
||||
:feedback-content (:content params)
|
||||
:feedback-error-href (:error-href params)
|
||||
:profile profile})
|
||||
nil))
|
||||
|
||||
@@ -22,4 +22,4 @@
|
||||
(t/is (contains? result :body))
|
||||
(t/is (contains? result :to))
|
||||
#_(t/is (contains? result :reply-to))
|
||||
(t/is (vector? (:body result)))))
|
||||
(t/is (map? (:body result)))))
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
;; From app.main.data.workspace we can use directly because it causes a circular dependency
|
||||
(def reload-file nil)
|
||||
|
||||
;; Will contain the latest error report assigned
|
||||
(def last-report nil)
|
||||
|
||||
(defn- print-data!
|
||||
[data]
|
||||
(-> data
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
(def dashboard-page*
|
||||
(mf/lazy-component app.main.ui.dashboard/dashboard*))
|
||||
|
||||
(def settings-page
|
||||
(mf/lazy-component app.main.ui.settings/settings))
|
||||
(def settings-page*
|
||||
(mf/lazy-component app.main.ui.settings/settings*))
|
||||
|
||||
(def workspace-page*
|
||||
(mf/lazy-component app.main.ui.workspace/workspace*))
|
||||
@@ -197,14 +197,13 @@
|
||||
:settings-subscription
|
||||
:settings-access-tokens
|
||||
:settings-notifications)
|
||||
(let [params (get params :query)
|
||||
type (some-> params :type)
|
||||
report-id (some-> params :report-id)
|
||||
url-error (some-> params :url-error)]
|
||||
[:? [:& settings-page {:route route
|
||||
:type type
|
||||
:report-id report-id
|
||||
:url-error url-error}]])
|
||||
(let [params (get params :query)
|
||||
error-report-id (some-> params :error-report-id uuid/parse*)]
|
||||
[:? [:> settings-page*
|
||||
{:route route
|
||||
:type (get params :type)
|
||||
:error-report-id error-report-id
|
||||
:error-href (get params :error-href)}]])
|
||||
|
||||
:debug-icons-preview
|
||||
(when *assert*
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
[app.main.ui.settings.access-tokens :refer [access-tokens-page]]
|
||||
[app.main.ui.settings.change-email]
|
||||
[app.main.ui.settings.delete-account]
|
||||
[app.main.ui.settings.feedback :refer [feedback-page]]
|
||||
[app.main.ui.settings.feedback :refer [feedback-page*]]
|
||||
[app.main.ui.settings.notifications :refer [notifications-page*]]
|
||||
[app.main.ui.settings.options :refer [options-page]]
|
||||
[app.main.ui.settings.password :refer [password-page]]
|
||||
@@ -33,8 +33,8 @@
|
||||
[:div {:class (stl/css :dashboard-title)}
|
||||
[:h1 {:data-testid "account-title"} (tr "dashboard.your-account-title")]]])
|
||||
|
||||
(mf/defc settings
|
||||
[{:keys [route type report-id url-error]}]
|
||||
(mf/defc settings*
|
||||
[{:keys [route type error-report-id error-href]}]
|
||||
(let [section (get-in route [:data :name])
|
||||
profile (mf/deref refs/profile)]
|
||||
|
||||
@@ -60,9 +60,9 @@
|
||||
[:& profile-page]
|
||||
|
||||
:settings-feedback
|
||||
[:& feedback-page {:type type
|
||||
:report-id report-id
|
||||
:url-error url-error}]
|
||||
[:> feedback-page* {:type type
|
||||
:error-report-id error-report-id
|
||||
:error-href error-href}]
|
||||
|
||||
:settings-password
|
||||
[:& password-page]
|
||||
|
||||
@@ -8,45 +8,63 @@
|
||||
"Feedback form."
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.schema :as sm]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.errors :as errors]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn schema:feedback-form [url-error]
|
||||
(def ^:private schema:feedback-form
|
||||
[:map {:title "FeedbackForm"}
|
||||
[:subject [::sm/text {:max 250}]]
|
||||
[:type [:string {:max 250}]]
|
||||
[:content [::sm/text {:max 5000}]]
|
||||
[:penpot-link [::sm/text {:max 2048 :value (or url-error "") :optional true}]]])
|
||||
[:error-report {:optional true} ::sm/text]
|
||||
[:error-href {:optional true} [::sm/text {:max 2048}]]])
|
||||
|
||||
(mf/defc feedback-form
|
||||
(mf/defc feedback-form*
|
||||
{::mf/private true}
|
||||
[{:keys [report type url-error]}]
|
||||
(let [profile (mf/deref refs/profile)
|
||||
initial (mf/with-memo [url-error]
|
||||
{:subject ""
|
||||
:type (or type "")
|
||||
:content ""
|
||||
:penpot-link url-error})
|
||||
form (fm/use-form :schema (schema:feedback-form url-error) :initial initial)
|
||||
loading (mf/use-state false)
|
||||
report (wapi/create-blob report "text/plain")
|
||||
report-uri (wapi/create-uri report)
|
||||
[{:keys [error-report type error-href]}]
|
||||
(let [profile (mf/deref refs/profile)
|
||||
|
||||
initial
|
||||
(mf/with-memo [error-href error-report]
|
||||
(d/without-nils
|
||||
{:subject ""
|
||||
:type (d/nilv type "")
|
||||
:content ""
|
||||
:error-href error-href
|
||||
:error-report error-report}))
|
||||
|
||||
form
|
||||
(fm/use-form :schema schema:feedback-form
|
||||
:initial initial)
|
||||
|
||||
loading
|
||||
(mf/use-state false)
|
||||
|
||||
report
|
||||
(mf/with-memo [error-report]
|
||||
(wapi/create-blob error-report "text/plain"))
|
||||
|
||||
on-download
|
||||
(mf/use-fn
|
||||
(mf/deps report)
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/trigger-download-uri "report" "text/plain" report-uri)))
|
||||
(let [uri (wapi/create-uri report)]
|
||||
(dom/trigger-download-uri "report" "text/plain" uri)
|
||||
(tm/schedule-on-idle #(wapi/revoke-uri uri)))))
|
||||
|
||||
|
||||
on-succes
|
||||
(mf/use-fn
|
||||
@@ -74,11 +92,12 @@
|
||||
(->> (rp/cmd! :send-user-feedback data)
|
||||
(rx/subs! on-succes on-error)))))]
|
||||
|
||||
|
||||
[:& fm/form {:class (stl/css :feedback-form)
|
||||
:on-submit on-submit
|
||||
:form form}
|
||||
|
||||
;; --- Feedback section
|
||||
;; --- Feedback section
|
||||
[:h2 {:class (stl/css :field-title :feedback-title)} (tr "feedback.title-contact-us")]
|
||||
[:p {:class (stl/css :field-text :feedback-title)} (tr "feedback.subtitle")]
|
||||
|
||||
@@ -106,7 +125,7 @@
|
||||
[:div {:class (stl/css :fields-row)}
|
||||
[:p {:class (stl/css :field-text)} (tr "feedback.penpot.link")]
|
||||
[:& fm/input {:label ""
|
||||
:name :penpot-link
|
||||
:name :error-href
|
||||
:placeholder "https://penpot.app/"
|
||||
:show-success? true}]
|
||||
|
||||
@@ -136,13 +155,15 @@
|
||||
(tr "feedback.twitter-title")]
|
||||
[:p {:class (stl/css :field-text)} (tr "feedback.twitter-subtitle1")]]))
|
||||
|
||||
(mf/defc feedback-page
|
||||
[{:keys [type report-id url-error]}]
|
||||
(mf/defc feedback-page*
|
||||
[{:keys [error-report-id] :as props}]
|
||||
(mf/with-effect []
|
||||
(dom/set-html-title (tr "title.settings.feedback")))
|
||||
(let [report (.getItem js/localStorage report-id)]
|
||||
|
||||
(let [report (when (= error-report-id (:id errors/last-report))
|
||||
(:content errors/last-report))
|
||||
props (mf/spread-props props {:error-report report})]
|
||||
|
||||
[:div {:class (stl/css :dashboard-settings)}
|
||||
[:div {:class (stl/css :form-container)}
|
||||
[:& feedback-form {:report report
|
||||
:type type
|
||||
:url-error url-error}]]]))
|
||||
[:> feedback-form* props]]]))
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.uri :as u]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.auth :refer [is-authenticated?]]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.errors :as errors]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.router :as rt]
|
||||
@@ -29,6 +31,7 @@
|
||||
[app.main.ui.viewer.header :as viewer.header]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
@@ -352,16 +355,20 @@
|
||||
(mf/defc internal-error*
|
||||
[{:keys [on-reset report] :as props}]
|
||||
(let [report-uri (mf/use-ref nil)
|
||||
on-reset (or on-reset #(st/emit! (rt/assign-exception nil)))
|
||||
on-reset (or on-reset #(st/emit! (rt/assign-exception nil)))
|
||||
|
||||
support-contact-click
|
||||
(mf/use-fn
|
||||
(mf/deps on-reset report)
|
||||
(fn []
|
||||
(let [report-id (str "report-" (random-uuid))]
|
||||
(.setItem js/localStorage report-id report)
|
||||
(st/emit! (rt/nav :settings-feedback {:type "issue"
|
||||
:report-id report-id
|
||||
:url-error (rt/get-current-href)})))))
|
||||
(tm/schedule on-reset)
|
||||
(let [error-report-id (uuid/next)
|
||||
error-href (rt/get-current-href)]
|
||||
(set! errors/last-report {:id error-report-id :content report})
|
||||
(st/emit!
|
||||
(rt/nav :settings-feedback {:type "issue"
|
||||
:error-report-id error-report-id
|
||||
:error-href error-href})))))
|
||||
|
||||
on-download
|
||||
(mf/use-fn
|
||||
@@ -372,6 +379,7 @@
|
||||
|
||||
(mf/with-effect [report]
|
||||
(when (some? report)
|
||||
(set! errors/last-report report)
|
||||
(let [report (wapi/create-blob report "text/plain")
|
||||
uri (wapi/create-uri report)]
|
||||
(mf/set-ref-val! report-uri uri)
|
||||
@@ -444,6 +452,7 @@
|
||||
:path (get route :path)
|
||||
:report report
|
||||
:params params}))))
|
||||
|
||||
(case type
|
||||
:not-found
|
||||
[:> not-found* {}]
|
||||
|
||||
Reference in New Issue
Block a user