From afa176542076f3cf526000354e1b45859e269365 Mon Sep 17 00:00:00 2001 From: zenn Date: Fri, 14 Aug 2026 10:24:35 +0900 Subject: [PATCH] Complete transaction favorite actions --- composables/useTransactionFavorites.ts | 74 +++++++++++++++++++++++++ docs/changelog.md | 2 + pages/add.vue | 75 +++++++++++++++++++++++++- pages/transactions/[id].vue | 42 +++++++++++++-- todo.md | 1 - 5 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 composables/useTransactionFavorites.ts diff --git a/composables/useTransactionFavorites.ts b/composables/useTransactionFavorites.ts new file mode 100644 index 0000000..be53c91 --- /dev/null +++ b/composables/useTransactionFavorites.ts @@ -0,0 +1,74 @@ +type FavoriteTransactionInput = { + type: 'income' | 'expense' + assetId: string + categoryId: string + amount: number + title: string + memo?: string + latitude?: number | null + longitude?: number | null +} + +export type FavoriteTransaction = FavoriteTransactionInput & { + id: string + savedAt: string +} + +const storageKey = 'money_favorite_transactions' + +function readFavorites() { + if (!import.meta.client) return [] + try { + const parsed = JSON.parse(localStorage.getItem(storageKey) || '[]') + return Array.isArray(parsed) ? parsed as FavoriteTransaction[] : [] + } catch { + return [] + } +} + +function writeFavorites(items: FavoriteTransaction[]) { + if (!import.meta.client) return + localStorage.setItem(storageKey, JSON.stringify(items.slice(0, 20))) +} + +export function useTransactionFavorites() { + const favorites = useState('transactionFavorites', () => []) + + function loadFavorites() { + favorites.value = readFavorites() + } + + function saveFavorite(input: FavoriteTransactionInput) { + const title = input.title.trim() + if (!title || !input.assetId || !input.categoryId || !input.amount) return null + const items = readFavorites().filter(item => !( + item.type === input.type && + item.assetId === input.assetId && + item.categoryId === input.categoryId && + item.amount === input.amount && + item.title === title + )) + const favorite: FavoriteTransaction = { + ...input, + title, + memo: input.memo?.trim().slice(0, 60) || '', + id: globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(36).slice(2)}`, + savedAt: new Date().toISOString() + } + writeFavorites([favorite, ...items]) + loadFavorites() + return favorite + } + + function removeFavorite(id: string) { + writeFavorites(readFavorites().filter(item => item.id !== id)) + loadFavorites() + } + + return { + favorites, + loadFavorites, + saveFavorite, + removeFavorite + } +} diff --git a/docs/changelog.md b/docs/changelog.md index e4805a9..6664894 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -71,3 +71,5 @@ - 거래 입력/수정의 금액 입력을 기본 숫자 input에서 숫자만 정리하는 텍스트 입력으로 바꾸고 천 단위 표시를 적용했다. - 통계 화면의 카테고리 집계를 최종 카테고리 기준으로 바꾸고 목록 높이와 보조 정보를 더 촘촘하게 정리했다. - 자산 화면을 넓은 화면에서 카드 그리드와 상세 2열 구조로 보이도록 조정했다. +- 거래 상세 화면의 자주사용 버튼을 실제 템플릿 저장 기능과 연결하고, 거래 추가 화면에서 자주사용 내역을 바텀시트로 불러올 수 있도록 했다. +- 거래 삭제 확인을 브라우저 기본 confirm 대신 앱 스타일의 바텀시트로 변경했다. diff --git a/pages/add.vue b/pages/add.vue index 7149c03..2d8cfab 100644 --- a/pages/add.vue +++ b/pages/add.vue @@ -12,7 +12,15 @@

{{ form.type === 'income' ? '수입' : '지출' }}

- + + + +
+ + +
+
+
+

자주사용

+
+
+
+ + +
+

+ 저장된 자주사용 내역이 없습니다. +

+
+
+
+