import { Injectable, inject } from '@angular/core'; import { Observable, of } from 'rxjs'; import { delay } from 'rxjs/operators'; import { AdminTransaction, AdminTransactionListFilters, AdminTransactionsListResult } from '../models/admin-transaction.model'; import { AdminTransactionsGateway } from './admin-transactions-gateway.interface'; import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway'; import { AdminAuthService } from '@marketplaces/auth'; const METHODS = ['card', 'qr', 'cash_on_delivery']; @Injectable({ providedIn: 'root' }) export class AdminTransactionsLocalGateway implements AdminTransactionsGateway { private readonly adminAuth = inject(AdminAuthService); private cache: AdminTransaction[] | null = null; constructor(private readonly ordersGateway: AdminOrdersLocalGateway) {} private get currentActor(): string { return this.adminAuth.displayName() ?? 'admin'; } loadTransactions(filters: AdminTransactionListFilters): Observable { return new Observable(subscriber => { this.ensureData().then(() => { const filtered = (this.cache ?? []) .filter(tx => !filters.search || `${tx.orderNumber} ${tx.method}`.toLowerCase().includes(filters.search.toLowerCase())) .filter(tx => filters.status === 'all' || tx.status === filters.status) .filter(tx => filters.type === 'all' || tx.type === filters.type) .sort((left, right) => right.createdAt.localeCompare(left.createdAt)); const start = (filters.page - 1) * filters.pageSize; subscriber.next({ items: filtered.slice(start, start + filters.pageSize), total: filtered.length, page: filters.page, pageSize: filters.pageSize, }); subscriber.complete(); }); }).pipe(delay(50)); } retryFailed(id: string): Observable { return this.mutate(id, tx => ({ ...tx, status: 'retried', updatedAt: new Date().toISOString(), audit: [...tx.audit, { action: 'Retried failed transaction', actor: this.currentActor, timestamp: new Date().toISOString() }], })); } setFraudFlag(id: string, flagged: boolean): Observable { return this.mutate(id, tx => ({ ...tx, fraudFlag: flagged, updatedAt: new Date().toISOString(), audit: [...tx.audit, { action: flagged ? 'Flagged as fraud' : 'Fraud flag cleared', actor: this.currentActor, timestamp: new Date().toISOString() }], })); } private mutate(id: string, update: (tx: AdminTransaction) => AdminTransaction): Observable { const existing = (this.cache ?? []).find(tx => tx.id === id); if (!existing) { return of(null); } const updated = update(existing); this.cache = (this.cache ?? []).map(tx => tx.id === id ? updated : tx); return of(updated).pipe(delay(50)); } private async ensureData(): Promise { if (this.cache) { return; } const orders = await new Promise<{ id: string; orderNumber: string; total: number; currency: string; status: string; createdAt: string }[]>(resolve => this.ordersGateway.loadOrders({ search: '', status: 'all', page: 1, pageSize: 1000 }).subscribe(result => resolve(result.items)) ); this.cache = orders.map((order, index) => this.seedTransaction(order, index)); } private seedTransaction(order: { id: string; orderNumber: string; total: number; currency: string; status: string; createdAt: string }, index: number): AdminTransaction { const status = order.status === 'cancelled' ? 'failed' : order.status === 'refunded' ? 'success' : index % 7 === 0 ? 'pending' : 'success'; return { id: `tx-${index + 1}`, orderId: order.id, orderNumber: order.orderNumber, type: order.status === 'refunded' ? 'refund' : index % 3 === 0 ? 'qr_payment' : 'payment', method: METHODS[index % METHODS.length], status, amount: order.total, currency: order.currency, fraudFlag: index % 11 === 0, audit: [{ action: 'Transaction created', actor: 'system', timestamp: order.createdAt }], createdAt: order.createdAt, updatedAt: order.createdAt, }; } }