Split desktop and mobile layouts with shared page data so APIs fetch once per context.

Add layout shells, desktop views, module-level page caches, and UI fixes for auth, home, wallet, transactions, address, and related pages.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-14 10:08:46 +03:30
co-authored by Cursor
parent 4404b4cb92
commit a5e458ed25
234 changed files with 18425 additions and 5503 deletions
@@ -0,0 +1,103 @@
import type { TableEngineInput } from '@/models/table'
export type BoursecuTransactionTabId =
| 'trades'
| 'deposit'
| 'withdrawal'
| 'physical'
export type BoursecuTradeSide = 'buy' | 'sell'
export type BoursecuTransactionFetchMode = 'transaction' | 'deposit'
export interface BoursecuTransactionTab {
id: BoursecuTransactionTabId
label: string
emptyText: string
showTradeToggle?: boolean
fetchMode?: BoursecuTransactionFetchMode
typeFilters?: number[]
}
export const BOURSECU_TRANSACTION_TABS: BoursecuTransactionTab[] = [
{
id: 'trades',
label: 'همه',
emptyText: 'معامله‌ای برای شما به ثبت نرسیده است',
showTradeToggle: true,
typeFilters: [1, 2],
},
{
id: 'deposit',
label: 'واریز',
emptyText: 'واریزی برای شما به ثبت نرسیده است',
fetchMode: 'deposit',
},
{
id: 'withdrawal',
label: 'برداشت',
emptyText: 'برداشتی برای شما به ثبت نرسیده است',
typeFilters: [4],
},
{
id: 'physical',
label: 'تحویل فیزیکی',
emptyText: 'درخواست تحویل فیزیکی برای شما ثبت نشده است',
typeFilters: [9],
},
]
const TRADE_TYPE_BY_SIDE: Record<BoursecuTradeSide, number> = {
buy: 1,
sell: 2,
}
export function buildBoursecuTransactionPagination(
tab: BoursecuTransactionTab,
tradeSide: BoursecuTradeSide,
dateFilter?: string,
statusFilter?: string
): TableEngineInput {
const filters: TableEngineInput['filters'] = []
if (tab.fetchMode !== 'deposit' && tab.typeFilters?.length) {
const typeValue =
tab.showTradeToggle && tab.typeFilters.includes(TRADE_TYPE_BY_SIDE[tradeSide])
? TRADE_TYPE_BY_SIDE[tradeSide]
: tab.typeFilters[0]
filters.push({
field: 'type',
cr: 'and',
type: 'number',
op: 'eq',
value: typeValue,
})
}
if (dateFilter) {
filters.push({
field: 'created_at',
cr: 'and',
type: 'date',
op: 'eq',
value: dateFilter,
})
}
if (statusFilter === 'active' || statusFilter === 'inactive') {
filters.push({
field: 'active',
cr: 'and',
type: 'boolean',
op: 'eq',
value: statusFilter === 'active',
})
}
return {
page: 0,
limit: 7,
filters,
}
}