feat(#39): Add a Statistics tab to the ledger view
This commit is contained in:
+55
-1
@@ -904,6 +904,34 @@ paths:
|
||||
$ref: "#/components/schemas/TransactionResponse"
|
||||
"404":
|
||||
description: No ledger with this id
|
||||
/ledgers/{ledgerId}/profit:
|
||||
get:
|
||||
tags:
|
||||
- transaction
|
||||
summary: Get the daily profit series for a single ledger
|
||||
operationId: profitPerDayInLedger
|
||||
parameters:
|
||||
- name: ledgerId
|
||||
in: path
|
||||
description: Id of the ledger
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: "Daily profit for the ledger, ascending by date, only for days\
|
||||
\ with ISK activity"
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/DailyProfitResponse"
|
||||
"400":
|
||||
description: The ledger is the system ledger or a combined ledger
|
||||
"404":
|
||||
description: No ledger with this id
|
||||
/ledgers/{ledgerId}/balance:
|
||||
get:
|
||||
tags:
|
||||
@@ -1682,7 +1710,7 @@ components:
|
||||
type: string
|
||||
format: date-time
|
||||
description: When the order was issued.
|
||||
example: 2026-06-22T05:12:25Z
|
||||
example: 2026-06-22T05:12:25
|
||||
escrow:
|
||||
type: number
|
||||
description: ISK held in escrow for a buy order; null for sell orders.
|
||||
@@ -1910,6 +1938,32 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
DailyProfitResponse:
|
||||
type: object
|
||||
description: "Profit for a single day: ISK in, ISK out, and their difference."
|
||||
properties:
|
||||
date:
|
||||
type: string
|
||||
format: date
|
||||
description: The UTC (EVE) day this profit is for.
|
||||
example: 2026-06-22
|
||||
iskIn:
|
||||
type: number
|
||||
description: Total ISK that entered the ledger on this day.
|
||||
example: 1500000.0
|
||||
iskOut:
|
||||
type: number
|
||||
description: Total ISK that left the ledger on this day.
|
||||
example: 500000.0
|
||||
profit:
|
||||
type: number
|
||||
description: "Profit for the day: iskIn minus iskOut."
|
||||
example: 1000000.0
|
||||
required:
|
||||
- date
|
||||
- iskIn
|
||||
- iskOut
|
||||
- profit
|
||||
BalanceResponse:
|
||||
type: object
|
||||
description: "The balance of a ledger: its ISK total plus the quantity of each\
|
||||
|
||||
Generated
+30
@@ -14,6 +14,7 @@
|
||||
"@vueuse/integrations": "^14.3.0",
|
||||
"@vueuse/router": "^14.3.0",
|
||||
"axios": "^1.4.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"gemory": "file:",
|
||||
"loglevel": "^1.8.1",
|
||||
"loglevel-plugin-prefix": "^0.8.4",
|
||||
@@ -21,6 +22,7 @@
|
||||
"pinia": "^3.0.4",
|
||||
"sortablejs": "^1.15.7",
|
||||
"vue": "^3.3.4",
|
||||
"vue-chartjs": "^5.3.4",
|
||||
"vue-router": "^5.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -229,6 +231,12 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@kurkle/color": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
|
||||
@@ -1483,6 +1491,18 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
|
||||
@@ -2975,6 +2995,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-chartjs": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.4.tgz",
|
||||
"integrity": "sha512-x3Fqob8RQvrTdssfi9ecsCzEkFOd8JPmNwSkSQzdfKj/uBsRJs/Y88cZcZIEcPsTVfMGwMo4MOoihoDG2DoE/g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"chart.js": "^4.1.1",
|
||||
"vue": "^3.0.0-0 || ^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.1.0.tgz",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"@vueuse/integrations": "^14.3.0",
|
||||
"@vueuse/router": "^14.3.0",
|
||||
"axios": "^1.4.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"gemory": "file:",
|
||||
"loglevel": "^1.8.1",
|
||||
"loglevel-plugin-prefix": "^0.8.4",
|
||||
@@ -23,6 +24,7 @@
|
||||
"pinia": "^3.0.4",
|
||||
"sortablejs": "^1.15.7",
|
||||
"vue": "^3.3.4",
|
||||
"vue-chartjs": "^5.3.4",
|
||||
"vue-router": "^5.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, watch} from "vue";
|
||||
|
||||
const props = withDefaults(defineProps<{ min?: number; max?: number }>(), {
|
||||
min: 0,
|
||||
max: 100,
|
||||
});
|
||||
|
||||
const from = defineModel<number>('from', {default: 0});
|
||||
const to = defineModel<number>('to', {default: 0});
|
||||
|
||||
const container = ref<HTMLElement>();
|
||||
|
||||
const dragFill = (event: PointerEvent) => {
|
||||
const rect = container.value?.getBoundingClientRect();
|
||||
|
||||
if (!rect) {
|
||||
return;
|
||||
}
|
||||
|
||||
const span = (props.max - props.min) || 1;
|
||||
const width = to.value - from.value;
|
||||
const startX = event.clientX;
|
||||
const startFrom = from.value;
|
||||
|
||||
const onMove = (moveEvent: PointerEvent) => {
|
||||
const delta = Math.round(((moveEvent.clientX - startX) / rect.width) * span);
|
||||
const newFrom = Math.min(Math.max(startFrom + delta, props.min), props.max - width);
|
||||
|
||||
from.value = newFrom;
|
||||
to.value = newFrom + width;
|
||||
};
|
||||
|
||||
const onUp = () => {
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
};
|
||||
|
||||
window.addEventListener('pointermove', onMove);
|
||||
window.addEventListener('pointerup', onUp);
|
||||
};
|
||||
|
||||
watch(from, value => {
|
||||
if (value > to.value) {
|
||||
to.value = value;
|
||||
}
|
||||
});
|
||||
watch(to, value => {
|
||||
if (value < from.value) {
|
||||
from.value = value;
|
||||
}
|
||||
});
|
||||
|
||||
const fillStyle = computed(() => {
|
||||
const span = (props.max - props.min) || 1;
|
||||
|
||||
return {
|
||||
left: `${((from.value - props.min) / span) * 100}%`,
|
||||
width: `${((to.value - from.value) / span) * 100}%`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="container" class="dual-range relative h-6 select-none">
|
||||
<div class="absolute inset-x-0 top-1/2 h-1 -translate-y-1/2 rounded-full bg-gray-700 pointer-events-none"></div>
|
||||
<div class="absolute top-1/2 h-2 -translate-y-1/2 rounded-full bg-emerald-500 cursor-grab active:cursor-grabbing"
|
||||
:style="fillStyle" @pointerdown="dragFill"></div>
|
||||
<input type="range" :min="min" :max="max" v-model.number="from"
|
||||
class="absolute inset-0 w-full h-full m-0 bg-transparent appearance-none pointer-events-none" />
|
||||
<input type="range" :min="min" :max="max" v-model.number="to"
|
||||
class="absolute inset-0 w-full h-full m-0 bg-transparent appearance-none pointer-events-none" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@reference "@/style.css";
|
||||
|
||||
.dual-range input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
@apply w-4 h-4 rounded-full bg-emerald-500 border-2 border-gray-200 cursor-pointer pointer-events-auto;
|
||||
}
|
||||
|
||||
.dual-range input[type="range"]::-moz-range-thumb {
|
||||
@apply w-4 h-4 rounded-full bg-emerald-500 border-2 border-gray-200 cursor-pointer pointer-events-auto;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
export { default as ClipboardButton } from './ClipboardButton.vue';
|
||||
export { default as Dropdown } from './Dropdown.vue';
|
||||
export { default as DualRangeSlider } from './DualRangeSlider.vue';
|
||||
export { default as LoadingSpinner } from './LoadingSpinner.vue';
|
||||
export { default as Modal } from './Modal.vue';
|
||||
export { default as ProgressBar } from './ProgressBar.vue';
|
||||
|
||||
@@ -190,6 +190,27 @@ export interface CreateMainLedgerRequest {
|
||||
*/
|
||||
'name': string;
|
||||
}
|
||||
/**
|
||||
* Profit for a single day: ISK in, ISK out, and their difference.
|
||||
*/
|
||||
export interface DailyProfitResponse {
|
||||
/**
|
||||
* The UTC (EVE) day this profit is for.
|
||||
*/
|
||||
'date': string;
|
||||
/**
|
||||
* Total ISK that entered the ledger on this day.
|
||||
*/
|
||||
'iskIn': number;
|
||||
/**
|
||||
* Total ISK that left the ledger on this day.
|
||||
*/
|
||||
'iskOut': number;
|
||||
/**
|
||||
* Profit for the day: iskIn minus iskOut.
|
||||
*/
|
||||
'profit': number;
|
||||
}
|
||||
/**
|
||||
* A market location (NPC station) with its containing solar system and region.
|
||||
*/
|
||||
@@ -3707,6 +3728,40 @@ export const TransactionApiAxiosParamCreator = function (configuration?: Configu
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Get the daily profit series for a single ledger
|
||||
* @param {string} ledgerId Id of the ledger
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
profitPerDayInLedger: async (ledgerId: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'ledgerId' is not null or undefined
|
||||
assertParamExists('profitPerDayInLedger', 'ledgerId', ledgerId)
|
||||
const localVarPath = `/ledgers/{ledgerId}/profit`
|
||||
.replace('{ledgerId}', encodeURIComponent(String(ledgerId)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
localVarHeaderParameter['Accept'] = '*/*';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
@@ -3734,6 +3789,19 @@ export const TransactionApiFp = function(configuration?: Configuration) {
|
||||
const localVarOperationServerBasePath = operationServerMap['TransactionApi.finAllTransactionsInLedger']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Get the daily profit series for a single ledger
|
||||
* @param {string} ledgerId Id of the ledger
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async profitPerDayInLedger(ledgerId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<DailyProfitResponse>>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.profitPerDayInLedger(ledgerId, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['TransactionApi.profitPerDayInLedger']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3753,6 +3821,16 @@ export const TransactionApiFactory = function (configuration?: Configuration, ba
|
||||
finAllTransactionsInLedger(ledgerId: string, options?: RawAxiosRequestConfig): AxiosPromise<Array<TransactionResponse>> {
|
||||
return localVarFp.finAllTransactionsInLedger(ledgerId, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Get the daily profit series for a single ledger
|
||||
* @param {string} ledgerId Id of the ledger
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
profitPerDayInLedger(ledgerId: string, options?: RawAxiosRequestConfig): AxiosPromise<Array<DailyProfitResponse>> {
|
||||
return localVarFp.profitPerDayInLedger(ledgerId, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3770,6 +3848,17 @@ export class TransactionApi extends BaseAPI {
|
||||
public finAllTransactionsInLedger(ledgerId: string, options?: RawAxiosRequestConfig) {
|
||||
return TransactionApiFp(this.configuration).finAllTransactionsInLedger(ledgerId, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary Get the daily profit series for a single ledger
|
||||
* @param {string} ledgerId Id of the ledger
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public profitPerDayInLedger(ledgerId: string, options?: RawAxiosRequestConfig) {
|
||||
return TransactionApiFp(this.configuration).profitPerDayInLedger(ledgerId, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {RouterLink, RouterView} from 'vue-router';
|
||||
import {LedgerLabel, useLedgerParam} from "@/ledger";
|
||||
import {isMain, LedgerLabel, useLedgerParam} from "@/ledger";
|
||||
import {routeNames} from "@/routes.ts";
|
||||
import {IskLabel} from "@/market";
|
||||
|
||||
@@ -28,6 +28,9 @@ const {ledger} = useLedgerParam();
|
||||
<RouterLink :to="{name: routeNames.listLedgerAcquisitions}" class="tab">
|
||||
<span>Acquisitions</span>
|
||||
</RouterLink>
|
||||
<RouterLink v-if="isMain(ledger)" :to="{name: routeNames.viewLedgerStatistics}" class="tab">
|
||||
<span>Statistics</span>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<RouterView />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, watch} from "vue";
|
||||
import {
|
||||
BarController,
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
Chart as ChartJS,
|
||||
type ChartData,
|
||||
type ChartDataset,
|
||||
type ChartOptions,
|
||||
Filler,
|
||||
Legend,
|
||||
LinearScale,
|
||||
LineController,
|
||||
LineElement,
|
||||
PointElement,
|
||||
Tooltip,
|
||||
} from "chart.js";
|
||||
import {Chart} from "vue-chartjs";
|
||||
import {useLedgerParam} from "@/ledger";
|
||||
import {transactionApi} from "@/mammon";
|
||||
import {useProcessedResource} from "@/activity";
|
||||
import {DailyProfitResponse} from "@/generated/mammon";
|
||||
import {IskLabel} from "@/market";
|
||||
import {formatIsk} from "@/formaters";
|
||||
import {DualRangeSlider} from "@/components";
|
||||
|
||||
ChartJS.register(
|
||||
CategoryScale, LinearScale,
|
||||
BarController, BarElement,
|
||||
LineController, LineElement, PointElement,
|
||||
Tooltip, Legend, Filler,
|
||||
);
|
||||
|
||||
const {ledgerId} = useLedgerParam();
|
||||
|
||||
const profits = useProcessedResource<DailyProfitResponse[]>(async () => {
|
||||
if (!ledgerId.value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const {data} = await transactionApi.profitPerDayInLedger(ledgerId.value);
|
||||
|
||||
return data;
|
||||
}, []);
|
||||
|
||||
const from = ref(0);
|
||||
const to = ref(0);
|
||||
|
||||
watch(() => profits.value.length, length => {
|
||||
from.value = 0;
|
||||
to.value = Math.max(length - 1, 0);
|
||||
}, {immediate: true});
|
||||
|
||||
const maxIndex = computed(() => Math.max(profits.value.length - 1, 0));
|
||||
|
||||
const range = computed(() => profits.value.slice(from.value, to.value + 1));
|
||||
|
||||
const total = computed(() => range.value.reduce((sum, p) => sum + p.profit, 0));
|
||||
|
||||
const cumulativeProfit = computed(() => {
|
||||
let running = 0;
|
||||
|
||||
return range.value.map(p => (running += p.profit));
|
||||
});
|
||||
|
||||
const chartData = computed<ChartData<'bar' | 'line'>>(() => {
|
||||
const cumulative: ChartDataset<'line'> = {
|
||||
type: 'line',
|
||||
label: 'Cumulative profit',
|
||||
data: cumulativeProfit.value,
|
||||
borderColor: '#38bdf8',
|
||||
backgroundColor: 'rgba(56, 189, 248, 0.15)',
|
||||
pointBackgroundColor: '#38bdf8',
|
||||
fill: true,
|
||||
tension: 0.2,
|
||||
yAxisID: 'y',
|
||||
order: 0,
|
||||
};
|
||||
|
||||
const iskIn: ChartDataset<'bar'> = {
|
||||
type: 'bar',
|
||||
label: 'ISK in',
|
||||
data: range.value.map(p => p.iskIn),
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.7)',
|
||||
yAxisID: 'y',
|
||||
order: 1,
|
||||
};
|
||||
|
||||
const iskOut: ChartDataset<'bar'> = {
|
||||
type: 'bar',
|
||||
label: 'ISK out',
|
||||
data: range.value.map(p => -p.iskOut),
|
||||
backgroundColor: 'rgba(180, 83, 9, 0.7)',
|
||||
yAxisID: 'y',
|
||||
order: 1,
|
||||
};
|
||||
|
||||
return {
|
||||
labels: range.value.map(p => p.date),
|
||||
datasets: [cumulative, iskIn, iskOut],
|
||||
};
|
||||
});
|
||||
|
||||
const chartOptions = computed<ChartOptions<'bar' | 'line'>>(() => ({
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {mode: 'index', intersect: false},
|
||||
plugins: {
|
||||
legend: {display: true},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: ctx => `${ctx.dataset.label}: ${formatIsk(Math.abs(ctx.parsed.y ?? 0))}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
},
|
||||
y: {
|
||||
position: 'left',
|
||||
stacked: true,
|
||||
ticks: {
|
||||
callback: value => formatIsk(Math.abs(Number(value))),
|
||||
},
|
||||
}
|
||||
},
|
||||
}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<div class="flex justify-end mb-4">
|
||||
<span class="mr-2">Total profit:</span>
|
||||
<IskLabel :amount="total" />
|
||||
</div>
|
||||
|
||||
<p v-if="profits.length === 0" class="text-center text-gray-400 mt-8">
|
||||
No trade profit recorded for this ledger
|
||||
</p>
|
||||
<template v-else>
|
||||
<div class="h-96">
|
||||
<Chart type="bar" :data="chartData" :options="chartOptions" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 mt-4">
|
||||
<span class="text-sm w-24 text-gray-400">{{ profits[from]?.date }}</span>
|
||||
<DualRangeSlider class="grow" :min="0" :max="maxIndex" v-model:from="from" v-model:to="to" />
|
||||
<span class="text-sm w-24 text-right text-gray-400">{{ profits[to]?.date }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -13,6 +13,7 @@ export const routeNames = {
|
||||
viewLedgerBalance: 'view-ledger-balance',
|
||||
listLedgerTransactions: 'list-ledger-transactions',
|
||||
listLedgerAcquisitions: 'list-ledger-acquisitions',
|
||||
viewLedgerStatistics: 'view-ledger-statistics',
|
||||
editRuleBook: 'edit-rule-book',
|
||||
marketTypes: 'market-types',
|
||||
appraise: 'appraise',
|
||||
@@ -31,6 +32,7 @@ export const routes: RouteRecordRaw[] = [
|
||||
{path: 'balance', name: routeNames.viewLedgerBalance, component: () => import('@/pages/ledger/ViewLedgerBalance.vue')},
|
||||
{path: 'transactions', name: routeNames.listLedgerTransactions, component: () => import('@/pages/ledger/ListLedgerTransactions.vue')},
|
||||
{path: 'acquisitions', name: routeNames.listLedgerAcquisitions, component: () => import('@/pages/ledger/ViewLedgerAcquisitions.vue')},
|
||||
{path: 'statistics', name: routeNames.viewLedgerStatistics, component: () => import('@/pages/ledger/ViewLedgerStatistics.vue')},
|
||||
]},
|
||||
]},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user