Merge branch 'main' into login
This commit is contained in:
commit
e038c84baf
@ -1,18 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- 한글 버튼들 -->
|
<ul class="alphabet-list list-unstyled d-flex flex-wrap mb-0">
|
||||||
<ul>
|
<li v-for="char in koreanChars" :key="char" class="mt-2 mx-1">
|
||||||
<li v-for="char in koreanChars" :key="char" >
|
<button
|
||||||
<button type="button" class="nav-link" @click="filterByKoreanChar(char)">
|
type="button"
|
||||||
|
class="btn"
|
||||||
|
:class="selectedAlphabet === char ? 'btn-primary' : 'btn-outline-primary'"
|
||||||
|
@click="selectAlphabet(char)"
|
||||||
|
>
|
||||||
{{ char }}
|
{{ char }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul class="alphabet-list list-unstyled d-flex flex-wrap mb-0">
|
||||||
<!-- 영어 버튼들 -->
|
<li v-for="char in englishChars" :key="char" class="mt-2 mx-1">
|
||||||
<ul>
|
<button
|
||||||
<li v-for="char in englishChars" :key="char">
|
type="button"
|
||||||
<button type="button" class="nav-link" @click="filterByEnglishChar(char)">
|
class="btn"
|
||||||
|
:class="selectedAlphabet === char ? 'btn-primary' : 'btn-outline-primary'"
|
||||||
|
@click="selectAlphabet(char)"
|
||||||
|
>
|
||||||
{{ char }}
|
{{ char }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@ -21,28 +28,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const koreanChars = ['ㄱ', 'ㄴ', 'ㄷ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅅ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
|
||||||
|
const englishChars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
|
||||||
|
|
||||||
|
const selectedAlphabet = ref(null);
|
||||||
|
//emit정의
|
||||||
const emit = defineEmits();
|
const emit = defineEmits();
|
||||||
|
const selectAlphabet = (alphabet) => {
|
||||||
const koreanChars = ['ㄱ', 'ㄴ', 'ㄷ', 'ㄹ'];
|
selectedAlphabet.value = selectedAlphabet.value === alphabet ? null : alphabet;
|
||||||
const englishChars = ['a', 'b', 'c', 'd', 'e'];
|
emit('update:data',selectedAlphabet.value);
|
||||||
|
|
||||||
// 한글 필터링
|
|
||||||
const filterByKoreanChar = (char) => {
|
|
||||||
emit('filter', char, 'korean');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 영어 필터링
|
|
||||||
const filterByEnglishChar = (char) => {
|
|
||||||
emit('filter', char, 'english');
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.nav-pills {
|
.alphabet-list {
|
||||||
display: flex;
|
margin-left: -0.25rem;
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.alphabet-list {
|
||||||
|
overflow: scroll;
|
||||||
|
flex-wrap: nowrap !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<li class="mt-5">
|
<li class="mt-5 card p-5">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="w-100 d-flex align-items-center">
|
<div class="w-100 d-flex align-items-center">
|
||||||
<span class="btn btn-primary">{{ item.category }}</span>
|
<span class="btn btn-primary">{{ item.category }}</span>
|
||||||
@ -7,7 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<EditBtn />
|
<EditBtn />
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-5">{{ item.WRDDICCON }}</p>
|
<p class="mt-5" v-html="$common.contentToHtml(item.WRDDICCON)"></p>
|
||||||
<div class="d-flex justify-content-between flex-wrap gap-2 mb-2">
|
<div class="d-flex justify-content-between flex-wrap gap-2 mb-2">
|
||||||
<div class="d-flex flex-wrap align-items-center mb-50">
|
<div class="d-flex flex-wrap align-items-center mb-50">
|
||||||
<div class="avatar avatar-sm me-2">
|
<div class="avatar avatar-sm me-2">
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
<label class="col-md-2 col-form-label">카테고리 <span class="text-danger">*</span></label>
|
<label class="col-md-2 col-form-label">카테고리 <span class="text-danger">*</span></label>
|
||||||
<div class="d-flex flex-wrap align-items-center mt-3 ms-1">
|
<div class="d-flex flex-wrap align-items-center mt-3 ms-1">
|
||||||
<div
|
<div
|
||||||
v-for="(categoryName, index) in categoryList"
|
v-for="(category, index) in categoryList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="form-check me-3"
|
class="form-check me-3"
|
||||||
>
|
>
|
||||||
@ -30,18 +30,19 @@
|
|||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
type="radio"
|
type="radio"
|
||||||
:id="`category-${index}`"
|
:id="`category-${index}`"
|
||||||
:value="index"
|
:value="category.CMNCODVAL"
|
||||||
v-model="category"
|
v-model="categoryValue"
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" :for="`category-${index}`">
|
<label class="form-check-label" :for="`category-${index}`">
|
||||||
{{ categoryName }}
|
{{ category.CMNCODNAM }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="invalid-feedback" :class="categoryAlert ? 'display-block' : ''">카테고리를 선택해주세요.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 비밀번호 필드 -->
|
<!-- 비밀번호 필드 -->
|
||||||
<div v-if="category === 1" class="mb-4">
|
<div v-if="categoryValue === 300102" class="mb-4">
|
||||||
<FormInput
|
<FormInput
|
||||||
title="비밀번호"
|
title="비밀번호"
|
||||||
name="pw"
|
name="pw"
|
||||||
@ -94,25 +95,39 @@
|
|||||||
import QEditor from '@c/editor/QEditor.vue';
|
import QEditor from '@c/editor/QEditor.vue';
|
||||||
import FormInput from '@c/input/FormInput.vue';
|
import FormInput from '@c/input/FormInput.vue';
|
||||||
import FormFile from '@c/input/FormFile.vue';
|
import FormFile from '@c/input/FormFile.vue';
|
||||||
import { getCurrentInstance, ref } from 'vue';
|
import { getCurrentInstance, ref, onMounted } from 'vue';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import axios from '@api';
|
import axios from '@api';
|
||||||
|
|
||||||
const categoryList = ['자유', '익명', '공지사항']; // 카테고리 이름
|
const categoryList = ref([]);
|
||||||
const title = ref('');
|
const title = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const category = ref(0); // 기본값 0
|
const categoryValue = ref(null);
|
||||||
const content = ref('');
|
const content = ref('');
|
||||||
const attachFiles = ref(null);
|
const attachFiles = ref(null);
|
||||||
const isFileValid = ref(true); // 파일 유효성 상태 추가
|
const isFileValid = ref(true);
|
||||||
|
|
||||||
const titleAlert = ref(false);
|
const titleAlert = ref(false);
|
||||||
const passwordAlert = ref(false);
|
const passwordAlert = ref(false);
|
||||||
const contentAlert = ref(false);
|
const contentAlert = ref(false);
|
||||||
|
const categoryAlert = ref(false);
|
||||||
const attachFilesAlert = ref(false);
|
const attachFilesAlert = ref(false);
|
||||||
|
|
||||||
const { appContext } = getCurrentInstance();
|
const { appContext } = getCurrentInstance();
|
||||||
const $common = appContext.config.globalProperties.$common; // $common에 접근
|
const $common = appContext.config.globalProperties.$common;
|
||||||
|
|
||||||
|
const fetchCategories = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('board/categories');
|
||||||
|
categoryList.value = response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('카테고리 불러오기 오류:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchCategories();
|
||||||
|
});
|
||||||
|
|
||||||
const goList = () => {
|
const goList = () => {
|
||||||
router.push('/board');
|
router.push('/board');
|
||||||
@ -120,10 +135,11 @@ const goList = () => {
|
|||||||
|
|
||||||
const write = async () => {
|
const write = async () => {
|
||||||
titleAlert.value = !title.value;
|
titleAlert.value = !title.value;
|
||||||
passwordAlert.value = category.value === 1 && !password.value;
|
passwordAlert.value = categoryValue.value === 300102 && !password.value;
|
||||||
contentAlert.value = !content.value;
|
contentAlert.value = !content.value;
|
||||||
|
categoryAlert.value = !categoryValue.value;
|
||||||
|
|
||||||
if (titleAlert.value || passwordAlert.value || contentAlert.value || !isFileValid.value) {
|
if (titleAlert.value || passwordAlert.value || contentAlert.value || categoryAlert.value || !isFileValid.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +147,8 @@ const write = async () => {
|
|||||||
const boardData = {
|
const boardData = {
|
||||||
LOCBRDTTL: title.value,
|
LOCBRDTTL: title.value,
|
||||||
LOCBRDCON: $common.deltaAsJson(content.value),
|
LOCBRDCON: $common.deltaAsJson(content.value),
|
||||||
LOCBRDPWD: category.value === 1 ? password.value : null,
|
LOCBRDPWD: categoryValue.value === 300102 ? password.value : null,
|
||||||
LOCBRDTYP: category.value === 1 ? 'S' : 'F',
|
LOCBRDTYP: categoryValue.value
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data: boardResponse } = await axios.post('board', boardData);
|
const { data: boardResponse } = await axios.post('board', boardData);
|
||||||
|
|||||||
@ -1,28 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
|
|
||||||
<div class="card">
|
<div class="card p-5">
|
||||||
<!-- 타이틀, 검색 -->
|
<!-- 타이틀, 검색 -->
|
||||||
<div class="row mt-4">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-12 col-md-6">
|
||||||
<h5 class="mb-0">용어집</h5>
|
<h5 class="mb-0 title">용어집</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-12 col-md-6">
|
||||||
<SearchBar @update:data="search"/>
|
<SearchBar @update:data="search"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 단어 갯수, 작성하기 -->
|
<!-- 단어 갯수, 작성하기 -->
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
단어 : {{ filteredList.length }}
|
단어 : {{ total }}
|
||||||
<WriteButton @click="toggleWriteForm" />
|
<WriteButton @click="toggleWriteForm" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ㄱ ㄴ ㄷ ㄹ -->
|
<!-- ㄱ ㄴ ㄷ ㄹ -->
|
||||||
<div>
|
<div>
|
||||||
<DictAlphabetFilter/>
|
<DictAlphabetFilter @update:data="handleSelectedAlphabetChange" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 카테고리 -->
|
<!-- 카테고리 -->
|
||||||
<div v-if="cateList.length" class="mt-5">
|
<div v-if="cateList.length" class="mt-5">
|
||||||
<CategoryBtn :lists="cateList" />
|
<CategoryBtn :lists="cateList" />
|
||||||
@ -32,36 +32,43 @@
|
|||||||
<div v-if="isWriteVisible" class="mt-5">
|
<div v-if="isWriteVisible" class="mt-5">
|
||||||
<DictWrite @close="isWriteVisible = false" />
|
<DictWrite @close="isWriteVisible = false" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 용어 리스트 -->
|
|
||||||
<div class="mt-10">
|
|
||||||
<!-- 로딩 중일 때 -->
|
|
||||||
<div v-if="loading">로딩 중...</div>
|
|
||||||
|
|
||||||
<!-- 에러 메시지 -->
|
|
||||||
<div v-if="error" class="error">{{ error }}</div>
|
|
||||||
|
|
||||||
<!-- 단어 목록 -->
|
|
||||||
<ul v-if="filteredList.length" class="px-0 list-unstyled">
|
|
||||||
<DictCard
|
|
||||||
v-for="item in filteredList"
|
|
||||||
:key="item.WRDDICSEQ"
|
|
||||||
:item="item"
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<!-- 데이터가 없을 때 -->
|
|
||||||
<div v-else-if="!loading && !error">용어집의 용어가 없습니다.</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 용어 리스트 -->
|
||||||
|
<div class="mt-10">
|
||||||
|
<!-- 로딩 중일 때 -->
|
||||||
|
<div v-if="loading">로딩 중...</div>
|
||||||
|
|
||||||
|
<!-- 에러 메시지 -->
|
||||||
|
<div v-if="error" class="error">{{ error }}</div>
|
||||||
|
|
||||||
|
<!-- 단어 목록 -->
|
||||||
|
<ul v-if="total > 0" class="px-0 list-unstyled">
|
||||||
|
<DictCard
|
||||||
|
v-for="item in wordList"
|
||||||
|
:key="item.WRDDICSEQ"
|
||||||
|
:item="item"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
<!-- <ul v-if="wordList.length > 0" class="px-0 list-unstyled">
|
||||||
|
<DictCard
|
||||||
|
v-for="item in wordList"
|
||||||
|
:key="item.WRDDICSEQ"
|
||||||
|
:item="item"
|
||||||
|
/>
|
||||||
|
</ul> -->
|
||||||
|
|
||||||
|
<!-- 데이터가 없을 때 -->
|
||||||
|
<div v-else-if="!loading && !error">용어집의 용어가 없습니다.</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watchEffect, computed } from 'vue';
|
import { ref, watchEffect, computed, onMounted } from 'vue';
|
||||||
import axios from '@api';
|
import axios from '@api';
|
||||||
import SearchBar from '@c/search/SearchBar.vue';
|
import SearchBar from '@c/search/SearchBar.vue';
|
||||||
import WriteButton from '@c/button/WriteBtn.vue';
|
import WriteButton from '@c/button/WriteBtn.vue';
|
||||||
@ -69,7 +76,7 @@
|
|||||||
import DictCard from '@/components/wordDict/DictCard.vue';
|
import DictCard from '@/components/wordDict/DictCard.vue';
|
||||||
import DictWrite from '@/components/wordDict/DictWrite.vue';
|
import DictWrite from '@/components/wordDict/DictWrite.vue';
|
||||||
import DictAlphabetFilter from '@/components/wordDict/DictAlphabetFilter.vue';
|
import DictAlphabetFilter from '@/components/wordDict/DictAlphabetFilter.vue';
|
||||||
|
|
||||||
|
|
||||||
// 공통
|
// 공통
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@ -77,72 +84,110 @@
|
|||||||
|
|
||||||
// 용어집
|
// 용어집
|
||||||
const wordList = ref([]);
|
const wordList = ref([]);
|
||||||
|
//용어집 총개수
|
||||||
|
const total = ref(0);
|
||||||
// 카테고리
|
// 카테고리
|
||||||
const cateList = ref([]);
|
const cateList = ref([]);
|
||||||
|
//선택된 알파벳
|
||||||
|
const selectedAlphabet = ref('');
|
||||||
// 검색
|
// 검색
|
||||||
const searchText = ref('');
|
const searchText = ref('');
|
||||||
|
|
||||||
// 작성
|
// 작성
|
||||||
const isWriteVisible = ref(false);
|
const isWriteVisible = ref(false);
|
||||||
|
|
||||||
// 용어집 및 카테고리 목록 통합 API 호출
|
// 데이터 로드
|
||||||
const fetchAllData = async () => {
|
onMounted(() => {
|
||||||
loading.value = true;
|
getwordList(); //용어목록조회
|
||||||
error.value = '';
|
getwordCategory(); //카테고리목록조회
|
||||||
try {
|
});
|
||||||
// 용어집
|
//용어 목록
|
||||||
const wordResponse = await axios.get('worddict/getWordList');
|
const getwordList = (searchKeyword='',indexKeyword='') => {
|
||||||
wordList.value = wordResponse.data.data.data;
|
axios.get('worddict/getWordList',{
|
||||||
console.log('용어집 데이터:', wordList.value);
|
//목록조회시 파라미터 전달
|
||||||
// 카테고리
|
params: { searchKeyword: searchKeyword
|
||||||
const categoryResponse = await axios.get('worddict/getWordCategory');
|
,indexKeyword:indexKeyword
|
||||||
cateList.value = categoryResponse.data.data;
|
}
|
||||||
console.log('카테고리 데이터:', cateList.value);
|
})
|
||||||
} catch (err) {
|
.then(res => {
|
||||||
error.value = '데이터를 가져오는 중 문제가 발생했습니다.';
|
wordList.value = res.data.data.data; // 용어 목록 저장
|
||||||
} finally {
|
total.value = res.data.data.total; // 총 개수 저장
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('데이터 로드 오류:', err);
|
||||||
|
error.value = '데이터를 가져오는 중 문제가 발생했습니다.';
|
||||||
|
loading.value = false; // 로딩 종료
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 카테고리 목록
|
||||||
|
const getwordCategory = () => {
|
||||||
|
axios.get('worddict/getWordCategory')
|
||||||
|
.then(res => {
|
||||||
|
cateList.value = res.data.data; // 카테고리 목록 저장
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('카테고리 로드 오류:', err);
|
||||||
|
error.value = '카테고리 데이터를 가져오는 중 문제가 발생했습니다.';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleSelectedAlphabetChange = (newAlphabet) => {
|
||||||
|
selectedAlphabet.value = newAlphabet;
|
||||||
|
getwordList(searchText.value,selectedAlphabet.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 검색
|
// 검색
|
||||||
const search = (e) => {
|
const search = (e) => {
|
||||||
const trimmedSearchText = e.trim();
|
searchText.value = e.trim();
|
||||||
if (trimmedSearchText.length === 0) {
|
getwordList(searchText.value,selectedAlphabet.value);
|
||||||
alert('검색어를 입력해주세요.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
searchText.value = trimmedSearchText;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 용어집 및 카테고리 목록 통합 API 호출
|
||||||
|
// const fetchAllData = async () => {
|
||||||
|
// loading.value = true;
|
||||||
|
// error.value = '';
|
||||||
|
// try {
|
||||||
|
// // 용어집
|
||||||
|
// // const wordResponse = await axios.get('worddict/getWordList');
|
||||||
|
// //wordList.value = wordResponse.data.data.data;
|
||||||
|
// //console.log('용어집 데이터:', wordList.value);
|
||||||
|
|
||||||
|
// // 카테고리
|
||||||
|
// const categoryResponse = await axios.get('worddict/getWordCategory');
|
||||||
|
// cateList.value = categoryResponse.data.data;
|
||||||
|
// console.log('카테고리 데이터:', cateList.value);
|
||||||
|
// } catch (err) {
|
||||||
|
// error.value = '데이터를 가져오는 중 문제가 발생했습니다.';
|
||||||
|
// } finally {
|
||||||
|
// loading.value = false;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
// 검색
|
// 검색
|
||||||
const filteredList = computed(() =>
|
// const filteredList = computed(() =>
|
||||||
wordList.value.filter(item =>
|
// wordList.value.filter(item =>
|
||||||
item.WRDDICTTL.toLowerCase().includes(searchText.value.toLowerCase())
|
// item.WRDDICTTL.toLowerCase().includes(searchText.value.toLowerCase())
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
|
|
||||||
// 작성 toggle
|
// 작성 toggle
|
||||||
const toggleWriteForm = () => {
|
const toggleWriteForm = () => {
|
||||||
isWriteVisible.value = !isWriteVisible.value;
|
isWriteVisible.value = !isWriteVisible.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// `watchEffect`로 API 호출
|
|
||||||
watchEffect(() => {
|
|
||||||
fetchAllData();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.card > div {
|
|
||||||
padding: 0 30px
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: red;
|
color: red;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.title {
|
||||||
|
margin-bottom: 0.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user