273 lines
7.9 KiB
Vue

<template>
<os-modal class="report-modal" :title="title" :open="isOpen" @cancel="cancel">
<transition name="ds-transition-fade">
<div v-if="success" class="ds-flex ds-flex-centered hc-modal-success">
<sweetalert-icon icon="success" />
</div>
</transition>
<!-- eslint-disable-next-line vue/no-v-html -->
<p v-html="message" />
<div class="ds-mb-small"></div>
<fieldset class="report-radio-group" data-test="report-radio-group" role="radiogroup">
<legend>{{ $t('report.reason.category.label') }}</legend>
<div
v-for="(option, index) in form.reasonCategoryOptions"
:key="option.value"
class="report-radio-option"
:class="{ 'report-radio-option-selected': form.reasonCategory === option }"
role="radio"
:aria-checked="String(form.reasonCategory === option)"
:tabindex="form.reasonCategory === option || (!form.reasonCategory && index === 0) ? 0 : -1"
@click="selectReasonCategory(option)"
@keydown.space.prevent="selectReasonCategory(option)"
@keydown.enter.prevent="selectReasonCategory(option)"
@keydown.down.prevent="focusRadioOption(index + 1)"
@keydown.up.prevent="focusRadioOption(index - 1)"
>
<span class="report-radio-option-mark" />
<span class="report-radio-option-label">{{ option.label }}</span>
</div>
</fieldset>
<ds-input
class="reason-description"
v-model="form.reasonDescription"
:schema="formSchema.reasonDescription"
:label="$t('report.reason.description.label')"
:placeholder="$t('report.reason.description.placeholder')"
type="textarea"
rows="5"
/>
<small class="smallTag">
{{ form.reasonDescription.length }}/{{ formSchema.reasonDescription.max }}
</small>
<template #footer>
<os-button class="cancel" variant="primary" appearance="outline" @click="cancel">
<template #icon>
<os-icon :icon="icons.close" />
</template>
{{ $t('report.cancel') }}
</os-button>
<os-button
class="confirm"
variant="danger"
appearance="filled"
:disabled="!form.reasonCategory"
:loading="loading"
@click="confirm"
>
<template #icon>
<os-icon :icon="icons.exclamationCircle" />
</template>
{{ $t('report.submit') }}
</os-button>
</template>
</os-modal>
</template>
<script>
import { OsButton, OsIcon, OsModal } from '@ocelot-social/ui'
import { iconRegistry } from '~/utils/iconRegistry'
import { SweetalertIcon } from 'vue-sweetalert-icons'
import { reportMutation } from '~/graphql/Moderation.js'
import { valuesReasonCategoryOptions } from '~/constants/modals.js'
import validReport from '~/components/utils/ReportModal'
export default {
name: 'ReportModal',
components: {
OsButton,
OsIcon,
OsModal,
SweetalertIcon,
},
props: {
name: { type: String, default: '' },
type: { type: String, required: true },
id: { type: String, required: true },
},
data() {
return {
isOpen: true,
success: false,
loading: false,
form: {
reasonCategory: null,
reasonCategoryOptions: [],
reasonDescription: '',
},
}
},
computed: {
title() {
return this.$t(`report.${this.type}.title`)
},
message() {
const name = this.$filters.truncate(this.name, 30)
return this.$t(`report.${this.type}.message`, { name })
},
formSchema() {
const validReportSchema = validReport({ translate: this.$t })
return {
...validReportSchema.formSchema,
}
},
},
created() {
this.icons = iconRegistry
this.form.reasonCategoryOptions = valuesReasonCategoryOptions.map((reasonCategory) => {
return {
label: this.$t('report.reason.category.options.' + reasonCategory),
value: reasonCategory,
}
})
},
methods: {
async cancel() {
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
// await this.modalData.buttons.cancel.callback()
this.isOpen = false
setTimeout(() => {
this.$emit('close')
}, 1000)
},
selectReasonCategory(option) {
this.form.reasonCategory = option
this.$nextTick(() => {
const selected = this.$el.querySelector('.report-radio-option[aria-checked="true"]')
if (selected) selected.focus()
})
},
focusRadioOption(index) {
const options = this.form.reasonCategoryOptions
const wrappedIndex = (index + options.length) % options.length
this.selectReasonCategory(options[wrappedIndex])
},
async confirm() {
const { reasonCategory, reasonDescription } = this.form
this.loading = true
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
// await this.modalData.buttons.confirm.callback()
this.$apollo
.mutate({
mutation: reportMutation(),
variables: {
resourceId: this.id,
reasonCategory: reasonCategory.value,
reasonDescription,
},
})
.then(({ _data }) => {
this.success = true
this.$toast.success(this.$t('report.success'))
setTimeout(() => {
this.isOpen = false
setTimeout(() => {
this.success = false
this.$emit('close')
}, 500)
}, 1500)
this.loading = false
})
.catch((err) => {
this.$emit('close')
this.success = false
switch (err.message) {
case 'GraphQL error: User':
this.$toast.error(this.$t('report.user.error'))
break
case 'GraphQL error: Post':
this.$toast.error(this.$t('report.contribution.error'))
break
case 'GraphQL error: Comment':
this.$toast.error(this.$t('report.comment.error'))
break
default:
this.$toast.error(err.message)
}
this.isOpen = false
this.loading = false
})
},
},
}
</script>
<style lang="scss">
.report-modal.os-modal {
width: 700px !important;
max-width: 700px !important;
}
.report-modal .report-radio-group {
border: none;
padding: 0;
margin: 0;
}
.report-modal .report-radio-group legend {
font-weight: bold;
margin-bottom: $space-xx-small;
}
.report-modal .report-radio-option {
display: flex;
align-items: center;
width: 100%;
padding: 4px 0;
cursor: pointer;
user-select: none;
}
.report-modal .report-radio-option-mark {
display: inline-block;
position: relative;
width: 16px;
height: 16px;
min-width: 16px;
border: 2px solid $border-color-base;
background-color: $background-color-base;
border-radius: 50%;
margin-right: 8px;
}
.report-modal .report-radio-option-mark::before {
position: absolute;
content: '';
top: 50%;
left: 50%;
transform: translateY(-50%) translateX(-50%) scale(0);
opacity: 0;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: $text-color-primary;
transition: all 0.1s ease-in;
}
.report-modal .report-radio-option-selected .report-radio-option-mark::before {
opacity: 1;
transform: translateY(-50%) translateX(-50%) scale(1);
}
.report-modal .report-radio-option-label {
cursor: pointer;
flex: 1;
}
.report-modal .reason-description {
margin-top: $space-x-small !important;
margin-bottom: $space-xx-small !important;
}
.report-modal .smallTag {
width: 100%;
position: relative;
left: 90%;
}
.report-modal .hc-modal-success {
pointer-events: none;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: #fff;
opacity: 1;
z-index: $z-index-modal;
border-radius: $border-radius-x-large;
}
</style>