데스티니 해방에 현재 진행 상태 섹션 추가
QuestSelector를 chapters/imageBase prop 받도록 일반화한 뒤, Destiny에 시작 날짜/진행 중인 결전/현재 결의 입력 섹션 구현. 결의 입력 max는 20,000 (6챕터 요구량 15,000 여유 포함). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0448b0bfc8
commit
d506c022ca
3 changed files with 77 additions and 9 deletions
|
|
@ -1,11 +1,17 @@
|
|||
import { DESTINY_CHAPTERS, DESTINY_QUEST_IMAGE_BASE } from '../data'
|
||||
import dayjs from 'dayjs'
|
||||
import { DESTINY_CHAPTERS, DESTINY_QUEST_IMAGE_BASE, formatDate } from '../data'
|
||||
import { useLiberationStore } from '../store'
|
||||
import ProgressBar from './components/ProgressBar'
|
||||
import QuestSelector from './components/QuestSelector'
|
||||
import PointsInput from './components/PointsInput'
|
||||
import DatePicker from '../../../components/common/DatePicker'
|
||||
|
||||
export default function Destiny() {
|
||||
const calcMode = useLiberationStore((s) => s.destinyCalcMode)
|
||||
const setCalcMode = useLiberationStore((s) => s.setDestinyCalcMode)
|
||||
const state = useLiberationStore((s) => s.destinyCalcMode === 'weekly' ? s.destinyWeekly : s.destinySimple)
|
||||
const updateSlot = useLiberationStore((s) => s.updateDestinySlot)
|
||||
const setState = (updater) => updateSlot(updater)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -49,6 +55,66 @@ export default function Destiny() {
|
|||
completionDate={null}
|
||||
completionColor="var(--destiny-date)"
|
||||
/>
|
||||
|
||||
{/* 현재 진행 상태 입력 */}
|
||||
<div
|
||||
className="max-w-3xl mx-auto rounded-2xl border p-6 space-y-4"
|
||||
style={{
|
||||
background: 'var(--panel-bg)',
|
||||
borderColor: 'var(--panel-border)',
|
||||
boxShadow: 'var(--panel-shadow)',
|
||||
}}
|
||||
>
|
||||
<div className="text-lg font-semibold" style={{ color: 'var(--accent-bright)' }}>현재 진행 상태</div>
|
||||
|
||||
<div className="grid gap-3 grid-cols-3">
|
||||
<div className="space-y-1.5">
|
||||
<label className="block text-xs" style={{ color: 'var(--text-muted)' }}>시작 날짜</label>
|
||||
<DatePicker
|
||||
value={formatDate(state.startDate)}
|
||||
onChange={(d) => setState((prev) => ({ ...prev, startDate: dayjs(d).toISOString() }))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="block text-xs" style={{ color: 'var(--text-muted)' }}>진행 중인 퀘스트</label>
|
||||
<QuestSelector
|
||||
chapters={DESTINY_CHAPTERS}
|
||||
imageBase={DESTINY_QUEST_IMAGE_BASE}
|
||||
value={state.startChapter}
|
||||
onChange={(idx) => setState((prev) => ({ ...prev, startChapter: idx }))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="block text-xs" style={{ color: 'var(--text-muted)' }}>현재 결의</label>
|
||||
<div
|
||||
className="flex items-stretch rounded-lg border focus-within:border-[var(--input-border-focus)] hover:border-[var(--input-border-hover)]"
|
||||
style={{
|
||||
background: 'var(--input-bg)',
|
||||
borderColor: 'var(--input-border)',
|
||||
}}
|
||||
>
|
||||
<PointsInput
|
||||
value={state.currentPoints}
|
||||
max={20000}
|
||||
onChange={(n) => setState((prev) => ({ ...prev, currentPoints: n }))}
|
||||
className="flex-1 min-w-0 bg-transparent px-3 h-12 text-base text-right tabular-nums outline-none"
|
||||
style={{ color: 'var(--text-strong)' }}
|
||||
/>
|
||||
<span
|
||||
className="flex items-center px-3 text-base border-l select-none tabular-nums"
|
||||
style={{
|
||||
borderColor: 'var(--input-border)',
|
||||
color: 'var(--text-dim)',
|
||||
}}
|
||||
>
|
||||
/ {(DESTINY_CHAPTERS[state.startChapter]?.required ?? 0).toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@ export default function Genesis() {
|
|||
<div className="space-y-1.5">
|
||||
<label className="block text-xs" style={{ color: 'var(--text-muted)' }}>진행 중인 퀘스트</label>
|
||||
<QuestSelector
|
||||
chapters={GENESIS_CHAPTERS}
|
||||
imageBase={QUEST_BOSS_IMAGE_BASE}
|
||||
value={state.startChapter}
|
||||
onChange={(idx) => setState((prev) => ({ ...prev, startChapter: idx }))}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useState, useEffect, useRef } from 'react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { GENESIS_CHAPTERS, QUEST_BOSS_IMAGE_BASE } from '../../data'
|
||||
|
||||
/**
|
||||
* 진행 중인 퀘스트 드롭다운
|
||||
* - 보스 초상화 + 이름 텍스트
|
||||
* 진행 중인 퀘스트 드롭다운 (보스 초상화 + 이름)
|
||||
* @param {Array} chapters - { idx, boss, ... }[]
|
||||
* @param {string} imageBase - 보스 초상화 S3 base URL
|
||||
*/
|
||||
export default function QuestSelector({ value, onChange }) {
|
||||
export default function QuestSelector({ chapters, imageBase, value, onChange }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const ref = useRef(null)
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ export default function QuestSelector({ value, onChange }) {
|
|||
return () => document.removeEventListener('mousedown', handler)
|
||||
}, [open])
|
||||
|
||||
const selected = GENESIS_CHAPTERS[value]
|
||||
const selected = chapters[value]
|
||||
|
||||
return (
|
||||
<div ref={ref} className="relative">
|
||||
|
|
@ -38,7 +38,7 @@ export default function QuestSelector({ value, onChange }) {
|
|||
style={{ background: 'var(--surface-nested)' }}
|
||||
>
|
||||
<img
|
||||
src={`${QUEST_BOSS_IMAGE_BASE}/${selected.boss}.webp`}
|
||||
src={`${imageBase}/${selected.boss}.webp`}
|
||||
alt=""
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
|
@ -69,7 +69,7 @@ export default function QuestSelector({ value, onChange }) {
|
|||
boxShadow: 'var(--popup-shadow)',
|
||||
}}
|
||||
>
|
||||
{GENESIS_CHAPTERS.map((chapter) => {
|
||||
{chapters.map((chapter) => {
|
||||
const isSelected = chapter.idx === value
|
||||
return (
|
||||
<button
|
||||
|
|
@ -86,7 +86,7 @@ export default function QuestSelector({ value, onChange }) {
|
|||
style={{ background: 'var(--surface-nested)' }}
|
||||
>
|
||||
<img
|
||||
src={`${QUEST_BOSS_IMAGE_BASE}/${chapter.boss}.webp`}
|
||||
src={`${imageBase}/${chapter.boss}.webp`}
|
||||
alt=""
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue