scan front

This commit is contained in:
Sirttas
2026-06-11 20:32:46 +02:00
parent dd031551ca
commit 7ca38aee70
11 changed files with 155 additions and 175 deletions
+67
View File
@@ -0,0 +1,67 @@
<script setup lang="ts">
import { getMarketTypes, TaxInput, useApraisalStore, useMarketTaxStore } from "@/market";
import { BuyModal } from '@/market/acquisition';
import { ScanResult, ScanResultTable, toScanResult } from '@/market/scan';
import { marketApi } from "@/mammon";
import { useStorage } from "@vueuse/core";
import { ref, watch } from 'vue';
const buyModal = ref<typeof BuyModal>();
const apraisalStore = useApraisalStore();
const marketTaxStore = useMarketTaxStore();
const days = useStorage('market-scan-days', 365);
const items = ref<ScanResult[]>([]);
const loading = ref(false);
const scan = async () => {
loading.value = true;
try {
const { data } = await marketApi.scanMarket(
String(days.value),
String(marketTaxStore.brokerFee / 100),
String(marketTaxStore.scc / 100)
);
const types = await getMarketTypes(data.map(r => r.marketTypeId));
const prices = await apraisalStore.getPrices(types);
items.value = data.flatMap(r => {
const type = types.find(t => t.id === r.marketTypeId);
const price = prices.find(p => p.type.id === r.marketTypeId);
return type && price ? [toScanResult(r, type, price)] : [];
});
} finally {
loading.value = false;
}
}
watch([days, () => marketTaxStore.brokerFee, () => marketTaxStore.scc], scan, { immediate: true });
</script>
<template>
<div class="flex mb-2 mt-4">
<div class="flex justify-self-end ms-auto">
<TaxInput />
<div class="end">
<span>Days: </span>
<input type="number" min="1" max="365" step="1" v-model="days" />
</div>
</div>
</div>
<hr />
<div v-if="loading" class="text-center mt-4">
<span>Scanning market</span>
</div>
<template v-else>
<ScanResultTable :items="items" @buy="(type, buy, sell) => buyModal?.open(type, { 'Buy': buy, 'Sell': sell })" />
<BuyModal ref="buyModal" />
</template>
</template>
<style scoped>
@reference "@/style.css";
div.end {
@apply justify-self-end ms-2;
}
</style>
-82
View File
@@ -1,82 +0,0 @@
<script setup lang="ts">
import { Modal, ProgressBar } from "@/components";
import { MarketType, MarketTypeInput, MarketTypePrice, getHistory, getMarketTypes, useApraisalStore } from "@/market";
import { BuyModal } from '@/market/acquisition';
import { TrackingResult, TrackingResultTable, createResult, useMarketTrackingStore } from '@/market/tracking';
import { ref, watch } from 'vue';
const buyModal = ref<typeof BuyModal>();
const item = ref<MarketType>();
const apraisalStore = useApraisalStore();
const marketTrackingStore = useMarketTrackingStore();
const items = ref<TrackingResult[]>([]);
const addOrRelaod = async (type: MarketType) => {
const typeID = type.id;
const [history, price] = await Promise.all([
getHistory(typeID),
apraisalStore.getPrice(type)
]);
const itm = {
type,
history,
buy: price.buy,
sell: price.sell,
orderCount: price.orderCount
};
if (items.value.some(i => i.type.id === typeID)) {
items.value = items.value.map(i => i.type.id === typeID ? itm : i);
} else {
items.value = [ ...items.value, itm];
marketTrackingStore.addType(typeID);
}
}
const addItem = async () => {
if (!item.value) {
// TODO error
return;
}
addOrRelaod(item.value);
item.value = undefined;
}
const removeItem = (type: MarketType) => {
items.value = items.value.filter(i => i.type.id !== type.id);
marketTrackingStore.removeType(type.id);
}
watch(() => marketTrackingStore.types, async t => {
const typesToLoad = t.filter(t => !items.value.some(i => i.type.id === t));
if (typesToLoad.length === 0) {
return;
}
const prices = await apraisalStore.getPrices(await getMarketTypes(typesToLoad));
typesToLoad.forEach(async i => items.value.push(await createResult(i, prices.find(p => p.type.id === i) as MarketTypePrice)));
}, { immediate: true });
</script>
<template>
<div class="grid mb-2 mt-4">
<div class="w-auto flex">
<span>Item: </span>
<MarketTypeInput class="ms-2" v-model="item" @submit="addItem"/>
<button class="justify-self-end ms-2" @click="addItem">Add</button>
</div>
</div>
<template v-if="items.length > 0">
<hr />
<TrackingResultTable :items="items" @buy="(type, buy, sell) => buyModal?.open(type, { 'Buy': buy, 'Sell': sell })" @remove="removeItem" />
<BuyModal ref="buyModal" />
<Modal :open="items.length > 0 && items.length < marketTrackingStore.types.length">
<div class="ms-auto me-auto mb-2 w-96">
<ProgressBar :value="items.length" :total="marketTrackingStore.types.length" />
</div>
</Modal>
</template>
</template>
+8 -23
View File
@@ -1,14 +1,14 @@
<script setup lang="ts">
import {ClipboardButton} from '@/components';
import {getMarketType, MarketType, MarketTypeInput, useApraisalStore} from "@/market";
import {getMarketType, MarketType, MarketTypeInput, useApraisalStore, useMarketTaxStore} from "@/market";
import {AcquisitionResultTable, BuyModal, useAcquiredTypesStore} from '@/market/acquisition';
import {createResult, TrackingResultTable, useMarketTrackingStore} from '@/market/tracking';
import {BookmarkIcon, BookmarkSlashIcon, ShoppingCartIcon} from '@heroicons/vue/24/outline';
import {buildScanResult, ScanResultTable} from '@/market/scan';
import {ShoppingCartIcon} from '@heroicons/vue/24/outline';
import log from "loglevel";
import {computed, ref, watch} from "vue";
import {useRoute, useRouter} from "vue-router";
import {routeNames} from "@/routes";
import {computedAsync} from "@vueuse/core";
import {computedAsync, useStorage} from "@vueuse/core";
const buyModal = ref<typeof BuyModal>();
@@ -18,12 +18,12 @@ const item = ref<MarketType>();
const inputItem = ref<MarketType>();
const apraisalStore = useApraisalStore();
const marketTaxStore = useMarketTaxStore();
const days = useStorage('market-scan-days', 365);
const price = computedAsync(() => item.value ? apraisalStore.getPrice(item.value) : undefined);
const marketTrackingStore = useMarketTrackingStore();
const result = computedAsync(async () => item.value && price.value ? await createResult(item.value?.id, price.value) : undefined);
const result = computedAsync(async () => price.value ? await buildScanResult(price.value, days.value, marketTaxStore.calculateProfit) : undefined);
const acquiredTypesStore = useAcquiredTypesStore();
const isTracked = computed(() => item.value ? marketTrackingStore.types.includes(item.value.id) : false);
const acquisitions = computed(() => {
const p = price.value;
@@ -36,17 +36,6 @@ const acquisitions = computed(() => {
sell: p.sell
}));
});
const toogleTracking = () => {
if (!item.value) {
return;
}
if (isTracked.value) {
marketTrackingStore.removeType(item.value.id);
} else {
marketTrackingStore.addType(item.value.id);
}
}
const view = () => {
if (!inputItem.value) {
return;
@@ -93,10 +82,6 @@ watch(useRoute(), async route => {
<div class="ms-auto">
<ClipboardButton class="ms-1" :value="item.name" />
<button v-if="price" class="btn-icon ms-1" title="Add acquisitions" @click="buyModal?.open(item, { 'Buy': price.buy, 'Sell': price.sell })"><ShoppingCartIcon /></button>
<button class="btn-icon ms-1" :title="isTracked ? 'Untrack' : 'Track'" @click="toogleTracking">
<BookmarkSlashIcon v-if="isTracked" />
<BookmarkIcon v-else />
</button>
</div>
</div>
<p v-if="item.description" class="text-sm">{{ item.description }}</p>
@@ -104,7 +89,7 @@ watch(useRoute(), async route => {
</div>
<div v-if="result" class="mb-4">
<span>Market Info:</span>
<TrackingResultTable :items="[result]" infoOnly :ignoredColums="['name', 'acquisitions']" />
<ScanResultTable :items="[result]" infoOnly :ignoredColums="['name', 'acquisitions']" />
</div>
<div v-if="acquisitions && acquisitions.length > 0">
<span>Acquisitions:</span>