v0.1.7 - 저장 레이어 분리 시작

This commit is contained in:
2026-04-21 17:41:20 +09:00
parent 066cab311b
commit 7ff8bd06b1
7 changed files with 124 additions and 86 deletions

View File

@@ -3,8 +3,12 @@ import { computed, reactive, ref, watch, nextTick } from 'vue'
import MiniCalendar from './components/MiniCalendar.vue'
import PlannerPage from './components/PlannerPage.vue'
import StatsDashboard from './components/StatsDashboard.vue'
import {
createInitialPlannerRecords,
persistPlannerState,
restorePlannerUiState,
} from './lib/plannerStorage'
const STORAGE_KEY = 'ten-minute-planner-state'
const screenMode = ref('planner')
const viewMode = ref('focus')
const printLayout = ref('single')
@@ -190,62 +194,15 @@ function normalizeRecord(record) {
}
}
function createInitialPlannerRecords() {
const baseRecords = Object.fromEntries(
Object.entries(plannerSeed).map(([key, record]) => [key, normalizeRecord(record)]),
)
const plannerRecords = reactive(createInitialPlannerRecords(plannerSeed, normalizeRecord))
if (typeof window === 'undefined') {
return baseRecords
}
try {
const savedState = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? '{}')
const savedRecords = savedState.records ?? {}
Object.entries(savedRecords).forEach(([key, record]) => {
baseRecords[key] = normalizeRecord(record)
})
} catch (error) {
console.warn('저장된 플래너 상태를 불러오지 못했습니다.', error)
}
return baseRecords
}
const plannerRecords = reactive(createInitialPlannerRecords())
function restoreDateState() {
if (typeof window === 'undefined') {
return
}
try {
const savedState = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? '{}')
if (savedState.selectedDate) {
selectedDate.value = toDateValue(savedState.selectedDate, selectedDate.value)
}
if (savedState.calendarViewDate) {
calendarViewDate.value = toDateValue(savedState.calendarViewDate, selectedDate.value)
} else {
calendarViewDate.value = new Date(selectedDate.value)
}
if (savedState.statsRangeStart) {
statsRangeStart.value = savedState.statsRangeStart
}
if (savedState.statsRangeEnd) {
statsRangeEnd.value = savedState.statsRangeEnd
}
} catch (error) {
console.warn('저장된 날짜 상태를 불러오지 못했습니다.', error)
}
}
restoreDateState()
restorePlannerUiState({
selectedDate,
calendarViewDate,
statsRangeStart,
statsRangeEnd,
toDateValue,
})
function getPlannerRecord(date) {
const key = toKey(date)
@@ -560,32 +517,13 @@ const bestDay = computed(() => {
watch(
[plannerRecords, selectedDate, calendarViewDate, statsRangeStart, statsRangeEnd],
() => {
if (typeof window === 'undefined') {
return
}
const serializableRecords = Object.fromEntries(
Object.entries(plannerRecords).map(([key, record]) => [
key,
{
...record,
tasks: record.tasks.map((task) => ({ ...task })),
memo: record.memo.map((item) => ({ ...item })),
timetable: [...record.timetable],
},
]),
)
window.localStorage.setItem(
STORAGE_KEY,
JSON.stringify({
selectedDate: selectedDate.value.toISOString(),
calendarViewDate: calendarViewDate.value.toISOString(),
statsRangeStart: normalizedStatsRange.value.startKey,
statsRangeEnd: normalizedStatsRange.value.endKey,
records: serializableRecords,
}),
)
persistPlannerState({
plannerRecords,
selectedDate: selectedDate.value,
calendarViewDate: calendarViewDate.value,
statsRangeStart: normalizedStatsRange.value.startKey,
statsRangeEnd: normalizedStatsRange.value.endKey,
})
},
{ deep: true },
)

92
src/lib/plannerStorage.js Normal file
View File

@@ -0,0 +1,92 @@
const STORAGE_KEY = 'ten-minute-planner-state'
function readStorageState() {
if (typeof window === 'undefined') {
return {}
}
try {
return JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? '{}')
} catch (error) {
console.warn('저장된 플래너 상태를 불러오지 못했습니다.', error)
return {}
}
}
export function createInitialPlannerRecords(seedRecords, normalizeRecord) {
const baseRecords = Object.fromEntries(
Object.entries(seedRecords).map(([key, record]) => [key, normalizeRecord(record)]),
)
const savedState = readStorageState()
const savedRecords = savedState.records ?? {}
Object.entries(savedRecords).forEach(([key, record]) => {
baseRecords[key] = normalizeRecord(record)
})
return baseRecords
}
export function restorePlannerUiState({
selectedDate,
calendarViewDate,
statsRangeStart,
statsRangeEnd,
toDateValue,
}) {
const savedState = readStorageState()
if (savedState.selectedDate) {
selectedDate.value = toDateValue(savedState.selectedDate, selectedDate.value)
}
if (savedState.calendarViewDate) {
calendarViewDate.value = toDateValue(savedState.calendarViewDate, selectedDate.value)
} else {
calendarViewDate.value = new Date(selectedDate.value)
}
if (savedState.statsRangeStart) {
statsRangeStart.value = savedState.statsRangeStart
}
if (savedState.statsRangeEnd) {
statsRangeEnd.value = savedState.statsRangeEnd
}
}
export function persistPlannerState({
plannerRecords,
selectedDate,
calendarViewDate,
statsRangeStart,
statsRangeEnd,
}) {
if (typeof window === 'undefined') {
return
}
const serializableRecords = Object.fromEntries(
Object.entries(plannerRecords).map(([key, record]) => [
key,
{
...record,
tasks: record.tasks.map((task) => ({ ...task })),
memo: record.memo.map((item) => ({ ...item })),
timetable: [...record.timetable],
},
]),
)
window.localStorage.setItem(
STORAGE_KEY,
JSON.stringify({
selectedDate: selectedDate.toISOString(),
calendarViewDate: calendarViewDate.toISOString(),
statsRangeStart,
statsRangeEnd,
records: serializableRecords,
}),
)
}