mirror of
https://github.com/penpot/penpot.git
synced 2026-05-06 16:48:48 +00:00
✨ Add Nitrate's advanced permissions
* ✨ Add Nitrate's advanced permissions * 📎 Code review
This commit is contained in:
parent
94f8370d98
commit
4ddabaebff
@ -348,6 +348,20 @@
|
||||
[:map
|
||||
[:cancel-at [:maybe schema:timestamp]]])
|
||||
|
||||
(defn- get-org-permissions-api
|
||||
[cfg {:keys [organization-id] :as params}]
|
||||
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||
(request-to-nitrate cfg :get
|
||||
(str baseuri
|
||||
"/api/organizations/"
|
||||
organization-id
|
||||
"/permissions")
|
||||
[:map
|
||||
[:organization-id ::sm/uuid]
|
||||
[:owner-id ::sm/uuid]
|
||||
[:create-teams [:enum "any" "onlyMe"]]]
|
||||
params)))
|
||||
|
||||
(defn- redeem-activation-code-api
|
||||
[cfg params]
|
||||
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||
@ -372,6 +386,7 @@
|
||||
:add-profile-to-org (partial add-profile-to-org-api cfg)
|
||||
:remove-profile-from-org (partial remove-profile-from-org-api cfg)
|
||||
:remove-profile-from-all-orgs (partial remove-profile-from-all-orgs-api cfg)
|
||||
:get-org-permissions (partial get-org-permissions-api cfg)
|
||||
:delete-team (partial delete-team-api cfg)
|
||||
:remove-team-from-org (partial remove-team-from-org-api cfg)
|
||||
:get-subscription (partial get-subscription-api cfg)
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.time :as ct]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.nitrate :as nitrate]
|
||||
[app.rpc :as-alias rpc]
|
||||
@ -305,6 +306,20 @@
|
||||
(assert-not-default-team cfg team-id)
|
||||
(assert-membership cfg profile-id organization-id)
|
||||
|
||||
(when (contains? cf/flags :nitrate)
|
||||
(let [org-perms (nitrate/call cfg :get-org-permissions
|
||||
{:organization-id organization-id})]
|
||||
(if (nil? org-perms)
|
||||
(ex/raise :type :validation
|
||||
:code :not-allowed
|
||||
:hint "Unable to verify organization permissions")
|
||||
(let [create-perm (:create-teams org-perms)
|
||||
is-owner? (= profile-id (:owner-id org-perms))]
|
||||
(when (and (= create-perm "onlyMe") (not is-owner?))
|
||||
(ex/raise :type :validation
|
||||
:code :not-allowed
|
||||
:hint "You are not allowed to add teams in this organization"))))))
|
||||
|
||||
(let [team-members (db/query cfg :team-profile-rel {:team-id team-id})]
|
||||
;; Add teammates to the org if needed
|
||||
(doseq [{member-id :profile-id} team-members
|
||||
|
||||
@ -506,11 +506,27 @@
|
||||
(sv/defmethod ::create-team
|
||||
{::doc/added "1.17"
|
||||
::sm/params schema:create-team}
|
||||
[cfg {:keys [::rpc/profile-id] :as params}]
|
||||
[cfg {:keys [::rpc/profile-id organization-id] :as params}]
|
||||
|
||||
(quotes/check! cfg {::quotes/id ::quotes/teams-per-profile
|
||||
::quotes/profile-id profile-id})
|
||||
|
||||
;; When creating inside an org, verify the user has permission to do so.
|
||||
;; Fail closed: if org permissions cannot be fetched, deny the operation.
|
||||
(when (and organization-id (contains? cf/flags :nitrate))
|
||||
(let [org-perms (nitrate/call cfg :get-org-permissions
|
||||
{:organization-id organization-id})]
|
||||
(if (nil? org-perms)
|
||||
(ex/raise :type :validation
|
||||
:code :not-allowed
|
||||
:hint "Unable to verify organization permissions")
|
||||
(let [create-perm (:create-teams org-perms)
|
||||
is-owner? (= profile-id (:owner-id org-perms))]
|
||||
(when (and (= create-perm "onlyMe") (not is-owner?))
|
||||
(ex/raise :type :validation
|
||||
:code :not-allowed
|
||||
:hint "You are not allowed to create teams in this organization"))))))
|
||||
|
||||
(let [features (-> (cfeat/get-enabled-features cf/flags)
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/difference cfeat/no-team-inheritable-features))
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
[:slug ::sm/text]
|
||||
[:owner-id ::sm/uuid]
|
||||
[:avatar-bg-url ::sm/uri]
|
||||
[:logo-id {:optional true} [:maybe ::sm/uuid]]])
|
||||
[:logo-id {:optional true} [:maybe ::sm/uuid]]
|
||||
[:create-teams {:optional true} [:maybe [:enum "any" "onlyMe"]]]])
|
||||
|
||||
|
||||
(def schema:team-with-organization
|
||||
@ -31,7 +32,8 @@
|
||||
[:custom-photo :organization-custom-photo]
|
||||
[:slug :organization-slug]
|
||||
[:avatar-bg-url :organization-avatar-bg-url]
|
||||
[:owner-id :organization-owner-id]])
|
||||
[:owner-id :organization-owner-id]
|
||||
[:create-teams :organization-create-teams]])
|
||||
|
||||
(defn apply-organization
|
||||
"Updates a team map with organization fields sourced from org.
|
||||
@ -47,4 +49,4 @@
|
||||
(assoc acc team-k v)
|
||||
(dissoc acc team-k))))
|
||||
team
|
||||
organization->team-keys)))
|
||||
organization->team-keys)))
|
||||
|
||||
@ -134,3 +134,81 @@
|
||||
(rx/mapcat
|
||||
(fn [_]
|
||||
(rx/of (modal/hide))))))))
|
||||
|
||||
(defn show-add-team-to-org-modal
|
||||
"Fetches fresh team/org data, then shows the add-to-org modal
|
||||
restricted to orgs where the user has permission, or the no-permission
|
||||
modal if none qualify."
|
||||
[{:keys [team-id]}]
|
||||
(ptk/reify ::show-add-team-to-org-modal
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [profile-id (dm/get-in state [:profile :id])]
|
||||
(->> (rp/cmd! :get-teams)
|
||||
(rx/mapcat
|
||||
(fn [teams]
|
||||
(let [all-orgs (map dt/team->organization
|
||||
(filter #(and (:is-default %) (:organization-id %)) teams))
|
||||
orgs (filter (fn [org]
|
||||
(let [perm (:create-teams org)
|
||||
is-own? (= profile-id (:owner-id org))]
|
||||
(or (= perm "any") is-own?))) all-orgs)
|
||||
team (first (filter #(= (:id %) team-id) teams))
|
||||
on-confirm (fn [organization-id]
|
||||
(st/emit! (add-team-to-org {:team-id team-id
|
||||
:organization-id organization-id})))]
|
||||
(rx/of (dt/teams-fetched teams)
|
||||
(if (empty? orgs)
|
||||
(modal/show :no-org-allows-create-team {})
|
||||
(let [has-filtered? (< (count orgs) (count all-orgs))
|
||||
extra-props (when has-filtered?
|
||||
{:info-message-key "dashboard.select-org-modal.permission-info"})]
|
||||
(modal/show :select-organization-modal
|
||||
(merge {:organizations orgs
|
||||
:current-organization-id (:organization-id team)
|
||||
:on-confirm on-confirm
|
||||
:title-key "dashboard.select-org-modal.title"
|
||||
:choose-key "dashboard.select-org-modal.choose"
|
||||
:placeholder-key "dashboard.select-org-modal.select"
|
||||
:accept-key "dashboard.select-org-modal.accept"
|
||||
:cancel-key "labels.cancel"}
|
||||
extra-props)))))))))))))
|
||||
|
||||
(defn show-change-team-org-modal
|
||||
"Fetches fresh team/org data, then shows the change-org modal
|
||||
restricted to orgs where the user has permission, or the no-permission
|
||||
modal if none qualify."
|
||||
[{:keys [team-id]}]
|
||||
(ptk/reify ::show-change-team-org-modal
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [profile-id (dm/get-in state [:profile :id])]
|
||||
(->> (rp/cmd! :get-teams)
|
||||
(rx/mapcat
|
||||
(fn [teams]
|
||||
(let [all-orgs (map dt/team->organization
|
||||
(filter #(and (:is-default %) (:organization-id %)) teams))
|
||||
orgs (filter (fn [org]
|
||||
(let [perm (:create-teams org)
|
||||
is-own? (= profile-id (:owner-id org))]
|
||||
(or (= perm "any") is-own?))) all-orgs)
|
||||
team (first (filter #(= (:id %) team-id) teams))
|
||||
on-confirm (fn [organization-id]
|
||||
(st/emit! (add-team-to-org {:team-id team-id
|
||||
:organization-id organization-id})))]
|
||||
(rx/of (dt/teams-fetched teams)
|
||||
(if (empty? orgs)
|
||||
(modal/show :no-org-allows-create-team {})
|
||||
(let [has-filtered? (< (count orgs) (count all-orgs))
|
||||
extra-props (when has-filtered?
|
||||
{:info-message-key "dashboard.select-org-modal.permission-info"})]
|
||||
(modal/show :select-organization-modal
|
||||
(merge {:organizations orgs
|
||||
:current-organization-id (:organization-id team)
|
||||
:on-confirm on-confirm
|
||||
:title-key "dashboard.change-org-modal.title"
|
||||
:choose-key "dashboard.change-org-modal.choose"
|
||||
:placeholder-key "dashboard.change-org-modal.select"
|
||||
:accept-key "dashboard.change-org-modal.accept"
|
||||
:cancel-key "labels.cancel"}
|
||||
extra-props)))))))))))))
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
[app.config :as cf]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.media :as di]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.profile :as dp]
|
||||
[app.main.features :as features]
|
||||
[app.main.repo :as rp]
|
||||
@ -50,7 +51,8 @@
|
||||
:organization-name
|
||||
:organization-slug
|
||||
:organization-owner-id
|
||||
:organization-avatar-bg-url))]
|
||||
:organization-avatar-bg-url
|
||||
:organization-create-teams))]
|
||||
(update state :teams assoc id team-updated)))
|
||||
state
|
||||
teams)))))
|
||||
@ -63,6 +65,30 @@
|
||||
(->> (rp/cmd! :get-teams)
|
||||
(rx/map teams-fetched)))))
|
||||
|
||||
(defn check-and-create-team
|
||||
"Fetches fresh team data from the server to ensure up-to-date org
|
||||
permissions, then shows the team-form modal or a no-permission modal."
|
||||
[team-id]
|
||||
(ptk/reify ::check-and-create-team
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [profile-id (dm/get-in state [:profile :id])]
|
||||
(->> (rp/cmd! :get-teams)
|
||||
(rx/mapcat
|
||||
(fn [teams]
|
||||
(let [team (d/seek #(= (:id %) team-id) teams)
|
||||
in-org? (and (contains? cf/flags :nitrate) (:organization-id team))
|
||||
create-perm (:organization-create-teams team)
|
||||
is-owner? (= profile-id (:organization-owner-id team))
|
||||
can-create? (or (not in-org?) (= create-perm "any") is-owner?)]
|
||||
(rx/of (teams-fetched teams)
|
||||
(if can-create?
|
||||
(modal/show :team-form (if in-org?
|
||||
{:organization-id (:organization-id team)
|
||||
:organization-name (:organization-name team)}
|
||||
{}))
|
||||
(modal/show :no-permission-create-team {:organization-name (:organization-name team)})))))))))))
|
||||
|
||||
;; --- EVENT: fetch-members
|
||||
|
||||
(defn- members-fetched
|
||||
@ -598,5 +624,6 @@
|
||||
:avatar-bg-url (:organization-avatar-bg-url team)
|
||||
:custom-photo (:organization-custom-photo team)
|
||||
:name (:organization-name team)
|
||||
:default-team-id (:id team)})
|
||||
:default-team-id (:id team)
|
||||
:create-teams (:organization-create-teams team)})
|
||||
|
||||
|
||||
@ -384,10 +384,9 @@
|
||||
(mf/use-fn
|
||||
(mf/deps team)
|
||||
(fn []
|
||||
(let [params (if (and (contains? cf/flags :nitrate) (:organization-id team))
|
||||
{:organization-id (:organization-id team)}
|
||||
{})]
|
||||
(st/emit! (modal/show :team-form params)))))
|
||||
(if (contains? cf/flags :nitrate)
|
||||
(st/emit! (dtm/check-and-create-team (:id team)))
|
||||
(st/emit! (modal/show :team-form {})))))
|
||||
|
||||
on-team-click
|
||||
(mf/use-fn
|
||||
|
||||
@ -810,7 +810,7 @@
|
||||
(mf/defc select-organization-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :select-organization-modal}
|
||||
[{:keys [organizations current-organization-id on-confirm title-key text-key choose-key placeholder-key accept-key cancel-key]}]
|
||||
[{:keys [organizations current-organization-id on-confirm title-key text-key choose-key placeholder-key accept-key cancel-key info-message-key]}]
|
||||
(let [valid-organizations (mf/with-memo [organizations]
|
||||
(remove #(= (:id %) current-organization-id) organizations))
|
||||
options (mf/with-memo [valid-organizations]
|
||||
@ -848,6 +848,9 @@
|
||||
[:div {:class (stl/css :modal-content :modal-select-org-text)} (tr text-key)])
|
||||
|
||||
[:div
|
||||
(when info-message-key
|
||||
[:div {:class (stl/css :modal-select-org-info)}
|
||||
(tr info-message-key)])
|
||||
[:div {:class (stl/css :modal-select-org-content)}
|
||||
(tr choose-key)]
|
||||
[:> combobox* {:id "selected-id"
|
||||
@ -1366,18 +1369,29 @@
|
||||
can-edit (or (:is-owner permissions)
|
||||
(:is-admin permissions))
|
||||
|
||||
organizations (mf/deref refs/teams)
|
||||
organizations (mf/with-memo [organizations]
|
||||
(->> (vals organizations)
|
||||
(filter :is-default)
|
||||
(filter :organization-id)
|
||||
(map dtm/team->organization)))
|
||||
profile (mf/deref refs/profile)
|
||||
profile-id (:id profile)
|
||||
|
||||
all-organizations (mf/deref refs/teams)
|
||||
all-organizations (mf/with-memo [all-organizations]
|
||||
(->> (vals all-organizations)
|
||||
(filter :is-default)
|
||||
(filter :organization-id)
|
||||
(map dtm/team->organization)))
|
||||
|
||||
;; Filter to orgs where user is allowed to create/add teams
|
||||
organizations (mf/with-memo [all-organizations profile-id]
|
||||
(->> all-organizations
|
||||
(filter (fn [org]
|
||||
(let [perm (:create-teams org)
|
||||
is-owner? (= profile-id (:owner-id org))]
|
||||
(or (= perm "any") is-owner?))))))
|
||||
|
||||
can-change-organization? (mf/with-memo [organizations]
|
||||
(> (count organizations) 1))
|
||||
|
||||
can-add-to-organization? (mf/with-memo [organizations]
|
||||
(and (pos? (count organizations))
|
||||
can-add-to-organization? (mf/with-memo [organizations all-organizations]
|
||||
(and (pos? (count all-organizations))
|
||||
(not (:is-default team))))
|
||||
|
||||
show-org-options-menu*
|
||||
@ -1424,41 +1438,17 @@
|
||||
:accept-style :danger}]
|
||||
(st/emit! (modal/show params)))))
|
||||
|
||||
on-add-team-to-org-confirm
|
||||
(mf/use-fn
|
||||
(mf/deps team)
|
||||
(fn [organization-id]
|
||||
(let [organization (d/seek #(= organization-id (:id %)) organizations)]
|
||||
(when organization
|
||||
(st/emit! (dnt/add-team-to-org {:team-id (:id team)
|
||||
:organization-id organization-id}))))))
|
||||
|
||||
on-add-team-to-org
|
||||
(mf/use-fn
|
||||
(mf/deps organizations on-add-team-to-org-confirm)
|
||||
(mf/deps team)
|
||||
(fn []
|
||||
(st/emit! (modal/show :select-organization-modal {:organizations organizations
|
||||
:current-organization-id (:organization-id team)
|
||||
:on-confirm on-add-team-to-org-confirm
|
||||
:title-key "dashboard.select-org-modal.title"
|
||||
:choose-key "dashboard.select-org-modal.choose"
|
||||
:placeholder-key "dashboard.select-org-modal.select"
|
||||
:accept-key "dashboard.select-org-modal.accept"
|
||||
:cancel-key "labels.cancel"}))))
|
||||
(st/emit! (dnt/show-add-team-to-org-modal {:team-id (:id team)}))))
|
||||
|
||||
on-change-team-org
|
||||
(mf/use-fn
|
||||
(mf/deps organizations on-add-team-to-org-confirm)
|
||||
(mf/deps team)
|
||||
(fn []
|
||||
(st/emit! (modal/show :select-organization-modal {:organizations organizations
|
||||
:current-organization-id (:organization-id team)
|
||||
:on-confirm on-add-team-to-org-confirm
|
||||
:title-key "dashboard.change-org-modal.title"
|
||||
:text-key "dashboard.change-org-modal.text"
|
||||
:choose-key "dashboard.change-org-modal.choose"
|
||||
:placeholder-key "dashboard.change-org-modal.select"
|
||||
:accept-key "dashboard.change-org-modal.accept"
|
||||
:cancel-key "labels.cancel"}))))]
|
||||
(st/emit! (dnt/show-change-team-org-modal {:team-id (:id team)}))))]
|
||||
|
||||
(mf/with-effect [team]
|
||||
(dom/set-html-title (tr "title.team-settings"
|
||||
|
||||
@ -887,6 +887,13 @@
|
||||
margin-block-end: var(--sp-s);
|
||||
}
|
||||
|
||||
.modal-select-org-info {
|
||||
@include t.use-typography("body-medium");
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
margin-block-end: var(--sp-xxxl);
|
||||
}
|
||||
|
||||
.modal-select-org-title {
|
||||
@include t.use-typography("title-medium");
|
||||
|
||||
|
||||
@ -42,16 +42,19 @@
|
||||
(modal/hide))))
|
||||
|
||||
(defn- on-error
|
||||
[form _response]
|
||||
(let [id (get-in @form [:clean-data :id])]
|
||||
(if id
|
||||
(rx/of (ntf/error "Error on updating team."))
|
||||
(rx/of (ntf/error "Error on creating team.")))))
|
||||
[form organization-name response]
|
||||
(let [id (get-in @form [:clean-data :id])
|
||||
code (-> response ex-data :code)]
|
||||
(if (= code :not-allowed)
|
||||
(rx/of (modal/show :no-permission-create-team {:organization-name organization-name}))
|
||||
(if id
|
||||
(rx/of (ntf/error "Error on updating team."))
|
||||
(rx/of (ntf/error "Error on creating team."))))))
|
||||
|
||||
(defn- on-create-submit
|
||||
[form]
|
||||
[form organization-name]
|
||||
(let [mdata {:on-success (partial on-create-success form)
|
||||
:on-error (partial on-error form)}
|
||||
:on-error (partial on-error form organization-name)}
|
||||
data (:clean-data @form)
|
||||
params (cond-> {:name (:name data)}
|
||||
(:organization-id data) (assoc :organization-id (:organization-id data)))]
|
||||
@ -61,23 +64,23 @@
|
||||
(defn- on-update-submit
|
||||
[form]
|
||||
(let [mdata {:on-success (partial on-update-success form)
|
||||
:on-error (partial on-error form)}
|
||||
:on-error (partial on-error form nil)}
|
||||
data (:clean-data @form)
|
||||
team (select-keys data [:id :name])] ;; Only send name and id for updates
|
||||
team (select-keys data [:id :name])]
|
||||
(st/emit! (dtm/update-team (with-meta team mdata))
|
||||
(modal/hide))))
|
||||
|
||||
(defn- on-submit
|
||||
[form _]
|
||||
[organization-name form _]
|
||||
(let [data (:clean-data @form)]
|
||||
(if (:id data)
|
||||
(on-update-submit form)
|
||||
(on-create-submit form))))
|
||||
(on-create-submit form organization-name))))
|
||||
|
||||
(mf/defc team-form-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :team-form}
|
||||
[{:keys [team organization-id] :as props}]
|
||||
[{:keys [team organization-id organization-name] :as props}]
|
||||
(let [initial (mf/use-memo
|
||||
(mf/deps team organization-id)
|
||||
(fn []
|
||||
@ -89,18 +92,22 @@
|
||||
organization-id (assoc :organization-id organization-id)))))
|
||||
form (fm/use-form :schema schema:team-form
|
||||
:initial initial)
|
||||
on-submit* (mf/use-fn
|
||||
(mf/deps organization-name)
|
||||
(partial on-submit organization-name))
|
||||
handle-keydown
|
||||
(mf/use-fn
|
||||
(mf/deps organization-name)
|
||||
(fn [e]
|
||||
(when (kbd/enter? e)
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e)
|
||||
(on-submit form e))))]
|
||||
(on-submit organization-name form e))))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:& fm/form {:form form
|
||||
:on-submit on-submit
|
||||
:on-submit on-submit*
|
||||
:class (stl/css :team-form)}
|
||||
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
@ -132,3 +139,31 @@
|
||||
:class (stl/css :accept-btn)}]]]]]]))
|
||||
|
||||
|
||||
(mf/defc no-permission-create-team-modal*
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :no-permission-create-team}
|
||||
[{:keys [organization-name]}]
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
[:h2 {:class (stl/css :modal-title)}
|
||||
(tr "labels.create-team")]
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click modal/hide!} deprecated-icon/close]]
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:div (tr "dashboard.no-permission-create-team.message" organization-name)]]]])
|
||||
|
||||
|
||||
(mf/defc no-org-allows-create-team-modal*
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :no-org-allows-create-team}
|
||||
[_props]
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
[:h2 {:class (stl/css :modal-title)}
|
||||
(tr "dashboard.select-org-modal.title")]
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click modal/hide!} deprecated-icon/close]]
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:div (tr "dashboard.no-org-allows-create-team.message")]]]])
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "ds/_utils.scss" as *;
|
||||
@use "ds/typography.scss" as t;
|
||||
|
||||
.modal-overlay {
|
||||
@extend %modal-overlay-base;
|
||||
@ -15,11 +17,11 @@
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
margin-bottom: deprecated.$s-24;
|
||||
margin-block-end: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include deprecated.uppercase-title-typography;
|
||||
@include t.use-typography("headline-large");
|
||||
|
||||
color: var(--modal-title-foreground-color);
|
||||
}
|
||||
@ -29,33 +31,31 @@
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin-bottom: deprecated.$s-24;
|
||||
margin-block-end: var(--sp-xxl);
|
||||
color: var(--color-foreground-secondary);
|
||||
|
||||
@include t.use-typography("body-large");
|
||||
}
|
||||
|
||||
.team-form {
|
||||
min-width: deprecated.$s-400;
|
||||
min-inline-size: px2rem(400);
|
||||
}
|
||||
|
||||
.group-name-input {
|
||||
@extend %input-element-label;
|
||||
@include deprecated.body-small-typography;
|
||||
|
||||
margin-bottom: deprecated.$s-8;
|
||||
margin-block-end: var(--sp-s);
|
||||
}
|
||||
|
||||
label {
|
||||
@include deprecated.flex-column;
|
||||
@include deprecated.body-small-typography;
|
||||
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
height: 100%;
|
||||
|
||||
input {
|
||||
@include deprecated.body-small-typography;
|
||||
}
|
||||
}
|
||||
.group-name-input label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xs);
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
|
||||
@ -9395,4 +9395,13 @@ msgid "subscription.current-plan.nitrate-trial"
|
||||
msgstr "Nitrate Trial"
|
||||
|
||||
msgid "subscription.current-plan.enterprise"
|
||||
msgstr "Enterprise"
|
||||
msgstr "Enterprise"
|
||||
|
||||
msgid "dashboard.no-permission-create-team.message"
|
||||
msgstr "You are not allowed to create teams within %s organization. If you need more information, contact the owner."
|
||||
|
||||
msgid "dashboard.no-org-allows-create-team.message"
|
||||
msgstr "You don't have permission to add teams to any of your organizations."
|
||||
|
||||
msgid "dashboard.select-org-modal.permission-info"
|
||||
msgstr "Here you find all your organizations where you are allowed to create or add teams."
|
||||
|
||||
@ -9103,4 +9103,13 @@ msgid "subscription.current-plan.nitrate-trial"
|
||||
msgstr "Nitrate (Prueba)"
|
||||
|
||||
msgid "subscription.current-plan.enterprise"
|
||||
msgstr "Enterprise"
|
||||
msgstr "Enterprise"
|
||||
|
||||
msgid "dashboard.no-permission-create-team.message"
|
||||
msgstr "No tienes permiso para crear equipos en la organización %s. Si necesitas más información, contacta con el propietario."
|
||||
|
||||
msgid "dashboard.no-org-allows-create-team.message"
|
||||
msgstr "No tienes permiso para añadir equipos a ninguna de tus organizaciones."
|
||||
|
||||
msgid "dashboard.select-org-modal.permission-info"
|
||||
msgstr "Aquí encontrarás todas las organizaciones en las que tienes permiso para crear o añadir equipos."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user