Initial commit - Basic bank transactions dashboard structure with Astro and TypeScript

This commit is contained in:
Peter Wood
2025-04-23 20:57:42 -04:00
parent b2dae7e868
commit 0060013561
19 changed files with 767 additions and 16 deletions

View File

@@ -0,0 +1,14 @@
---
import { formatCurrency } from '../utils'; // We'll create this util
import type { Account } from '../types';
interface Props {
account: Account;
}
const { account } = Astro.props;
---
<div class="account-summary">
<h4>Account Summary</h4>
<p>Balance: <span id="account-balance">{formatCurrency(account.balance)}</span></p>
<!-- Add more summary info if needed -->
</div>

View File

@@ -0,0 +1,51 @@
---
// This component needs client-side JS for the toggle
---
<section class="add-transaction-section">
<button
id="toggle-form-btn"
class="toggle-form-btn"
aria-expanded="false"
aria-controls="add-transaction-form"
>
Add Transaction +
</button>
<form id="add-transaction-form" class="collapsible-form collapsed">
<h4>New Transaction</h4>
<div class="form-group">
<label for="txn-date">Date</label>
<input type="date" id="txn-date" name="date" required>
</div>
<div class="form-group">
<label for="txn-description">Description</label>
<input type="text" id="txn-description" name="description" required placeholder="e.g. Groceries">
</div>
<div class="form-group">
<label for="txn-amount">Amount</label>
<input type="number" id="txn-amount" name="amount" step="0.01" required placeholder="e.g. -25.50 or 1200.00">
</div>
<!-- In a real app, prevent default and use JS to submit -->
<button type="submit">Save</button>
</form>
</section>
<script>
// Script to handle the collapsible form toggle
const toggleBtn = document.getElementById('toggle-form-btn');
const form = document.getElementById('add-transaction-form');
if (toggleBtn && form) {
toggleBtn.addEventListener('click', () => {
const isExpanded = toggleBtn.getAttribute('aria-expanded') === 'true';
toggleBtn.setAttribute('aria-expanded', String(!isExpanded));
form.classList.toggle('collapsed');
toggleBtn.textContent = isExpanded ? 'Add Transaction +' : 'Hide Form -';
// Optional: Focus first input when opening
if (!isExpanded) {
form.querySelector('input')?.focus();
}
});
} else {
console.error("Toggle button or form not found");
}
</script>

View File

@@ -0,0 +1,17 @@
---
import TransactionTable from './TransactionTable.astro';
import type { Account, Transaction } from '../types';
interface Props {
account: Account;
transactions: Transaction[];
}
const { account, transactions } = Astro.props;
---
<main class="main-content">
<header class="main-header">
<h1>Transactions for <span id="current-account-name">{account.name} (***{account.last4})</span></h1>
</header>
<TransactionTable transactions={transactions} client:load /> {/* Make table updatable */}
</main>

View File

@@ -0,0 +1,31 @@
---
import AddTransactionForm from './AddTransactionForm.astro';
import AccountSummary from './AccountSummary.astro';
import type { Account } from '../types'; // We'll define this type
interface Props {
accounts: Account[];
initialAccount: Account; // Pass the initially selected account
}
const { accounts, initialAccount } = Astro.props;
---
<aside class="sidebar">
<div class="sidebar-header">
<h2>My Bank</h2>
</div>
<nav class="account-nav">
<h3>Accounts</h3>
<select id="account-select" name="account">
{accounts.map(account => (
<option value={account.id} selected={account.id === initialAccount.id}>
{account.name} (***{account.last4})
</option>
))}
</select>
</nav>
<AddTransactionForm client:load /> {/* Make form toggle interactive */}
<AccountSummary account={initialAccount} client:load /> {/* Make summary updatable */}
</aside>

View File

@@ -0,0 +1,45 @@
---
import { formatCurrency, formatDate } from '../utils';
import type { Transaction } from '../types';
interface Props {
transactions: Transaction[];
}
const { transactions } = Astro.props;
// Sort transactions by date descending for display
const sortedTransactions = [...transactions].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
---
<section class="transaction-list">
<table id="transaction-table">
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th class="amount-col">Amount</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="transaction-table-body">
{sortedTransactions.map(txn => (
<tr data-txn-id={txn.id}>
<td>{formatDate(txn.date)}</td>
<td>{txn.description}</td>
<td class={`amount-col ${txn.amount >= 0 ? 'amount-positive' : 'amount-negative'}`}>
{formatCurrency(txn.amount)}
</td>
<td>
<button class="action-btn edit-btn" title="Edit transaction (not implemented)">Edit</button>
<button class="action-btn delete-btn" title="Delete transaction (not implemented)">Delete</button>
</td>
</tr>
))}
{sortedTransactions.length === 0 && (
<tr>
<td colspan="4" style="text-align: center; font-style: italic; color: #777;">No transactions found for this account.</td>
</tr>
)}
</tbody>
</table>
</section>