/** * Billing Mandate View Page * * Shows mandate-level balances and transactions for SysAdmins. * Includes filtering by mandate. */ import React, { useEffect, useState, useMemo } from 'react'; import { useApiRequest } from '../../hooks/useApi'; import { fetchMandateViewBalances, fetchMandateViewTransactions, type MandateBalance, type BillingTransaction } from '../../api/billingApi'; import { BillingNav } from './BillingNav'; import styles from './Billing.module.css'; // ============================================================================ // MANDATE BALANCE TABLE // ============================================================================ interface MandateBalanceTableProps { balances: MandateBalance[]; selectedMandateId: string | null; onSelectMandate: (mandateId: string | null) => void; } const MandateBalanceTable: React.FC = ({ balances, selectedMandateId, onSelectMandate }) => { const formatCurrency = (amount: number) => { return new Intl.NumberFormat('de-CH', { style: 'currency', currency: 'CHF' }).format(amount); }; const getBillingModelLabel = (model: string) => { if (model === 'PREPAY_USER') return 'Prepaid (Benutzer)'; return 'Prepaid (Mandant)'; }; return (
{balances.map((balance) => ( ))}
Mandant Billing-Modell Anzahl Benutzer Standard-Guthaben Gesamtguthaben Aktion
{balance.mandateName || balance.mandateId} {getBillingModelLabel(balance.billingModel)} {balance.userCount} {formatCurrency(balance.defaultUserCredit)} {formatCurrency(balance.totalBalance)}
); }; // ============================================================================ // TRANSACTION TABLE // ============================================================================ interface TransactionTableProps { transactions: BillingTransaction[]; } const TransactionTable: React.FC = ({ transactions }) => { const formatCurrency = (amount: number) => { return new Intl.NumberFormat('de-CH', { style: 'currency', currency: 'CHF' }).format(amount); }; const formatDate = (dateString?: string) => { if (!dateString) return '-'; return new Date(dateString).toLocaleString('de-CH', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }; const getTypeClass = (type: string) => { switch (type) { case 'CREDIT': return styles.credit; case 'DEBIT': return styles.debit; case 'ADJUSTMENT': return styles.adjustment; default: return ''; } }; const getTypeLabel = (type: string) => { switch (type) { case 'CREDIT': return 'Gutschrift'; case 'DEBIT': return 'Belastung'; case 'ADJUSTMENT': return 'Korrektur'; default: return type; } }; return (
{transactions.map((t) => ( ))}
Datum Mandant Typ Beschreibung Anbieter Modell Feature Betrag
{formatDate(t.createdAt)} {t.mandateName || '-'} {getTypeLabel(t.transactionType)} {t.description} {t.aicoreProvider || '-'} {t.aicoreModel || '-'} {t.featureCode || '-'} {t.transactionType === 'DEBIT' ? '-' : '+'}{formatCurrency(t.amount)}
); }; // ============================================================================ // MAIN COMPONENT // ============================================================================ export const BillingMandateView: React.FC = () => { const { request, isLoading: loading } = useApiRequest(); const [balances, setBalances] = useState([]); const [transactions, setTransactions] = useState([]); const [selectedMandateId, setSelectedMandateId] = useState(null); const [limit, setLimit] = useState(100); // Load data useEffect(() => { const loadData = async () => { try { const [balanceData, transactionData] = await Promise.all([ fetchMandateViewBalances(request), fetchMandateViewTransactions(request, limit) ]); setBalances(Array.isArray(balanceData) ? balanceData : []); setTransactions(Array.isArray(transactionData) ? transactionData : []); } catch (err) { console.error('Error loading mandate view data:', err); setBalances([]); setTransactions([]); } }; loadData(); }, [request, limit]); // Filter transactions by selected mandate const filteredTransactions = useMemo(() => { if (!selectedMandateId) return transactions; return transactions.filter(t => t.mandateId === selectedMandateId); }, [transactions, selectedMandateId]); const handleLoadMore = () => { setLimit(prev => prev + 100); }; return (

Mandanten-Billing

Guthaben und Transaktionen pro Mandant

{/* Mandate Balances */}

Mandanten-Guthaben

{loading && balances.length === 0 ? (
Lade Daten...
) : balances.length === 0 ? (
Keine Mandanten mit Billing-Settings vorhanden
) : ( )}
{/* Transactions */}

Transaktionen {selectedMandateId && ( (gefiltert nach {balances.find(b => b.mandateId === selectedMandateId)?.mandateName || selectedMandateId}) )}

{loading && transactions.length === 0 ? (
Lade Transaktionen...
) : filteredTransactions.length === 0 ? (
Keine Transaktionen vorhanden
) : ( <> {transactions.length >= limit && (
)} )}
); }; export default BillingMandateView;