Add nitrate remove team from org

This commit is contained in:
Pablo Alba 2026-04-16 11:46:05 +02:00 committed by GitHub
parent 65a0fcb15b
commit 39f4c13493
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 298 additions and 26 deletions

View File

@ -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)}))

View File

@ -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))

View File

@ -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)))

View File

@ -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

View 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})))

View File

@ -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))))

View File

@ -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))))))))

View File

@ -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)

View File

@ -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")]

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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"