transaction list display

This commit is contained in:
Sirttas
2026-05-31 23:10:50 +02:00
parent 47ee14319d
commit 3235cf21ba
11 changed files with 126 additions and 18 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ import {ref} from 'vue';
interface Props {
inline?: boolean;
autoClose: boolean;
autoClose?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
+5 -1
View File
@@ -2,9 +2,12 @@
import {isCombined, Ledger} from "@/ledger/ledger.ts";
import {FolderOpenIcon} from '@heroicons/vue/24/outline';
import {RouterLink} from "vue-router";
import {routeNames} from "@/routes.ts";
interface Props {
ledger: Ledger;
link?: boolean;
}
const props = defineProps<Props>();
@@ -14,6 +17,7 @@ const props = defineProps<Props>();
<div class="flex">
<FolderOpenIcon v-if="isCombined(ledger)" class="w-4 me-1" />
<div v-else class="w-4 me-1"/>
<span>{{ ledger.name }}</span>
<RouterLink v-if="link" :to="{name: routeNames.listLedgerTransactions, params: {ledgerId: ledger.ledgerId}}">{{ ledger.name }}</RouterLink>
<span v-else>{{ ledger.name }}</span>
</div>
</template>
+6 -1
View File
@@ -6,12 +6,14 @@ import {
LedgerResponseTypeEnum,
MainLedgerResponse,
MainLedgerResponseTypeEnum,
TransactionResponse,
TransferResponseTypeEnum,
UpdateCombinedLedgerRequest,
UpdateMainLedgerRequest
} from "@/generated/mammon";
import {defineStore} from "pinia";
import {ref, triggerRef} from "vue";
import {ledgerApi} from "@/mammon";
import {ledgerApi, transactionApi} from "@/mammon";
export const LedgerTypes = LedgerResponseTypeEnum;
@@ -20,6 +22,7 @@ export type MainLedger = MainLedgerResponse & {type: MainLedgerResponseTypeEnum}
export type CombinedLedger = CombinedLedgerResponse & {type: CombinedLedgerResponseTypeEnum}
export type Ledger = MainLedger | CombinedLedger;
export const TransferTypes = TransferResponseTypeEnum;
export const systemLedgerRef = 'system';
export const systemLedger = {
@@ -71,3 +74,5 @@ export const useLedgersStore = defineStore('ledgers', () => {
return {ledgers, findById, findAllById, createMain, createCombined, updateMain, updateCombined, refresh};
})
export const findAllTransactionInLeger = (ledger: Ledger | string): Promise<TransactionResponse[]> => transactionApi.finAllTransactionsInLedger(typeof ledger == 'string' ? ledger : ledger.ledgerId).then(response => response.data)
+3 -1
View File
@@ -6,7 +6,8 @@ import {
CharacterRuleBookApi,
LedgerApi,
ProcessingApi,
RuleBookApi
RuleBookApi,
TransactionApi
} from "@/generated/mammon";
export const mammonUrl = import.meta.env.VITE_MAMMON_URL;
@@ -22,6 +23,7 @@ const mammonAxiosInstance = axios.create({
logResource(mammonAxiosInstance)
export const ledgerApi = new LedgerApi(undefined, mammonUrl, mammonAxiosInstance);
export const transactionApi = new TransactionApi(undefined, mammonUrl, mammonAxiosInstance);
export const characterApi = new CharacterApi(undefined, mammonUrl, mammonAxiosInstance);
export const ruleBookApi = new RuleBookApi(undefined, mammonUrl, mammonAxiosInstance);
export const characterRuleBookApi = new CharacterRuleBookApi(undefined, mammonUrl, mammonAxiosInstance);
+13
View File
@@ -0,0 +1,13 @@
<script setup lang="ts">
import {formatIsk} from "@/formaters";
interface Props {
amount: number;
}
const { amount } = defineProps<Props>();
</script>
<template>
<span :class="amount >= 0 ? 'text-emerald-400' : 'text-amber-700'">{{ formatIsk(amount) }}</span>
</template>
+2
View File
@@ -6,3 +6,5 @@ export * from './type';
export * from './appraisal';
export * from './market';
export { default as IskLabel } from './IskLabel.vue';
+21 -8
View File
@@ -1,27 +1,40 @@
import {esiAxiosInstance} from '@/service';
export type MarketType = {
id: number;
type_id: number;
group_id: number;
marketgroup_id: number;
market_group_id: number;
name: string;
published: boolean;
description: string;
basePrice: number;
base_price: number;
icon_id: number;
volume: number;
portionSize: number;
portion_size: number;
}
const cache = new Map<number, MarketType>(); // TODO move to pinia store
const fetchType = (id: number): Promise<MarketType> => {
if (cache.has(id)) {
return Promise.resolve(cache.get(id)!);
}
return esiAxiosInstance.get<MarketType>(`/universe/types/${id}/`).then(r => {
cache.set(id, r.data);
return r.data;
});
};
export const getMarketType = async (type: string | number): Promise<MarketType> => (await getMarketTypes([type]))[0];
export const getMarketTypes = async (types: (string | number)[]): Promise<MarketType[]> => {
if (types.length === 0) {
return [];
} else if (types.length === 1 && typeof types[0] === "number") {
return [];
}
return []
const ids = types.filter((t): t is number => typeof t === 'number');
return Promise.all(ids.map(fetchType));
}
const blueprintMarketGrous = [ // TODO add all groups
const blueprintMarketGroups = [ // TODO add all groups
2,
2157,
2159,
@@ -0,0 +1,67 @@
<script setup lang="ts">
import {ref, watch} from "vue";
import {useRoute} from "vue-router";
import log from "loglevel";
import {findAllTransactionInLeger, Ledger, TransferTypes, useLedgersStore} from "@/ledger";
import {computedAsync} from "@vueuse/core";
import {TransactionResponse} from "@/generated/mammon";
import {formatEveDate} from "@/formaters.ts";
import {IskLabel} from "@/market";
const {findById, refresh} = useLedgersStore();
const ledger = ref<Ledger>();
const transactions = computedAsync<TransactionResponse[]>(async () => {
if (ledger.value) {
return await findAllTransactionInLeger(ledger.value.ledgerId);
}
return [];
}, []);
const getIskBalance = (transaction: TransactionResponse) => {
const ledgerId = ledger.value?.ledgerId;
if (!ledgerId) {
return 0;
}
let balance = 0;
for (const transfer of transaction.transfers) {
if (transfer.type === TransferTypes.Isk) {
if (transfer.toLedgerId === ledgerId) {
balance += transfer.amount;
} else if (transfer.fromLedgerId === ledgerId) {
balance -= transfer.amount;
}
}
}
return balance;
}
watch(useRoute(), async route => {
if (route.params.ledgerId) {
const id = typeof route.params.ledgerId === 'string' ? route.params.ledgerId : route.params.ledgerId[0];
await refresh() // FIXME
ledger.value = findById(id)
log.info('Loaded ledger:', ledger.value);
} else {
ledger.value = undefined;
log.info('No ledger to load');
}
}, { immediate: true })
</script>
<template>
<div class="mt-4">
<div class="flex items-end gap-2 mt-2" v-for="transaction in transactions" :key="transaction.transactionId">
<span>{{formatEveDate(new Date(transaction.datetime))}}</span>
<IskLabel :amount="getIskBalance(transaction)" />
<span>{{transaction.description}}</span>
</div>
</div>
</template>
+3 -3
View File
@@ -4,7 +4,7 @@ import {EditLedgerModal, LedgerLabel, useLedgersStore} from "@/ledger";
import {storeToRefs} from "pinia";
import {nextTick, ref} from "vue";
import {PencilSquareIcon} from "@heroicons/vue/24/outline";
import {formatIsk} from "@/formaters.ts";
import {IskLabel} from "@/market";
const {ledgers} = storeToRefs(useLedgersStore());
@@ -22,9 +22,9 @@ const openEdit = async (ledgerId: string) => {
<template>
<div class="mt-4">
<div v-for="ledger in ledgers" :key="ledger.ledgerId" class="flex items-center mb-2">
<LedgerLabel :ledger="ledger" />
<LedgerLabel :ledger="ledger" :link="true" />
<div class="flex grow">
<span class="ms-2">{{ formatIsk(ledger.balance) }}</span>
<IskLabel class="ms-2" :amount="ledger.balance" />
</div>
<button class="btn-icon ms-2" @click="openEdit(ledger.ledgerId)"><PencilSquareIcon /></button>
</div>
+1 -1
View File
@@ -12,7 +12,7 @@ const {characters} = storeToRefs(useCharactersStore());
<div class="grid mb-2 mt-4">
<div v-for="character in characters" :key="character.characterId" class="flex items-center mb-2">
<CharacterLabel class="flex grow" :character="character" />
<RouterLink class="btn-icon ms-2" :to="{ name: routeNames.characterRulebook, params: { characterId: character.characterId } }"><PencilSquareIcon /></RouterLink>
<RouterLink class="btn-icon ms-2" :to="{ name: routeNames.editCharacterRulebook, params: { characterId: character.characterId } }"><PencilSquareIcon /></RouterLink>
</div>
</div>
</template>
+4 -2
View File
@@ -3,10 +3,11 @@ import {RouteRecordRaw} from 'vue-router';
export const routeNames = {
home: 'home',
callback: 'callback',
listLedgerTransactions: 'list-ledger-tTransactions',
listRuleBooks: 'list-rule-books',
newRuleBook: 'new-rule-book',
editRuleBook: 'edit-rule-book',
characterRulebook: 'character-rulebook',
editCharacterRulebook: 'edit-character-rule-book',
marketTypes: 'market-types',
about: 'about',
} as const;
@@ -17,6 +18,7 @@ export const routes: RouteRecordRaw[] = [
{path: '/ledgers', component: () => import('@/pages/Ledgers.vue'), children: [
{path: '', component: () => import('@/pages/ledger/ListLedgers.vue')},
{path: ':ledgerId/transactions', name: routeNames.listLedgerTransactions, component: () => import('@/pages/ledger/ListLedgerTransactions.vue')},
]},
{path: '/rules', component: () => import('@/pages/Rules.vue'), children: [
@@ -28,7 +30,7 @@ export const routes: RouteRecordRaw[] = [
]},
{path: '/characters/rules', children: [
{path: '', component: () => import('@/pages/rules/ListCharacterRuleBooks.vue')},
{path: '/characters/:characterId/rules', name: routeNames.characterRulebook, component: () => import('@/pages/rules/EditCharacterRuleBook.vue')},
{path: '/characters/:characterId/rules', name: routeNames.editCharacterRulebook, component: () => import('@/pages/rules/EditCharacterRuleBook.vue')},
]}
]},