transitioned from astro to react

This commit is contained in:
Peter Wood
2025-06-04 21:03:32 -04:00
parent 570ed2d1b4
commit 52547578a7
32 changed files with 2631 additions and 4865 deletions

View File

@@ -0,0 +1,189 @@
import { Router } from 'express';
import { accountService, transactionService } from '../data/db.service.js';
import type { Transaction, TransactionStatus, TransactionType } from '../types.js';
const router = Router();
// Helper function to transform update data
function transformUpdateData(updateData: Partial<Omit<Transaction, 'id'>>) {
const typedUpdateData: {
accountId?: string;
date?: Date;
description?: string;
amount?: number;
category?: string;
status?: TransactionStatus;
type?: TransactionType;
notes?: string;
tags?: string;
} = {};
// Convert string date to Date object if provided
if (updateData.date) {
typedUpdateData.date =
typeof updateData.date === 'string' ? new Date(updateData.date) : updateData.date;
}
// Convert amount to number if provided
if (updateData.amount !== undefined) {
typedUpdateData.amount = Number(updateData.amount);
}
// Copy other fields
if (updateData.accountId) typedUpdateData.accountId = updateData.accountId;
if (updateData.description) typedUpdateData.description = updateData.description;
if (updateData.category !== undefined)
typedUpdateData.category = updateData.category || undefined;
if (updateData.status) typedUpdateData.status = updateData.status as TransactionStatus;
if (updateData.type) typedUpdateData.type = updateData.type as TransactionType;
if (updateData.notes !== undefined) typedUpdateData.notes = updateData.notes || undefined;
if (updateData.tags !== undefined) typedUpdateData.tags = updateData.tags || undefined;
return typedUpdateData;
}
// POST /api/transactions - Create new transaction
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
router.post('/', async (req: any, res: any) => {
try {
const transaction = req.body as Omit<Transaction, 'id'>;
// Validate required fields
if (
!transaction.accountId ||
!transaction.date ||
!transaction.description ||
transaction.amount === undefined
) {
return res.status(400).json({ error: 'Missing required fields' });
}
// Validate account exists
const account = await accountService.getById(transaction.accountId);
if (!account) {
return res.status(404).json({ error: 'Account not found' });
}
// Convert string date to Date object if needed
const transactionDate =
typeof transaction.date === 'string' ? new Date(transaction.date) : transaction.date;
// Create new transaction with database service
const newTransaction = await transactionService.create({
accountId: transaction.accountId,
date: transactionDate,
description: transaction.description,
amount: Number(transaction.amount),
category: transaction.category || undefined,
status: transaction.status as TransactionStatus,
type: transaction.type as TransactionType,
notes: transaction.notes || undefined,
tags: transaction.tags || undefined,
});
// Convert Decimal to number for response
const response = {
...newTransaction,
amount: Number(newTransaction.amount),
};
res.status(201).json(response);
} catch (error) {
console.error('Error creating transaction:', error);
res.status(500).json({ error: 'Failed to create transaction' });
}
});
// GET /api/transactions/:id - Get single transaction
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
router.get('/:id', async (req: any, res: any) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({ error: 'Transaction ID is required' });
}
const transaction = await transactionService.getById(id);
if (!transaction) {
return res.status(404).json({ error: 'Transaction not found' });
}
// Convert Decimal to number for response
const response = {
...transaction,
amount: Number(transaction.amount),
};
res.json(response);
} catch (error) {
console.error('Error fetching transaction:', error);
res.status(500).json({ error: 'Failed to fetch transaction' });
}
});
// PUT /api/transactions/:id - Update transaction
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
router.put('/:id', async (req: any, res: any) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({ error: 'Transaction ID is required' });
}
// Check if transaction exists
const existingTransaction = await transactionService.getById(id);
if (!existingTransaction) {
return res.status(404).json({ error: 'Transaction not found' });
}
const updateData = req.body as Partial<Omit<Transaction, 'id'>>;
const typedUpdateData = transformUpdateData(updateData);
const updatedTransaction = await transactionService.update(id, typedUpdateData);
if (!updatedTransaction) {
return res.status(404).json({ error: 'Transaction not found or could not be updated' });
}
// Convert Decimal to number for response
const response = {
...updatedTransaction,
amount: Number(updatedTransaction.amount),
};
res.json(response);
} catch (error) {
console.error('Error updating transaction:', error);
res.status(500).json({ error: 'Failed to update transaction' });
}
});
// DELETE /api/transactions/:id - Delete transaction
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
router.delete('/:id', async (req: any, res: any) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({ error: 'Transaction ID is required' });
}
// Check if transaction exists
const existingTransaction = await transactionService.getById(id);
if (!existingTransaction) {
return res.status(404).json({ error: 'Transaction not found' });
}
await transactionService.delete(id);
res.status(204).send();
} catch (error) {
console.error('Error deleting transaction:', error);
res.status(500).json({ error: 'Failed to delete transaction' });
}
});
export default router;