193 lines
10 KiB
Vue
193 lines
10 KiB
Vue
<script setup>
|
|
import { toApiUrl } from '../../lib/runtime'
|
|
|
|
const props = defineProps({
|
|
tierlistsMode: { type: String, required: true },
|
|
templateRequests: { type: Array, required: true },
|
|
openTemplateRequestPreview: { type: Function, required: true },
|
|
fmt: { type: Function, required: true },
|
|
templateRequestTargetLabel: { type: Function, required: true },
|
|
templateRequestStatusLabel: { type: Function, required: true },
|
|
templateRequestSourceUrl: { type: Function, required: true },
|
|
startTemplateRequestReview: { type: Function, required: true },
|
|
completeTemplateRequest: { type: Function, required: true },
|
|
adminTierLists: { type: Array, required: true },
|
|
tierListThumbUrl: { type: Function, required: true },
|
|
openAdminTierList: { type: Function, required: true },
|
|
tierListAuthorDisplayName: { type: Function, required: true },
|
|
tierListVisibilityLabel: { type: Function, required: true },
|
|
openTierListExtraItemModal: { type: Function, required: true },
|
|
openTierListImportModal: { type: Function, required: true },
|
|
adminTierListPage: { type: Number, required: true },
|
|
adminTierListPageCount: { type: Number, required: true },
|
|
adminTierListTotal: { type: Number, required: true },
|
|
adminTierListStats: { type: Object, required: true },
|
|
openAdminTierListManageModal: { type: Function, required: true },
|
|
moveAdminTierListPage: { type: Function, required: true },
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="props.tierlistsMode === 'requests'" class="panel">
|
|
<div class="sectionHeader">
|
|
<div>
|
|
<div class="panel__title">사용자 요청</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="!props.templateRequests.length" class="hint">현재 처리 대기 중인 템플릿 요청이 없어요.</div>
|
|
<div v-else class="templateRequestList">
|
|
<article v-for="request in props.templateRequests" :key="request.id" class="tierAdminCard templateRequestCard templateRequestCard--aligned">
|
|
<div class="templateRequestCard__side">
|
|
<a
|
|
class="tierAdminCard__preview templateRequestCard__preview"
|
|
:href="props.templateRequestSourceUrl(request) || undefined"
|
|
:target="props.templateRequestSourceUrl(request) ? '_blank' : undefined"
|
|
:rel="props.templateRequestSourceUrl(request) ? 'noreferrer' : undefined"
|
|
:aria-disabled="!props.templateRequestSourceUrl(request)"
|
|
>
|
|
<img v-if="request.thumbnailSrc" class="tierAdminCard__thumb" :src="toApiUrl(request.thumbnailSrc)" :alt="request.sourceTierListTitle" draggable="false" />
|
|
<div v-else class="tierAdminCard__thumb tierAdminCard__thumb--empty"></div>
|
|
</a>
|
|
<div class="templateRequestCard__thumbMeta">
|
|
<template v-if="request.type === 'create'">
|
|
<label class="templateRequestField">
|
|
<span class="templateRequestField__label">템플릿 이름</span>
|
|
<input v-model="request.draftTopicName" class="input" placeholder="새 템플릿 이름" />
|
|
</label>
|
|
<label class="templateRequestField">
|
|
<span class="templateRequestField__label">템플릿 ID</span>
|
|
<input v-model="request.draftTopicId" class="input" placeholder="임시 템플릿 ID" />
|
|
</label>
|
|
</template>
|
|
<template v-else>
|
|
<div class="templateRequestCard__thumbLabel">템플릿 이름</div>
|
|
<div class="templateRequestCard__thumbValue">{{ request.draftTopicName || request.sourceTopicName || '-' }}</div>
|
|
<div class="templateRequestCard__thumbLabel">템플릿 ID</div>
|
|
<div class="templateRequestCard__thumbValue">{{ request.draftTopicId || request.sourceTopicId || '-' }}</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tierAdminCard__body">
|
|
<div class="tierAdminCard__head">
|
|
<div>
|
|
<span
|
|
class="pill templateRequestCard__cornerBadge"
|
|
:class="request.type === 'create' ? 'pill--create' : 'pill--owned'"
|
|
>
|
|
{{ request.type === 'create' ? '신규 템플릿' : '보유 템플릿' }}
|
|
</span>
|
|
<div class="tierAdminCard__title">{{ request.sourceTierListTitle }}</div>
|
|
<div v-if="request.sourceDescription" class="tierAdminCard__desc">{{ request.sourceDescription }}</div>
|
|
<div class="tierAdminCard__meta">
|
|
{{ request.requesterName }} · {{ props.fmt(request.createdAt) }}
|
|
</div>
|
|
<div class="tierAdminCard__meta">{{ props.templateRequestTargetLabel(request) }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tierAdminCard__stats">
|
|
<span class="pill">추가 아이템 {{ request.items?.length || 0 }}개</span>
|
|
<span v-if="request.type === 'create' && (request.targetTopicName || request.targetTopicId)" class="pill pill--soft">
|
|
연결됨 · {{ request.targetTopicName || request.targetTopicId }}
|
|
</span>
|
|
<span class="pill" :class="{ 'pill--accent': request.status === 'reviewing' }">{{ props.templateRequestStatusLabel(request) }}</span>
|
|
</div>
|
|
|
|
<div v-if="request.items?.length" class="tierAdminItemList templateRequestCard__items">
|
|
<button v-for="item in request.items" :key="item.id" class="tierAdminItem" type="button">
|
|
<img class="tierAdminItem__thumb" :src="toApiUrl(item.src)" :alt="item.label" />
|
|
<div class="tierAdminItem__title">{{ item.label }}</div>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="templateRequestCard__footer">
|
|
<div class="templateRequestCard__footerLeft"></div>
|
|
<div class="templateRequestCard__actions">
|
|
<button class="btn btn--primary" :disabled="request.isHandling" @click="props.startTemplateRequestReview(request)">
|
|
{{
|
|
request.isHandling
|
|
? '이동중...'
|
|
: request.type === 'create' && (request.targetTopicName || request.targetTopicId)
|
|
? '연결된 템플릿 열기'
|
|
: '확인하기'
|
|
}}
|
|
</button>
|
|
<button class="btn btn--ghost" :disabled="request.isHandling || request.status !== 'reviewing'" @click="props.completeTemplateRequest(request)">처리 완료</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else class="panel">
|
|
<div class="sectionHeader">
|
|
<div>
|
|
<div class="panel__title">전체 티어표 관리</div>
|
|
<div class="tierAdminHeaderStats">
|
|
<span class="pill">전체 {{ props.adminTierListStats.total || 0 }}개</span>
|
|
<span class="pill pill--soft">공개 {{ props.adminTierListStats.publicCount || 0 }}개</span>
|
|
<span class="pill pill--soft">비공개 {{ props.adminTierListStats.privateCount || 0 }}개</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="!props.adminTierLists.length" class="hint">조건에 맞는 티어표가 없어요.</div>
|
|
<div v-else class="tierAdminList">
|
|
<article v-for="tierList in props.adminTierLists" :key="tierList.id" class="tierAdminCard">
|
|
<button class="tierAdminCard__preview" type="button" @click="props.openAdminTierList(tierList)">
|
|
<img v-if="props.tierListThumbUrl(tierList)" class="tierAdminCard__thumb" :src="props.tierListThumbUrl(tierList)" :alt="tierList.title" draggable="false" />
|
|
<div v-else class="tierAdminCard__thumb tierAdminCard__thumb--empty"></div>
|
|
</button>
|
|
|
|
<div class="tierAdminCard__body">
|
|
<div class="tierAdminCard__head">
|
|
<div>
|
|
<div class="tierAdminCard__title">{{ tierList.title }}</div>
|
|
<div v-if="tierList.description" class="tierAdminCard__desc">{{ tierList.description }}</div>
|
|
<div class="tierAdminCard__meta">
|
|
{{ tierList.topicName || tierList.topicId }} · {{ props.tierListAuthorDisplayName(tierList) }}
|
|
</div>
|
|
<div class="tierAdminCard__meta">{{ props.fmt(tierList.updatedAt) }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tierAdminCard__stats">
|
|
<span class="pill" :class="tierList.isPublic ? 'pill--public' : 'pill--private'">{{ props.tierListVisibilityLabel(tierList) }}</span>
|
|
<span class="pill">전체 아이템 {{ tierList.itemCount }}개</span>
|
|
<span class="pill" :class="{ 'pill--accent': tierList.extraItemCount > 0 }">추가 아이템 {{ tierList.extraItemCount }}개</span>
|
|
</div>
|
|
|
|
<div v-if="tierList.extraItems?.length" class="tierAdminSection">
|
|
<div class="tierAdminSection__title">추가로 넣은 아이템</div>
|
|
<div class="tierAdminItemList">
|
|
<button v-for="item in tierList.extraItems" :key="item.id" class="tierAdminItem" @click="props.openTierListExtraItemModal(item, tierList)">
|
|
<img class="tierAdminItem__thumb" :src="toApiUrl(item.src)" :alt="item.label" />
|
|
<div class="tierAdminItem__title">{{ item.label }}</div>
|
|
</button>
|
|
</div>
|
|
<div class="tierAdminSection__actions">
|
|
<button class="btn btn--ghost btn--small" @click="props.openTierListImportModal(tierList, tierList.extraItems)">추가 아이템 전체 가져오기</button>
|
|
<button v-if="tierList.topicId === 'freeform'" class="btn btn--primary btn--small" @click="props.openTierListImportModal(tierList, tierList.extraItems)">
|
|
새 템플릿으로 가져오기
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tierAdminSection__actions">
|
|
<button class="btn btn--ghost btn--small" @click="props.openAdminTierListManageModal(tierList)">관리</button>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
|
|
<div class="pager">
|
|
<button class="btn btn--ghost" :disabled="props.adminTierListPage <= 1" @click="props.moveAdminTierListPage(-1)">이전</button>
|
|
<div class="pager__info">{{ props.adminTierListPage }} / {{ props.adminTierListPageCount }} 페이지 · 총 {{ props.adminTierListTotal }}개</div>
|
|
<button class="btn btn--ghost" :disabled="props.adminTierListPage >= props.adminTierListPageCount" @click="props.moveAdminTierListPage(1)">다음</button>
|
|
</div>
|
|
</div>
|
|
</template>
|