Private
Public Access
1
0

Add payoff amount, fix dialog overlay and nav highlight for sub-routes

- Show estimated payoff amount (balance + accrued interest) in loan summary
- Display loan balances as absolute values in red
- Fix dialog overlay blocking UI after edit by closing in finally with nextTick
- Highlight parent nav item when viewing sub-routes (e.g. /loans/:id)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-14 22:12:50 -05:00
parent d28faa6838
commit f148b35ab2
2 changed files with 36 additions and 5 deletions
@@ -159,6 +159,8 @@ const navItems = [
function isNavActive(navRoute: string) {
if (route.path === navRoute) return true
// Highlight parent nav for sub-routes (e.g. /loans/:id highlights Loans)
if (navRoute !== '/' && route.path.startsWith(navRoute + '/')) return true
// Highlight "Transactions" when viewing account-scoped transactions
if (navRoute === '/transactions' && route.name === 'account-transactions') return true
return false
+34 -5
View File
@@ -18,7 +18,7 @@
<v-card-text>
<div class="d-flex justify-space-between mb-1">
<span class="text-medium-emphasis">Balance</span>
<span class="font-weight-bold">{{ formatCurrency(account.balance) }}</span>
<span class="font-weight-bold text-red">{{ formatCurrency(Math.abs(account.balance)) }}</span>
</div>
<div v-if="account.interestRate" class="d-flex justify-space-between mb-1">
<span class="text-medium-emphasis">Interest Rate</span>
@@ -69,7 +69,11 @@
</v-col>
<v-col cols="6" md="3">
<div class="text-medium-emphasis text-caption">Current Balance</div>
<div class="text-h6">{{ formatCurrency(loanDetail.currentBalance) }}</div>
<div class="text-h6 text-red">{{ formatCurrency(Math.abs(loanDetail.currentBalance)) }}</div>
</v-col>
<v-col v-if="payoffAmount != null" cols="6" md="3">
<div class="text-medium-emphasis text-caption">Est. Payoff Amount</div>
<div class="text-h6 text-red">{{ formatCurrency(payoffAmount) }}</div>
</v-col>
<v-col cols="6" md="3">
<div class="text-medium-emphasis text-caption">Interest Rate</div>
@@ -250,7 +254,7 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { ref, computed, nextTick, onMounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { loansApi } from '@/services/loans'
import { useAccountsStore } from '@/stores/accounts'
@@ -287,6 +291,30 @@ const filteredSchedule = computed(() => {
return loanDetail.value.amortizationSchedule.filter(e => e.paymentDate.split('T')[0] >= today)
})
const dailyAccrualTypes = ['AutoLoan', 'LineOfCredit']
const payoffAmount = computed(() => {
if (!loanDetail.value || !props.id) return null
const balance = Math.abs(loanDetail.value.currentBalance)
if (balance === 0) return 0
const account = accountsStore.accounts.find(a => a.id === props.id)
if (!account) return balance
const rate = loanDetail.value.interestRate / 100
const today = new Date()
const todayStr = today.toISOString().split('T')[0]
const schedule = loanDetail.value.amortizationSchedule
const lastPast = [...schedule].reverse().find(e => e.paymentDate.split('T')[0] <= todayStr)
if (!lastPast) return balance
const lastDate = new Date(lastPast.paymentDate.split('T')[0] + 'T00:00:00')
const todayMidnight = new Date(todayStr + 'T00:00:00')
const days = Math.round((todayMidnight.getTime() - lastDate.getTime()) / 86400000)
if (days <= 0) return balance
const isDailyAccrual = dailyAccrualTypes.includes(account.type)
const accruedInterest = isDailyAccrual
? balance * rate / 365 * days
: balance * rate / 12 * (days / 30)
return balance + accruedInterest
})
const amortHeaders = [
{ title: '#', key: 'paymentNumber', width: '60px' },
{ title: 'Date', key: 'paymentDate', width: '110px' },
@@ -404,12 +432,13 @@ async function saveLoanDetail() {
await loansApi.createLoanDetail(props.id, payload)
showSnackbar('Loan details created')
}
showLoanDialog.value = false
await loadLoanDetail()
} catch {
showSnackbar('Failed to save loan details', 'error')
} finally {
saving.value = false
showLoanDialog.value = false
await nextTick()
await loadLoanDetail()
}
}