From b4ada4b9a200c0381b844b43c21e5e6ef9245e9c Mon Sep 17 00:00:00 2001 From: zenn Date: Wed, 1 Apr 2026 10:11:48 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A6=B4=EB=A6=AC=EC=8A=A4:=20v1.3.9=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=B5=9C=EC=A0=81=ED=99=94=20?= =?UTF-8?q?=ED=8C=A8=EB=84=90=20=EB=B2=94=EC=9C=84=EC=99=80=20=ED=8B=B0?= =?UTF-8?q?=EC=96=B4=20=ED=96=89=20=EC=82=AD=EC=A0=9C=20UX=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/todo.md | 2 +- docs/update.md | 5 ++ frontend/src/views/AdminView.vue | 13 ++-- frontend/src/views/TierEditorView.vue | 90 +++++++++++++++++++++------ 4 files changed, 86 insertions(+), 24 deletions(-) diff --git a/docs/todo.md b/docs/todo.md index f32490c..af01523 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -1,7 +1,7 @@ # 할 일 및 이슈 ## 즉시 확인 필요 -- 티어표 랭크부분 삭제 버튼 최소화 필요 (각 라인별 우측 상단에 absolute 방식의 x 아이콘으로 변경. 클릭시 라인 삭제 경고를 보여주고 확인후 삭제 ) +- SVG 파일을 원본 소스로 보유하고 있는데 전부 img 형식으로 사용되고 있는것 같음. 수정 보완 필요. - 티어표 형식 추가 필요. 최근 게임들은 S, A, B,C 같은 랭크 뿐만 아니라 가로 열도 나누어진형태의 티어표를 원함 (공격, 방어, 지원 등 각 파트별 랭크를 보고싶어함) - 레거시 파일 정리 스크립트는 준비됐으므로, 운영 단계에서는 cron 등으로 주기 실행할지와 삭제 전 보관 기간을 함께 정한다. - 관리자 기본 아이템 다중 업로드는 현재 파일명 기반 자동 라벨만 지원하므로, 필요하면 업로드 후 일괄 라벨 수정/정렬 UX를 추가 검토한다. diff --git a/docs/update.md b/docs/update.md index 2d8ec5c..ea26150 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,10 @@ # 업데이트 로그 +## 2026-04-01 v1.3.9 +- 관리자 오른쪽 사이드의 Image Optimization 패널은 이제 기본 탭인 목록 관리에서만 노출되도록 줄여, 게임/아이템/티어표/회원 관리 화면에서는 실제 작업 패널에 더 집중할 수 있게 정리함. +- 커스텀 아이템 상세의 '이미 사용 중인 게임' 목록에서는 개인 보드용 freeform 템플릿을 제외하고, 실제 템플릿에 연결된 게임만 보이도록 다듬음. +- 티어표 행 삭제는 큰 버튼 대신 우측 상단의 작은 x 아이콘으로 바꾸고, 삭제 시 아이템이 풀 영역으로 돌아간다는 안내를 포함한 확인 모달을 거친 뒤 삭제되도록 개선함. + ## 2026-03-31 v1.3.8 - 홈 화면 게임 즐겨찾기 버튼은 일반 문자 별 대신 'kid_star.svg' 아이콘을 사용하도록 바꿔, 기존 아이콘 시스템과 같은 문법으로 정리함. - 실제로 더 이상 참조되지 않는 예전 업로드 파일을 정리하는 레거시 업로드 클린업 스크립트를 추가하고, 루트/백엔드 실행 스크립트도 함께 연결함. diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue index 17d0eae..7250838 100644 --- a/frontend/src/views/AdminView.vue +++ b/frontend/src/views/AdminView.vue @@ -257,6 +257,9 @@ const imageDiagnosticsCards = computed(() => { { label: '절감률', value: `${Math.round((stats.savingsRatio || 0) * 100)}%` }, ] }) +const visibleLinkedGames = computed(() => + (modalTargetCustomItem.value?.linkedGames || []).filter((game) => game?.id && game.id !== 'freeform') +) const imageStatsPeriodLabel = computed(() => (imageStatsMonth.value ? `${imageStatsMonth.value} 기준` : '전체 기간')) @@ -1726,11 +1729,11 @@ async function saveFeaturedOrder() {
- 이미 사용 중인 게임 -
- {{ game.name }} + 템플릿에 사용 중인 게임 +
+ {{ game.name }}
-
아직 연결된 게임이 없어요.
+
아직 템플릿에 연결된 게임이 없어요.
@@ -1925,7 +1928,7 @@ async function saveFeaturedOrder() { -
+
Image Optimization
diff --git a/frontend/src/views/TierEditorView.vue b/frontend/src/views/TierEditorView.vue index b3a54f8..b39dda5 100644 --- a/frontend/src/views/TierEditorView.vue +++ b/frontend/src/views/TierEditorView.vue @@ -45,6 +45,8 @@ const isTemplateUpdateModalOpen = ref(false) const templateRequestDraftTitle = ref('') const templateRequestDraftDescription = ref('') const isDeleteModalOpen = ref(false) +const isGroupDeleteModalOpen = ref(false) +const pendingRemoveGroupId = ref('') const ownerId = ref('') const authorName = ref('') const authorAccountName = ref('') @@ -280,7 +282,7 @@ async function addGroup() { await syncSortables() } -async function removeGroup(groupId) { +async function performRemoveGroup(groupId) { if (groups.value.length <= 1) return const target = groups.value.find((group) => group.id === groupId) if (!target) return @@ -290,6 +292,24 @@ async function removeGroup(groupId) { await syncSortables() } +function openGroupDeleteModal(groupId) { + if (!canEdit.value || groups.value.length <= 1 || !groupId) return + pendingRemoveGroupId.value = groupId + isGroupDeleteModalOpen.value = true +} + +function closeGroupDeleteModal() { + isGroupDeleteModalOpen.value = false + pendingRemoveGroupId.value = '' +} + +async function confirmRemoveGroup() { + const groupId = pendingRemoveGroupId.value + closeGroupDeleteModal() + if (!groupId) return + await performRemoveGroup(groupId) +} + function addCustomImage(file) { if (!file || !file.type.startsWith('image/')) return const url = URL.createObjectURL(file) @@ -833,6 +853,19 @@ onUnmounted(() => {
+
+ +
+
@@ -884,9 +917,18 @@ onUnmounted(() => {
{{ g.name }}
{ .previewOnly__label { display: grid; place-items: center; - padding: 10px 8px; + padding: 10px 12px; text-align: center; font-weight: 900; border-radius: 14px; @@ -1512,6 +1554,7 @@ onUnmounted(() => { align-items: stretch; } .row__label { + position: relative; border-radius: 16px; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.12); @@ -1519,7 +1562,7 @@ onUnmounted(() => { gap: 8px; align-items: center; justify-content: center; - padding: 10px 8px; + padding: 10px 12px; font-weight: 900; overflow: hidden; } @@ -1547,26 +1590,37 @@ onUnmounted(() => { outline: none; min-width: 0; } +.rowRemoveIcon { + position: absolute; + top: 8px; + right: 8px; + width: 24px; + height: 24px; + border-radius: 999px; + border: 1px solid rgba(239, 68, 68, 0.2); + background: rgba(15, 15, 15, 0.7); + color: rgba(255, 255, 255, 0.86); + display: grid; + place-items: center; + padding: 0; + cursor: pointer; + font-size: 14px; + line-height: 1; +} +.rowRemoveIcon:hover { + background: rgba(70, 20, 20, 0.82); + border-color: rgba(239, 68, 68, 0.34); +} +.rowRemoveIcon:disabled { + opacity: 0.4; + cursor: not-allowed; +} .row__exportName { width: 100%; text-align: center; font-weight: 900; word-break: break-word; } -.rowRemoveBtn { - padding: 6px 10px; - border-radius: 10px; - border: 1px solid rgba(239, 68, 68, 0.28); - background: rgba(239, 68, 68, 0.12); - color: rgba(255, 255, 255, 0.92); - cursor: pointer; - font-weight: 800; - flex: 0 0 auto; -} -.rowRemoveBtn:disabled { - opacity: 0.45; - cursor: not-allowed; -} .row__drop { border-radius: 16px; background: rgba(0, 0, 0, 0.18);