maplestory/frontend/src/features/liberation/Liberation.jsx

172 lines
4.7 KiB
React
Raw Normal View History

import { useState, useMemo, useEffect } from 'react'
import {
GENESIS_CHAPTERS,
GENESIS_TOTAL,
WEEKLY_BOSSES,
MONTHLY_BOSSES,
calcPoints,
addWeeks,
formatDate,
} from './data'
import WeekCard from './components/WeekCard'
import QuestSelector from './components/QuestSelector'
import PointsInput from './components/PointsInput'
import ProgressBar from './components/ProgressBar'
import DatePicker from '../../components/DatePicker'
const STORAGE_KEY = 'maple-liberation'
function makeEmptyWeek(startDate) {
const bosses = {}
WEEKLY_BOSSES.forEach((b) => {
bosses[b.key] = {
enabled: false,
difficulty: b.difficulties[0].key,
party: 1,
}
})
return {
startDate: startDate.toISOString(),
bosses,
blackMage: {
enabled: false,
difficulty: MONTHLY_BOSSES[0].difficulties[0].key,
party: 1,
},
}
}
function calcWeekPoints(weekData) {
let points = 0
WEEKLY_BOSSES.forEach((b) => {
const sel = weekData.bosses[b.key]
if (!sel?.enabled) return
const diff = b.difficulties.find((d) => d.key === sel.difficulty)
if (!diff) return
points += calcPoints(diff.points, sel.party)
})
if (weekData.blackMage?.enabled) {
const bm = MONTHLY_BOSSES[0]
const diff = bm.difficulties.find((d) => d.key === weekData.blackMage.difficulty)
if (diff) points += calcPoints(diff.points, weekData.blackMage.party)
}
return points
}
export default function Liberation() {
const [state, setState] = useState(() => {
const saved = localStorage.getItem(STORAGE_KEY)
if (saved) {
try { return JSON.parse(saved) } catch { /* ignore */ }
}
return {
startChapter: 0,
currentPoints: 0,
weeks: [makeEmptyWeek(new Date())],
}
})
useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
}, [state])
// 주차별 계산
const progressByWeek = useMemo(() => {
const result = []
const startConsumedBefore = GENESIS_CHAPTERS
.slice(0, state.startChapter)
.reduce((s, c) => s + c.required, 0)
let totalAccumulated = startConsumedBefore + state.currentPoints
for (const week of state.weeks) {
const earned = calcWeekPoints(week)
totalAccumulated += earned
let temp = totalAccumulated
let chapterIdx = 0
while (chapterIdx < GENESIS_CHAPTERS.length && temp >= GENESIS_CHAPTERS[chapterIdx].required) {
temp -= GENESIS_CHAPTERS[chapterIdx].required
chapterIdx++
}
const isCompleted = totalAccumulated >= GENESIS_TOTAL
const chapterInfo = isCompleted
? { name: '완료', current: GENESIS_TOTAL, required: GENESIS_TOTAL }
: {
name: GENESIS_CHAPTERS[chapterIdx]?.boss || '',
current: temp,
required: GENESIS_CHAPTERS[chapterIdx]?.required || 0,
}
result.push({
points: earned,
cumulative: totalAccumulated,
completed: isCompleted,
chapterInfo,
})
}
return result
}, [state])
const completedWeekIdx = progressByWeek.findIndex((w) => w.completed)
const isDone = completedWeekIdx >= 0
const completionDate = isDone
? addWeeks(new Date(state.weeks[completedWeekIdx].startDate), 1)
: null
const updateWeek = (idx, newWeekData) => {
setState((prev) => ({
...prev,
weeks: prev.weeks.map((w, i) => (i === idx ? newWeekData : w)),
}))
}
const addWeek = () => {
setState((prev) => {
const lastWeek = prev.weeks[prev.weeks.length - 1]
const nextStart = addWeeks(new Date(lastWeek.startDate), 1)
return {
...prev,
weeks: [...prev.weeks, { ...lastWeek, startDate: nextStart.toISOString() }],
}
})
}
const removeWeek = (idx) => {
setState((prev) => ({ ...prev, weeks: prev.weeks.filter((_, i) => i !== idx) }))
}
const resetAll = () => {
if (!confirm('입력한 내용을 모두 초기화하시겠습니까?')) return
setState({
startChapter: 0,
currentPoints: 0,
weeks: [makeEmptyWeek(new Date())],
})
}
const setFirstWeekDate = (dateStr) => {
const d = new Date(dateStr)
setState((prev) => {
const weeks = prev.weeks.map((w, i) => ({
...w,
startDate: addWeeks(d, i).toISOString(),
}))
return { ...prev, weeks }
})
}
const totalCumulative = progressByWeek[progressByWeek.length - 1]?.cumulative
|| (GENESIS_CHAPTERS.slice(0, state.startChapter).reduce((s, c) => s + c.required, 0) + state.currentPoints)
const overallProgress = Math.min((totalCumulative / GENESIS_TOTAL) * 100, 100)
return (
<div className="space-y-6">
<ProgressBar
totalAccumulated={totalCumulative}
completionDate={isDone ? formatDate(completionDate) : null}
/>
</div>
)
}