Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
147 lines
4.4 KiB
TypeScript
147 lines
4.4 KiB
TypeScript
import type { BankAccountOwnerType, BankAccountResponseDto, CreateBankAccountDto, WithdrawalResponseDto } from '@/api/generated/models'
|
|
import {
|
|
createServiceError,
|
|
errorResult,
|
|
handleServiceError,
|
|
type ServiceCallOptions,
|
|
type ServiceResult,
|
|
shouldBubbleErrorToParent,
|
|
successResult,
|
|
} from '@/services/errorHandler'
|
|
import { unwrapApiData } from '@/services/apiResponse'
|
|
import { getFinancial } from '@/api/generated/financial/financial'
|
|
import { parseRemittanceList, unwrapApiPayload } from '@/helpers/listResponse'
|
|
import { ANALYTICS_EVENTS, trackAnalyticsEventOnce } from '@/lib/analytics'
|
|
import { texts } from '@/texts'
|
|
|
|
// docs/workflows/fa/settlement-withdrawal.md — Path A (self-service wallet
|
|
// withdrawal). Distinct from admin settlement (Path B), which never
|
|
// touches `wallets.balance` — out of scope here.
|
|
|
|
export type { BankAccountOwnerType }
|
|
export type BankAccount = BankAccountResponseDto & {
|
|
verificationStatus?: 'pending_review' | 'approved' | 'rejected'
|
|
rejectionReason?: string | null
|
|
nationalCode?: string | null
|
|
}
|
|
export type CreateBankAccountPayload = CreateBankAccountDto & {
|
|
nationalCode?: string
|
|
}
|
|
export type WithdrawalRequest = WithdrawalResponseDto
|
|
|
|
const financialApi = getFinancial()
|
|
|
|
export const LIST_BANK_ACCOUNTS = async (options?: ServiceCallOptions): Promise<ServiceResult<BankAccount[]>> => {
|
|
try {
|
|
const res = await financialApi.bankAccountsControllerListMine()
|
|
const payload = unwrapApiPayload(res.data)
|
|
const items = Array.isArray(payload)
|
|
? payload
|
|
: Array.isArray((payload as { items?: unknown[] }).items)
|
|
? (payload as { items: unknown[] }).items
|
|
: []
|
|
|
|
return successResult(items as BankAccount[])
|
|
} catch (error) {
|
|
const normalizedError = handleServiceError(error, options)
|
|
|
|
if (shouldBubbleErrorToParent(options)) {
|
|
throw normalizedError
|
|
}
|
|
|
|
return errorResult(normalizedError)
|
|
}
|
|
}
|
|
|
|
export const CREATE_BANK_ACCOUNT = async (
|
|
payload: CreateBankAccountPayload,
|
|
options?: ServiceCallOptions
|
|
): Promise<ServiceResult<BankAccount>> => {
|
|
try {
|
|
const res = await financialApi.bankAccountsControllerCreate(payload)
|
|
const body = res.data
|
|
|
|
if (!body.success) {
|
|
throw createServiceError(body.message || texts.wallet.bankAccountCreateFailed)
|
|
}
|
|
|
|
const result = unwrapApiData(body)
|
|
|
|
if (!result?.id) {
|
|
throw createServiceError(texts.common.invalidServerResponse)
|
|
}
|
|
|
|
return successResult(result)
|
|
} catch (error) {
|
|
const normalizedError = handleServiceError(error, options)
|
|
|
|
if (shouldBubbleErrorToParent(options)) {
|
|
throw normalizedError
|
|
}
|
|
|
|
return errorResult(normalizedError)
|
|
}
|
|
}
|
|
|
|
export const LIST_WITHDRAWAL_REQUESTS = async (options?: ServiceCallOptions): Promise<ServiceResult<{ items: WithdrawalRequest[] }>> => {
|
|
try {
|
|
const res = await financialApi.withdrawalRequestsControllerListMine({ page: 1, pageSize: 30, sort: '-createdAt' })
|
|
|
|
const parsed = parseRemittanceList<WithdrawalRequest>(res.data, 'items')
|
|
|
|
return successResult({ items: parsed.items })
|
|
} catch (error) {
|
|
const normalizedError = handleServiceError(error, options)
|
|
|
|
if (shouldBubbleErrorToParent(options)) {
|
|
throw normalizedError
|
|
}
|
|
|
|
return errorResult(normalizedError)
|
|
}
|
|
}
|
|
|
|
// amount is in Toman (platform-wide convention — see payment.md's note on
|
|
// the matching CreatePaymentDto Swagger typo; CreateWithdrawalRequestDto
|
|
// has the same "Rials" typo in its Swagger annotation).
|
|
export const CREATE_WITHDRAWAL_REQUEST = async (
|
|
bankAccountId: string,
|
|
amount: number,
|
|
options?: ServiceCallOptions
|
|
): Promise<ServiceResult<WithdrawalRequest>> => {
|
|
try {
|
|
const res = await financialApi.withdrawalRequestsControllerCreate({
|
|
bankAccountId,
|
|
amount,
|
|
})
|
|
const payload = res.data
|
|
|
|
if (!payload.success) {
|
|
throw createServiceError(payload.message || texts.wallet.withdrawalCreateFailed)
|
|
}
|
|
|
|
const result = unwrapApiData(payload)
|
|
|
|
if (!result?.id) {
|
|
throw createServiceError(texts.common.invalidServerResponse)
|
|
}
|
|
|
|
trackAnalyticsEventOnce(ANALYTICS_EVENTS.WITHDRAWAL_REQUESTED, result.id, {
|
|
withdrawal_id: result.id,
|
|
value: amount * 10,
|
|
currency: 'IRR',
|
|
withdrawal_status: result.status,
|
|
})
|
|
|
|
return successResult(result)
|
|
} catch (error) {
|
|
const normalizedError = handleServiceError(error, options)
|
|
|
|
if (shouldBubbleErrorToParent(options)) {
|
|
throw normalizedError
|
|
}
|
|
|
|
return errorResult(normalizedError)
|
|
}
|
|
}
|