mirror of
https://github.com/acedanger/finance.git
synced 2025-12-05 22:50:12 -08:00
Add VSCode task for automatic build on project open and update dependencies
Remove .vscode from .gitignore to allow for project-specific settings. Introduce a VSCode task to run `npm run dev` automatically when the project is opened. Update environment configuration files and dependencies to enhance support for the Node adapter. Fixes #13
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import type { Transaction } from "../types";
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import type { Transaction } from '../types';
|
||||
// Import store atoms and actions
|
||||
import {
|
||||
currentAccountId as currentAccountIdStore,
|
||||
transactionToEdit as transactionToEditStore,
|
||||
cancelEditingTransaction,
|
||||
transactionSaved,
|
||||
} from "../stores/transactionStore";
|
||||
} from '../stores/transactionStore';
|
||||
|
||||
// Remove props that now come from the store
|
||||
interface AddTransactionFormProps {}
|
||||
@@ -18,9 +18,9 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
const transactionToEdit = useStore(transactionToEditStore);
|
||||
|
||||
// --- State Variables ---
|
||||
const [date, setDate] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [amount, setAmount] = useState("");
|
||||
const [date, setDate] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [amount, setAmount] = useState('');
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -32,7 +32,7 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
useEffect(() => {
|
||||
// Only set default date if not editing
|
||||
if (!transactionToEdit) {
|
||||
setDate(new Date().toISOString().split("T")[0]);
|
||||
setDate(new Date().toISOString().split('T')[0]);
|
||||
}
|
||||
}, [transactionToEdit]); // Rerun if edit mode changes
|
||||
|
||||
@@ -48,17 +48,14 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
// Directly format the date object (usually interpreted as UTC midnight)
|
||||
// into the YYYY-MM-DD format required by the input.
|
||||
// No timezone adjustment needed here.
|
||||
setDate(dateObj.toISOString().split("T")[0]);
|
||||
setDate(dateObj.toISOString().split('T')[0]);
|
||||
} else {
|
||||
console.warn(
|
||||
"Invalid date received for editing:",
|
||||
transactionToEdit.date
|
||||
);
|
||||
setDate(""); // Set to empty if invalid
|
||||
console.warn('Invalid date received for editing:', transactionToEdit.date);
|
||||
setDate(''); // Set to empty if invalid
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error parsing date for editing:", e);
|
||||
setDate(""); // Set to empty on error
|
||||
console.error('Error parsing date for editing:', e);
|
||||
setDate(''); // Set to empty on error
|
||||
}
|
||||
setDescription(transactionToEdit.description);
|
||||
setAmount(transactionToEdit.amount.toString());
|
||||
@@ -75,9 +72,9 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
// --- Helper Functions ---
|
||||
const resetForm = () => {
|
||||
setEditingId(null);
|
||||
setDate(new Date().toISOString().split("T")[0]);
|
||||
setDescription("");
|
||||
setAmount("");
|
||||
setDate(new Date().toISOString().split('T')[0]);
|
||||
setDescription('');
|
||||
setAmount('');
|
||||
setError(null);
|
||||
// Don't reset isLoading here, it's handled in submit/cancel
|
||||
};
|
||||
@@ -85,28 +82,28 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
const validateForm = (): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (!description || description.trim().length < 2) {
|
||||
errors.push("Description must be at least 2 characters long");
|
||||
errors.push('Description must be at least 2 characters long');
|
||||
}
|
||||
if (!amount) {
|
||||
errors.push("Amount is required");
|
||||
errors.push('Amount is required');
|
||||
} else {
|
||||
const amountNum = parseFloat(amount);
|
||||
if (isNaN(amountNum)) {
|
||||
errors.push("Amount must be a valid number");
|
||||
errors.push('Amount must be a valid number');
|
||||
} else if (amountNum === 0) {
|
||||
errors.push("Amount cannot be zero");
|
||||
errors.push('Amount cannot be zero');
|
||||
}
|
||||
}
|
||||
if (!date) {
|
||||
errors.push("Date is required");
|
||||
errors.push('Date is required');
|
||||
} else {
|
||||
try {
|
||||
const dateObj = new Date(date + "T00:00:00"); // Treat input as local date
|
||||
const dateObj = new Date(date + 'T00:00:00'); // Treat input as local date
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
errors.push("Invalid date format");
|
||||
errors.push('Invalid date format');
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push("Invalid date format");
|
||||
errors.push('Invalid date format');
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
@@ -118,13 +115,13 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
setError(null);
|
||||
|
||||
if (isLoading || !currentAccountId) {
|
||||
if (!currentAccountId) setError("No account selected.");
|
||||
if (!currentAccountId) setError('No account selected.');
|
||||
return;
|
||||
}
|
||||
|
||||
const validationErrors = validateForm();
|
||||
if (validationErrors.length > 0) {
|
||||
setError(validationErrors.join(". "));
|
||||
setError(validationErrors.join('. '));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,21 +137,17 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
amount: parseFloat(amount),
|
||||
};
|
||||
|
||||
const method = editingId ? "PUT" : "POST";
|
||||
const url = editingId
|
||||
? `/api/transactions/${editingId}`
|
||||
: "/api/transactions";
|
||||
const method = editingId ? 'PUT' : 'POST';
|
||||
const url = editingId ? `/api/transactions/${editingId}` : '/api/transactions';
|
||||
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(transactionData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMsg = `Failed to ${
|
||||
isEditMode ? "update" : "create"
|
||||
} transaction`;
|
||||
let errorMsg = `Failed to ${isEditMode ? 'update' : 'create'} transaction`;
|
||||
try {
|
||||
const errorData = await response.json();
|
||||
errorMsg = errorData.error || errorMsg;
|
||||
@@ -169,9 +162,7 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
transactionSaved(savedTransaction); // Call store action instead of prop callback
|
||||
resetForm(); // Reset form on success
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "An unexpected error occurred"
|
||||
);
|
||||
setError(err instanceof Error ? err.message : 'An unexpected error occurred');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -185,7 +176,7 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
// --- JSX ---
|
||||
return (
|
||||
<form id="add-transaction-form-react" onSubmit={handleSubmit} noValidate>
|
||||
<h4>{isEditMode ? "Edit Transaction" : "New Transaction"}</h4>
|
||||
<h4>{isEditMode ? 'Edit Transaction' : 'New Transaction'}</h4>
|
||||
{error && <div className="error-message">{error}</div>}
|
||||
|
||||
<div className="form-group">
|
||||
@@ -228,29 +219,18 @@ export default function AddTransactionForm({}: AddTransactionFormProps) {
|
||||
placeholder="e.g. -25.50 or 1200.00"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<small className="help-text">
|
||||
Use negative numbers for expenses (e.g., -50.00)
|
||||
</small>
|
||||
<small className="help-text">Use negative numbers for expenses (e.g., -50.00)</small>
|
||||
</div>
|
||||
<div className="button-group">
|
||||
<button
|
||||
type="submit"
|
||||
className={`form-submit-btn ${isLoading ? "loading" : ""}`}
|
||||
className={`form-submit-btn ${isLoading ? 'loading' : ''}`}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading
|
||||
? "Saving..."
|
||||
: isEditMode
|
||||
? "Update Transaction"
|
||||
: "Save Transaction"}
|
||||
{isLoading ? 'Saving...' : isEditMode ? 'Update Transaction' : 'Save Transaction'}
|
||||
</button>
|
||||
{isEditMode && (
|
||||
<button
|
||||
type="button"
|
||||
className="cancel-btn"
|
||||
onClick={handleCancel}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<button type="button" className="cancel-btn" onClick={handleCancel} disabled={isLoading}>
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user