mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 00:55:25 +00:00
feat: allow bulk deleting attribution groups for moderators (#6839)
* feat: allow bulk deleting attribution groups for moderators * prepr
This commit is contained in:
@@ -235,10 +235,7 @@ async function clearAllGroups() {
|
||||
const groups = await client.labrinth.attribution_internal.listProjectAttribution(
|
||||
props.project_id,
|
||||
)
|
||||
|
||||
for (const group of groups) {
|
||||
await client.labrinth.attribution_internal.deleteGroup(group.id)
|
||||
}
|
||||
await client.labrinth.attribution_internal.deleteGroups(groups.map((group) => group.id))
|
||||
|
||||
await queryClient.invalidateQueries({ queryKey: ['project-attribution', props.project_id] })
|
||||
} catch (error) {
|
||||
|
||||
@@ -3899,6 +3899,21 @@
|
||||
"project.settings.permissions.completed.title": {
|
||||
"message": "Permissions completed!"
|
||||
},
|
||||
"project.settings.permissions.delete-all-groups": {
|
||||
"message": "Delete all groups"
|
||||
},
|
||||
"project.settings.permissions.delete-all-groups-confirmation.description": {
|
||||
"message": "This will permanently delete all {count, plural, one {# attribution group} other {# attribution groups}} for this project and all files inside them. This action cannot be undone."
|
||||
},
|
||||
"project.settings.permissions.delete-all-groups-confirmation.title": {
|
||||
"message": "Delete all attribution groups?"
|
||||
},
|
||||
"project.settings.permissions.delete-all-groups-error": {
|
||||
"message": "Could not delete all attribution groups"
|
||||
},
|
||||
"project.settings.permissions.delete-all-groups-success": {
|
||||
"message": "All attribution groups were deleted."
|
||||
},
|
||||
"project.settings.permissions.empty-state.description": {
|
||||
"message": "None of your project's versions contain external content, so you don't need to worry about obtaining permissions."
|
||||
},
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
RightArrowIcon,
|
||||
ScaleIcon,
|
||||
SearchIcon,
|
||||
SpinnerIcon,
|
||||
TrashIcon,
|
||||
UnfoldVerticalIcon,
|
||||
XCircleIcon,
|
||||
} from '@modrinth/assets'
|
||||
@@ -18,20 +20,22 @@ import {
|
||||
Combobox,
|
||||
type ComboboxOption,
|
||||
commonMessages,
|
||||
ConfirmModal,
|
||||
createAttributionGroupTitle,
|
||||
defineMessage,
|
||||
defineMessages,
|
||||
EmptyState,
|
||||
ExternalProjectPermissionsCard,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { isStaff } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, ref, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import { setupAttributionModerationProvider } from '~/providers/setup/attribution-moderation'
|
||||
|
||||
@@ -47,6 +51,10 @@ const isModerator = computed(() => {
|
||||
const { formatMessage } = useVIntl()
|
||||
const { projectV2: project } = injectProjectPageContext()
|
||||
const { labrinth } = injectModrinthClient()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const queryClient = useQueryClient()
|
||||
const deleteAllGroupsModalRef =
|
||||
useTemplateRef<InstanceType<typeof ConfirmModal>>('deleteAllGroupsModalRef')
|
||||
|
||||
type SortType = 'status' | 'most_files' | 'recently_edited' | 'rejected'
|
||||
|
||||
@@ -369,6 +377,46 @@ const messages = defineMessages({
|
||||
defaultMessage:
|
||||
'{count, plural, =0 {No attributions need approval} one {# attribution needs approval} other {# attributions need approval}}',
|
||||
},
|
||||
deleteAllGroups: {
|
||||
id: 'project.settings.permissions.delete-all-groups',
|
||||
defaultMessage: 'Delete all groups',
|
||||
},
|
||||
deleteAllGroupsConfirmationTitle: {
|
||||
id: 'project.settings.permissions.delete-all-groups-confirmation.title',
|
||||
defaultMessage: 'Delete all attribution groups?',
|
||||
},
|
||||
deleteAllGroupsConfirmationDescription: {
|
||||
id: 'project.settings.permissions.delete-all-groups-confirmation.description',
|
||||
defaultMessage:
|
||||
'This will permanently delete all {count, plural, one {# attribution group} other {# attribution groups}} for this project and all files inside them. This action cannot be undone.',
|
||||
},
|
||||
deleteAllGroupsSuccess: {
|
||||
id: 'project.settings.permissions.delete-all-groups-success',
|
||||
defaultMessage: 'All attribution groups were deleted.',
|
||||
},
|
||||
deleteAllGroupsError: {
|
||||
id: 'project.settings.permissions.delete-all-groups-error',
|
||||
defaultMessage: 'Could not delete all attribution groups',
|
||||
},
|
||||
})
|
||||
|
||||
const deleteAllGroupsMutation = useMutation({
|
||||
mutationFn: () => labrinth.attribution_internal.deleteAllGroups(project.value.id),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ['project-attribution', project.value.id] })
|
||||
addNotification({
|
||||
type: 'success',
|
||||
title: formatMessage(messages.deleteAllGroups),
|
||||
text: formatMessage(messages.deleteAllGroupsSuccess),
|
||||
})
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
title: formatMessage(messages.deleteAllGroupsError),
|
||||
text: error.message,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
function defaultCardCollapsed(group: Labrinth.Attribution.Internal.AttributionGroup): boolean {
|
||||
@@ -412,12 +460,26 @@ function toggleAllCardsCollapsed() {
|
||||
cardCollapsedById.value = next
|
||||
}
|
||||
|
||||
function showDeleteAllGroupsConfirmation() {
|
||||
deleteAllGroupsModalRef.value?.show()
|
||||
}
|
||||
|
||||
function dismissInfoBanner() {
|
||||
flags.value.dismissedExternalProjectsInfo = true
|
||||
saveFeatureFlags()
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<ConfirmModal
|
||||
v-if="isModerator"
|
||||
ref="deleteAllGroupsModalRef"
|
||||
:title="formatMessage(messages.deleteAllGroupsConfirmationTitle)"
|
||||
:description="
|
||||
formatMessage(messages.deleteAllGroupsConfirmationDescription, { count: totalGroups })
|
||||
"
|
||||
:proceed-label="formatMessage(messages.deleteAllGroups)"
|
||||
@proceed="deleteAllGroupsMutation.mutate()"
|
||||
/>
|
||||
<Admonition
|
||||
v-if="!flags.dismissedExternalProjectsInfo && !isModerator"
|
||||
type="info"
|
||||
@@ -500,7 +562,7 @@ function dismissInfoBanner() {
|
||||
/>
|
||||
</template>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="grid grid-cols-[1fr_auto_auto] gap-2">
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-[minmax(0,1fr)_auto_auto_auto]">
|
||||
<StyledInput
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
@@ -554,6 +616,21 @@ function dismissInfoBanner() {
|
||||
{{ expandCollapseAllLabel }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="isModerator" color="red" type="outlined">
|
||||
<button
|
||||
type="button"
|
||||
class="!h-[40px]"
|
||||
:disabled="deleteAllGroupsMutation.isPending.value"
|
||||
@click="showDeleteAllGroupsConfirmation"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="deleteAllGroupsMutation.isPending.value"
|
||||
class="size-5 flex-shrink-0 animate-spin"
|
||||
/>
|
||||
<TrashIcon v-else class="size-5 flex-shrink-0" />
|
||||
{{ formatMessage(messages.deleteAllGroups) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isModerator" class="mt-4 flex flex-wrap items-center gap-3">
|
||||
|
||||
Reference in New Issue
Block a user