Fix editing project team member permissions (#5315)

* Fix editing project team member permissions

* prepare

* add success notifications

---------

Co-authored-by: tdgao <mr.trumgao@gmail.com>
This commit is contained in:
aecsocket
2026-02-06 22:56:36 +00:00
committed by GitHub
parent d713cea180
commit b34564a770
2 changed files with 48 additions and 16 deletions

View File

@@ -499,9 +499,26 @@
</div> </div>
</template> </template>
<div class="input-group"> <div class="input-group">
<!--
if we save changes and update an org member which:
- is not currently overridden (!allOrgMembers[index].oldOverride)
- and we're not changing them to be overridden (!allOrgMembers[index].override)
then we end up editing an org member which, in the backend, doesn't exist.
the api doesn't let us do that, we can only do:
- !override -> override: POST member
- override -> !override: DELETE member
- override -> override: PATCH member
- !override -> !override: do nothing
we don't allow clicking the button in that last case.
-->
<button <button
class="iconified-button brand-button" class="iconified-button brand-button"
:disabled="(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER" :disabled="
(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
(!allOrgMembers[index].oldOverride && !allOrgMembers[index].override)
"
@click="updateOrgMember(index)" @click="updateOrgMember(index)"
> >
<SaveIcon /> <SaveIcon />
@@ -565,22 +582,21 @@ function initMembers() {
const selectedMembersForOrg = orgMembers.map((partialOrgMember) => { const selectedMembersForOrg = orgMembers.map((partialOrgMember) => {
const foundMember = allMembers.value.find((tM) => tM.user.id === partialOrgMember.user.id) const foundMember = allMembers.value.find((tM) => tM.user.id === partialOrgMember.user.id)
const returnVal = foundMember ?? partialOrgMember
// If replacing a partial with a full member, we need to mark as such. const base = foundMember ?? partialOrgMember
returnVal.override = !!foundMember return {
returnVal.oldOverride = !!foundMember ...base,
override: !!foundMember,
returnVal.is_owner = partialOrgMember.is_owner oldOverride: !!foundMember,
is_owner: partialOrgMember.is_owner,
return returnVal }
}) })
allOrgMembers.value = selectedMembersForOrg allOrgMembers.value = selectedMembersForOrg
allTeamMembers.value = allMembers.value.filter( allTeamMembers.value = allMembers.value
(x) => !selectedMembersForOrg.some((y) => y.user.id === x.user.id), .filter((x) => !selectedMembersForOrg.some((y) => y.user.id === x.user.id))
) .map((x) => ({ ...x }))
} }
watch([allMembers, organization, project, currentMember], initMembers) watch([allMembers, organization, project, currentMember], initMembers)
@@ -688,6 +704,11 @@ const removeTeamMember = async (index) => {
}, },
) )
await updateMembers() await updateMembers()
addNotification({
title: 'Member removed',
text: "Your project's member has been removed.",
type: 'success',
})
} catch (err) { } catch (err) {
addNotification({ addNotification({
title: 'An error occurred', title: 'An error occurred',
@@ -748,6 +769,11 @@ const transferOwnership = async (index) => {
user_id: allTeamMembers.value[index].user.id, user_id: allTeamMembers.value[index].user.id,
}, },
}) })
addNotification({
title: 'Member ownership transferred',
text: `${allTeamMembers.value[index].user.username} is now the owner of the project.`,
type: 'success',
})
await updateMembers() await updateMembers()
} catch (err) { } catch (err) {
addNotification({ addNotification({
@@ -795,6 +821,11 @@ async function updateOrgMember(index) {
) )
} }
await updateMembers() await updateMembers()
addNotification({
title: 'Member(s) updated',
text: "Your project's member(s) has been updated.",
type: 'success',
})
} catch (err) { } catch (err) {
addNotification({ addNotification({
title: 'An error occurred', title: 'An error occurred',

View File

@@ -14,6 +14,7 @@ use crate::queue::session::AuthQueue;
use crate::routes::ApiError; use crate::routes::ApiError;
use actix_web::{HttpRequest, HttpResponse, web}; use actix_web::{HttpRequest, HttpResponse, web};
use ariadne::ids::UserId; use ariadne::ids::UserId;
use eyre::eyre;
use rust_decimal::Decimal; use rust_decimal::Decimal;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -708,10 +709,10 @@ pub async fn edit_team_member(
DBTeamMember::get_from_user_id_pending(id, user_id, &**pool) DBTeamMember::get_from_user_id_pending(id, user_id, &**pool)
.await? .await?
.ok_or_else(|| { .ok_or_else(|| {
ApiError::CustomAuthentication( ApiError::Request(eyre!(
"You don't have permission to edit members of this team" "This member does not exist in this team - \
.to_string(), the member must first be created via `POST`"
) ))
})?; })?;
let mut transaction = pool.begin().await?; let mut transaction = pool.begin().await?;