diff --git a/apps/frontend/src/components/ui/ConfirmTransferOrgModal.vue b/apps/frontend/src/components/ui/ConfirmTransferOrgModal.vue new file mode 100644 index 0000000000..d1abbb07db --- /dev/null +++ b/apps/frontend/src/components/ui/ConfirmTransferOrgModal.vue @@ -0,0 +1,106 @@ + + + + + Transfer + + {{ organization.name }} + + + + + Only transfer your organization to people you trust. The new owner will have full control + over the organization and all its projects. + + + + + + {{ currentOwner.username }} + {{ currentOwner.role }} + + + + + + + {{ transferTo.username }} + {{ transferTo.role }} + + + + + You will immediately lose owner access to this organization + The new owner can modify or delete the organization and all its projects + This action cannot be undone + + + + To confirm this transfer, type + {{ organization.name }} below + + + + + + + + + + Cancel + + + + + + Transfer ownership + + + + + + + + diff --git a/apps/frontend/src/components/ui/ConfirmTransferProjectModal.vue b/apps/frontend/src/components/ui/ConfirmTransferProjectModal.vue new file mode 100644 index 0000000000..08db70ff7f --- /dev/null +++ b/apps/frontend/src/components/ui/ConfirmTransferProjectModal.vue @@ -0,0 +1,118 @@ + + + + + Transfer + + {{ project.name }} + + + + + Only transfer your project to people you trust. Malicious actors may try to buy your project + to publish harmful files. + + + + + + {{ currentOwner.username }} + {{ currentOwner.role }} + + + + + + + + {{ transferTo.username || transferTo.name }} + + {{ transferTo.role || 'Member' }} + + + + + You will immediately lose owner access to this project + The new owner can modify or delete the project at any time + This action cannot be undone + + + + To confirm this transfer, type + {{ project.name }} below + + + + + + + + + + Cancel + + + + + + Transfer ownership + + + + + + + + diff --git a/apps/frontend/src/pages/[type]/[id]/settings/members.vue b/apps/frontend/src/pages/[type]/[id]/settings/members.vue index a80d7cb676..9a5ef6f175 100644 --- a/apps/frontend/src/pages/[type]/[id]/settings/members.vue +++ b/apps/frontend/src/pages/[type]/[id]/settings/members.vue @@ -1,5 +1,17 @@ + Transfer ownership @@ -298,7 +310,11 @@ :options="organizations || []" :disabled="!currentMember?.is_owner || organizations?.length === 0" /> - + Transfer management @@ -552,6 +568,7 @@ import { } from '@modrinth/ui' import { Multiselect } from 'vue-multiselect' +import ConfirmTransferProjectModal from '~/components/ui/ConfirmTransferProjectModal.vue' import { removeSelfFromTeam } from '~/helpers/teams.js' const { addNotification } = injectNotificationManager() @@ -563,6 +580,8 @@ const { invalidate, } = injectProjectPageContext() +console.log(project) + const cosmetics = useCosmetics() const auth = await useAuth() @@ -601,6 +620,8 @@ initMembers() const currentUsername = ref('') const openTeamMembers = ref([]) const selectedOrganization = ref(null) +const transferData = ref(null) +const transferModal = ref(null) const { data: organizations } = useAsyncData('organizations', () => { return useBaseFetch('user/' + auth.value?.user.id + '/organizations', { @@ -755,6 +776,34 @@ const updateTeamMember = async (index) => { stopLoading() } +const openTransferModal = (index, e) => { + transferData.value = { + target: { + avatar_url: allTeamMembers.value[index]?.avatar_url, + username: allTeamMembers.value[index]?.user?.username, + role: allTeamMembers.value[index]?.role || 'Member', + }, + onConfirm: () => transferOwnership(index), + } + nextTick(() => { + transferModal.value?.show(e) + }) +} + +const openTransferToOrgModal = (e) => { + if (!selectedOrganization.value) return + transferData.value = { + target: { + avatar_url: selectedOrganization.value.icon_url, + name: selectedOrganization.value.name, + }, + onConfirm: () => onAddToOrg(), + } + nextTick(() => { + transferModal.value?.show(e) + }) +} + const transferOwnership = async (index) => { startLoading() diff --git a/apps/frontend/src/pages/organization/[id]/settings/members.vue b/apps/frontend/src/pages/organization/[id]/settings/members.vue index e387dfa304..cabd6ccc2c 100644 --- a/apps/frontend/src/pages/organization/[id]/settings/members.vue +++ b/apps/frontend/src/pages/organization/[id]/settings/members.vue @@ -1,5 +1,21 @@ + @@ -205,7 +221,7 @@ onTransferOwnership(organization.team_id, member.user.id)" + @click="(e) => openTransferModal(member, e)" > Transfer ownership @@ -233,8 +249,9 @@ import { injectNotificationManager, StyledInput, } from '@modrinth/ui' -import { ref } from 'vue' +import { nextTick, ref } from 'vue' +import ConfirmTransferOrgModal from '~/components/ui/ConfirmTransferOrgModal.vue' import { removeTeamMember } from '~/helpers/teams.js' import { injectOrganizationContext } from '~/providers/organization-context.ts' import { isPermission } from '~/utils/permissions.ts' @@ -246,6 +263,8 @@ const auth = await useAuth() const currentUsername = ref('') const openTeamMembers = ref([]) +const transferTargetMember = ref(null) +const transferModal = ref(null) const allTeamMembers = ref(organization.value.members) @@ -344,6 +363,13 @@ const onUpdateTeamMember = useClientTry(async (teamId, member) => { }) }) +const openTransferModal = (member, e) => { + transferTargetMember.value = member + nextTick(() => { + transferModal.value?.show(e) + }) +} + const onTransferOwnership = useClientTry(async (teamId, uid) => { const data = { user_id: uid, diff --git a/apps/labrinth/src/routes/v3/teams.rs b/apps/labrinth/src/routes/v3/teams.rs index 1b5d7e7968..aaba5d2f94 100644 --- a/apps/labrinth/src/routes/v3/teams.rs +++ b/apps/labrinth/src/routes/v3/teams.rs @@ -904,7 +904,7 @@ pub async fn transfer_ownership( && project_item.inner.organization_id.is_some() { return Err(ApiError::InvalidInput( - "You cannot transfer ownership of a project team that is owend by an organization" + "You cannot transfer ownership of a project team that is owned by an organization" .to_string(), )); }
+ To confirm this transfer, type + {{ organization.name }} below +
+ To confirm this transfer, type + {{ project.name }} below +