From 1dbca1bcd44b1a9b173f15f53dbf1c05e7fb0672 Mon Sep 17 00:00:00 2001 From: MOHAN Date: Thu, 16 Jul 2026 23:43:23 +0530 Subject: [PATCH] Add transaction comments UI --- app/api/transactions/[id]/comments/route.ts | 16 +++ app/transactions/page.tsx | 117 +++++++++++++++++++- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 app/api/transactions/[id]/comments/route.ts diff --git a/app/api/transactions/[id]/comments/route.ts b/app/api/transactions/[id]/comments/route.ts new file mode 100644 index 0000000..1fd0384 --- /dev/null +++ b/app/api/transactions/[id]/comments/route.ts @@ -0,0 +1,16 @@ +import { NextRequest } from "next/server"; +import { proxyRequest } from "@/lib/backend"; + +export async function GET( + req: NextRequest, + { params }: { params: { id: string } } +) { + return proxyRequest(req, `transactions/${params.id}/comments`); +} + +export async function POST( + req: NextRequest, + { params }: { params: { id: string } } +) { + return proxyRequest(req, `transactions/${params.id}/comments`); +} diff --git a/app/transactions/page.tsx b/app/transactions/page.tsx index 19a7485..df9d919 100644 --- a/app/transactions/page.tsx +++ b/app/transactions/page.tsx @@ -22,9 +22,21 @@ type TransactionRow = { }; status?: string; hidden?: boolean; + commentCount?: number; date: string; }; +type TransactionComment = { + id: string; + body: string; + createdAt: string; + updatedAt: string; + author: { + displayName: string; + email?: string | null; + }; +}; + type Account = { viewRef: string; institutionName: string; @@ -125,6 +137,10 @@ export default function TransactionsPage() { splitYoursPercent: "50", }); const [editingRef, setEditingRef] = useState(null); + const [commentRef, setCommentRef] = useState(null); + const [comments, setComments] = useState([]); + const [commentDraft, setCommentDraft] = useState(""); + const [commentStatus, setCommentStatus] = useState(""); const [editForm, setEditForm] = useState({ category: "", note: "", @@ -400,6 +416,37 @@ export default function TransactionsPage() { }); }; + const openComments = async (row: TransactionRow) => { + setCommentRef(row.viewRef); + setComments([]); + setCommentDraft(""); + setCommentStatus("Loading comments..."); + const res = await apiFetch(`/api/transactions/${row.viewRef}/comments`); + if (res.error) { + setCommentStatus(res.error.message ?? "Unable to load comments."); + return; + } + setComments(res.data ?? []); + setCommentStatus(""); + }; + + const createComment = async () => { + if (!commentRef || !commentDraft.trim()) return; + setCommentStatus("Posting comment..."); + const res = await apiFetch(`/api/transactions/${commentRef}/comments`, { + method: "POST", + body: JSON.stringify({ body: commentDraft }), + }); + if (res.error) { + setCommentStatus(res.error.message ?? "Unable to post comment."); + return; + } + if (res.data) setComments((prev) => [...prev, res.data]); + setCommentDraft(""); + setCommentStatus(""); + await load(); + }; + const saveEdit = async () => { if (!editingRef) return; setStatus("Saving edits..."); @@ -423,6 +470,7 @@ export default function TransactionsPage() { const inputCls = "mt-2 w-full rounded-md border border-border bg-background/50 px-3 py-2 text-sm text-foreground focus:border-primary focus:ring-primary focus:outline-none"; const labelCls = "text-xs font-semibold text-muted-foreground uppercase tracking-wider"; + const selectedCommentRow = rows.find((row) => row.viewRef === commentRef) ?? null; return ( @@ -780,6 +828,68 @@ export default function TransactionsPage() { {status && (
{status}
)} + {selectedCommentRow && ( +
+
+
+

Transaction chat

+

{selectedCommentRow.description ?? selectedCommentRow.name ?? "Transaction"}

+

+ {new Date(selectedCommentRow.date).toLocaleDateString()} ยท {formatAmount(selectedCommentRow.amount).display} +

+
+ +
+
+
+ {comments.map((comment) => ( +
+
+

{comment.author.displayName}

+

{new Date(comment.createdAt).toLocaleString()}

+
+

{comment.body}

+
+ ))} + {!comments.length && !commentStatus && ( +

No comments yet.

+ )} + {commentStatus &&

{commentStatus}

} +
+
+