mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
✨ Add nitrate remove team from org
This commit is contained in:
parent
65a0fcb15b
commit
39f4c13493
@ -286,6 +286,17 @@
|
||||
"/remove-user")
|
||||
nil params)))
|
||||
|
||||
(defn- remove-team-from-org-api
|
||||
[cfg {:keys [team-id organization-id] :as params}]
|
||||
(let [baseuri (cf/get :nitrate-backend-uri)
|
||||
params (assoc params :request-params {:team-id team-id})]
|
||||
(request-to-nitrate cfg :post
|
||||
(str baseuri
|
||||
"/api/organizations/"
|
||||
organization-id
|
||||
"/remove-team")
|
||||
nil params)))
|
||||
|
||||
(defn- delete-team-api
|
||||
[cfg {:keys [team-id] :as params}]
|
||||
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||
@ -327,6 +338,7 @@
|
||||
:add-profile-to-org (partial add-profile-to-org-api cfg)
|
||||
:remove-profile-from-org (partial remove-profile-from-org-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)
|
||||
:connectivity (partial get-connectivity-api cfg)}))
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.notifications :as notifications]
|
||||
[app.util.services :as sv]))
|
||||
|
||||
|
||||
@ -169,3 +170,30 @@
|
||||
(nitrate/call cfg :remove-profile-from-org {:profile-id profile-id :org-id org-id})
|
||||
|
||||
nil))
|
||||
|
||||
|
||||
(def ^:private schema:remove-team-from-org
|
||||
[:map
|
||||
[:team-id ::sm/uuid]
|
||||
[:organization-id ::sm/uuid]])
|
||||
|
||||
(sv/defmethod ::remove-team-from-org
|
||||
{::doc/added "2.16"
|
||||
::sm/params schema:remove-team-from-org}
|
||||
[cfg {:keys [::rpc/profile-id team-id organization-id organization-name]}]
|
||||
(let [perms (teams/get-permissions cfg profile-id team-id)
|
||||
team (teams/get-team-info cfg {:id team-id})]
|
||||
|
||||
(when-not (:is-owner perms)
|
||||
(ex/raise :type :validation
|
||||
:code :insufficient-permissions))
|
||||
|
||||
(when (:is-default team)
|
||||
(ex/raise :type :validation
|
||||
:code :cant-remove-default-team))
|
||||
|
||||
;; Api call to nitrate
|
||||
(nitrate/call cfg :remove-team-from-org {:team-id team-id :organization-id organization-id})
|
||||
|
||||
(notifications/notify-team-change cfg team-id nil nil organization-name "dashboard.team-no-longer-belong-org")
|
||||
nil))
|
||||
@ -471,8 +471,8 @@
|
||||
;; --- COMMAND QUERY: get-team-info
|
||||
|
||||
(defn get-team-info
|
||||
[{:keys [::db/conn] :as cfg} {:keys [id] :as params}]
|
||||
(-> (db/get* conn :team
|
||||
[cfg {:keys [id] :as params}]
|
||||
(-> (db/get* cfg :team
|
||||
{:id id}
|
||||
{::sql/columns [:id :is-default :features]})
|
||||
(decode-row)))
|
||||
|
||||
@ -13,16 +13,15 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.profile :refer [schema:profile, schema:basic-profile]]
|
||||
[app.common.types.team :refer [schema:team]]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.msgbus :as mbus]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.commands.profile :as profile]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.rpc.commands.teams-invitations :as ti]
|
||||
[app.rpc.doc :as doc]
|
||||
[app.rpc.notifications :as notifications]
|
||||
[app.util.services :as sv]))
|
||||
|
||||
|
||||
@ -89,19 +88,7 @@
|
||||
[:organization-id ::sm/uuid]
|
||||
[:organization-name ::sm/text]])
|
||||
|
||||
(defn notify-team-change
|
||||
[cfg team-id team-name organization-id organization-name notification]
|
||||
(let [msgbus (::mbus/msgbus cfg)]
|
||||
(mbus/pub! msgbus
|
||||
;;TODO There is a bug on dashboard with teams notifications.
|
||||
;;For now we send it to uuid/zero instead of team-id
|
||||
:topic uuid/zero
|
||||
:message {:type :team-org-change
|
||||
:team-id team-id
|
||||
:team-name team-name
|
||||
:organization-id organization-id
|
||||
:organization-name organization-name
|
||||
:notification notification})))
|
||||
|
||||
|
||||
|
||||
(sv/defmethod ::notify-team-change
|
||||
@ -110,7 +97,8 @@
|
||||
::sm/params schema:notify-team-change
|
||||
::rpc/auth false}
|
||||
[cfg {:keys [id organization-id organization-name]}]
|
||||
(notify-team-change cfg id nil organization-id organization-name nil))
|
||||
(notifications/notify-team-change cfg id nil organization-id organization-name nil)
|
||||
nil)
|
||||
|
||||
;; ---- API: notify-user-added-to-organization
|
||||
|
||||
@ -248,7 +236,7 @@ RETURNING id, name;")
|
||||
|
||||
;; Notify users
|
||||
(doseq [team updated-teams]
|
||||
(notify-team-change cfg (:id team) (:name team) nil org-name "dashboard.org-deleted"))))))))
|
||||
(notifications/notify-team-change cfg (:id team) (:name team) nil org-name "dashboard.org-deleted"))))))))
|
||||
|
||||
;; ---- API: get-profile-by-email
|
||||
|
||||
|
||||
24
backend/src/app/rpc/notifications.clj
Normal file
24
backend/src/app/rpc/notifications.clj
Normal file
@ -0,0 +1,24 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.rpc.notifications
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.msgbus :as mbus]))
|
||||
|
||||
(defn notify-team-change
|
||||
[cfg team-id team-name organization-id organization-name notification]
|
||||
(let [msgbus (::mbus/msgbus cfg)]
|
||||
(mbus/pub! msgbus
|
||||
;;TODO There is a bug on dashboard with teams notifications.
|
||||
;;For now we send it to uuid/zero instead of team-id
|
||||
:topic uuid/zero
|
||||
:message {:type :team-org-change
|
||||
:team-id team-id
|
||||
:team-name team-name
|
||||
:organization-id organization-id
|
||||
:organization-name organization-name
|
||||
:notification notification})))
|
||||
@ -702,9 +702,11 @@
|
||||
(if (contains? cf/flags :nitrate)
|
||||
(d/update-in-when state [:teams team-id]
|
||||
(fn [team]
|
||||
(cond-> (assoc team
|
||||
:organization-id organization-id
|
||||
:organization-name organization-name)
|
||||
(cond-> team
|
||||
(some? organization-id) (assoc :organization-id organization-id)
|
||||
(nil? organization-id) (dissoc :organization-id)
|
||||
(some? organization-name) (assoc :organization-name organization-name)
|
||||
(nil? organization-name) (dissoc :organization-name)
|
||||
team-name (assoc :name team-name))))
|
||||
state))))
|
||||
|
||||
|
||||
@ -111,3 +111,15 @@
|
||||
:type :toast
|
||||
:level :success}))))
|
||||
(rx/catch on-error))))))
|
||||
|
||||
|
||||
(defn remove-team-from-org
|
||||
[{:keys [team-id organization-id organization-name] :as params}]
|
||||
(ptk/reify ::remove-team-from-org
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(->> (rp/cmd! ::remove-team-from-org {:team-id team-id :organization-id organization-id :organization-name organization-name})
|
||||
(rx/mapcat
|
||||
(fn [_]
|
||||
(rx/of
|
||||
(modal/hide))))))))
|
||||
@ -34,7 +34,8 @@
|
||||
items
|
||||
cancel-label
|
||||
accept-label
|
||||
accept-style] :as props}]
|
||||
accept-style
|
||||
hint-level] :as props}]
|
||||
(let [on-accept (or on-accept identity)
|
||||
on-cancel (or on-cancel identity)
|
||||
message (or message (tr "ds.confirm-title"))
|
||||
@ -84,7 +85,7 @@
|
||||
(when (and (string? scd-message) (not= scd-message ""))
|
||||
[:h3 {:class (stl/css :modal-scd-msg)} scd-message])
|
||||
(when (string? hint)
|
||||
[:> context-notification* {:level :info
|
||||
[:> context-notification* {:level (or hint-level :info)
|
||||
:appearance :ghost}
|
||||
hint])
|
||||
(when (string? error-msg)
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.nitrate :as dnt]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.team :as dtm]
|
||||
[app.main.refs :as refs]
|
||||
@ -21,6 +22,7 @@
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.components.org-avatar :refer [org-avatar*]]
|
||||
[app.main.ui.dashboard.change-owner]
|
||||
[app.main.ui.dashboard.subscription :refer [members-cta*
|
||||
show-subscription-members-banner?
|
||||
@ -44,6 +46,9 @@
|
||||
(def ^:private menu-icon
|
||||
(deprecated-icon/icon-xref :menu (stl/css :menu-icon)))
|
||||
|
||||
(def ^:private org-menu-icon
|
||||
(deprecated-icon/icon-xref :menu (stl/css :org-menu-icon)))
|
||||
|
||||
(def ^:private warning-icon
|
||||
(deprecated-icon/icon-xref :msg-warning (stl/css :warning-icon)))
|
||||
|
||||
@ -1274,7 +1279,8 @@
|
||||
(mf/defc team-settings-page*
|
||||
{::mf/props :obj}
|
||||
[{:keys [team]}]
|
||||
(let [finput (mf/use-ref)
|
||||
(let [nitrate? (contains? cfg/flags :nitrate)
|
||||
finput (mf/use-ref)
|
||||
|
||||
members (get team :members)
|
||||
stats (get team :stats)
|
||||
@ -1285,12 +1291,49 @@
|
||||
can-edit (or (:is-owner permissions)
|
||||
(:is-admin permissions))
|
||||
|
||||
show-org-options-menu*
|
||||
(mf/use-state false)
|
||||
|
||||
show-org-options-menu?
|
||||
(deref show-org-options-menu*)
|
||||
|
||||
on-show-options-click
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! show-org-options-menu* not)))
|
||||
|
||||
close-org-options-menu
|
||||
(mf/use-fn #(reset! show-org-options-menu* false))
|
||||
|
||||
on-image-click
|
||||
(mf/use-fn #(dom/click (mf/ref-val finput)))
|
||||
|
||||
on-file-selected
|
||||
(fn [file]
|
||||
(st/emit! (dtm/update-team-photo file)))]
|
||||
(st/emit! (dtm/update-team-photo file)))
|
||||
|
||||
remove-team-from-org-fn
|
||||
(mf/use-fn
|
||||
(mf/deps team)
|
||||
(fn []
|
||||
(st/emit! (dnt/remove-team-from-org {:team-id (:id team)
|
||||
:organization-id (:organization-id team)
|
||||
:organization-name (:organization-name team)}))))
|
||||
|
||||
on-remove-team-from-org
|
||||
(mf/use-fn
|
||||
(mf/deps team)
|
||||
(fn []
|
||||
(let [params {:type :confirm
|
||||
:title (tr "modals.remove-team-org.title")
|
||||
:message (tr "modals.remove-team-org.text" (:name team) (:organization-name team))
|
||||
:hint (tr "modals.remove-team-org.info")
|
||||
:hint-level :default
|
||||
:accept-label (tr "modals.remove-team-org.accept")
|
||||
:on-accept remove-team-from-org-fn
|
||||
:accept-style :danger}]
|
||||
(st/emit! (modal/show params)))))]
|
||||
|
||||
(mf/with-effect [team]
|
||||
(dom/set-html-title (tr "title.team-settings"
|
||||
@ -1324,6 +1367,35 @@
|
||||
[:div {:class (stl/css :block-text)}
|
||||
(:name team)]]
|
||||
|
||||
(when nitrate?
|
||||
[:div {:class (stl/css :block)}
|
||||
[:div {:class (stl/css :block-label)}
|
||||
(tr "dashboard.team-organization")]
|
||||
(if (:organization-id team)
|
||||
[:div {:class (stl/css :block-content)}
|
||||
[:div {:class (stl/css :org-block-content)}
|
||||
[:> org-avatar* {:org team :size "xxxl"}]
|
||||
[:span {:class (stl/css :block-text)}
|
||||
(:organization-name team)]
|
||||
|
||||
(when (and (:is-owner permissions) (not (:is-default team)))
|
||||
[:*
|
||||
[:> button* {:variant "ghost"
|
||||
:type "button"
|
||||
:class (stl/css-case :org-options-btn (not show-org-options-menu?) :org-options-btn-open show-org-options-menu?)
|
||||
:on-click on-show-options-click}
|
||||
org-menu-icon
|
||||
|
||||
[:& dropdown {:show show-org-options-menu? :on-close close-org-options-menu :dropdown-id "org-options"}
|
||||
[:ul {:class (stl/css :org-dropdown)
|
||||
:role "listbox"}
|
||||
[:li {:on-click on-remove-team-from-org
|
||||
:class (stl/css :org-dropdown-item)}
|
||||
(tr "dashboard.team-organization.remove")]]]]])]]
|
||||
[:div {:class (stl/css :block-content)}
|
||||
[:span {:class (stl/css :block-text)}
|
||||
(tr "dashboard.team-organization.none")]])])
|
||||
|
||||
[:div {:class (stl/css :block)}
|
||||
[:div {:class (stl/css :block-label)}
|
||||
(tr "dashboard.team-members")]
|
||||
|
||||
@ -51,6 +51,7 @@
|
||||
|
||||
.block-text {
|
||||
color: var(--color-foreground-primary);
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
.block-content {
|
||||
@ -869,3 +870,81 @@
|
||||
margin-block-start: var(--sp-xxxl);
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.org-block-content {
|
||||
display: grid;
|
||||
grid-template-columns: var(--sp-xxxl) 1fr var(--sp-xxxl);
|
||||
align-items: center;
|
||||
gap: var(--sp-m);
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.org-options-btn {
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
|
||||
--stroke-color: var(--color-foreground-primary);
|
||||
|
||||
&:hover {
|
||||
--stroke-color: var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.org-options-btn-open {
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
|
||||
--stroke-color: var(--color-accent-primary);
|
||||
|
||||
background-color: var(--color-background-tertiary);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.org-menu-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(--stroke-color);
|
||||
}
|
||||
|
||||
.org-dropdown {
|
||||
box-shadow: var(--el-shadow-dark);
|
||||
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;
|
||||
top: var(--sp-xxxl);
|
||||
width: fit-content;
|
||||
min-width: $sz-160;
|
||||
}
|
||||
|
||||
.org-dropdown-item {
|
||||
@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;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-background-quaternary);
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,6 +344,9 @@ msgstr "Restore file"
|
||||
msgid "dashboard.org-deleted"
|
||||
msgstr "The %s organization has been deleted."
|
||||
|
||||
msgid "dashboard.team-no-longer-belong-org"
|
||||
msgstr "This team no longer belongs to the organization %s"
|
||||
|
||||
#: src/app/main/ui/dashboard/placeholder.cljs:41
|
||||
msgid "dashboard.add-file"
|
||||
msgstr "Add file"
|
||||
@ -1126,6 +1129,18 @@ msgstr "Team info"
|
||||
msgid "dashboard.team-members"
|
||||
msgstr "Team members"
|
||||
|
||||
msgid "dashboard.team-organization"
|
||||
msgstr "Team organization"
|
||||
|
||||
msgid "dashboard.team-organization.none"
|
||||
msgstr "This team is not part of any organization"
|
||||
|
||||
msgid "dashboard.team-organization.change"
|
||||
msgstr "Change team organization"
|
||||
|
||||
msgid "dashboard.team-organization.remove"
|
||||
msgstr "Remove team from organization"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:1344
|
||||
msgid "dashboard.team-projects"
|
||||
msgstr "Team projects"
|
||||
@ -9089,6 +9104,18 @@ msgstr "Go to dashboard"
|
||||
msgid "webgl.modals.webgl-unavailable.cta-troubleshooting"
|
||||
msgstr "Troubleshooting guide"
|
||||
|
||||
msgid "modals.remove-team-org.title"
|
||||
msgstr "REMOVE TEAM FROM THE ORGANIZATION"
|
||||
|
||||
msgid "modals.remove-team-org.text"
|
||||
msgstr "Are you sure you want to remove the '%s' team from the '%s' organization?"
|
||||
|
||||
msgid "modals.remove-team-org.info"
|
||||
msgstr "Projects and files will remain available to team members, but the organization's settings will no longer apply."
|
||||
|
||||
msgid "modals.remove-team-org.accept"
|
||||
msgstr "Remove from organization"
|
||||
|
||||
#, unused
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Click to close the path"
|
||||
|
||||
@ -353,6 +353,9 @@ msgstr "Restaurar archivo"
|
||||
msgid "dashboard.org-deleted"
|
||||
msgstr "La organización %s se ha borrado."
|
||||
|
||||
msgid "dashboard.team-no-longer-belong-org"
|
||||
msgstr "Este equipo ya no pertenece a la organización %s"
|
||||
|
||||
#: src/app/main/ui/dashboard/placeholder.cljs:41
|
||||
msgid "dashboard.add-file"
|
||||
msgstr "Añadir archivo"
|
||||
@ -1130,6 +1133,18 @@ msgstr "Información del equipo"
|
||||
msgid "dashboard.team-members"
|
||||
msgstr "Integrantes del equipo"
|
||||
|
||||
msgid "dashboard.team-organization"
|
||||
msgstr "Organización del equipo"
|
||||
|
||||
msgid "dashboard.team-organization.none"
|
||||
msgstr "Este equipo no pertenece a ninguna organización"
|
||||
|
||||
msgid "dashboard.team-organization.change"
|
||||
msgstr "Cambiar el equipo de organización"
|
||||
|
||||
msgid "dashboard.team-organization.remove"
|
||||
msgstr "Eliminar equipo de la organización"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:1344
|
||||
msgid "dashboard.team-projects"
|
||||
msgstr "Proyectos del equipo"
|
||||
@ -8867,6 +8882,18 @@ msgstr "Ir al panel"
|
||||
msgid "webgl.modals.webgl-unavailable.cta-troubleshooting"
|
||||
msgstr "Guía de solución de problemas"
|
||||
|
||||
msgid "modals.remove-team-org.title"
|
||||
msgstr "ELIMINAR EQUIPO DE LA ORGANIZACIÓN"
|
||||
|
||||
msgid "modals.remove-team-org.text"
|
||||
msgstr "¿Estás seguro de que quieres eliminar el equipo %s de la organización %s?"
|
||||
|
||||
msgid "modals.remove-team-org.info"
|
||||
msgstr "Los proyectos y archivos seguirán estando disponibles para los miembros del equipo, pero la configuración de la organización dejará de aplicarse."
|
||||
|
||||
msgid "modals.remove-team-org.accept"
|
||||
msgstr "Eliminar de la organización"
|
||||
|
||||
#, unused
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Pulsar para cerrar la ruta"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user