Case Study · Invoicing Tool

MoneyMaker App

An internal tool — a guided wizard that turns my own tracked hours into a QuickBooks invoice — designed so every number is obviously safe to send, or obviously not.

MoneyMaker App interface showing an invoice review screen with client line items.

It replaced a fragile manual process — reshaping the same data by hand across five file formats, 1–2 hours a cycle, partly run through a tool built by a developer I can no longer reach (a real business-continuity risk). The friction was bad enough that invoicing sometimes slipped, and that hit my own cash flow directly.

Role

Research, Design & Product

Scope

End to end

Year

2026

Stack

Node.js · QuickBooks CSV

~90%
less time per invoicing cycle
5 → 1
file formats, reduced to one clean import
91 entries
handled calmly on a single real invoice

How this was built

I directed an AI development partner through implementation the way a senior designer directs an engineer.

Real product judgment, a running decision log, and accountability for the result — not just prompting for code. The decisions below are the evidence: each one is a call I made and can defend, from business logic to a financial-data trust bug to measured accessibility.

The Product

In the order a user actually moves

Upload, set up the client only when a project needs it, review and fix, then export. The core design problem was trust — this touches real client billing data, so every screen has to answer one question: is this safe to send yet?

MoneyMaker Upload screen showing a drag-and-drop area for an OfficeTime export.

Step 1 · Upload. Drop the OfficeTime export — it stays on your machine, and the stray tracking column is detected and dropped automatically.

MoneyMaker Set up client screen with client mapping fields and validation.

Only when needed · Set up client. A gate that appears solely to fill the one gap — here, a single service still needs a QuickBooks item.

MoneyMaker Invoice details screen showing confirmed and unconfirmed line items.

Invoice details. The few things QuickBooks needs that OfficeTime doesn’t include — invoice number, terms, and a drafted description per category, ready to adjust.

Review & Fix — the centerpiece

The dense grid, doing the real work. When everything checks out, “Rows to fix” reads 0 and the invoice is calm.

MoneyMaker Review and Fix screen showing a clean state with all entries resolved and confirmed.

Review & Fix (clean). 91 entries, $9,809.90, zero rows to fix — the calm state a user should recognize instantly.

MoneyMaker Review and Fix screen showing flagged rows that need attention before export.

Review & Fix (flagged). Live validation at real volume: a “2 rows to fix” count, an “Only issues” filter, per-row highlighting, and inline guidance — never one blocking banner.

MoneyMaker Export success screen confirming the invoice was generated.

Step 3 · Export. A clean hand-off — downloaded once, with a manual “download again” and step-by-step QuickBooks import guidance.

MoneyMaker Manage clients screen listing all clients and their billing setups.

Manage clients. The mapping that makes future uploads “just work” — projects to customers, categories to items, per-service rates.

Decision log

Six calls, and why I made them

Not a generic process diagram — the actual before/after moments, kept the way I kept them while building.

01
Business judgment

Invoice numbering that doesn’t interrupt the cycle

Before

A single shared invoice sequence is simplest, but gives away privacy and leverage unnecessarily. The obvious fix, a sequence per client, couldn’t be trusted by hand: tracked manually, it risked duplicates and mix-ups with no way to catch them.

After

I designed a system that auto-suggests each client’s next number (e.g. SP-2604) the moment their last invoice was actually sent, with a manual override always in reach. The common path is invisible; the exception is one field away.

02
Failure states at scale

A messy invoice should never feel like a dead end

Before

A real invoice can carry dozens of problem rows at once. A single blocking error banner turns that into a wall — fix one thing, re-run, hit the wall again.

After

One honest “Rows to fix” count, an “Only issues” filter, and per-row inline flags. You triage 91 line items the way you’d triage a real to-do list.

03
Financial-data trust bug

The warning that cleared itself on the wrong row

Before

Editing one row’s rate silently cleared a different row’s “still needs confirming” warning. The validation checked whether any rate field had been touched — not each field’s own state.

After

Root-caused it to shared state and rebuilt it as real per-field tracking. Each row now owns its own status, independently.

“A warning that clears itself on the wrong row isn’t a UI bug on a financial document — it’s a trust bug.”

04
Remove the failure mode

Deleting the landmine, not labeling it

