mirror of
https://github.com/penpot/penpot.git
synced 2025-01-23 23:18:48 -05:00
📎 Add more events instrumentation
This commit is contained in:
parent
bf6211903c
commit
8acc9af1f5
10 changed files with 216 additions and 189 deletions
|
@ -180,17 +180,6 @@
|
|||
|
||||
;; --- HTTP HANDLERS
|
||||
|
||||
(defn extract-utm-props
|
||||
"Extracts additional data from user params."
|
||||
[params]
|
||||
(reduce-kv (fn [params k v]
|
||||
(let [sk (name k)]
|
||||
(cond-> params
|
||||
(str/starts-with? sk "utm_")
|
||||
(assoc (->> sk str/kebab (keyword "penpot")) v))))
|
||||
{}
|
||||
params))
|
||||
|
||||
(defn- retrieve-profile
|
||||
[{:keys [pool executor] :as cfg} info]
|
||||
(px/with-dispatch executor
|
||||
|
@ -252,7 +241,7 @@
|
|||
(defn- auth-handler
|
||||
[{:keys [tokens] :as cfg} {:keys [params] :as request} respond raise]
|
||||
(try
|
||||
(let [props (extract-utm-props params)
|
||||
(let [props (audit/extract-utm-params params)
|
||||
state (tokens :generate
|
||||
{:iss :oauth
|
||||
:invitation-token (:invitation-token params)
|
||||
|
|
|
@ -34,6 +34,20 @@
|
|||
(yrq/get-header request "x-real-ip")
|
||||
(yrq/remote-addr request)))
|
||||
|
||||
(defn extract-utm-params
|
||||
"Extracts additional data from params and namespace them under
|
||||
`penpot` ns."
|
||||
[params]
|
||||
(letfn [(process-param [params k v]
|
||||
(let [sk (d/name k)]
|
||||
(cond-> params
|
||||
(str/starts-with? sk "utm_")
|
||||
(assoc (->> sk str/kebab (keyword "penpot")) v)
|
||||
|
||||
(str/starts-with? sk "mtm_")
|
||||
(assoc (->> sk str/kebab (keyword "penpot")) v))))]
|
||||
(reduce-kv process-param {} params)))
|
||||
|
||||
(defn profile->props
|
||||
[profile]
|
||||
(-> profile
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.emails :as eml]
|
||||
[app.http.oauth :refer [extract-utm-props]]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.media :as media]
|
||||
[app.rpc.mutations.teams :as teams]
|
||||
|
@ -223,7 +222,7 @@
|
|||
[conn params]
|
||||
(let [id (or (:id params) (uuid/next))
|
||||
|
||||
props (-> (extract-utm-props params)
|
||||
props (-> (audit/extract-utm-params params)
|
||||
(merge (:props params))
|
||||
(db/tjson))
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.emails :as eml]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.media :as media]
|
||||
[app.rpc.mutations.projects :as projects]
|
||||
[app.rpc.permissions :as perms]
|
||||
|
@ -357,14 +358,14 @@
|
|||
:opt-un [::email ::emails]))
|
||||
|
||||
(sv/defmethod ::invite-team-member
|
||||
"A rpc call that allow to send a single or multiple invitations to
|
||||
join the team."
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id email emails role] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [perms (teams/get-permissions conn profile-id team-id)
|
||||
profile (db/get-by-id conn :profile profile-id)
|
||||
team (db/get-by-id conn :team team-id)
|
||||
emails (or emails #{})
|
||||
emails (if email (conj emails email) emails)
|
||||
]
|
||||
emails (cond-> (or emails #{}) (string? email) (conj email))]
|
||||
|
||||
(when-not (:is-admin perms)
|
||||
(ex/raise :type :validation
|
||||
|
@ -385,7 +386,9 @@
|
|||
:profile profile
|
||||
:role role))
|
||||
)
|
||||
nil)))
|
||||
|
||||
(with-meta {}
|
||||
{::audit/props {:invitations (count emails)}}))))
|
||||
|
||||
(def sql:upsert-team-invitation
|
||||
"insert into team_invitation(team_id, email_to, role, valid_until)
|
||||
|
@ -395,19 +398,19 @@
|
|||
|
||||
(defn- create-team-invitation
|
||||
[{:keys [conn tokens team profile role email] :as cfg}]
|
||||
(let [member (profile/retrieve-profile-data-by-email conn email)
|
||||
(let [member (profile/retrieve-profile-data-by-email conn email)
|
||||
token-exp (dt/in-future "48h")
|
||||
itoken (tokens :generate
|
||||
{:iss :team-invitation
|
||||
:exp token-exp
|
||||
:profile-id (:id profile)
|
||||
:role role
|
||||
:team-id (:id team)
|
||||
:member-email (:email member email)
|
||||
:member-id (:id member)})
|
||||
ptoken (tokens :generate-predefined
|
||||
{:iss :profile-identity
|
||||
:profile-id (:id profile)})]
|
||||
itoken (tokens :generate
|
||||
{:iss :team-invitation
|
||||
:exp token-exp
|
||||
:profile-id (:id profile)
|
||||
:role role
|
||||
:team-id (:id team)
|
||||
:member-email (:email member email)
|
||||
:member-id (:id member)})
|
||||
ptoken (tokens :generate-predefined
|
||||
{:iss :profile-identity
|
||||
:profile-id (:id profile)})]
|
||||
|
||||
(when (and member (not (eml/allow-send-emails? conn member)))
|
||||
(ex/raise :type :validation
|
||||
|
@ -443,21 +446,14 @@
|
|||
(s/and ::create-team (s/keys :req-un [::emails ::role])))
|
||||
|
||||
(sv/defmethod ::create-team-and-invite-members
|
||||
[{:keys [pool audit] :as cfg} {:keys [profile-id emails role] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id emails role] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [team (create-team conn params)
|
||||
profile (db/get-by-id conn :profile profile-id)]
|
||||
(let [team (create-team conn params)
|
||||
audit-fn (:audit cfg)
|
||||
profile (db/get-by-id conn :profile profile-id)]
|
||||
|
||||
;; Create invitations for all provided emails.
|
||||
(doseq [email emails]
|
||||
(audit :cmd :submit
|
||||
:type "mutation"
|
||||
:name "create-team-invitation"
|
||||
:profile-id profile-id
|
||||
:props {:email email
|
||||
:role role
|
||||
:profile-id profile-id})
|
||||
|
||||
(create-team-invitation
|
||||
(assoc cfg
|
||||
:conn conn
|
||||
|
@ -465,8 +461,17 @@
|
|||
:profile profile
|
||||
:email email
|
||||
:role role)))
|
||||
team)))
|
||||
|
||||
(with-meta team
|
||||
{:before-complete
|
||||
#(audit-fn :cmd :submit
|
||||
:type "mutation"
|
||||
:name "invite-team-member"
|
||||
:profile-id profile-id
|
||||
:props {:emails emails
|
||||
:role role
|
||||
:profile-id profile-id
|
||||
:invitations (count emails)})}))))
|
||||
|
||||
;; --- Mutation: Update invitation role
|
||||
|
||||
|
|
|
@ -44,16 +44,15 @@
|
|||
|
||||
;; (th/print-result! out)
|
||||
|
||||
(t/is (nil? (:result out)))
|
||||
(t/is (= {} (:result out)))
|
||||
(t/is (= 1 (:call-count (deref mock))))
|
||||
(t/is (= 1 (:num invitation))))
|
||||
|
||||
|
||||
;; invite internal user without complaints
|
||||
(th/reset-mock! mock)
|
||||
(let [data (assoc data :email (:email profile2))
|
||||
out (th/mutation! data)]
|
||||
(t/is (nil? (:result out)))
|
||||
(t/is (= {} (:result out)))
|
||||
(t/is (= 1 (:call-count (deref mock)))))
|
||||
|
||||
;; invite user with complaint
|
||||
|
@ -61,7 +60,7 @@
|
|||
(th/reset-mock! mock)
|
||||
(let [data (assoc data :email "foo@bar.com")
|
||||
out (th/mutation! data)]
|
||||
(t/is (nil? (:result out)))
|
||||
(t/is (= {} (:result out)))
|
||||
(t/is (= 1 (:call-count (deref mock)))))
|
||||
|
||||
;; invite user with bounce
|
||||
|
|
|
@ -426,21 +426,21 @@
|
|||
(rx/tap #(tm/schedule on-success))
|
||||
(rx/catch on-error))))))
|
||||
|
||||
(defn invite-team-member
|
||||
[{:keys [emails role] :as params}]
|
||||
(defn invite-team-members
|
||||
[{:keys [emails role team-id resend?] :as params}]
|
||||
(us/assert ::us/set-of-emails emails)
|
||||
(us/assert ::us/keyword role)
|
||||
(ptk/reify ::invite-team-member
|
||||
(us/assert ::us/uuid team-id)
|
||||
(ptk/reify ::invite-team-members
|
||||
IDeref
|
||||
(-deref [_] {:role role})
|
||||
(-deref [_] {:role role :team-id team-id :resend? resend?})
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(watch [_ _ _]
|
||||
(let [{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)
|
||||
team-id (:current-team-id state)
|
||||
params (assoc params :team-id team-id)]
|
||||
params (dissoc params :resend?)]
|
||||
(->> (rp/mutation! :invite-team-member params)
|
||||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
(derive :app.main.data.dashboard/delete-team-member ::generic-action)
|
||||
(derive :app.main.data.dashboard/duplicate-project ::generic-action)
|
||||
(derive :app.main.data.dashboard/file-created ::generic-action)
|
||||
(derive :app.main.data.dashboard/invite-team-member ::generic-action)
|
||||
(derive :app.main.data.dashboard/invite-team-members ::generic-action)
|
||||
(derive :app.main.data.dashboard/leave-team ::generic-action)
|
||||
(derive :app.main.data.dashboard/move-files ::generic-action)
|
||||
(derive :app.main.data.dashboard/move-project ::generic-action)
|
||||
|
@ -113,6 +113,7 @@
|
|||
(derive :app.main.data.workspace.persistence/attach-library ::generic-action)
|
||||
(derive :app.main.data.workspace.persistence/detach-library ::generic-action)
|
||||
(derive :app.main.data.workspace.persistence/set-file-shard ::generic-action)
|
||||
(derive :app.main.data.workspace.selection/toggle-focus-mode ::generic-action)
|
||||
(derive :app.main.data.workspace/create-page ::generic-action)
|
||||
(derive :app.main.data.workspace/set-workspace-layout ::generic-action)
|
||||
(derive :app.main.data.workspace/toggle-layout-flag ::generic-action)
|
||||
|
|
|
@ -293,29 +293,6 @@
|
|||
(rx/catch (constantly (rx/of 1)))
|
||||
(rx/map #(logged-out params)))))))
|
||||
|
||||
;; --- EVENT: register
|
||||
|
||||
(s/def ::register
|
||||
(s/keys :req-un [::fullname ::password ::email]))
|
||||
|
||||
(defn register
|
||||
"Create a register event instance."
|
||||
[data]
|
||||
(s/assert ::register data)
|
||||
(ptk/reify ::register
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [{:keys [on-error on-success]
|
||||
:or {on-error identity
|
||||
on-success identity}} (meta data)]
|
||||
(->> (rp/mutation :register-profile data)
|
||||
(rx/tap on-success)
|
||||
(rx/catch on-error))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(swap! storage dissoc ::redirect-to))))
|
||||
|
||||
;; --- Update Profile
|
||||
|
||||
(defn update-profile
|
||||
|
|
|
@ -68,8 +68,8 @@
|
|||
(st/emit! (dm/error (tr "errors.generic")))))
|
||||
|
||||
(defn- handle-prepare-register-success
|
||||
[_form {:keys [token] :as result}]
|
||||
(st/emit! (rt/nav :auth-register-validate {} {:token token})))
|
||||
[_ params]
|
||||
(st/emit! (rt/nav :auth-register-validate {} params)))
|
||||
|
||||
(mf/defc register-form
|
||||
[{:keys [params] :as props}]
|
||||
|
@ -83,8 +83,9 @@
|
|||
(mf/use-callback
|
||||
(fn [form _event]
|
||||
(reset! submitted? true)
|
||||
(let [params (:clean-data @form)]
|
||||
(->> (rp/mutation :prepare-register-profile params)
|
||||
(let [cdata (:clean-data @form)]
|
||||
(->> (rp/mutation :prepare-register-profile cdata)
|
||||
(rx/map #(merge % params))
|
||||
(rx/finalize #(reset! submitted? false))
|
||||
(rx/subs (partial handle-prepare-register-success form)
|
||||
(partial handle-prepare-register-error form))))))
|
||||
|
@ -160,13 +161,6 @@
|
|||
(defn- handle-register-error
|
||||
[form error]
|
||||
(case (:code error)
|
||||
:registration-disabled
|
||||
(st/emit! (dm/error (tr "errors.registration-disabled")))
|
||||
|
||||
:email-has-permanent-bounces
|
||||
(let [email (get @form [:data :email])]
|
||||
(st/emit! (dm/error (tr "errors.email-has-permanent-bounces" email))))
|
||||
|
||||
:email-already-exists
|
||||
(swap! form assoc-in [:errors :email]
|
||||
{:message "errors.email-already-exists"})
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
(ns app.main.ui.dashboard.team
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.spec :as us]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.messages :as msg]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -27,15 +28,14 @@
|
|||
[cljs.spec.alpha :as s]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
;; TEAM SECTION
|
||||
|
||||
(mf/defc header
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [section team] :as props}]
|
||||
(let [go-members (st/emitf (dd/go-to-team-members))
|
||||
go-settings (st/emitf (dd/go-to-team-settings))
|
||||
go-invitations (st/emitf (dd/go-to-team-invitations))
|
||||
invite-member (st/emitf (modal/show {:type ::invite-member :team team}))
|
||||
(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 #(st/emit! (modal/show {:type :invite-members :team team})))
|
||||
|
||||
members-section? (= section :dashboard-team-members)
|
||||
settings-section? (= section :dashboard-team-settings)
|
||||
invitations-section? (= section :dashboard-team-invitations)
|
||||
|
@ -62,12 +62,16 @@
|
|||
(tr "dashboard.invite-profile")]
|
||||
[:div.blank-space])]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; INVITATIONS MODAL
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn get-available-roles
|
||||
[permissions]
|
||||
(->> [{:value "editor" :label (tr "labels.editor")}
|
||||
(when (:is-admin permissions)
|
||||
{:value "admin" :label (tr "labels.admin")})
|
||||
;; Temporarily disabled viewer role
|
||||
;; Temporarily disabled viewer roles
|
||||
;; https://tree.taiga.io/project/uxboxproject/issue/1083
|
||||
;; {:value "viewer" :label (tr "labels.viewer")}
|
||||
]
|
||||
|
@ -75,31 +79,34 @@
|
|||
|
||||
(s/def ::emails (s/and ::us/set-of-emails d/not-empty?))
|
||||
(s/def ::role ::us/keyword)
|
||||
(s/def ::invite-member-form
|
||||
(s/keys :req-un [::role ::emails]))
|
||||
(s/def ::team-id ::us/uuid)
|
||||
|
||||
(mf/defc invite-member-modal
|
||||
(s/def ::invite-member-form
|
||||
(s/keys :req-un [::role ::emails ::team-id]))
|
||||
|
||||
(mf/defc invite-members-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as ::invite-member}
|
||||
::mf/register-as :invite-members}
|
||||
[{:keys [team]}]
|
||||
(let [perms (:permissions team)
|
||||
roles (mf/use-memo (mf/deps perms) #(get-available-roles perms))
|
||||
initial (mf/use-memo (constantly {:role "editor"}))
|
||||
initial (mf/use-memo (constantly {:role "editor" :team-id (:id team)}))
|
||||
form (fm/use-form :spec ::invite-member-form
|
||||
:initial initial)
|
||||
error-text (mf/use-state "")
|
||||
|
||||
on-success
|
||||
(st/emitf (dm/success (tr "notifications.invitation-email-sent"))
|
||||
(modal/hide)
|
||||
(dd/fetch-team-invitations))
|
||||
(fn []
|
||||
(st/emit! (msg/success (tr "notifications.invitation-email-sent"))
|
||||
(modal/hide)
|
||||
(dd/fetch-team-invitations)))
|
||||
|
||||
on-error
|
||||
(fn [{:keys [type code] :as error}]
|
||||
(cond
|
||||
(and (= :validation type)
|
||||
(= :profile-is-muted code))
|
||||
(st/emit! (dm/error (tr "errors.profile-is-muted"))
|
||||
(st/emit! (msg/error (tr "errors.profile-is-muted"))
|
||||
(modal/hide))
|
||||
|
||||
(and (= :validation type)
|
||||
|
@ -108,7 +115,7 @@
|
|||
(swap! error-text (tr "errors.email-spam-or-permanent-bounces" (:email error)))
|
||||
|
||||
:else
|
||||
(st/emit! (dm/error (tr "errors.generic"))
|
||||
(st/emit! (msg/error (tr "errors.generic"))
|
||||
(modal/hide))))
|
||||
|
||||
on-submit
|
||||
|
@ -116,9 +123,9 @@
|
|||
(let [params (:clean-data @form)
|
||||
mdata {:on-success (partial on-success form)
|
||||
:on-error (partial on-error form)}]
|
||||
(st/emit! (dd/invite-team-member (with-meta params mdata))
|
||||
(st/emit! (dd/invite-team-members (with-meta params mdata))
|
||||
(dd/fetch-team-invitations))))]
|
||||
|
||||
|
||||
[:div.modal.dashboard-invite-modal.form-container
|
||||
[:& fm/form {:on-submit on-submit :form form}
|
||||
[:div.title
|
||||
|
@ -141,7 +148,9 @@
|
|||
[:div.action-buttons
|
||||
[:& fm/submit-button {:label (tr "modals.invite-member-confirm.accept")}]]]]))
|
||||
|
||||
;; TEAM MEMBERS SECTION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; MEMBERS SECTION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mf/defc member-info [{:keys [member profile] :as props}]
|
||||
(let [is-you? (= (:id profile) (:id member))]
|
||||
|
@ -210,101 +219,126 @@
|
|||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [team member members profile] :as props}]
|
||||
|
||||
(let [set-role
|
||||
(fn [role]
|
||||
(let [params {:member-id (:id member) :role role}]
|
||||
(st/emit! (dd/update-team-member-role params))))
|
||||
owner? (get-in team [:permissions :is-owner])
|
||||
(let [owner? (dm/get-in team [:permissions :is-owner])
|
||||
set-role
|
||||
(mf/use-fn
|
||||
(mf/deps member)
|
||||
(fn [role]
|
||||
(let [params {:member-id (:id member) :role role}]
|
||||
(st/emit! (dd/update-team-member-role params)))))
|
||||
|
||||
set-owner-fn (partial set-role :owner)
|
||||
set-admin (partial set-role :admin)
|
||||
set-editor (partial set-role :editor)
|
||||
|
||||
set-owner-fn (mf/use-fn (mf/deps set-role) (partial set-role :owner))
|
||||
set-admin (mf/use-fn (mf/deps set-role) (partial set-role :admin))
|
||||
set-editor (mf/use-fn (mf/deps set-role) (partial set-role :editor))
|
||||
;; set-viewer (partial set-role :viewer)
|
||||
|
||||
set-owner
|
||||
(fn [member]
|
||||
(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.promote-owner-confirm.title")
|
||||
:message (tr "modals.promote-owner-confirm.message" (:name member))
|
||||
:scd-message (tr "modals.promote-owner-confirm.hint")
|
||||
:accept-label (tr "modals.promote-owner-confirm.accept")
|
||||
:on-accept set-owner-fn
|
||||
:accept-style :primary})))
|
||||
(mf/use-fn
|
||||
(mf/deps set-owner-fn member)
|
||||
(fn [member]
|
||||
(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.promote-owner-confirm.title")
|
||||
:message (tr "modals.promote-owner-confirm.message" (:name member))
|
||||
:scd-message (tr "modals.promote-owner-confirm.hint")
|
||||
:accept-label (tr "modals.promote-owner-confirm.accept")
|
||||
:on-accept set-owner-fn
|
||||
:accept-style :primary}))))
|
||||
|
||||
delete-member-fn
|
||||
(st/emitf (dd/delete-team-member {:member-id (:id member)}))
|
||||
(mf/use-fn
|
||||
(mf/deps member)
|
||||
(fn [] (st/emit! (dd/delete-team-member {:member-id (:id member)}))))
|
||||
|
||||
on-success
|
||||
(fn []
|
||||
(st/emit! (dd/go-to-projects (:default-team-id profile))
|
||||
(modal/hide)
|
||||
(du/fetch-teams)))
|
||||
(mf/use-fn
|
||||
(mf/deps profile)
|
||||
(fn []
|
||||
(st/emit! (dd/go-to-projects (:default-team-id profile))
|
||||
(modal/hide)
|
||||
(du/fetch-teams))))
|
||||
|
||||
on-error
|
||||
(fn [{:keys [code] :as error}]
|
||||
(condp = code
|
||||
(mf/use-fn
|
||||
(fn [{:keys [code] :as error}]
|
||||
(condp = code
|
||||
|
||||
:no-enough-members-for-leave
|
||||
(rx/of (dm/error (tr "errors.team-leave.insufficient-members")))
|
||||
:no-enough-members-for-leave
|
||||
(rx/of (msg/error (tr "errors.team-leave.insufficient-members")))
|
||||
|
||||
:member-does-not-exist
|
||||
(rx/of (dm/error (tr "errors.team-leave.member-does-not-exists")))
|
||||
:member-does-not-exist
|
||||
(rx/of (msg/error (tr "errors.team-leave.member-does-not-exists")))
|
||||
|
||||
:owner-cant-leave-team
|
||||
(rx/of (dm/error (tr "errors.team-leave.owner-cant-leave")))
|
||||
:owner-cant-leave-team
|
||||
(rx/of (msg/error (tr "errors.team-leave.owner-cant-leave")))
|
||||
|
||||
(rx/throw error)))
|
||||
(rx/throw error))))
|
||||
|
||||
delete-fn
|
||||
(fn []
|
||||
(st/emit! (dd/delete-team (with-meta team {:on-success on-success
|
||||
:on-error on-error}))))
|
||||
(mf/use-fn
|
||||
(mf/deps team on-success on-error)
|
||||
(fn []
|
||||
(st/emit! (dd/delete-team (with-meta team {:on-success on-success
|
||||
:on-error on-error})))))
|
||||
|
||||
leave-fn
|
||||
(fn [member-id]
|
||||
(let [params (cond-> {} (uuid? member-id) (assoc :reassign-to member-id))]
|
||||
(st/emit! (dd/leave-team (with-meta params
|
||||
{:on-success on-success
|
||||
:on-error on-error})))))
|
||||
(mf/use-fn
|
||||
(mf/deps on-success on-error)
|
||||
(fn [member-id]
|
||||
(let [params (cond-> {} (uuid? member-id) (assoc :reassign-to member-id))]
|
||||
(st/emit! (dd/leave-team (with-meta params
|
||||
{:on-success on-success
|
||||
:on-error on-error}))))))
|
||||
|
||||
leave-and-close
|
||||
(st/emitf (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-and-close-confirm.message" (:name team))
|
||||
:scd-message (tr "modals.leave-and-close-confirm.hint")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept delete-fn}))
|
||||
(mf/use-fn
|
||||
(mf/deps delete-fn)
|
||||
(fn []
|
||||
(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-and-close-confirm.message" (:name team))
|
||||
:scd-message (tr "modals.leave-and-close-confirm.hint")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept delete-fn}))))
|
||||
|
||||
change-owner-and-leave
|
||||
(fn []
|
||||
(st/emit! (dd/fetch-team-members)
|
||||
(modal/show
|
||||
{:type :leave-and-reassign
|
||||
:profile profile
|
||||
:team team
|
||||
:accept leave-fn})))
|
||||
(mf/use-fn
|
||||
(mf/deps profile team leave-fn)
|
||||
(fn []
|
||||
(st/emit! (dd/fetch-team-members)
|
||||
(modal/show
|
||||
{:type :leave-and-reassign
|
||||
:profile profile
|
||||
:team team
|
||||
:accept leave-fn}))))
|
||||
|
||||
leave
|
||||
(st/emitf (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-confirm.message")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept leave-fn}))
|
||||
(mf/use-fn
|
||||
(mf/deps leave-fn)
|
||||
(fn []
|
||||
(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-confirm.message")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept leave-fn}))))
|
||||
|
||||
preset-leave (cond (= 1 (count members)) leave-and-close
|
||||
(= true owner?) change-owner-and-leave
|
||||
:else leave)
|
||||
|
||||
delete
|
||||
(st/emitf (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-team-member-confirm.title")
|
||||
:message (tr "modals.delete-team-member-confirm.message")
|
||||
:accept-label (tr "modals.delete-team-member-confirm.accept")
|
||||
:on-accept delete-member-fn}))]
|
||||
(mf/use-fn
|
||||
(mf/deps delete-member-fn)
|
||||
(fn []
|
||||
(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-team-member-confirm.title")
|
||||
:message (tr "modals.delete-team-member-confirm.message")
|
||||
:accept-label (tr "modals.delete-team-member-confirm.accept")
|
||||
:on-accept delete-member-fn}))))]
|
||||
|
||||
[:div.table-row
|
||||
[:div.table-field.name
|
||||
|
@ -361,7 +395,9 @@
|
|||
:team team
|
||||
:members-map members-map}]]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; INVITATIONS SECTION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mf/defc invitation-role-selector
|
||||
[{:keys [can-invite? role status change-to-admin change-to-editor] :as props}]
|
||||
|
@ -418,7 +454,7 @@
|
|||
:pending)
|
||||
|
||||
on-success
|
||||
#(st/emit! (dm/success (tr "notifications.invitation-email-sent"))
|
||||
#(st/emit! (msg/success (tr "notifications.invitation-email-sent"))
|
||||
(modal/hide)
|
||||
(dd/fetch-team-invitations))
|
||||
|
||||
|
@ -428,18 +464,18 @@
|
|||
(cond
|
||||
(and (= :validation type)
|
||||
(= :profile-is-muted code))
|
||||
(dm/error (tr "errors.profile-is-muted"))
|
||||
(msg/error (tr "errors.profile-is-muted"))
|
||||
|
||||
(and (= :validation type)
|
||||
(= :member-is-muted code))
|
||||
(dm/error (tr "errors.member-is-muted"))
|
||||
(msg/error (tr "errors.member-is-muted"))
|
||||
|
||||
(and (= :validation type)
|
||||
(= :email-has-permanent-bounces code))
|
||||
(dm/error (tr "errors.email-has-permanent-bounces" email))
|
||||
(msg/error (tr "errors.email-has-permanent-bounces" email))
|
||||
|
||||
:else
|
||||
(dm/error (tr "errors.generic"))))
|
||||
(msg/error (tr "errors.generic"))))
|
||||
|
||||
change-rol
|
||||
(fn [role]
|
||||
|
@ -455,20 +491,31 @@
|
|||
|
||||
resend-invitation
|
||||
(fn []
|
||||
(let [params {:email email :team-id (:id team) :role invitation-role}
|
||||
(let [params {:email email
|
||||
:team-id (:id team)
|
||||
:resend? true
|
||||
:role invitation-role}
|
||||
mdata {:on-success on-success
|
||||
:on-error (partial on-error email)}]
|
||||
(st/emit! (dd/invite-team-member (with-meta params mdata)))
|
||||
(st/emit! (dd/fetch-team-invitations))))]
|
||||
(st/emit! (dd/invite-team-members (with-meta params mdata))
|
||||
(dd/fetch-team-invitations))))]
|
||||
[:div.table-row
|
||||
[:div.table-field.mail email]
|
||||
[:div.table-field.roles [:& invitation-role-selector {:can-invite? can-invite?
|
||||
:role invitation-role
|
||||
:status status
|
||||
:change-to-editor (partial change-rol :editor)
|
||||
:change-to-admin (partial change-rol :admin)}]]
|
||||
[:div.table-field.status [:& invitation-status-badge {:status status}]]
|
||||
[:div.table-field.actions [:& invitation-actions {:can-modify? can-invite? :delete delete-invitation :resend resend-invitation}]]]))
|
||||
[:div.table-field.roles
|
||||
[:& invitation-role-selector
|
||||
{:can-invite? can-invite?
|
||||
:role invitation-role
|
||||
:status status
|
||||
:change-to-editor (partial change-rol :editor)
|
||||
:change-to-admin (partial change-rol :admin)}]]
|
||||
|
||||
[:div.table-field.status
|
||||
[:& invitation-status-badge {:status status}]]
|
||||
[:div.table-field.actions
|
||||
[:& invitation-actions
|
||||
{:can-modify? can-invite?
|
||||
:delete delete-invitation
|
||||
:resend resend-invitation}]]]))
|
||||
|
||||
(mf/defc empty-invitation-table [can-invite?]
|
||||
[:div.empty-invitations
|
||||
|
@ -513,7 +560,9 @@
|
|||
[:& invitation-section {:team team
|
||||
:invitations invitations}]]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SETTINGS SECTION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mf/defc team-settings-page
|
||||
[{:keys [team] :as props}]
|
||||
|
|
Loading…
Add table
Reference in a new issue