diff --git a/.cursor/skills/abshode-domain-lookup/learned-notes.md b/.cursor/skills/abshode-domain-lookup/learned-notes.md index 2f690f6..bf4c7ea 100644 --- a/.cursor/skills/abshode-domain-lookup/learned-notes.md +++ b/.cursor/skills/abshode-domain-lookup/learned-notes.md @@ -3,6 +3,12 @@ فرمت هر خط: - YYYY-MM-DD | موضوع | مسیر فایل(ها) | نکتهٔ ۱ خطی +- 2026-10-21 | نمودار خانه = لندینگ | `LandingLiveChartMain.vue` + `useLandingLiveChart` + `zaarboxPriceHistory`؛ جایگزین `PriceChart` در PriceChartSection/DesktopChart* | همان `dlanding-live__main` SVG؛ داده از zaarbox + `/api/price/last` +- 2026-10-20 | ظاهر خرید اقساطی | `views/base/installment/InstallmentPanel.vue` | دسکتاپ کلاس‌های `dgift` مثل کارت هدیه؛ موبایل کارت `rounded-xl` و توضیح بالای فرم +- 2026-10-20 | خرید اقساطی دیجی‌پی | سرور `DigipayInstallmentService` + تنظیمات `enable_digipay_installment`/`digipay_*`؛ اپ `views/base/installment`؛ ادمین `customers/digipayInstallment` | لینک احراز در تنظیمات؛ مانده اعتبار را ادمین با موبایل می‌نویسد؛ خرید مس را به کیف gold می‌نشاند +- 2026-10-20 | پاپ‌آپ احراز عملیات | واریز/برداشت `useWalletPage.ensureIdentity`؛ هدیه `useGiftCardPage`؛ خرید/فروش دیالوگ `noCompleteVisible` در gold / copper&silver / DealProduct روی mount اگر is_legal=0 | همه هست؛ معامله با ورود صفحه نه با کلیک ثبت +- 2026-10-20 | هشدار قیمت موبایل | `useUserPage.ts` منوی `notifyTrigger` | فقط موبایل و با فلگ `has_page_price_trigger`؛ دسکتاپ همان سایدبار +- 2026-10-20 | آیکون درباره ما | `assets/img/boursecu-about-icon.png` + `getBoursecuActionIconSrc('about')` در `navigation.ts` و `useUserPage.ts` | آدمک حباب گفتار جایگزین info-circle در سایدبار و منوی امکانات بیشتر - 2026-10-19 | قفل احراز واریز/هدیه | `useWalletPage` + `useGiftCardPage` + `assertCustomerIdentityVerified` روی withdraw و giftcard/use | is_legal=0 دیالوگ می‌دهد و API هم رد می‌کند - 2026-10-19 | آیکون پشتیبانی/هشدار/قوانین | `assets/img/boursecu-support-icon.png` + `price-trigger` + `rules` و `navigation.ts` / `useUserPage.ts` | آدمک=پشتیبانی، زنگ دلار=هشدار قیمت سایدبار، برگه=قوانین - 2026-09-08 | لیست کارت هدیه کاربر | `views/base/giftCard/` + `api/giftCard.ts` + سرور `GiftCardController::mine` / `GET customer/giftcard/mine` | زیر فرم: available (اختصاصی + عمومی use_once=0 تک‌کد) و history؛ تخفیف فقط نمایش/سبد نه giftcard/use @@ -39,3 +45,6 @@ - 2026-10-17 | باز/بسته خرید فروش محصول | `utils/productTradeOpen.ts` + `gold/index.vue` | `open_buy`/`open_sell`؛ بدون ProductItem هر دو باز - 2026-10-17 | نمودار مهمان ۴۰۱ | `usePriceChart.ts` + `api/home.ts` + سرور `routes/api.php` `POST /api/price/all` | مهمان product/list نمی‌زند؛ تاریخچه نمودار از `/api/price/all` (همان priceData) - 2026-10-17 | قیمت زنده مهمان | `bootstrap.ts` + `priceSocket.ts` + سرور `/api/price/stream` بدون auth | SSE بدون توکن؛ polling پشتیبان product/list فقط با توکن +- 2026-10-21 | نمودار خانه = کارت لندینگ | `home/components/LandingLiveChartMain.vue` + `useLandingLiveChart.ts` + `styles/landing-live-chart-main.less` | موبایل PriceChartSection و دسکتاپ DesktopChartSection/Panel همین کارت؛ استایل از main.less +- 2026-10-21 | چیدمان کارت نمودار خانه | همان دو فایل vue/less | هدر: نام+وضعیت راست، تب محصول چپ؛ قیمت راست و تغییر چپ؛ تب بازه کنار عنوان نمودار +- 2026-10-21 | تب نمودار = محصول فعال | `useLandingLiveChart.ts` + `LandingLiveChartMain.vue` | تب از کاتالوگ `active && show`؛ داده با `price_offset_percent` همان محصول diff --git a/components.d.ts b/components.d.ts index d4a22b5..471e08b 100644 --- a/components.d.ts +++ b/components.d.ts @@ -14,6 +14,7 @@ declare module 'vue' { AFormItem: typeof import('ant-design-vue/es')['FormItem'] AImage: typeof import('ant-design-vue/es')['Image'] AInput: typeof import('ant-design-vue/es')['Input'] + AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] AModal: typeof import('ant-design-vue/es')['Modal'] AppCreditLink: typeof import('./src/components/AppCreditLink.vue')['default'] diff --git a/src/api/digipayInstallment.ts b/src/api/digipayInstallment.ts new file mode 100644 index 0000000..4c42f6e --- /dev/null +++ b/src/api/digipayInstallment.ts @@ -0,0 +1,26 @@ +import useAxios from '@/utils/useAxios' + +export interface DigipayInstallmentStatus { + enabled: boolean + status: 'none' | 'pending' | 'active' | string + plan4_remaining_toman: number + plan12_remaining_toman: number + gram_price_rial: number + config: { + onboarding_url: string + fee_shop_percent: number + fee_gateway_percent: number + fee_extra_percent: number + plan4_cap_toman: number + plan12_cap_toman: number + } +} + +export const fetchDigipayInstallment = () => + useAxios.get('/customer/digipay_installment') + +export const startDigipayInstallment = () => + useAxios.post('/customer/digipay_installment/start') + +export const buyDigipayInstallment = (plan: 4 | 12, amountToman: number) => + useAxios.post('/customer/digipay_installment/buy', { plan, amount_toman: amountToman }) diff --git a/src/api/zaarboxPriceHistory.ts b/src/api/zaarboxPriceHistory.ts new file mode 100644 index 0000000..281d135 --- /dev/null +++ b/src/api/zaarboxPriceHistory.ts @@ -0,0 +1,71 @@ +/** تاریخچه قیمت عمومی بورس کالا (بدون توکن) — کاتد مس */ + +export const ZAANBOX_COPPER_CTHD_ID = '8c19e06a-446e-3d56-9cc0-750f7c6cd767' + +export const ZAANBOX_HISTORY_URL = `https://price.zaarbox.ir/api/history/${ZAANBOX_COPPER_CTHD_ID}` + +export type ZaanboxHistoryPoint = { + time: string + price: number +} + +export type ZaanboxHistoryResponse = { + history?: ZaanboxHistoryPoint[] + data?: { history?: ZaanboxHistoryPoint[] } +} + +export type LandingHistoryRange = 'DAILY' | 'WEEKLY' | 'MONTHLY' + +export type LandingHistorySample = { + timeMs: number + price: number +} + +const RANGE_MS: Record = { + DAILY: 1 * 24 * 60 * 60 * 1000, + WEEKLY: 7 * 24 * 60 * 60 * 1000, + MONTHLY: 30 * 24 * 60 * 60 * 1000, +} + +const HISTORY_LIMIT = 1_000_000_000 + +function resolveHistoryRows(payload: unknown): ZaanboxHistoryPoint[] { + if (!payload || typeof payload !== 'object') return [] + const body = payload as ZaanboxHistoryResponse + if (Array.isArray(body.history)) return body.history + if (Array.isArray(body.data?.history)) return body.data!.history! + return [] +} + +export async function fetchZaanboxPriceHistory( + range: LandingHistoryRange, + now = new Date(), +): Promise { + const end = now + const start = new Date(end.getTime() - RANGE_MS[range]) + const url = new URL(ZAANBOX_HISTORY_URL) + url.searchParams.set('start', start.toISOString()) + url.searchParams.set('end', end.toISOString()) + url.searchParams.set('limit', String(HISTORY_LIMIT)) + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { Accept: 'application/json' }, + }) + + if (!response.ok) { + throw new Error(`Zaanbox history HTTP ${response.status}`) + } + + const payload = (await response.json()) as unknown + const rows = resolveHistoryRows(payload) + + return rows + .map((row) => { + const timeMs = Date.parse(String(row.time ?? '')) + const price = Number(row.price) + return { timeMs, price } + }) + .filter((point) => Number.isFinite(point.timeMs) && Number.isFinite(point.price) && point.price > 0) + .sort((a, b) => a.timeMs - b.timeMs) +} diff --git a/src/app/setupRouter.ts b/src/app/setupRouter.ts index 7c04976..ba20c09 100644 --- a/src/app/setupRouter.ts +++ b/src/app/setupRouter.ts @@ -93,6 +93,17 @@ export function setupRouterGuards() { } } + if (to.name === 'installment') { + const { useSettingsStore } = await import('@/store/settingsStore') + const settingsStore = useSettingsStore() + if (!settingsStore.loaded) { + await settingsStore.fetchSettings() + } + if (!settingsStore.isSettingTruthy('enable_digipay_installment')) { + return next({ name: (from.name as string) || 'user' }) + } + } + if (to.name === 'giftCard') { const { useSettingsStore } = await import('@/store/settingsStore') const settingsStore = useSettingsStore() diff --git a/src/assets/css/main.less b/src/assets/css/main.less index 1a737a9..4ed0e00 100644 --- a/src/assets/css/main.less +++ b/src/assets/css/main.less @@ -16,6 +16,7 @@ @import (less) '../../views/base/rules/desktop/styles/desktop-rules.less'; @import (less) '../../views/base/rules/permissions/desktop/styles/desktop-permissions.less'; @import (less) '../../views/base/giftCard/desktop/styles/desktop-gift-card.less'; +@import (less) '../../views/base/home/styles/landing-live-chart-main.less'; @import (less) '../../views/base/address/desktop/styles/desktop-address.less'; @import (less) '../../views/base/educate/desktop/styles/desktop-educate.less'; @import (less) '../../views/base/aboutUs/desktop/styles/desktop-about-us.less'; diff --git a/src/assets/img/boursecu-about-icon.png b/src/assets/img/boursecu-about-icon.png new file mode 100644 index 0000000..df683d1 Binary files /dev/null and b/src/assets/img/boursecu-about-icon.png differ diff --git a/src/components/TradeFeeConfirmSummary.vue b/src/components/TradeFeeConfirmSummary.vue index d2d3601..2ce7a58 100644 --- a/src/components/TradeFeeConfirmSummary.vue +++ b/src/components/TradeFeeConfirmSummary.vue @@ -1,7 +1,7 @@