frontend_nyla/src/pages/billing/BillingTransactions.tsx
2026-04-26 18:11:52 +02:00

152 lines
4.7 KiB
TypeScript

/**
* Billing Transactions Page
*
* Zeigt die Transaktionshistorie für den Benutzer.
*/
import React, { useEffect, useState } from 'react';
import { useBilling, type BillingTransaction } from '../../hooks/useBilling';
import { BillingNav } from './BillingNav';
import styles from './Billing.module.css';
import { useLanguage } from '../../providers/language/LanguageContext';
// ============================================================================
// TRANSACTION ROW COMPONENT
// ============================================================================
interface TransactionRowProps {
transaction: BillingTransaction;
}
const TransactionRow: React.FC<TransactionRowProps> = ({ transaction }) => {
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 (
<tr>
<td>{formatDate(transaction.sysCreatedAt)}</td>
<td>{transaction.mandateName || '-'}</td>
<td>
<span className={`${styles.transactionType} ${getTypeClass(transaction.transactionType)}`}>
{getTypeLabel(transaction.transactionType)}
</span>
</td>
<td>{transaction.description}</td>
<td>{transaction.aicoreProvider || '-'}</td>
<td>{transaction.aicoreModel || '-'}</td>
<td>{transaction.featureCode || '-'}</td>
<td style={{ textAlign: 'right' }}>
{transaction.transactionType === 'DEBIT' ? '-' : '+'}{formatCurrency(transaction.amount)}
</td>
</tr>
);
};
// ============================================================================
// MAIN COMPONENT
// ============================================================================
export const BillingTransactions: React.FC = () => {
const { t } = useLanguage();
const { transactions, loading, loadTransactions } = useBilling();
const [limit, setLimit] = useState(50);
useEffect(() => {
loadTransactions(limit);
}, [limit, loadTransactions]);
const handleLoadMore = () => {
setLimit(prev => prev + 50);
};
return (
<div className={styles.billingDashboard}>
<header className={styles.pageHeader}>
<h1>{t('Transaktionen')}</h1>
<p className={styles.subtitle}>{t('Übersicht aller Kontobewegungen')}</p>
</header>
<BillingNav />
<section className={styles.section}>
{loading && transactions.length === 0 ? (
<div className={styles.loadingPlaceholder}>{t('Transaktionen laden')}</div>
) : transactions.length === 0 ? (
<div className={styles.noData}>{t('Keine Transaktionen vorhanden')}</div>
) : (
<>
<div style={{ overflowX: 'auto' }}>
<table className={styles.transactionsTable}>
<thead>
<tr>
<th>Datum</th>
<th>{t('Mandant')}</th>
<th>Typ</th>
<th>{t('Beschreibung')}</th>
<th>Anbieter</th>
<th>Modell</th>
<th>Feature</th>
<th style={{ textAlign: 'right' }}>{t('Betrag')}</th>
</tr>
</thead>
<tbody>
{transactions.map((transaction) => (
<TransactionRow key={transaction.id} transaction={transaction} />
))}
</tbody>
</table>
</div>
{transactions.length >= limit && (
<div style={{ textAlign: 'center', marginTop: 'var(--spacing-md)' }}>
<button
className={`${styles.button} ${styles.buttonSecondary}`}
onClick={handleLoadMore}
disabled={loading}
>
{loading ? t('Laden') : t('Mehr laden')}
</button>
</div>
)}
</>
)}
</section>
</div>
);
};
export default BillingTransactions;