Add assets chart hover so the portfolio line shows the value under the pointer. Co-authored-by: Cursor <cursoragent@cursor.com>
140 lines
4.0 KiB
TypeScript
140 lines
4.0 KiB
TypeScript
import { computed, ref, watch, type ComputedRef } from 'vue'
|
|
import {
|
|
fetchZaanboxPriceHistory,
|
|
type LandingHistoryRange,
|
|
} from '@/api/zaarboxPriceHistory'
|
|
import { buildSeriesChartPaths } from '@/utils/chartAreaPaths'
|
|
import { usePrice } from '@/utils/usePrice'
|
|
const CHART_RANGE_OPTIONS = [
|
|
{ value: 'DAILY' as const, shortLabel: '۲۴ ساعت', label: 'ارزش دارایی — ۲۴ ساعت گذشته' },
|
|
{ value: 'WEEKLY' as const, shortLabel: 'هفته', label: 'ارزش دارایی — هفته گذشته' },
|
|
{ value: 'MONTHLY' as const, shortLabel: 'ماه', label: 'ارزش دارایی — ماه گذشته' },
|
|
]
|
|
|
|
export function useAssetsPortfolioChart(
|
|
cashRial: ComputedRef<number>,
|
|
metalRial: ComputedRef<number>,
|
|
) {
|
|
const chartRange = ref<LandingHistoryRange>('MONTHLY')
|
|
const chartDrawn = ref(false)
|
|
const chartLoading = ref(false)
|
|
const priceHistory = ref<Array<{ timeMs: number; price: number }>>([])
|
|
|
|
async function loadChartHistory(range: LandingHistoryRange) {
|
|
chartLoading.value = true
|
|
try {
|
|
priceHistory.value = await fetchZaanboxPriceHistory(range)
|
|
} catch {
|
|
priceHistory.value = []
|
|
} finally {
|
|
chartLoading.value = false
|
|
chartDrawn.value = true
|
|
}
|
|
}
|
|
|
|
function setChartRange(range: LandingHistoryRange) {
|
|
chartHover.value = null
|
|
chartRange.value = range
|
|
void loadChartHistory(range)
|
|
}
|
|
|
|
const portfolioSeries = computed(() => {
|
|
const cash = Math.max(0, cashRial.value)
|
|
const metal = Math.max(0, metalRial.value)
|
|
const prices = priceHistory.value
|
|
|
|
if (prices.length < 2) {
|
|
const total = cash + metal
|
|
if (total <= 0) return []
|
|
return prices.map((point) => ({ timeMs: point.timeMs, value: total }))
|
|
}
|
|
|
|
const refPrice = prices[prices.length - 1]?.price
|
|
if (!refPrice || metal <= 0) {
|
|
const total = cash + metal
|
|
return prices.map((point) => ({ timeMs: point.timeMs, value: total }))
|
|
}
|
|
|
|
return prices.map((point) => ({
|
|
timeMs: point.timeMs,
|
|
value: Math.round(cash + metal * (point.price / refPrice)),
|
|
}))
|
|
})
|
|
|
|
const portfolioChart = computed(() => buildSeriesChartPaths(portfolioSeries.value))
|
|
|
|
const chartHover = ref<{
|
|
xPercent: number
|
|
yPercent: number
|
|
valueLabel: string
|
|
timeLabel: string
|
|
} | null>(null)
|
|
|
|
function formatChartTimeLabel(timeMs: number) {
|
|
try {
|
|
return new Intl.DateTimeFormat('fa-IR', {
|
|
timeZone: 'Asia/Tehran',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
}).format(new Date(timeMs))
|
|
} catch {
|
|
return ''
|
|
}
|
|
}
|
|
|
|
function onChartPointerMove(event: PointerEvent) {
|
|
const chart = portfolioChart.value
|
|
if (!chart.hasData || chart.samples.length < 2) {
|
|
chartHover.value = null
|
|
return
|
|
}
|
|
const target = event.currentTarget as HTMLElement | null
|
|
if (!target) return
|
|
const rect = target.getBoundingClientRect()
|
|
if (rect.width <= 0 || rect.height <= 0) return
|
|
const ratio = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width))
|
|
const index = Math.round(ratio * (chart.samples.length - 1))
|
|
const sample = chart.samples[index]
|
|
if (!sample) return
|
|
chartHover.value = {
|
|
xPercent: (sample.x / chart.width) * 100,
|
|
yPercent: (sample.y / chart.height) * 100,
|
|
valueLabel: usePrice(sample.value),
|
|
timeLabel: formatChartTimeLabel(sample.timeMs),
|
|
}
|
|
}
|
|
|
|
function onChartPointerLeave() {
|
|
chartHover.value = null
|
|
}
|
|
|
|
watch(
|
|
() => cashRial.value + metalRial.value,
|
|
(total) => {
|
|
if (total > 0 && priceHistory.value.length === 0) {
|
|
void loadChartHistory(chartRange.value)
|
|
}
|
|
},
|
|
{ flush: 'post' },
|
|
)
|
|
|
|
async function initAssetsPortfolioChart() {
|
|
await loadChartHistory(chartRange.value)
|
|
}
|
|
|
|
return {
|
|
chartRange,
|
|
chartRangeOptions: CHART_RANGE_OPTIONS,
|
|
chartDrawn,
|
|
chartLoading,
|
|
portfolioChart,
|
|
chartHover,
|
|
onChartPointerMove,
|
|
onChartPointerLeave,
|
|
setChartRange,
|
|
initAssetsPortfolioChart,
|
|
}
|
|
}
|