Merge origin/modular into feature/responsive-layout-refactor.

Keep the responsive layout refactor while bringing in modular updates for support UI, pricing, categories, and config loading.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-14 10:24:43 +03:30
co-authored by Cursor
19 changed files with 523 additions and 97 deletions
+91 -17
View File
@@ -2,6 +2,10 @@ import type { Product } from '@/models/products'
import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price'
import { isMetalCommodityGroup, isMeltedProductUsingAppPrice, isMeltedTradeGroup } from '@/utils/productCategories'
function productUsesLastPriceIfZero(product: Product): boolean {
return Boolean(product.use_last_price_if_zero)
}
export function resolvePriceOffsetRatio(offsetPercent?: number | null) {
const offset = Number(offsetPercent ?? 0)
if (!offset) {
@@ -35,44 +39,114 @@ export function applyChartPriceOffset(basePrice: number, offsetPercent?: number
return Math.round(basePrice + basePrice * ratio)
}
/** قیمت خرید نمایشی محصول (همان مقداری که در لیست و اکشن‌شیت نشان داده می‌شود) */
export function getProductBuyPrice(product: Product): number {
/** قیمت پایه بدون کارمزد — اگر صفر باشد نباید فقط cash_fee به‌عنوان قیمت نمایش داده شود */
export function getProductBaseBuyPrice(product: Product): number {
if (isMeltedProductUsingAppPrice(product)) {
const base = applyPriceOffsetPercent(getTradeBuyPrice(lastPrice.value), product.price_offset_percent)
return base + (product.cash_fee_buy ?? 0)
const apiPrice = Number(product.price ?? 0)
const useLast = productUsesLastPriceIfZero(product)
if (!useLast) {
const liveBuy = getTradeBuyPrice(lastPrice.value)
if (liveBuy > 0) {
return applyPriceOffsetPercent(liveBuy, product.price_offset_percent)
}
return 0
}
if (apiPrice > 0) {
return apiPrice
}
const liveBuy = getTradeBuyPrice(lastPrice.value)
if (liveBuy > 0) {
return applyPriceOffsetPercent(liveBuy, product.price_offset_percent)
}
return 0
}
if (isMeltedTradeGroup(product.group_type)) {
return (product.price ?? 0) + (product.cash_fee_buy ?? 0)
return Number(product.price ?? 0)
}
if (isMetalCommodityGroup(product.group_type)) {
const base = (product.price_profit ?? 0) + (product.price ?? 0)
return base + (product.cash_fee_buy ?? 0)
return Number(product.price_profit ?? 0) + Number(product.price ?? 0)
}
const basePrice = (product.price ?? 0) + (product.cash_fee_buy ?? 0)
return product.direct_price === 0
? basePrice * (product.amount ?? 0)
: basePrice
return Number(product.price ?? 0)
}
/** قیمت فروش نمایشی محصول آبشده‌ای */
export function getProductSellPrice(product: Product): number {
/** قیمت خرید نمایشی محصول (همان مقداری که در لیست و اکشن‌شیت نشان داده می‌شود) */
export function getProductBuyPrice(product: Product): number {
const base = getProductBaseBuyPrice(product)
if (base <= 0) {
return 0
}
const fee = Number(product.cash_fee_buy ?? 0)
if (
isMeltedProductUsingAppPrice(product)
|| isMeltedTradeGroup(product.group_type)
|| isMetalCommodityGroup(product.group_type)
) {
return base + fee
}
const withFee = base + fee
return product.direct_price === 0
? withFee * Number(product.amount ?? 0)
: withFee
}
function getProductBaseSellPrice(product: Product): number {
if (isMeltedProductUsingAppPrice(product)) {
const base = applyPriceOffsetPercent(getTradeSellPrice(lastPrice.value), product.price_offset_percent)
return base - (product.cash_fee_sell ?? 0)
const apiPrice = Number(product.price ?? 0)
const useLast = productUsesLastPriceIfZero(product)
if (!useLast) {
const liveSell = getTradeSellPrice(lastPrice.value)
if (liveSell > 0) {
return applyPriceOffsetPercent(liveSell, product.price_offset_percent)
}
return 0
}
if (apiPrice > 0) {
return apiPrice
}
const liveSell = getTradeSellPrice(lastPrice.value)
if (liveSell > 0) {
return applyPriceOffsetPercent(liveSell, product.price_offset_percent)
}
return 0
}
if (isMeltedTradeGroup(product.group_type)) {
return (product.price ?? 0) - (product.cash_fee_sell ?? 0)
return Number(product.price ?? 0)
}
return getTradeSellPrice(lastPrice.value)
}
/** قیمت فروش نمایشی محصول آبشده‌ای */
export function getProductSellPrice(product: Product): number {
const base = getProductBaseSellPrice(product)
if (base <= 0) {
return 0
}
if (isMeltedProductUsingAppPrice(product) || isMeltedTradeGroup(product.group_type)) {
return base - Number(product.cash_fee_sell ?? 0)
}
return base
}
export function isProductPriceInactive(product: Product): boolean {
return getProductBuyPrice(product) <= 0
return getProductBaseBuyPrice(product) <= 0
}
export { isMeltedTradeGroup, isMeltedProductUsingAppPrice } from '@/utils/productCategories'