feat: make MH Paid column inline-editable
Add PATCH /api/payments/:id/mh-paid-amount for direct value updates. Clicking the MH Paid cell opens an input; Enter/blur saves and refreshes, Escape cancels. Dash placeholder is also clickable to enter a value. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -427,6 +427,34 @@ router.patch(
|
||||
}
|
||||
);
|
||||
|
||||
// PATCH /api/payments/:id/mh-paid-amount
|
||||
router.patch(
|
||||
"/:id/mh-paid-amount",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const paymentId = parseIntOrError(req.params.id, "Payment ID");
|
||||
const raw = req.body.mhPaidAmount;
|
||||
const mhPaidAmount = parseFloat(raw);
|
||||
if (isNaN(mhPaidAmount) || mhPaidAmount < 0) {
|
||||
return res.status(400).json({ message: "Invalid mhPaidAmount value" });
|
||||
}
|
||||
|
||||
const updated = await prisma.payment.update({
|
||||
where: { id: paymentId },
|
||||
data: { mhPaidAmount, updatedById: userId },
|
||||
});
|
||||
|
||||
return res.json({ ...updated, mhPaidAmount: Number(updated.mhPaidAmount) });
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : "Failed to update MH paid amount";
|
||||
return res.status(500).json({ message });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// PATCH /api/payments/:id/mh-payment-check
|
||||
router.patch(
|
||||
"/:id/mh-payment-check",
|
||||
|
||||
@@ -99,6 +99,8 @@ export default function PaymentsRecentTable({
|
||||
const [selectedPaymentId, setSelectedPaymentId] = useState<number | null>(null);
|
||||
const [checkedPaymentIds, setCheckedPaymentIds] = useState<Set<number>>(new Set());
|
||||
const [isMhChecking, setIsMhChecking] = useState(false);
|
||||
const [editingMhPaidId, setEditingMhPaidId] = useState<number | null>(null);
|
||||
const [editingMhPaidValue, setEditingMhPaidValue] = useState<string>("");
|
||||
|
||||
const [isRevertOpen, setIsRevertOpen] = useState(false);
|
||||
const [revertPaymentId, setRevertPaymentId] = useState<number | null>(null);
|
||||
@@ -741,12 +743,60 @@ export default function PaymentsRecentTable({
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
{payment.mhPaidAmount != null ? (
|
||||
<span className="text-sm font-medium text-green-700">
|
||||
${Number(payment.mhPaidAmount).toFixed(2)}
|
||||
</span>
|
||||
{editingMhPaidId === payment.id ? (
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.01"
|
||||
autoFocus
|
||||
className="w-24 border border-blue-400 rounded px-1 py-0.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-300"
|
||||
value={editingMhPaidValue}
|
||||
onChange={(e) => setEditingMhPaidValue(e.target.value)}
|
||||
onKeyDown={async (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.currentTarget.blur();
|
||||
} else if (e.key === "Escape") {
|
||||
setEditingMhPaidId(null);
|
||||
}
|
||||
}}
|
||||
onBlur={async () => {
|
||||
const val = parseFloat(editingMhPaidValue);
|
||||
if (!isNaN(val) && val >= 0) {
|
||||
try {
|
||||
const res = await apiRequest(
|
||||
"PATCH",
|
||||
`/api/payments/${payment.id}/mh-paid-amount`,
|
||||
{ mhPaidAmount: val }
|
||||
);
|
||||
if (res.ok) {
|
||||
await queryClient.invalidateQueries({ queryKey: QK_PAYMENTS_RECENT_BASE });
|
||||
} else {
|
||||
toast({ title: "Error", description: "Failed to save MH paid amount.", variant: "destructive" });
|
||||
}
|
||||
} catch {
|
||||
toast({ title: "Error", description: "Failed to save MH paid amount.", variant: "destructive" });
|
||||
}
|
||||
}
|
||||
setEditingMhPaidId(null);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span className="text-xs text-gray-400">—</span>
|
||||
<span
|
||||
className="text-sm font-medium text-green-700 cursor-pointer hover:underline hover:text-green-900"
|
||||
title="Click to edit"
|
||||
onClick={() => {
|
||||
setEditingMhPaidId(payment.id);
|
||||
setEditingMhPaidValue(
|
||||
payment.mhPaidAmount != null
|
||||
? Number(payment.mhPaidAmount).toFixed(2)
|
||||
: "0.00"
|
||||
);
|
||||
}}
|
||||
>
|
||||
{payment.mhPaidAmount != null
|
||||
? `$${Number(payment.mhPaidAmount).toFixed(2)}`
|
||||
: <span className="text-gray-400 font-normal">—</span>}
|
||||
</span>
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user