185 lines
5.7 KiB
TypeScript
185 lines
5.7 KiB
TypeScript
/**
|
|
* 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;
|
|
}
|
|
} |