mirror of
https://github.com/modrinth/code.git
synced 2026-07-31 21:26:40 +00:00
feat: add moderation status action button to tech review (#6484)
* feat: add moderation status action button to tech review * chore: run prepr
This commit is contained in:
@@ -11,11 +11,15 @@ import {
|
||||
CopyIcon,
|
||||
DownloadIcon,
|
||||
EllipsisVerticalIcon,
|
||||
EyeOffIcon,
|
||||
LinkIcon,
|
||||
LoaderCircleIcon,
|
||||
ScaleIcon,
|
||||
ShieldCheckIcon,
|
||||
SpinnerIcon,
|
||||
TimerIcon,
|
||||
TriangleAlertIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { type TechReviewContext, techReviewQuickReplies } from '@modrinth/moderation'
|
||||
import {
|
||||
@@ -23,6 +27,7 @@ import {
|
||||
ButtonStyled,
|
||||
Collapsible,
|
||||
CollapsibleRegion,
|
||||
commonMessages,
|
||||
getProjectTypeIcon,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
@@ -30,6 +35,7 @@ import {
|
||||
type OverflowMenuOption,
|
||||
useFormatBytes,
|
||||
useFormatDateTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { NavTabs } from '@modrinth/ui'
|
||||
import {
|
||||
@@ -47,6 +53,7 @@ import ThreadView from '~/components/ui/thread/ThreadView.vue'
|
||||
|
||||
const auth = await useAuth()
|
||||
const featureFlags = useFeatureFlags()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const formatDateTimeUtc = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
@@ -96,6 +103,16 @@ const emit = defineEmits<{
|
||||
showMaliciousSummary: [unsafeFiles: UnsafeFile[]]
|
||||
}>()
|
||||
|
||||
const projectStatus = ref<Labrinth.Projects.v2.ProjectStatus>(props.item.project.status)
|
||||
const isProjectApproved = computed(() => {
|
||||
return (
|
||||
projectStatus.value === 'approved' ||
|
||||
projectStatus.value === 'archived' ||
|
||||
projectStatus.value === 'unlisted' ||
|
||||
projectStatus.value === 'private'
|
||||
)
|
||||
})
|
||||
|
||||
const quickActions = computed<OverflowMenuOption[]>(() => {
|
||||
const actions: OverflowMenuOption[] = []
|
||||
|
||||
@@ -141,6 +158,53 @@ const quickActions = computed<OverflowMenuOption[]>(() => {
|
||||
return actions
|
||||
})
|
||||
|
||||
const isLoadingStatusAction = ref(false)
|
||||
const projectStatusActions = computed<OverflowMenuOption[]>(() => [
|
||||
{
|
||||
id: 'approve',
|
||||
color: 'green',
|
||||
action: () => setStatus('approved'),
|
||||
hoverFilled: true,
|
||||
disabled: isProjectApproved.value || isLoadingStatusAction.value,
|
||||
},
|
||||
{
|
||||
id: 'withhold',
|
||||
color: 'orange',
|
||||
action: () => setStatus('withheld'),
|
||||
hoverFilled: true,
|
||||
disabled: projectStatus.value === 'withheld' || isLoadingStatusAction.value,
|
||||
},
|
||||
{
|
||||
id: 'send-to-review',
|
||||
action: () => setStatus('processing'),
|
||||
hoverFilled: true,
|
||||
disabled: projectStatus.value === 'processing' || isLoadingStatusAction.value,
|
||||
},
|
||||
{
|
||||
id: 'reject',
|
||||
color: 'red',
|
||||
action: () => setStatus('rejected'),
|
||||
hoverFilled: true,
|
||||
disabled: projectStatus.value === 'rejected' || isLoadingStatusAction.value,
|
||||
},
|
||||
])
|
||||
|
||||
async function setStatus(status: Labrinth.Projects.v2.ProjectStatus) {
|
||||
isLoadingStatusAction.value = true
|
||||
try {
|
||||
await client.labrinth.projects_v2.edit(props.item.project.id, { status })
|
||||
|
||||
projectStatus.value = status
|
||||
} catch (err) {
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: (err as any)?.data?.description ? (err as any).data.description : String(err),
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
isLoadingStatusAction.value = false
|
||||
}
|
||||
|
||||
type Tab = 'Thread' | 'Files' | 'File'
|
||||
const tabs: readonly ('Thread' | 'Files')[] = ['Thread', 'Files']
|
||||
const currentTab = ref<Tab>('Thread')
|
||||
@@ -347,13 +411,6 @@ const severityColor = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const isProjectApproved = computed(() => {
|
||||
const status = props.item.project.status
|
||||
return (
|
||||
status === 'approved' || status === 'archived' || status === 'unlisted' || status === 'private'
|
||||
)
|
||||
})
|
||||
|
||||
const formattedDate = computed(() => {
|
||||
const dates = props.item.reports.map((r) => new Date(r.created))
|
||||
const earliest = new Date(Math.min(...dates.map((d) => d.getTime())))
|
||||
@@ -1117,6 +1174,37 @@ async function handleSubmitReview(verdict: 'safe' | 'unsafe') {
|
||||
<BugIcon /> Fail
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="standard">
|
||||
<OverflowMenu
|
||||
class="btn-dropdown-animation"
|
||||
:disabled="isLoadingStatusAction"
|
||||
:options="projectStatusActions"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="isLoadingStatusAction"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ScaleIcon v-else aria-hidden="true" />
|
||||
Set Status
|
||||
<template #approve>
|
||||
<CheckIcon aria-hidden="true" />
|
||||
Approve
|
||||
</template>
|
||||
<template #withhold>
|
||||
<EyeOffIcon aria-hidden="true" />
|
||||
Withhold
|
||||
</template>
|
||||
<template #send-to-review>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Send to review
|
||||
</template>
|
||||
<template #reject>
|
||||
<XIcon aria-hidden="true" />
|
||||
Reject
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="featureFlags.developerMode" type="outlined">
|
||||
<button @click="emit('showMaliciousSummary', unsafeFiles)">Debug Summary</button>
|
||||
</ButtonStyled>
|
||||
|
||||
@@ -375,7 +375,7 @@ const authorLink = computed(() =>
|
||||
</section>
|
||||
<section id="changes">
|
||||
<h3 class="mt-0 mb-2 text-lg font-semibold">{{ formatMessage(messages.changes) }}</h3>
|
||||
<div class="p-4 bg-surface-3 rounded-2xl flex border-solid border border-surface-4">
|
||||
<div class="p-4 bg-surface-3 rounded-2xl border-solid border border-surface-4">
|
||||
<div
|
||||
v-if="version.changelog"
|
||||
class="markdown-body"
|
||||
|
||||
Reference in New Issue
Block a user