Files
boursecu_user/src/views/base/transactions/composables/useTransactionsPage.ts
T
2026-06-29 11:20:02 +02:00

233 lines
7.3 KiB
TypeScript

import { computed, inject, onMounted, provide, ref, type ComponentPublicInstance, type InjectionKey } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
import { getProductsCatalog, getProductsCatalogRef } from '@/composables/useProductsCatalog'
import { loading, loadingMore } from '@/utils'
import {
BOURSECU_TRANSACTION_TABS,
getBoursecuStatusFilterGroup,
getBoursecuStatusOptionsForTab,
type BoursecuTradeSide,
type BoursecuTransactionTab,
type BoursecuTransactionTabId,
} from '../config/boursecuTabs'
import {
getActiveBoursecuTab,
initTransactionsPageData,
loadMoreTransactions,
reloadTransactionsTable,
resetTransactionsPagination,
transactionActiveTabId,
transactionDateFilter,
transactionDateFromFilter,
transactionDateToFilter,
transactionItems,
transactionPagination,
transactionStatusFilter,
transactionProductFilter,
transactionTableOutput,
transactionTradeSide,
transactionsBoursecuMode,
} from './useTransactionsPageData'
export interface TransactionsPageContext {
pagination: typeof transactionPagination
items: typeof transactionItems
tableOutput: typeof transactionTableOutput
showFilter: ReturnType<typeof ref<boolean>>
dateFilter: typeof transactionDateFilter
dateFromFilter: typeof transactionDateFromFilter
dateToFilter: typeof transactionDateToFilter
statusFilter: typeof transactionStatusFilter
productFilter: typeof transactionProductFilter
productOptions: ReturnType<typeof computed<{ value: number; label: string }[]>>
showProductFilter: ReturnType<typeof computed<boolean>>
mobileObserverEl: ReturnType<typeof ref<HTMLElement | null>>
desktopObserverEl: ReturnType<typeof ref<HTMLElement | null>>
activeTabId: typeof transactionActiveTabId
tradeSide: typeof transactionTradeSide
activeTab: ReturnType<typeof computed<BoursecuTransactionTab>>
statusOptions: ReturnType<typeof computed<ReturnType<typeof getBoursecuStatusOptionsForTab>>>
emptyText: ReturnType<typeof computed<string>>
resetPagination: typeof resetTransactionsPagination
initTable: () => Promise<void>
reloadTable: typeof reloadTransactionsTable
applyFilter: () => Promise<void>
setActiveTab: (tabId: BoursecuTransactionTabId) => Promise<void>
setTradeSide: (side: BoursecuTradeSide) => Promise<void>
applyInlineFilters: () => Promise<void>
bindMobileObserver: (el: Element | ComponentPublicInstance | null) => void
bindDesktopObserver: (el: Element | ComponentPublicInstance | null) => void
}
export const TRANSACTIONS_PAGE_KEY: InjectionKey<TransactionsPageContext> =
Symbol('transactionsPage')
export function useTransactionsPageProvider(options?: { boursecu?: boolean }) {
const isBoursecu = options?.boursecu ?? false
const isDesktop = useDesktopBreakpoint()
const showFilter = ref(false)
const mobileObserverEl = ref<HTMLElement | null>(null)
const desktopObserverEl = ref<HTMLElement | null>(null)
const activeObserverEl = computed(() =>
isDesktop.value ? desktopObserverEl.value : mobileObserverEl.value,
)
const activeTab = computed<BoursecuTransactionTab>(
() =>
BOURSECU_TRANSACTION_TABS.find((tab) => tab.id === transactionActiveTabId.value) ??
BOURSECU_TRANSACTION_TABS[0],
)
const statusOptions = computed(() => getBoursecuStatusOptionsForTab(activeTab.value))
const productOptions = computed(() =>
getProductsCatalogRef().value
.filter((product) => product.active && product.show)
.map((product) => ({
value: product.id,
label: product.title,
})),
)
const showProductFilter = computed(
() => transactionsBoursecuMode.value && activeTab.value.id !== 'deposit',
)
const emptyText = computed(() => {
if (!transactionsBoursecuMode.value) {
return 'تراکنشی موجود نیست'
}
if (activeTab.value.id === 'trades' && transactionTradeSide.value === 'sell') {
return 'معامله فروشی برای شما به ثبت نرسیده است'
}
return activeTab.value.emptyText
})
async function initTable() {
await reloadTransactionsTable()
}
async function applyFilter() {
await reloadTransactionsTable()
showFilter.value = false
}
async function setActiveTab(tabId: BoursecuTransactionTabId) {
if (transactionActiveTabId.value === tabId) {
return
}
const currentTab = activeTab.value
const nextTab =
BOURSECU_TRANSACTION_TABS.find((tab) => tab.id === tabId) ?? BOURSECU_TRANSACTION_TABS[0]
if (getBoursecuStatusFilterGroup(currentTab) !== getBoursecuStatusFilterGroup(nextTab)) {
transactionStatusFilter.value = undefined
}
transactionActiveTabId.value = tabId
await reloadTransactionsTable()
}
async function setTradeSide(side: BoursecuTradeSide) {
if (transactionTradeSide.value === side) {
return
}
transactionTradeSide.value = side
if (getActiveBoursecuTab().showTradeToggle) {
await reloadTransactionsTable()
}
}
async function applyInlineFilters() {
await reloadTransactionsTable()
}
function resolveObserverElement(el: Element | ComponentPublicInstance | null) {
if (!el) return null
if (el instanceof HTMLElement) return el
const root = (el as ComponentPublicInstance).$el
return root instanceof HTMLElement ? root : null
}
function bindMobileObserver(el: Element | ComponentPublicInstance | null) {
mobileObserverEl.value = resolveObserverElement(el)
}
function bindDesktopObserver(el: Element | ComponentPublicInstance | null) {
desktopObserverEl.value = resolveObserverElement(el)
}
useIntersectionObserver(
activeObserverEl,
async ([entry]) => {
if (entry?.isIntersecting) {
await loadMoreTransactions()
}
},
{
root: null,
rootMargin: '300px',
threshold: 0.01,
},
)
onMounted(async () => {
if (isBoursecu) {
await getProductsCatalog()
}
await initTransactionsPageData(isBoursecu)
})
const context: TransactionsPageContext = {
pagination: transactionPagination,
items: transactionItems,
tableOutput: transactionTableOutput,
showFilter,
dateFilter: transactionDateFilter,
dateFromFilter: transactionDateFromFilter,
dateToFilter: transactionDateToFilter,
statusFilter: transactionStatusFilter,
productFilter: transactionProductFilter,
productOptions,
showProductFilter,
mobileObserverEl,
desktopObserverEl,
activeTabId: transactionActiveTabId,
tradeSide: transactionTradeSide,
activeTab,
statusOptions,
emptyText,
resetPagination: resetTransactionsPagination,
initTable,
reloadTable: reloadTransactionsTable,
applyFilter,
setActiveTab,
setTradeSide,
applyInlineFilters,
bindMobileObserver,
bindDesktopObserver,
}
provide(TRANSACTIONS_PAGE_KEY, context)
return context
}
export function useTransactionsPageContext() {
const context = inject(TRANSACTIONS_PAGE_KEY)
if (!context) {
throw new Error('useTransactionsPageContext must be used within useTransactionsPageProvider')
}
return context
}
/** @deprecated Use useTransactionsPageProvider in index + useTransactionsPageContext in views. */
export function useTransactionsPage(options?: { boursecu?: boolean }) {
return useTransactionsPageProvider(options)
}