|
|
|
|
@@ -34,8 +34,10 @@ const games = ref([])
|
|
|
|
|
const selectedGameId = ref('')
|
|
|
|
|
const selectedGame = ref(null)
|
|
|
|
|
const featuredGameIds = ref([])
|
|
|
|
|
const gameAdminQuery = ref('')
|
|
|
|
|
const gameAdminSort = ref('recent')
|
|
|
|
|
const gamePickerModalOpen = ref(false)
|
|
|
|
|
const gamePickerMode = ref('game-admin')
|
|
|
|
|
const gamePickerQuery = ref('')
|
|
|
|
|
const gamePickerSort = ref('recent')
|
|
|
|
|
|
|
|
|
|
const customItems = ref([])
|
|
|
|
|
const customItemQuery = ref('')
|
|
|
|
|
@@ -44,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('')
|
|
|
|
|
@@ -197,8 +197,8 @@ const featuredGames = computed(() =>
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
)
|
|
|
|
|
const availableGamesForFeatured = computed(() => games.value.filter((game) => !featuredGameIds.value.includes(game.id)))
|
|
|
|
|
const filteredAdminGames = computed(() => {
|
|
|
|
|
const query = gameAdminQuery.value.trim().toLowerCase()
|
|
|
|
|
const filteredGamePickerGames = computed(() => {
|
|
|
|
|
const query = gamePickerQuery.value.trim().toLowerCase()
|
|
|
|
|
const list = games.value.filter((game) => {
|
|
|
|
|
if (!query) return true
|
|
|
|
|
const haystack = `${game.name || ''} ${game.id || ''}`.toLowerCase()
|
|
|
|
|
@@ -206,10 +206,11 @@ const filteredAdminGames = computed(() => {
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return list.slice().sort((a, b) => {
|
|
|
|
|
if (gameAdminSort.value === 'oldest') return Number(a.createdAt || 0) - Number(b.createdAt || 0)
|
|
|
|
|
if (gamePickerSort.value === 'oldest') return Number(a.createdAt || 0) - Number(b.createdAt || 0)
|
|
|
|
|
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 '목록 관리'
|
|
|
|
|
@@ -289,6 +290,7 @@ const adminOverviewStats = computed(() => {
|
|
|
|
|
const isAnyModalOpen = computed(
|
|
|
|
|
() =>
|
|
|
|
|
gameCreateModalOpen.value ||
|
|
|
|
|
gamePickerModalOpen.value ||
|
|
|
|
|
userEditModalOpen.value ||
|
|
|
|
|
userPasswordModalOpen.value ||
|
|
|
|
|
userDeleteModalOpen.value ||
|
|
|
|
|
@@ -424,7 +426,9 @@ watch(
|
|
|
|
|
const nextGameId = typeof route.query.gameId === 'string' ? route.query.gameId : ''
|
|
|
|
|
if (nextGameId && nextGameId !== selectedGameId.value) {
|
|
|
|
|
selectedGameId.value = nextGameId
|
|
|
|
|
loadGame()
|
|
|
|
|
queueMicrotask(() => {
|
|
|
|
|
if (selectedGameId.value === nextGameId) void loadGame()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
@@ -485,7 +489,6 @@ watch(
|
|
|
|
|
customItemQuery.value = ''
|
|
|
|
|
customItemFilter.value = 'all'
|
|
|
|
|
customItemPage.value = 1
|
|
|
|
|
customItemModalGameQuery.value = ''
|
|
|
|
|
await refreshCustomItems()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
@@ -617,21 +620,7 @@ 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 linkedCustomItemGameIds = computed(() => new Set(visibleLinkedGames.value.map((game) => game.id).filter(Boolean)))
|
|
|
|
|
|
|
|
|
|
const imageStatsPeriodLabel = computed(() => (imageStatsMonth.value ? `${imageStatsMonth.value} 기준` : '전체 기간'))
|
|
|
|
|
const imageStatsYearOptions = computed(() => {
|
|
|
|
|
@@ -1035,8 +1024,6 @@ const {
|
|
|
|
|
customItemModalDraftLabel,
|
|
|
|
|
customItemModalLabelSaving,
|
|
|
|
|
customItemModalTargetGameId,
|
|
|
|
|
customItemModalGameQuery,
|
|
|
|
|
customItemModalGameSort,
|
|
|
|
|
games,
|
|
|
|
|
selectedGameId,
|
|
|
|
|
refreshCustomItems,
|
|
|
|
|
@@ -1303,6 +1290,36 @@ function setAdminTierListGameId(gameId) {
|
|
|
|
|
refreshAdminTierLists()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openGamePickerModal(mode = 'game-admin') {
|
|
|
|
|
gamePickerMode.value = mode
|
|
|
|
|
gamePickerQuery.value = ''
|
|
|
|
|
gamePickerSort.value = 'recent'
|
|
|
|
|
gamePickerModalOpen.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeGamePickerModal() {
|
|
|
|
|
gamePickerModalOpen.value = false
|
|
|
|
|
gamePickerQuery.value = ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function chooseGameFromPicker(gameId) {
|
|
|
|
|
if (!gameId) return
|
|
|
|
|
if (gamePickerMode.value === 'tierlists-filter') {
|
|
|
|
|
setAdminTierListGameId(gameId)
|
|
|
|
|
closeGamePickerModal()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (gamePickerMode.value === 'custom-item-target') {
|
|
|
|
|
if (linkedCustomItemGameIds.value.has(gameId)) return
|
|
|
|
|
customItemModalTargetGameId.value = gameId
|
|
|
|
|
closeGamePickerModal()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await selectAdminGame(gameId)
|
|
|
|
|
closeGamePickerModal()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function changeAdminTierListLimit(limit) {
|
|
|
|
|
adminTierListLimit.value = limit
|
|
|
|
|
adminTierListPage.value = 1
|
|
|
|
|
@@ -1931,45 +1948,19 @@ function userAvatarFallback(user) {
|
|
|
|
|
<div class="modalCard modalCard--customItem" role="dialog" aria-modal="true">
|
|
|
|
|
<div v-if="modalTargetCustomItem" class="customItemModal">
|
|
|
|
|
<aside class="customItemModal__pickerPanel">
|
|
|
|
|
<div class="customItemModal__selected">
|
|
|
|
|
<img class="customItemModal__selectedImage" :src="toApiUrl(modalTargetCustomItem.src)" :alt="modalTargetCustomItem.label" />
|
|
|
|
|
<div class="customItemModal__selectedMeta">
|
|
|
|
|
<div class="customItemModal__selectedTitle">{{ modalTargetCustomItem.label }}</div>
|
|
|
|
|
<div class="customItemModal__selectedChips">
|
|
|
|
|
<span class="pill">{{ modalTargetCustomItem.sourceLabel }}</span>
|
|
|
|
|
<span class="pill" v-if="modalTargetCustomItem.ownerName">{{ modalTargetCustomItem.ownerName }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<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>
|
|
|
|
|
@@ -2015,6 +2006,58 @@ function userAvatarFallback(user) {
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="gamePickerModalOpen" class="modalOverlay" @click.self="closeGamePickerModal">
|
|
|
|
|
<div class="modalCard" role="dialog" aria-modal="true">
|
|
|
|
|
<div class="modalCard__titleRow">
|
|
|
|
|
<div>
|
|
|
|
|
<div class="modalCard__title">게임 선택</div>
|
|
|
|
|
<div class="modalCard__desc">
|
|
|
|
|
{{ gamePickerMode === 'tierlists-filter' ? '특정 게임의 티어표만 보려면 게임을 선택하세요.' : '관리할 게임을 검색해서 바로 열 수 있어요.' }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<button class="btn btn--ghost btn--small" @click="closeGamePickerModal">닫기</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modalCard__form">
|
|
|
|
|
<input v-model="gamePickerQuery" class="input" placeholder="게임 이름 또는 ID 검색" />
|
|
|
|
|
<select v-model="gamePickerSort" class="select">
|
|
|
|
|
<option value="recent">최신순</option>
|
|
|
|
|
<option value="oldest">오래된순</option>
|
|
|
|
|
</select>
|
|
|
|
|
<button
|
|
|
|
|
v-if="gamePickerMode === 'tierlists-filter' && adminTierListGameId"
|
|
|
|
|
class="btn btn--ghost"
|
|
|
|
|
type="button"
|
|
|
|
|
@click="setAdminTierListGameId(''); closeGamePickerModal()"
|
|
|
|
|
>
|
|
|
|
|
모든 게임 보기
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="gamePickerModalList">
|
|
|
|
|
<button
|
|
|
|
|
v-for="game in filteredGamePickerGames"
|
|
|
|
|
:key="game.id"
|
|
|
|
|
class="adminGamePicker__item"
|
|
|
|
|
:class="{
|
|
|
|
|
'adminGamePicker__item--active': gamePickerMode === 'tierlists-filter'
|
|
|
|
|
? adminTierListGameId === game.id
|
|
|
|
|
: gamePickerMode === 'custom-item-target'
|
|
|
|
|
? customItemModalTargetGameId === game.id
|
|
|
|
|
: selectedGameId === game.id,
|
|
|
|
|
'adminGamePicker__item--disabled': gamePickerMode === 'custom-item-target' && linkedCustomItemGameIds.has(game.id),
|
|
|
|
|
}"
|
|
|
|
|
type="button"
|
|
|
|
|
:disabled="gamePickerMode === 'custom-item-target' && linkedCustomItemGameIds.has(game.id)"
|
|
|
|
|
@click="chooseGameFromPicker(game.id)"
|
|
|
|
|
>
|
|
|
|
|
<span class="adminGamePicker__name">{{ game.name }}</span>
|
|
|
|
|
<span class="adminGamePicker__meta">{{ game.id }}</span>
|
|
|
|
|
<span v-if="gamePickerMode === 'custom-item-target' && linkedCustomItemGameIds.has(game.id)" class="adminGamePicker__state">이미 추가됨</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div v-if="!filteredGamePickerGames.length" class="hint hint--tight">검색 결과가 없어요.</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="customItemDeleteModalOpen" class="modalOverlay" @click.self="closeCustomItemDeleteModal">
|
|
|
|
|
<div class="modalCard" role="dialog" aria-modal="true">
|
|
|
|
|
<div class="modalCard__title">아이템 삭제</div>
|
|
|
|
|
@@ -2169,24 +2212,11 @@ function userAvatarFallback(user) {
|
|
|
|
|
<div class="adminSidebar__label">Game</div>
|
|
|
|
|
<div class="adminSidebar__group">
|
|
|
|
|
<button class="btn btn--primary" @click="openGameCreateModal">새 게임 생성</button>
|
|
|
|
|
<input v-model="gameAdminQuery" class="input" placeholder="게임 이름 또는 ID 검색" />
|
|
|
|
|
<select v-model="gameAdminSort" class="select">
|
|
|
|
|
<option value="recent">최신순</option>
|
|
|
|
|
<option value="oldest">오래된순</option>
|
|
|
|
|
</select>
|
|
|
|
|
<div class="adminGamePicker">
|
|
|
|
|
<button
|
|
|
|
|
v-for="game in filteredAdminGames"
|
|
|
|
|
:key="game.id"
|
|
|
|
|
class="adminGamePicker__item"
|
|
|
|
|
:class="{ 'adminGamePicker__item--active': selectedGameId === game.id }"
|
|
|
|
|
type="button"
|
|
|
|
|
@click="selectAdminGame(game.id)"
|
|
|
|
|
>
|
|
|
|
|
<span class="adminGamePicker__name">{{ game.name }}</span>
|
|
|
|
|
<span class="adminGamePicker__meta">{{ game.id }}</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div v-if="!filteredAdminGames.length" class="hint hint--tight">검색 결과가 없어요.</div>
|
|
|
|
|
<button class="btn btn--ghost" @click="openGamePickerModal('game-admin')">게임 선택</button>
|
|
|
|
|
<div v-if="selectedGame?.game" class="adminSelectionCard">
|
|
|
|
|
<div class="adminSelectionCard__label">선택한 게임</div>
|
|
|
|
|
<div class="adminSelectionCard__title">{{ selectedGame.game.name }}</div>
|
|
|
|
|
<div class="adminSelectionCard__meta">{{ selectedGame.game.id }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="selectedGameId && !hasSelectedGame && !isGameLoading" class="hint hint--tight">선택된 게임 ID: {{ selectedGameId }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
@@ -2195,8 +2225,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))">
|
|
|
|
|
@@ -2241,17 +2273,22 @@ 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>
|
|
|
|
|
<select :value="adminTierListGameId" class="select" @change="setAdminTierListGameId($event.target.value)">
|
|
|
|
|
<option value="">모든 게임</option>
|
|
|
|
|
<option v-for="game in games" :key="game.id" :value="game.id">{{ game.name }}</option>
|
|
|
|
|
</select>
|
|
|
|
|
<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>
|
|
|
|
|
<div class="adminSelectionCard__title">{{ games.find((game) => game.id === adminTierListGameId)?.name || adminTierListGameId }}</div>
|
|
|
|
|
<div class="adminSelectionCard__meta">{{ adminTierListGameId }}</div>
|
|
|
|
|
<button class="btn btn--ghost btn--small" @click="setAdminTierListGameId('')">필터 해제</button>
|
|
|
|
|
</div>
|
|
|
|
|
<select :value="adminTierListLimit" class="select" @change="changeAdminTierListLimit(Number($event.target.value))">
|
|
|
|
|
<option :value="50">50개씩 보기</option>
|
|
|
|
|
<option :value="200">200개씩 보기</option>
|
|
|
|
|
@@ -2352,6 +2389,7 @@ function userAvatarFallback(user) {
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</aside>
|
|
|
|
|
<div v-show="globalRightRailOpen" class="adminSidebarFooter adminUiScope">Copyright © 2026 zenn. All rights reserved.</div>
|
|
|
|
|
</Teleport>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
@@ -2430,6 +2468,15 @@ function userAvatarFallback(user) {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope.adminSidebarFooter {
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
padding: 0 4px 2px;
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: var(--theme-text-faint);
|
|
|
|
|
opacity: 0.72;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminSidebar__panel {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
@@ -2486,6 +2533,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;
|
|
|
|
|
}
|
|
|
|
|
@@ -2537,6 +2593,11 @@ function userAvatarFallback(user) {
|
|
|
|
|
border-color: rgba(77, 127, 233, 0.58);
|
|
|
|
|
background: rgba(77, 127, 233, 0.12);
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminGamePicker__item--disabled {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
opacity: 0.58;
|
|
|
|
|
border-style: dashed;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminGamePicker__name {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
@@ -2548,6 +2609,39 @@ function userAvatarFallback(user) {
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminGamePicker__state {
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: var(--theme-text-faint);
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .gamePickerModalList {
|
|
|
|
|
margin-top: 14px;
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
max-height: min(56dvh, 520px);
|
|
|
|
|
overflow: auto;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminSelectionCard {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 12px 13px;
|
|
|
|
|
border-radius: 14px;
|
|
|
|
|
border: 1px solid var(--theme-border);
|
|
|
|
|
background: var(--theme-pill-bg);
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminSelectionCard__label {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: var(--theme-text-faint);
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminSelectionCard__title {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .adminSelectionCard__meta {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: var(--theme-text-soft);
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .sidebarStat {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
@@ -3373,35 +3467,6 @@ function userAvatarFallback(user) {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .customItemModal__selected {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
padding: 14px;
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
border: 1px solid var(--theme-border);
|
|
|
|
|
background: var(--theme-surface-soft);
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .customItemModal__selectedImage {
|
|
|
|
|
width: 100%;
|
|
|
|
|
aspect-ratio: 1 / 1;
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
border: 1px solid var(--theme-border);
|
|
|
|
|
background: var(--theme-surface-soft);
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .customItemModal__selectedMeta {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .customItemModal__selectedTitle {
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .customItemModal__selectedChips {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.adminUiScope .customItemModal__pickerEyebrow {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
@@ -3412,46 +3477,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;
|
|
|
|
|
|