- Additional Imports (at the top with other imports)
import { useState } from 'react';
import { FaTimes } from 'react-icons/fa';
- New Styled Components (add after the existing styled components)
const ModalOverlay = styled(motion.div)`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 20px;
`;
const ModalContent = styled(motion.div)`
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 20px;
padding: 32px;
width: 90%;
max-width: 1000px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.3);
position: relative;
`;
const ModalHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 2px solid rgba(102, 126, 234, 0.1);
h3 {
font-size: 24px;
font-weight: 700;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0;
}
`;
const CloseButton = styled(motion.button)`
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #667eea;
transition: all 0.3s ease;
&:hover {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
transform: rotate(90deg);
}
`;
const TransactionTable = styled(motion.table)`
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
`;
const StatusBadge = styled.span`
padding: 6px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
${props => {
switch (props.status) {
case 'SUCCESS':
return `
background: rgba(34, 197, 94, 0.1);
color: #059669;
border: 1px solid rgba(34, 197, 94, 0.2);
`;
case 'FAILURE':
return `
background: rgba(239, 68, 68, 0.1);
color: #dc2626;
border: 1px solid rgba(239, 68, 68, 0.2);
`;
case 'PENDING':
return `
background: rgba(245, 158, 11, 0.1);
color: #d97706;
border: 1px solid rgba(245, 158, 11, 0.2);
`;
default:
return `
background: rgba(156, 163, 175, 0.1);
color: #6b7280;
border: 1px solid rgba(156, 163, 175, 0.2);
`;
}
}}
`;
const TypeBadge = styled.span`
padding: 6px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
${props => {
switch (props.type) {
case 'DEPOSIT':
return `
background: rgba(34, 197, 94, 0.1);
color: #059669;
border: 1px solid rgba(34, 197, 94, 0.2);
`;
case 'WITHDRAWAL':
return `
background: rgba(239, 68, 68, 0.1);
color: #dc2626;
border: 1px solid rgba(239, 68, 68, 0.2);
`;
case 'TRANSFER':
return `
background: rgba(59, 130, 246, 0.1);
color: #2563eb;
border: 1px solid rgba(59, 130, 246, 0.2);
`;
case 'LOAN':
return `
background: rgba(168, 85, 247, 0.1);
color: #7c3aed;
border: 1px solid rgba(168, 85, 247, 0.2);
`;
default:
return `
background: rgba(156, 163, 175, 0.1);
color: #6b7280;
border: 1px solid rgba(156, 163, 175, 0.2);
`;
}
}}
`;
const EmptyState = styled(motion.div)`
text-align: center;
padding: 60px 20px;
color: #6b7280;
h4 {
font-size: 20px;
font-weight: 600;
margin-bottom: 8px;
color: #374151;
}
p {
font-size: 16px;
margin: 0;
}
`;
- State Management (add inside the Profile component, after the existing useQuery)
const [selectedAccount, setSelectedAccount] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const { data: transactions, isLoading: isTransactionsLoading } = useQuery({
queryKey: ['transactions', selectedAccount],
queryFn: async () => {
if (!selectedAccount) return [];
try {
const response = await api.get(`/transactions/${selectedAccount}`);
return response.data.data;
} catch (error) {
console.error('Error fetching transactions', error);
toast.error("Error fetching transactions");
return [];
}
},
enabled: !!selectedAccount
});
- Helper Functions (add inside the Profile component)
const openTransactionModal = (accountNumber) => {
setSelectedAccount(accountNumber);
setIsModalOpen(true);
};
const closeTransactionModal = () => {
setIsModalOpen(false);
setSelectedAccount(null);
};
const formatTransactionAmount = (amount, type) => {
const formattedAmount = formatCurrency(amount);
if (type === 'DEPOSIT') return `+${formattedAmount}`;
if (type === 'WITHDRAWAL') return `-${formattedAmount}`;
return formattedAmount;
};
- Update the View Transactions Button (replace the existing ActionLink for transactions)
<ActionLink
variants={actionLinkVariants}
onClick={() => openTransactionModal(account.accountNumber)}
>
<FaEye /> View Transactions
</ActionLink>
- Add the Modal (add this right before the closing tag)
{isModalOpen && (
<ModalOverlay
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={closeTransactionModal}
>
<ModalContent
initial={{ opacity: 0, scale: 0.8, y: 50 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.8, y: 50 }}
onClick={(e) => e.stopPropagation()}
>
<ModalHeader>
<h3>Transaction History - {selectedAccount}</h3>
<CloseButton
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={closeTransactionModal}
>
<FaTimes />
</CloseButton>
</ModalHeader>
{isTransactionsLoading ? (
<div style={{ textAlign: 'center', padding: '40px' }}>
Loading transactions...
</div>
) : transactions?.length === 0 ? (
<EmptyState
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
<h4>No Transactions Found</h4>
<p>This account has no transaction history yet.</p>
</EmptyState>
) : (
<TransactionTable
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
<thead>
<tr>
<TableHeader>Date</TableHeader>
<TableHeader>Type</TableHeader>
<TableHeader>From Account</TableHeader>
<TableHeader>To Account</TableHeader>
<TableHeader>Amount</TableHeader>
<TableHeader>Description</TableHeader>
<TableHeader>Status</TableHeader>
</tr>
</thead>
<tbody>
{transactions?.map((transaction) => (
<TableRow key={transaction.id}>
<TableCell>{formatDate(transaction.timestamp)}</TableCell>
<TableCell>
<TypeBadge type={transaction.type}>
{transaction.type}
</TypeBadge>
</TableCell>
<TableCell>
{transaction.fromAccount ? (
<code style={{
background: 'rgba(102,126,234,0.1)',
padding: '4px 8px',
borderRadius: '6px',
fontSize: '12px'
}}>
{transaction.fromAccount.accountNumber}
</code>
) : (
<span style={{ color: '#9ca3af', fontStyle: 'italic' }}>
External
</span>
)}
</TableCell>
<TableCell>
{transaction.toAccount ? (
<code style={{
background: 'rgba(102,126,234,0.1)',
padding: '4px 8px',
borderRadius: '6px',
fontSize: '12px'
}}>
{transaction.toAccount.accountNumber}
</code>
) : (
<span style={{ color: '#9ca3af', fontStyle: 'italic' }}>
External
</span>
)}
</TableCell>
<TableCell>
<span style={{
fontWeight: 'bold',
color: transaction.type === 'DEPOSIT' ? '#059669' :
transaction.type === 'WITHDRAWAL' ? '#dc2626' : '#374151'
}}>
{formatTransactionAmount(transaction.amount, transaction.type)}
</span>
</TableCell>
<TableCell>{transaction.description || 'No description'}</TableCell>
<TableCell>
<StatusBadge status={transaction.status}>
{transaction.status}
</StatusBadge>
</TableCell>
</TableRow>
))}
</tbody>
</TransactionTable>
)}
</ModalContent>
</ModalOverlay>
)}
<h3>Accounts</h3>
{
isLoading ? ( <div>fetching accounts...</div>) : isError ? (<div>
Error fetching accounts!
</div> ) : accounts.length === 0 ? <div>
<p>You dont have any accounts yet. Create your first account</p>
<CreateButton onClick={() => {
navigate("/dashboard/open-account")
}}>Create Account</CreateButton>
</div> :
<Table initial={{opacity: 0, y:30}}
animate={{
opacity: 1,
y:0,
transition: {
duration: 0.6,
ease: 'easeOut',
staggerChildren: 0.05
}
}}>
<thead>
<tr>
<TableHeader>Sr. No</TableHeader>
<TableHeader>Account Number</TableHeader>
<TableHeader>Account Holder Name</TableHeader>
<TableHeader>Date of Creation</TableHeader>
<TableHeader>Account Balance</TableHeader>
<TableHeader>Actions</TableHeader>
</tr>
</thead>
<tbody>
{accounts?.map((account,idx) => (
<TableRow key={idx} whileHover="hover" >
<TableCell>{idx + 1}</TableCell>
<TableCell>
<code style={{
background: 'rgba(102,126,234,0.1)',
padding: '4px 8px',
borderRadius: '6px',
fontSize: '14px',
fontWeight: '600'
}}>{account.accountNumber}</code>
</TableCell>
<TableCell>{user?.name}</TableCell>
<TableCell>{formatDate(account.createdAt)}</TableCell>
<TableCell>
<ActionLink href="#view-balance" onClick={() => {
toast(`Balance ${formatCurrency(account.balance)}`)
}}>View Balance</ActionLink>
</TableCell>
<TableCell>
<ActionLink variants={actionLinkVariants} href="#view-transactions"><FaEye /> View Transactions</ActionLink>
</TableCell>
</TableRow>
))}
</tbody>
</Table>
}
Top comments (0)