릴리스: v1.2.57 관리자 패널 정리와 업데이트 로그 정렬
This commit is contained in:
@@ -64,12 +64,15 @@ const uploadFiles = ref([])
|
||||
const thumbFile = ref(null)
|
||||
const itemPreviewUrls = ref([])
|
||||
const isItemDragOver = ref(false)
|
||||
const isThumbDragOver = ref(false)
|
||||
const thumbPreviewUrl = ref('')
|
||||
const itemFileInput = ref(null)
|
||||
const thumbFileInput = ref(null)
|
||||
const featuredListEl = ref(null)
|
||||
const featuredSortable = ref(null)
|
||||
const userAvatarInputs = ref({})
|
||||
const isGameLoading = ref(false)
|
||||
const gameCreateModalOpen = ref(false)
|
||||
|
||||
const hasSelectedGame = computed(() => !!selectedGame.value?.game?.id)
|
||||
const canApplyThumbnail = computed(() => !!thumbFile.value && !!selectedGameId.value)
|
||||
@@ -168,6 +171,30 @@ onUnmounted(() => {
|
||||
destroyFeaturedSortable()
|
||||
})
|
||||
|
||||
function clearPreviewUrl(kind) {
|
||||
if (kind === 'item') {
|
||||
itemPreviewUrls.value.forEach((url) => {
|
||||
if (url) URL.revokeObjectURL(url)
|
||||
})
|
||||
itemPreviewUrls.value = []
|
||||
return
|
||||
}
|
||||
|
||||
if (thumbPreviewUrl.value) {
|
||||
URL.revokeObjectURL(thumbPreviewUrl.value)
|
||||
thumbPreviewUrl.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
function resetFileInput(kind) {
|
||||
if (kind === 'item') {
|
||||
if (itemFileInput.value) itemFileInput.value.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
if (thumbFileInput.value) thumbFileInput.value.value = ''
|
||||
}
|
||||
|
||||
watch(error, (message) => {
|
||||
if (!message) return
|
||||
toast.error(message)
|
||||
@@ -180,6 +207,15 @@ watch(success, (message) => {
|
||||
success.value = ''
|
||||
})
|
||||
|
||||
watch(
|
||||
() => activeTab.value,
|
||||
async (tab) => {
|
||||
if (tab === 'game-admin' && selectedGameId.value && !selectedGame.value?.game?.id) {
|
||||
await loadGame()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function resetMessages() {
|
||||
error.value = ''
|
||||
success.value = ''
|
||||
@@ -201,6 +237,22 @@ function setTierlistsMode(mode) {
|
||||
tierlistsMode.value = mode
|
||||
}
|
||||
|
||||
function openGameCreateModal() {
|
||||
resetMessages()
|
||||
newGameId.value = ''
|
||||
newGameName.value = ''
|
||||
gameCreateModalOpen.value = true
|
||||
}
|
||||
|
||||
function closeGameCreateModal() {
|
||||
gameCreateModalOpen.value = false
|
||||
}
|
||||
|
||||
async function handleSelectedGameChange(event) {
|
||||
selectedGameId.value = event?.target?.value || ''
|
||||
await loadGame()
|
||||
}
|
||||
|
||||
async function refreshGames() {
|
||||
try {
|
||||
const data = await api.listGames()
|
||||
@@ -404,6 +456,7 @@ async function loadGame() {
|
||||
}
|
||||
|
||||
try {
|
||||
isGameLoading.value = true
|
||||
const data = await api.getGame(selectedGameId.value)
|
||||
selectedGame.value = {
|
||||
...data,
|
||||
@@ -413,7 +466,11 @@ async function loadGame() {
|
||||
})),
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[AdminView] loadGame failed', selectedGameId.value, e)
|
||||
selectedGame.value = null
|
||||
error.value = '게임 정보를 불러오지 못했어요.'
|
||||
} finally {
|
||||
isGameLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,26 +481,56 @@ async function createGame() {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: newGameId.value, name: newGameName.value }),
|
||||
body: JSON.stringify({ id: newGameId.value.trim(), name: newGameName.value.trim() }),
|
||||
})
|
||||
if (!res.ok) throw new Error('failed')
|
||||
|
||||
const data = await res.json()
|
||||
await refreshGames()
|
||||
selectedGameId.value = data.game.id
|
||||
closeGameCreateModal()
|
||||
await loadGame()
|
||||
success.value = '게임이 생성됐어요. 이어서 썸네일과 아이템을 관리할 수 있어요.'
|
||||
success.value = '게임이 생성됐어요. 이어서 썸네일과 기본 아이템을 관리할 수 있어요.'
|
||||
} catch (e) {
|
||||
error.value = '게임 생성 실패(관리자 권한/중복 ID 확인)'
|
||||
}
|
||||
}
|
||||
|
||||
function onThumb(event) {
|
||||
thumbFile.value = event.target.files && event.target.files[0] ? event.target.files[0] : null
|
||||
function handleThumbFile(file) {
|
||||
const nextFile = file && (file.type || '').startsWith('image/') ? file : null
|
||||
thumbFile.value = nextFile
|
||||
clearPreviewUrl('thumb')
|
||||
if (thumbFile.value) thumbPreviewUrl.value = URL.createObjectURL(thumbFile.value)
|
||||
}
|
||||
|
||||
function onThumb(event) {
|
||||
handleThumbFile(event.target.files && event.target.files[0] ? event.target.files[0] : null)
|
||||
}
|
||||
|
||||
function openThumbFilePicker() {
|
||||
thumbFileInput.value?.click()
|
||||
}
|
||||
|
||||
function onThumbDragEnter(event) {
|
||||
event.preventDefault()
|
||||
isThumbDragOver.value = true
|
||||
}
|
||||
|
||||
function onThumbDragOver(event) {
|
||||
event.preventDefault()
|
||||
isThumbDragOver.value = true
|
||||
}
|
||||
|
||||
function onThumbDragLeave(event) {
|
||||
if (event.currentTarget === event.target) isThumbDragOver.value = false
|
||||
}
|
||||
|
||||
function onThumbDrop(event) {
|
||||
event.preventDefault()
|
||||
isThumbDragOver.value = false
|
||||
handleThumbFile(event.dataTransfer?.files?.[0] || null)
|
||||
}
|
||||
|
||||
function onFile(event) {
|
||||
handleItemFiles(event.target.files)
|
||||
}
|
||||
@@ -1122,36 +1209,34 @@ async function saveFeaturedOrder() {
|
||||
<div class="panel">
|
||||
<div class="sectionHeader">
|
||||
<div>
|
||||
<div class="panel__title">게임 선택 및 생성</div>
|
||||
<div class="hint hint--tight">우측 사이드가 아니라 이 화면 안에서 게임을 만들고, 기존 게임을 선택해 바로 상세 관리로 이어집니다.</div>
|
||||
<div class="panel__title">게임 관리</div>
|
||||
<div class="hint hint--tight">등록된 게임을 선택하면 아래에서 썸네일과 기본 아이템을 바로 수정할 수 있어요.</div>
|
||||
</div>
|
||||
<button class="btn btn--ghost" @click="refreshGames">게임 목록 새로고침</button>
|
||||
<button class="btn btn--primary" @click="openGameCreateModal">새 게임 생성</button>
|
||||
</div>
|
||||
|
||||
<div class="gameManagerGrid">
|
||||
<section class="adminCard">
|
||||
<div class="section__title">등록된 게임 선택</div>
|
||||
<div class="gameManagerCard__body">
|
||||
<select v-model="selectedGameId" class="select" @change="loadGame">
|
||||
<select :value="selectedGameId" class="select" @change="handleSelectedGameChange">
|
||||
<option value="">게임을 선택해주세요</option>
|
||||
<option v-for="game in games" :key="game.id" :value="game.id">{{ game.name }} ({{ game.id }})</option>
|
||||
</select>
|
||||
<div class="hint hint--tight">선택하면 아래 상세 영역에서 썸네일과 기본 아이템을 바로 수정할 수 있어요.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="adminCard">
|
||||
<div class="section__title">새 게임 만들기</div>
|
||||
<div class="gameManagerCard__body">
|
||||
<input v-model="newGameId" class="input" placeholder="game id (영문/숫자)" />
|
||||
<input v-model="newGameName" class="input" placeholder="게임 이름" />
|
||||
<button class="btn btn--primary" @click="createGame">게임 생성</button>
|
||||
<div v-if="selectedGameId && !hasSelectedGame && !isGameLoading" class="hint hint--tight">선택된 게임 ID: {{ selectedGameId }}</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="hasSelectedGame" class="panel">
|
||||
<div v-if="isGameLoading" class="panel panel--empty">
|
||||
<div class="emptyState">
|
||||
<div class="emptyState__title">게임 정보를 불러오는 중이에요.</div>
|
||||
<div class="emptyState__desc">선택한 게임의 썸네일과 기본 아이템을 곧 표시합니다.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="hasSelectedGame" class="panel">
|
||||
<div class="detailHead">
|
||||
<div>
|
||||
<div class="panel__title">선택된 게임 정보</div>
|
||||
@@ -1166,12 +1251,25 @@ async function saveFeaturedOrder() {
|
||||
<div class="section section--topGrid">
|
||||
<section class="adminCard">
|
||||
<div class="section__title">썸네일 적용</div>
|
||||
<div class="uploadPreviewCard">
|
||||
<input ref="thumbFileInput" type="file" accept="image/*" class="srOnlyInput" @change="onThumb" />
|
||||
<button
|
||||
class="thumbDropZone"
|
||||
:class="{ 'thumbDropZone--active': isThumbDragOver }"
|
||||
type="button"
|
||||
@click="openThumbFilePicker"
|
||||
@dragenter="onThumbDragEnter"
|
||||
@dragover="onThumbDragOver"
|
||||
@dragleave="onThumbDragLeave"
|
||||
@drop="onThumbDrop"
|
||||
>
|
||||
<img v-if="displayThumbnailUrl" class="selectedThumb" :src="displayThumbnailUrl" :alt="selectedGame.game.name" />
|
||||
<div v-else class="selectedThumb selectedThumb--empty">썸네일 미리보기</div>
|
||||
</div>
|
||||
<div v-else class="selectedThumb selectedThumb--empty">대표 썸네일</div>
|
||||
<div class="thumbDropZone__copy">
|
||||
<div class="thumbDropZone__title">클릭하거나 드래그해서 썸네일 추가</div>
|
||||
<div class="thumbDropZone__desc">다른 업로드 화면처럼 이미지 한 장을 바로 지정할 수 있어요.</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="uploadControls">
|
||||
<input ref="thumbFileInput" type="file" accept="image/*" class="inputFile" @change="onThumb" />
|
||||
<button class="btn" :disabled="!canApplyThumbnail" @click="uploadThumbnail">썸네일 적용</button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -1242,6 +1340,7 @@ async function saveFeaturedOrder() {
|
||||
<div class="emptyState__desc">
|
||||
위에서 기존 게임을 선택하거나 새 게임을 만든 뒤, 같은 화면에서 바로 썸네일과 기본 아이템을 정리할 수 있어요.
|
||||
</div>
|
||||
<div v-if="selectedGameId" class="hint hint--tight">선택한 게임을 찾지 못했거나 로딩 중 오류가 발생했어요. 다시 선택해보세요.</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1253,11 +1352,25 @@ async function saveFeaturedOrder() {
|
||||
<article v-for="item in customItems" :key="item.id" class="customItemCard">
|
||||
<img class="customItemCard__image" :src="toApiUrl(item.src)" :alt="item.label" />
|
||||
<div class="customItemCard__body">
|
||||
<div class="customItemCard__title">{{ item.label }}</div>
|
||||
<div class="customItemCard__meta">파일: {{ item.src.split('/').pop() }}</div>
|
||||
<div class="customItemCard__meta">업로더: {{ item.ownerName }}</div>
|
||||
<div class="customItemCard__meta">사용 중: {{ item.usageCount }}개 티어표</div>
|
||||
<div class="customItemCard__meta">{{ fmt(item.createdAt) }}</div>
|
||||
<div class="customItemCard__title" :title="item.label">{{ item.label }}</div>
|
||||
<div class="customItemCard__metaList">
|
||||
<div class="customItemCard__metaRow">
|
||||
<span>파일</span>
|
||||
<strong :title="item.src.split('/').pop()">{{ item.src.split('/').pop() }}</strong>
|
||||
</div>
|
||||
<div class="customItemCard__metaRow">
|
||||
<span>업로더</span>
|
||||
<strong :title="item.ownerName">{{ item.ownerName }}</strong>
|
||||
</div>
|
||||
<div class="customItemCard__metaRow">
|
||||
<span>사용 중</span>
|
||||
<strong>{{ item.usageCount }}개 티어표</strong>
|
||||
</div>
|
||||
<div class="customItemCard__metaRow">
|
||||
<span>등록일</span>
|
||||
<strong>{{ fmt(item.createdAt) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="customItemCard__actions">
|
||||
<a class="btn btn--small btn--ghost" :href="toApiUrl(item.src)" :download="item.label">이미지 다운로드</a>
|
||||
<button class="btn btn--small btn--ghost" :disabled="!customItemTargetGameId || item.isPromoting" @click="promoteCustomItem(item)">
|
||||
@@ -1462,6 +1575,21 @@ async function saveFeaturedOrder() {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="gameCreateModalOpen" class="modalOverlay" @click.self="closeGameCreateModal">
|
||||
<div class="modalCard" role="dialog" aria-modal="true">
|
||||
<div class="modalCard__title">새 게임 만들기</div>
|
||||
<div class="modalCard__desc">게임 이름과 고유 ID를 입력한 뒤 생성하면 바로 아래 상세 관리 화면으로 이어집니다.</div>
|
||||
<div class="modalCard__form">
|
||||
<input v-model="newGameName" class="input" placeholder="게임 이름" />
|
||||
<input v-model="newGameId" class="input" placeholder="game id (영문/숫자)" @keydown.enter.prevent="createGame" />
|
||||
</div>
|
||||
<div class="modalCard__actions">
|
||||
<button class="btn btn--ghost" @click="closeGameCreateModal">취소</button>
|
||||
<button class="btn btn--primary" :disabled="!newGameId.trim() || !newGameName.trim()" @click="createGame">게임 생성</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="userPasswordModalOpen" class="modalOverlay" @click.self="closeUserPasswordModal">
|
||||
<div class="modalCard" role="dialog" aria-modal="true">
|
||||
<div class="modalCard__title">비밀번호 초기화</div>
|
||||
@@ -1576,47 +1704,7 @@ async function saveFeaturedOrder() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="activeTab === 'featured'" class="adminSidebar__panel">
|
||||
<div class="adminSidebar__label">Featured</div>
|
||||
<div class="adminSidebar__actions">
|
||||
<button class="btn btn--ghost" @click="refreshGames">목록 새로고침</button>
|
||||
<button class="btn btn--primary" @click="saveFeaturedOrder">순서 저장</button>
|
||||
</div>
|
||||
<div class="adminSidebar__stats">
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">상단 고정</span>
|
||||
<strong class="sidebarStat__value">{{ featuredGameIds.length }}/50</strong>
|
||||
</div>
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">추가 가능</span>
|
||||
<strong class="sidebarStat__value">{{ Math.max(0, 50 - featuredGameIds.length) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeTab === 'game-admin'" class="adminSidebar__panel">
|
||||
<div class="adminSidebar__label">Game Summary</div>
|
||||
<div class="adminSidebar__actions">
|
||||
<button class="btn btn--ghost" @click="refreshGames">게임 목록 새로고침</button>
|
||||
<button v-if="hasSelectedGame" class="btn btn--ghost" @click="loadGame">선택 게임 다시 불러오기</button>
|
||||
</div>
|
||||
<div class="adminSidebar__stats">
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">전체 게임</span>
|
||||
<strong class="sidebarStat__value">{{ games.length }}</strong>
|
||||
</div>
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">선택 상태</span>
|
||||
<strong class="sidebarStat__value">{{ hasSelectedGame ? '활성' : '대기' }}</strong>
|
||||
</div>
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">기본 아이템</span>
|
||||
<strong class="sidebarStat__value">{{ selectedGame?.items?.length || 0 }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeTab === 'items'" class="adminSidebar__panel">
|
||||
<section v-if="activeTab === 'items'" class="adminSidebar__panel">
|
||||
<div class="adminSidebar__label">Filters</div>
|
||||
<div class="adminSidebar__group">
|
||||
<input v-model="customItemQuery" class="input" placeholder="파일명, 라벨, 업로더 검색" @keydown.enter.prevent="submitCustomItemSearch" />
|
||||
@@ -1662,17 +1750,7 @@ async function saveFeaturedOrder() {
|
||||
전체 티어표 관리
|
||||
</button>
|
||||
</div>
|
||||
<template v-if="tierlistsMode === 'requests'">
|
||||
<div class="adminSidebar__actions">
|
||||
<button class="btn btn--ghost" @click="refreshTemplateRequests">요청 새로고침</button>
|
||||
</div>
|
||||
<div class="adminSidebar__stats">
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">대기 요청</span>
|
||||
<strong class="sidebarStat__value">{{ templateRequests.length }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="tierlistsMode === 'requests'"></template>
|
||||
<template v-else>
|
||||
<div class="adminSidebar__group">
|
||||
<input
|
||||
@@ -1703,22 +1781,6 @@ async function saveFeaturedOrder() {
|
||||
</template>
|
||||
</section>
|
||||
|
||||
<section v-else class="adminSidebar__panel">
|
||||
<div class="adminSidebar__label">Users</div>
|
||||
<div class="adminSidebar__actions">
|
||||
<button class="btn btn--ghost" @click="refreshUsers">회원 새로고침</button>
|
||||
</div>
|
||||
<div class="adminSidebar__stats">
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">가입 회원</span>
|
||||
<strong class="sidebarStat__value">{{ users.length }}</strong>
|
||||
</div>
|
||||
<div class="sidebarStat">
|
||||
<span class="sidebarStat__label">관리자 수</span>
|
||||
<strong class="sidebarStat__value">{{ users.filter((user) => user.isAdmin).length }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</Teleport>
|
||||
</template>
|
||||
@@ -2229,6 +2291,35 @@ async function saveFeaturedOrder() {
|
||||
place-items: center;
|
||||
color: rgba(255, 255, 255, 0.62);
|
||||
}
|
||||
.thumbDropZone {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
justify-items: start;
|
||||
padding: 16px;
|
||||
border-radius: 18px;
|
||||
border: 1px dashed rgba(255, 255, 255, 0.18);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
text-align: left;
|
||||
transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease;
|
||||
}
|
||||
.thumbDropZone--active {
|
||||
border-color: rgba(96, 165, 250, 0.56);
|
||||
background: rgba(96, 165, 250, 0.08);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.thumbDropZone__copy {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
.thumbDropZone__title {
|
||||
font-weight: 900;
|
||||
}
|
||||
.thumbDropZone__desc {
|
||||
color: rgba(255, 255, 255, 0.68);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.itemComposer {
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
@@ -2346,54 +2437,74 @@ async function saveFeaturedOrder() {
|
||||
.customItemGrid {
|
||||
margin-top: 14px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 280px));
|
||||
gap: 12px;
|
||||
justify-content: start;
|
||||
}
|
||||
.customItemCard {
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
gap: 14px;
|
||||
padding: 14px;
|
||||
min-width: 0;
|
||||
}
|
||||
.customItemCard__image {
|
||||
width: clamp(88px, 22vw, 150px);
|
||||
flex: 0 1 150px;
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 12px;
|
||||
border-radius: 14px;
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
.customItemCard__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
align-content: start;
|
||||
}
|
||||
.customItemCard__actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 4px;
|
||||
gap: 8px;
|
||||
margin-top: auto;
|
||||
}
|
||||
.customItemCard__actions > * {
|
||||
width: 100%;
|
||||
}
|
||||
.customItemCard__title {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-word;
|
||||
font-weight: 900;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.customItemCard__meta {
|
||||
opacity: 0.72;
|
||||
font-size: 13px;
|
||||
.customItemCard__metaList {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.customItemCard__metaRow {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
.customItemCard__metaRow span {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.46);
|
||||
}
|
||||
.customItemCard__metaRow strong {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
}
|
||||
.pager {
|
||||
margin-top: 16px;
|
||||
|
||||
Reference in New Issue
Block a user