/** * Subscription Renewal History Component * Displays historical subscription renewals with details about plan changes, renewals, and usage. */ import React, { useState, useEffect } from 'react'; import { Box, Table, TableBody, TableContainer, TableHead, TableRow, TablePagination, IconButton, Tooltip, CircularProgress, Chip, Typography, } from '@mui/material'; import { Refresh as RefreshIcon, TrendingUp as TrendingUpIcon, TrendingDown as TrendingDownIcon, Add as AddIcon, } from '@mui/icons-material'; import { RefreshCw } from 'lucide-react'; // Use lucide-react for header icon import { billingService } from '../../services/billingService'; import { SubscriptionRenewal, RenewalHistoryResponse } from '../../types/billing'; import { TerminalPaper, TerminalTypography, TerminalTableCell, TerminalTableRow, TerminalAlert, TerminalChip, TerminalChipSuccess, TerminalChipWarning, TerminalChipError, terminalColors } from '../SchedulerDashboard/terminalTheme'; import { showToastNotification } from '../../utils/toastNotifications'; interface SubscriptionRenewalHistoryProps { userId?: string; terminalTheme?: boolean; initialLimit?: number; } const SubscriptionRenewalHistory: React.FC = ({ userId, terminalTheme = false, initialLimit = 20 }) => { const [renewals, setRenewals] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(initialLimit); const [totalCount, setTotalCount] = useState(0); const fetchRenewals = async () => { try { setLoading(true); setError(null); const response: RenewalHistoryResponse = await billingService.getRenewalHistory( userId, rowsPerPage, page * rowsPerPage ); setRenewals(response.renewals || []); setTotalCount(response.total_count || 0); // Don't show success toast on automatic refresh - only show errors } catch (err: any) { const errorMessage = err.message || 'Failed to fetch renewal history'; setError(errorMessage); console.error('Error fetching renewal history:', err); // Show error toast showToastNotification(errorMessage, 'error'); } finally { setLoading(false); } }; useEffect(() => { fetchRenewals(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [page, rowsPerPage, userId]); const handleChangePage = (_event: unknown, newPage: number) => { setPage(newPage); }; const handleChangeRowsPerPage = (event: React.ChangeEvent) => { setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); }; const formatDate = (dateString: string | null) => { if (!dateString) return 'N/A'; try { return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch { return 'Invalid Date'; } }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(amount); }; const getRenewalTypeIcon = (type: string): React.ReactElement | undefined => { switch (type) { case 'upgrade': return ; case 'downgrade': return ; case 'renewal': return ; case 'new': return ; default: return undefined; } }; const getRenewalTypeChip = (type: string) => { const ChipComponent = terminalTheme ? TerminalChip : Chip; const chipStyles = { upgrade: { backgroundColor: 'rgba(34, 197, 94, 0.2)', color: '#22c55e' }, downgrade: { backgroundColor: 'rgba(245, 158, 11, 0.2)', color: '#f59e0b' }, renewal: { backgroundColor: 'rgba(59, 130, 246, 0.2)', color: '#3b82f6' }, new: { backgroundColor: 'rgba(59, 130, 246, 0.2)', color: '#3b82f6' }, }; const chipStyle = chipStyles[type as keyof typeof chipStyles] || chipStyles.renewal; const label = type.charAt(0).toUpperCase() + type.slice(1); const icon = getRenewalTypeIcon(type); if (terminalTheme) { const TerminalChipComponent = type === 'upgrade' ? TerminalChipSuccess : type === 'downgrade' ? TerminalChipWarning : TerminalChip; return ( ); } return ( ); }; // Conditional component selection based on terminal theme const PaperComponent = terminalTheme ? TerminalPaper : Box; // Alert component wrapper for terminal theme const AlertWrapper: React.FC<{ children: React.ReactNode; severity?: 'error' | 'info' | 'warning'; sx?: any }> = ({ children, severity = 'info', sx }) => { if (terminalTheme) { return ( {children} ); } return ( {children} ); }; return ( {terminalTheme ? ( ) : ( )} {terminalTheme ? ( Subscription Renewal History ) : ( Subscription Renewal History )} {loading && renewals.length === 0 ? ( ) : error ? ( {terminalTheme ? ( {error} ) : ( {error} )} ) : renewals.length === 0 ? ( {terminalTheme ? ( No renewal history found. Your subscription renewals will appear here. ) : ( No renewal history found. Your subscription renewals will appear here. )} ) : ( <> # Type Plan Previous Plan Billing Cycle Period Start Period End Amount Status {renewals.map((renewal) => ( {terminalTheme ? ( #{renewal.renewal_count} ) : ( #{renewal.renewal_count} )} {getRenewalTypeChip(renewal.renewal_type)} {terminalTheme ? ( <> {renewal.plan_name} {renewal.plan_tier} ) : ( <> {renewal.plan_name} {renewal.plan_tier} )} {renewal.previous_plan_name ? ( terminalTheme ? ( <> {renewal.previous_plan_name} {renewal.previous_plan_tier} ) : ( <> {renewal.previous_plan_name} {renewal.previous_plan_tier} ) ) : ( terminalTheme ? ( N/A ) : ( N/A ) )} {terminalTheme ? ( {renewal.billing_cycle} ) : ( {renewal.billing_cycle} )} {terminalTheme ? ( {formatDate(renewal.new_period_start)} ) : ( {formatDate(renewal.new_period_start)} )} {terminalTheme ? ( {formatDate(renewal.new_period_end)} ) : ( {formatDate(renewal.new_period_end)} )} {terminalTheme ? ( {formatCurrency(renewal.payment_amount)} ) : ( {formatCurrency(renewal.payment_amount)} )} {renewal.payment_status === 'paid' ? ( terminalTheme ? ( ) : ( ) ) : renewal.payment_status === 'pending' ? ( terminalTheme ? ( ) : ( ) ) : ( terminalTheme ? ( ) : ( ) )} ))}
)}
); }; export default SubscriptionRenewalHistory;