/** * Billing View Model * * View model for sponsor billing data with UI-specific transformations. * Transforms BillingViewData into UI-ready state with formatting and derived fields. */ import type { BillingViewData } from '@/lib/view-data/BillingViewData'; import { ViewModel } from "../contracts/view-models/ViewModel"; import { CurrencyDisplay } from "../display-objects/CurrencyDisplay"; import { DateDisplay } from "../display-objects/DateDisplay"; /** * BillingViewModel * * View Model for sponsor billing data. * Transforms BillingViewData into UI-ready state with formatting and derived fields. */ export class BillingViewModel extends ViewModel { paymentMethods: PaymentMethodViewModel[]; invoices: InvoiceViewModel[]; stats: BillingStatsViewModel; constructor(viewData: BillingViewData) { super(); this.paymentMethods = viewData.paymentMethods.map(pm => new PaymentMethodViewModel(pm)); this.invoices = viewData.invoices.map(inv => new InvoiceViewModel(inv)); this.stats = new BillingStatsViewModel(viewData.stats); } } /** * PaymentMethodViewModel * * View Model for payment method data. * Provides formatted display labels and expiry information. */ export class PaymentMethodViewModel extends ViewModel { id: string; type: 'card' | 'bank' | 'sepa'; last4: string; brand?: string; isDefault: boolean; expiryMonth?: number; expiryYear?: number; bankName?: string; // UI-specific derived fields (primitive outputs only) readonly displayLabel: string; readonly expiryDisplay: string | null; constructor(viewData: { id: string; type: 'card' | 'bank' | 'sepa'; last4: string; brand?: string; isDefault: boolean; expiryMonth?: number; expiryYear?: number; bankName?: string; displayLabel: string; expiryDisplay: string | null; }) { super(); this.id = viewData.id; this.type = viewData.type; this.last4 = viewData.last4; this.brand = viewData.brand; this.isDefault = viewData.isDefault; this.expiryMonth = viewData.expiryMonth; this.expiryYear = viewData.expiryYear; this.bankName = viewData.bankName; this.displayLabel = viewData.displayLabel; this.expiryDisplay = viewData.expiryDisplay; } } /** * InvoiceViewModel * * View Model for invoice data. * Provides formatted amounts, dates, and derived status flags. */ export class InvoiceViewModel extends ViewModel { id: string; invoiceNumber: string; date: Date; dueDate: Date; amount: number; vatAmount: number; totalAmount: number; status: 'paid' | 'pending' | 'overdue' | 'failed'; description: string; sponsorshipType: 'league' | 'team' | 'driver' | 'race' | 'platform'; pdfUrl: string; // UI-specific derived fields (primitive outputs only) readonly formattedTotalAmount: string; readonly formattedVatAmount: string; readonly formattedDate: string; readonly isOverdue: boolean; constructor(viewData: { id: string; invoiceNumber: string; date: string; dueDate: string; amount: number; vatAmount: number; totalAmount: number; status: 'paid' | 'pending' | 'overdue' | 'failed'; description: string; sponsorshipType: 'league' | 'team' | 'driver' | 'race' | 'platform'; pdfUrl: string; formattedTotalAmount: string; formattedVatAmount: string; formattedDate: string; isOverdue: boolean; }) { super(); this.id = viewData.id; this.invoiceNumber = viewData.invoiceNumber; this.date = new Date(viewData.date); this.dueDate = new Date(viewData.dueDate); this.amount = viewData.amount; this.vatAmount = viewData.vatAmount; this.totalAmount = viewData.totalAmount; this.status = viewData.status; this.description = viewData.description; this.sponsorshipType = viewData.sponsorshipType; this.pdfUrl = viewData.pdfUrl; this.formattedTotalAmount = viewData.formattedTotalAmount; this.formattedVatAmount = viewData.formattedVatAmount; this.formattedDate = viewData.formattedDate; this.isOverdue = viewData.isOverdue; } } /** * BillingStatsViewModel * * View Model for billing statistics. * Provides formatted monetary fields and derived metrics. */ export class BillingStatsViewModel extends ViewModel { totalSpent: number; pendingAmount: number; nextPaymentDate: Date; nextPaymentAmount: number; activeSponsorships: number; averageMonthlySpend: number; // UI-specific derived fields (primitive outputs only) readonly formattedTotalSpent: string; readonly formattedPendingAmount: string; readonly formattedNextPaymentAmount: string; readonly formattedAverageMonthlySpend: string; readonly formattedNextPaymentDate: string; constructor(viewData: { totalSpent: number; pendingAmount: number; nextPaymentDate: string; nextPaymentAmount: number; activeSponsorships: number; averageMonthlySpend: number; formattedTotalSpent: string; formattedPendingAmount: string; formattedNextPaymentAmount: string; formattedAverageMonthlySpend: string; formattedNextPaymentDate: string; }) { super(); this.totalSpent = viewData.totalSpent; this.pendingAmount = viewData.pendingAmount; this.nextPaymentDate = new Date(viewData.nextPaymentDate); this.nextPaymentAmount = viewData.nextPaymentAmount; this.activeSponsorships = viewData.activeSponsorships; this.averageMonthlySpend = viewData.averageMonthlySpend; this.formattedTotalSpent = viewData.formattedTotalSpent; this.formattedPendingAmount = viewData.formattedPendingAmount; this.formattedNextPaymentAmount = viewData.formattedNextPaymentAmount; this.formattedAverageMonthlySpend = viewData.formattedAverageMonthlySpend; this.formattedNextPaymentDate = viewData.formattedNextPaymentDate; } }