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
@@ -164,8 +164,9 @@ function formatRings(value: number) {
.copper-convert {
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: 0.75rem;
text-align: right;
color: #334155;
}
.copper-convert__lead {
@@ -175,28 +176,13 @@ function formatRings(value: number) {
color: #64748b;
}
.copper-convert__panel {
position: relative;
padding: 0.75rem 0.875rem 0.7rem;
border-radius: 1rem;
background: linear-gradient(145deg, #0f2744 0%, #152f4f 48%, #1a365d 100%);
border: 1px solid rgba(201, 160, 98, 0.35);
box-shadow:
0 12px 28px rgba(15, 39, 68, 0.22),
inset 0 1px 0 rgba(255, 255, 255, 0.06);
overflow: hidden;
}
.copper-convert__panel::before {
content: '';
position: absolute;
top: -40px;
inset-inline-end: -30px;
width: 120px;
height: 120px;
border-radius: 50%;
background: radial-gradient(circle, rgba(232, 180, 140, 0.28) 0%, transparent 70%);
pointer-events: none;
.copper-convert__panel,
.copper-convert__form,
.copper-convert__result {
padding: 0.875rem;
border-radius: 0.875rem;
background: #fff;
border: 1px solid #e5e7eb;
}
.copper-convert__panel-head {
@@ -208,17 +194,18 @@ function formatRings(value: number) {
margin-bottom: 0.75rem;
}
.copper-convert__panel-kicker {
.copper-convert__panel-kicker,
.copper-convert__result-kicker {
font-size: 0.6875rem;
font-weight: 700;
letter-spacing: 0.04em;
color: rgba(201, 160, 98, 0.95);
color: var(--colorPrimary, #2563eb);
}
.copper-convert__panel-title {
.copper-convert__panel-title,
.copper-convert__result-title {
font-size: 0.9375rem;
font-weight: 800;
color: #f8fafc;
color: #0f172a;
}
.copper-convert__price-group {
@@ -230,7 +217,7 @@ function formatRings(value: number) {
.copper-convert__price-group--wire {
margin-bottom: 0;
padding-top: 0.65rem;
border-top: 1px solid rgba(255, 255, 255, 0.08);
border-top: 1px solid #f1f5f9;
}
.copper-convert__group-label {
@@ -238,7 +225,7 @@ function formatRings(value: number) {
margin-bottom: 0.45rem;
font-size: 0.6875rem;
font-weight: 600;
color: rgba(226, 232, 240, 0.75);
color: #64748b;
}
.copper-convert__price-grid {
@@ -258,29 +245,29 @@ function formatRings(value: number) {
min-width: 0;
padding: 0.45rem 0.5rem;
border-radius: 0.65rem;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.1);
background: #f8fafc;
border: 1px solid #e5e7eb;
}
.copper-convert__chip--accent {
background: rgba(201, 160, 98, 0.12);
border-color: rgba(201, 160, 98, 0.35);
background: #eff5f3;
border-color: #d4e3de;
}
.copper-convert__chip--muted .copper-convert__chip-value {
color: rgba(248, 250, 252, 0.45);
color: #94a3b8;
}
.copper-convert__chip-label {
font-size: 0.625rem;
line-height: 1.45;
color: rgba(226, 232, 240, 0.85);
color: #64748b;
}
.copper-convert__chip-value {
font-size: 0.6875rem;
font-weight: 700;
color: #f8fafc;
color: #0f172a;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -290,11 +277,6 @@ function formatRings(value: number) {
display: flex;
flex-direction: column;
gap: 0.65rem;
margin-top: 0.125rem;
padding: 0.75rem 0.875rem;
border-radius: 1rem;
background: #faf8f5;
border: 1px solid rgba(201, 160, 98, 0.22);
}
.copper-convert__field-label {
@@ -327,9 +309,9 @@ function formatRings(value: number) {
}
.copper-convert__pill--active {
color: #0f2744;
color: var(--colorPrimary, #2563eb);
background: #fff;
box-shadow: 0 2px 8px rgba(15, 39, 68, 0.12);
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.08);
}
.copper-convert__weight-wrap {
@@ -352,7 +334,7 @@ function formatRings(value: number) {
background: transparent;
font-size: 1.125rem;
font-weight: 800;
color: #0f2744;
color: #0f172a;
text-align: right;
direction: ltr;
}
@@ -367,16 +349,13 @@ function formatRings(value: number) {
flex-shrink: 0;
font-size: 0.75rem;
font-weight: 800;
color: #c9a062;
color: var(--colorPrimary, #2563eb);
}
.copper-convert__result {
margin-top: 0.125rem;
padding: 0.875rem;
border-radius: 1rem;
background: linear-gradient(180deg, #fff 0%, #faf8f5 100%);
border: 1px solid rgba(201, 160, 98, 0.35);
box-shadow: 0 8px 24px rgba(15, 39, 68, 0.08);
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.copper-convert__result-head {
@@ -386,17 +365,6 @@ function formatRings(value: number) {
margin-bottom: 0.75rem;
}
.copper-convert__result-kicker {
font-size: 0.6875rem;
font-weight: 700;
color: #c9a062;
}
.copper-convert__result-title {
font-size: 0.9375rem;
font-weight: 800;
color: #0f2744;
}
.copper-convert__rings {
display: grid;
@@ -410,23 +378,11 @@ function formatRings(value: number) {
padding: 0.75rem 0.5rem;
border-radius: 0.85rem;
text-align: center;
background: linear-gradient(160deg, #0f2744 0%, #1a365d 100%);
border: 1px solid rgba(201, 160, 98, 0.4);
background: #eff5f3;
border: 1px solid #d4e3de;
overflow: hidden;
}
.copper-convert__ring-card::after {
content: '';
position: absolute;
inset-inline-start: 50%;
bottom: -24px;
width: 80px;
height: 48px;
transform: translateX(-50%);
background: radial-gradient(ellipse, rgba(201, 160, 98, 0.35) 0%, transparent 70%);
pointer-events: none;
}
.copper-convert__ring-badge {
display: inline-block;
margin-bottom: 0.35rem;
@@ -434,8 +390,8 @@ function formatRings(value: number) {
border-radius: 999px;
font-size: 0.625rem;
font-weight: 700;
color: #0f2744;
background: rgba(201, 160, 98, 0.95);
color: var(--colorPrimary, #2563eb);
background: #fff;
}
.copper-convert__ring-count {
@@ -443,20 +399,20 @@ function formatRings(value: number) {
font-size: 1.5rem;
font-weight: 800;
line-height: 1.2;
color: #f8fafc;
color: #0f172a;
}
.copper-convert__ring-caption {
margin: 0.15rem 0 0;
font-size: 0.6875rem;
font-weight: 600;
color: rgba(226, 232, 240, 0.85);
color: #64748b;
}
.copper-convert__ring-sub {
margin: 0.35rem 0 0;
font-size: 0.625rem;
color: rgba(201, 160, 98, 0.9);
color: #64748b;
}
.copper-convert__summary {
@@ -479,7 +435,7 @@ function formatRings(value: number) {
.copper-convert__summary-row strong {
font-size: 0.8125rem;
font-weight: 800;
color: #0f2744;
color: #0f172a;
}
.copper-convert__summary-row strong small {
@@ -1,6 +1,8 @@
import {
computed,
inject,
onMounted,
onUnmounted,
provide,
ref,
type ComputedRef,
@@ -144,14 +146,14 @@ export function useCopperConvertPage() {
}
}
async function refreshLivePrices() {
onMounted(() => {
startLastPricePolling()
await loadCatalog()
}
void loadCatalog()
})
function stopLivePrices() {
onUnmounted(() => {
stopLastPricePolling()
}
})
const ctx: CopperConvertPageContext = {
loading,
@@ -163,7 +165,7 @@ export function useCopperConvertPage() {
}
provide(COPPER_CONVERT_PAGE_KEY, ctx)
return { ...ctx, refreshLivePrices, stopLivePrices, loadCatalog }
return ctx
}
export function useCopperConvertPageContext() {
@@ -0,0 +1,77 @@
<template>
<div class="dconvert">
<div class="dconvert__container">
<header class="dconvert-topbar">
<button type="button" class="dconvert-topbar__back" @click="router.back()">
<BasicIcon icon="vuesax-linear:arrow-right-1" size="18" />
<span>بازگشت</span>
</button>
<div class="dconvert-topbar__meta">
<p class="dconvert-topbar__eyebrow">محصول</p>
<h1 class="dconvert-topbar__title">تبدیل محصول</h1>
</div>
</header>
<section class="dconvert-surface">
<CopperConvertCalculator />
</section>
</div>
</div>
</template>
<script lang="ts" setup>
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import router from '@/router'
import CopperConvertCalculator from '../components/CopperConvertCalculator.vue'
</script>
<style scoped lang="less">
.dconvert {
min-height: 100%;
padding: 1.25rem 1.5rem 2rem;
}
.dconvert__container {
max-width: 720px;
margin: 0 auto;
}
.dconvert-topbar {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
}
.dconvert-topbar__back {
display: inline-flex;
align-items: center;
gap: 0.25rem;
border: none;
background: transparent;
color: var(--colorPrimary, #2563eb);
font-family: inherit;
font-weight: 700;
cursor: pointer;
}
.dconvert-topbar__eyebrow {
margin: 0;
font-size: 0.75rem;
color: #64748b;
}
.dconvert-topbar__title {
margin: 0;
font-size: 1.25rem;
font-weight: 800;
color: #0f172a;
}
.dconvert-surface {
border: 1px solid #e5e7eb;
border-radius: 0.875rem;
background: #fff;
padding: 1.25rem;
}
</style>
+24
View File
@@ -0,0 +1,24 @@
<template>
<div class="convert-page-root">
<DesktopConvertView v-show="isDesktop" />
<MobileConvertView v-show="!isDesktop" />
</div>
</template>
<script lang="ts" setup>
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
import { useCopperConvertPage } from './composables/useCopperConvertPage'
import DesktopConvertView from './desktop/DesktopConvertView.vue'
import MobileConvertView from './mobile/MobileConvertView.vue'
const isDesktop = useDesktopBreakpoint()
useCopperConvertPage()
</script>
<style scoped>
.convert-page-root {
min-height: 100%;
height: 100%;
}
</style>
@@ -0,0 +1,15 @@
<template>
<div>
<MobilePageHeader title="تبدیل محصول" show-back />
<div class="px-5 mpage-content pb-2">
<div class="border-gray-200 border-1 rounded-xl p-4 relative bg-white">
<CopperConvertCalculator />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
import CopperConvertCalculator from '../components/CopperConvertCalculator.vue'
</script>