diff --git a/.astro/content-assets.mjs b/.astro/content-assets.mjs
deleted file mode 100644
index 2b8b823..0000000
--- a/.astro/content-assets.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export default new Map();
\ No newline at end of file
diff --git a/.astro/content-modules.mjs b/.astro/content-modules.mjs
deleted file mode 100644
index 2b8b823..0000000
--- a/.astro/content-modules.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export default new Map();
\ No newline at end of file
diff --git a/.astro/data-store.json b/.astro/data-store.json
deleted file mode 100644
index daa2b96..0000000
--- a/.astro/data-store.json
+++ /dev/null
@@ -1 +0,0 @@
-[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.7.5","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[]},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"responsiveImages\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false},\"legacy\":{\"collections\":false}}"]
\ No newline at end of file
diff --git a/.astro/settings.json b/.astro/settings.json
deleted file mode 100644
index 5b2b775..0000000
--- a/.astro/settings.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "_variables": {
- "lastUpdateCheck": 1745454555490
- }
-}
\ No newline at end of file
diff --git a/.astro/types.d.ts b/.astro/types.d.ts
deleted file mode 100644
index 03d7cc4..0000000
--- a/.astro/types.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-///
-///
\ No newline at end of file
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index d476e3f..07d81cb 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -9,7 +9,7 @@ This project is a web user interface (UI) for a CRUD (Create, Read, Update, Dele
* **Framework:** Astro (latest version)
* **Language:** TypeScript, JavaScript (client-side scripts), HTML, CSS
* **Styling:** Plain CSS (`src/styles/global.css`)
-* **Data:** Currently using in-memory arrays. **The goal is to eventually integrate with a backend API.**
+* **Data:** Using Astro's built-in API routes in `src/pages/api/` with a temporary in-memory store (`src/data/store.ts`). **The goal is to eventually replace the in-memory store with a persistent database.**
## Current State & Key Features
@@ -17,7 +17,18 @@ This project is a web user interface (UI) for a CRUD (Create, Read, Update, Dele
* **Sidebar:** (`src/components/Sidebar.astro`) Contains account selection dropdown and a collapsible section for adding new transactions. Includes an account summary section.
* **Main Content:** (`src/components/MainContent.astro`) Displays the header with the current account name and the transaction list.
* **Components:** Separate Astro components exist for major UI sections (Sidebar, MainContent, TransactionTable, AddTransactionForm, AccountSummary).
-* **Data Loading:** Currently using empty arrays for accounts and transactions, initialized in `src/pages/index.astro`.
+* **API Integration:**
+ * API routes structure implemented in `src/pages/api/`
+ * Temporary data store in `src/data/store.ts`
+ * All API endpoints implemented and ready to use:
+ * GET /api/accounts - List all accounts
+ * GET /api/accounts/:id - Get single account details
+ * GET /api/accounts/:id/transactions - Get transactions for an account
+ * POST /api/transactions - Create new transaction
+ * PUT /api/transactions/:id - Update existing transaction
+ * DELETE /api/transactions/:id - Delete transaction
+ * Error handling and validation included
+ * Prepared for future database integration with modular store design
* **Account Switching:** Selecting an account from the dropdown in the sidebar correctly updates the Main Content area (header, transaction table) and the Account Summary section using client-side JavaScript (`
\ No newline at end of file
diff --git a/src/components/TransactionTable.astro b/src/components/TransactionTable.astro
index f62b66b..3382cfd 100644
--- a/src/components/TransactionTable.astro
+++ b/src/components/TransactionTable.astro
@@ -10,10 +10,18 @@ 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());
+
+// TODO: UI/UX Improvements
+// - Add sorting functionality for all columns
+// - Implement pagination for large transaction lists
+// - Add transaction filtering capabilities
+// - Implement row hover actions
+// - Add transaction details expansion/collapse
+// - Consider adding bulk actions (delete, categorize)
---
-
+
-
+
Date
Description
@@ -30,8 +38,8 @@ const sortedTransactions = [...transactions].sort((a, b) => new Date(b.date).get
{formatCurrency(txn.amount)}
-
-
+
+
))}
diff --git a/src/data/store.ts b/src/data/store.ts
new file mode 100644
index 0000000..d2cb2a8
--- /dev/null
+++ b/src/data/store.ts
@@ -0,0 +1,57 @@
+// TODO: Database Integration & Persistence
+// - Implement database schema design
+// - Add database connection pooling
+// - Implement data migration strategy
+// - Add database backup and recovery plans
+// - Implement data validation layer
+// - Add transaction logging
+// - Implement audit trail
+// - Add data archival strategy
+
+import type { Account, Transaction } from "../types";
+
+// TODO: Replace in-memory store with persistent database
+// - Implement database connection and configuration
+// - Create database schema for accounts and transactions
+// - Add migration system for schema changes
+// - Update all CRUD operations to use database instead of arrays
+
+// Temporary in-memory store for development
+export const accounts: Account[] = [
+ {
+ id: "1",
+ name: "Checking Account",
+ last4: "4321",
+ balance: 2500.0,
+ },
+ {
+ id: "2",
+ name: "Savings Account",
+ last4: "8765",
+ balance: 10000.0,
+ },
+];
+
+export const transactions: Transaction[] = [
+ {
+ id: "1",
+ accountId: "1",
+ date: "2025-04-20",
+ description: "Grocery Store",
+ amount: -75.5,
+ },
+ {
+ id: "2",
+ accountId: "1",
+ date: "2025-04-21",
+ description: "Salary Deposit",
+ amount: 3000.0,
+ },
+ {
+ id: "3",
+ accountId: "2",
+ date: "2025-04-22",
+ description: "Transfer to Savings",
+ amount: 500.0,
+ },
+];
diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
index 817b9d3..7a1c02d 100644
--- a/src/layouts/BaseLayout.astro
+++ b/src/layouts/BaseLayout.astro
@@ -3,6 +3,15 @@ interface Props {
title: string;
}
+// TODO: Accessibility Improvements
+// - Add ARIA landmarks for main regions
+// - Implement keyboard navigation
+// - Add skip navigation links
+// - Ensure proper heading hierarchy
+// - Add focus management for modals/dialogs
+// - Implement proper announcements for dynamic content
+// - Add high contrast theme support
+
const { title } = Astro.props;
---
diff --git a/src/pages/api/accounts/[id]/index.ts b/src/pages/api/accounts/[id]/index.ts
new file mode 100644
index 0000000..2479188
--- /dev/null
+++ b/src/pages/api/accounts/[id]/index.ts
@@ -0,0 +1,22 @@
+import type { APIRoute } from "astro";
+import { accounts } from "../../../../data/store";
+
+export const GET: APIRoute = async ({ params }) => {
+ const account = accounts.find((a) => a.id === params.id);
+
+ if (!account) {
+ return new Response(JSON.stringify({ error: "Account not found" }), {
+ status: 404,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ }
+
+ return new Response(JSON.stringify(account), {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+};
diff --git a/src/pages/api/accounts/[id]/transactions/index.ts b/src/pages/api/accounts/[id]/transactions/index.ts
new file mode 100644
index 0000000..bb8234f
--- /dev/null
+++ b/src/pages/api/accounts/[id]/transactions/index.ts
@@ -0,0 +1,15 @@
+import type { APIRoute } from "astro";
+import { transactions } from "../../../../../data/store";
+
+export const GET: APIRoute = async ({ params }) => {
+ const accountTransactions = transactions.filter(
+ (t) => t.accountId === params.id
+ );
+
+ return new Response(JSON.stringify(accountTransactions), {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+};
diff --git a/src/pages/api/accounts/index.ts b/src/pages/api/accounts/index.ts
new file mode 100644
index 0000000..f569def
--- /dev/null
+++ b/src/pages/api/accounts/index.ts
@@ -0,0 +1,11 @@
+import type { APIRoute } from "astro";
+import { accounts } from "../../../data/store";
+
+export const GET: APIRoute = async () => {
+ return new Response(JSON.stringify(accounts), {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+};
diff --git a/src/pages/api/transactions/[id]/index.ts b/src/pages/api/transactions/[id]/index.ts
new file mode 100644
index 0000000..f84ea5e
--- /dev/null
+++ b/src/pages/api/transactions/[id]/index.ts
@@ -0,0 +1,126 @@
+import type { APIRoute } from "astro";
+import { transactions, accounts } from "../../../../data/store";
+import type { Transaction } from "../../../../types";
+
+export const PUT: APIRoute = async ({ request, params }) => {
+ const { id } = params;
+
+ if (!id) {
+ return new Response(
+ JSON.stringify({ error: "Transaction ID is required" }),
+ {
+ status: 400,
+ headers: { "Content-Type": "application/json" },
+ }
+ );
+ }
+
+ try {
+ const updates = (await request.json()) as Partial;
+ const transactionIndex = transactions.findIndex((t) => t.id === id);
+
+ if (transactionIndex === -1) {
+ return new Response(JSON.stringify({ error: "Transaction not found" }), {
+ status: 404,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+
+ const oldTransaction = transactions[transactionIndex];
+
+ // Get the old account first
+ const oldAccount = accounts.find((a) => a.id === oldTransaction.accountId);
+ if (!oldAccount) {
+ return new Response(JSON.stringify({ error: "Account not found" }), {
+ status: 404,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+
+ // If account is changing, validate new account exists
+ let newAccount = oldAccount;
+ if (updates.accountId && updates.accountId !== oldTransaction.accountId) {
+ const foundAccount = accounts.find((a) => a.id === updates.accountId);
+ if (!foundAccount) {
+ return new Response(JSON.stringify({ error: "Account not found" }), {
+ status: 404,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ newAccount = foundAccount;
+ }
+
+ // First, remove the old transaction's effect on the old account
+ oldAccount.balance -= oldTransaction.amount;
+
+ // Create updated transaction
+ const updatedTransaction: Transaction = {
+ ...oldTransaction,
+ ...updates,
+ id: id, // Ensure ID doesn't change
+ };
+
+ // Then add the new transaction's effect to the appropriate account
+ if (newAccount === oldAccount) {
+ // If same account, just add the new amount
+ oldAccount.balance += updatedTransaction.amount;
+ } else {
+ // If different account, add to the new account
+ newAccount.balance += updatedTransaction.amount;
+ }
+
+ // Update transaction in array
+ transactions[transactionIndex] = updatedTransaction;
+
+ return new Response(JSON.stringify(updatedTransaction), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ } catch (error) {
+ return new Response(JSON.stringify({ error: "Invalid request body" }), {
+ status: 400,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+};
+
+export const DELETE: APIRoute = async ({ params }) => {
+ const { id } = params;
+
+ if (!id) {
+ return new Response(
+ JSON.stringify({ error: "Transaction ID is required" }),
+ {
+ status: 400,
+ headers: { "Content-Type": "application/json" },
+ }
+ );
+ }
+
+ const transactionIndex = transactions.findIndex((t) => t.id === id);
+
+ if (transactionIndex === -1) {
+ return new Response(JSON.stringify({ error: "Transaction not found" }), {
+ status: 404,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+
+ const transaction = transactions[transactionIndex];
+ const account = accounts.find((a) => a.id === transaction.accountId);
+
+ if (!account) {
+ return new Response(JSON.stringify({ error: "Account not found" }), {
+ status: 404,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+
+ // Update account balance
+ account.balance -= transaction.amount;
+
+ // Remove transaction from array
+ transactions.splice(transactionIndex, 1);
+
+ return new Response(null, { status: 204 });
+};
diff --git a/src/pages/api/transactions/index.ts b/src/pages/api/transactions/index.ts
new file mode 100644
index 0000000..ef5d74e
--- /dev/null
+++ b/src/pages/api/transactions/index.ts
@@ -0,0 +1,79 @@
+/**
+ * TODO: Security Improvements
+ * - Add input validation and sanitization
+ * - Implement rate limiting for API endpoints
+ * - Add request authentication
+ * - Implement CSRF protection
+ * - Add request logging and monitoring
+ * - Implement secure session management
+ * - Add API versioning
+ * - Set up proper CORS configuration
+ */
+
+import type { APIRoute } from "astro";
+import { transactions, accounts } from "../../../data/store";
+import type { Transaction } from "../../../types";
+
+/**
+ * TODO: API Improvements
+ * - Add request rate limiting
+ * - Implement proper API authentication
+ * - Add input sanitization
+ * - Implement request validation middleware
+ * - Add API versioning
+ * - Consider implementing GraphQL for more flexible queries
+ * - Add proper logging and monitoring
+ */
+
+export const POST: APIRoute = async ({ request }) => {
+ try {
+ const transaction = (await request.json()) as Omit;
+
+ // Validate required fields
+ if (
+ !transaction.accountId ||
+ !transaction.date ||
+ !transaction.description ||
+ transaction.amount === undefined
+ ) {
+ return new Response(
+ JSON.stringify({ error: "Missing required fields" }),
+ {
+ status: 400,
+ headers: { "Content-Type": "application/json" },
+ }
+ );
+ }
+
+ // Validate account exists
+ const account = accounts.find((a) => a.id === transaction.accountId);
+ if (!account) {
+ return new Response(JSON.stringify({ error: "Account not found" }), {
+ status: 404,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+
+ // Create new transaction with generated ID
+ const newTransaction: Transaction = {
+ ...transaction,
+ id: (transactions.length + 1).toString(), // Simple ID generation for demo
+ };
+
+ // Update account balance
+ account.balance += transaction.amount;
+
+ // Add to transactions array
+ transactions.push(newTransaction);
+
+ return new Response(JSON.stringify(newTransaction), {
+ status: 201,
+ headers: { "Content-Type": "application/json" },
+ });
+ } catch (error) {
+ return new Response(JSON.stringify({ error: "Invalid request body" }), {
+ status: 400,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+};
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 19b86f1..4a51af1 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -3,106 +3,283 @@ import BaseLayout from '../layouts/BaseLayout.astro';
import Sidebar from '../components/Sidebar.astro';
import MainContent from '../components/MainContent.astro';
import type { Account, Transaction } from '../types';
-import { formatCurrency, formatDate } from '../utils';
-// Initialize with empty arrays until API integration
-const accounts: Account[] = [];
-const allTransactions: Transaction[] = [];
+// Fetch accounts from API
+const accountsResponse = await fetch('http://localhost:4321/api/accounts');
+const accounts: Account[] = await accountsResponse.json();
-// Create an empty initial account
-const initialAccount: Account = {
+// Initialize with first account or empty account if none exist
+const initialAccount: Account = accounts[0] || {
id: '',
name: 'No accounts available',
last4: '0000',
balance: 0
};
-const initialTransactions: Transaction[] = [];
+
+// Fetch initial transactions if we have an account
+let initialTransactions: Transaction[] = [];
+if (initialAccount.id) {
+ const transactionsResponse = await fetch(`http://localhost:4321/api/accounts/${initialAccount.id}/transactions`);
+ initialTransactions = await transactionsResponse.json();
+}
---
+
+
+
+
+
-