mirror of
https://github.com/acedanger/finance.git
synced 2025-12-05 22:50:12 -08:00
feat: enhance module resolution and improve API error handling
This commit is contained in:
@@ -15,6 +15,15 @@ export default defineConfig({
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': '/src',
|
||||
'@components': '/src/components',
|
||||
'@layouts': '/src/layouts',
|
||||
'@data': '/src/data',
|
||||
'@pages': '/src/pages',
|
||||
'@styles': '/src/styles',
|
||||
'@stores': '/src/stores',
|
||||
'@utils': '/src/utils',
|
||||
'@types': '/src/types.ts',
|
||||
// Use the browser version of react-dom/server for client-side rendering
|
||||
'react-dom/server.browser': 'react-dom/cjs/react-dom-server.browser.production.min.js',
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { currentAccountId as currentAccountIdStore, refreshKey } from '@stores/transactionStore';
|
||||
import type { Account } from '@types';
|
||||
import { formatCurrency } from '@utils/formatters';
|
||||
import { formatCurrency } from '@utils';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function AccountSummary() {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
---
|
||||
import type { Account } from '@types';
|
||||
import type { Account, Transaction } from "@types";
|
||||
import TransactionTable from "./TransactionTable.astro";
|
||||
|
||||
interface Props {
|
||||
account: Account;
|
||||
transactions: Transaction[];
|
||||
}
|
||||
|
||||
const { account } = Astro.props;
|
||||
const { account, transactions } = Astro.props;
|
||||
---
|
||||
|
||||
<main class="main-content">
|
||||
<header class="main-header">
|
||||
<h1>
|
||||
Transactions for <span id="current-account-name"
|
||||
>{account.name} (***{account.accountNumber.slice(-3)})</span>
|
||||
>{account.name} (***{account.accountNumber.slice(-3)})</span
|
||||
>
|
||||
</h1>
|
||||
</header>
|
||||
<TransactionTable client:load />
|
||||
<TransactionTable client:load transactions={transactions} />
|
||||
</main>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
import type { Account } from '@types';
|
||||
import type { Account } from "@types";
|
||||
import AddTransactionForm from "./AddTransactionForm";
|
||||
import AccountSummary from "./AccountSummary";
|
||||
|
||||
interface Props {
|
||||
accounts: Account[];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
interface Props {
|
||||
export interface Props {
|
||||
title: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ import { AccountStatus, AccountType, accountService } from '@data/db.service';
|
||||
import type { Account } from '@types';
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
export const GET: APIRoute = async () => {
|
||||
export const GET: APIRoute = async ({ params, request }) => {
|
||||
try {
|
||||
console.log('GET /api/accounts - Fetching all accounts');
|
||||
const accounts = await accountService.getAll();
|
||||
console.log('GET /api/accounts - Found accounts:', accounts?.length ?? 0);
|
||||
|
||||
return new Response(JSON.stringify(accounts), {
|
||||
return new Response(JSON.stringify(accounts || []), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -14,12 +16,19 @@ export const GET: APIRoute = async () => {
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching accounts:', error);
|
||||
return new Response(JSON.stringify({ error: 'Failed to fetch accounts' }), {
|
||||
// Always return a proper JSON response, even in error cases
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'Failed to fetch accounts',
|
||||
details: error instanceof Error ? error.message : 'Unknown error',
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
---
|
||||
import type { Account, Transaction } from '@types';
|
||||
import BaseLayout from "@layouts/BaseLayout.astro";
|
||||
import MainContent from "@components/MainContent.astro";
|
||||
import Sidebar from "@components/Sidebar.astro";
|
||||
import AddTransactionForm from "@components/AddTransactionForm";
|
||||
import type { Account, Transaction } from "@types";
|
||||
|
||||
// Get the base URL from the incoming request
|
||||
const baseUrl = new URL(Astro.request.url).origin;
|
||||
|
||||
// Fetch accounts from API using absolute URL constructed from the request
|
||||
const accountsResponse = await fetch(`${baseUrl}/api/accounts`);
|
||||
const accounts: Account[] = await accountsResponse.json();
|
||||
// Create an instance of Astro's built-in fetch which handles SSR correctly
|
||||
let accounts: Account[] = [];
|
||||
try {
|
||||
const accountsResponse = await fetch(new URL("/api/accounts", Astro.url));
|
||||
if (!accountsResponse.ok) {
|
||||
const error = await accountsResponse.text();
|
||||
console.error("Failed to fetch accounts:", error);
|
||||
// Continue with empty accounts array
|
||||
} else {
|
||||
accounts = await accountsResponse.json();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching accounts:", error);
|
||||
// Continue with empty accounts array
|
||||
}
|
||||
|
||||
// Initialize with first account or empty account if none exist
|
||||
const initialAccount: Account = accounts[0] || {
|
||||
id: '',
|
||||
name: 'No accounts available',
|
||||
accountNumber: '000000',
|
||||
id: "",
|
||||
name: "No accounts available",
|
||||
accountNumber: "000000",
|
||||
balance: 0,
|
||||
bankName: '',
|
||||
bankName: "",
|
||||
};
|
||||
|
||||
// Fetch initial transactions if we have an account, using absolute URL
|
||||
// Fetch initial transactions if we have an account
|
||||
let initialTransactions: Transaction[] = [];
|
||||
if (initialAccount.id) {
|
||||
const transactionsResponse = await fetch(
|
||||
`${baseUrl}/api/accounts/${initialAccount.id}/transactions`,
|
||||
new URL(`/api/accounts/${initialAccount.id}/transactions`, Astro.url),
|
||||
);
|
||||
initialTransactions = await transactionsResponse.json();
|
||||
}
|
||||
@@ -30,7 +43,10 @@ if (initialAccount.id) {
|
||||
<BaseLayout title="Bank Transactions Dashboard">
|
||||
<div class="dashboard-layout">
|
||||
<Sidebar accounts={accounts} initialAccount={initialAccount} />
|
||||
<MainContent account={initialAccount} />
|
||||
<MainContent
|
||||
account={initialAccount}
|
||||
transactions={initialTransactions}
|
||||
/>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -56,20 +72,24 @@ if (initialAccount.id) {
|
||||
);
|
||||
|
||||
// --- DOM Elements ---
|
||||
const accountSelect = document.getElementById("account-select");
|
||||
const accountSelect = document.getElementById(
|
||||
"account-select",
|
||||
) as HTMLSelectElement | null;
|
||||
const currentAccountNameSpan = document.getElementById(
|
||||
"current-account-name",
|
||||
);
|
||||
) as HTMLSpanElement | null;
|
||||
const addTransactionSection = document.getElementById(
|
||||
"add-transaction-section",
|
||||
);
|
||||
const toggleAddTxnBtn = document.getElementById("toggle-add-txn-btn");
|
||||
) as HTMLElement | null;
|
||||
const toggleAddTxnBtn = document.getElementById(
|
||||
"toggle-add-txn-btn",
|
||||
) as HTMLButtonElement | null;
|
||||
|
||||
console.log("Initial setup - Account:", initialAccountData);
|
||||
console.log("Initial setup - Transactions:", initialTransactionsData);
|
||||
|
||||
// --- Helper Functions ---
|
||||
async function fetchAccountDetails(accountId) {
|
||||
async function fetchAccountDetails(accountId: string) {
|
||||
console.log("Fetching details for account:", accountId);
|
||||
try {
|
||||
const response = await fetch(`/api/accounts/${accountId}`);
|
||||
@@ -83,7 +103,7 @@ if (initialAccount.id) {
|
||||
}
|
||||
|
||||
// --- Update UI Function ---
|
||||
async function updateUIForAccount(accountId) {
|
||||
async function updateUIForAccount(accountId: string) {
|
||||
console.log("Updating UI for account:", accountId);
|
||||
|
||||
// Update the store with the current account ID
|
||||
@@ -118,7 +138,7 @@ if (initialAccount.id) {
|
||||
}
|
||||
|
||||
// --- Transaction Actions ---
|
||||
async function handleEditTransaction(txnId) {
|
||||
async function handleEditTransaction(txnId: string) {
|
||||
console.log("Edit transaction requested:", txnId);
|
||||
try {
|
||||
const accountId = currentAccountId.get();
|
||||
@@ -133,7 +153,9 @@ if (initialAccount.id) {
|
||||
if (!response.ok)
|
||||
throw new Error("Failed to fetch transactions for edit");
|
||||
const transactions = await response.json();
|
||||
const transaction = transactions.find((t) => t.id === txnId);
|
||||
const transaction = transactions.find(
|
||||
(t: { id: string }) => t.id === txnId,
|
||||
);
|
||||
|
||||
if (!transaction) {
|
||||
throw new Error("Transaction not found for editing");
|
||||
@@ -164,18 +186,18 @@ if (initialAccount.id) {
|
||||
|
||||
// --- Event Listeners ---
|
||||
if (accountSelect) {
|
||||
accountSelect.addEventListener("change", (event) => {
|
||||
const target = event.target;
|
||||
if (target && target.value) {
|
||||
accountSelect.addEventListener("change", (event: Event) => {
|
||||
const target = event.target as HTMLSelectElement;
|
||||
if (target?.value) {
|
||||
updateUIForAccount(target.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
const target = event.target;
|
||||
if (target && target.classList.contains("edit-btn")) {
|
||||
const row = target.closest("[data-txn-id]");
|
||||
document.addEventListener("click", (event: Event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
if (target?.classList?.contains("edit-btn")) {
|
||||
const row = target.closest("[data-txn-id]") as HTMLElement;
|
||||
if (row) {
|
||||
const txnId = row.dataset.txnId;
|
||||
if (txnId) handleEditTransaction(txnId);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"@pages/*": ["src/pages/*"],
|
||||
"@styles/*": ["src/styles/*"],
|
||||
"@stores/*": ["src/stores/*"],
|
||||
"@utils/*": ["src/utils.ts"],
|
||||
"@utils": ["src/utils"],
|
||||
"@types": ["src/types.ts"]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user