Skip to content

Fix glitches reported by auction customers#8

Open
curiousdaniel wants to merge 1 commit into
mainfrom
fix/clerking-and-invoice-glitches
Open

Fix glitches reported by auction customers#8
curiousdaniel wants to merge 1 commit into
mainfrom
fix/clerking-and-invoice-glitches

Conversation

@curiousdaniel
Copy link
Copy Markdown
Collaborator

Summary

Eight reliability bugs reported by customers during live auctions, traced to concrete root causes and fixed end-to-end. Bugs only — feature requests deferred to a later plan.

  • Bidder didn't register / had to retry — submit lock + paddle pre-fill gate + immediate single-event snapshot flush so background pulls can't wipe local-only bidders.
  • Same item entered multiple timessubmittingRef guard on the sale form (covers onSubmit and Shift+Enter), button disabled in flight, and a 1.5s (event, lot, suffix, paddle, lineHammer) signature guard. Inside-tx salesOnLot.count() re-checks catch multi-device races.
  • Clerk re-picks consignor every lot — consignor and linkedConsignorId no longer cleared after each sale; lot autofill still overrides; new "Keep consignor between lots" pref (default on) in clerkFormPrefs.
  • Invoices tab scroll-jumping — sort by invoiceNumber (stable) instead of generatedAt; recalculateAndPersistInvoice is a true no-op when totals match; upsertInvoiceForBidder only bumps generatedAt when sales were actually allocated.
  • Total flickers partial → complete — allocate + recompute + persist now in one db.transaction("rw", [events, invoices, sales]); useLiveQuery fires once.
  • Paid invoices reverting to unpaidPaymentModal bumps generatedAt and uses enqueueInvoicePatch (not full invoice.put); applyRemoteOp and snapshotMerge preserve local paid status, paymentMethod, paymentDate, and generatedAt when an incoming row is unpaid.
  • Credit-card icon needs many clicksconfirming state on PaymentModal; row-reorder and revert fixes above remove the other contributors.
  • Items duplicated / missing on invoices — auto-call upsertInvoiceForBidder after every successful sale (kills "missing items"); inside-tx duplicate-sale guard; one-shot repairDuplicateInvoiceLines pass detaches legacy duplicate sale rows attached to the same lot on one invoice (gated by clerkbid:duplicateInvoiceLinesRepaired:v1); InvoiceDetail does display-side dedupe by lotId as belt-and-suspenders.

Files

  • app/(protected)/invoices/page.tsx
  • components/bidders/BidderForm.tsx
  • components/clerking/SaleForm.tsx
  • components/invoices/InvoiceDetail.tsx
  • components/invoices/PaymentModal.tsx
  • components/providers/UserDbProvider.tsx
  • lib/clerkFormPrefs.ts (+ test)
  • lib/db/repairDuplicateInvoiceLines.ts (new, + test)
  • lib/services/invoiceLogic.ts (+ test)
  • lib/services/snapshotMerge.ts (+ test)
  • lib/sync/ops/applyRemoteOp.ts

Test plan

  • npx vitest run — 162 tests pass (added: paid-status protection in snapshot merge, no-op recalc, sticky-consignor pref, duplicate-invoice-line repair).
  • npx tsc --noEmit clean.
  • npx next lint — only two pre-existing warnings in untouched code.
  • Smoke test on a staging device: register a bidder fast, double-press Record sale, mark an invoice paid then trigger a remote recalc, confirm no scroll jumping while ops fly in.
  • On a fresh DB upgraded from v6, confirm one-shot repair runs once and detaches legacy duplicates.

Made with Cursor

Submit-lock guards prevent double-submits across bidder add, sale form,
and payment modal. Bidder adds immediately flush a single-event cloud
snapshot so background pulls can't wipe local-only bidders.

Sticky consignor: stop clearing consignor between lots in clerking;
add an opt-out preference (default on). Lot autofill still overrides
when a different consignor is loaded.

Invoices stop scroll-jumping: list sorts by invoice number instead of
generatedAt; recalc skips the row update and generatedAt bump when
nothing actually changed; allocate + recalc now run in one Dexie
transaction so totals update once instead of flickering.

Paid invoices stop reverting to unpaid: PaymentModal bumps generatedAt
and uses enqueueInvoicePatch instead of full invoice.put. applyRemoteOp
and snapshotMerge preserve local paid status when an incoming row says
unpaid.

Auto-allocate sale to the bidder's open invoice on every successful
clerk write, so items can no longer go missing from invoices. Inside-tx
duplicate-sale guard catches multi-device races. One-shot repair pass
detaches legacy duplicate sale rows attached to the same lot on one
invoice (gated by a localStorage flag). InvoiceDetail does display-side
dedupe by lotId as a belt-and-suspenders.

Tests cover paid-status protection, no-op recalc, sticky consignor
pref, and the duplicate-line repair pass. All 162 tests pass; tsc and
next lint clean.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerkpad Ready Ready Preview, Comment May 21, 2026 4:47am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant