feat(#8): Handle polymorphic activity source

This commit is contained in:
Sirttas
2026-07-03 14:00:42 +02:00
parent 052a0a5d03
commit 8d73fe3ed1
8 changed files with 154 additions and 31 deletions
+51 -15
View File
@@ -1499,6 +1499,46 @@ components:
type: string
required:
- type
ActivitySourceResponse:
type: object
description: "Where an activity or transaction came from: a character, a corporation\
\ wallet, or nothing (a manual entry)."
properties:
type:
type: string
description: The kind of source.
enum:
- CHARACTER
- CORPORATION
- NONE
example: CHARACTER
characterId:
type:
- integer
- "null"
format: int64
description: "EVE character id, when the source is a character."
example: 2112625428
corporationId:
type:
- integer
- "null"
format: int64
description: "EVE corporation id, when the source is a corporation."
example: 98000001
division:
type:
- integer
- "null"
format: int32
description: "Corporation wallet division (1-7), when the source is a corporation\
\ and it is known."
example: 4
required:
- characterId
- corporationId
- division
- type
IskTransferResponse:
allOf:
- $ref: "#/components/schemas/TransferResponse"
@@ -1563,13 +1603,10 @@ components:
format: uuid
description: Unique transaction identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
characterId:
type:
- integer
- "null"
format: int64
description: "EVE character id the transaction is attributed to, if any."
example: 2112625428
source:
$ref: "#/components/schemas/ActivitySourceResponse"
description: "Where the transaction is attributed: a character, a corporation,\
\ or nothing."
datetime:
type: string
format: date-time
@@ -1585,9 +1622,9 @@ components:
items:
$ref: "#/components/schemas/TransferResponse"
required:
- characterId
- datetime
- description
- source
- transactionId
- transfers
TransferResponse:
@@ -1649,17 +1686,16 @@ components:
format: uuid
description: Unique acquisition identifier.
example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
characterId:
type: integer
format: int64
description: EVE character id that made the acquisition.
example: 2112625428
source:
$ref: "#/components/schemas/ActivitySourceResponse"
description: "Where the acquisition came from: a character, a corporation,\
\ or nothing."
marketTypeId:
type: integer
format: int64
description: EVE market type (item) id that was acquired.
example: 34
source:
origin:
type: string
description: How the item entered the inventory.
enum:
@@ -1686,9 +1722,9 @@ components:
example: 5.42
required:
- acquisitionId
- characterId
- datetime
- marketTypeId
- origin
- quantity
- remaining
- source
+32
View File
@@ -0,0 +1,32 @@
<script setup lang="ts">
import {computedAsync} from "@vueuse/core";
import {ActivitySourceResponse, ActivitySourceResponseTypeEnum} from "@/generated/mammon";
import {CharacterLabel, useCharactersStore} from "@/characters";
import {CorporationLabel} from "@/corporations";
interface Props {
source: ActivitySourceResponse;
size?: number;
}
const props = withDefaults(defineProps<Props>(), {
size: 32
});
const charactersStore = useCharactersStore();
const character = computedAsync(async () => {
if (props.source.type === ActivitySourceResponseTypeEnum.Character && props.source.characterId) {
return await charactersStore.findById(props.source.characterId);
}
return undefined;
}, undefined);
</script>
<template>
<CharacterLabel v-if="source.type === ActivitySourceResponseTypeEnum.Character && character" :character="character" :size="size" />
<CorporationLabel v-else-if="source.type === ActivitySourceResponseTypeEnum.Corporation && source.corporationId" :corporation-id="source.corporationId" :division="source.division" :size="size" />
<span v-else class="text-slate-400">&mdash;</span>
</template>
+1
View File
@@ -0,0 +1 @@
export {default as SourceLabel} from './SourceLabel.vue';
+19
View File
@@ -0,0 +1,19 @@
<script setup lang="ts">
interface Props {
corporationId: number;
division?: number | null;
size?: number;
}
const props = withDefaults(defineProps<Props>(), {
size: 32
})
</script>
<template>
<div class="flex">
<img class="me-2" :src="`https://images.evetech.net/corporations/${corporationId}/logo?size=${size}`" />
<span>Corporation<template v-if="division"> &middot; Division {{ division }}</template></span>
</div>
</template>
+1
View File
@@ -0,0 +1 @@
export {default as CorporationLabel} from './CorporationLabel.vue';
+37 -7
View File
@@ -32,9 +32,9 @@ export interface AcquisitionResponse {
*/
'acquisitionId': string;
/**
* EVE character id that made the acquisition.
* Where the acquisition came from: a character, a corporation, or nothing.
*/
'characterId': number;
'source': ActivitySourceResponse;
/**
* EVE market type (item) id that was acquired.
*/
@@ -42,7 +42,7 @@ export interface AcquisitionResponse {
/**
* How the item entered the inventory.
*/
'source': AcquisitionResponseSourceEnum;
'origin': AcquisitionResponseOriginEnum;
/**
* When the acquisition occurred.
*/
@@ -61,12 +61,42 @@ export interface AcquisitionResponse {
'unitCost': number;
}
export const AcquisitionResponseSourceEnum = {
export const AcquisitionResponseOriginEnum = {
Bought: 'BOUGHT',
Manual: 'MANUAL',
} as const;
export type AcquisitionResponseSourceEnum = typeof AcquisitionResponseSourceEnum[keyof typeof AcquisitionResponseSourceEnum];
export type AcquisitionResponseOriginEnum = typeof AcquisitionResponseOriginEnum[keyof typeof AcquisitionResponseOriginEnum];
/**
* Where an activity or transaction came from: a character, a corporation wallet, or nothing (a manual entry).
*/
export interface ActivitySourceResponse {
/**
* The kind of source.
*/
'type': ActivitySourceResponseTypeEnum;
/**
* EVE character id, when the source is a character.
*/
'characterId': number | null;
/**
* EVE corporation id, when the source is a corporation.
*/
'corporationId': number | null;
/**
* Corporation wallet division (1-7), when the source is a corporation and it is known.
*/
'division': number | null;
}
export const ActivitySourceResponseTypeEnum = {
Character: 'CHARACTER',
Corporation: 'CORPORATION',
None: 'NONE',
} as const;
export type ActivitySourceResponseTypeEnum = typeof ActivitySourceResponseTypeEnum[keyof typeof ActivitySourceResponseTypeEnum];
/**
* The balance of a ledger: its ISK total plus the quantity of each item type held.
@@ -604,9 +634,9 @@ export interface TransactionResponse {
*/
'transactionId': string;
/**
* EVE character id the transaction is attributed to, if any.
* Where the transaction is attributed: a character, a corporation, or nothing.
*/
'characterId': number | null;
'source': ActivitySourceResponse;
/**
* When the transaction occurred.
*/
+2 -2
View File
@@ -1,7 +1,7 @@
import {defineStore} from "pinia";
import {computed, ref} from "vue";
import {acquisitionApi} from "@/mammon";
import {AcquisitionResponse, AcquisitionResponseSourceEnum} from "@/generated/mammon";
import {AcquisitionResponse, ActivitySourceResponse} from "@/generated/mammon";
export type AcquiredTypeSource = 'bo' | 'so' | 'prod' | 'misc';
@@ -12,7 +12,7 @@ export type RawAcquiredType = {
remaining: number;
price: number;
date: Date;
source: AcquisitionResponseSourceEnum;
source: ActivitySourceResponse;
}
const toAcquiredType = (a: AcquisitionResponse): RawAcquiredType => ({
+11 -7
View File
@@ -2,12 +2,13 @@
import {findAllTransactionInLeger, useLedgerParam} from "@/ledger";
import {computedAsync} from "@vueuse/core";
import {TransactionResponse} from "@/generated/mammon";
import {ActivitySourceResponseTypeEnum, TransactionResponse} from "@/generated/mammon";
import {IskLabel} from "@/market";
import {SortableHeader, useSort, VirtualScrollTable} from "@/components/table";
import {TransferList, TransferTypes} from "@/transaction";
import {Dropdown} from "@/components";
import {CharacterLabel, useCharactersStore} from "@/characters";
import {SourceLabel} from "@/activity";
import {useCharactersStore} from "@/characters";
import {formatEveDate} from "@/formaters.ts";
const {ledgerId} = useLedgerParam();
@@ -21,10 +22,13 @@ const transactions = computedAsync<TransactionResponse[]>(async () => {
}, []);
const { sortedArray, headerProps } = useSort(computedAsync(() => Promise.all(transactions.value.map(async transaction => {
const character = await charactersStore.findById(transaction.characterId);
const source = transaction.source;
const character = source.type === ActivitySourceResponseTypeEnum.Character && source.characterId
? await charactersStore.findById(source.characterId)
: undefined;
return {
character,
characterName: character?.name ?? "",
source,
sourceName: character?.name ?? (source.type === ActivitySourceResponseTypeEnum.Corporation ? "Corporation" : ""),
transactionId: transaction.transactionId,
description: transaction.description,
date: new Date(transaction.datetime),
@@ -59,7 +63,7 @@ const getIskBalance = (transaction: TransactionResponse) => {
<template #default="{ list }">
<thead>
<tr>
<SortableHeader v-bind="headerProps" sortKey="characterName">Character</SortableHeader>
<SortableHeader v-bind="headerProps" sortKey="sourceName">Source</SortableHeader>
<SortableHeader v-bind="headerProps" sortKey="date">Date</SortableHeader>
<SortableHeader v-bind="headerProps" sortKey="balance">Isk Change</SortableHeader>
<SortableHeader v-bind="headerProps" sortKey="description">Description</SortableHeader>
@@ -68,7 +72,7 @@ const getIskBalance = (transaction: TransactionResponse) => {
<tbody>
<tr v-for="t in list" :key="t.data.transactionId">
<td>
<CharacterLabel v-if="t.data.character" :character="t.data.character" />
<SourceLabel :source="t.data.source" />
</td>
<td>
<Dropdown class="transfer-dropdown">