import React, { useState } from 'react'; import { toast } from 'sonner'; import { api } from '@/api.js'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog'; import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, } from '@/components/ui/alert-dialog'; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from '@/components/ui/select'; const METHOD_NONE = 'none'; function PaymentModal({ payment, autopayEnabled, onClose, onSave }) { const [amount, setAmount] = useState(String(payment.amount)); const [date, setDate] = useState(payment.paid_date); // Use METHOD_NONE sentinel — empty string value crashes Radix Select const [method, setMethod] = useState(payment.method || METHOD_NONE); const [notes, setNotes] = useState(payment.notes || ''); const [autopayFailure, setAutopayFailure] = useState(!!payment.autopay_failure); const [busy, setBusy] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const showAutopayFailure = autopayEnabled || method === 'autopay'; async function handleSave(e) { e.preventDefault(); setBusy(true); try { await api.updatePayment(payment.id, { amount: parseFloat(amount), paid_date: date, method: method === METHOD_NONE ? null : method, notes: notes || null, autopay_failure: showAutopayFailure ? (autopayFailure ? 1 : 0) : undefined, }); toast.success('Payment saved'); onSave(); onClose(); } catch (err) { toast.error(err.message); } finally { setBusy(false); } } async function handleDelete() { setBusy(true); try { await api.deletePayment(payment.id); toast.success('Payment moved to recovery. Bill is now marked as unpaid.', { action: { label: 'Undo', onClick: async () => { try { await api.restorePayment(payment.id); toast.success('Payment restored'); onSave(); } catch (err) { toast.error(err.message || 'Failed to restore payment'); } }, }, }); onSave(); onClose(); } catch (err) { toast.error(err.message); } finally { setBusy(false); } } return ( <> { if (!v) onClose(); }}> Edit Payment Amount ($) * setAmount(e.target.value)} className="font-mono bg-background/50 border-border/60" /> Paid Date * setDate(e.target.value)} className="font-mono bg-background/50 border-border/60" /> Method — Bank Transfer Card Autopay Check Cash Notes setNotes(e.target.value)} className="bg-background/50 border-border/60" /> {showAutopayFailure && ( setAutopayFailure(e.target.checked)} className="h-4 w-4 rounded border-border accent-amber-500" /> Autopay failed — paid manually )} setConfirmDelete(true)} className="text-xs" title="Removes this payment record. The bill itself is NOT deleted." > Remove Payment Cancel Save Remove this payment? This marks the payment as removed and reverses any debt balance update. You can undo it from the toast. Cancel {busy ? 'Removing...' : 'Remove Payment'} > ); } export default PaymentModal;