Добавил уведомление о создании отчета
This commit is contained in:
@@ -10,8 +10,16 @@ export function useNotification() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const success = () => {
|
const success = (title, description = '', options = {}) => {
|
||||||
|
return showNotification(
|
||||||
|
{
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
meta: options.meta || new Date().toLocaleDateString(),
|
||||||
|
...options,
|
||||||
|
type: 'success'
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorApi = (data, content = '', options = {}) => {
|
const errorApi = (data, content = '', options = {}) => {
|
||||||
@@ -36,6 +44,7 @@ export function useNotification() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
errorApi
|
errorApi,
|
||||||
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,7 +229,6 @@ watch([dateModel, departmentModel], () => {
|
|||||||
<NDropdown trigger="click" :options="exportMenu" @select="download">
|
<NDropdown trigger="click" :options="exportMenu" @select="download">
|
||||||
<NButton>
|
<NButton>
|
||||||
<template #icon><TbDownload /></template>
|
<template #icon><TbDownload /></template>
|
||||||
Скачать
|
|
||||||
</NButton>
|
</NButton>
|
||||||
</NDropdown>
|
</NDropdown>
|
||||||
<NButton v-if="canManage" @click="save">
|
<NButton v-if="canManage" @click="save">
|
||||||
@@ -330,7 +329,7 @@ watch([dateModel, departmentModel], () => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
border: 1px solid var(--n-border-color, rgba(255,255,255,.1));
|
border: 1px solid var(--border-color, rgba(255,255,255,.1));
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
background: var(--card-color);
|
background: var(--card-color);
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
@@ -356,9 +355,9 @@ watch([dateModel, departmentModel], () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 1px solid var(--n-border-color, rgba(255,255,255,.12));
|
border: 1px solid var(--border-color, rgba(255,255,255,.12));
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--n-card-color);
|
background: var(--card-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.slide-enter-active, .slide-leave-active { transition: opacity .15s, transform .15s; }
|
.slide-enter-active, .slide-leave-active { transition: opacity .15s, transform .15s; }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { NText } from 'naive-ui'
|
import { NText, NEl } from 'naive-ui'
|
||||||
import { TbPlus } from 'vue-icons-plus/tb'
|
import { TbPlus } from 'vue-icons-plus/tb'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
@@ -8,7 +8,7 @@ defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="preset-card">
|
<NEl class="preset-card">
|
||||||
<div v-if="preset.key === 'blank'" class="preset-blank">
|
<div v-if="preset.key === 'blank'" class="preset-blank">
|
||||||
<TbPlus :size="26" />
|
<TbPlus :size="26" />
|
||||||
</div>
|
</div>
|
||||||
@@ -17,7 +17,7 @@ defineProps({
|
|||||||
</div>
|
</div>
|
||||||
<NText class="preset-title">{{ preset.label }}</NText>
|
<NText class="preset-title">{{ preset.label }}</NText>
|
||||||
<NText depth="3" class="preset-desc">{{ preset.description }}</NText>
|
<NText depth="3" class="preset-desc">{{ preset.description }}</NText>
|
||||||
</div>
|
</NEl>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import {
|
import {
|
||||||
NButton, NText, NIcon, NSelect, NScrollbar, NGrid, NGi, NAlert, NRadio, NEmpty,
|
NButton, NText, NIcon, NSelect, NScrollbar, NGrid, NGi, NAlert, NRadio, NEmpty, NEl
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import {
|
import {
|
||||||
TbX, TbChevronRight, TbCalendar, TbTrendingUp, TbLayoutGrid, TbDatabase,
|
TbX, TbChevronRight, TbCalendar, TbTrendingUp, TbLayoutGrid, TbDatabase,
|
||||||
@@ -60,7 +60,7 @@ const filterOptions = (def) => (def.options ?? [])
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="settings">
|
<NEl class="settings">
|
||||||
<!-- ROOT -->
|
<!-- ROOT -->
|
||||||
<template v-if="view === 'root'">
|
<template v-if="view === 'root'">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
@@ -242,11 +242,11 @@ const filterOptions = (def) => (def.options ?? [])
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</NEl>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.settings { padding: 4px 2px; background: var(--n-) }
|
.settings { padding: 4px 2px; }
|
||||||
.head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
|
.head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
|
||||||
.head-title { font-size: 18px; font-weight: 600; }
|
.head-title { font-size: 18px; font-weight: 600; }
|
||||||
.sub-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
|
.sub-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
|
||||||
@@ -257,15 +257,15 @@ const filterOptions = (def) => (def.options ?? [])
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border: 1px solid var(--n-border-color, rgba(255,255,255,.1));
|
border: 1px solid var(--border-color, rgba(255,255,255,.1));
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: var(--n-card-color);
|
background: var(--card-color);
|
||||||
transition: border-color .15s;
|
transition: border-color .15s;
|
||||||
}
|
}
|
||||||
.row:hover { border-color: var(--primary-color); }
|
.row:hover { border-color: var(--primary-color); }
|
||||||
.row.static, .row.static:hover { cursor: default; border-color: var(--n-border-color, rgba(255,255,255,.1)); }
|
.row.static, .row.static:hover { cursor: default; border-color: var(--border-color, rgba(255,255,255,.1)); }
|
||||||
.row.disabled { opacity: .5; pointer-events: none; }
|
.row.disabled { opacity: .5; pointer-events: none; }
|
||||||
.row-icon { color: var(--primary-color); }
|
.row-icon { color: var(--primary-color); }
|
||||||
.row-label { flex: 1; font-weight: 500; }
|
.row-label { flex: 1; font-weight: 500; }
|
||||||
@@ -293,7 +293,7 @@ const filterOptions = (def) => (def.options ?? [])
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--n-text-color-2);
|
color: var(--n-text-color-2);
|
||||||
}
|
}
|
||||||
.seg-btn.active { background: var(--n-card-color); font-weight: 600; box-shadow: 0 1px 3px rgba(0,0,0,.1); }
|
.seg-btn.active { background: var(--card-color); font-weight: 600; box-shadow: 0 1px 3px rgba(0,0,0,.1); }
|
||||||
.data-list { display: flex; flex-direction: column; gap: 8px; }
|
.data-list { display: flex; flex-direction: column; gap: 8px; }
|
||||||
.data-card {
|
.data-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -301,7 +301,7 @@ const filterOptions = (def) => (def.options ?? [])
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border: 1px solid var(--n-border-color, rgba(255,255,255,.1));
|
border: 1px solid var(--border-color, rgba(255,255,255,.1));
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,16 +32,17 @@ const pick = (preset) => {
|
|||||||
:show="show"
|
:show="show"
|
||||||
preset="card"
|
preset="card"
|
||||||
title="Шаблоны для отчёта"
|
title="Шаблоны для отчёта"
|
||||||
style="width: 860px; max-width: 94vw;"
|
class="max-w-[860px] h-[580px] relative"
|
||||||
class="h-[580px]"
|
:content-scrollable="true"
|
||||||
@update:show="emit('update:show', $event)"
|
@update:show="emit('update:show', $event)"
|
||||||
>
|
>
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<NText depth="3" style="font-size: 13px;">Создавайте отчёт по шаблонам</NText>
|
<NText depth="3" style="font-size: 13px;">Создавайте отчёт по шаблонам</NText>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="picker">
|
<div class="picker relative flex gap-2 items-start">
|
||||||
<div class="picker-side">
|
<!-- Боковая панель с sticky -->
|
||||||
|
<div class="picker-side sticky top-0 shrink-0 align-start">
|
||||||
<NInput v-model:value="search" placeholder="Поиск" clearable size="small">
|
<NInput v-model:value="search" placeholder="Поиск" clearable size="small">
|
||||||
<template #prefix><TbSearch :size="14" /></template>
|
<template #prefix><TbSearch :size="14" /></template>
|
||||||
</NInput>
|
</NInput>
|
||||||
@@ -58,13 +59,14 @@ const pick = (preset) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NScrollbar style="max-height: 60vh;" class="picker-main">
|
<!-- Контент справа -->
|
||||||
|
<div style="flex: 1; min-width: 0;">
|
||||||
<NGrid responsive="screen" cols="2 s:3" :x-gap="12" :y-gap="12" class="pt-0.5">
|
<NGrid responsive="screen" cols="2 s:3" :x-gap="12" :y-gap="12" class="pt-0.5">
|
||||||
<NGi v-for="preset in filtered" :key="preset.key">
|
<NGi v-for="preset in filtered" :key="preset.key">
|
||||||
<PresetCard :preset="preset" @click="pick(preset)" />
|
<PresetCard :preset="preset" @click="pick(preset)" />
|
||||||
</NGi>
|
</NGi>
|
||||||
</NGrid>
|
</NGrid>
|
||||||
</NScrollbar>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NModal>
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { router, Link } from '@inertiajs/vue3'
|
|||||||
import {
|
import {
|
||||||
NButton, NText, NDataTable, NEmpty, NDropdown, NIcon, NScrollbar,
|
NButton, NText, NDataTable, NEmpty, NDropdown, NIcon, NScrollbar,
|
||||||
NTag, useDialog, useMessage,
|
NTag, useDialog, useMessage,
|
||||||
|
NEl,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import { TbReportMedical, TbPlus, TbDotsVertical, TbLayoutGrid } from 'vue-icons-plus/tb'
|
import { TbReportMedical, TbPlus, TbDotsVertical, TbLayoutGrid } from 'vue-icons-plus/tb'
|
||||||
import AppLayout from '../../Layouts/AppLayout.vue'
|
import AppLayout from '../../Layouts/AppLayout.vue'
|
||||||
@@ -54,7 +55,7 @@ const remove = (row) => {
|
|||||||
const rowMenu = (row) => [
|
const rowMenu = (row) => [
|
||||||
{ key: 'open', label: 'Открыть' },
|
{ key: 'open', label: 'Открыть' },
|
||||||
{ key: 'duplicate', label: 'Дублировать' },
|
{ key: 'duplicate', label: 'Дублировать' },
|
||||||
{ key: 'delete', label: 'Удалить', props: { style: 'color: var(--error-color);' } },
|
{ key: 'delete', label: 'Удалить' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const onMenuSelect = (key, row) => {
|
const onMenuSelect = (key, row) => {
|
||||||
@@ -85,7 +86,8 @@ const columns = computed(() => [
|
|||||||
options: rowMenu(row),
|
options: rowMenu(row),
|
||||||
onSelect: (key) => onMenuSelect(key, row),
|
onSelect: (key) => onMenuSelect(key, row),
|
||||||
}, () => h(NButton, {
|
}, () => h(NButton, {
|
||||||
text: true,
|
round: true,
|
||||||
|
size: 'small',
|
||||||
onClick: (e) => e.stopPropagation(),
|
onClick: (e) => e.stopPropagation(),
|
||||||
}, () => h(NIcon, null, () => h(TbDotsVertical)))) : null,
|
}, () => h(NIcon, null, () => h(TbDotsVertical)))) : null,
|
||||||
},
|
},
|
||||||
@@ -114,10 +116,10 @@ const columns = computed(() => [
|
|||||||
</template>
|
</template>
|
||||||
<NScrollbar x-scrollable class="pb-3">
|
<NScrollbar x-scrollable class="pb-3">
|
||||||
<div class="create-row">
|
<div class="create-row">
|
||||||
<div class="create-card" @click="openPreset({ key: 'blank' })">
|
<NEl class="create-card" @click="openPreset({ key: 'blank' })">
|
||||||
<div class="blank-tile"><NIcon :size="28"><TbPlus /></NIcon></div>
|
<div class="blank-tile"><NIcon :size="28"><TbPlus /></NIcon></div>
|
||||||
<NText class="blank-title">Отчёт с нуля</NText>
|
<NText class="blank-title">Отчёт с нуля</NText>
|
||||||
</div>
|
</NEl>
|
||||||
<div v-for="preset in featured" :key="preset.key" class="create-card" @click="openPreset(preset)">
|
<div v-for="preset in featured" :key="preset.key" class="create-card" @click="openPreset(preset)">
|
||||||
<PresetCard :preset="preset" />
|
<PresetCard :preset="preset" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppLayout from "../../../Layouts/AppLayout.vue";
|
import AppLayout from "../../../Layouts/AppLayout.vue";
|
||||||
import {NFlex, NTag, NDataTable, NButton, NTabs, NTabPane, NSpace} from 'naive-ui'
|
import {NFlex, NTag, NDataTable, NButton, NTabs, NTabPane, NSpace} from 'naive-ui'
|
||||||
|
import {useNotification} from "../../../Composables/useNotification.js"
|
||||||
import AppContainer from "../../../Components/AppContainer.vue";
|
import AppContainer from "../../../Components/AppContainer.vue";
|
||||||
import AppPanel from "../../../Components/AppPanel.vue";
|
import AppPanel from "../../../Components/AppPanel.vue";
|
||||||
import ShiftPickerQuery from "../../../Components/ShiftPickerQuery.vue";
|
import ShiftPickerQuery from "../../../Components/ShiftPickerQuery.vue";
|
||||||
@@ -163,13 +164,46 @@ const onClickDeleteButton = async (historyId) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {success} = useNotification()
|
||||||
|
|
||||||
|
const notifyReportSaved = () => {
|
||||||
|
const notification = success('Сохранено', 'Отчет успешно сохранен', {
|
||||||
|
meta: '',
|
||||||
|
duration: 5000,
|
||||||
|
action: () => h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
text: true,
|
||||||
|
type: 'success',
|
||||||
|
onClick: () => {
|
||||||
|
notification.destroy()
|
||||||
|
router.visit('/')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{default: () => 'Вернуться на главную страницу'}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
router.post('/nurse/report/save', {
|
const queryString = window.location.search;
|
||||||
|
const params = new URLSearchParams(queryString);
|
||||||
|
const url = new URL(`${window.location.origin}/nurse/report/save`)
|
||||||
|
const startAt = params.get('startAt')
|
||||||
|
const endAt = params.get('endAt')
|
||||||
|
if (startAt && startAt !== 'null') url.searchParams.append('startAt', startAt)
|
||||||
|
if (endAt && endAt !== 'null') url.searchParams.append('endAt', endAt)
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
router.post(url.toString(), {
|
||||||
userId: props.selectedUserId,
|
userId: props.selectedUserId,
|
||||||
departmentId: props.selectedDepartmentId,
|
departmentId: props.selectedDepartmentId,
|
||||||
}, {
|
}, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
alert('Сохранено')
|
notifyReportSaved()
|
||||||
|
},
|
||||||
|
onFinish: () => {
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppPanel from "../../../Components/AppPanel.vue";
|
import AppPanel from "../../../Components/AppPanel.vue";
|
||||||
import {NNumberAnimation, NStatistic} from "naive-ui";
|
import {NNumberAnimation, NStatistic, NTooltip} from "naive-ui";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
label: String,
|
label: String,
|
||||||
counter: {
|
counter: {
|
||||||
@@ -35,15 +35,27 @@ const props = defineProps({
|
|||||||
<div class="flex flex-col items-center justify-center text-center py-2">
|
<div class="flex flex-col items-center justify-center text-center py-2">
|
||||||
<NStatistic :label="label">
|
<NStatistic :label="label">
|
||||||
<template v-if="isDoubleCounter">
|
<template v-if="isDoubleCounter">
|
||||||
<span :class="counterClass">
|
<NTooltip>
|
||||||
<NNumberAnimation :from="0" :to="counter" />
|
<template #trigger>
|
||||||
<span v-if="percent" style="color: var(--n-close-icon-color)">%</span>
|
<span :class="counterClass">
|
||||||
</span>
|
<NNumberAnimation :from="0" :to="counter" />
|
||||||
|
<span v-if="percent" style="color: var(--n-close-icon-color)">%</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
Данные из МИС
|
||||||
|
</NTooltip>
|
||||||
|
|
||||||
<span style="color: var(--n-close-icon-color)"> / </span>
|
<span style="color: var(--n-close-icon-color)"> / </span>
|
||||||
<span :class="counterSuffixClass">
|
<NTooltip>
|
||||||
<NNumberAnimation :from="0" :to="counterSuffix" />
|
<template #trigger>
|
||||||
<span v-if="percent" style="color: var(--n-close-icon-color)">%</span>
|
<span :class="counterSuffixClass">
|
||||||
</span>
|
<NNumberAnimation :from="0" :to="counterSuffix" />
|
||||||
|
<span v-if="percent" style="color: var(--n-close-icon-color)">%</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
Данные из журнала пациентов
|
||||||
|
</NTooltip>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<span v-else :class="counterClass">
|
<span v-else :class="counterClass">
|
||||||
<NNumberAnimation :from="0" :to="counter" />
|
<NNumberAnimation :from="0" :to="counter" />
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppLayout from "../../Layouts/AppLayout.vue";
|
import AppLayout from "../../Layouts/AppLayout.vue";
|
||||||
import {useReportStore} from "../../Stores/report.js";
|
import {useReportStore} from "../../Stores/report.js";
|
||||||
import {onMounted, ref, watch, provide, computed} from "vue";
|
import {onMounted, ref, watch, provide, computed, h} from "vue";
|
||||||
|
import {useNotification} from "../../Composables/useNotification.js";
|
||||||
import {useAuthStore} from "../../Stores/auth.js";
|
import {useAuthStore} from "../../Stores/auth.js";
|
||||||
import {
|
import {
|
||||||
NFormItem,
|
NFormItem,
|
||||||
@@ -88,6 +89,26 @@ const props = defineProps({
|
|||||||
const reportStore = useReportStore()
|
const reportStore = useReportStore()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const userDepartment = authStore.userDepartment
|
const userDepartment = authStore.userDepartment
|
||||||
|
const {success} = useNotification()
|
||||||
|
|
||||||
|
const notifyReportSaved = () => {
|
||||||
|
const notification = success('Сохранено', 'Отчет успешно сохранен', {
|
||||||
|
meta: '',
|
||||||
|
duration: 5000,
|
||||||
|
action: () => h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
text: true,
|
||||||
|
type: 'success',
|
||||||
|
onClick: () => {
|
||||||
|
notification.destroy()
|
||||||
|
router.visit('/')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{default: () => 'Вернуться на главную страницу'}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const patientCollection = ref({...props.patients})
|
const patientCollection = ref({...props.patients})
|
||||||
const nursePatientCollection = ref({...props.nursePatients})
|
const nursePatientCollection = ref({...props.nursePatients})
|
||||||
@@ -142,7 +163,7 @@ const submit = () => {
|
|||||||
departmentId: props.selectedDepartmentId,
|
departmentId: props.selectedDepartmentId,
|
||||||
}, {
|
}, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
alert('Сохранено')
|
notifyReportSaved()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user