mirror of
https://github.com/penpot/penpot.git
synced 2025-07-31 12:28:38 +02:00
🎉 Add the ability to copy team invitation link
This commit is contained in:
parent
a6b26f0563
commit
f11da06637
9 changed files with 262 additions and 135 deletions
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.media :as di]
|
||||
|
@ -18,6 +19,7 @@
|
|||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[potok.core :as ptk]))
|
||||
|
@ -403,7 +405,7 @@
|
|||
params {:name name
|
||||
:emails #{emails}
|
||||
:role role}]
|
||||
(->> (rp/cmd! :create-team-and-invitations params)
|
||||
(->> (rp/cmd! :create-team-with-invitations params)
|
||||
(rx/tap on-success)
|
||||
(rx/map team-created)
|
||||
(rx/catch on-error))))))
|
||||
|
@ -509,6 +511,36 @@
|
|||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
|
||||
(defn copy-invitation-link
|
||||
[{:keys [email team-id] :as params}]
|
||||
(us/assert! ::us/email email)
|
||||
(us/assert! ::us/uuid team-id)
|
||||
|
||||
(ptk/reify ::copy-invitation-link
|
||||
IDeref
|
||||
(-deref [_] {:email email :team-id team-id})
|
||||
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)
|
||||
router (:router state)]
|
||||
|
||||
(->> (rp/cmd! :get-team-invitation-token params)
|
||||
(rx/map (fn [params]
|
||||
(rt/resolve router :auth-verify-token {} params)))
|
||||
(rx/map (fn [fragment]
|
||||
(assoc @cf/public-uri :fragment fragment)))
|
||||
(rx/tap (fn [uri]
|
||||
(wapi/write-to-clipboard (str uri))))
|
||||
(rx/tap on-success)
|
||||
(rx/ignore)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
|
||||
(defn update-team-invitation-role
|
||||
[{:keys [email team-id role] :as params}]
|
||||
(us/assert! ::us/email email)
|
||||
|
|
|
@ -448,81 +448,113 @@
|
|||
:pending (= status :pending))}
|
||||
[:span.status-label (tr status-label)]]))
|
||||
|
||||
(mf/defc invitation-actions [{:keys [can-modify? delete resend] :as props}]
|
||||
(let [show? (mf/use-state false)]
|
||||
(when can-modify?
|
||||
[:*
|
||||
[:span.icon {:on-click #(reset! show? true)} [i/actions]]
|
||||
[:& dropdown {:show @show?
|
||||
:on-close #(reset! show? false)}
|
||||
[:ul.dropdown.actions-dropdown
|
||||
[:li {:on-click resend} (tr "labels.resend-invitation")]
|
||||
[:li {:on-click delete} (tr "labels.delete-invitation")]]]])))
|
||||
(mf/defc invitation-actions
|
||||
[{:keys [invitation team] :as props}]
|
||||
(let [show? (mf/use-state false)
|
||||
|
||||
team-id (:id team)
|
||||
email (:email invitation)
|
||||
role (:role invitation)
|
||||
|
||||
on-resend-success
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(st/emit! (msg/success (tr "notifications.invitation-email-sent"))
|
||||
(modal/hide))))
|
||||
|
||||
on-copy-success
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(st/emit! (msg/success (tr "notifications.invitation-link-copied"))
|
||||
(modal/hide))))
|
||||
|
||||
on-error
|
||||
(mf/use-fn
|
||||
(mf/deps email)
|
||||
(fn [{:keys [type code] :as error}]
|
||||
(cond
|
||||
(and (= :validation type)
|
||||
(= :profile-is-muted code))
|
||||
(rx/of (msg/error (tr "errors.profile-is-muted")))
|
||||
|
||||
(and (= :validation type)
|
||||
(= :member-is-muted code))
|
||||
(rx/of (msg/error (tr "errors.member-is-muted")))
|
||||
|
||||
(and (= :validation type)
|
||||
(= :email-has-permanent-bounces code))
|
||||
(rx/of (msg/error (tr "errors.email-has-permanent-bounces" email)))
|
||||
|
||||
:else
|
||||
(rx/throw error))))
|
||||
|
||||
delete-fn
|
||||
(mf/use-fn
|
||||
(mf/deps email team-id)
|
||||
(fn []
|
||||
(let [params {:email email :team-id team-id}
|
||||
mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}]
|
||||
(st/emit! (dd/delete-team-invitation (with-meta params mdata))))))
|
||||
|
||||
resend-fn
|
||||
(mf/use-fn
|
||||
(mf/deps email team-id)
|
||||
(fn []
|
||||
(let [params (with-meta {:emails [email]
|
||||
:team-id team-id
|
||||
:resend? true
|
||||
:role role}
|
||||
{:on-success on-resend-success
|
||||
:on-error on-error})]
|
||||
(st/emit!
|
||||
(-> (dd/invite-team-members params)
|
||||
(with-meta {::ev/origin :team}))))))
|
||||
|
||||
copy-fn
|
||||
(mf/use-fn
|
||||
(mf/deps email team-id)
|
||||
(fn []
|
||||
(let [params (with-meta {:email email :team-id team-id}
|
||||
{:on-success on-copy-success
|
||||
:on-error on-error})]
|
||||
(prn "KKK1")
|
||||
(st/emit!
|
||||
(-> (dd/copy-invitation-link params)
|
||||
(with-meta {::ev/origin :team}))))))]
|
||||
|
||||
|
||||
[:*
|
||||
[:span.icon {:on-click #(reset! show? true)} [i/actions]]
|
||||
[:& dropdown {:show @show?
|
||||
:on-close #(reset! show? false)}
|
||||
[:ul.dropdown.actions-dropdown
|
||||
[:li {:on-click copy-fn} (tr "labels.copy-invitation-link")]
|
||||
[:li {:on-click resend-fn} (tr "labels.resend-invitation")]
|
||||
[:li {:on-click delete-fn} (tr "labels.delete-invitation")]]]]))
|
||||
|
||||
(mf/defc invitation-row
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [invitation can-invite? team] :as props}]
|
||||
|
||||
(let [expired? (:expired invitation)
|
||||
email (:email invitation)
|
||||
invitation-role (:role invitation)
|
||||
status (if expired?
|
||||
:expired
|
||||
:pending)
|
||||
|
||||
on-success
|
||||
#(st/emit! (msg/success (tr "notifications.invitation-email-sent"))
|
||||
(modal/hide)
|
||||
(dd/fetch-team-invitations))
|
||||
|
||||
|
||||
on-error
|
||||
(fn [email {:keys [type code] :as error}]
|
||||
(cond
|
||||
(and (= :validation type)
|
||||
(= :profile-is-muted code))
|
||||
(msg/error (tr "errors.profile-is-muted"))
|
||||
|
||||
(and (= :validation type)
|
||||
(= :member-is-muted code))
|
||||
(msg/error (tr "errors.member-is-muted"))
|
||||
|
||||
(and (= :validation type)
|
||||
(= :email-has-permanent-bounces code))
|
||||
(msg/error (tr "errors.email-has-permanent-bounces" email))
|
||||
|
||||
:else
|
||||
(msg/error (tr "errors.generic"))))
|
||||
(let [expired? (:expired invitation)
|
||||
email (:email invitation)
|
||||
role (:role invitation)
|
||||
status (if expired? :expired :pending)
|
||||
|
||||
change-rol
|
||||
(fn [role]
|
||||
(let [params {:email email :team-id (:id team) :role role}
|
||||
mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}]
|
||||
(st/emit! (dd/update-team-invitation-role (with-meta params mdata)))))
|
||||
(mf/use-fn
|
||||
(mf/deps team email)
|
||||
(fn [role]
|
||||
(let [params {:email email :team-id (:id team) :role role}
|
||||
mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}]
|
||||
(st/emit! (dd/update-team-invitation-role (with-meta params mdata))))))]
|
||||
|
||||
delete-invitation
|
||||
(fn []
|
||||
(let [params {:email email :team-id (:id team)}
|
||||
mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}]
|
||||
(st/emit! (dd/delete-team-invitation (with-meta params mdata)))))
|
||||
|
||||
resend-invitation
|
||||
(fn []
|
||||
(let [params {:emails [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-members (with-meta params mdata))
|
||||
(with-meta {::ev/origin :team}))
|
||||
(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
|
||||
:role role
|
||||
:status status
|
||||
:change-to-editor (partial change-rol :editor)
|
||||
:change-to-admin (partial change-rol :admin)}]]
|
||||
|
@ -530,20 +562,22 @@
|
|||
[: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}]]]))
|
||||
(when can-invite?
|
||||
[:& invitation-actions
|
||||
{:invitation invitation
|
||||
:team team}])]]))
|
||||
|
||||
(mf/defc empty-invitation-table [can-invite?]
|
||||
(mf/defc empty-invitation-table
|
||||
[{:keys [can-invite?] :as props}]
|
||||
[:div.empty-invitations
|
||||
[:span (tr "labels.no-invitations")]
|
||||
(when (:can-invite? can-invite?) [:span (tr "labels.no-invitations-hint")])])
|
||||
(when can-invite?
|
||||
[:span (tr "labels.no-invitations-hint")])])
|
||||
|
||||
(mf/defc invitation-section
|
||||
[{:keys [team invitations] :as props}]
|
||||
(let [owner? (get-in team [:permissions :is-owner])
|
||||
admin? (get-in team [:permissions :is-admin])
|
||||
(let [owner? (dm/get-in team [:permissions :is-owner])
|
||||
admin? (dm/get-in team [:permissions :is-admin])
|
||||
can-invite? (or owner? admin?)]
|
||||
|
||||
[:div.dashboard-table.invitations
|
||||
|
@ -555,7 +589,11 @@
|
|||
[:& empty-invitation-table {:can-invite? can-invite?}]
|
||||
[:div.table-rows
|
||||
(for [invitation invitations]
|
||||
[:& invitation-row {:key (:email invitation) :invitation invitation :can-invite? can-invite? :team team}])])]))
|
||||
[:& invitation-row
|
||||
{:key (:email invitation)
|
||||
:invitation invitation
|
||||
:can-invite? can-invite?
|
||||
:team team}])])]))
|
||||
|
||||
(mf/defc team-invitations-page
|
||||
[{:keys [team] :as props}]
|
||||
|
@ -568,7 +606,7 @@
|
|||
(tr "dashboard.your-penpot")
|
||||
(:name team)))))
|
||||
|
||||
(mf/with-effect
|
||||
(mf/with-effect []
|
||||
(st/emit! (dd/fetch-team-invitations)))
|
||||
|
||||
[:*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue