릴리스: v1.3.73 관리자 게임 선택 UX 정리

This commit is contained in:
2026-04-02 15:50:20 +09:00
parent 5aae278fd3
commit 494f04d9a7
6 changed files with 49 additions and 95 deletions

View File

@@ -46,8 +46,6 @@ const customItemLimit = ref(50)
const customItemTotal = ref(0)
const customItemFilter = ref('all')
const customItemModalTargetGameId = ref('')
const customItemModalGameQuery = ref('')
const customItemModalGameSort = ref('recent')
const adminTierLists = ref([])
const adminTierListQuery = ref('')
@@ -212,6 +210,7 @@ const filteredGamePickerGames = computed(() => {
return Number(b.createdAt || 0) - Number(a.createdAt || 0)
})
})
const customItemTargetGame = computed(() => games.value.find((game) => game.id === customItemModalTargetGameId.value) || null)
const importModalItemCount = computed(() => importModalItems.value.length)
const activeTabTitle = computed(() => {
if (activeTab.value === 'featured') return '목록 관리'
@@ -490,7 +489,6 @@ watch(
customItemQuery.value = ''
customItemFilter.value = 'all'
customItemPage.value = 1
customItemModalGameQuery.value = ''
await refreshCustomItems()
return
}
@@ -622,21 +620,6 @@ const imageDiagnosticsCards = computed(() => {
const visibleLinkedGames = computed(() =>
(modalTargetCustomItem.value?.linkedGames || []).filter((game) => game?.id && game.id !== 'freeform')
)
const filteredCustomItemModalGames = computed(() => {
const query = customItemModalGameQuery.value.trim().toLowerCase()
const linkedIds = new Set(visibleLinkedGames.value.map((game) => game.id))
const list = games.value.filter((game) => {
if (!query) return true
return `${game.name || ''} ${game.id || ''}`.toLowerCase().includes(query)
})
return list.slice().sort((a, b) => {
const linkedDelta = Number(linkedIds.has(a.id)) - Number(linkedIds.has(b.id))
if (linkedDelta !== 0) return linkedDelta
if (customItemModalGameSort.value === 'oldest') return Number(a.createdAt || 0) - Number(b.createdAt || 0)
return Number(b.createdAt || 0) - Number(a.createdAt || 0)
})
})
const imageStatsPeriodLabel = computed(() => (imageStatsMonth.value ? `${imageStatsMonth.value} 기준` : '전체 기간'))
const imageStatsYearOptions = computed(() => {
@@ -1040,8 +1023,6 @@ const {
customItemModalDraftLabel,
customItemModalLabelSaving,
customItemModalTargetGameId,
customItemModalGameQuery,
customItemModalGameSort,
games,
selectedGameId,
refreshCustomItems,
@@ -1327,6 +1308,11 @@ async function chooseGameFromPicker(gameId) {
closeGamePickerModal()
return
}
if (gamePickerMode.value === 'custom-item-target') {
customItemModalTargetGameId.value = gameId
closeGamePickerModal()
return
}
await selectAdminGame(gameId)
closeGamePickerModal()
@@ -1963,32 +1949,16 @@ function userAvatarFallback(user) {
<div class="customItemModal__pickerHead">
<div class="customItemModal__pickerEyebrow">GAME PICKER</div>
<div class="customItemModal__pickerTitle">템플릿으로 추가할 게임</div>
</div>
<div class="adminSelectionCard">
<div class="adminSelectionCard__label">선택한 게임</div>
<div class="adminSelectionCard__title">{{ customItemTargetGame?.name || '아직 선택하지 않음' }}</div>
<div class="adminSelectionCard__meta">{{ customItemTargetGame?.id || '게임을 골라 주세요.' }}</div>
</div>
<div class="customItemModal__pickerActions">
<button class="btn btn--ghost" type="button" @click="openGamePickerModal('custom-item-target')">게임 선택</button>
<button class="btn btn--ghost btn--small customItemModal__createGameButton" type="button" @click="openGameCreateModal"> 템플릿 만들기</button>
</div>
<div class="customItemModal__pickerControls">
<input v-model="customItemModalGameQuery" class="input input--dense" placeholder="게임 이름, ID 검색" />
<select v-model="customItemModalGameSort" class="select">
<option value="recent">최신순</option>
<option value="oldest">오래된순</option>
</select>
</div>
<div class="customItemModal__gameList">
<button
v-for="game in filteredCustomItemModalGames"
:key="game.id"
type="button"
class="customItemModal__gameItem"
:class="{
'customItemModal__gameItem--active': customItemModalTargetGameId === game.id,
'customItemModal__gameItem--linked': visibleLinkedGames.some((entry) => entry.id === game.id),
}"
@click="customItemModalTargetGameId = game.id"
>
<span class="customItemModal__gameName">{{ game.name }}</span>
<span class="customItemModal__gameMeta">{{ game.id }}</span>
<span v-if="visibleLinkedGames.some((entry) => entry.id === game.id)" class="customItemModal__gameState">이미 포함됨</span>
</button>
</div>
</aside>
<div class="customItemModal__body">
<button class="customItemModal__close" type="button" @click="closeCustomItemModal">닫기</button>
@@ -2244,8 +2214,10 @@ function userAvatarFallback(user) {
<section v-else-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" />
<button class="btn btn--ghost" @click="submitCustomItemSearch">검색</button>
<div class="adminSidebar__inlineRow">
<input v-model="customItemQuery" class="input" placeholder="파일명, 라벨, 업로더 검색" @keydown.enter.prevent="submitCustomItemSearch" />
<button class="btn btn--ghost" @click="submitCustomItemSearch">검색</button>
</div>
</div>
<div class="adminSidebar__group">
<select :value="customItemLimit" class="select" @change="changeCustomItemLimit(Number($event.target.value))">
@@ -2290,13 +2262,15 @@ function userAvatarFallback(user) {
<template v-if="tierlistsMode === 'requests'"></template>
<template v-else>
<div class="adminSidebar__group">
<input
v-model="adminTierListQuery"
class="input"
placeholder="제목, 작성자, 게임 이름 검색"
@keydown.enter.prevent="submitAdminTierListSearch"
/>
<button class="btn btn--ghost" @click="submitAdminTierListSearch">검색</button>
<div class="adminSidebar__inlineRow">
<input
v-model="adminTierListQuery"
class="input"
placeholder="제목, 작성자, 게임 이름 검색"
@keydown.enter.prevent="submitAdminTierListSearch"
/>
<button class="btn btn--ghost" @click="submitAdminTierListSearch">검색</button>
</div>
<button class="btn btn--ghost" @click="openGamePickerModal('tierlists-filter')">게임 선택</button>
<div v-if="adminTierListGameId" class="adminSelectionCard">
<div class="adminSelectionCard__label">필터된 게임</div>
@@ -2538,6 +2512,15 @@ function userAvatarFallback(user) {
display: grid;
gap: 10px;
}
.adminUiScope .adminSidebar__inlineRow {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 8px;
align-items: center;
}
.adminUiScope .adminSidebar__inlineRow .btn {
white-space: nowrap;
}
.adminUiScope .adminSidebar__group--monthPicker {
align-items: start;
}
@@ -3463,46 +3446,13 @@ function userAvatarFallback(user) {
font-size: 18px;
font-weight: 900;
}
.adminUiScope .customItemModal__pickerControls {
.adminUiScope .customItemModal__pickerActions {
display: grid;
gap: 10px;
}
.adminUiScope .customItemModal__gameList {
display: grid;
gap: 8px;
max-height: 440px;
overflow: auto;
}
.adminUiScope .customItemModal__createGameButton {
justify-self: start;
}
.adminUiScope .customItemModal__gameItem {
display: grid;
gap: 4px;
padding: 12px 13px;
border-radius: 16px;
border: 1px solid var(--theme-border);
background: var(--theme-surface-soft);
color: var(--theme-text);
text-align: left;
cursor: pointer;
}
.adminUiScope .customItemModal__gameItem--active {
border-color: rgba(96, 165, 250, 0.42);
background: rgba(96, 165, 250, 0.12);
}
.adminUiScope .customItemModal__gameItem--linked {
border-style: dashed;
}
.adminUiScope .customItemModal__gameName {
font-size: 13px;
font-weight: 800;
}
.adminUiScope .customItemModal__gameMeta,
.adminUiScope .customItemModal__gameState {
font-size: 11px;
color: var(--theme-text-soft);
}
.adminUiScope .customItemModal__body {
min-width: 0;
min-height: 0;