Skip to content

Beanstown/ClaimFlow

Repository files navigation

ClaimFlow

A modern insurance-claim-check / mortgage loss-draft disbursement portal — built as an opinionated reaction to how brittle and dead-end the real ones are.

The headline feature: a homeowner can't get trapped by a typo. If they enter a check with the wrong cents, the portal won't let them silently re-submit a duplicate, and gives them a real correction path with a full audit trail.

Open in GitHub Codespaces Open in StackBlitz

Codespaces and StackBlitz run the seeded SQLite demo with no extra config — they pick up .devcontainer/devcontainer.json and stackblitz.json respectively.

What's in the demo

Persona Path What you can do
Homeowner /dashboard See claim status, funds release stages, documents, activity, and submitted checks
Homeowner /dashboard/checks/new Submit a new check with currency-safe input + review screen
Homeowner /dashboard/checks/[id]/correct File a correction request on an existing check
Servicer admin /admin Review pending corrections, see flagged duplicate attempts, release funds
Compliance /audit Append-only audit feed of every create, update, correction, and release

The seed script pre-loads:

  • One homeowner (Alex Rivera) and one servicer admin (Jordan Park)
  • One mortgage loan (LN-44820137) on a Texas property
  • One hailstorm claim (HC-2026-00913) with three release stages
  • One submitted check #123456 for $28,000.82 — the typo, 82¢ instead of 02¢
  • One blocked duplicate-submission attempt at $28,000.02
  • One pending correction request sitting in the admin queue

Try it in 90 seconds

  1. Open /dashboard/checks/new.
  2. Use check number 123456 and any amount, payees, etc.
  3. Click through the review screen and submit.
  4. The duplicate-block modal appears with View existing / File correction.
  5. Hop to /admin and approve the seeded correction. Watch the check amount update.
  6. Open /audit to see the paired audit entries with the old/new diff.

Local dev

git clone https://github.com/Beanstown/ClaimFlow
cd ClaimFlow
npm install
cp .env.example .env
npm run setup     # prisma db push + seed demo data
npm run dev       # localhost:3000

Or one-shot via the helper script:

bash scripts/init.sh

Stack

  • Next.js 16 App Router + TypeScript
  • Tailwind CSS with shadcn-style primitives (no UI vendor lock-in — owned in src/components/ui)
  • Prisma ORM + SQLite for self-contained demo
  • Server actions for mutations, no separate API layer
  • Zod + React Hook Form for validation
  • Sonner for toasts, Radix UI for accessible dialogs/tabs

Screenshots

Landing

Landing

Homeowner dashboard

Homeowner dashboard

Add a claim check

Add claim check

Servicer admin queue

Admin queue

Audit log

Audit log

To regenerate: bash scripts/screenshots.sh (requires Google Chrome; expects dev server on port 3037 — pass URL_BASE=http://localhost:3000 to override).

How duplicate prevention works

The data model treats a check number as unique within the (loan, claim) tuple:

model Check {
  // ...
  loanNumber  String
  claimNumber String
  checkNumber String

  @@unique([loanNumber, claimNumber, checkNumber])
}

loanNumber and claimNumber are denormalized onto the Check row specifically so the unique constraint is enforced at the DB level — not just by app code. SQLite, Postgres, and MySQL all back this with a real UNIQUE INDEX.

The submit flow (src/app/actions/checks.ts) does not rely on catching P2002 after-the-fact. It looks up the conflict first and surfaces a useful payload:

const existing = await prisma.check.findUnique({
  where: { loanNumber_claimNumber_checkNumber: { ... } },
});

if (existing) {
  // Log the attempt for the admin queue.
  await prisma.duplicateAttempt.create({ ... });
  await prisma.auditLog.create({ ... });
  return { ok: false, kind: "duplicate", existingCheck, attemptedAmountCents };
}

The client form (new-check-form.tsx) treats the duplicate response as a non-error state and opens the DuplicateBlockModal with two clear next actions: view the existing check or file a correction with the new amount pre-filled. No dead-end error toast.

This is why loanNumber and claimNumber are duplicated on the Check row — Prisma's composite unique constraint must reference scalar fields on the same model. We trade a tiny bit of denormalization for a guarantee the database itself enforces.

How the correction workflow works

  1. Homeowner files a correction via /dashboard/checks/[id]/correct. The check itself is not mutated — a CorrectionRequest row is created with status: "pending", plus an AuditLog entry recording oldValue and newValue.
  2. Stacking is blocked. Only one pending correction per check at a time (server-side check in fileCorrection).
  3. Admin reviews at /admin. Approve or reject with an optional reviewer note.
  4. Approval runs in a single Prisma transaction:
    • update the check's amountCents
    • flip the correction to approved
    • write a check.update audit entry with old/new values
    • write a correction.approve audit entry
  5. Rejection writes a correction.reject entry and leaves the check untouched. The homeowner can then file a new correction with more detail.

Audit log

Every state-changing action goes through the same AuditLog table. Each entry captures:

  • actorId + actorEmail — who did it
  • actioncheck.create, check.duplicate_blocked, check.update, correction.request, correction.approve, correction.reject, funds.release
  • entityType + entityId — what was acted on
  • fieldName, oldValue, newValue — the diff (when applicable)
  • reason — free text for context (correction reason, reviewer note, etc.)

The log is rendered at /audit with old → new diffs and entity references. It's append-only by convention; there's no UI surface to edit or delete entries.

Money handling

All currency values are stored as integer cents (Int in the schema). The form input (CurrencyInput) clamps user input to two decimal places, formats on blur, and emits cents to the form state. This is the same reason the typo bug exists in the real world — bad currency UX combined with float math — and it's intentional that ClaimFlow treats it as a first-class concern.

Deploying for real

The demo runs on SQLite, which is ideal for local dev and Codespaces but not for a shared production deploy. To put this in front of users:

  • Vercel + Turso (LibSQL) — cheapest path. Swap the Prisma datasource to LibSQL and Turso's free tier handles persistence. Requires @libsql/client adapter.
  • Vercel + Postgres — change provider = "postgresql" and point DATABASE_URL at Vercel Postgres / Neon / Supabase.
  • Fly.io / Railway — keep SQLite, mount a persistent volume for prisma/dev.db.

The code itself is provider-agnostic; only prisma/schema.prisma and DATABASE_URL change.

Project layout

prisma/
  schema.prisma        # data model + unique constraints
  seed.ts              # the typo-and-duplicate demo scenario
src/
  app/
    page.tsx           # landing
    dashboard/         # homeowner views
    admin/             # servicer admin queue
    audit/             # compliance audit log
    actions/           # server actions: checks, corrections, funds
  components/
    ui/                # shadcn-style primitives, owned in-repo
    site-header.tsx
    duplicate-block-modal.tsx
    currency-input.tsx
    check-status-pill.tsx
  lib/
    db.ts              # Prisma singleton
    validation.ts      # Zod schemas
    utils.ts           # formatCurrency, parseCurrencyToCents, etc.
    session.ts         # demo "session" (no auth in prototype)
docs/screenshots/      # README assets
scripts/
  init.sh              # one-shot setup
  screenshots.sh       # regenerate README screenshots
.devcontainer/         # GitHub Codespaces config
stackblitz.json        # StackBlitz config

Status

Prototype. Original work — not affiliated with any real loss-draft platform or insurance brand. Auth is stubbed out (a real deploy would drop in NextAuth or Clerk and gate /admin behind a servicer role).

License

MIT. See LICENSE.

About

Mortgage loss-draft claim portal prototype with duplicate-check prevention, correction workflow, and audit trail

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages