mirror of
https://github.com/penpot/penpot.git
synced 2025-03-18 10:41:29 -05:00
🎉 Add team webhooks section
This commit is contained in:
parent
02d619ed48
commit
cdbfec4f19
11 changed files with 677 additions and 5 deletions
|
@ -12,7 +12,7 @@
|
|||
[app.db :as db]
|
||||
[app.http.client :as http]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.queries.teams :refer [check-edition-permissions!]]
|
||||
[app.rpc.queries.teams :refer [check-edition-permissions! check-read-permissions!]]
|
||||
[app.util.services :as sv]
|
||||
[app.util.time :as dt]
|
||||
[app.worker :as-alias wrk]
|
||||
|
@ -49,7 +49,7 @@
|
|||
(instance? javax.net.ssl.SSLHandshakeException exception)
|
||||
(ex/raise :type :validation
|
||||
:code :webhook-validation
|
||||
:hint "ssl-validaton")
|
||||
:hint "ssl-validation")
|
||||
|
||||
:else
|
||||
(ex/raise :type :validation
|
||||
|
@ -118,3 +118,19 @@
|
|||
(check-edition-permissions! conn profile-id (:team-id whook))
|
||||
(db/delete! conn :webhook {:id id})
|
||||
nil)))
|
||||
|
||||
;; --- Query: Webhooks
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::get-webhooks
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
|
||||
(def sql:get-webhooks
|
||||
"select id, uri, mtype, is_active, error_code, error_count
|
||||
from webhook where team_id = ? order by uri")
|
||||
|
||||
(sv/defmethod ::get-webhooks
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id team-id)
|
||||
(db/exec! conn [sql:get-webhooks team-id])))
|
|
@ -93,7 +93,8 @@
|
|||
}
|
||||
|
||||
.dashboard-team-members,
|
||||
.dashboard-team-invitations {
|
||||
.dashboard-team-invitations,
|
||||
.dashboard-team-webhooks {
|
||||
.empty-invitations {
|
||||
height: 156px;
|
||||
max-width: 1040px;
|
||||
|
@ -197,6 +198,71 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.uri,
|
||||
&.active {
|
||||
width: 48%;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
&.last-delivery {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
position: relative;
|
||||
.success svg {
|
||||
fill: $color-primary;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.failure svg {
|
||||
fill: $color-warning;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -58px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
text-align: center;
|
||||
|
||||
.label {
|
||||
border-radius: 3px;
|
||||
color: $color-white;
|
||||
background-color: $color-black;
|
||||
white-space: nowrap;
|
||||
padding: 12px 20px;
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
margin: 0 auto;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-top: 8px solid $color-black;
|
||||
}
|
||||
}
|
||||
|
||||
.last-delivery-icon:hover {
|
||||
.tooltip {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
|
@ -380,3 +446,96 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-team-webhooks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.webhooks-hero-container {
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.upload-button {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.webhooks-hero {
|
||||
font-size: $fs14;
|
||||
|
||||
padding: $size-6;
|
||||
background-color: $color-white;
|
||||
margin-top: $size-6;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.banner {
|
||||
background-color: unset;
|
||||
|
||||
display: flex;
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 0px;
|
||||
padding-right: 10px;
|
||||
svg {
|
||||
fill: $color-info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
h2 {
|
||||
margin-bottom: $size-4;
|
||||
color: $color-black;
|
||||
}
|
||||
width: 80%;
|
||||
color: $color-gray-40;
|
||||
p {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.webhooks-empty {
|
||||
text-align: center;
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
padding: $size-6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px dashed $color-gray-20;
|
||||
color: $color-gray-40;
|
||||
margin-top: 12px;
|
||||
min-height: 136px;
|
||||
}
|
||||
}
|
||||
|
||||
.webhooks-modal {
|
||||
.action-buttons {
|
||||
gap: 10px;
|
||||
}
|
||||
.input-checkbox label {
|
||||
font-size: 14px;
|
||||
color: $color-black;
|
||||
}
|
||||
|
||||
.explain {
|
||||
font-size: 12px;
|
||||
color: $color-gray-40;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,6 +149,24 @@
|
|||
(->> (rp/query! :team-invitations {:team-id team-id})
|
||||
(rx/map team-invitations-fetched))))))
|
||||
|
||||
;; --- EVENT: fetch-team-webhooks
|
||||
|
||||
(defn team-webhooks-fetched
|
||||
[webhooks]
|
||||
(ptk/reify ::team-webhooks-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :dashboard-team-webhooks webhooks))))
|
||||
|
||||
(defn fetch-team-webhooks
|
||||
[]
|
||||
(ptk/reify ::fetch-team-webhooks
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(->> (rp/command! :get-webhooks {:team-id team-id})
|
||||
(rx/map team-webhooks-fetched))))))
|
||||
|
||||
;; --- EVENT: fetch-projects
|
||||
|
||||
(defn projects-fetched
|
||||
|
@ -522,6 +540,61 @@
|
|||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
(defn delete-team-webhook
|
||||
[{:keys [id] :as params}]
|
||||
(us/assert ::us/uuid id)
|
||||
(ptk/reify ::delete-team-webhook
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)
|
||||
params (assoc params :team-id team-id)
|
||||
{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)]
|
||||
(->> (rp/command! :delete-webhook params)
|
||||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
(s/def ::mtype
|
||||
#{"application/json"
|
||||
"application/x-www-form-urlencoded"
|
||||
"application/transit+json"})
|
||||
|
||||
(defn update-team-webhook
|
||||
[{:keys [id uri mtype is-active] :as params}]
|
||||
(us/assert ::us/uuid id)
|
||||
(us/assert ::us/uri uri)
|
||||
(us/assert ::mtype mtype)
|
||||
(us/assert ::us/boolean is-active)
|
||||
(ptk/reify ::update-team-webhook
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)
|
||||
params (assoc params :team-id team-id)
|
||||
{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)]
|
||||
(->> (rp/command! :update-webhook params)
|
||||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
(defn create-team-webhook
|
||||
[{:keys [uri mtype is-active] :as params}]
|
||||
(us/assert ::us/uri uri)
|
||||
(us/assert ::mtype mtype)
|
||||
(us/assert ::us/boolean is-active)
|
||||
(ptk/reify ::create-team-webhook
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)
|
||||
params (assoc params :team-id team-id)
|
||||
{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)]
|
||||
(->> (rp/command! :create-webhook params)
|
||||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
;; --- EVENT: delete-team
|
||||
|
||||
(defn delete-team
|
||||
|
@ -913,6 +986,14 @@
|
|||
(let [team-id (:current-team-id state)]
|
||||
(rx/of (rt/nav :dashboard-team-invitations {:team-id team-id}))))))
|
||||
|
||||
(defn go-to-team-webhooks
|
||||
[]
|
||||
(ptk/reify ::go-to-team-webhooks
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(rx/of (rt/nav :dashboard-team-webhooks {:team-id team-id}))))))
|
||||
|
||||
(defn go-to-team-settings
|
||||
[]
|
||||
(ptk/reify ::go-to-team-settings
|
||||
|
|
|
@ -74,6 +74,9 @@
|
|||
(def dashboard-team-invitations
|
||||
(l/derived :dashboard-team-invitations st/state))
|
||||
|
||||
(def dashboard-team-webhooks
|
||||
(l/derived :dashboard-team-webhooks st/state))
|
||||
|
||||
(def dashboard-selected-project
|
||||
(l/derived (fn [state]
|
||||
(dm/get-in state [:dashboard-local :selected-project]))
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
:dashboard-font-providers
|
||||
:dashboard-team-members
|
||||
:dashboard-team-invitations
|
||||
:dashboard-team-webhooks
|
||||
:dashboard-team-settings)
|
||||
|
||||
[:*
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
[app.main.ui.dashboard.projects :refer [projects-section]]
|
||||
[app.main.ui.dashboard.search :refer [search-page]]
|
||||
[app.main.ui.dashboard.sidebar :refer [sidebar]]
|
||||
[app.main.ui.dashboard.team :refer [team-settings-page team-members-page team-invitations-page]]
|
||||
[app.main.ui.dashboard.team :refer [team-settings-page team-members-page team-invitations-page team-webhooks-page]]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
|
@ -236,6 +236,9 @@
|
|||
:dashboard-team-invitations
|
||||
[:& team-invitations-page {:team team}]
|
||||
|
||||
:dashboard-team-webhooks
|
||||
[:& team-webhooks-page {:team team}]
|
||||
|
||||
:dashboard-team-settings
|
||||
[:& team-settings-page {:team team :profile profile}]
|
||||
|
||||
|
|
|
@ -245,6 +245,7 @@
|
|||
[{:keys [team profile] :as props}]
|
||||
(let [go-members #(st/emit! (dd/go-to-team-members))
|
||||
go-invitations #(st/emit! (dd/go-to-team-invitations))
|
||||
go-webhooks #(st/emit! (dd/go-to-team-webhooks))
|
||||
go-settings #(st/emit! (dd/go-to-team-settings))
|
||||
|
||||
members-map (mf/deref refs/dashboard-team-members)
|
||||
|
@ -323,6 +324,7 @@
|
|||
[:ul.dropdown.options-dropdown
|
||||
[:li {:on-click go-members :data-test "team-members"} (tr "labels.members")]
|
||||
[:li {:on-click go-invitations :data-test "team-invitations"} (tr "labels.invitations")]
|
||||
[:li {:on-click go-webhooks :data-test "team-webhooks"} (tr "labels.webhooks")]
|
||||
[:li {:on-click go-settings :data-test "team-settings"} (tr "labels.settings")]
|
||||
[:hr]
|
||||
(when can-rename?
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc header
|
||||
|
@ -35,13 +36,15 @@
|
|||
(let [go-members (mf/use-fn #(st/emit! (dd/go-to-team-members)))
|
||||
go-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings)))
|
||||
go-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations)))
|
||||
invite-member (mf/use-fn
|
||||
go-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks)))
|
||||
invite-member (mf/use-fn
|
||||
(mf/deps team)
|
||||
#(st/emit! (modal/show {:type :invite-members :team team :origin :team})))
|
||||
|
||||
members-section? (= section :dashboard-team-members)
|
||||
settings-section? (= section :dashboard-team-settings)
|
||||
invitations-section? (= section :dashboard-team-invitations)
|
||||
webhooks-section? (= section :dashboard-team-webhooks)
|
||||
permissions (:permissions team)]
|
||||
|
||||
[:header.dashboard-header.team
|
||||
|
@ -50,6 +53,7 @@
|
|||
members-section? (tr "labels.members")
|
||||
settings-section? (tr "labels.settings")
|
||||
invitations-section? (tr "labels.invitations")
|
||||
webhooks-section? (tr "labels.webhooks")
|
||||
:else nil)]]
|
||||
[:nav.dashboard-header-menu
|
||||
[:ul.dashboard-header-options
|
||||
|
@ -57,6 +61,8 @@
|
|||
[:a {:on-click go-members} (tr "labels.members")]]
|
||||
[:li {:class (when invitations-section? "active")}
|
||||
[:a {:on-click go-invitations} (tr "labels.invitations")]]
|
||||
[:li {:class (when webhooks-section? "active")}
|
||||
[:a {:on-click go-webhooks} (tr "labels.webhooks")]]
|
||||
[:li {:class (when settings-section? "active")}
|
||||
[:a {:on-click go-settings} (tr "labels.settings")]]]]
|
||||
[:div.dashboard-buttons
|
||||
|
@ -571,6 +577,238 @@
|
|||
[:& invitation-section {:team team
|
||||
:invitations invitations}]]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; WEBHOOKS SECTION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(s/def ::uri ::us/not-empty-string)
|
||||
(s/def ::mtype ::us/not-empty-string)
|
||||
(s/def ::webhook-form
|
||||
(s/keys :req-un [::uri ::mtype]))
|
||||
|
||||
(mf/defc webhook-modal {::mf/register modal/components
|
||||
::mf/register-as :webhook}
|
||||
[{:keys [webhook] :as props}]
|
||||
(let [initial (mf/use-memo (fn [] (or webhook {:is-active false :mtype "application/json"})))
|
||||
form (fm/use-form :spec ::webhook-form
|
||||
:initial initial)
|
||||
mtypes [{:label "application/json" :value "application/json"}
|
||||
{:label "application/x-www-form-urlencoded" :value "application/x-www-form-urlencoded"}
|
||||
{:label "application/transit+json" :value "application/transit+json"}]
|
||||
|
||||
on-success
|
||||
(fn [message]
|
||||
(st/emit! (dd/fetch-team-webhooks)
|
||||
(msg/success message)
|
||||
(modal/hide)))
|
||||
|
||||
on-error
|
||||
(fn [message {:keys [type code hint] :as error}]
|
||||
(let [message (if (and (= type :validation) (= code :webhook-validation))
|
||||
(str message " "
|
||||
(case hint
|
||||
"ssl-validation" (tr "errors.webhooks.ssl-validation")
|
||||
"")) ;; TODO Add more error codes when back defines them
|
||||
message)]
|
||||
(rx/of (msg/error message))))
|
||||
|
||||
on-create-submit
|
||||
(fn []
|
||||
(let [mdata {:on-success #(on-success (tr "dashboard.webhooks.create.success"))
|
||||
:on-error (partial on-error (tr "dashboard.webhooks.create.error"))}
|
||||
webhook {:uri (get-in @form [:clean-data :uri])
|
||||
:mtype (get-in @form [:clean-data :mtype])
|
||||
:is-active (get-in @form [:clean-data :is-active])}]
|
||||
(st/emit! (dd/create-team-webhook (with-meta webhook mdata)))))
|
||||
|
||||
on-update-submit
|
||||
(fn []
|
||||
(let [mdata {:on-success #(on-success (tr "dashboard.webhooks.update.success"))
|
||||
:on-error (partial on-error (tr "dashboard.webhooks.update.error"))}
|
||||
webhook (get @form :clean-data)]
|
||||
(st/emit! (dd/update-team-webhook (with-meta webhook mdata)))))
|
||||
|
||||
on-submit
|
||||
#(let [data (:clean-data @form)]
|
||||
(if (:id data)
|
||||
(on-update-submit)
|
||||
(on-create-submit)))]
|
||||
|
||||
[:div.modal-overlay
|
||||
[:div.modal-container.webhooks-modal
|
||||
[:& fm/form {:form form :on-submit on-submit}
|
||||
|
||||
[:div.modal-header
|
||||
[:div.modal-header-title
|
||||
(if webhook
|
||||
[:h2 (tr "modals.edit-webhook.title")]
|
||||
[:h2 (tr "modals.create-webhook.title")])]
|
||||
|
||||
[:div.modal-close-button
|
||||
{:on-click #(st/emit! (modal/hide))} i/close]]
|
||||
|
||||
[:div.modal-content.generic-form
|
||||
[:div.fields-container
|
||||
[:div.fields-row
|
||||
[:& fm/input {:type "text"
|
||||
:auto-focus? true
|
||||
:form form
|
||||
:name :uri
|
||||
:label (tr "modals.create-webhook.url.label")
|
||||
:placeholder (tr "modals.create-webhook.url.placeholder")}]]
|
||||
|
||||
[:div.fields-row
|
||||
[:& fm/select {:options mtypes
|
||||
:label (tr "dashboard.webhooks.content-type")
|
||||
:default "application/json"
|
||||
:name :mtype}]]]
|
||||
[:div.fields-row
|
||||
[:div.input-checkbox.check-primary
|
||||
[:& fm/input {:type "checkbox"
|
||||
:form form
|
||||
:name :is-active
|
||||
:label (tr "dashboard.webhooks.active")}]
|
||||
]
|
||||
[:div.explain (tr "dashboard.webhooks.active.explain")]]]
|
||||
|
||||
|
||||
|
||||
[:div.modal-footer
|
||||
[:div.action-buttons
|
||||
[:input.btn-gray.btn-large
|
||||
{:type "button"
|
||||
:value (tr "labels.cancel")
|
||||
:on-click #(modal/hide!)}]
|
||||
[:& fm/submit-button
|
||||
{:label (if webhook
|
||||
(tr "modals.edit-webhook.submit-label")
|
||||
(tr "modals.create-webhook.submit-label"))}]]]]]]))
|
||||
|
||||
|
||||
(mf/defc webhooks-hero
|
||||
[]
|
||||
[:div.banner
|
||||
[:div.title (tr "labels.webhooks")
|
||||
[:div.description (tr "dashboard.webhooks.description")]]
|
||||
[:div.create-container
|
||||
[:div.create (tr "dashboard.webhooks.create")]]]
|
||||
|
||||
[:div.webhooks-hero-container
|
||||
[:div.webhooks-hero
|
||||
[:div.desc
|
||||
[:h2 (tr "labels.webhooks")]
|
||||
[:& i18n/tr-html {:label "dashboard.webhooks.description"}]]
|
||||
|
||||
[:div.btn-primary
|
||||
{:on-click #(st/emit! (modal/show :webhook {}))}
|
||||
[:span (tr "dashboard.webhooks.create")]]]])
|
||||
|
||||
|
||||
(mf/defc webhook-actions
|
||||
[{:keys [on-edit on-delete] :as props}]
|
||||
(let [show? (mf/use-state false)]
|
||||
[:*
|
||||
[:span.icon {:on-click #(reset! show? true)} [i/actions]]
|
||||
[:& dropdown {:show @show?
|
||||
:on-close #(reset! show? false)}
|
||||
[:ul.dropdown.actions-dropdown
|
||||
[:li {:on-click on-edit} (tr "labels.edit")]
|
||||
[:li {:on-click on-delete} (tr "labels.delete")]]]]))
|
||||
|
||||
(mf/defc last-delivery-icon
|
||||
[{:keys [success? text] :as props}]
|
||||
[:div.last-delivery-icon
|
||||
[:div.tooltip
|
||||
[:div.label text]
|
||||
[:div.arrow-down]]
|
||||
(if success?
|
||||
[:span.icon.success i/msg-success]
|
||||
[:span.icon.failure i/msg-warning])])
|
||||
|
||||
(mf/defc webhook-item
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [webhook] :as props}]
|
||||
(let [on-edit #(st/emit! (modal/show :webhook {:webhook webhook}))
|
||||
error-code (:error-code webhook)
|
||||
extract-status
|
||||
(fn [error-code]
|
||||
(let [status (-> error-code
|
||||
(str/split "-")
|
||||
last
|
||||
parse-long)]
|
||||
(if (nil? status)
|
||||
""
|
||||
status)))
|
||||
delete-fn
|
||||
(fn []
|
||||
(let [params {:id (:id webhook)}
|
||||
mdata {:on-success #(st/emit! (dd/fetch-team-webhooks))}]
|
||||
(st/emit! (dd/delete-team-webhook (with-meta params mdata)))))
|
||||
on-delete #(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-webhook.title")
|
||||
:message (tr "modals.delete-webhook.message")
|
||||
:accept-label (tr "modals.delete-webhook.accept")
|
||||
:on-accept delete-fn}))
|
||||
last-delivery-text (cond
|
||||
(nil? error-code)
|
||||
(tr "webhooks.last-delivery.success")
|
||||
|
||||
(= error-code "ssl-validation")
|
||||
(str (tr "errors.webhooks.last-delivery") " " (tr "errors.webhooks.ssl-validation"))
|
||||
|
||||
(str/starts-with? error-code "unexpected-status")
|
||||
(str (tr "errors.webhooks.last-delivery")
|
||||
" "
|
||||
(tr "errors.webhooks.unexpected-status" (extract-status error-code)))
|
||||
|
||||
:else
|
||||
(tr "errors.webhooks.last-delivery"))]
|
||||
[:div.table-row
|
||||
[:div.table-field.last-delivery
|
||||
[:div.icon-container
|
||||
[:& last-delivery-icon {:success? (nil? error-code) :text last-delivery-text}]]]
|
||||
[:div.table-field.uri
|
||||
[:div (:uri webhook)]]
|
||||
[:div.table-field.active
|
||||
[:div (if (:is-active webhook) (tr "labels.active") (tr "labels.inactive"))]]
|
||||
[:div.table-field.actions
|
||||
[:& webhook-actions {:on-edit on-edit
|
||||
:on-delete on-delete}]]]))
|
||||
|
||||
|
||||
(mf/defc webhooks-list
|
||||
[{:keys [webhooks] :as props}]
|
||||
[:div.dashboard-table
|
||||
[:div.table-rows
|
||||
(for [webhook webhooks]
|
||||
[:& webhook-item {:webhook webhook :key (:id webhook)}])]])
|
||||
|
||||
(mf/defc team-webhooks-page
|
||||
[{:keys [team] :as props}]
|
||||
(let [webhooks (mf/deref refs/dashboard-team-webhooks)]
|
||||
|
||||
(mf/with-effect [team]
|
||||
(dom/set-html-title
|
||||
(tr "title.team-webhooks"
|
||||
(if (:is-default team)
|
||||
(tr "dashboard.your-penpot")
|
||||
(:name team)))))
|
||||
|
||||
(mf/with-effect [team]
|
||||
(st/emit! (dd/fetch-team-webhooks)))
|
||||
|
||||
[:*
|
||||
[:& header {:team team :section :dashboard-team-webhooks}]
|
||||
[:section.dashboard-container.dashboard-team-webhooks
|
||||
[:div
|
||||
[:& webhooks-hero]
|
||||
(if (empty? webhooks)
|
||||
[:div.webhooks-empty
|
||||
[:div (tr "dashboard.webhooks.empty.no-webhooks")]
|
||||
[:div (tr "dashboard.webhooks.empty.add-one")]]
|
||||
[:& webhooks-list {:webhooks webhooks}])]]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SETTINGS SECTION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
["/dashboard/team/:team-id"
|
||||
["/members" :dashboard-team-members]
|
||||
["/invitations" :dashboard-team-invitations]
|
||||
["/webhooks" :dashboard-team-webhooks]
|
||||
["/settings" :dashboard-team-settings]
|
||||
["/projects" :dashboard-projects]
|
||||
["/search" :dashboard-search]
|
||||
|
|
|
@ -669,6 +669,51 @@ msgstr "Your name"
|
|||
msgid "dashboard.your-penpot"
|
||||
msgstr "Your Penpot"
|
||||
|
||||
msgid "dashboard.webhooks.description"
|
||||
msgstr "Webhooks are a simple way to allow other websites and apps to be notified when certain events happen at Penpot. We’ll send a POST request to each of the URLs you provide."
|
||||
|
||||
msgid "dashboard.webhooks.create"
|
||||
msgstr "Create webhook"
|
||||
|
||||
msgid "dashboard.webhooks.empty.no-webhooks"
|
||||
msgstr "No webhooks created so far."
|
||||
|
||||
msgid "dashboard.webhooks.empty.add-one"
|
||||
msgstr "Press the button \"Add webhook\" to add one."
|
||||
|
||||
msgid "dashboard.webhooks.content-type"
|
||||
msgstr "Content type"
|
||||
|
||||
msgid "dashboard.webhooks.active"
|
||||
msgstr "Is active"
|
||||
|
||||
msgid "dashboard.webhooks.active.explain"
|
||||
msgstr "When this hook is triggered event details will be delivered"
|
||||
|
||||
msgid "webhooks.last-delivery.success"
|
||||
msgstr "Last delivery was successfull."
|
||||
|
||||
msgid "errors.webhooks.last-delivery"
|
||||
msgstr "Last delivery was not successfull."
|
||||
|
||||
msgid "errors.webhooks.ssl-validation"
|
||||
msgstr "Error on SSL validation."
|
||||
|
||||
msgid "errors.webhooks.unexpected-status"
|
||||
msgstr "Unexpected status %s"
|
||||
|
||||
msgid "dashboard.webhooks.update.error"
|
||||
msgstr "Error on updating webhook."
|
||||
|
||||
msgid "dashboard.webhooks.update.success"
|
||||
msgstr "Webhook updated successfully."
|
||||
|
||||
msgid "dashboard.webhooks.create.error"
|
||||
msgstr "Error on creating webhook."
|
||||
|
||||
msgid "dashboard.webhooks.create.success"
|
||||
msgstr "Webhook created successfully."
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-ok"
|
||||
msgstr "Ok"
|
||||
|
@ -1468,6 +1513,15 @@ msgstr "(you)"
|
|||
msgid "labels.your-account"
|
||||
msgstr "Your account"
|
||||
|
||||
msgid "labels.webhooks"
|
||||
msgstr "Webhooks"
|
||||
|
||||
msgid "labels.active"
|
||||
msgstr "Active"
|
||||
|
||||
msgid "labels.inactive"
|
||||
msgstr "Inactive"
|
||||
|
||||
#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs
|
||||
msgid "media.loading"
|
||||
msgstr "Loading image…"
|
||||
|
@ -1682,6 +1736,33 @@ msgstr "Are you sure you want to delete this member from the team?"
|
|||
msgid "modals.delete-team-member-confirm.title"
|
||||
msgstr "Delete team member"
|
||||
|
||||
msgid "modals.delete-webhook.title"
|
||||
msgstr "Deleting webhook"
|
||||
|
||||
msgid "modals.delete-webhook.message"
|
||||
msgstr "Are you sure you want to delete this webhook?"
|
||||
|
||||
msgid "modals.delete-webhook.accept"
|
||||
msgstr "Delete webhook"
|
||||
|
||||
msgid "modals.create-webhook.title"
|
||||
msgstr "Create webhook"
|
||||
|
||||
msgid "modals.create-webhook.submit-label"
|
||||
msgstr "Create webhook"
|
||||
|
||||
msgid "modals.create-webhook.url.label"
|
||||
msgstr "Payload URL"
|
||||
|
||||
msgid "modals.create-webhook.url.placeholder"
|
||||
msgstr "https://example.com/postreceive"
|
||||
|
||||
msgid "modals.edit-webhook.title"
|
||||
msgstr "Edit webhook"
|
||||
|
||||
msgid "modals.edit-webhook.submit-label"
|
||||
msgstr "Edit webhook"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "modals.invite-member-confirm.accept"
|
||||
msgstr "Send invitation"
|
||||
|
@ -2489,6 +2570,9 @@ msgstr "Profile - Penpot"
|
|||
msgid "title.team-invitations"
|
||||
msgstr "Invitations - %s - Penpot"
|
||||
|
||||
msgid "title.team-webhooks"
|
||||
msgstr "Webhooks - %s - Penpot"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "title.team-members"
|
||||
msgstr "Members - %s - Penpot"
|
||||
|
|
|
@ -713,6 +713,51 @@ msgstr "Tu nombre"
|
|||
msgid "dashboard.your-penpot"
|
||||
msgstr "Tu Penpot"
|
||||
|
||||
msgid "dashboard.webhooks.description"
|
||||
msgstr "Los webhooks son una forma simple de permitir notificar a otros sitios web y aplicaciones cuando ocurren ciertos eventos en Penpot. Enviaremos una petición POST a cada una de las URLs que indiques."
|
||||
|
||||
msgid "dashboard.webhooks.create"
|
||||
msgstr "Crear webhook"
|
||||
|
||||
msgid "dashboard.webhooks.empty.no-webhooks"
|
||||
msgstr "No hay ningún webhook aún."
|
||||
|
||||
msgid "dashboard.webhooks.empty.add-one"
|
||||
msgstr "Pulsa el botón \"Crear webhook\" para añadir uno."
|
||||
|
||||
msgid "dashboard.webhooks.content-type"
|
||||
msgstr "Tipo de contenido"
|
||||
|
||||
msgid "dashboard.webhooks.active"
|
||||
msgstr "Activo"
|
||||
|
||||
msgid "dashboard.webhooks.active.explain"
|
||||
msgstr "Cuando se active este webhook se enviarán detalles del evento"
|
||||
|
||||
msgid "webhooks.last-delivery.success"
|
||||
msgstr "El último envío fue correcto."
|
||||
|
||||
msgid "errors.webhooks.last-delivery"
|
||||
msgstr "Hubo un problema en el último envío."
|
||||
|
||||
msgid "errors.webhooks.ssl-validation"
|
||||
msgstr "Error en la validación SSL."
|
||||
|
||||
msgid "errors.webhooks.unexpected-status"
|
||||
msgstr "Estado inesperado %s"
|
||||
|
||||
msgid "dashboard.webhooks.update.error"
|
||||
msgstr "Error modificando el webhook"
|
||||
|
||||
msgid "dashboard.webhooks.update.success"
|
||||
msgstr "Webhook modificado con éxito"
|
||||
|
||||
msgid "dashboard.webhooks.create.error"
|
||||
msgstr "Error creando con éxito"
|
||||
|
||||
msgid "dashboard.webhooks.create.success"
|
||||
msgstr "Webhook creado con éxito"
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-ok"
|
||||
msgstr "Ok"
|
||||
|
@ -1653,6 +1698,15 @@ msgstr "(tú)"
|
|||
msgid "labels.your-account"
|
||||
msgstr "Tu cuenta"
|
||||
|
||||
msgid "labels.webhooks"
|
||||
msgstr "Webhooks"
|
||||
|
||||
msgid "labels.active"
|
||||
msgstr "Activo"
|
||||
|
||||
msgid "labels.inactive"
|
||||
msgstr "Inactivo"
|
||||
|
||||
#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs
|
||||
msgid "media.loading"
|
||||
msgstr "Cargando imagen…"
|
||||
|
@ -1880,6 +1934,33 @@ msgstr "¿Seguro que quieres eliminar este integrante del equipo?"
|
|||
msgid "modals.delete-team-member-confirm.title"
|
||||
msgstr "Eliminar integrante del equipo"
|
||||
|
||||
msgid "modals.delete-webhook.title"
|
||||
msgstr "Borrando webhook"
|
||||
|
||||
msgid "modals.delete-webhook.message"
|
||||
msgstr "¿Seguro que quieres borrar este webhook?"
|
||||
|
||||
msgid "modals.delete-webhook.accept"
|
||||
msgstr "Borrar webhook"
|
||||
|
||||
msgid "modals.create-webhook.title"
|
||||
msgstr "Crear webhook"
|
||||
|
||||
msgid "modals.create-webhook.submit-label"
|
||||
msgstr "Crear webhook"
|
||||
|
||||
msgid "modals.create-webhook.url.label"
|
||||
msgstr "Payload URL"
|
||||
|
||||
msgid "modals.create-webhook.url.placeholder"
|
||||
msgstr "https://example.com/postreceive"
|
||||
|
||||
msgid "modals.edit-webhook.title"
|
||||
msgstr "Modificar webhook"
|
||||
|
||||
msgid "modals.edit-webhook.submit-label"
|
||||
msgstr "Modificar webhook"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "modals.invite-member-confirm.accept"
|
||||
msgstr "Enviar invitacion"
|
||||
|
@ -2818,6 +2899,9 @@ msgstr "Perfil - Penpot"
|
|||
msgid "title.team-invitations"
|
||||
msgstr "Invitaciones - %s - Penpot"
|
||||
|
||||
msgid "title.team-webhooks"
|
||||
msgstr "Webhooks - %s - Penpot"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "title.team-members"
|
||||
msgstr "Integrantes - %s - Penpot"
|
||||
|
|
Loading…
Add table
Reference in a new issue