mirror of
https://github.com/modrinth/code.git
synced 2026-09-05 06:19:11 +00:00
feat: add project status and target filters to reports (#7345)
* feat: add project status and target filters * prepr --------- Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
co-authored by
Prospector
parent
19dc819821
commit
6b5f5e8065
@@ -72,6 +72,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="report.project?.status"
|
||||||
|
class="flex items-center gap-1 rounded-full border border-solid border-surface-5 bg-surface-4 px-2.5 py-1"
|
||||||
|
>
|
||||||
|
<Badge :type="report.project?.status" class="text-sm" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="report.item_type === 'version' && report.version"
|
v-if="report.item_type === 'version' && report.version"
|
||||||
class="text-sm text-secondary"
|
class="text-sm text-secondary"
|
||||||
@@ -229,6 +236,7 @@ import {
|
|||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { type ExtendedReport, reportQuickReplies } from '@modrinth/moderation'
|
import { type ExtendedReport, reportQuickReplies } from '@modrinth/moderation'
|
||||||
import {
|
import {
|
||||||
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
ButtonLink,
|
ButtonLink,
|
||||||
CollapsibleRegion,
|
CollapsibleRegion,
|
||||||
|
|||||||
@@ -53,11 +53,11 @@
|
|||||||
|
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
v-model="currentReporterOrProject"
|
v-model="currentReporterOrProject"
|
||||||
:options="reporterOrProjectOptions"
|
:options="reportFilterOptions"
|
||||||
:max-height="500"
|
:max-height="500"
|
||||||
dropdown-min-width="360px"
|
dropdown-min-width="360px"
|
||||||
no-options-message="no options found"
|
no-options-message="no options found"
|
||||||
:searchable="reporterOrProjectOptions.length > 6"
|
:searchable="reportFilterOptions.length > 6"
|
||||||
:max-tag-rows="1"
|
:max-tag-rows="1"
|
||||||
fit-content
|
fit-content
|
||||||
trigger-type="base"
|
trigger-type="base"
|
||||||
@@ -86,6 +86,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #top>
|
<template #top>
|
||||||
|
<div
|
||||||
|
class="flex flex-row justify-between gap-1 border-0 border-b border-solid border-b-surface-5 py-2"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="section in hiddenSections"
|
||||||
|
:key="section.key"
|
||||||
|
class="flex flex-row gap-2 px-4"
|
||||||
|
>
|
||||||
|
<span class="font-semibold text-primary">
|
||||||
|
{{ section.key }}
|
||||||
|
</span>
|
||||||
|
<Checkbox v-model="section.enabled" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -208,8 +222,9 @@ import {
|
|||||||
SortAscIcon,
|
SortAscIcon,
|
||||||
SortDescIcon,
|
SortDescIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import type { ExtendedReport } from '@modrinth/moderation'
|
import type { ExtendedReport, OwnershipTarget } from '@modrinth/moderation'
|
||||||
import {
|
import {
|
||||||
|
Checkbox,
|
||||||
Combobox,
|
Combobox,
|
||||||
type ComboboxOption,
|
type ComboboxOption,
|
||||||
commonMessages,
|
commonMessages,
|
||||||
@@ -467,25 +482,48 @@ watch(
|
|||||||
|
|
||||||
watch(() => route.query, readFiltersFromRoute, { deep: true })
|
watch(() => route.query, readFiltersFromRoute, { deep: true })
|
||||||
|
|
||||||
|
type FilterSection = {
|
||||||
|
key: 'project' | 'reporter' | 'target'
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
const hiddenSections = ref<FilterSection[]>([
|
||||||
|
{ key: 'project', enabled: true },
|
||||||
|
{ key: 'reporter', enabled: true },
|
||||||
|
{ key: 'target', enabled: true },
|
||||||
|
])
|
||||||
|
|
||||||
type ReportedType<T> = T & { report_item_count: number }
|
type ReportedType<T> = T & { report_item_count: number }
|
||||||
const reporterOrProjectOptions = computed<MultiSelectItem<string>[]>(() => {
|
const reportFilterOptions = computed<MultiSelectItem<string>[]>(() => {
|
||||||
if (!allReports.value) return []
|
if (!allReports.value) return []
|
||||||
const options: MultiSelectItem<string>[] = []
|
const options: MultiSelectItem<string>[] = []
|
||||||
|
|
||||||
const uniqueProjectIds: { [id: string]: ReportedType<Labrinth.Projects.v2.Project> } = {}
|
const uniqueProjectIds: { [id: string]: ReportedType<Labrinth.Projects.v2.Project> } = {}
|
||||||
const uniqueReporterIds: { [id: string]: ReportedType<User> } = {}
|
const uniqueReporterIds: { [id: string]: ReportedType<User> } = {}
|
||||||
|
const uniqueTargetIds: { [id: string]: ReportedType<OwnershipTarget> } = {}
|
||||||
|
|
||||||
|
const projectsEnabled =
|
||||||
|
hiddenSections.value.find((section) => section.key === 'project')?.enabled ?? true
|
||||||
|
const reportersEnabled =
|
||||||
|
hiddenSections.value.find((section) => section.key === 'reporter')?.enabled ?? true
|
||||||
|
const targetsEnabled =
|
||||||
|
hiddenSections.value.find((section) => section.key === 'target')?.enabled ?? true
|
||||||
|
|
||||||
for (const report of filteredReports.value) {
|
for (const report of filteredReports.value) {
|
||||||
if (report.project)
|
if (report.project && projectsEnabled)
|
||||||
uniqueProjectIds[report.project.id] = {
|
uniqueProjectIds[report.project.id] = {
|
||||||
...report.project,
|
...report.project,
|
||||||
report_item_count: (uniqueProjectIds[report.project.id]?.report_item_count || 0) + 1,
|
report_item_count: (uniqueProjectIds[report.project.id]?.report_item_count || 0) + 1,
|
||||||
}
|
}
|
||||||
if (report.reporter_user)
|
if (report.reporter_user && reportersEnabled)
|
||||||
uniqueReporterIds[report.reporter_user.id] = {
|
uniqueReporterIds[report.reporter_user.id] = {
|
||||||
...report.reporter_user,
|
...report.reporter_user,
|
||||||
report_item_count: (uniqueReporterIds[report.reporter_user.id]?.report_item_count || 0) + 1,
|
report_item_count: (uniqueReporterIds[report.reporter_user.id]?.report_item_count || 0) + 1,
|
||||||
}
|
}
|
||||||
|
if (report.target && targetsEnabled)
|
||||||
|
uniqueTargetIds[report.target.slug] = {
|
||||||
|
...report.target,
|
||||||
|
report_item_count: (uniqueTargetIds[report.target.slug]?.report_item_count || 0) + 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(uniqueProjectIds).length !== 0) {
|
if (Object.keys(uniqueProjectIds).length !== 0) {
|
||||||
@@ -505,20 +543,39 @@ const reporterOrProjectOptions = computed<MultiSelectItem<string>[]>(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
options.push({ type: 'section-header', label: 'Reporters' })
|
if (Object.keys(uniqueReporterIds).length !== 0) {
|
||||||
Object.values(uniqueReporterIds)
|
options.push({ type: 'section-header', label: 'Reporters' })
|
||||||
.sort((a, b) =>
|
Object.values(uniqueReporterIds)
|
||||||
a.report_item_count === b.report_item_count
|
.sort((a, b) =>
|
||||||
? a.username.localeCompare(b.username)
|
a.report_item_count === b.report_item_count
|
||||||
: b.report_item_count - a.report_item_count,
|
? a.username.localeCompare(b.username)
|
||||||
)
|
: b.report_item_count - a.report_item_count,
|
||||||
.forEach((reporter) => {
|
)
|
||||||
options.push({
|
.forEach((reporter) => {
|
||||||
value: `reporter/${reporter.id}`,
|
options.push({
|
||||||
label: `${reporter.username} (${formatNumber(reporter.report_item_count)})`,
|
value: `reporter/${reporter.id}`,
|
||||||
icon: reporter.avatar_url ? h('img', { src: reporter.avatar_url }) : undefined,
|
label: `${reporter.username} (${formatNumber(reporter.report_item_count)})`,
|
||||||
|
icon: reporter.avatar_url ? h('img', { src: reporter.avatar_url }) : undefined,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if (Object.keys(uniqueTargetIds).length !== 0) {
|
||||||
|
options.push({ type: 'section-header', label: 'Targets' })
|
||||||
|
Object.values(uniqueTargetIds)
|
||||||
|
.sort((a, b) =>
|
||||||
|
a.report_item_count === b.report_item_count
|
||||||
|
? a.name.localeCompare(b.name)
|
||||||
|
: b.report_item_count - a.report_item_count,
|
||||||
|
)
|
||||||
|
.forEach((target) => {
|
||||||
|
options.push({
|
||||||
|
value: `target/${target.slug}`,
|
||||||
|
label: `${target.name} (${formatNumber(target.report_item_count)})`,
|
||||||
|
icon: target.avatar_url ? h('img', { src: target.avatar_url }) : undefined,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return options
|
return options
|
||||||
})
|
})
|
||||||
@@ -753,12 +810,13 @@ const sortedReports = computed(() => {
|
|||||||
reporterOrProjectFilter.length === 0
|
reporterOrProjectFilter.length === 0
|
||||||
? [...filteredReports.value]
|
? [...filteredReports.value]
|
||||||
: filteredReports.value.filter((report) => {
|
: filteredReports.value.filter((report) => {
|
||||||
const reporterOrProjectFilterLookup = new Set(reporterOrProjectFilter)
|
const lookup = new Set(reporterOrProjectFilter)
|
||||||
const reporterValue = report.reporter_user ? `reporter/${report.reporter_user.id}` : null
|
const reporterValue = report.reporter_user ? `reporter/${report.reporter_user.id}` : null
|
||||||
const projectValue = report.project ? `project/${report.project.id}` : null
|
const projectValue = report.project ? `project/${report.project.id}` : null
|
||||||
|
const targetValue = report.target ? `target/${report.target.slug}` : null
|
||||||
return (
|
return (
|
||||||
(reporterValue && reporterOrProjectFilterLookup.has(reporterValue)) ||
|
(reporterValue && lookup.has(reporterValue)) ||
|
||||||
(projectValue && reporterOrProjectFilterLookup.has(projectValue))
|
(projectValue && lookup.has(projectValue)) | (targetValue && lookup.has(targetValue))
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user