Files
tier-maker/frontend/src/components/admin/AdminTierlistsSection.vue

194 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)"
@click.prevent="props.templateRequestSourceUrl(request) && window.open(props.templateRequestSourceUrl(request), '_blank', 'noopener,noreferrer')"
>
<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.draftGameName" class="input" placeholder="새 게임 이름" />
</label>
<label class="templateRequestField">
<span class="templateRequestField__label">게임 ID</span>
<input v-model="request.draftGameId" class="input" placeholder="임시 게임 ID" />
</label>
</template>
<template v-else>
<div class="templateRequestCard__thumbLabel">게임 이름</div>
<div class="templateRequestCard__thumbValue">{{ request.draftGameName || request.sourceGameName || '-' }}</div>
<div class="templateRequestCard__thumbLabel">게임 ID</div>
<div class="templateRequestCard__thumbValue">{{ request.draftGameId || request.sourceGameId || '-' }}</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.targetGameName || request.targetGameId)" class="pill pill--soft">
연결됨 · {{ request.targetGameName || request.targetGameId }}
</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.targetGameName || request.targetGameId)
? '연결된 게임 열기'
: '확인하기'
}}
</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.gameName || tierList.gameId }} · {{ 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.gameId === '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>