import { useState, useEffect } from "react"; import { format, parseISO } from "date-fns"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Calendar as CalendarUI } from "@/components/ui/calendar"; import { Calendar as CalendarIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import type { Holding } from "@/types/finance"; import { mergeHistory, historyPointsFor, ASSET_CATEGORIES } from "@/lib/holdings"; interface Props { holding: Holding; open: boolean; onOpenChange: (open: boolean) => void; onSave: (updated: Holding) => void; } export function EditHoldingDialog({ holding, open, onOpenChange, onSave }: Props) { const [name, setName] = useState(holding.name); const [category, setCategory] = useState(holding.category ?? "Other"); const [currency, setCurrency] = useState(holding.currency ?? "USD"); const [details, setDetails] = useState(holding.details ?? ""); const [amount, setAmount] = useState(String(holding.costBasis)); const [currentValue, setCurrentValue] = useState(String(holding.price)); const [ticker, setTicker] = useState(holding.symbol ?? ""); const [quantity, setQuantity] = useState(String(holding.shares ?? 1)); const [dateObj, setDateObj] = useState(() => { if (holding.dateInvested) { try { return parseISO(holding.dateInvested); } catch { /* fall through */ } } return new Date(); }); // Sync fields whenever the holding prop changes (e.g. opening for a different row) useEffect(() => { setName(holding.name); setCategory(holding.category ?? "Other"); setCurrency(holding.currency ?? "USD"); setDetails(holding.details ?? ""); setAmount(String(holding.costBasis)); setCurrentValue(String(holding.price)); setTicker(holding.symbol ?? ""); setQuantity(String(holding.shares ?? 1)); if (holding.dateInvested) { try { setDateObj(parseISO(holding.dateInvested)); } catch { setDateObj(new Date()); } } else { setDateObj(new Date()); } }, [holding]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!name || !amount) return; const dateStr = format(dateObj, "yyyy-MM-dd"); const costBasis = parseFloat(amount) || 0; const price = currentValue ? parseFloat(currentValue) : costBasis; const shares = parseFloat(quantity) > 0 ? parseFloat(quantity) : 1; // Merge, never replace: this dialog used to rebuild `history` from scratch, // so editing a holding erased every price point it had accumulated. const history = mergeHistory( holding.history, historyPointsFor(dateStr, costBasis, price, shares) ); const derived = name.length > 4 ? name.substring(0, 4).toUpperCase() : name.toUpperCase(); onSave({ ...holding, name, // A linked public ticker wins (enables live tracking); else fall back to a // name-derived placeholder for still-private holdings. symbol: ticker.trim() ? ticker.trim().toUpperCase() : derived, shares, price, costBasis, change: price - costBasis, changePercent: costBasis > 0 ? ((price - costBasis) / costBasis) * 100 : 0, currency, category, details, dateInvested: dateStr, history, }); onOpenChange(false); }; return ( Edit Investment
setName(e.target.value)} required />
d && setDateObj(d)} initialFocus />
setAmount(e.target.value)} required />
setQuantity(e.target.value)} />
setCurrentValue(e.target.value)} />
setTicker(e.target.value)} />

For a public ticker, enter avg cost in its trading currency (e.g. USD for SPCX). The live price will display in that native currency.

setDetails(e.target.value)} />
); }