fix: delete icon issue & more cleanup
(cherry picked from commit 65435cf2b52157d15a00371c75d431d352cf6443) # Conflicts: # frontend/src/components/BulkDeleteLinkedDocModal.vue # frontend/src/components/DeleteLinkedDocModal.vue # frontend/src/components/ListViews/LinkedDocsListView.vue # frontend/src/pages/Deal.vue # frontend/src/pages/Lead.vue
This commit is contained in:
parent
516b8e09a1
commit
547ffc5f65
154
frontend/src/components/BulkDeleteLinkedDocModal.vue
Normal file
154
frontend/src/components/BulkDeleteLinkedDocModal.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ size: 'xl' }">
|
||||
<template #body>
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl leading-6 text-ink-gray-9 font-semibold">
|
||||
{{ __('Delete') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Button variant="ghost" icon="x" @click="show = false" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-ink-gray-5">
|
||||
{{
|
||||
__('Are you sure you want to delete {0} items?', [
|
||||
props.items?.length,
|
||||
])
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 pb-7 pt-0 sm:px-6">
|
||||
<div class="flex flex-row-reverse gap-2">
|
||||
<Button
|
||||
:label="__('Delete {0} items', [props.items.length])"
|
||||
icon-left="trash-2"
|
||||
variant="solid"
|
||||
theme="red"
|
||||
@click="confirmDelete()"
|
||||
/>
|
||||
<Button
|
||||
:label="__('Unlink and delete {0} items', [props.items.length])"
|
||||
icon-left="unlock"
|
||||
variant="solid"
|
||||
@click="confirmUnlink()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #body v-if="confirmDeleteInfo.show">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl leading-6 text-ink-gray-9 font-semibold">
|
||||
{{ __('Delete') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Button variant="ghost" icon="x" @click="show = false" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-ink-gray-5">
|
||||
{{
|
||||
confirmDeleteInfo.delete
|
||||
? __(
|
||||
'This will delete selected items and items linked to it, are you sure?',
|
||||
)
|
||||
: __(
|
||||
'This will delete selected items and unlink linked items to it, are you sure?',
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 pb-7 pt-0 sm:px-6">
|
||||
<div class="flex flex-row-reverse gap-2">
|
||||
<Button
|
||||
:label="
|
||||
confirmDeleteInfo.delete ? __('Delete') : __('Unlink and delete')
|
||||
"
|
||||
:icon-left="confirmDeleteInfo.delete ? 'trash-2' : 'unlock'"
|
||||
variant="solid"
|
||||
theme="red"
|
||||
@click="deleteDocs()"
|
||||
/>
|
||||
<Button
|
||||
:label="__('Cancel')"
|
||||
variant="subtle"
|
||||
@click="confirmDeleteInfo.show = false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { call } from 'frappe-ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const show = defineModel()
|
||||
const props = defineProps({
|
||||
doctype: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
reload: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const confirmDeleteInfo = ref({
|
||||
show: false,
|
||||
title: '',
|
||||
message: '',
|
||||
delete: false,
|
||||
})
|
||||
|
||||
const confirmDelete = () => {
|
||||
confirmDeleteInfo.value = {
|
||||
show: true,
|
||||
title: __('Delete'),
|
||||
message: __('Are you sure you want to delete {0} linked doc(s)?', [
|
||||
props.items.length,
|
||||
]),
|
||||
delete: true,
|
||||
}
|
||||
}
|
||||
|
||||
const confirmUnlink = () => {
|
||||
confirmDeleteInfo.value = {
|
||||
show: true,
|
||||
title: __('Unlink'),
|
||||
message: __('Are you sure you want to unlink {0} linked doc(s)?', [
|
||||
props.items.length,
|
||||
]),
|
||||
delete: false,
|
||||
}
|
||||
}
|
||||
|
||||
const deleteDocs = () => {
|
||||
call('crm.api.doc.delete_bulk_docs', {
|
||||
items: props.items,
|
||||
doctype: props.doctype,
|
||||
delete_linked: confirmDeleteInfo.value.delete,
|
||||
}).then(() => {
|
||||
confirmDeleteInfo.value = {
|
||||
show: false,
|
||||
title: '',
|
||||
}
|
||||
show.value = false
|
||||
props.reload()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
265
frontend/src/components/DeleteLinkedDocModal.vue
Normal file
265
frontend/src/components/DeleteLinkedDocModal.vue
Normal file
@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ size: 'xl' }">
|
||||
<template #body v-if="!confirmDeleteInfo.show">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl leading-6 text-ink-gray-9 font-semibold">
|
||||
{{
|
||||
linkedDocs?.length == 0
|
||||
? __('Delete')
|
||||
: __('Delete or unlink linked documents')
|
||||
}}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Button variant="ghost" icon="x" @click="show = false" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="linkedDocs?.length > 0">
|
||||
<span class="text-ink-gray-5 text-base">
|
||||
{{
|
||||
__(
|
||||
'Delete or unlink these linked documents before deleting this document',
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<LinkedDocsListView
|
||||
class="mt-4"
|
||||
:rows="linkedDocs"
|
||||
:columns="[
|
||||
{
|
||||
label: 'Document',
|
||||
key: 'title',
|
||||
},
|
||||
{
|
||||
label: 'Master',
|
||||
key: 'reference_doctype',
|
||||
width: '30%',
|
||||
},
|
||||
]"
|
||||
@selectionsChanged="
|
||||
(selections) => viewControls.updateSelections(selections)
|
||||
"
|
||||
:linkedDocsResource="linkedDocsResource"
|
||||
:unlinkLinkedDoc="unlinkLinkedDoc"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="linkedDocs?.length == 0" class="text-ink-gray-5 text-base">
|
||||
{{
|
||||
__('Are you sure you want to delete {0} - {1}?', [
|
||||
props.doctype,
|
||||
props.docname,
|
||||
])
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 pb-7 pt-0 sm:px-6">
|
||||
<div class="flex flex-row-reverse gap-2">
|
||||
<Button
|
||||
v-if="linkedDocs?.length > 0"
|
||||
:label="
|
||||
viewControls?.selections?.length == 0
|
||||
? __('Delete all')
|
||||
: __('Delete {0} item(s)', [viewControls?.selections?.length])
|
||||
"
|
||||
theme="red"
|
||||
variant="solid"
|
||||
icon-left="trash-2"
|
||||
@click="confirmDelete()"
|
||||
/>
|
||||
<Button
|
||||
v-if="linkedDocs?.length > 0"
|
||||
:label="
|
||||
viewControls?.selections?.length == 0
|
||||
? __('Unlink all')
|
||||
: __('Unlink {0} item(s)', [viewControls?.selections?.length])
|
||||
"
|
||||
variant="subtle"
|
||||
theme="gray"
|
||||
icon-left="unlock"
|
||||
@click="confirmUnlink()"
|
||||
/>
|
||||
<Button
|
||||
v-if="linkedDocs?.length == 0"
|
||||
variant="solid"
|
||||
icon-left="trash-2"
|
||||
:label="__('Delete')"
|
||||
:loading="isDealCreating"
|
||||
@click="deleteDoc()"
|
||||
theme="red"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #body v-if="confirmDeleteInfo.show">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl leading-6 text-ink-gray-9 font-semibold">
|
||||
{{ confirmDeleteInfo.title }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Button variant="ghost" icon="x" @click="show = false" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-ink-gray-5 text-base">
|
||||
{{ confirmDeleteInfo.message }}
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 mt-6">
|
||||
<Button variant="ghost" @click="cancel()">
|
||||
{{ __('Cancel') }}
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="confirmDeleteInfo.title"
|
||||
@click="removeDocLinks()"
|
||||
theme="red"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { createResource, call } from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const show = defineModel()
|
||||
const router = useRouter()
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
doctype: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
docname: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
reload: {
|
||||
type: Function,
|
||||
},
|
||||
})
|
||||
const viewControls = ref({
|
||||
selections: [],
|
||||
updateSelections: (selections) => {
|
||||
viewControls.value.selections = Array.from(selections || [])
|
||||
},
|
||||
})
|
||||
|
||||
const confirmDeleteInfo = ref({
|
||||
show: false,
|
||||
title: '',
|
||||
})
|
||||
|
||||
const linkedDocsResource = createResource({
|
||||
url: 'crm.api.doc.get_linked_docs_of_document',
|
||||
params: {
|
||||
doctype: props.doctype,
|
||||
docname: props.docname,
|
||||
},
|
||||
auto: true,
|
||||
validate(params) {
|
||||
if (!params?.doctype || !params?.docname) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const linkedDocs = computed(() => {
|
||||
return (
|
||||
linkedDocsResource.data?.map((doc) => ({
|
||||
id: doc.reference_docname,
|
||||
...doc,
|
||||
})) || []
|
||||
)
|
||||
})
|
||||
|
||||
const cancel = () => {
|
||||
confirmDeleteInfo.value.show = false
|
||||
viewControls.value.updateSelections([])
|
||||
}
|
||||
|
||||
const unlinkLinkedDoc = (doc) => {
|
||||
let selectedDocs = []
|
||||
if (viewControls.value.selections.length > 0) {
|
||||
Array.from(viewControls.value.selections).forEach((selection) => {
|
||||
const docData = linkedDocs.value.find((d) => d.id == selection)
|
||||
selectedDocs.push({
|
||||
doctype: docData.reference_doctype,
|
||||
docname: docData.reference_docname,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
selectedDocs = linkedDocs.value.map((doc) => ({
|
||||
doctype: doc.reference_doctype,
|
||||
docname: doc.reference_docname,
|
||||
}))
|
||||
}
|
||||
|
||||
call('crm.api.doc.remove_linked_doc_reference', {
|
||||
items: selectedDocs,
|
||||
remove_contact: props.doctype == 'Contact',
|
||||
delete: doc.delete,
|
||||
}).then(() => {
|
||||
linkedDocsResource.reload()
|
||||
confirmDeleteInfo.value = {
|
||||
show: false,
|
||||
title: '',
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const confirmDelete = () => {
|
||||
const items =
|
||||
viewControls.value.selections.length == 0
|
||||
? 'all'
|
||||
: viewControls.value.selections.length
|
||||
confirmDeleteInfo.value = {
|
||||
show: true,
|
||||
title: __('Delete linked item'),
|
||||
message: __('Are you sure you want to delete {0} linked item(s)?', [items]),
|
||||
delete: true,
|
||||
}
|
||||
}
|
||||
|
||||
const confirmUnlink = () => {
|
||||
const items =
|
||||
viewControls.value.selections.length == 0
|
||||
? 'all'
|
||||
: viewControls.value.selections.length
|
||||
confirmDeleteInfo.value = {
|
||||
show: true,
|
||||
title: __('Unlink linked item'),
|
||||
message: __('Are you sure you want to unlink {0} linked item(s)?', [items]),
|
||||
delete: false,
|
||||
}
|
||||
}
|
||||
|
||||
const removeDocLinks = () => {
|
||||
unlinkLinkedDoc({
|
||||
reference_doctype: props.doctype,
|
||||
reference_docname: props.docname,
|
||||
delete: confirmDeleteInfo.value.delete,
|
||||
})
|
||||
viewControls.value.updateSelections([])
|
||||
}
|
||||
|
||||
const deleteDoc = async () => {
|
||||
await call('frappe.client.delete', {
|
||||
doctype: props.doctype,
|
||||
name: props.docname,
|
||||
})
|
||||
router.push({ name: props.name })
|
||||
props?.reload?.()
|
||||
}
|
||||
</script>
|
||||
139
frontend/src/components/ListViews/LinkedDocsListView.vue
Normal file
139
frontend/src/components/ListViews/LinkedDocsListView.vue
Normal file
@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<ListView
|
||||
:class="$attrs.class"
|
||||
:columns="columns"
|
||||
:rows="rows"
|
||||
:options="{
|
||||
selectable: true,
|
||||
showTooltip: true,
|
||||
resizeColumn: true,
|
||||
}"
|
||||
row-key="reference_docname"
|
||||
@update:selections="(selections) => emit('selectionsChanged', selections)"
|
||||
ref="listViewRef"
|
||||
>
|
||||
<ListHeader @columnWidthUpdated="emit('columnWidthUpdated')">
|
||||
<ListHeaderItem
|
||||
v-for="column in columns"
|
||||
:key="column.key"
|
||||
:item="column"
|
||||
@columnWidthUpdated="emit('columnWidthUpdated', column)"
|
||||
>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<div class="*:mx-0 *:sm:mx-0">
|
||||
<ListRows :rows="rows" v-slot="{ idx, column, item, row }">
|
||||
<ListRowItem
|
||||
:item="item"
|
||||
@click="listViewRef.toggleRow(row['reference_docname'])"
|
||||
>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="column.key === 'title'"
|
||||
class="truncate text-base flex gap-2"
|
||||
>
|
||||
<span>
|
||||
{{ label }}
|
||||
</span>
|
||||
<FeatherIcon
|
||||
name="external-link"
|
||||
class="h-4 w-4 cursor-pointer"
|
||||
@click.stop="viewLinkedDoc(row)"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
v-if="column.key === 'reference_doctype'"
|
||||
class="truncate text-base flex gap-2"
|
||||
>
|
||||
{{ getDoctypeName(row.reference_doctype) }}
|
||||
</span>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRows>
|
||||
</div>
|
||||
</ListView>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ListRows from '@/components/ListViews/ListRows.vue'
|
||||
import { ListView, ListHeader, ListHeaderItem, ListRowItem } from 'frappe-ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
rows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
linkedDocsResource: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
unlinkLinkedDoc: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
selectable: true,
|
||||
showTooltip: true,
|
||||
resizeColumn: false,
|
||||
totalCount: 0,
|
||||
rowCount: 0,
|
||||
}),
|
||||
},
|
||||
})
|
||||
const emit = defineEmits([
|
||||
'loadMore',
|
||||
'updatePageCount',
|
||||
'columnWidthUpdated',
|
||||
'applyFilter',
|
||||
'applyLikeFilter',
|
||||
'likeDoc',
|
||||
'selectionsChanged',
|
||||
])
|
||||
|
||||
const listViewRef = ref(null)
|
||||
|
||||
const viewLinkedDoc = (doc) => {
|
||||
let page = ''
|
||||
let id = ''
|
||||
switch (doc.reference_doctype) {
|
||||
case 'CRM Lead':
|
||||
page = 'leads'
|
||||
id = doc.reference_docname
|
||||
break
|
||||
case 'CRM Call Log':
|
||||
page = 'call-logs'
|
||||
id = `view?open=${doc.reference_docname}`
|
||||
break
|
||||
case 'CRM Task':
|
||||
page = 'tasks'
|
||||
id = `view?open=${doc.reference_docname}`
|
||||
break
|
||||
case 'Contact':
|
||||
page = 'contacts'
|
||||
id = doc.reference_docname
|
||||
break
|
||||
case 'CRM Organization':
|
||||
page = 'organizations'
|
||||
id = doc.reference_docname
|
||||
break
|
||||
case 'FCRM Note':
|
||||
page = 'notes'
|
||||
id = `view?open=${doc.reference_docname}`
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
window.open(`/crm/${page}/${id}`)
|
||||
}
|
||||
|
||||
const getDoctypeName = (doctype) => {
|
||||
return doctype.replace(/^(CRM|FCRM)\s*/, '')
|
||||
}
|
||||
</script>
|
||||
@ -91,54 +91,57 @@
|
||||
<div class="flex gap-1.5">
|
||||
<Tooltip v-if="callEnabled" :text="__('Make a call')">
|
||||
<div>
|
||||
<Button class="h-7 w-7" @click="triggerCall">
|
||||
<template #icon>
|
||||
<PhoneIcon />
|
||||
</template>
|
||||
<Button @click="triggerCall">
|
||||
<template #icon><PhoneIcon /></template>
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip :text="__('Send an email')">
|
||||
<div>
|
||||
<Button
|
||||
class="h-7 w-7"
|
||||
@click="
|
||||
deal.data.email
|
||||
? openEmailBox()
|
||||
: toast.error(__('No email set'))
|
||||
"
|
||||
>
|
||||
<template #icon>
|
||||
<Email2Icon />
|
||||
</template>
|
||||
<template #icon><Email2Icon /></template>
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip :text="__('Go to website')">
|
||||
<div>
|
||||
<Button
|
||||
class="h-7 w-7"
|
||||
@click="
|
||||
deal.data.website
|
||||
? openWebsite(deal.data.website)
|
||||
: toast.error(__('No website set'))
|
||||
"
|
||||
>
|
||||
<template #icon>
|
||||
<LinkIcon />
|
||||
</template>
|
||||
<template #icon><LinkIcon /></template>
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip :text="__('Attach a file')">
|
||||
<div>
|
||||
<Button class="size-7" @click="showFilesUploader = true">
|
||||
<template #icon>
|
||||
<AttachmentIcon />
|
||||
</template>
|
||||
<Button @click="showFilesUploader = true">
|
||||
<template #icon><AttachmentIcon /></template>
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
<Tooltip :text="__('Delete')">
|
||||
<div>
|
||||
<Button
|
||||
@click="deleteDealWithModal(deal.data.name)"
|
||||
variant="subtle"
|
||||
icon="trash-2"
|
||||
theme="red"
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
>>>>>>> 65435cf2 (fix: delete icon issue & more cleanup)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -134,7 +134,6 @@
|
||||
<Tooltip v-if="callEnabled" :text="__('Make a call')">
|
||||
<div>
|
||||
<Button
|
||||
class="h-7 w-7"
|
||||
@click="
|
||||
() =>
|
||||
lead.data.mobile_no
|
||||
@ -151,7 +150,6 @@
|
||||
<Tooltip :text="__('Send an email')">
|
||||
<div>
|
||||
<Button
|
||||
class="h-7 w-7"
|
||||
@click="
|
||||
lead.data.email
|
||||
? openEmailBox()
|
||||
@ -167,7 +165,6 @@
|
||||
<Tooltip :text="__('Go to website')">
|
||||
<div>
|
||||
<Button
|
||||
class="h-7 w-7"
|
||||
@click="
|
||||
lead.data.website
|
||||
? openWebsite(lead.data.website)
|
||||
@ -182,13 +179,26 @@
|
||||
</Tooltip>
|
||||
<Tooltip :text="__('Attach a file')">
|
||||
<div>
|
||||
<Button class="h-7 w-7" @click="showFilesUploader = true">
|
||||
<Button @click="showFilesUploader = true">
|
||||
<template #icon>
|
||||
<AttachmentIcon />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
<Tooltip :text="__('Delete')">
|
||||
<div>
|
||||
<Button
|
||||
@click="deleteLeadWithModal(lead.data.name)"
|
||||
variant="subtle"
|
||||
theme="red"
|
||||
icon="trash-2"
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
>>>>>>> 65435cf2 (fix: delete icon issue & more cleanup)
|
||||
</div>
|
||||
<ErrorMessage :message="__(error)" />
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user