144 lines
3.9 KiB
Vue
144 lines
3.9 KiB
Vue
<script setup lang="ts">
|
|
import { formatMoney } from '@/api/data';
|
|
import { getSelectedProfile } from '@/api/profile';
|
|
import type { TransactionsListItem } from '@/api/transaction';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import CategoryLabel from './CategoryLabel.vue';
|
|
import { computed, type Ref } from 'vue';
|
|
import { AccountTypes } from '@/api/account';
|
|
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
|
|
type MoneyStyle = "positive" | "negative" | "neutral"
|
|
|
|
const props = defineProps<{ tx: TransactionsListItem }>()
|
|
// Defines the style to use for money based on which accounts are involved.
|
|
const moneyStyle: Ref<MoneyStyle> = computed(() => {
|
|
if (props.tx.debitedAccount !== null && props.tx.creditedAccount === null) {
|
|
const debitedAccountType = AccountTypes.of(props.tx.debitedAccount.type)
|
|
return debitedAccountType.debitsPositive
|
|
? "positive"
|
|
: "negative"
|
|
} else if (props.tx.creditedAccount !== null && props.tx.debitedAccount === null) {
|
|
const creditedAccountType = AccountTypes.of(props.tx.creditedAccount.type)
|
|
return creditedAccountType.debitsPositive
|
|
? "negative"
|
|
: "positive"
|
|
}
|
|
return "neutral"
|
|
})
|
|
|
|
function goToTransaction() {
|
|
const profile = getSelectedProfile(route)
|
|
router.push(`/profiles/${profile}/transactions/${props.tx.id}`)
|
|
}
|
|
</script>
|
|
<template>
|
|
<div class="transaction-card" @click="goToTransaction()">
|
|
<!-- Top row contains timestamp and amount. -->
|
|
<div class="transaction-card-top-row">
|
|
<div>
|
|
<div class="transaction-card-id">Transaction #{{ tx.id }}</div>
|
|
<div class="transaction-card-timestamp">
|
|
{{ new Date(tx.timestamp).toLocaleString() }}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="transaction-card-money" :class="{
|
|
'transaction-card-money-positive': moneyStyle === 'positive',
|
|
'transaction-card-money-negative': moneyStyle === 'negative'
|
|
}">
|
|
{{ formatMoney(tx.amount, tx.currency) }}
|
|
</div>
|
|
<div v-if="tx.creditedAccount !== null" class="transaction-card-account-label">
|
|
Credited to <span class="transaction-card-account-badge">{{ tx.creditedAccount.name }}</span>
|
|
</div>
|
|
<div v-if="tx.debitedAccount !== null" class="transaction-card-account-label">
|
|
Debited to <span class="transaction-card-account-badge">{{ tx.debitedAccount.name }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Middle row contains the description. -->
|
|
<div>
|
|
<p class="transaction-card-description">{{ tx.description }}</p>
|
|
</div>
|
|
|
|
<!-- Bottom row contains other links. -->
|
|
<div>
|
|
<CategoryLabel :category="tx.category" v-if="tx.category" style="margin-left: 0" />
|
|
<span class="transaction-card-vendor-label" v-if="tx.vendor">{{ tx.vendor.name }}</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<style lang="css">
|
|
.transaction-card {
|
|
background-color: var(--bg-primary);
|
|
padding: 0.5rem;
|
|
border-radius: 0.5rem;
|
|
margin: 0.5rem 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.transaction-card:hover {
|
|
background-color: var(--bg-page);
|
|
}
|
|
|
|
.transaction-card-top-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.transaction-card-id {
|
|
font-size: 0.8rem;
|
|
color: white;
|
|
}
|
|
|
|
.transaction-card-timestamp {
|
|
font-size: 0.75rem;
|
|
font-family: monospace;
|
|
font-weight: 400;
|
|
color: gray;
|
|
}
|
|
|
|
.transaction-card-money {
|
|
font-size: 0.9rem;
|
|
font-family: monospace;
|
|
color: white;
|
|
text-align: right;
|
|
}
|
|
|
|
.transaction-card-money-positive {
|
|
color: green;
|
|
}
|
|
|
|
.transaction-card-money-negative {
|
|
color: red;
|
|
}
|
|
|
|
.transaction-card-account-label {
|
|
font-size: 0.75rem;
|
|
color: gray;
|
|
text-align: right;
|
|
}
|
|
|
|
.transaction-card-account-badge {
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.transaction-card-description {
|
|
margin: 0.25rem 0;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.transaction-card-vendor-label {
|
|
background-color: var(--bg-secondary);
|
|
border-radius: 0.3rem;
|
|
padding: 0.1rem 0.2rem;
|
|
font-size: 0.9rem;
|
|
|
|
}
|
|
</style>
|