feat(invoices): accept button + status filter#474
Conversation
Add an Accept action on received invoices so the payer can confirm intent to pay: sets metadata.accepted_at and notifies the worker the invoice was accepted and will be paid soon. Accepting doesn't move money — it queues the invoice to be paid. Replace the extra "Accepted" tab with a status filter (All / Pending / Accepted / Paid) on both the Received and Sent tabs, so accepted-but- unpaid invoices can be triaged and paid quickly. No DB migration — reuses the gig_invoices.metadata JSONB column. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
Greptile SummaryThis PR adds an Accept action for received invoices and a client-side status filter (All / Pending / Accepted / Paid) on the invoices dashboard. Acceptance is stored as
Confidence Score: 3/5Safe to merge after fixing the notification type; the wrong label could mislead workers into believing payment was received when it has not. The accept route sends a notification with type payment_received even though no money has moved. Workers could misread this as payment confirmation and stop following up. The rest of the change — idempotency logic, auth guards, client state, and tests — is well-structured. src/app/api/gigs/[id]/invoice/[invoiceId]/accept/route.ts — notification type and missing error handling need attention before merging. Important Files Changed
Sequence DiagramsequenceDiagram
participant Payer as Payer (Browser)
participant API as POST /accept
participant DB as gig_invoices
participant Notif as notifications
participant Worker as Worker
Payer->>API: POST accept
API->>DB: SELECT invoice
DB-->>API: invoice row
API->>API: Guard poster_id + status
alt already accepted
API-->>Payer: 200 accepted_at existing
else first acceptance
API->>DB: UPDATE metadata.accepted_at
API->>Notif: INSERT type payment_received
Note over Notif: error silently dropped
API-->>Payer: 200 accepted_at new
end
Payer->>Payer: show Accepted indicator
Worker->>Worker: sees mislabelled notification
Reviews (1): Last reviewed commit: "feat(invoices): accept invoices + status..." | Re-trigger Greptile |
| const serviceSupabase = createServiceClient(); | ||
| await (serviceSupabase.from("notifications") as any).insert({ | ||
| user_id: invoice.worker_id, | ||
| type: "payment_received", |
There was a problem hiding this comment.
Wrong notification type for acceptance event
type: "payment_received" misrepresents an acceptance as a completed payment. If the notification UI, analytics, or any downstream handler branch on type, the worker would see a "payment received" signal when no money has moved yet. This could cause workers to treat an acceptance as full confirmation of payment, leading to disputes. A value like "invoice_accepted" (matching the surrounding title/body copy) is semantically correct.
| const serviceSupabase = createServiceClient(); | ||
| await (serviceSupabase.from("notifications") as any).insert({ | ||
| user_id: invoice.worker_id, | ||
| type: "payment_received", | ||
| title: "Invoice accepted", | ||
| body: `The client accepted your invoice for "${title}" and will pay it soon.`, | ||
| data: { | ||
| gig_id: invoice.gig_id, | ||
| invoice_id: invoice.id, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Notification insert errors are silently discarded
Supabase .insert() never throws — it returns { data, error }. The returned value is not destructured here, so a failed insert (DB error, missing table, RLS rejection) is completely invisible. At minimum the error should be logged with console.error so on-call engineers can detect notification delivery failures without running queries against the table.
| @@ -194,7 +239,7 @@ export default async function InvoicesDashboardPage({ | |||
| </div> | |||
There was a problem hiding this comment.
"Accepted — ready to pay" overlaps with "You owe" without making the relationship clear
totalOwed is computed over all awaiting-payment received invoices (including accepted ones), and totalAccepted is a strict subset of that same group. Displaying both as separate stat cards without any visual cue that one is contained in the other could cause a payer to mentally add the two figures and believe they owe more than they do. Consider labelling totalOwed as "Total owed (incl. accepted)" or subtracting totalAccepted from totalOwed to show a "Not yet committed" figure instead.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
What
Adds an Accept action on received invoices plus a status filter so a payer can triage and pay quickly.
metadata.accepted_atand notifies the worker that the invoice was accepted and will be paid soon. Accepting doesn't move money — it queues the invoice to be paid. Once accepted it shows a durable "Accepted — will be paid soon" indicator.POST /api/gigs/[id]/invoice/[invoiceId]/accept(mirrors the existingrejectroute; only the payer/poster can accept; guards paid/rejected/cancelled; idempotent).DB
No migration — reuses the
gig_invoices.metadataJSONB column (same soft-flag pattern asreplacement_requested_at).Tests
InvoicePaymentActions.test.tsxextended (accept click → endpoint → indicator, and already-accepted state). type-check + targeted tests green on top of current master.🤖 Generated with Claude Code