6552a97036
Integrate 6 new tool chains into the chat service: get_budget, get_budget_summary, get_loan_details, calculate_loan_payoff, list_payees, and get_scheduled_transactions. Also raises the search_transactions page_size cap from 25 to 50 and the upcoming bills display from 5 to 10. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
332 lines
12 KiB
Vue
332 lines
12 KiB
Vue
<template>
|
|
<!-- Transactions -->
|
|
<div v-if="type === 'transactions' && data?.items">
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Payee</th>
|
|
<th class="text-right">Amount</th>
|
|
<th>Category</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="txn in data.items.slice(0, 15)" :key="txn.id">
|
|
<td class="text-no-wrap">{{ formatDate(txn.date) }}</td>
|
|
<td>{{ txn.payeeName || 'Unknown' }}</td>
|
|
<td class="text-right text-no-wrap" :class="txn.amount >= 0 ? 'text-success' : 'text-error'">
|
|
{{ formatCurrency(txn.amount) }}
|
|
</td>
|
|
<td>{{ txn.categoryName || 'Uncategorized' }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
<div v-if="data.totalCount > 15" class="text-caption text-medium-emphasis ml-2">
|
|
Showing 15 of {{ data.totalCount }} transactions
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Spending Report -->
|
|
<div v-else-if="type === 'spending_report'">
|
|
<div class="text-subtitle-2 mb-1">
|
|
Total Spending: {{ formatCurrency(data.totalSpending) }}
|
|
</div>
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Category</th>
|
|
<th class="text-right">Amount</th>
|
|
<th class="text-right">%</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="cat in data.categories?.slice(0, 15)" :key="cat.categoryName">
|
|
<td>{{ cat.categoryName }}</td>
|
|
<td class="text-right">{{ formatCurrency(cat.amount) }}</td>
|
|
<td class="text-right">{{ cat.percentage.toFixed(1) }}%</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</div>
|
|
|
|
<!-- Income vs Expense -->
|
|
<div v-else-if="type === 'income_expense'">
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Month</th>
|
|
<th class="text-right">Income</th>
|
|
<th class="text-right">Expenses</th>
|
|
<th class="text-right">Net</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="month in data.months" :key="`${month.year}-${month.month}`">
|
|
<td>{{ month.year }}-{{ String(month.month).padStart(2, '0') }}</td>
|
|
<td class="text-right text-success">{{ formatCurrency(month.income) }}</td>
|
|
<td class="text-right text-error">{{ formatCurrency(month.expenses) }}</td>
|
|
<td class="text-right" :class="month.net >= 0 ? 'text-success' : 'text-error'">
|
|
{{ formatCurrency(month.net) }}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</div>
|
|
|
|
<!-- Accounts -->
|
|
<div v-else-if="type === 'accounts' && Array.isArray(data)">
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Account</th>
|
|
<th>Type</th>
|
|
<th class="text-right">Balance</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="acct in data" :key="acct.id">
|
|
<td>{{ acct.name }}</td>
|
|
<td>{{ acct.type }}</td>
|
|
<td class="text-right" :class="acct.balance >= 0 ? 'text-success' : 'text-error'">
|
|
{{ formatCurrency(acct.balance) }}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</div>
|
|
|
|
<!-- Dashboard -->
|
|
<div v-else-if="type === 'dashboard'">
|
|
<v-card variant="tonal" density="compact" class="mb-2 pa-3">
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Net Worth</span>
|
|
<span class="text-subtitle-2 font-weight-bold">{{ formatCurrency(data.netWorth) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Assets</span>
|
|
<span class="text-caption text-success">{{ formatCurrency(data.totalAssets) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between">
|
|
<span class="text-caption text-medium-emphasis">Liabilities</span>
|
|
<span class="text-caption text-error">{{ formatCurrency(data.totalLiabilities) }}</span>
|
|
</div>
|
|
</v-card>
|
|
</div>
|
|
|
|
<!-- Category Update -->
|
|
<div v-else-if="type === 'category_update'">
|
|
<v-alert type="success" variant="tonal" density="compact" class="mb-2 text-caption">
|
|
Updated "{{ data.payeeName || 'Transaction' }}" ({{ formatDate(data.date) }}, {{ formatCurrency(data.amount) }})
|
|
from "{{ data.oldCategory }}" to "{{ data.newCategory }}"
|
|
</v-alert>
|
|
</div>
|
|
|
|
<!-- Categories List -->
|
|
<div v-else-if="type === 'categories' && Array.isArray(data)">
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Type</th>
|
|
<th>Parent</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="cat in data" :key="cat.id">
|
|
<td>{{ cat.name }}</td>
|
|
<td>
|
|
<v-chip size="x-small" :color="cat.type === 'Income' ? 'success' : cat.type === 'Expense' ? 'error' : 'info'" variant="tonal">
|
|
{{ cat.type }}
|
|
</v-chip>
|
|
</td>
|
|
<td class="text-medium-emphasis">{{ cat.parentName || '-' }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</div>
|
|
|
|
<!-- Category Created -->
|
|
<div v-else-if="type === 'category_created'">
|
|
<v-alert type="success" variant="tonal" density="compact" class="mb-2 text-caption">
|
|
Created {{ data.type }} category "{{ data.name }}"{{ data.parentName ? ` under "${data.parentName}"` : '' }}
|
|
</v-alert>
|
|
</div>
|
|
|
|
<!-- Category Deleted -->
|
|
<div v-else-if="type === 'category_deleted'">
|
|
<v-alert type="warning" variant="tonal" density="compact" class="mb-2 text-caption">
|
|
Deleted {{ data.type }} category "{{ data.name }}"
|
|
</v-alert>
|
|
</div>
|
|
|
|
<!-- Budget -->
|
|
<div v-else-if="type === 'budget' && data?.items">
|
|
<div class="text-subtitle-2 mb-1">
|
|
Budget for {{ data.year }}-{{ String(data.month).padStart(2, '0') }}
|
|
</div>
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Category</th>
|
|
<th class="text-right">Budgeted</th>
|
|
<th class="text-right">Actual</th>
|
|
<th class="text-right">Remaining</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="item in data.items" :key="item.id">
|
|
<td>{{ item.categoryName }}</td>
|
|
<td class="text-right">{{ formatCurrency(item.budgetedAmount) }}</td>
|
|
<td class="text-right">{{ formatCurrency(item.actualAmount) }}</td>
|
|
<td class="text-right" :class="item.budgetedAmount - item.actualAmount >= 0 ? 'text-success' : 'text-error'">
|
|
{{ formatCurrency(item.budgetedAmount - item.actualAmount) }}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</div>
|
|
|
|
<!-- Budget Summary -->
|
|
<div v-else-if="type === 'budget_summary'">
|
|
<v-card variant="tonal" density="compact" class="mb-2 pa-3">
|
|
<div class="text-subtitle-2 mb-2">
|
|
Budget Summary — {{ data.year }}-{{ String(data.month).padStart(2, '0') }}
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Total Budgeted</span>
|
|
<span class="text-caption">{{ formatCurrency(data.totalBudgeted) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Total Spent</span>
|
|
<span class="text-caption">{{ formatCurrency(data.totalActual) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Remaining</span>
|
|
<span class="text-caption" :class="data.remaining >= 0 ? 'text-success' : 'text-error'">
|
|
{{ formatCurrency(data.remaining) }}
|
|
</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between">
|
|
<span class="text-caption text-medium-emphasis">Over Budget</span>
|
|
<span class="text-caption" :class="data.overBudgetCount > 0 ? 'text-error' : 'text-success'">
|
|
{{ data.overBudgetCount }} categories
|
|
</span>
|
|
</div>
|
|
</v-card>
|
|
</div>
|
|
|
|
<!-- Loan Details -->
|
|
<div v-else-if="type === 'loan_details'">
|
|
<v-card variant="tonal" density="compact" class="mb-2 pa-3">
|
|
<div class="text-subtitle-2 mb-2">{{ data.accountName }}</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Original Balance</span>
|
|
<span class="text-caption">{{ formatCurrency(data.originalBalance) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Current Balance</span>
|
|
<span class="text-caption text-error">{{ formatCurrency(data.currentBalance) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Interest Rate</span>
|
|
<span class="text-caption">{{ data.interestRate?.toFixed(2) }}%</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Monthly Payment</span>
|
|
<span class="text-caption">{{ formatCurrency(data.monthlyPayment) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Maturity Date</span>
|
|
<span class="text-caption">{{ formatDate(data.maturityDate) }}</span>
|
|
</div>
|
|
<div v-if="data.escrowAmount" class="d-flex justify-space-between">
|
|
<span class="text-caption text-medium-emphasis">Escrow</span>
|
|
<span class="text-caption">{{ formatCurrency(data.escrowAmount) }}</span>
|
|
</div>
|
|
</v-card>
|
|
</div>
|
|
|
|
<!-- Loan Payoff Scenario -->
|
|
<div v-else-if="type === 'loan_payoff'">
|
|
<v-card variant="tonal" density="compact" class="mb-2 pa-3">
|
|
<div class="text-subtitle-2 mb-2">Payoff Scenario</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Original Payoff</span>
|
|
<span class="text-caption">{{ formatDate(data.originalPayoffDate) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">New Payoff</span>
|
|
<span class="text-caption text-success">{{ formatDate(data.newPayoffDate) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between mb-1">
|
|
<span class="text-caption text-medium-emphasis">Interest Saved</span>
|
|
<span class="text-caption text-success">{{ formatCurrency(data.interestSaved) }}</span>
|
|
</div>
|
|
<div class="d-flex justify-space-between">
|
|
<span class="text-caption text-medium-emphasis">Months Saved</span>
|
|
<span class="text-caption text-success">{{ data.monthsSaved }}</span>
|
|
</div>
|
|
</v-card>
|
|
</div>
|
|
|
|
<!-- Payees -->
|
|
<div v-else-if="type === 'payees' && Array.isArray(data)">
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Default Category</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="payee in data" :key="payee.id">
|
|
<td>{{ payee.name }}</td>
|
|
<td class="text-medium-emphasis">{{ payee.defaultCategoryName || '-' }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</div>
|
|
|
|
<!-- Scheduled Transactions -->
|
|
<div v-else-if="type === 'scheduled_transactions' && Array.isArray(data)">
|
|
<v-table density="compact" class="text-caption rounded-lg mb-2">
|
|
<thead>
|
|
<tr>
|
|
<th>Payee</th>
|
|
<th class="text-right">Amount</th>
|
|
<th>Frequency</th>
|
|
<th>Next Due</th>
|
|
<th>Account</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="txn in data" :key="txn.id">
|
|
<td>{{ txn.payeeName || 'Unknown' }}</td>
|
|
<td class="text-right text-no-wrap" :class="txn.amount >= 0 ? 'text-success' : 'text-error'">
|
|
{{ formatCurrency(txn.amount) }}
|
|
</td>
|
|
<td>{{ txn.frequency }}</td>
|
|
<td class="text-no-wrap">{{ formatDate(txn.nextDueDate) }}</td>
|
|
<td>{{ txn.accountName }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
defineProps<{
|
|
type: string
|
|
data: any
|
|
}>()
|
|
|
|
function formatCurrency(value: number): string {
|
|
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value)
|
|
}
|
|
|
|
function formatDate(value: string): string {
|
|
if (!value) return ''
|
|
return new Date(value).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
|
}
|
|
</script>
|