import { useState, useRef } from 'react' import { useNavigate, useParams } from 'react-router-dom' import Select from '../../../components/Select' import ConfirmDialog from '../../../components/ConfirmDialog' const TYPE_OPTIONS = [ { value: '아케인', label: '아케인' }, { value: '어센틱', label: '어센틱' }, { value: '그랜드 어센틱', label: '그랜드 어센틱' }, ] const inputCls = 'w-full rounded-lg border border-white/10 bg-gray-950 px-3 py-2 text-sm outline-none focus:border-emerald-500/50 transition' function formatMesoKorean(n) { if (!n || n <= 0) return '' const eok = Math.floor(n / 100_000_000) const man = Math.floor((n % 100_000_000) / 10_000) const parts = [] if (eok) parts.push(`${eok}억`) if (man) parts.push(`${man.toLocaleString()}만`) if (!parts.length) return `${n.toLocaleString()}` return parts.join(' ') } function MesoInput({ value, onChange, ...rest }) { const display = value === '' || value == null ? '' : Number(String(value).replace(/[^\d]/g, '')).toLocaleString() const korean = formatMesoKorean(Number(String(value).replace(/[^\d]/g, '')) || 0) return (
{ const digits = e.target.value.replace(/[^\d]/g, '') onChange(digits) }} className={`${inputCls} tabular-nums text-right`} {...rest} />
{korean || '\u00A0'}
) } function Field({ label, hint, error, required, children }) { return (
{hint && {hint}}
{children} {error &&
{error}
}
) } export default function SymbolForm() { const navigate = useNavigate() const { id } = useParams() const isEdit = !!id const fileInputRef = useRef(null) const [type, setType] = useState('아케인') const [region, setRegion] = useState('') const [maxLevel, setMaxLevel] = useState('') const [dailyDefault, setDailyDefault] = useState('') const [weeklyDefault, setWeeklyDefault] = useState('') const [imageFile, setImageFile] = useState(null) const [imagePreview, setImagePreview] = useState(null) const [levels, setLevels] = useState([]) const [confirmDelete, setConfirmDelete] = useState(false) const handleFile = (e) => { const file = e.target.files?.[0] if (!file) return setImageFile(file) setImagePreview(URL.createObjectURL(file)) } const updateLevel = (idx, field, val) => { setLevels((prev) => prev.map((l, i) => (i === idx ? { ...l, [field]: val } : l))) } const adjustLevelRows = (newMax) => { const n = Number(newMax) if (!n || n < 2) return setLevels((prev) => { const rows = Array.from({ length: n - 1 }, (_, i) => { const level = i + 1 return prev.find((l) => l.level === level) || { level, required_count: '', meso_cost: '' } }) return rows }) } return (

{isEdit ? '심볼 편집' : '심볼 추가'}

심볼 정보와 레벨별 필요 개수/메소를 입력합니다

{/* 기본 정보 */}
기본 정보
setRegion(e.target.value)} className={inputCls} placeholder="소멸의 여로" />
{ setMaxLevel(e.target.value); adjustLevelRows(e.target.value) }} className={inputCls} min="2" /> setDailyDefault(e.target.value)} className={inputCls} /> setWeeklyDefault(e.target.value)} className={inputCls} />
{/* 레벨별 설정 */}
레벨별 필요 개수 · 메소
레벨 N → N+1 업그레이드 기준 (만렙-1행)
{levels.map((l, idx) => ( ))}
레벨 필요 심볼 수 메소
Lv.{l.level} {l.level + 1} updateLevel(idx, 'required_count', e.target.value)} className={`${inputCls} max-w-36`} placeholder="0" />
updateLevel(idx, 'meso_cost', v)} placeholder="0" />
{/* 하단 버튼 */}
{isEdit && ( )}
setConfirmDelete(false)} onConfirm={() => setConfirmDelete(false)} title="심볼 삭제" description="이 심볼을 삭제하시겠습니까?\n레벨별 데이터도 함께 삭제됩니다." confirmText="삭제" destructive />
) }