feat(libra): surface voided transactions in standalone history
Voided entries keep their '!' flag and gain a 'voided' tag per the libra reject convention, so detecting them needs a tag check rather than a new flag char. Render them inline with the existing 'x'-flag voided styling (grey XCircle icon, strike-through title/amount, red-tinted Voided badge) so users like Nancy can see their rejected entries instead of having them silently disappear from the list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4c704e5a41
commit
e9195978c1
1 changed files with 37 additions and 13 deletions
|
|
@ -46,6 +46,10 @@ function isExpense(t: Transaction): boolean {
|
||||||
return t.tags?.includes('expense-entry') ?? false
|
return t.tags?.includes('expense-entry') ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isVoided(t: Transaction): boolean {
|
||||||
|
return t.tags?.includes('voided') ?? false
|
||||||
|
}
|
||||||
|
|
||||||
const walletKey = computed(() => user.value?.wallets?.[0]?.inkey)
|
const walletKey = computed(() => user.value?.wallets?.[0]?.inkey)
|
||||||
|
|
||||||
// Fuzzy search state and configuration
|
// Fuzzy search state and configuration
|
||||||
|
|
@ -108,17 +112,20 @@ function formatAmount(amount: number): string {
|
||||||
return new Intl.NumberFormat('en-US').format(amount)
|
return new Intl.NumberFormat('en-US').format(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get status icon and color based on flag
|
// Get status icon and color. Voided entries (per libra convention) keep
|
||||||
function getStatusInfo(flag?: string) {
|
// flag='!' and carry a 'voided' tag — surface that as the icon regardless
|
||||||
switch (flag) {
|
// of the underlying flag.
|
||||||
|
function getStatusInfo(transaction: Transaction) {
|
||||||
|
if (isVoided(transaction)) {
|
||||||
|
return { icon: XCircle, color: 'text-muted-foreground', label: 'Voided' }
|
||||||
|
}
|
||||||
|
switch (transaction.flag) {
|
||||||
case '*':
|
case '*':
|
||||||
return { icon: CheckCircle2, color: 'text-green-600', label: 'Cleared' }
|
return { icon: CheckCircle2, color: 'text-green-600', label: 'Cleared' }
|
||||||
case '!':
|
case '!':
|
||||||
return { icon: Clock, color: 'text-orange-600', label: 'Pending' }
|
return { icon: Clock, color: 'text-orange-600', label: 'Pending' }
|
||||||
case '#':
|
case '#':
|
||||||
return { icon: Flag, color: 'text-red-600', label: 'Flagged' }
|
return { icon: Flag, color: 'text-red-600', label: 'Flagged' }
|
||||||
case 'x':
|
|
||||||
return { icon: XCircle, color: 'text-muted-foreground', label: 'Voided' }
|
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
@ -328,14 +335,19 @@ onMounted(() => {
|
||||||
<div class="flex items-center gap-2 mb-1">
|
<div class="flex items-center gap-2 mb-1">
|
||||||
<!-- Status Icon -->
|
<!-- Status Icon -->
|
||||||
<component
|
<component
|
||||||
v-if="getStatusInfo(transaction.flag)"
|
v-if="getStatusInfo(transaction)"
|
||||||
:is="getStatusInfo(transaction.flag)!.icon"
|
:is="getStatusInfo(transaction)!.icon"
|
||||||
:class="[
|
:class="[
|
||||||
'h-4 w-4 flex-shrink-0',
|
'h-4 w-4 flex-shrink-0',
|
||||||
getStatusInfo(transaction.flag)!.color
|
getStatusInfo(transaction)!.color
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<h3 class="font-medium text-sm sm:text-base truncate">
|
<h3
|
||||||
|
:class="[
|
||||||
|
'font-medium text-sm sm:text-base truncate',
|
||||||
|
isVoided(transaction) && 'line-through text-muted-foreground'
|
||||||
|
]"
|
||||||
|
>
|
||||||
{{ transaction.description }}
|
{{ transaction.description }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -346,10 +358,21 @@ onMounted(() => {
|
||||||
|
|
||||||
<!-- Amount -->
|
<!-- Amount -->
|
||||||
<div class="text-right flex-shrink-0">
|
<div class="text-right flex-shrink-0">
|
||||||
<p class="font-semibold text-sm sm:text-base">
|
<p
|
||||||
|
:class="[
|
||||||
|
'font-semibold text-sm sm:text-base',
|
||||||
|
isVoided(transaction) && 'line-through text-muted-foreground'
|
||||||
|
]"
|
||||||
|
>
|
||||||
{{ formatAmount(transaction.amount) }} sats
|
{{ formatAmount(transaction.amount) }} sats
|
||||||
</p>
|
</p>
|
||||||
<p v-if="transaction.fiat_amount" class="text-xs text-muted-foreground">
|
<p
|
||||||
|
v-if="transaction.fiat_amount"
|
||||||
|
:class="[
|
||||||
|
'text-xs text-muted-foreground',
|
||||||
|
isVoided(transaction) && 'line-through'
|
||||||
|
]"
|
||||||
|
>
|
||||||
{{ transaction.fiat_amount.toFixed(2) }} {{ transaction.fiat_currency }}
|
{{ transaction.fiat_amount.toFixed(2) }} {{ transaction.fiat_currency }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -376,10 +399,11 @@ onMounted(() => {
|
||||||
:class="[
|
:class="[
|
||||||
'text-xs',
|
'text-xs',
|
||||||
tag === 'income-entry' && 'bg-green-100 dark:bg-green-900/20 text-green-700 dark:text-green-300',
|
tag === 'income-entry' && 'bg-green-100 dark:bg-green-900/20 text-green-700 dark:text-green-300',
|
||||||
tag === 'expense-entry' && 'bg-red-100 dark:bg-red-900/20 text-red-700 dark:text-red-300'
|
tag === 'expense-entry' && 'bg-red-100 dark:bg-red-900/20 text-red-700 dark:text-red-300',
|
||||||
|
tag === 'voided' && 'bg-red-100 dark:bg-red-900/20 text-red-700 dark:text-red-300'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{ tag }}
|
{{ tag === 'voided' ? 'Voided' : tag }}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue