라우터 requiresPremium 제거, 사이드바 🔒 제거, 자동실행을 전 사용자로, RecurringView 한도 도달 시 추가 버튼 업셀 다이얼로그+힌트(→/upgrade). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -941,13 +941,11 @@ async function analyzePasted() {
|
||||
|
||||
onMounted(async () => {
|
||||
// 진입 시 밀린 정기 거래를 먼저 반영한 뒤 목록을 불러온다 (중복 없이)
|
||||
// 고정지출은 유료 전용 — 무료 회원은 호출 자체를 건너뛴다(403/업그레이드 리다이렉트 방지)
|
||||
if (isPremium.value) {
|
||||
try {
|
||||
await accountApi.runRecurrings()
|
||||
} catch {
|
||||
// 정기 거래 반영 실패는 가계부 조회를 막지 않는다
|
||||
}
|
||||
// 고정지출은 무료도 2건까지 사용 가능 → 모든 로그인 사용자에 대해 실행
|
||||
try {
|
||||
await accountApi.runRecurrings()
|
||||
} catch {
|
||||
// 정기 거래 반영 실패는 가계부 조회를 막지 않는다
|
||||
}
|
||||
load()
|
||||
loadTagOptions()
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { accountApi } from '@/api/accountApi'
|
||||
import { useDialog } from '@/composables/dialog'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import IconBtn from '@/components/ui/IconBtn.vue'
|
||||
import AppModal from '@/components/ui/AppModal.vue'
|
||||
import CategoryPicker from '@/components/ui/CategoryPicker.vue'
|
||||
import ChipSelect from '@/components/ui/ChipSelect.vue'
|
||||
|
||||
const dialog = useDialog()
|
||||
const auth = useAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
const recurrings = ref([])
|
||||
const wallets = ref([])
|
||||
|
||||
// 무료 회원 고정지출 등록 한도(써보고 → 업셀). 유료는 무제한. 백엔드에서도 강제.
|
||||
const FREE_RECURRING_LIMIT = 2
|
||||
const atRecurringLimit = computed(
|
||||
() => !auth.isPremium && recurrings.value.length >= FREE_RECURRING_LIMIT,
|
||||
)
|
||||
const categories = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
@@ -162,7 +172,15 @@ async function runNow() {
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
async function openCreate() {
|
||||
if (atRecurringLimit.value) {
|
||||
const go = await dialog.confirm(
|
||||
`무료 회원은 고정 지출을 ${FREE_RECURRING_LIMIT}개까지 등록할 수 있어요.\n유료로 업그레이드하면 무제한이에요.`,
|
||||
{ title: '등록 한도', okText: '업그레이드', cancelText: '닫기' },
|
||||
)
|
||||
if (go) router.push('/upgrade')
|
||||
return
|
||||
}
|
||||
editId.value = null
|
||||
Object.assign(form, {
|
||||
title: '', type: 'EXPENSE', amount: null, category: '', memo: '',
|
||||
@@ -245,6 +263,10 @@ onMounted(load)
|
||||
<IconBtn icon="refresh" title="지금 반영" @click="runNow" />
|
||||
<IconBtn icon="plus" title="고정 지출 추가" variant="primary" @click="openCreate" />
|
||||
</div>
|
||||
<p v-if="atRecurringLimit" class="msg recur-limit">
|
||||
무료 회원은 고정 지출을 {{ FREE_RECURRING_LIMIT }}개까지 등록할 수 있어요 ·
|
||||
<RouterLink to="/upgrade" class="recur-upsell">유료는 무제한 →</RouterLink>
|
||||
</p>
|
||||
|
||||
<p v-if="error" class="msg error">{{ error }}</p>
|
||||
<p v-if="loading" class="msg">불러오는 중...</p>
|
||||
@@ -475,6 +497,8 @@ button.primary { border-color: hsla(160, 100%, 37%, 1); color: hsla(160, 100%, 3
|
||||
.actions button { padding: 0.2rem 0.55rem; font-size: 0.82rem; }
|
||||
.msg { margin: 0.75rem 0; }
|
||||
.msg.error { color: #c0392b; }
|
||||
.msg.recur-limit { color: #b8860b; font-size: 0.85rem; }
|
||||
.recur-upsell { color: hsla(160, 100%, 37%, 1); font-weight: 700; }
|
||||
|
||||
/* 폼 */
|
||||
.recur-form {
|
||||
|
||||
Reference in New Issue
Block a user