feat(home): landing-style live chart, dynamic product tabs, and price copy
Replace home chart with landing live card, catalog-driven tabs, and smoother range transitions; normalize مظنه to قیمت in user-facing strings; add installment and zaarbox chart APIs.
This commit is contained in:
@@ -10,6 +10,7 @@ import boursecuAssetsIcon from '@/assets/img/boursecu-assets-icon.png'
|
||||
import boursecuHomeIcon from '@/assets/img/boursecu-home-icon.png'
|
||||
import boursecuSettingsIcon from '@/assets/img/boursecu-settings-icon.png'
|
||||
import boursecuSupportIcon from '@/assets/img/boursecu-support-icon.png'
|
||||
import boursecuAboutIcon from '@/assets/img/boursecu-about-icon.png'
|
||||
import boursecuPriceTriggerIcon from '@/assets/img/boursecu-price-trigger-icon.png'
|
||||
import boursecuRulesIcon from '@/assets/img/boursecu-rules-icon.png'
|
||||
|
||||
@@ -81,6 +82,7 @@ export function getBoursecuActionIconSrc(actionId: string): string | undefined {
|
||||
home: boursecuHomeIcon,
|
||||
settings: boursecuSettingsIcon,
|
||||
support: boursecuSupportIcon,
|
||||
about: boursecuAboutIcon,
|
||||
priceTrigger: boursecuPriceTriggerIcon,
|
||||
rules: boursecuRulesIcon,
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export function shouldNormalizeInputDigits(_options?: {
|
||||
type?: string
|
||||
inputmode?: string
|
||||
}): boolean {
|
||||
// همیشه ارقام فارسی/عربی → انگلیسی (لاگین، گرم، تعداد، مظنه، …)
|
||||
// همیشه ارقام فارسی/عربی → انگلیسی (لاگین، گرم، تعداد، قیمت، …)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/** جایگزینی «مظنه» و شکلهای وابسته با «قیمت» در متنهای نمایشی به مشتری */
|
||||
|
||||
const MAZANEH_TO_PRICE: Array<[RegExp, string]> = [
|
||||
[/مظنههای/g, 'قیمتهای'],
|
||||
[/مظنهها/g, 'قیمتها'],
|
||||
[/مظنهای/g, 'قیمتی'],
|
||||
[/مظنهای/g, 'قیمتی'],
|
||||
[/مظنهٔ/g, 'قیمت'],
|
||||
[/مظنه/g, 'قیمت'],
|
||||
]
|
||||
|
||||
export function replaceMazanehTerminology(text: string): string {
|
||||
let result = text
|
||||
for (const [pattern, replacement] of MAZANEH_TO_PRICE) {
|
||||
result = result.replace(pattern, replacement)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function polishUserFacingCopy(text: unknown): string {
|
||||
if (typeof text !== 'string') return ''
|
||||
const trimmed = text.trim()
|
||||
if (!trimmed) return ''
|
||||
return replaceMazanehTerminology(trimmed)
|
||||
}
|
||||
+3
-3
@@ -18,11 +18,11 @@ let lastSubscribers = 0
|
||||
/** @deprecated از lastPrice استفاده کنید — برای سازگاری با کد قدیمی sync میشود */
|
||||
export const price = ref<Price>()
|
||||
export const lastPrice = ref<Price>()
|
||||
/** حداقل یک بار تلاش برای گرفتن مظنه تمام شده (موفق یا ناموفق) */
|
||||
/** حداقل یک بار تلاش برای گرفتن قیمت تمام شده (موفق یا ناموفق) */
|
||||
export const lastPriceReady = ref(false)
|
||||
/**
|
||||
* بعد از اولین پاسخ خالی/no_quote، چند لحظه صبر میکنیم تا تیک SSE/زنده برسد
|
||||
* و «مظنه در دسترس نیست» زودهنگام نشان داده نشود.
|
||||
* و «قیمت در دسترس نیست» زودهنگام نشان داده نشود.
|
||||
*/
|
||||
export const lastPriceSettleGeneration = ref(0)
|
||||
export const olderPrice = ref<number | undefined>(0)
|
||||
@@ -92,7 +92,7 @@ function markLastPriceReady() {
|
||||
scheduleQuoteAbsenceSettle()
|
||||
}
|
||||
|
||||
/** قیمت آمده، بسته قطعی است، یا پنجرهٔ انتظارِ مظنه خالی تمام شده */
|
||||
/** قیمت آمده، بسته قطعی است، یا پنجرهٔ انتظارِ قیمت خالی تمام شده */
|
||||
export function isLastPriceAbsenceSettled(): boolean {
|
||||
void lastPriceSettleGeneration.value
|
||||
if (!lastPriceReady.value) return false
|
||||
|
||||
@@ -38,7 +38,7 @@ const STREAM_CONNECTING = 0
|
||||
const STREAM_OPEN = 1
|
||||
const STREAM_CLOSED = 2
|
||||
|
||||
/** fetch + ReadableStream — با توکن اختلاف قیمت ویژه؛ بدون توکن همان مظنهٔ عمومی (مهمان). */
|
||||
/** fetch + ReadableStream — با توکن اختلاف قیمت ویژه؛ بدون توکن همان قیمت عمومی (مهمان). */
|
||||
class AuthPriceStream extends EventTarget {
|
||||
readyState = STREAM_CONNECTING
|
||||
onopen: (() => void) | null = null
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ref } from 'vue'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import { isLastPriceAbsenceSettled, lastPrice, lastPriceReady } from '@/utils/price'
|
||||
import { replaceMazanehTerminology } from '@/utils/persianTerminology'
|
||||
|
||||
export interface StoreTradingHours {
|
||||
start: string
|
||||
@@ -253,7 +254,7 @@ export function isStoreClosed(): boolean {
|
||||
export const STORE_CLOSED_TITLE = 'فروشگاه بسته است'
|
||||
|
||||
export function stripHtmlMessage(message: string): string {
|
||||
return message.replace(/<[^>]*>/g, '').trim()
|
||||
return replaceMazanehTerminology(message.replace(/<[^>]*>/g, '').trim())
|
||||
}
|
||||
|
||||
export function getStoreClosedHint(customMessage?: unknown): string {
|
||||
@@ -283,7 +284,7 @@ export function shouldBlockStoreTrading(): boolean {
|
||||
|
||||
export type PriceDisplayState = 'open' | 'closed' | 'no_quote'
|
||||
|
||||
export const NO_QUOTE_MESSAGE = 'مظنه در دسترس نیست'
|
||||
export const NO_QUOTE_MESSAGE = 'قیمت در دسترس نیست'
|
||||
|
||||
/** سوییچ: خارج از ساعت کاری هم قیمت صفحه اصلی نشان داده شود */
|
||||
export function shouldShowHomePriceWhenClosed(): boolean {
|
||||
@@ -294,7 +295,7 @@ export function shouldShowLastSavedPrice(): boolean {
|
||||
return useSettingsStore().isSettingTruthy('show_last_price')
|
||||
}
|
||||
|
||||
/** فقط این منابع «آخرین قیمت ذخیرهشده» هستند؛ باقی price_source مظنهٔ زنده است */
|
||||
/** فقط این منابع «آخرین قیمت ذخیرهشده» هستند؛ باقی price_source قیمت زنده است */
|
||||
const STORED_PRICE_SOURCES = ['database_last']
|
||||
|
||||
function hasShowableHomePrice(p = lastPrice.value): boolean {
|
||||
@@ -319,7 +320,7 @@ function hasLiveQuote(p = lastPrice.value): boolean {
|
||||
/**
|
||||
* وضعیت نمایش قیمت در صفحه اصلی — همان منطق بکاند:
|
||||
* خارج از ساعت کاری (close_price روشن) → show_home_price_when_closed روشن: آخرین قیمت؛ خاموش: closed
|
||||
* داخل ساعت کاری → مظنه زنده: open ؛ وگرنه فقط با show_last_price آخرین قیمت؛ وگرنه no_quote
|
||||
* داخل ساعت کاری → قیمت زنده: open ؛ وگرنه فقط با show_last_price آخرین قیمت؛ وگرنه no_quote
|
||||
*/
|
||||
export function getPriceDisplayState(): PriceDisplayState {
|
||||
void productTradingHoursVersion.value
|
||||
@@ -348,7 +349,7 @@ export function getPriceDisplayState(): PriceDisplayState {
|
||||
|
||||
/**
|
||||
* اسکلتون تا وقتی قیمت قابلنمایش برسد، یا وضعیت بسته قطعی شود،
|
||||
* یا بعد از اولین پاسخ خالی پنجرهٔ settle تمام شود (مظنه واقعاً نیست).
|
||||
* یا بعد از اولین پاسخ خالی پنجرهٔ settle تمام شود (قیمت واقعاً نیست).
|
||||
*/
|
||||
export function isHomeQuotePending(): boolean {
|
||||
const settings = useSettingsStore()
|
||||
|
||||
Reference in New Issue
Block a user