From f53a0aaf4602ed20f8df7cb54129527cbd4580df Mon Sep 17 00:00:00 2001 From: renee-png Date: Sun, 7 Jun 2026 15:12:50 -0400 Subject: [PATCH] Owner Ledger: add $0.00 note entries Add an "Add Note" action that records a memo on an owner's ledger as a $0.00 entry (transaction_type 'note', debit/credit 0). Notes work on any ledger including paid-up ($0.00 balance) accounts, render as styled memo rows, and don't affect the running balance. The accounting sync treats a zero entry as a no-op, so no phantom AR is created. Co-Authored-By: Claude Opus 4.8 --- src/pages/OwnerLedgerPage.tsx | 69 +++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/src/pages/OwnerLedgerPage.tsx b/src/pages/OwnerLedgerPage.tsx index 1cf4b99..1fe316d 100644 --- a/src/pages/OwnerLedgerPage.tsx +++ b/src/pages/OwnerLedgerPage.tsx @@ -1,10 +1,11 @@ import { useState, useEffect, useMemo } from "react"; import { supabase } from "@/integrations/supabase/client"; import { useToast } from "@/hooks/use-toast"; -import { BookOpen, Plus, Search, Download, DollarSign } from "lucide-react"; +import { BookOpen, Plus, Search, Download, DollarSign, StickyNote } from "lucide-react"; import UnitOwnerSelect from "@/components/UnitOwnerSelect"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; @@ -22,7 +23,7 @@ const txnTypeLabels: Record = { assessment: "Assessment", special_assessment: "Special Assessment", late_fee: "Late Fee", interest: "Interest", fine: "Fine", admin_charge: "Admin Charge", payment: "Payment", reversal: "Reversal", - credit: "Credit", adjustment: "Adjustment" + credit: "Credit", adjustment: "Adjustment", note: "Note" }; export default function OwnerLedgerPage() { @@ -43,6 +44,9 @@ export default function OwnerLedgerPage() { credit: "", unit_id: "", }); + const [noteDialogOpen, setNoteDialogOpen] = useState(false); + const [noteSaving, setNoteSaving] = useState(false); + const [noteForm, setNoteForm] = useState({ date: new Date().toISOString().split("T")[0], text: "" }); useEffect(() => { Promise.all([ @@ -127,6 +131,31 @@ export default function OwnerLedgerPage() { await supabase.from("owners").update({ balance: newBal }).eq("id", selectedOwnerId); }; + // A note is a $0.00 ledger entry — a memo that records context without + // affecting the running balance. Works on any ledger, including $0 ones. + const handleAddNote = async () => { + if (!selectedOwnerId) return; + const text = noteForm.text.trim(); + if (!text) { toast({ variant: "destructive", title: "Note is empty" }); return; } + setNoteSaving(true); + const owner = owners.find(o => o.id === selectedOwnerId); + const { error } = await supabase.from("owner_ledger_entries").insert({ + owner_id: selectedOwnerId, + association_id: owner?.association_id, + unit_id: owner?.unit_id || null, + date: noteForm.date, + transaction_type: "note", + description: text, + debit: 0, + credit: 0, + }); + setNoteSaving(false); + if (error) { toast({ variant: "destructive", title: "Error", description: error.message }); return; } + toast({ title: "Note added" }); + setNoteDialogOpen(false); + fetchLedger(); + }; + const exportCSV = async () => { const rows = [ ["Date", "Type", "Description", "Debit", "Credit", "Balance"].join(","), @@ -145,6 +174,10 @@ export default function OwnerLedgerPage() {
+ + + + + Add Note +
+

+ A note is recorded on the ledger as a $0.00 entry — it documents context without changing the balance. +

+
setNoteForm({ ...noteForm, date: e.target.value })} />
+