You've already forked AstralRinth
forked from didirus/AstralRinth
Migrate to Turborepo (#1251)
This commit is contained in:
294
apps/frontend/src/pages/report.vue
Normal file
294
apps/frontend/src/pages/report.vue
Normal file
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<Card>
|
||||
<div class="content">
|
||||
<div>
|
||||
<h1 class="card-title-adjustments">Submit a Report</h1>
|
||||
<div>
|
||||
<p>
|
||||
Modding should be safe for everyone, so we take abuse and malicious intent seriously
|
||||
at Modrinth. If you encounter content that violates our
|
||||
<nuxt-link class="text-link" to="/legal/terms">Terms of Service</nuxt-link> or our
|
||||
<nuxt-link class="text-link" to="/legal/rules">Rules</nuxt-link>, please report it to
|
||||
us here.
|
||||
</p>
|
||||
<p>
|
||||
This form is intended exclusively for reporting abuse or harmful content to Modrinth
|
||||
staff. For bugs related to specific projects, please use the project's designated
|
||||
Issues link or Discord channel.
|
||||
</p>
|
||||
<p>
|
||||
Your privacy is important to us; rest assured that your identifying information will
|
||||
be kept confidential.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="report-info-section">
|
||||
<div class="report-info-item">
|
||||
<label for="report-item">Item type to report</label>
|
||||
<DropdownSelect
|
||||
id="report-item"
|
||||
v-model="reportItem"
|
||||
name="report-item"
|
||||
:options="reportItems"
|
||||
:display-name="capitalizeString"
|
||||
:multiple="false"
|
||||
:searchable="false"
|
||||
:show-no-results="false"
|
||||
:show-labels="false"
|
||||
placeholder="Choose report item"
|
||||
/>
|
||||
</div>
|
||||
<div class="report-info-item">
|
||||
<label for="report-item-id">Item ID</label>
|
||||
<input
|
||||
id="report-item-id"
|
||||
v-model="reportItemID"
|
||||
type="text"
|
||||
placeholder="ex. project ID"
|
||||
autocomplete="off"
|
||||
:disabled="reportItem === ''"
|
||||
/>
|
||||
</div>
|
||||
<div class="report-info-item">
|
||||
<label for="report-type">Reason for report</label>
|
||||
<DropdownSelect
|
||||
id="report-type"
|
||||
v-model="reportType"
|
||||
name="report-type"
|
||||
:options="reportTypes"
|
||||
:multiple="false"
|
||||
:searchable="false"
|
||||
:show-no-results="false"
|
||||
:show-labels="false"
|
||||
:display-name="capitalizeString"
|
||||
placeholder="Choose report type"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="report-submission-section">
|
||||
<div>
|
||||
<p>
|
||||
Please provide additional context about your report. Include links and images if
|
||||
possible. <strong>Empty reports will be closed.</strong>
|
||||
</p>
|
||||
</div>
|
||||
<MarkdownEditor v-model="reportBody" placeholder="" :on-image-upload="onImageUpload" />
|
||||
</div>
|
||||
<div class="submit-button">
|
||||
<Button
|
||||
id="submit-button"
|
||||
color="primary"
|
||||
:disabled="submitLoading || !canSubmit"
|
||||
@click="submitReport"
|
||||
>
|
||||
<SaveIcon />
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Card, Button, MarkdownEditor, DropdownSelect } from '@modrinth/ui'
|
||||
import { SaveIcon } from '@modrinth/assets'
|
||||
import { useImageUpload } from '~/composables/image-upload.ts'
|
||||
|
||||
const tags = useTags()
|
||||
const route = useNativeRoute()
|
||||
|
||||
const accessQuery = (id: string): string => {
|
||||
return route.query?.[id]?.toString() || ''
|
||||
}
|
||||
|
||||
const submitLoading = ref<boolean>(false)
|
||||
|
||||
const uploadedImageIDs = ref<string[]>([])
|
||||
|
||||
const reportBody = ref<string>(accessQuery('body'))
|
||||
const reportItem = ref<string>(accessQuery('item'))
|
||||
const reportItemID = ref<string>(accessQuery('itemID'))
|
||||
const reportType = ref<string>('')
|
||||
|
||||
const reportItems = ['project', 'version', 'user']
|
||||
const reportTypes = computed(() => tags.value.reportTypes)
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
return (
|
||||
reportItem.value !== '' &&
|
||||
reportItemID.value !== '' &&
|
||||
reportType.value !== '' &&
|
||||
reportBody.value !== ''
|
||||
)
|
||||
})
|
||||
|
||||
const submissionValidation = () => {
|
||||
if (!canSubmit.value) {
|
||||
throw new Error('Please fill out all required fields')
|
||||
}
|
||||
|
||||
if (reportItem.value === '') {
|
||||
throw new Error('Please select a report item')
|
||||
}
|
||||
|
||||
if (reportItemID.value === '') {
|
||||
throw new Error('Please enter a report item ID')
|
||||
}
|
||||
|
||||
if (reportType.value === '') {
|
||||
throw new Error('Please select a report type')
|
||||
}
|
||||
|
||||
if (reportBody.value === '') {
|
||||
throw new Error('Please enter a report body')
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const capitalizeString = (value?: string) => {
|
||||
if (!value) return ''
|
||||
return value?.charAt(0).toUpperCase() + value?.slice(1)
|
||||
}
|
||||
|
||||
const submitReport = async () => {
|
||||
submitLoading.value = true
|
||||
|
||||
let data: {
|
||||
[key: string]: unknown
|
||||
} = {
|
||||
report_type: reportType.value,
|
||||
item_type: reportItem.value,
|
||||
item_id: reportItemID.value,
|
||||
body: reportBody.value,
|
||||
}
|
||||
|
||||
function takeNLast<T>(arr: T[], n: number): T[] {
|
||||
return arr.slice(Math.max(arr.length - n, 0))
|
||||
}
|
||||
|
||||
if (uploadedImageIDs.value.length > 0) {
|
||||
data = {
|
||||
...data,
|
||||
uploaded_images: takeNLast(uploadedImageIDs.value, 10),
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
submissionValidation()
|
||||
} catch (error) {
|
||||
submitLoading.value = false
|
||||
|
||||
if (error instanceof Error) {
|
||||
addNotification({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: error.message,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = (await useBaseFetch('report', {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
})) as { id: string }
|
||||
|
||||
submitLoading.value = false
|
||||
|
||||
if (response?.id) {
|
||||
navigateTo(`/dashboard/report/${response.id}`)
|
||||
}
|
||||
} catch (error) {
|
||||
submitLoading.value = false
|
||||
|
||||
if (error instanceof Error) {
|
||||
addNotification({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: error.message,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const onImageUpload = async (file: File) => {
|
||||
const item = await useImageUpload(file, { context: 'report' })
|
||||
uploadedImageIDs.value.push(item.id)
|
||||
return item.url
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.submit-button {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
margin-top: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
.card-title-adjustments {
|
||||
margin-block: var(--spacing-card-md) var(--spacing-card-sm);
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 0.5rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 56rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
// TODO: Get rid of this hack when removing global styles from the website.
|
||||
// Overflow decides the behavior of md editor but also clips the border.
|
||||
// In the future, we should use ring instead of block-shadow for the
|
||||
// green ring around the md editor
|
||||
padding-inline: var(--gap-md);
|
||||
padding-bottom: var(--gap-md);
|
||||
margin-inline: calc(var(--gap-md) * -1);
|
||||
|
||||
display: grid;
|
||||
|
||||
// Disable horizontal stretch
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.report-info-section {
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
gap: var(--gap-md);
|
||||
|
||||
:global(.animated-dropdown) {
|
||||
& > .selected {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.report-info-item {
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: var(--gap-sm);
|
||||
color: var(--color-text-dark);
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: var(--font-weight-bold);
|
||||
margin-block: var(--spacing-card-md) var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user