Before

The raw OfficeTime export ships an extra column that had to be deleted before every upload. Forget it and nothing errored — every field just shifted over by one, silently.

After

I had the parser auto-detect and drop the column instead of adding a reminder. The mistake stopped being possible.

“A required manual step you can forget isn’t a workflow, it’s a landmine. The right fix wasn’t a warning before the landmine — it was removing the landmine.”

05
Accessibility, verified

Contrast measured, not eyeballed

Before

The colored status tints looked fine on screen. Measured, the translucent fills came in at 3.45:1 — below the 4.5:1 AA floor for the exact numbers a user relies on to trust the row.

After

Switched to solid status backgrounds and re-measured computationally until every state cleared AA. Legibility you can prove, not assume.

06
Craft that compounds

Small fixes that protect the whole

Before

The export screen re-downloaded the file on every back-navigation. Reordering clients reset your scroll position. Individually minor — together, the feel of a tool you can’t quite trust.

After

Download once per invoice with a manual “download again” always available; scroll position preserved on reorder. The details that make a tool feel steady.

+
Product trade-off

I built a real OAuth flow — then chose to retire it

Earlier in the project I designed a genuine connect-to-QuickBooks OAuth experience. Once the CSV path covered the real need, I made the deliberate call to retire the OAuth flow for something simpler and lower-risk. Worth including as a decision, not hidden as a dead end — knowing when to remove your own work is part of the judgment.

Outcome

Why it was worth it

Choosing the more rigorous path — real per-field state tracking, a parser that removes a failure mode instead of warning about it, measured contrast instead of eyeballed contrast — paid off. When the goal is a financial document you can actually trust, the extra effort is worth it every time. But it’s worth being honest about the trade-offs too:

What worked

  • Every line is flagged individually — never an all-or-nothing failure on export.
  • A forgotten manual spreadsheet step can no longer silently corrupt an invoice.
  • Every colored status indicator is now measured against WCAG AA, not just eyeballed.

Trade-offs

  • Single-user tool, never instrumented with analytics — no true before/after number to point to, only the honest estimate above.
  • The itemized breakdown is still a manual Print / Save as PDF step, not a generated PDF.
  • A real QuickBooks OAuth integration was built and worked — then deliberately retired once CSV import covered the need at lower ongoing risk; it isn’t live today by choice, not by failure.

Final thoughts

Working on MoneyMaker was equal parts product judgment and craft: deciding what a four-step wizard should actually gate versus let through, and then making sure every one of those gates was legible, accessible, and correct under real volume — not just in a short test file.

The throughline across almost every real bug this project surfaced is the same one: a manual, forgettable step, or a piece of state that meant two different things in two different places, both fixed the same way — not with better error copy, but by removing the condition that made the error possible at all. That’s the standard I’d bring to a shared, multi-tenant product, not just a personal tool.

What this demonstrates

Capabilities I bring to any product

MoneyMaker is one system, but the practice behind it travels — it’s how I approach research, trust, and craft on anything with real stakes.

Trust & legibility in financial data

Designing so a person can tell, at a glance, whether a number is safe to send or still needs a human’s confirmation.

Root-causing state bugs, not just filing them

Tracing a self-clearing warning to shared state and directing the fix to real per-field tracking — product judgment, not a ticket.

Accessibility as a verified practice

Computing contrast ratios against the AA floor and re-measuring after the fix, rather than trusting how it looks on screen.

Authentication flow, then a real trade-off

Designed a full OAuth connect-to-QuickBooks flow, then made the deliberate call to retire it for a simpler, lower-risk CSV path once that covered the need.

Dense, information-heavy interfaces

The Review & Fix grid stays calm and scannable across 91 line items on a single real invoice.

A documented, decision-logged process

Every decision above came from a running log — the reasoning is the deliverable, not a retrofit.

Technologies

Frontend

Plain HTML / CSS / vanilla JS — deliberately no framework. Every screen designed in Figma first, built to match.

Backend

Node.js, Express. Cookie-keyed in-memory session state — no database, by design, for a single-user local tool.

Integration

QuickBooks Online invoice-import CSV format. OAuth 2.0 (built, verified, deliberately retired).

Let’s talk

I design for the moment a person has to trust what’s on the screen.