Files
personal-finance/frontend-web/src/components/dialogs/EditLiabilityDialog.tsx
Alexander Zinn 40210c454e Add lock files for package management and update architecture documentation
- Introduced bun.lock and package-lock.json to manage dependencies for the project.
- Enhanced backend API architecture documentation with additional security and documentation guidelines.
- Made minor formatting adjustments across various files for consistency and clarity.
2025-12-11 02:11:43 -05:00

151 lines
5.1 KiB
TypeScript

import {useState, useEffect} from 'react';
import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle} from '@/components/ui/dialog';
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select';
import {Button} from '@/components/ui/button';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useAppDispatch, updateLiability, deleteLiability, type Liability} from '@/store';
import {validatePositiveNumber, validateRequired} from '@/lib/validation';
interface Props {
open: boolean;
onOpenChange: (open: boolean) => void;
liability: Liability | null;
}
const liabilityTypes = ['credit_card', 'loan', 'mortgage', 'other'] as const;
export default function EditLiabilityDialog({open, onOpenChange, liability}: Props) {
const dispatch = useAppDispatch();
const [form, setForm] = useState({name: '', type: '', balance: ''});
const [errors, setErrors] = useState({name: '', balance: ''});
useEffect(() => {
if (liability) {
setForm({
name: liability.name,
type: liability.type,
balance: liability.balance.toString()
});
setErrors({name: '', balance: ''});
}
}, [liability]);
const validate = (): boolean => {
const newErrors = {name: '', balance: ''};
let isValid = true;
if (!validateRequired(form.name)) {
newErrors.name = 'Name is required';
isValid = false;
}
const balanceNum = validatePositiveNumber(form.balance);
if (balanceNum === null) {
newErrors.balance = 'Please enter a valid positive number';
isValid = false;
}
setErrors(newErrors);
return isValid;
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!liability || !validate()) return;
const balanceNum = validatePositiveNumber(form.balance);
if (balanceNum === null) return;
dispatch(
updateLiability({
id: liability.id,
data: {
name: form.name.trim(),
type: form.type.toUpperCase() as 'CREDIT_CARD' | 'LOAN' | 'MORTGAGE' | 'OTHER',
currentBalance: balanceNum,
}
})
);
onOpenChange(false);
};
const handleDelete = () => {
if (!liability) return;
if (confirm(`Are you sure you want to delete "${liability.name}"?`)) {
dispatch(deleteLiability(liability.id));
onOpenChange(false);
}
};
if (!liability) return null;
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="card-elevated sm:max-w-md">
<DialogHeader>
<DialogTitle>Edit Liability</DialogTitle>
<DialogDescription>Update liability details or delete this liability</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit}>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="edit-name">Name</Label>
<Input
id="edit-name"
placeholder="e.g., Credit Card"
value={form.name}
onChange={e => setForm({...form, name: e.target.value})}
className="input-depth"
required
/>
{errors.name && <p className="text-xs text-red-400">{errors.name}</p>}
</div>
<div className="grid gap-2">
<Label>Type</Label>
<Select value={form.type} onValueChange={v => setForm({...form, type: v})} required>
<SelectTrigger className="input-depth">
<SelectValue placeholder="Select type" />
</SelectTrigger>
<SelectContent>
{liabilityTypes.map(t => (
<SelectItem key={t} value={t} className="capitalize">
{t.replace('_', ' ')}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-balance">Balance</Label>
<Input
id="edit-balance"
type="number"
step="0.01"
min="0"
placeholder="0.00"
value={form.balance}
onChange={e => setForm({...form, balance: e.target.value})}
className="input-depth"
required
/>
{errors.balance && <p className="text-xs text-red-400">{errors.balance}</p>}
</div>
</div>
<DialogFooter className="flex justify-between sm:justify-between">
<Button type="button" variant="destructive" onClick={handleDelete} size="sm">
Delete
</Button>
<div className="flex gap-2">
<Button type="button" variant="secondary" onClick={() => onOpenChange(false)}>
Cancel
</Button>
<Button type="submit">Save Changes</Button>
</div>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}