From 39cda0d958ce970384e5214a0b00fdc8d8ba736b Mon Sep 17 00:00:00 2001 From: caadiq Date: Mon, 13 Apr 2026 15:41:47 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B3=B4=EC=8A=A4=20=EA=B2=B0=EC=A0=95=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80(=ED=94=84?= =?UTF-8?q?=EB=A1=A0=ED=8A=B8)=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 보스 추가/편집/삭제 폼 (이름, 이미지, 난이도별 가격/인원) - BossList: 등록된 보스 카드 목록 + 추가 버튼 - 동적 라우트가 sub-path 지원하도록 변경 (:slug/*) - 커스텀 Checkbox/Select 컴포넌트 - number input 화살표 전역 제거 - 가격 입력 시 메소 단위 미리보기 표시 - 결정석 → 결정으로 용어 통일 Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/App.jsx | 4 +- frontend/src/components/Checkbox.jsx | 29 ++ frontend/src/components/Select.jsx | 69 ++++ .../src/features/boss-crystal/BossCrystal.jsx | 2 +- .../boss-crystal/BossCrystalAdmin.jsx | 16 +- .../features/boss-crystal/admin/BossForm.jsx | 337 ++++++++++++++++++ .../features/boss-crystal/admin/BossList.jsx | 78 ++++ .../features/boss-crystal/admin/constants.js | 24 ++ frontend/src/index.css | 10 + 9 files changed, 559 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/Checkbox.jsx create mode 100644 frontend/src/components/Select.jsx create mode 100644 frontend/src/features/boss-crystal/admin/BossForm.jsx create mode 100644 frontend/src/features/boss-crystal/admin/BossList.jsx create mode 100644 frontend/src/features/boss-crystal/admin/constants.js diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1fc3d14..348965a 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -20,11 +20,11 @@ export default function App() { } /> } /> } /> - } /> + } /> {/* 동적 기능 페이지 */} - } /> + } /> ) diff --git a/frontend/src/components/Checkbox.jsx b/frontend/src/components/Checkbox.jsx new file mode 100644 index 0000000..56f4669 --- /dev/null +++ b/frontend/src/components/Checkbox.jsx @@ -0,0 +1,29 @@ +/** + * 커스텀 체크박스 + * ...} /> + */ +export default function Checkbox({ checked, onChange, disabled, className = '', size = 'md' }) { + const sizeCls = size === 'sm' ? 'w-4 h-4' : 'w-5 h-5' + const iconSize = size === 'sm' ? 'text-[10px]' : 'text-xs' + + return ( + + ) +} diff --git a/frontend/src/components/Select.jsx b/frontend/src/components/Select.jsx new file mode 100644 index 0000000..2b8082d --- /dev/null +++ b/frontend/src/components/Select.jsx @@ -0,0 +1,69 @@ +import { useEffect, useRef, useState } from 'react' + +/** + * 커스텀 드롭다운 셀렉트 + * setName(e.target.value)} + placeholder="예: 검은 마법사" + className={inputCls} + /> + + + {/* 이미지 */} + + + + + {/* 난이도 */} + +
+ {DIFFICULTIES.map((d) => { + const v = difficulties[d.key] + const priceErr = errors[`price_${d.key}`] + return ( +
+
+ {/* 체크박스 + 난이도 이미지 (이미지 클릭으로도 토글 가능) */} +
updateDifficulty(d.key, { enabled: !v.enabled })} + > + updateDifficulty(d.key, { enabled: checked })} + /> + {d.label} { e.currentTarget.style.display = 'none' }} + /> +
+ + {/* 가격 */} +
+
+ updateDifficulty(d.key, { crystal_price: e.target.value })} + disabled={!v.enabled} + placeholder="결정 가격" + className={`w-full rounded-lg border bg-gray-900 pl-4 pr-28 py-2 text-sm outline-none focus:border-emerald-500/50 disabled:opacity-50 disabled:cursor-not-allowed transition ${ + priceErr ? 'border-red-500/40' : 'border-white/10' + }`} + /> + {v.crystal_price && v.enabled && ( + + {formatMeso(Number(v.crystal_price))} + + )} +
+
+ + {/* 최대 인원 */} +