🐛 Fix nitrate invitations org ux review

This commit is contained in:
Pablo Alba 2026-04-16 11:18:11 +02:00 committed by GitHub
parent 81061013b1
commit ac472c615a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 73 additions and 19 deletions

1
.gitignore vendored
View File

@ -82,3 +82,4 @@
/**/node_modules /**/node_modules
/**/.yarn/* /**/.yarn/*
/.pnpm-store /.pnpm-store
/.vscode

View File

@ -174,12 +174,12 @@
<div class="mj-column-per-100 mj-outlook-group-fix" <div class="mj-column-per-100 mj-outlook-group-fix"
style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"> style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;"
width="100%"> width="100%">
<tr> <tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div <div
style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;"> style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;">
Hi{{ user-name|abbreviate:25 }}, Hi{% if user-name %} {{ user-name|abbreviate:25 }}{% endif %},
</div> </div>
</td> </td>
</tr> </tr>
@ -187,7 +187,7 @@
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div <div
style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;"> style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;">
<b>{{invited-by|abbreviate:25}}</b> sent you an invitation to join the organization {{ org-name|abbreviate:25 }}: <b>{{invited-by|abbreviate:25}}</b> sent you an invitation to join the organization:
</div> </div>
</td> </td>
</tr> </tr>
@ -195,14 +195,19 @@
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div <div
style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;"> style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;">
<span style="display:inline-block;vertical-align:middle;width:20px;height:20px;border-radius: 50%;background-image:url('{{org-logo}}');overflow:hidden;text-align:center;font-weight: bold;font-size: 9px;line-height: 20px;"> <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="20" height="20" style="display:inline-block;vertical-align:middle;">
{{ org-initials }} <tr>
</span> <td width="20" height="20" align="center" valign="middle"
background="{{org-logo}}"
style="width:20px;height:20px;text-align:center;font-weight:bold;font-size:9px;line-height:20px;color:#ffffff;background-size:cover;background-position:center;background-repeat:no-repeat;border-radius: 50%;color:black">
{{org-initials}}
</td>
</tr>
</table>
<span style="display:inline-block; vertical-align: middle;padding-left:5px;height:20px;line-height: 20px;"> <span style="display:inline-block; vertical-align: middle;padding-left:5px;height:20px;line-height: 20px;">
“{{ org-name|abbreviate:25 }}” “{{ org-name|abbreviate:25 }}”
</span> </span>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -1,6 +1,7 @@
(ns app.nitrate (ns app.nitrate
"Module that make calls to the external nitrate aplication" "Module that make calls to the external nitrate aplication"
(:require (:require
[app.common.exceptions :as ex]
[app.common.json :as json] [app.common.json :as json]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
@ -130,7 +131,8 @@
(def ^:private schema:profile-org (def ^:private schema:profile-org
[:map [:map
[:is-member :boolean] [:is-member :boolean]
[:organization-id ::sm/uuid]]) [:organization-id ::sm/uuid]
[:default-team-id [:maybe ::sm/uuid]]])
;; TODO Unify with schemas on backend/src/app/http/management.clj ;; TODO Unify with schemas on backend/src/app/http/management.clj
@ -214,6 +216,17 @@
team-id) team-id)
schema:organization params))) schema:organization params)))
(defn- get-org-membership-api
[cfg {:keys [profile-id org-id] :as params}]
(let [baseuri (cf/get :nitrate-backend-uri)]
(request-to-nitrate cfg :get
(str baseuri
"/api/organizations/"
org-id
"/members/"
profile-id)
schema:profile-org params)))
(defn- get-org-membership-by-team-api (defn- get-org-membership-by-team-api
[cfg {:keys [profile-id team-id] :as params}] [cfg {:keys [profile-id team-id] :as params}]
(let [baseuri (cf/get :nitrate-backend-uri)] (let [baseuri (cf/get :nitrate-backend-uri)]
@ -308,6 +321,7 @@
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :nitrate)
{:get-team-org (partial get-team-org-api cfg) {:get-team-org (partial get-team-org-api cfg)
:set-team-org (partial set-team-org-api cfg) :set-team-org (partial set-team-org-api cfg)
:get-org-membership (partial get-org-membership-api cfg)
:get-org-membership-by-team (partial get-org-membership-by-team-api cfg) :get-org-membership-by-team (partial get-org-membership-by-team-api cfg)
:get-org-summary (partial get-org-summary-api cfg) :get-org-summary (partial get-org-summary-api cfg)
:add-profile-to-org (partial add-profile-to-org-api cfg) :add-profile-to-org (partial add-profile-to-org-api cfg)
@ -369,9 +383,10 @@
:is-default (:is-default params)) :is-default (:is-default params))
result (call cfg :set-team-org params)] result (call cfg :set-team-org params)]
(when (nil? result) (when (nil? result)
(throw (ex-info "Failed to set team organization" (ex/raise :type :internal
{:team-id (:id team) :code :failed-to-set-team-org
:organization-id (:organization-id params)}))) :context {:team-id (:id team)
:organization-id (:organization-id params)}))
team)) team))

View File

@ -16,6 +16,7 @@
[app.http.session :as session] [app.http.session :as session]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.main :as-alias main] [app.main :as-alias main]
[app.nitrate :as nitrate]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.commands.profile :as profile] [app.rpc.commands.profile :as profile]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
@ -175,13 +176,13 @@
org-id (assoc :org-id org-id))) org-id (assoc :org-id org-id)))
profile (db/get* conn :profile profile (db/get* conn :profile
{:id profile-id} {:id profile-id}
{:columns [:id :email]}) {:columns [:id :email :default-team-id]})
registration-disabled? (not (contains? cf/flags :registration))] registration-disabled? (not (contains? cf/flags :registration))
(when (nil? invitation) org-invitation? (and (contains? cf/flags :nitrate) org-id)
(ex/raise :type :validation membership (when org-invitation?
:code :invalid-token (nitrate/call cfg :get-org-membership {:profile-id profile-id
:hint "no invitation associated with the token")) :org-id org-id}))]
(if profile (if profile
(do (do
@ -191,6 +192,23 @@
:code :invalid-token :code :invalid-token
:hint "logged-in user does not matches the invitation")) :hint "logged-in user does not matches the invitation"))
(when (:is-member membership)
(ex/raise :type :validation
:code :already-an-org-member
:team-id (:default-team-id membership)
:hint "the user is already a member of the organization"))
(when (and org-invitation? (not (:organization-id membership)))
(ex/raise :type :validation
:code :org-not-found
:team-id (:default-team-id profile)
:hint "the organization doesn't exist"))
(when (nil? invitation)
(ex/raise :type :validation
:code :invalid-token
:hint "no invitation associated with the token"))
;; if we have logged-in user and it matches the invitation we proceed ;; if we have logged-in user and it matches the invitation we proceed
;; with accepting the invitation and joining the current profile to the ;; with accepting the invitation and joining the current profile to the

View File

@ -76,8 +76,17 @@
(fn [tdata] (fn [tdata]
(handle-token tdata)) (handle-token tdata))
(fn [cause] (fn [cause]
(let [{:keys [type code] :as error} (ex-data cause)] (let [{:keys [type code team-id] :as error} (ex-data cause)]
(cond (cond
(= :invalid-token-already-member code)
(st/emit!
(rt/nav :dashboard-recent {:team-id team-id}))
(= :org-not-found code)
(st/emit!
(rt/nav :dashboard-recent {:team-id team-id})
(ntf/error (tr "errors.org-not-found")))
(or (= :validation type) (or (= :validation type)
(= :invalid-token code) (= :invalid-token code)
(= :token-expired (:reason error))) (= :token-expired (:reason error)))

View File

@ -1667,6 +1667,9 @@ msgstr "Email or password is incorrect."
msgid "errors.wrong-old-password" msgid "errors.wrong-old-password"
msgstr "Old password is incorrect" msgstr "Old password is incorrect"
msgid "errors.org-not-found"
msgstr "That organization doesn't exists"
#: src/app/main/ui/settings/feedback.cljs:120 #: src/app/main/ui/settings/feedback.cljs:120
msgid "feedback.description" msgid "feedback.description"
msgstr "Description" msgstr "Description"

View File

@ -1636,6 +1636,9 @@ msgstr "El email o la contraseña son incorrectos."
msgid "errors.wrong-old-password" msgid "errors.wrong-old-password"
msgstr "La contraseña anterior no es correcta" msgstr "La contraseña anterior no es correcta"
msgid "errors.org-not-found"
msgstr "Esa organización no existe"
#: src/app/main/ui/settings/feedback.cljs:120 #: src/app/main/ui/settings/feedback.cljs:120
msgid "feedback.description" msgid "feedback.description"
msgstr "Descripción" msgstr "Descripción"