Polish boursecu UX: branding logos, home price stats, assets, and desktop footer.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+19
-1
@@ -1,4 +1,6 @@
|
||||
import boursecuLogo from '@/assets/img/boursemes_logofull.png'
|
||||
import boursecuSplashLogo from '@/assets/img/boursemes-splash.png'
|
||||
import boursecuLoginLogo from '@/assets/img/boursemes-login.png'
|
||||
import bazretalaLoginLogo from '@/assets/img/bazretala-login-logo.png'
|
||||
import bazretalaNavLogo from '@/assets/img/bazretala-nav-logo.png'
|
||||
import bazretalaFundBg from '@/assets/img/bazretala-fund-bg.png'
|
||||
@@ -46,7 +48,7 @@ export function resolveProjectColor(color?: string | null) {
|
||||
|
||||
export function getProjectLoginLogo(avatar?: string | null) {
|
||||
if (isBoursecuProject()) {
|
||||
return boursecuLogo
|
||||
return boursecuLoginLogo
|
||||
}
|
||||
if (isBazretalaProject()) {
|
||||
return bazretalaLoginLogo
|
||||
@@ -54,6 +56,14 @@ export function getProjectLoginLogo(avatar?: string | null) {
|
||||
return avatar ?? getConfigApp('avatar')
|
||||
}
|
||||
|
||||
/** لوگوی صفحه لودینگ اولیه — جدا از لوگوی لاگین/ناوبری */
|
||||
export function getProjectSplashLogo(avatar?: string | null) {
|
||||
if (isBoursecuProject()) {
|
||||
return boursecuSplashLogo
|
||||
}
|
||||
return getProjectLoginLogo(avatar)
|
||||
}
|
||||
|
||||
export function getProjectNavLogo(avatar?: string | null) {
|
||||
if (isBazretalaProject()) {
|
||||
return bazretalaNavLogo
|
||||
@@ -64,6 +74,14 @@ export function getProjectNavLogo(avatar?: string | null) {
|
||||
return avatar ?? getConfigApp('avatar')
|
||||
}
|
||||
|
||||
/** لوگوی ناوبری موبایل — boursecu همان لوگوی لاگین (پسزمینه سفید) */
|
||||
export function getProjectMobileNavLogo(avatar?: string | null) {
|
||||
if (isBoursecuProject()) {
|
||||
return boursecuLoginLogo
|
||||
}
|
||||
return getProjectNavLogo(avatar)
|
||||
}
|
||||
|
||||
export const BAZRETALA_SUPPORT_PHONE = '09121784258'
|
||||
|
||||
export function getProjectSupportPhone() {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
const PERSIAN_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const
|
||||
|
||||
export function toPersianDigits(value: string | number | null | undefined): string {
|
||||
if (value === null || value === undefined) return ''
|
||||
return String(value).replace(/\d/g, (digit) => PERSIAN_DIGITS[Number(digit)] ?? digit)
|
||||
}
|
||||
|
||||
export function toEnglishDigits(value: string): string {
|
||||
return value.replace(/[۰-۹]/g, (digit) => {
|
||||
const index = PERSIAN_DIGITS.indexOf(digit as (typeof PERSIAN_DIGITS)[number])
|
||||
return index >= 0 ? String(index) : digit
|
||||
})
|
||||
}
|
||||
|
||||
export function formatFixed(value: string | number | null | undefined, decimals = 2): string {
|
||||
const num = Number(value)
|
||||
if (!Number.isFinite(num)) return toPersianDigits(String(value ?? ''))
|
||||
return toPersianDigits(num.toFixed(decimals))
|
||||
}
|
||||
|
||||
export function formatPercent(
|
||||
value: number,
|
||||
decimals = 2,
|
||||
options: { showSign?: boolean; suffix?: string } = {},
|
||||
): string {
|
||||
const { showSign = false, suffix = '%' } = options
|
||||
const prefix = showSign && value > 0 ? '+' : value < 0 ? '-' : showSign ? '+' : ''
|
||||
return `${prefix}${formatFixed(Math.abs(value), decimals)}${suffix}`
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { toPersianDigits } from '@/utils/persianDigits'
|
||||
import type { Product } from '@/models/products'
|
||||
import { usePriceWithoutType } from '@/utils/usePrice'
|
||||
import type { UserWalletLine } from '@/models/wallet'
|
||||
import type { ProductFile } from '@/utils/file'
|
||||
import type { WalletInventoryCard, WalletMetalTotal } from '@/models/walletAssets'
|
||||
@@ -87,7 +87,7 @@ export function resolveMetalAssetsDisplayLabel(
|
||||
case METAL_COPPER:
|
||||
return 'دارایی مس'
|
||||
case METAL_GOLD:
|
||||
return 'دارایی طلایی'
|
||||
return 'دارایی شمش مس'
|
||||
case METAL_SILVER:
|
||||
return 'دارایی نقره'
|
||||
default:
|
||||
@@ -103,16 +103,44 @@ export interface HomeAssetRow {
|
||||
value: string
|
||||
metal?: WalletMetalTotal
|
||||
info: HomeAssetRowInfo
|
||||
/** boursecu — متن مودال توضیحات (info) */
|
||||
modalText?: string
|
||||
}
|
||||
|
||||
function formatInventoryCardProductLabel(card: WalletInventoryCard): string {
|
||||
return `دارایی ${card.title}`
|
||||
}
|
||||
|
||||
function formatBoursecuProductModalText(card: WalletInventoryCard): string {
|
||||
return `${formatInventoryCardProductLabel(card)} = موجودی ${card.title}`
|
||||
}
|
||||
|
||||
/** boursecu — متن مودال برای ردیفهای metal_totals */
|
||||
export function formatBoursecuMetalModalText(metal: WalletMetalTotal): string {
|
||||
switch (metal.metal_id) {
|
||||
case METAL_COPPER:
|
||||
return 'مجموع دارایی مس = جمع کاتد و مفتول و شمش'
|
||||
case METAL_GOLD:
|
||||
return 'دارایی شمش مس = موجودی شمش مس'
|
||||
case METAL_SILVER:
|
||||
return 'دارایی نقره = موجودی نقره'
|
||||
default:
|
||||
return `${resolveMetalAssetsDisplayLabel(metal, true)} = موجودی ${metal.label}`
|
||||
}
|
||||
}
|
||||
|
||||
function findMetalTotalById(metalTotals: WalletMetalTotal[], metalId: number) {
|
||||
return metalTotals.find((metal) => metal.metal_id === metalId)
|
||||
}
|
||||
|
||||
function formatBoursecuMetalRowValue(metal: WalletMetalTotal): string {
|
||||
const amount = usePriceWithoutType(Number(metal.total_weight ?? 0), false)
|
||||
if (metal.metal_id === METAL_GOLD) {
|
||||
return `${amount} عدد`
|
||||
}
|
||||
return `${amount}${metal.unit ?? ''}`
|
||||
}
|
||||
|
||||
/** boursecu home — هر محصول / فلز / ریال از customer/wallet/assets جدا نمایش داده میشود */
|
||||
export function buildBoursecuHomeAssetRows(
|
||||
summary: {
|
||||
@@ -142,6 +170,7 @@ export function buildBoursecuHomeAssetRows(
|
||||
value: formatInventoryCardValue(card),
|
||||
metal: findMetalTotalById(metalTotals, card.metal_id),
|
||||
info: 'metal',
|
||||
modalText: formatBoursecuProductModalText(card),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -153,9 +182,10 @@ export function buildBoursecuHomeAssetRows(
|
||||
rows.push({
|
||||
id: `metal-${metal.metal_id}`,
|
||||
label: resolveMetalAssetsDisplayLabel(metal, true),
|
||||
value: `${Number(metal.total_weight ?? 0)}${metal.unit ?? ''}`,
|
||||
value: formatBoursecuMetalRowValue(metal),
|
||||
metal,
|
||||
info: 'metal',
|
||||
modalText: formatBoursecuMetalModalText(metal),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -166,6 +196,7 @@ export function buildBoursecuHomeAssetRows(
|
||||
value: formatInventoryCardValue(card),
|
||||
metal: findMetalTotalById(metalTotals, card.metal_id),
|
||||
info: 'metal',
|
||||
modalText: formatBoursecuProductModalText(card),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -223,7 +254,7 @@ export function formatMetalAssetsInfoText(metal: WalletMetalTotal): string {
|
||||
}
|
||||
|
||||
function formatRialNumber(value: number): string {
|
||||
return Math.round(value).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return usePriceWithoutType(Math.round(value))
|
||||
}
|
||||
|
||||
/** متن توضیح مودال دارایی ریالی */
|
||||
@@ -276,7 +307,7 @@ export function formatInventoryCardValue(card: {
|
||||
}): string {
|
||||
const quantity = Number(card.quantity ?? 0)
|
||||
const unit = card.unit ?? (card.display_type === 'count' ? 'عدد' : 'گرم')
|
||||
return `${quantity} ${unit}`
|
||||
return `${usePriceWithoutType(quantity, false)} ${unit}`
|
||||
}
|
||||
|
||||
export function findProductInventoryCard(
|
||||
|
||||
+18
-15
@@ -1,24 +1,27 @@
|
||||
export function usePrice(price: number, digit = true, unit = 'ریال') {
|
||||
price= price??0
|
||||
let _price = price.toString()
|
||||
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹'
|
||||
|
||||
function formatGroupedPrice(price: string | number | null | undefined, digit: boolean) {
|
||||
let value = price === null || price === undefined ? '' : String(price)
|
||||
if (digit) {
|
||||
_price = _price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
value = value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
return `${_price} ${unit}`
|
||||
return value.replace(/\d/g, (d) => PERSIAN_DIGITS[Number(d)] ?? d)
|
||||
}
|
||||
|
||||
export function usePriceWithoutType(price?: number, digit = true, unit = 'ریال') {
|
||||
let _price = price?.toString()
|
||||
if (digit) {
|
||||
_price = _price?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
return `${_price}`
|
||||
export function usePrice(price: number, digit = true, unit = 'ریال') {
|
||||
price = price ?? 0
|
||||
const formatted = formatGroupedPrice(price, digit)
|
||||
return `${formatted} ${unit}`
|
||||
}
|
||||
|
||||
export function usePriceWithoutType(price?: number, digit = true) {
|
||||
return formatGroupedPrice(price ?? '', digit)
|
||||
}
|
||||
|
||||
export function useTowDecimal(num: string | number): string | number {
|
||||
const temp = num.toString().match(/^-?\d+(?:\.\d{0,2})?/)
|
||||
if (temp && temp.length > 0) return temp[0]
|
||||
return Math.round(+num * 100) / 100
|
||||
if (temp && temp.length > 0) return formatGroupedPrice(temp[0], false)
|
||||
return formatGroupedPrice(Math.round(+num * 100) / 100, false)
|
||||
}
|
||||
|
||||
export function useFormat(num: string | number, unit = '') {
|
||||
@@ -76,6 +79,6 @@ export function getTransactionGoldAmount(item: TransactionPriceSource): number {
|
||||
|
||||
export function formatGoldAmount(amount?: number | null): string {
|
||||
const num = Number(amount)
|
||||
if (!Number.isFinite(num)) return '0'
|
||||
return String(Number(num.toFixed(3)))
|
||||
if (!Number.isFinite(num)) return '۰'
|
||||
return formatGroupedPrice(Number(num.toFixed(3)), false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user