177 lines
6.3 KiB
TypeScript
177 lines
6.3 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
|
|
interface Transaction {
|
|
id: string;
|
|
envelopeId?: string;
|
|
amount: number;
|
|
description: string;
|
|
date: Date;
|
|
accountId: string;
|
|
}
|
|
|
|
interface TransactionEditProps {
|
|
transaction: Transaction;
|
|
envelopes: { id: string; name: string }[];
|
|
accounts: { id: string; name: string }[];
|
|
onSave: (updatedTransaction: Transaction) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
export default function TransactionEdit({ transaction, envelopes, accounts, onSave, onCancel }: TransactionEditProps) {
|
|
const [amount, setAmount] = useState(Math.abs(transaction.amount).toString());
|
|
const [description, setDescription] = useState(transaction.description);
|
|
const [date, setDate] = useState(transaction.date.toISOString().split('T')[0]);
|
|
const [selectedEnvelope, setSelectedEnvelope] = useState(transaction.envelopeId || '');
|
|
const [selectedAccount, setSelectedAccount] = useState(transaction.accountId);
|
|
const [isExpense, setIsExpense] = useState(transaction.amount < 0);
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
const numAmount = parseFloat(amount);
|
|
if (isNaN(numAmount) || !description.trim()) {
|
|
alert('Please enter a valid amount and description');
|
|
return;
|
|
}
|
|
|
|
const transactionAmount = isExpense ? -Math.abs(numAmount) : Math.abs(numAmount);
|
|
|
|
onSave({
|
|
...transaction,
|
|
amount: transactionAmount,
|
|
description: description.trim(),
|
|
date: new Date(date),
|
|
envelopeId: selectedEnvelope || undefined,
|
|
accountId: selectedAccount,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
<div className="bg-white rounded-lg p-6 w-full max-w-md mx-4">
|
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">Edit Transaction</h3>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Amount
|
|
</label>
|
|
<div className="relative">
|
|
<span className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">$</span>
|
|
<input
|
|
type="number"
|
|
step="0.01"
|
|
value={amount}
|
|
onChange={(e) => setAmount(e.target.value)}
|
|
placeholder="0.00"
|
|
className="w-full pl-8 pr-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
value={date}
|
|
onChange={(e) => setDate(e.target.value)}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Description
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
placeholder="Enter transaction description"
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Assign to Envelope
|
|
</label>
|
|
<select
|
|
value={selectedEnvelope}
|
|
onChange={(e) => setSelectedEnvelope(e.target.value)}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
<option value="">No envelope</option>
|
|
{envelopes.map((envelope) => (
|
|
<option key={envelope.id} value={envelope.id}>
|
|
{envelope.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Assign to Account
|
|
</label>
|
|
<select
|
|
value={selectedAccount}
|
|
onChange={(e) => setSelectedAccount(e.target.value)}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
{accounts.map((account) => (
|
|
<option key={account.id} value={account.id}>
|
|
{account.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-4 p-3 bg-gray-50 rounded-md">
|
|
<label className="flex items-center cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
checked={isExpense}
|
|
onChange={() => setIsExpense(true)}
|
|
className="mr-2"
|
|
/>
|
|
<span className="text-sm font-medium text-red-600">💸 Expense (Negative)</span>
|
|
</label>
|
|
<label className="flex items-center cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
checked={!isExpense}
|
|
onChange={() => setIsExpense(false)}
|
|
className="mr-2"
|
|
/>
|
|
<span className="text-sm font-medium text-green-600">💰 Income (Positive)</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div className="flex space-x-3 pt-4">
|
|
<button
|
|
type="submit"
|
|
className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
Save Changes
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={onCancel}
|
|
className="flex-1 bg-gray-300 text-gray-700 py-2 px-4 rounded-md hover:bg-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500"
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |