feat(convert,assets): convert page route and assets portfolio chart

Add convert mobile/desktop views and assets chart from price history; polish calculator and wallet/home integration.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-22 17:38:27 +03:30
co-authored by Cursor
parent 819e007e60
commit 20c871e66c
23 changed files with 872 additions and 131 deletions
@@ -0,0 +1,87 @@
import { computed, ref, watch, type ComputedRef } from 'vue'
import {
fetchZaanboxPriceHistory,
type LandingHistoryRange,
} from '@/api/zaarboxPriceHistory'
import { buildSeriesChartPaths } from '@/utils/chartAreaPaths'
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) {
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))
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,
setChartRange,
initAssetsPortfolioChart,
}
}