feat: 자주 쓰는 내역 UX 정리 + 빠른등록 견고화
- 등록 모달의 '자주 쓰는 내역 불러오기' 버튼·피커 모달 제거(번거로운 3단계 해소) (저장 기능은 유지) - 가계부 quick-bar 에 '⭐ 자주 쓰는 내역' 라벨 추가 - useQuick: 등록 성공과 목록 갱신 분리 — 갱신 단계 오류가 '등록 실패'로 오인되던 문제 방지(앱에서 등록 안 됨처럼 보이던 케이스 대응) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -654,7 +654,6 @@ async function doRegisterRecurring() {
|
|||||||
// ===== 자주 쓰는 내역(빠른 등록 템플릿) =====
|
// ===== 자주 쓰는 내역(빠른 등록 템플릿) =====
|
||||||
const quickEntries = ref([])
|
const quickEntries = ref([])
|
||||||
const quickEditMode = ref(false)
|
const quickEditMode = ref(false)
|
||||||
const quickPickerOpen = ref(false) // 불러오기 모달 피커
|
|
||||||
async function loadQuick() {
|
async function loadQuick() {
|
||||||
try {
|
try {
|
||||||
quickEntries.value = await accountApi.quickEntries()
|
quickEntries.value = await accountApi.quickEntries()
|
||||||
@@ -668,6 +667,7 @@ function quickLabel(q) {
|
|||||||
// 칩 탭 → 오늘 날짜로 즉시 등록
|
// 칩 탭 → 오늘 날짜로 즉시 등록
|
||||||
async function useQuick(q) {
|
async function useQuick(q) {
|
||||||
if (quickEditMode.value) return
|
if (quickEditMode.value) return
|
||||||
|
// 1) 등록 — 이 단계 실패만 '등록 실패'로 처리
|
||||||
try {
|
try {
|
||||||
await accountApi.create({
|
await accountApi.create({
|
||||||
entryDate: todayStr(),
|
entryDate: todayStr(),
|
||||||
@@ -680,11 +680,17 @@ async function useQuick(q) {
|
|||||||
installmentMonths: null,
|
installmentMonths: null,
|
||||||
tagIds: [],
|
tagIds: [],
|
||||||
})
|
})
|
||||||
await load()
|
|
||||||
await loadPendingCount()
|
|
||||||
flash(`'${quickLabel(q)}' ${won(q.amount)} 등록했습니다.`)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
flash(e.response?.data?.message || '빠른 등록에 실패했습니다.')
|
flash(e.response?.data?.message || '빠른 등록에 실패했습니다.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 2) 등록 성공 안내 + 목록 갱신(갱신 실패는 등록 성공을 가리지 않게 분리)
|
||||||
|
flash(`'${quickLabel(q)}' ${won(q.amount)} 등록했습니다.`)
|
||||||
|
try {
|
||||||
|
await load()
|
||||||
|
await loadPendingCount()
|
||||||
|
} catch {
|
||||||
|
/* 목록 갱신 실패는 무시 — 다음 진입/새로고침 시 반영 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function removeQuick(q) {
|
async function removeQuick(q) {
|
||||||
@@ -695,23 +701,6 @@ async function removeQuick(q) {
|
|||||||
flash(e.response?.data?.message || '삭제에 실패했습니다.')
|
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() {
|
async function saveAsQuick() {
|
||||||
if (form.type !== 'INCOME' && form.type !== 'EXPENSE') return
|
if (form.type !== 'INCOME' && form.type !== 'EXPENSE') return
|
||||||
@@ -830,6 +819,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<!-- 자주 쓰는 내역(빠른 등록) — 칩 탭 시 오늘 날짜로 즉시 등록 -->
|
<!-- 자주 쓰는 내역(빠른 등록) — 칩 탭 시 오늘 날짜로 즉시 등록 -->
|
||||||
<div v-if="quickEntries.length" class="quick-bar">
|
<div v-if="quickEntries.length" class="quick-bar">
|
||||||
|
<span class="quick-bar-label">⭐ 자주 쓰는 내역</span>
|
||||||
<button
|
<button
|
||||||
v-for="q in quickEntries" :key="q.id" type="button" class="quick-chip"
|
v-for="q in quickEntries" :key="q.id" type="button" class="quick-chip"
|
||||||
:title="quickEditMode ? '' : '오늘 날짜로 바로 등록'" @click="useQuick(q)"
|
:title="quickEditMode ? '' : '오늘 날짜로 바로 등록'" @click="useQuick(q)"
|
||||||
@@ -989,13 +979,6 @@ onMounted(async () => {
|
|||||||
<div v-if="ocrRunning" class="receipt-progress"><div class="bar"></div></div>
|
<div v-if="ocrRunning" class="receipt-progress"><div class="bar"></div></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>거래일<input v-model="form.entryDate" type="date" :disabled="submitting" /></label>
|
||||||
<label>구분
|
<label>구분
|
||||||
<select v-model="form.type" :disabled="submitting" @change="onTypeChange">
|
<select v-model="form.type" :disabled="submitting" @change="onTypeChange">
|
||||||
@@ -1180,29 +1163,6 @@ onMounted(async () => {
|
|||||||
</Teleport>
|
</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">
|
<Teleport to="body">
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
@@ -1852,6 +1812,12 @@ button.primary {
|
|||||||
margin: 0.4rem 0 0.2rem;
|
margin: 0.4rem 0 0.2rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.quick-bar-label {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.65;
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
}
|
||||||
.quick-chip {
|
.quick-chip {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user