mirror of
https://github.com/penpot/penpot.git
synced 2025-12-12 06:24:17 +01:00
✨ Invitations management improvements (#7230)
* ✨ Invitations management improvements * 📎 Change invite email subject * 📎 Update icon usage * ♻️ Fix css file --------- Co-authored-by: Eva Marco <evamarcod@gmail.com>
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
### :sparkles: New features & Enhancements
|
### :sparkles: New features & Enhancements
|
||||||
- Show current Penpot version [Taiga #11603](https://tree.taiga.io/project/penpot/us/11603)
|
- Show current Penpot version [Taiga #11603](https://tree.taiga.io/project/penpot/us/11603)
|
||||||
- Switch several variant copies at the same time [Taiga #11411](https://tree.taiga.io/project/penpot/us/11411)
|
- Switch several variant copies at the same time [Taiga #11411](https://tree.taiga.io/project/penpot/us/11411)
|
||||||
|
- Invitations management improvements [Taiga #3479](https://tree.taiga.io/project/penpot/us/3479)
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Invitation to join {{team}}
|
{{invited-by|abbreviate:25}} has invited you to join the team “{{ team|abbreviate:25 }}”
|
||||||
@@ -224,8 +224,25 @@
|
|||||||
(def ^:private xf:map-email (map :email))
|
(def ^:private xf:map-email (map :email))
|
||||||
|
|
||||||
(defn- create-team-invitations
|
(defn- create-team-invitations
|
||||||
[{:keys [::db/conn] :as cfg} {:keys [profile team role emails] :as params}]
|
"Unified function to handle both create and resend team invitations.
|
||||||
(let [emails (set emails)
|
Accepts either:
|
||||||
|
- emails (set) + role (single role for all emails)
|
||||||
|
- invitations (vector of {:email :role} maps)"
|
||||||
|
[{:keys [::db/conn] :as cfg} {:keys [profile team role emails invitations] :as params}]
|
||||||
|
(let [;; Normalize input to a consistent format: [{:email :role}]
|
||||||
|
invitation-data (cond
|
||||||
|
;; Case 1: emails + single role (create invitations style)
|
||||||
|
(and emails role)
|
||||||
|
(map (fn [email] {:email email :role role}) emails)
|
||||||
|
|
||||||
|
;; Case 2: invitations with individual roles (resend invitations style)
|
||||||
|
(some? invitations)
|
||||||
|
invitations
|
||||||
|
|
||||||
|
:else
|
||||||
|
(throw (ex-info "Invalid parameters: must provide either emails+role or invitations" {})))
|
||||||
|
|
||||||
|
invitation-emails (into #{} (map :email) invitation-data)
|
||||||
|
|
||||||
join-requests (->> (get-valid-access-request-profiles conn (:id team))
|
join-requests (->> (get-valid-access-request-profiles conn (:id team))
|
||||||
(d/index-by :email))
|
(d/index-by :email))
|
||||||
@@ -235,51 +252,84 @@
|
|||||||
|
|
||||||
invitations (into #{}
|
invitations (into #{}
|
||||||
(comp
|
(comp
|
||||||
;; We don't re-send inviation to
|
;; We don't re-send invitations to
|
||||||
;; already existing members
|
;; already existing members
|
||||||
(remove team-members)
|
(remove #(contains? team-members (:email %)))
|
||||||
;; We don't send invitations to
|
;; We don't send invitations to
|
||||||
;; join-requested members
|
;; join-requested members
|
||||||
(remove join-requests)
|
(remove #(contains? join-requests (:email %)))
|
||||||
(map (fn [email] (assoc params :email email)))
|
(map (fn [{:keys [email role]}]
|
||||||
(keep (partial create-invitation cfg)))
|
(create-invitation cfg
|
||||||
emails)]
|
(-> params
|
||||||
|
(assoc :email email)
|
||||||
|
(assoc :role role)))))
|
||||||
|
(remove nil?))
|
||||||
|
invitation-data)]
|
||||||
|
|
||||||
;; For requested invitations, do not send invitation emails, add
|
;; For requested invitations, do not send invitation emails, add
|
||||||
;; the user directly to the team
|
;; the user directly to the team
|
||||||
(->> join-requests
|
(->> join-requests
|
||||||
(filter #(contains? emails (key %)))
|
(filter #(contains? invitation-emails (key %)))
|
||||||
(map val)
|
(map (fn [[email member]]
|
||||||
(run! (partial add-member-to-team conn profile team role)))
|
(let [role (:role (first (filter #(= (:email %) email) invitation-data)))]
|
||||||
|
(add-member-to-team conn profile team role member))))
|
||||||
|
(doall))
|
||||||
|
|
||||||
invitations))
|
invitations))
|
||||||
|
|
||||||
(def ^:private schema:create-team-invitations
|
(def ^:private schema:create-team-invitations
|
||||||
|
[:and
|
||||||
[:map {:title "create-team-invitations"}
|
[:map {:title "create-team-invitations"}
|
||||||
[:team-id ::sm/uuid]
|
[:team-id ::sm/uuid]
|
||||||
[:role types.team/schema:role]
|
;; Support both formats:
|
||||||
[:emails [::sm/set ::sm/email]]])
|
;; 1. emails (set) + role (single role for all)
|
||||||
|
;; 2. invitations (vector of {:email :role} maps)
|
||||||
|
[:emails {:optional true} [::sm/set ::sm/email]]
|
||||||
|
[:role {:optional true} types.team/schema:role]
|
||||||
|
[:invitations {:optional true} [:vector [:map
|
||||||
|
[:email ::sm/email]
|
||||||
|
[:role types.team/schema:role]]]]]
|
||||||
|
|
||||||
|
;; Ensure exactly one format is provided
|
||||||
|
[:fn (fn [params]
|
||||||
|
(let [has-emails-role (and (contains? params :emails)
|
||||||
|
(contains? params :role))
|
||||||
|
has-invitations (contains? params :invitations)]
|
||||||
|
(and (or has-emails-role has-invitations)
|
||||||
|
(not (and has-emails-role has-invitations)))))]])
|
||||||
|
|
||||||
(def ^:private max-invitations-by-request-threshold
|
(def ^:private max-invitations-by-request-threshold
|
||||||
"The number of invitations can be sent in a single rpc request"
|
"The number of invitations can be sent in a single rpc request"
|
||||||
25)
|
25)
|
||||||
|
|
||||||
(sv/defmethod ::create-team-invitations
|
(sv/defmethod ::create-team-invitations
|
||||||
"A rpc call that allow to send a single or multiple invitations to
|
"A rpc call that allows to send single or multiple invitations to join the team.
|
||||||
join the team."
|
|
||||||
|
Supports two parameter formats:
|
||||||
|
1. emails (set) + role (single role for all emails)
|
||||||
|
2. invitations (vector of {:email :role} maps for individual roles)"
|
||||||
{::doc/added "1.17"
|
{::doc/added "1.17"
|
||||||
::doc/module :teams
|
::doc/module :teams
|
||||||
::sm/params schema:create-team-invitations}
|
::sm/params schema:create-team-invitations}
|
||||||
[cfg {:keys [::rpc/profile-id team-id emails] :as params}]
|
[cfg {:keys [::rpc/profile-id team-id role emails] :as params}]
|
||||||
(let [perms (teams/get-permissions cfg profile-id team-id)
|
(let [perms (teams/get-permissions cfg profile-id team-id)
|
||||||
profile (db/get-by-id cfg :profile profile-id)
|
profile (db/get-by-id cfg :profile profile-id)
|
||||||
emails (into #{} (map profile/clean-email) emails)]
|
;; Determine which format is being used
|
||||||
|
using-emails-format? (and emails role)
|
||||||
|
;; Handle both parameter formats
|
||||||
|
emails (if using-emails-format?
|
||||||
|
(into #{} (map profile/clean-email) emails)
|
||||||
|
#{})
|
||||||
|
;; Calculate total invitation count for both formats
|
||||||
|
invitation-count (if using-emails-format?
|
||||||
|
(count emails)
|
||||||
|
(count (:invitations params)))]
|
||||||
|
|
||||||
(when-not (:is-admin perms)
|
(when-not (:is-admin perms)
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :insufficient-permissions))
|
:code :insufficient-permissions))
|
||||||
|
|
||||||
(when (> (count emails) max-invitations-by-request-threshold)
|
(when (> invitation-count max-invitations-by-request-threshold)
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :max-invitations-by-request
|
:code :max-invitations-by-request
|
||||||
:hint "the maximum of invitation on single request is reached"
|
:hint "the maximum of invitation on single request is reached"
|
||||||
@@ -288,7 +338,7 @@
|
|||||||
(-> cfg
|
(-> cfg
|
||||||
(assoc ::quotes/profile-id profile-id)
|
(assoc ::quotes/profile-id profile-id)
|
||||||
(assoc ::quotes/team-id team-id)
|
(assoc ::quotes/team-id team-id)
|
||||||
(assoc ::quotes/incr (count emails))
|
(assoc ::quotes/incr invitation-count)
|
||||||
(quotes/check! {::quotes/id ::quotes/invitations-per-team}
|
(quotes/check! {::quotes/id ::quotes/invitations-per-team}
|
||||||
{::quotes/id ::quotes/profiles-per-team}))
|
{::quotes/id ::quotes/profiles-per-team}))
|
||||||
|
|
||||||
@@ -304,7 +354,12 @@
|
|||||||
(-> params
|
(-> params
|
||||||
(assoc :profile profile)
|
(assoc :profile profile)
|
||||||
(assoc :team team)
|
(assoc :team team)
|
||||||
(assoc :emails emails)))]
|
;; Pass parameters in the correct format for the unified function
|
||||||
|
(cond-> using-emails-format?
|
||||||
|
;; If using emails+role format, ensure both are present
|
||||||
|
(assoc :emails emails :role role)
|
||||||
|
;; If using invitations format, the :invitations key is already in params
|
||||||
|
(not using-emails-format?) identity)))]
|
||||||
|
|
||||||
(with-meta {:total (count invitations)
|
(with-meta {:total (count invitations)
|
||||||
:invitations invitations}
|
:invitations invitations}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
[app.util.storage :as storage]
|
[app.util.storage :as storage]
|
||||||
[app.util.webapi :as wapi]
|
[app.util.webapi :as wapi]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
|
[clojure.string :as str]
|
||||||
[potok.v2.core :as ptk]))
|
[potok.v2.core :as ptk]))
|
||||||
|
|
||||||
(log/set-level! :warn)
|
(log/set-level! :warn)
|
||||||
@@ -351,26 +352,46 @@
|
|||||||
(rx/catch on-error))))))
|
(rx/catch on-error))))))
|
||||||
|
|
||||||
(defn create-invitations
|
(defn create-invitations
|
||||||
[{:keys [emails role team-id resend?] :as params}]
|
"Unified function to create invitations. Supports two parameter formats:
|
||||||
|
1. {:emails #{...} :role :admin :team-id uuid} - single role for all emails
|
||||||
|
2. {:invitations [{:email ... :role ...}] :team-id uuid} - individual roles per email"
|
||||||
|
[{:keys [emails role team-id invitations resend?] :as params}]
|
||||||
|
|
||||||
(assert (keyword? role))
|
|
||||||
(assert (uuid? team-id))
|
(assert (uuid? team-id))
|
||||||
(assert (sm/check-set-of-emails emails))
|
;; Validate input format - must have either emails+role OR invitations
|
||||||
|
(assert (or (and emails role (sm/check-set-of-emails emails) (keyword? role))
|
||||||
|
(and invitations
|
||||||
|
(sm/check-set-of-emails (map :email invitations))
|
||||||
|
(every? #(contains? ctt/valid-roles (:role %)) invitations)))
|
||||||
|
"Must provide either emails+role or invitations with individual roles")
|
||||||
|
|
||||||
(ptk/reify ::create-invitations
|
(ptk/reify ::create-invitations
|
||||||
ev/Event
|
ev/Event
|
||||||
(-data [_]
|
(-data [_]
|
||||||
{:role role
|
{:role (if invitations
|
||||||
|
(->> invitations (map :role) distinct (map name) (str/join ", "))
|
||||||
|
(name role))
|
||||||
:team-id team-id
|
:team-id team-id
|
||||||
:resend resend?})
|
:resend (boolean resend?)})
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [it _ _]
|
(watch [it _ _]
|
||||||
(let [{:keys [on-success on-error]
|
(let [{:keys [on-success on-error]
|
||||||
:or {on-success identity
|
:or {on-success identity
|
||||||
on-error rx/throw}} (meta params)
|
on-error rx/throw}} (meta params)
|
||||||
params (dissoc params :resend?)]
|
;; Prepare parameters based on format
|
||||||
(->> (rp/cmd! :create-team-invitations (with-meta params (meta it)))
|
rpc-params (cond
|
||||||
|
;; Format 1: emails + single role
|
||||||
|
(and emails role)
|
||||||
|
{:emails emails :role role :team-id team-id}
|
||||||
|
|
||||||
|
;; Format 2: invitations with individual roles
|
||||||
|
invitations
|
||||||
|
{:invitations invitations :team-id team-id}
|
||||||
|
|
||||||
|
:else
|
||||||
|
(throw (ex-info " Invalid parameters " params)))]
|
||||||
|
(->> (rp/cmd! :create-team-invitations (with-meta rpc-params (meta it)))
|
||||||
(rx/tap on-success)
|
(rx/tap on-success)
|
||||||
(rx/catch on-error))))))
|
(rx/catch on-error))))))
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,12 @@
|
|||||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||||
[app.main.ui.components.forms :as fm]
|
[app.main.ui.components.forms :as fm]
|
||||||
[app.main.ui.dashboard.change-owner]
|
[app.main.ui.dashboard.change-owner]
|
||||||
[app.main.ui.dashboard.subscription :refer [team*
|
[app.main.ui.dashboard.subscription :refer [members-cta*
|
||||||
members-cta*
|
show-subscription-members-banner?
|
||||||
show-subscription-members-banner?]]
|
team*]]
|
||||||
[app.main.ui.dashboard.team-form]
|
[app.main.ui.dashboard.team-form]
|
||||||
|
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||||
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
[app.main.ui.icons :as deprecated-icon]
|
[app.main.ui.icons :as deprecated-icon]
|
||||||
[app.main.ui.notifications.badge :refer [badge-notification]]
|
[app.main.ui.notifications.badge :refer [badge-notification]]
|
||||||
@@ -604,11 +606,7 @@
|
|||||||
{::mf/props :obj
|
{::mf/props :obj
|
||||||
::mf/private true}
|
::mf/private true}
|
||||||
[{:keys [invitation team-id]}]
|
[{:keys [invitation team-id]}]
|
||||||
(let [show? (mf/use-state false)
|
(let [email (:email invitation)
|
||||||
|
|
||||||
email (:email invitation)
|
|
||||||
role (:role invitation)
|
|
||||||
|
|
||||||
on-error
|
on-error
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps email)
|
(mf/deps email)
|
||||||
@@ -631,35 +629,6 @@
|
|||||||
:else
|
:else
|
||||||
(rx/throw cause)))))
|
(rx/throw cause)))))
|
||||||
|
|
||||||
on-delete
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps email team-id)
|
|
||||||
(fn []
|
|
||||||
(let [params {:email email :team-id team-id}
|
|
||||||
mdata {:on-success #(st/emit! (dtm/fetch-invitations))}]
|
|
||||||
(st/emit! (dtm/delete-invitation (with-meta params mdata))))))
|
|
||||||
|
|
||||||
on-resend-success
|
|
||||||
(mf/use-fn
|
|
||||||
(fn []
|
|
||||||
(st/emit! (ntf/success (tr "notifications.invitation-email-sent"))
|
|
||||||
(modal/hide)
|
|
||||||
(dtm/fetch-invitations))))
|
|
||||||
|
|
||||||
on-resend
|
|
||||||
(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!
|
|
||||||
(-> (dtm/create-invitations params)
|
|
||||||
(with-meta {::ev/origin :team}))))))
|
|
||||||
|
|
||||||
on-copy-success
|
on-copy-success
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn []
|
(fn []
|
||||||
@@ -675,33 +644,18 @@
|
|||||||
:on-error on-error})]
|
:on-error on-error})]
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(-> (dtm/copy-invitation-link params)
|
(-> (dtm/copy-invitation-link params)
|
||||||
(with-meta {::ev/origin :team}))))))
|
(with-meta {::ev/origin :team}))))))]
|
||||||
|
|
||||||
on-hide (mf/use-fn #(reset! show? false))
|
[:> icon-button* {:variant "ghost"
|
||||||
on-show (mf/use-fn #(reset! show? true))]
|
:aria-label (tr "labels.copy-invitation-link")
|
||||||
|
:on-click on-copy
|
||||||
[:*
|
:icon "clipboard"}]))
|
||||||
[:button {:class (stl/css :menu-btn)
|
|
||||||
:on-click on-show}
|
|
||||||
menu-icon]
|
|
||||||
|
|
||||||
[:& dropdown {:show @show? :on-close on-hide :dropdown-id "invitation-actions"}
|
|
||||||
[:ul {:class (stl/css :actions-dropdown :invitations-dropdown)}
|
|
||||||
[:li {:on-click on-copy
|
|
||||||
:class (stl/css :action-dropdown-item)}
|
|
||||||
(tr "labels.copy-invitation-link")]
|
|
||||||
[:li {:on-click on-resend
|
|
||||||
:class (stl/css :action-dropdown-item)}
|
|
||||||
(tr "labels.resend-invitation")]
|
|
||||||
[:li {:on-click on-delete
|
|
||||||
:class (stl/css :action-dropdown-item)}
|
|
||||||
(tr "labels.delete-invitation")]]]]))
|
|
||||||
|
|
||||||
(mf/defc invitation-row*
|
(mf/defc invitation-row*
|
||||||
{::mf/wrap [mf/memo]
|
{::mf/wrap [mf/memo]
|
||||||
::mf/private true
|
::mf/private true
|
||||||
::mf/props :obj}
|
::mf/props :obj}
|
||||||
[{:keys [invitation can-invite team-id]}]
|
[{:keys [invitation can-invite team-id selected on-select-change]}]
|
||||||
|
|
||||||
(let [expired? (:expired invitation)
|
(let [expired? (:expired invitation)
|
||||||
email (:email invitation)
|
email (:email invitation)
|
||||||
@@ -714,6 +668,17 @@
|
|||||||
(tr "labels.expired-invitation")
|
(tr "labels.expired-invitation")
|
||||||
(tr "labels.pending-invitation"))
|
(tr "labels.pending-invitation"))
|
||||||
|
|
||||||
|
is-selected? (fn [email]
|
||||||
|
(contains? @selected email))
|
||||||
|
|
||||||
|
on-change
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps on-select-change)
|
||||||
|
(fn [event]
|
||||||
|
(let [email (-> (dom/get-current-target event)
|
||||||
|
(dom/get-data "attr"))]
|
||||||
|
(on-select-change email))))
|
||||||
|
|
||||||
on-change-role
|
on-change-role
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps email team-id)
|
(mf/deps email team-id)
|
||||||
@@ -723,7 +688,20 @@
|
|||||||
(st/emit! (dtm/update-invitation-role (with-meta params mdata))))))]
|
(st/emit! (dtm/update-invitation-role (with-meta params mdata))))))]
|
||||||
|
|
||||||
[:div {:class (stl/css :table-row :table-row-invitations)}
|
[:div {:class (stl/css :table-row :table-row-invitations)}
|
||||||
[:div {:class (stl/css :table-field :field-email)} email]
|
[:div {:class (stl/css :table-field :field-email)}
|
||||||
|
[:div {:class (stl/css :input-wrapper)}
|
||||||
|
[:label {:for (str "email-" email)}
|
||||||
|
[:span {:class (stl/css-case :input-checkbox true
|
||||||
|
:global/checked (is-selected? email))}
|
||||||
|
deprecated-icon/status-tick]
|
||||||
|
|
||||||
|
[:input {:type "checkbox"
|
||||||
|
:id (str "email-" email)
|
||||||
|
:data-attr email
|
||||||
|
:value email
|
||||||
|
:checked (is-selected? email)
|
||||||
|
:on-change on-change}]]]
|
||||||
|
email]
|
||||||
|
|
||||||
[:div {:class (stl/css :table-field :field-roles)}
|
[:div {:class (stl/css :table-field :field-roles)}
|
||||||
[:> invitation-role-selector*
|
[:> invitation-role-selector*
|
||||||
@@ -766,33 +744,233 @@
|
|||||||
(tr "dashboard.invite-profile")]
|
(tr "dashboard.invite-profile")]
|
||||||
[:div {:class (stl/css :blank-space)}]])]))
|
[:div {:class (stl/css :blank-space)}]])]))
|
||||||
|
|
||||||
|
(mf/defc invitation-modal
|
||||||
|
{::mf/register modal/components
|
||||||
|
::mf/register-as :invitation-modal}
|
||||||
|
[{:keys [selected delete on-confirm]}]
|
||||||
|
[:div {:class (stl/css :modal-overlay)}
|
||||||
|
[:div {:class (stl/css :modal-invitation-container :modal-container)}
|
||||||
|
[:div {:class (stl/css :modal-header)}
|
||||||
|
[:h2 {:class (stl/css :modal-title)}
|
||||||
|
(if delete
|
||||||
|
(tr "dashboard.invitation-modal.title.delete-invitations")
|
||||||
|
(tr "dashboard.invitation-modal.title.resend-invitations"))]
|
||||||
|
|
||||||
|
[:button {:class (stl/css :modal-close-btn)
|
||||||
|
:on-click modal/hide!} deprecated-icon/close]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-invitation-content)}
|
||||||
|
[:p
|
||||||
|
(if delete
|
||||||
|
(tr "dashboard.invitation-modal.delete")
|
||||||
|
(tr "dashboard.invitation-modal.resend"))]
|
||||||
|
[:div {:class (stl/css :invitation-list)}
|
||||||
|
(for [{:keys [email role]} selected]
|
||||||
|
[:p {:key email}
|
||||||
|
(str "- " email " (" (tr (str "labels." (name role))) ")")])]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-footer)}
|
||||||
|
[:div {:class (stl/css :action-buttons :modal-invitation-action-buttons)}
|
||||||
|
(when-not delete
|
||||||
|
[:> button*
|
||||||
|
{:class (stl/css :cancel-button)
|
||||||
|
:variant "secondary"
|
||||||
|
:type "button"
|
||||||
|
:on-click modal/hide!}
|
||||||
|
(tr "labels.cancel")])
|
||||||
|
[:> button*
|
||||||
|
{:class (stl/css :accept-btn)
|
||||||
|
:variant "primary"
|
||||||
|
:type "button"
|
||||||
|
:on-click on-confirm}
|
||||||
|
(if delete
|
||||||
|
(tr "labels.continue")
|
||||||
|
(tr "labels.resend"))]]]]])
|
||||||
|
|
||||||
(mf/defc invitation-section*
|
(mf/defc invitation-section*
|
||||||
{::mf/props :obj
|
{::mf/props :obj
|
||||||
::mf/private true}
|
::mf/private true}
|
||||||
[{:keys [team]}]
|
[{:keys [team]}]
|
||||||
(let [permissions (get team :permissions)
|
(let [permissions (get team :permissions)
|
||||||
invitations (get team :invitations)
|
invitations (mf/use-state (get team :invitations))
|
||||||
|
|
||||||
team-id (get team :id)
|
team-id (get team :id)
|
||||||
|
|
||||||
owner? (get permissions :is-owner)
|
owner? (get permissions :is-owner)
|
||||||
admin? (get permissions :is-admin)
|
admin? (get permissions :is-admin)
|
||||||
can-invite? (or owner? admin?)]
|
can-invite? (or owner? admin?)
|
||||||
|
|
||||||
|
selected (mf/use-state #{})
|
||||||
|
|
||||||
|
;; Sort state: {:field :status/:role, :direction :asc/:desc}
|
||||||
|
sort-state (mf/use-state {:field nil :direction :asc})
|
||||||
|
|
||||||
|
selected-invitations (mf/with-memo [selected invitations]
|
||||||
|
(filterv #(contains? @selected (:email %)) @invitations))
|
||||||
|
|
||||||
|
on-select-change
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps selected)
|
||||||
|
(fn [email]
|
||||||
|
(if (contains? @selected email)
|
||||||
|
(swap! selected disj email)
|
||||||
|
(swap! selected conj email))))
|
||||||
|
|
||||||
|
on-confirm-delete
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps selected team-id)
|
||||||
|
(fn []
|
||||||
|
(doseq [email @selected]
|
||||||
|
(let [params {:email email :team-id team-id}
|
||||||
|
mdata {:on-success #(st/emit! (ntf/success (tr "notifications.invitation-deleted"))
|
||||||
|
(dtm/fetch-invitations)
|
||||||
|
(modal/hide))}]
|
||||||
|
(st/emit! (dtm/delete-invitation (with-meta params mdata)))))
|
||||||
|
(reset! selected #{})))
|
||||||
|
|
||||||
|
on-delete
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps selected-invitations team-id)
|
||||||
|
(fn []
|
||||||
|
(st/emit! (modal/show :invitation-modal {:selected selected-invitations :delete true :on-confirm on-confirm-delete}))))
|
||||||
|
|
||||||
|
on-error
|
||||||
|
(fn [form]
|
||||||
|
(let [{:keys [type code] :as error} (ex-data form)]
|
||||||
|
(println form)
|
||||||
|
(cond
|
||||||
|
(and (= :validation type)
|
||||||
|
(= :profile-is-muted code))
|
||||||
|
(st/emit! (ntf/error (tr "errors.profile-is-muted"))
|
||||||
|
(modal/hide))
|
||||||
|
|
||||||
|
(and (= :validation type)
|
||||||
|
(= :max-invitations-by-request code))
|
||||||
|
(st/emit! (ntf/error (tr "errors.maximum-invitations-by-request-reached" (:threshold error))))
|
||||||
|
|
||||||
|
(and (= :restriction type)
|
||||||
|
(= :max-quote-reached code))
|
||||||
|
(st/emit! (ntf/error (tr "errors.max-quote-reached" (:target error))))
|
||||||
|
|
||||||
|
(or (= :member-is-muted code)
|
||||||
|
(= :email-has-permanent-bounces code)
|
||||||
|
(= :email-has-complaints code))
|
||||||
|
(st/emit! (ntf/error (tr "errors.email-spam-or-permanent-bounces" (:email error))))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (ntf/error (tr "errors.generic"))
|
||||||
|
(modal/hide)))))
|
||||||
|
|
||||||
|
on-resend-success
|
||||||
|
(mf/use-fn
|
||||||
|
(fn []
|
||||||
|
(st/emit! (ntf/success (tr "notifications.invitation-email-sent"))
|
||||||
|
(modal/hide)
|
||||||
|
(dtm/fetch-invitations))
|
||||||
|
(reset! selected #{})))
|
||||||
|
|
||||||
|
on-confirm-resend
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps selected-invitations team-id on-resend-success)
|
||||||
|
(fn []
|
||||||
|
(modal/hide!)
|
||||||
|
(let [params (with-meta {:invitations selected-invitations
|
||||||
|
:team-id team-id
|
||||||
|
:resend? true}
|
||||||
|
{:on-success on-resend-success
|
||||||
|
:on-error on-error})]
|
||||||
|
|
||||||
|
(st/emit!
|
||||||
|
(-> (dtm/create-invitations params)
|
||||||
|
(with-meta {::ev/origin :team}))))))
|
||||||
|
|
||||||
|
on-resend
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps team-id selected-invitations)
|
||||||
|
(fn []
|
||||||
|
(st/emit! (modal/show :invitation-modal {:selected selected-invitations :on-confirm on-confirm-resend}))))
|
||||||
|
|
||||||
|
on-order-by-status
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps sort-state)
|
||||||
|
(fn []
|
||||||
|
(let [current-field (:field @sort-state)
|
||||||
|
current-direction (:direction @sort-state)
|
||||||
|
new-direction (if (= current-field :status)
|
||||||
|
(if (= current-direction :asc) :desc :asc)
|
||||||
|
:asc)]
|
||||||
|
(println @invitations)
|
||||||
|
(swap! sort-state assoc :field :status :direction new-direction)
|
||||||
|
(swap! invitations #(let [sorted (sort-by (juxt :expired :email) %)]
|
||||||
|
(if (= new-direction :desc)
|
||||||
|
(reverse sorted)
|
||||||
|
sorted))))))
|
||||||
|
|
||||||
|
on-order-by-role
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps sort-state)
|
||||||
|
(fn []
|
||||||
|
(let [current-field (:field @sort-state)
|
||||||
|
current-direction (:direction @sort-state)
|
||||||
|
new-direction (if (= current-field :role)
|
||||||
|
(if (= current-direction :asc) :desc :asc)
|
||||||
|
:asc)]
|
||||||
|
(swap! sort-state assoc :field :role :direction new-direction)
|
||||||
|
(swap! invitations #(let [sorted (sort-by (juxt :role :email) %)]
|
||||||
|
(if (= new-direction :desc)
|
||||||
|
(reverse sorted)
|
||||||
|
sorted))))))]
|
||||||
|
|
||||||
|
(mf/with-effect [team]
|
||||||
|
(reset! invitations (get team :invitations))
|
||||||
|
(reset! sort-state {:field nil :direction :asc}))
|
||||||
|
|
||||||
[:div {:class (stl/css :invitations)}
|
[:div {:class (stl/css :invitations)}
|
||||||
|
(when (> (count @selected) 0)
|
||||||
|
[:*
|
||||||
|
[:div {:class (stl/css :invitations-actions)}
|
||||||
|
[:div
|
||||||
|
(str (count @selected) " invitations selected")]
|
||||||
|
[:div
|
||||||
|
[:> button* {:variant "secondary"
|
||||||
|
:type "button"
|
||||||
|
:on-click on-resend}
|
||||||
|
(tr "labels.resend-invitation")]]
|
||||||
|
[:> icon-button* {:on-click on-delete
|
||||||
|
:variant "destructive"
|
||||||
|
:aria-label (tr "labels.delete-invitation")
|
||||||
|
:icon "delete"}]]])
|
||||||
[:div {:class (stl/css :table-header)}
|
[:div {:class (stl/css :table-header)}
|
||||||
[:div {:class (stl/css :title-field-name)} (tr "labels.invitations")]
|
[:div {:class (stl/css :title-field-name)} (tr "labels.invitations")]
|
||||||
[:div {:class (stl/css :title-field-role)} (tr "labels.role")]
|
[:div {:class (stl/css :title-field-role)} (tr "labels.role")
|
||||||
[:div {:class (stl/css :title-field-status)} (tr "labels.status")]]
|
[:> icon-button* {:variant "action"
|
||||||
(if (empty? invitations)
|
:class (stl/css-case :sort-active (= (:field @sort-state) :role)
|
||||||
|
:sort-inactive (not= (:field @sort-state) :role))
|
||||||
|
:aria-label (tr "dashboard.order-invitations-by-role")
|
||||||
|
:icon (if (= (:field @sort-state) :role)
|
||||||
|
(if (= (:direction @sort-state) :asc) "arrow-down" "arrow-up")
|
||||||
|
"arrow-down")
|
||||||
|
:on-click on-order-by-role}]]
|
||||||
|
[:div {:class (stl/css :title-field-status)} (tr "labels.status")
|
||||||
|
[:> icon-button* {:variant "action"
|
||||||
|
:class (stl/css-case :sort-active (= (:field @sort-state) :status)
|
||||||
|
:sort-inactive (not= (:field @sort-state) :status))
|
||||||
|
:aria-label (tr "dashboard.order-invitations-by-status")
|
||||||
|
:icon (if (= (:field @sort-state) :status)
|
||||||
|
(if (= (:direction @sort-state) :asc) "arrow-down" "arrow-up")
|
||||||
|
"arrow-down")
|
||||||
|
:on-click on-order-by-status}]]]
|
||||||
|
(if (empty? @invitations)
|
||||||
[:> empty-invitation-table* {:can-invite can-invite? :team team}]
|
[:> empty-invitation-table* {:can-invite can-invite? :team team}]
|
||||||
[:div {:class (stl/css :table-rows)}
|
[:div {:class (stl/css :table-rows)}
|
||||||
(for [invitation invitations]
|
(for [invitation @invitations]
|
||||||
[:> invitation-row*
|
[:> invitation-row*
|
||||||
{:key (:email invitation)
|
{:key (:email invitation)
|
||||||
:invitation invitation
|
:invitation invitation
|
||||||
:can-invite can-invite?
|
:can-invite can-invite?
|
||||||
:team-id team-id}])])]))
|
:team-id team-id
|
||||||
|
:selected selected
|
||||||
|
:on-select-change on-select-change}])])]))
|
||||||
|
|
||||||
(mf/defc team-invitations-page*
|
(mf/defc team-invitations-page*
|
||||||
{::mf/props :obj}
|
{::mf/props :obj}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
@use "refactor/common-refactor.scss" as deprecated;
|
@use "refactor/common-refactor.scss" as deprecated;
|
||||||
@use "common/refactor/common-dashboard";
|
@use "common/refactor/common-dashboard";
|
||||||
|
@use "ds/_utils.scss" as *;
|
||||||
|
@use "ds/typography.scss" as t;
|
||||||
|
@use "ds/_sizes.scss" as *;
|
||||||
|
@use "ds/_borders.scss" as *;
|
||||||
|
@use "ds/mixins.scss" as *;
|
||||||
|
|
||||||
// Dashboard team settings
|
// Dashboard team settings
|
||||||
.dashboard-team-settings {
|
.dashboard-team-settings {
|
||||||
@@ -13,68 +18,75 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-top: deprecated.$s-1 solid var(--panel-border-color);
|
border-top: $b-1 solid var(--color-background-quaternary);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-inline-start: deprecated.$s-20;
|
padding-inline-start: var(--sp-xl);
|
||||||
padding-block-start: deprecated.$s-20;
|
padding-block-start: var(--sp-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-container {
|
.settings-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: deprecated.$s-24;
|
gap: var(--sp-xxl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.block {
|
.block {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-rows: min-content;
|
grid-auto-rows: min-content;
|
||||||
gap: deprecated.$s-8;
|
gap: var(--sp-s);
|
||||||
max-width: deprecated.$s-1000;
|
max-width: $sz-1000;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-block {
|
.info-block {
|
||||||
margin-block-start: deprecated.$s-16;
|
margin-block-start: var(--sp-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-label {
|
.block-label {
|
||||||
@include deprecated.headlineSmallTypography;
|
@include t.use-typography("headline-small");
|
||||||
color: var(--title-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-text {
|
.block-text {
|
||||||
color: var(--title-foreground-color-hover);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-content {
|
.block-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: deprecated.$s-32 1fr;
|
grid-template-columns: var(--sp-xxxl) 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: deprecated.$s-12;
|
gap: var(--sp-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.owner-icon {
|
.owner-icon {
|
||||||
width: deprecated.$s-32;
|
width: $sz-32;
|
||||||
height: deprecated.$s-32;
|
height: $sz-32;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-icon,
|
.user-icon,
|
||||||
.document-icon,
|
.document-icon,
|
||||||
.group-icon {
|
.group-icon {
|
||||||
@extend .button-icon;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: $sz-16;
|
||||||
|
width: $sz-16;
|
||||||
|
color: transparent;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: $b-1;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
stroke: var(--icon-foreground);
|
stroke: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-icon {
|
.team-icon {
|
||||||
--update-button-opacity: 0;
|
--update-button-opacity: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: deprecated.$s-120;
|
height: $sz-120;
|
||||||
width: deprecated.$s-120;
|
width: $sz-120;
|
||||||
padding: deprecated.$s-16;
|
padding: var(--sp-l);
|
||||||
margin-block-end: deprecated.$s-32;
|
margin-block-end: var(--sp-xxxl);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
--update-button-opacity: 1;
|
--update-button-opacity: 1;
|
||||||
@@ -86,29 +98,40 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: deprecated.$s-120;
|
width: $sz-120;
|
||||||
height: deprecated.$s-120;
|
height: $sz-120;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update-overlay {
|
.update-overlay {
|
||||||
opacity: var(--update-button-opacity);
|
opacity: var(--update-button-opacity);
|
||||||
@include deprecated.buttonStyle;
|
border: none;
|
||||||
@include deprecated.flexCenter;
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: deprecated.$z-index-modal;
|
z-index: var(--z-index-set);
|
||||||
border-radius: deprecated.$br-circle;
|
border-radius: $br-circle;
|
||||||
background-color: var(--color-accent-primary);
|
background-color: var(--color-accent-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-icon {
|
.image-icon {
|
||||||
@extend .button-icon;
|
display: flex;
|
||||||
min-width: deprecated.$s-24;
|
justify-content: center;
|
||||||
min-height: deprecated.$s-24;
|
align-items: center;
|
||||||
stroke: var(--icon-foreground-hover);
|
height: $sz-16;
|
||||||
|
width: $sz-16;
|
||||||
|
color: transparent;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: $b-1;
|
||||||
|
min-width: $sz-24;
|
||||||
|
min-height: $sz-24;
|
||||||
|
stroke: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEAM MEMBERS PAGE
|
// TEAM MEMBERS PAGE
|
||||||
@@ -117,9 +140,9 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-inline-start: deprecated.$s-20;
|
padding-inline-start: var(--sp-xl);
|
||||||
padding-block-start: deprecated.$s-20;
|
padding-block-start: var(--sp-xl);
|
||||||
border-top: deprecated.$s-1 solid var(--panel-border-color);
|
border-top: $b-1 solid var(--color-background-quaternary);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
}
|
}
|
||||||
@@ -133,62 +156,69 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
max-width: deprecated.$s-1000;
|
max-width: $sz-1000;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-header {
|
.table-header {
|
||||||
@include deprecated.headlineSmallTypography;
|
@include t.use-typography("headline-small");
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 43% 1fr deprecated.$s-108 deprecated.$s-12;
|
grid-template-columns: 43% 1fr px2rem(108) var(--sp-m);
|
||||||
height: deprecated.$s-40;
|
height: $sz-40;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: deprecated.$s-1000;
|
max-width: $sz-1000;
|
||||||
padding: 0 deprecated.$s-16;
|
padding: 0 var(--sp-l);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
color: var(--title-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-rows {
|
.table-rows {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-rows: deprecated.$s-64;
|
grid-auto-rows: px2rem(64);
|
||||||
gap: deprecated.$s-16;
|
gap: var(--sp-l);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-width: deprecated.$s-1000;
|
max-width: $sz-1000;
|
||||||
margin-top: deprecated.$s-16;
|
margin-top: var(--sp-l);
|
||||||
color: var(--title-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-row {
|
.table-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 43% 1fr auto;
|
grid-template-columns: 43% 1fr auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: deprecated.$s-64;
|
height: px2rem(64);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 deprecated.$s-16;
|
padding: 0 var(--sp-l);
|
||||||
border-radius: deprecated.$br-8;
|
border-radius: $br-8;
|
||||||
background-color: var(--dashboard-list-background-color);
|
background-color: var(--color-background-tertiary);
|
||||||
color: var(--dashboard-list-foreground-color);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-field-name {
|
.title-field-name {
|
||||||
width: 43%;
|
width: 43%;
|
||||||
min-width: deprecated.$s-300;
|
min-width: px2rem(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-field-roles {
|
.title-field-role {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title-field-role,
|
||||||
|
.title-field-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--sp-s);
|
||||||
|
}
|
||||||
|
|
||||||
.field-name {
|
.field-name {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: auto 1fr;
|
||||||
gap: deprecated.$s-16;
|
gap: var(--sp-l);
|
||||||
width: 43%;
|
width: 43%;
|
||||||
min-width: deprecated.$s-300;
|
min-width: px2rem(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-roles {
|
.field-roles {
|
||||||
@@ -202,9 +232,9 @@
|
|||||||
|
|
||||||
// MEMBER INFO
|
// MEMBER INFO
|
||||||
.member-image {
|
.member-image {
|
||||||
height: deprecated.$s-32;
|
height: $sz-32;
|
||||||
width: deprecated.$s-32;
|
width: $sz-32;
|
||||||
border-radius: deprecated.$br-circle;
|
border-radius: $br-circle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.member-info {
|
.member-info {
|
||||||
@@ -215,34 +245,34 @@
|
|||||||
|
|
||||||
.member-name,
|
.member-name,
|
||||||
.member-email {
|
.member-email {
|
||||||
@include deprecated.textEllipsis;
|
@include textEllipsis;
|
||||||
@include deprecated.bodyLargeTypography;
|
@include t.use-typography("body-large");
|
||||||
}
|
}
|
||||||
|
|
||||||
.member-email {
|
.member-email {
|
||||||
@include deprecated.bodySmallTypography;
|
@include t.use-typography("body-small");
|
||||||
color: var(--dashboard-list-text-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.you {
|
.you {
|
||||||
color: var(--dashboard-list-text-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
margin-left: deprecated.$s-6;
|
margin-left: px2rem(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ROL INFO
|
// ROL INFO
|
||||||
.rol-selector {
|
.rol-selector {
|
||||||
|
@include t.use-typography("body-medium");
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: deprecated.$s-32;
|
height: $sz-32;
|
||||||
min-width: deprecated.$s-160;
|
min-width: $sz-160;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
padding: deprecated.$s-4 deprecated.$s-8;
|
padding: var(--sp-xs) var(--sp-s);
|
||||||
border-radius: deprecated.$br-8;
|
border-radius: $br-8;
|
||||||
border-color: var(--menu-background-color-hover);
|
border-color: var(--color-background-quaternary);
|
||||||
background-color: var(--menu-background-color-hover);
|
background-color: var(--color-background-quaternary);
|
||||||
font-size: deprecated.$fs-14;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-priv {
|
.has-priv {
|
||||||
@@ -254,37 +284,96 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.roles-dropdown {
|
.roles-dropdown {
|
||||||
@extend .menu-dropdown;
|
box-shadow: var(--el-shadow-dark);
|
||||||
bottom: calc(-1 * deprecated.$s-76);
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--sp-xs);
|
||||||
|
position: absolute;
|
||||||
|
padding: var(--sp-xs);
|
||||||
|
border-radius: $br-8;
|
||||||
|
z-index: var(--z-index-dropdown);
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
background-color: var(--color-background-tertiary);
|
||||||
|
border: $b-2 solid var(--color-background-quaternary);
|
||||||
|
margin: 0;
|
||||||
|
bottom: calc(-1 * px2rem(76));
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
min-width: deprecated.$s-160;
|
min-width: $sz-160;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rol-dropdown-item {
|
.rol-dropdown-item {
|
||||||
@extend .menu-item-base;
|
@include t.use-typography("body-small");
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: $sz-28;
|
||||||
|
width: 100%;
|
||||||
|
padding: px2rem(6);
|
||||||
|
border-radius: $br-8;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-quaternary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MEMBER ACTIONS
|
// MEMBER ACTIONS
|
||||||
.menu-icon {
|
.menu-icon {
|
||||||
@extend .button-icon;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: $sz-16;
|
||||||
|
width: $sz-16;
|
||||||
|
color: transparent;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: $b-1;
|
||||||
stroke: var(--color-foreground-primary);
|
stroke: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-btn {
|
.menu-btn {
|
||||||
@include deprecated.buttonStyle;
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-checkbox {
|
||||||
|
// TODO: remove this extended class.
|
||||||
|
@extend .input-checkbox;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions-dropdown {
|
.actions-dropdown {
|
||||||
@extend .menu-dropdown;
|
box-shadow: var(--el-shadow-dark);
|
||||||
bottom: calc(-1 * deprecated.$s-32);
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--sp-xs);
|
||||||
|
position: absolute;
|
||||||
|
padding: var(--sp-xs);
|
||||||
|
border-radius: $br-8;
|
||||||
|
z-index: var(--z-index-dropdown);
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
background-color: var(--color-background-tertiary);
|
||||||
|
border: $b-2 solid var(--color-background-quaternary);
|
||||||
|
margin: 0;
|
||||||
|
bottom: calc(-1 * var(--sp-xxxl));
|
||||||
right: 0;
|
right: 0;
|
||||||
left: unset;
|
left: unset;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
min-width: deprecated.$s-160;
|
min-width: $sz-160;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-dropdown-item {
|
.action-dropdown-item {
|
||||||
@extend .menu-item-base;
|
@include t.use-typography("body-small");
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: $sz-28;
|
||||||
|
width: 100%;
|
||||||
|
padding: px2rem(6);
|
||||||
|
border-radius: $br-8;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-quaternary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEAM INVITATION PAGE
|
// TEAM INVITATION PAGE
|
||||||
@@ -293,9 +382,9 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-inline-start: deprecated.$s-20;
|
padding-inline-start: var(--sp-xl);
|
||||||
padding-block-start: deprecated.$s-20;
|
padding-block-start: var(--sp-xl);
|
||||||
border-top: deprecated.$s-1 solid var(--panel-border-color);
|
border-top: $b-1 solid var(--color-background-quaternary);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
}
|
}
|
||||||
@@ -304,12 +393,27 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
max-width: deprecated.$s-1000;
|
max-width: $sz-1000;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invitations-actions {
|
||||||
|
@include t.use-typography("body-medium");
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--sp-l);
|
||||||
|
color: var(--title-foreground-color);
|
||||||
|
height: $sz-40;
|
||||||
|
margin-block-end: px2rem(36);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-notification {
|
||||||
|
margin-block-end: var(--sp-xxl);
|
||||||
|
}
|
||||||
|
|
||||||
.table-row-invitations {
|
.table-row-invitations {
|
||||||
grid-template-columns: 43% 1fr deprecated.$s-108 deprecated.$s-12;
|
grid-template-columns: 43% 1fr $sz-88 var(--sp-xxl);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,19 +421,20 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
height: deprecated.$s-156;
|
height: px2rem(156);
|
||||||
max-width: deprecated.$s-1000;
|
max-width: $sz-1000;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: deprecated.$s-16;
|
margin-top: var(--sp-l);
|
||||||
border: deprecated.$s-1 solid var(--panel-border-color);
|
border: $b-1 solid var(--color-background-quaternary);
|
||||||
border-radius: deprecated.$br-8;
|
border-radius: $br-8;
|
||||||
color: var(--dashboard-list-text-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-empty-invitations {
|
.btn-empty-invitations {
|
||||||
|
// TODO: Remove this extend add DS component
|
||||||
@extend .button-primary;
|
@extend .button-primary;
|
||||||
margin-block-start: deprecated.$s-16;
|
margin-block-start: var(--sp-l);
|
||||||
padding-inline: deprecated.$s-12;
|
padding-inline: var(--sp-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-field-status {
|
.title-field-status {
|
||||||
@@ -337,16 +442,25 @@
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sort-inactive {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.field-email {
|
.field-email {
|
||||||
@include deprecated.textEllipsis;
|
@include textEllipsis;
|
||||||
@include deprecated.bodyLargeTypography;
|
@include t.use-typography("body-large");
|
||||||
display: grid;
|
display: flex;
|
||||||
|
gap: var(--sp-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.invitations-dropdown {
|
.invitations-dropdown {
|
||||||
bottom: calc(-1 * deprecated.$s-112);
|
bottom: calc(-1 * px2rem(112));
|
||||||
right: calc(-1 * deprecated.$s-20);
|
right: calc(-1 * var(--sp-xl));
|
||||||
}
|
}
|
||||||
|
|
||||||
// WEBHOOKS SECTION
|
// WEBHOOKS SECTION
|
||||||
@@ -354,63 +468,64 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
gap: deprecated.$s-24;
|
gap: var(--sp-xxl);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-inline-start: deprecated.$s-20;
|
padding-inline-start: var(--sp-xl);
|
||||||
padding-block-start: deprecated.$s-20;
|
padding-block-start: var(--sp-xl);
|
||||||
border-top: deprecated.$s-1 solid var(--panel-border-color);
|
border-top: $b-1 solid var(--color-background-quaternary);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.webhooks-hero-container {
|
.webhooks-hero-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
margin: deprecated.$s-80 auto deprecated.$s-20 auto;
|
margin: px2rem(80) auto var(--sp-xl) auto;
|
||||||
gap: deprecated.$s-24;
|
gap: var(--sp-xxl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.webhooks-empty {
|
.webhooks-empty {
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
height: deprecated.$s-156;
|
height: px2rem(156);
|
||||||
max-width: deprecated.$s-1000;
|
max-width: $sz-1000;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: deprecated.$s-32;
|
padding: var(--sp-xxxl);
|
||||||
border: deprecated.$s-1 solid var(--panel-border-color);
|
border: $b-1 solid var(--color-background-quaternary);
|
||||||
border-radius: deprecated.$br-8;
|
border-radius: $br-8;
|
||||||
color: var(--dashboard-list-text-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.webhooks-hero {
|
.webhooks-hero {
|
||||||
font-size: deprecated.$fs-14;
|
@include t.use-typography("body-medium");
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr auto;
|
grid-template-rows: auto 1fr auto;
|
||||||
gap: deprecated.$s-32;
|
gap: var(--sp-xxxl);
|
||||||
margin-top: deprecated.$s-32;
|
margin-top: var(--sp-xxxl);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: deprecated.$s-32;
|
padding: var(--sp-xxxl);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: deprecated.$s-468;
|
width: px2rem(468);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-title {
|
.hero-title {
|
||||||
@include deprecated.bigTitleTipography;
|
@include t.use-typography("title-large");
|
||||||
color: var(--dashboard-list-foreground-color);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-desc {
|
.hero-desc {
|
||||||
|
@include t.use-typography("body-large");
|
||||||
color: var(--color-foreground-secondary);
|
color: var(--color-foreground-secondary);
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
font-size: deprecated.$fs-16;
|
max-width: $sz-512;
|
||||||
max-width: deprecated.$s-512;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-btn {
|
.hero-btn {
|
||||||
|
//TODO: Remove this extended class using a DS component
|
||||||
@extend .button-primary;
|
@extend .button-primary;
|
||||||
height: deprecated.$s-32;
|
height: $sz-32;
|
||||||
max-width: deprecated.$s-512;
|
max-width: $sz-512;
|
||||||
}
|
}
|
||||||
|
|
||||||
.webhook-table {
|
.webhook-table {
|
||||||
@@ -421,7 +536,7 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: auto 1fr auto auto;
|
grid-template-columns: auto 1fr auto auto;
|
||||||
gap: deprecated.$s-16;
|
gap: var(--sp-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
@@ -429,90 +544,143 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-disabled {
|
.menu-disabled {
|
||||||
color: var(--icon-foreground);
|
color: var(--color-foreground-secondary);
|
||||||
width: deprecated.$s-28;
|
width: $sz-28;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.webhook-actions-dropdown {
|
.webhook-actions-dropdown {
|
||||||
@extend .menu-dropdown;
|
box-shadow: var(--el-shadow-dark);
|
||||||
right: calc(-1 * deprecated.$s-16);
|
display: flex;
|
||||||
bottom: calc(-1 * deprecated.$s-40);
|
flex-direction: column;
|
||||||
|
gap: var(--sp-xs);
|
||||||
|
position: absolute;
|
||||||
|
padding: var(--sp-xs);
|
||||||
|
border-radius: $br-8;
|
||||||
|
z-index: var(--z-index-dropdown);
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
background-color: var(--color-background-tertiary);
|
||||||
|
border: $b-2 solid var(--color-background-quaternary);
|
||||||
|
margin: 0;
|
||||||
|
right: calc(-1 * var(--sp-l));
|
||||||
|
bottom: calc(-1 * $sz-40);
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
min-width: deprecated.$s-160;
|
min-width: $sz-160;
|
||||||
}
|
}
|
||||||
|
|
||||||
.webhook-dropdown-item {
|
.webhook-dropdown-item {
|
||||||
@extend .menu-item-base;
|
@include t.use-typography("body-small");
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: $sz-28;
|
||||||
|
width: 100%;
|
||||||
|
padding: px2rem(6);
|
||||||
|
border-radius: $br-8;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-quaternary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-icon {
|
.success-icon {
|
||||||
@extend .button-icon;
|
display: flex;
|
||||||
stroke: var(--alert-icon-foreground-color-success);
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: $sz-16;
|
||||||
|
width: $sz-16;
|
||||||
|
color: transparent;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: $b-1;
|
||||||
|
stroke: var(--color-accent-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-icon {
|
.warning-icon {
|
||||||
@extend .button-icon;
|
display: flex;
|
||||||
stroke: var(--alert-icon-foreground-color-warning);
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: $sz-16;
|
||||||
|
width: $sz-16;
|
||||||
|
color: transparent;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: $b-1;
|
||||||
|
stroke: var(--color-accent-warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
// INVITE MEMBERS MODAL
|
// INVITE MEMBERS MODAL
|
||||||
.modal-team-container {
|
.modal-team-container {
|
||||||
@extend .modal-container-base;
|
position: relative;
|
||||||
@include deprecated.menuShadow;
|
padding: var(--sp-xxxl);
|
||||||
|
border-radius: $br-8;
|
||||||
|
background-color: var(--color-background-primary);
|
||||||
|
border: $b-2 solid var(--color-background-quaternary);
|
||||||
|
min-width: $sz-364;
|
||||||
|
min-height: $sz-192;
|
||||||
|
max-width: $sz-512;
|
||||||
|
max-height: $sz-512;
|
||||||
|
box-shadow: var(--el-shadow-dark);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: deprecated.$s-72;
|
top: px2rem(72);
|
||||||
right: deprecated.$s-12;
|
right: var(--sp-m);
|
||||||
left: unset;
|
left: unset;
|
||||||
width: deprecated.$s-400;
|
width: $sz-400;
|
||||||
padding: deprecated.$s-32;
|
padding: var(--sp-xxxl);
|
||||||
background-color: var(--modal-background-color);
|
background-color: var(--color-background-primary);
|
||||||
|
|
||||||
&.hero {
|
&.hero {
|
||||||
top: deprecated.$s-216;
|
top: px2rem(216);
|
||||||
right: deprecated.$s-32;
|
right: var(--sp-xxxl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-team-container-workspace {
|
.modal-team-container-workspace {
|
||||||
top: deprecated.$s-40;
|
top: $sz-40;
|
||||||
z-index: deprecated.$z-index-modal;
|
z-index: var(--z-index-set);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-title {
|
.modal-title {
|
||||||
@include deprecated.headlineMediumTypography;
|
@include t.use-typography("headline-medium");
|
||||||
height: deprecated.$s-32;
|
height: $sz-32;
|
||||||
color: var(--modal-title-foreground-color);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-select {
|
.role-select {
|
||||||
@include deprecated.flexColumn;
|
display: flex;
|
||||||
row-gap: deprecated.$s-8;
|
flex-direction: column;
|
||||||
|
gap: var(--sp-xs);
|
||||||
|
row-gap: var(--sp-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow-icon {
|
.arrow-icon {
|
||||||
@extend .button-icon;
|
display: flex;
|
||||||
stroke: var(--icon-foreground);
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: $sz-16;
|
||||||
|
width: $sz-16;
|
||||||
|
color: transparent;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: $b-1;
|
||||||
|
stroke: var(--color-foreground-secondary);
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.invite-team-member-text {
|
.invite-team-member-text {
|
||||||
@include deprecated.bodyLargeTypography;
|
@include t.use-typography("body-large");
|
||||||
margin: 0 0 deprecated.$s-16 0;
|
margin: 0 0 var(--sp-l) 0;
|
||||||
color: var(--modal-title-foreground-color);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-title {
|
.role-title {
|
||||||
@include deprecated.bodyLargeTypography;
|
@include t.use-typography("body-large");
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--modal-title-foreground-color);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.invitation-row {
|
.invitation-row {
|
||||||
margin-top: deprecated.$s-8;
|
margin-top: var(--sp-s);
|
||||||
margin-bottom: deprecated.$s-24;
|
margin-bottom: var(--sp-xxl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
@@ -521,46 +689,68 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.accept-btn {
|
.accept-btn {
|
||||||
|
// TODO: remove this extend class creating a modal component
|
||||||
@extend .modal-accept-btn;
|
@extend .modal-accept-btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WEBHOOKS MODAL
|
// WEBHOOKS MODAL
|
||||||
|
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
@extend .modal-overlay-base;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: var(--z-index-set);
|
||||||
|
background-color: var(--overlay-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-container {
|
.modal-container {
|
||||||
@extend .modal-container-base;
|
position: relative;
|
||||||
|
padding: var(--sp-xxxl);
|
||||||
|
border-radius: $br-8;
|
||||||
|
background-color: var(--color-background-primary);
|
||||||
|
border: $b-2 solid var(--color-background-quaternary);
|
||||||
|
min-width: $sz-364;
|
||||||
|
min-height: $sz-192;
|
||||||
|
max-width: $sz-512;
|
||||||
|
max-height: $sz-512;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
margin-bottom: deprecated.$s-24;
|
margin-bottom: var(--sp-xxl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-title {
|
.modal-title {
|
||||||
@include deprecated.uppercaseTitleTipography;
|
@include t.use-typography("title-small");
|
||||||
color: var(--modal-title-foreground-color);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-close-btn {
|
.modal-close-btn {
|
||||||
|
// TODO remove extended class creating a modal component
|
||||||
@extend .modal-close-btn-base;
|
@extend .modal-close-btn-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
@include deprecated.flexColumn;
|
@include t.use-typography("body-small");
|
||||||
gap: deprecated.$s-24;
|
display: flex;
|
||||||
@include deprecated.bodySmallTypography;
|
flex-direction: column;
|
||||||
margin-bottom: deprecated.$s-24;
|
gap: var(--sp-xxl);
|
||||||
|
margin-bottom: var(--sp-xxl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fields-row {
|
.fields-row {
|
||||||
@include deprecated.flexColumn;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--sp-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.select-title {
|
.select-title {
|
||||||
@include deprecated.bodySmallTypography;
|
@include t.use-typography("body-small");
|
||||||
color: var(--modal-title-foreground-color);
|
color: var(--color-foreground-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-input-checkbox {
|
.custom-input-checkbox {
|
||||||
@@ -568,9 +758,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
color: var(--modal-text-foreground-color);
|
color: var(--color-foreground-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this extended classes creating a modal component
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
@extend .modal-action-btns;
|
@extend .modal-action-btns;
|
||||||
|
|
||||||
@@ -583,8 +774,61 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this extended class using input component
|
||||||
.email-input {
|
.email-input {
|
||||||
|
@include t.use-typography("body-small");
|
||||||
@extend .input-base;
|
@extend .input-base;
|
||||||
@include deprecated.bodySmallTypography;
|
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
// TODO: Fix this nested classes.
|
||||||
|
.input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
label {
|
||||||
|
@include t.use-typography("body-small");
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: px2rem(6);
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-foreground-secondary);
|
||||||
|
span {
|
||||||
|
@extend .checkbox-icon;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
span {
|
||||||
|
border-color: var(--color-accent-primary-muted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:focus-within {
|
||||||
|
span {
|
||||||
|
border-color: var(--color-accent-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// INVITATION MODAL
|
||||||
|
|
||||||
|
.modal-invitation-container {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-invitation-content {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invitation-list p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-invitation-action-buttons {
|
||||||
|
margin-block-start: var(--sp-xxxl);
|
||||||
|
gap: var(--sp-s);
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ $sz-40: px2rem(40);
|
|||||||
$sz-48: px2rem(48);
|
$sz-48: px2rem(48);
|
||||||
$sz-80: px2rem(80);
|
$sz-80: px2rem(80);
|
||||||
$sz-88: px2rem(88);
|
$sz-88: px2rem(88);
|
||||||
|
$sz-120: px2rem(120);
|
||||||
$sz-154: px2rem(154);
|
$sz-154: px2rem(154);
|
||||||
$sz-160: px2rem(160);
|
$sz-160: px2rem(160);
|
||||||
$sz-192: px2rem(192);
|
$sz-192: px2rem(192);
|
||||||
@@ -35,6 +36,8 @@ $sz-400: px2rem(400);
|
|||||||
$sz-430: px2rem(430);
|
$sz-430: px2rem(430);
|
||||||
$sz-480: px2rem(480);
|
$sz-480: px2rem(480);
|
||||||
$sz-500: px2rem(500);
|
$sz-500: px2rem(500);
|
||||||
|
$sz-512: px2rem(512);
|
||||||
$sz-520: px2rem(520);
|
$sz-520: px2rem(520);
|
||||||
$sz-712: px2rem(712);
|
$sz-712: px2rem(712);
|
||||||
$sz-964: px2rem(964);
|
$sz-964: px2rem(964);
|
||||||
|
$sz-1000: px2rem(1000);
|
||||||
|
|||||||
@@ -8189,3 +8189,33 @@ msgstr "Autosaved versions will be kept for %s days."
|
|||||||
#, unused
|
#, unused
|
||||||
msgid "workspace.viewport.click-to-close-path"
|
msgid "workspace.viewport.click-to-close-path"
|
||||||
msgstr "Click to close the path"
|
msgstr "Click to close the path"
|
||||||
|
|
||||||
|
msgid "labels.sources"
|
||||||
|
msgstr "Sources"
|
||||||
|
|
||||||
|
msgid "labels.pinned-projects"
|
||||||
|
msgstr "Pinned Projects"
|
||||||
|
|
||||||
|
msgid "labels.resend"
|
||||||
|
msgstr "Resend"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.title.delete-invitations"
|
||||||
|
msgstr "Delete invitations"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.title.resend-invitations"
|
||||||
|
msgstr "Resend invitations"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.delete"
|
||||||
|
msgstr "You're going to delete the invitations to:"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.resend"
|
||||||
|
msgstr "You're going to resend the invitations to:"
|
||||||
|
|
||||||
|
msgid "dashboard.order-invitations-by-status"
|
||||||
|
msgstr "Order by status"
|
||||||
|
|
||||||
|
msgid "dashboard.order-invitations-by-role"
|
||||||
|
msgstr "Order by role"
|
||||||
|
|
||||||
|
msgid "notifications.invitation-deleted"
|
||||||
|
msgstr "Invitation deleted successfully"
|
||||||
|
|||||||
@@ -8070,3 +8070,33 @@ msgstr "Los autoguardados duran %s días."
|
|||||||
#, unused
|
#, unused
|
||||||
msgid "workspace.viewport.click-to-close-path"
|
msgid "workspace.viewport.click-to-close-path"
|
||||||
msgstr "Pulsar para cerrar la ruta"
|
msgstr "Pulsar para cerrar la ruta"
|
||||||
|
|
||||||
|
msgid "labels.sources"
|
||||||
|
msgstr "Recursos"
|
||||||
|
|
||||||
|
msgid "labels.pinned-projects"
|
||||||
|
msgstr "Proyectos fijados"
|
||||||
|
|
||||||
|
msgid "labels.resend"
|
||||||
|
msgstr "Reenviar"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.title.delete-invitations"
|
||||||
|
msgstr "Eliminar invitaciones"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.title.resend-invitations"
|
||||||
|
msgstr "Reenviar invitaciones"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.delete"
|
||||||
|
msgstr "Vas a eliminar las invitaciones para:"
|
||||||
|
|
||||||
|
msgid "dashboard.invitation-modal.resend"
|
||||||
|
msgstr "Vas a reenviar las invitaciones para:"
|
||||||
|
|
||||||
|
msgid "dashboard.order-invitations-by-status"
|
||||||
|
msgstr "Ordenar por estado"
|
||||||
|
|
||||||
|
msgid "dashboard.order-invitations-by-role"
|
||||||
|
msgstr "Ordenar por rol"
|
||||||
|
|
||||||
|
msgid "notifications.invitation-deleted"
|
||||||
|
msgstr "Invitación eliminada con éxito"
|
||||||
|
|||||||
Reference in New Issue
Block a user