feat: 자주 쓰는 내역 UX 정리 + 빠른등록 견고화

- 등록 모달의 '자주 쓰는 내역 불러오기' 버튼·피커 모달 제거(번거로운 3단계 해소)
  (저장 기능은 유지)
- 가계부 quick-bar 에 ' 자주 쓰는 내역' 라벨 추가
- useQuick: 등록 성공과 목록 갱신 분리 — 갱신 단계 오류가 '등록 실패'로
  오인되던 문제 방지(앱에서 등록 안 됨처럼 보이던 케이스 대응)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-27 23:43:57 +09:00
parent c4c58d2e24
commit 5bfa45ed30
+17 -51
View File
@@ -654,7 +654,6 @@ async function doRegisterRecurring() {
// ===== 자주 쓰는 내역(빠른 등록 템플릿) =====
const quickEntries = ref([])
const quickEditMode = ref(false)
const quickPickerOpen = ref(false) // 불러오기 모달 피커
async function loadQuick() {
try {
quickEntries.value = await accountApi.quickEntries()
@@ -668,6 +667,7 @@ function quickLabel(q) {
// 칩 탭 → 오늘 날짜로 즉시 등록
async function useQuick(q) {
if (quickEditMode.value) return
// 1) 등록 — 이 단계 실패만 '등록 실패'로 처리
try {
await accountApi.create({
entryDate: todayStr(),
@@ -680,11 +680,17 @@ async function useQuick(q) {
installmentMonths: null,
tagIds: [],
})
await load()
await loadPendingCount()
flash(`'${quickLabel(q)}' ${won(q.amount)} 등록했습니다.`)
} catch (e) {
flash(e.response?.data?.message || '빠른 등록에 실패했습니다.')
return
}
// 2) 등록 성공 안내 + 목록 갱신(갱신 실패는 등록 성공을 가리지 않게 분리)
flash(`'${quickLabel(q)}' ${won(q.amount)} 등록했습니다.`)
try {
await load()
await loadPendingCount()
} catch {
/* 목록 갱신 실패는 무시 — 다음 진입/새로고침 시 반영 */
}
}
async function removeQuick(q) {
@@ -695,23 +701,6 @@ async function removeQuick(q) {
flash(e.response?.data?.message || '삭제에 실패했습니다.')
}
}
// 신규 추가 시: 자주 쓰는 내역을 폼에 불러오기(채움, 저장은 사용자가)
function applyQuick(q) {
if (!q) return
form.type = q.type
form.category = q.category || ''
form.amount = q.amount
form.memo = q.memo || ''
if (q.walletId) {
form.walletId = q.walletId
form.walletKind = walletKindOf(q.walletId) || form.walletKind
}
syncCategoryMajor()
}
function pickQuick(q) {
applyQuick(q)
quickPickerOpen.value = false
}
// 현재 폼 값을 자주 쓰는 내역으로 저장
async function saveAsQuick() {
if (form.type !== 'INCOME' && form.type !== 'EXPENSE') return
@@ -830,6 +819,7 @@ onMounted(async () => {
<!-- 자주 쓰는 내역(빠른 등록) 오늘 날짜로 즉시 등록 -->
<div v-if="quickEntries.length" class="quick-bar">
<span class="quick-bar-label"> 자주 쓰는 내역</span>
<button
v-for="q in quickEntries" :key="q.id" type="button" class="quick-chip"
:title="quickEditMode ? '' : '오늘 날짜로 바로 등록'" @click="useQuick(q)"
@@ -989,13 +979,6 @@ onMounted(async () => {
<div v-if="ocrRunning" class="receipt-progress"><div class="bar"></div></div>
</div>
<!-- 신규 추가 : 자주 쓰는 내역 불러오기(모달 피커) -->
<button
v-if="!editId && quickEntries.length"
type="button" class="to-recurring to-quick"
:disabled="submitting" @click="quickPickerOpen = true"
> 자주 쓰는 내역 불러오기</button>
<label>거래일<input v-model="form.entryDate" type="date" :disabled="submitting" /></label>
<label>구분
<select v-model="form.type" :disabled="submitting" @change="onTypeChange">
@@ -1180,29 +1163,6 @@ onMounted(async () => {
</Teleport>
<!-- 자주 쓰는 내역 불러오기 (모달 피커) -->
<Teleport to="body">
<Transition name="fade">
<div v-if="quickPickerOpen" class="modal-backdrop quick-picker-backdrop" @click.self="quickPickerOpen = false">
<div class="modal quick-picker" role="dialog" aria-modal="true">
<button class="close" type="button" @click="quickPickerOpen = false">×</button>
<h2>자주 쓰는 내역 불러오기</h2>
<ul v-if="quickEntries.length" class="qp-list">
<li v-for="q in quickEntries" :key="q.id">
<button type="button" class="qp-item" @click="pickQuick(q)">
<span class="qp-label">{{ quickLabel(q) }}</span>
<span class="qp-meta">
<span v-if="q.category" class="qp-cat">{{ q.category }}</span>
<span class="qp-amt" :class="q.type === 'INCOME' ? 'income' : 'expense'">{{ q.type === 'INCOME' ? '+' : '-' }}{{ won(q.amount) }}</span>
</span>
</button>
</li>
</ul>
<p v-else class="msg">저장된 자주 쓰는 내역이 없습니다.</p>
</div>
</div>
</Transition>
</Teleport>
<!-- 문자/푸시 붙여넣기 (모달) -->
<Teleport to="body">
<Transition name="fade">
@@ -1852,6 +1812,12 @@ button.primary {
margin: 0.4rem 0 0.2rem;
align-items: center;
}
.quick-bar-label {
font-size: 0.82rem;
font-weight: 600;
opacity: 0.65;
margin-right: 0.2rem;
}
.quick-chip {
display: inline-flex;
align-items: center;