Refactor index.astro to improve type safety, enhance UI update functions, and streamline event handling for transactions

This commit is contained in:
GitHub Copilot
2025-04-24 08:37:33 -04:00
parent b76a24edba
commit bb6bd75434
2 changed files with 57 additions and 38 deletions

View File

@@ -3,6 +3,7 @@ import BaseLayout from '../layouts/BaseLayout.astro';
import Sidebar from '../components/Sidebar.astro'; import Sidebar from '../components/Sidebar.astro';
import MainContent from '../components/MainContent.astro'; import MainContent from '../components/MainContent.astro';
import type { Account, Transaction } from '../types'; import type { Account, Transaction } from '../types';
import type { TransactionEventDetail } from '../types/events';
import { formatCurrency, formatDate } from '../utils'; import { formatCurrency, formatDate } from '../utils';
// Fetch accounts from API // Fetch accounts from API
@@ -32,23 +33,29 @@ if (initialAccount.id) {
</BaseLayout> </BaseLayout>
<script> <script>
// Import types for client-side script
type Transaction = import('../types').Transaction;
type Account = import('../types').Account;
type TransactionEventDetail = import('../types/events').TransactionEventDetail;
// --- DOM Elements --- // --- DOM Elements ---
const accountSelect = document.getElementById('account-select'); const accountSelect = document.getElementById('account-select') as HTMLSelectElement;
const currentAccountNameSpan = document.getElementById('current-account-name'); const currentAccountNameSpan = document.getElementById('current-account-name');
const accountBalanceSpan = document.getElementById('account-balance'); const accountBalanceSpan = document.getElementById('account-balance');
const transactionTableBody = document.getElementById('transaction-table-body'); const transactionTableBody = document.getElementById('transaction-table-body');
const transactionSection = document.getElementById('transaction-section');
// --- Helper Functions --- // --- Helper Functions ---
function formatCurrency(amount) { function formatCurrency(amount: number): string {
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount);
} }
function formatDate(dateString) { function formatDate(dateString: string): string {
const date = new Date(dateString + 'T00:00:00'); const date = new Date(dateString + 'T00:00:00');
return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'short', day: 'numeric' }).format(date); return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'short', day: 'numeric' }).format(date);
} }
async function fetchAccountTransactions(accountId) { async function fetchAccountTransactions(accountId: string): Promise<Transaction[]> {
try { try {
const response = await fetch(`/api/accounts/${accountId}/transactions`); const response = await fetch(`/api/accounts/${accountId}/transactions`);
if (!response.ok) throw new Error('Failed to fetch transactions'); if (!response.ok) throw new Error('Failed to fetch transactions');
@@ -59,7 +66,7 @@ if (initialAccount.id) {
} }
} }
async function fetchAccountDetails(accountId) { async function fetchAccountDetails(accountId: string): Promise<Account | null> {
try { try {
const response = await fetch(`/api/accounts/${accountId}`); const response = await fetch(`/api/accounts/${accountId}`);
if (!response.ok) throw new Error('Failed to fetch account details'); if (!response.ok) throw new Error('Failed to fetch account details');
@@ -71,10 +78,9 @@ if (initialAccount.id) {
} }
// --- Update UI Function --- // --- Update UI Function ---
async function updateUIForAccount(accountId) { async function updateUIForAccount(accountId: string): Promise<void> {
console.log("Updating UI for account:", accountId); console.log("Updating UI for account:", accountId);
const transactionSection = document.getElementById('transaction-section');
if (transactionSection) { if (transactionSection) {
transactionSection.classList.add('loading'); transactionSection.classList.add('loading');
} }
@@ -116,7 +122,7 @@ if (initialAccount.id) {
} }
} }
function updateTransactionTable(transactions) { function updateTransactionTable(transactions: Transaction[]): void {
if (!transactionTableBody) return; if (!transactionTableBody) return;
const sortedTransactions = [...transactions].sort((a, b) => const sortedTransactions = [...transactions].sort((a, b) =>
@@ -152,17 +158,31 @@ if (initialAccount.id) {
transactionTableBody.appendChild(row); transactionTableBody.appendChild(row);
}); });
// Add event listeners for edit and delete buttons
setupTransactionButtons(); setupTransactionButtons();
} }
function setupTransactionButtons(): void {
const transactionRows = transactionTableBody?.querySelectorAll('tr[data-txn-id]');
transactionRows?.forEach(row => {
const txnId = row.getAttribute('data-txn-id');
if (!txnId) return;
// Delete button handler
const deleteBtn = row.querySelector('.delete-btn');
deleteBtn?.addEventListener('click', () => handleDeleteTransaction(txnId));
// Edit button handler
const editBtn = row.querySelector('.edit-btn');
editBtn?.addEventListener('click', () => handleEditTransaction(txnId));
});
}
// --- Transaction Actions --- // --- Transaction Actions ---
async function handleDeleteTransaction(txnId) { async function handleDeleteTransaction(txnId: string): Promise<void> {
if (!confirm('Are you sure you want to delete this transaction?')) { if (!confirm('Are you sure you want to delete this transaction?')) {
return; return;
} }
const transactionSection = document.getElementById('transaction-section');
if (transactionSection) { if (transactionSection) {
transactionSection.classList.add('loading'); transactionSection.classList.add('loading');
} }
@@ -191,10 +211,12 @@ if (initialAccount.id) {
} }
} }
async function handleEditTransaction(txnId) { async function handleEditTransaction(txnId: string): Promise<void> {
try { try {
// Find transaction in current transactions list // Find transaction in current transactions list
const currentAccountId = accountSelect?.value; const currentAccountId = accountSelect?.value;
if (!currentAccountId) return;
const transactions = await fetchAccountTransactions(currentAccountId); const transactions = await fetchAccountTransactions(currentAccountId);
const transaction = transactions.find(t => t.id === txnId); const transaction = transactions.find(t => t.id === txnId);
@@ -203,7 +225,7 @@ if (initialAccount.id) {
} }
// Trigger edit mode in form // Trigger edit mode in form
document.dispatchEvent(new CustomEvent('editTransaction', { document.dispatchEvent(new CustomEvent<TransactionEventDetail>('editTransaction', {
detail: { transaction } detail: { transaction }
})); }));
@@ -212,44 +234,28 @@ if (initialAccount.id) {
} }
} }
function setupTransactionButtons() {
const transactionRows = transactionTableBody?.querySelectorAll('tr[data-txn-id]');
transactionRows?.forEach(row => {
const txnId = row.getAttribute('data-txn-id');
if (!txnId) return;
// Delete button handler
const deleteBtn = row.querySelector('.delete-btn');
deleteBtn?.addEventListener('click', () => handleDeleteTransaction(txnId));
// Edit button handler
const editBtn = row.querySelector('.edit-btn');
editBtn?.addEventListener('click', () => handleEditTransaction(txnId));
});
}
// --- Event Listeners --- // --- Event Listeners ---
if (accountSelect) { if (accountSelect) {
accountSelect.addEventListener('change', async (event) => { accountSelect.addEventListener('change', (event: Event) => {
const selectedAccountId = event.target.value; const target = event.target as HTMLSelectElement;
await updateUIForAccount(selectedAccountId); updateUIForAccount(target.value);
}); });
} }
// Listen for transaction events // Listen for transaction events
document.addEventListener('transactionCreated', async (event) => { document.addEventListener('transactionCreated', ((event: CustomEvent<TransactionEventDetail>) => {
const currentAccountId = accountSelect?.value; const currentAccountId = accountSelect?.value;
if (currentAccountId) { if (currentAccountId) {
await updateUIForAccount(currentAccountId); updateUIForAccount(currentAccountId);
} }
}); }) as EventListener);
document.addEventListener('transactionUpdated', async (event) => { document.addEventListener('transactionUpdated', ((event: CustomEvent<TransactionEventDetail>) => {
const currentAccountId = accountSelect?.value; const currentAccountId = accountSelect?.value;
if (currentAccountId) { if (currentAccountId) {
await updateUIForAccount(currentAccountId); updateUIForAccount(currentAccountId);
} }
}); }) as EventListener);
// Initial load with transactions if available // Initial load with transactions if available
const initialAccountId = accountSelect?.value; const initialAccountId = accountSelect?.value;

13
src/types/events.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { Transaction } from "../types";
export interface TransactionEventDetail {
transaction: Transaction;
}
declare global {
interface WindowEventMap {
transactionCreated: CustomEvent<TransactionEventDetail>;
transactionUpdated: CustomEvent<TransactionEventDetail>;
editTransaction: CustomEvent<TransactionEventDetail>;
}
}