In-build product brief · React + Supabase · v1 scope

DayFlow. A spending tracker built around one question.

Where did the money actually go today? Most personal-finance apps answer that question after the fact and at the wrong granularity. DayFlow is built to answer it tonight, in twelve seconds, with no setup ritual.

In-build brief
This is the spec, not the launch post. The problem statement is firm, the data model is firm, the wireframe is firm. The codebase is partial. I'm publishing the brief now because the most useful PM artifact for the kind of work I want to do next is the spec, not the screenshot. The build is downstream of the brief.
Why this work

Most personal-finance apps optimize for the audit. I want one that optimizes for the night-of decision.

Mint, YNAB, Copilot, the credit-card companies' own dashboards: all of them are built to answer "where did my money go this month" with a chart you scroll past. That's the wrong question and the wrong tempo. The question that actually changes behavior is "did I overspend today?" answered before tomorrow starts. DayFlow is the smallest possible product that answers exactly that, fast.

Craft on display
  • Problem framing
  • Scope discipline
  • Data modeling
  • React + TypeScript
  • Supabase auth + RLS
  • PWA UX
  • Spec writing
  • Cutting features
The brief in one paragraph
  • Who. Me first. Then anyone who wants a tonight-not-tomorrow spending check, doesn't want to connect their bank, and isn't trying to build a forecasting model.
  • What. A PWA that opens to one screen showing today's spend, today's budget, and a one-tap "log a charge" entry point.
  • Why now. Existing tools are either too heavy (YNAB, Copilot) or too noisy (bank dashboards). The gap is a thin client built around the "before bed" check.
  • What's in v1. Manual entry, daily target, one-screen view, one-tap add. Auth via magic link. PWA-installable.
  • What's deliberately not in v1. Bank linking. Categories beyond a tag field. Forecasting. Budget periods other than daily. See the scope table below.

01 / The problemThe night-before-bed check that doesn't have a tool

I've used most of the major personal-finance apps. They are all good at the same thing and they all have the same blind spot. The thing they're good at is the post-mortem: at the end of the month, here's where your money went, broken down by category, with a chart. The blind spot is the in-the-moment decision: at 9:47 p.m. on a Wednesday, am I in or out of bounds for today, and should I order the second beer.

The post-mortem use case isn't worthless. It's just the wrong tool for the job that actually changes spending behavior. The job that actually changes behavior is the daily checkpoint, because the unit of self-correction is a day, not a month. By the time the monthly chart arrives, the behavior it reports on is already locked in.

The unit of self-correction is a day. Most personal-finance tools report on the wrong unit and at the wrong cadence. The gap is a small, fast, opinionated daily check.

The other thing that kills existing tools for this use case is the setup ritual. Bank linking is friction. Category configuration is friction. Multi-account reconciliation is friction. The version of the product that delivers value tonight is the version that lets you log a $42 charge in two taps and see the day-over-day shape of the week without any of the above.

02 / Scope disciplineWhat's in v1, what's out, what's later

The biggest decision on this build is what I'm not doing in v1. The temptation to build the kitchen-sink personal-finance product is real. The version of this that ships in eight weeks is small on purpose:

In v1
Manual transaction entry. Two taps from app open: amount, optional tag, save. No category picker, no merchant lookup, no receipt scan.
In v1
One daily target. A single dollar number for today's discretionary spend. Set once a week, not per category.
In v1
Today screen as the home. Big number for today's spend, smaller number for today's target, list of today's entries. That's the screen you open to.
In v1
Last-7-days strip. A small visual at the bottom of the home screen showing the last seven days versus target. Provides week shape without becoming a separate screen.
In v1
PWA install. Add-to-home-screen on iOS and Android. Opens like a native app. Offline-tolerant for read.
Out of v1
Bank linking. Plaid integration is the obvious feature request. It's also the feature that most increases support load, regulatory exposure, and abandonment rate during onboarding. v1 ships without it on purpose.
Out of v1
Categories. Free-text tag only. Category systems demand maintenance from the user, which is the friction that kills similar apps. If users want categorization, the tag field becomes structured later.
Out of v1
Monthly / yearly views. Wrong cadence for the product's purpose. Adding them invites scope creep toward the post-mortem product, which is exactly what I'm trying not to build.
Out of v1
Multi-currency, joint accounts, savings goals, forecasting. All reasonable. None of them are the smallest version of the product. They live in v2-or-never territory until v1 has actual users.
Recurring entries. A user can mark a transaction as recurring weekly/monthly and it auto-posts. Useful, but not on the critical path for v1.
Daily target by day-of-week. Friday/Saturday targets are realistically higher than Tuesday targets. v1 uses one number to keep the model simple. v1.1 lets you set seven.

03 / WireframeThe one screen that has to work

The product is approximately one screen. If that screen is right, the product is right. If that screen is wrong, no amount of secondary navigation will save it. Sketch:

9:47 PM DayFlow · Wed
$87 of $120
$33 left for tonight
Week so far
$412
target $480 · pace +14%
Today
Coffeemorning
$6
Lunchwork
$18
Groceriesfood
$47
Gascar
$16
+ Add charge

Home screen, evening state. The headline number answers the question the product exists to answer.

The hierarchy is intentional: the biggest number is "today's spend vs today's target." The second biggest is "week so far vs week target." Everything else is supporting evidence for those two. The add button is the only persistent action because adding charges is the only thing the user has to do for the product to work.

04 / Data modelThree tables, one job

The schema is small on purpose. Anything more elaborate is paying for features that aren't in v1.

users
Supabase auth handles the bulk; this table is the per-user app state.
iduuidprimary key, references auth.users
daily_target_centsinttoday's spend target, stored in cents
timezonetextIANA tz, defines what "today" means
created_attimestamptzdefault now()
charges
A single spending event. The atomic unit of the product.
iduuidprimary key, default gen_random_uuid()
user_iduuidFK users.id, RLS enforced
amount_centsintpositive integer, no signed-amount logic in v1
labeltextuser-supplied, free text, max 60 chars
tagtextoptional, free text, indexed for v1.1
occurred_ondatecomputed from occurred_at + user tz
occurred_attimestamptzdefaults to now()
created_attimestamptzaudit field, separate from occurred_at
target_overrides
For days where the user wants a different target than the default. v1.1 uses this for day-of-week targets.
user_iduuidFK, composite PK with date
datedatethe day this override applies to
target_centsintoverrides users.daily_target_cents for this date
reasontextoptional, e.g. "anniversary dinner"

Three notes on the model. One, all amounts are integer cents to dodge floating-point rounding. Two, occurred_on is computed from occurred_at and the user's timezone, because "today" is a per-user concept and the database needs to know what user A's "today" is, not the server's UTC today. Three, the tag field is free text intentionally. Adding a category table is a half-day of work and a lifetime of category-bikeshed support requests; v1 deliberately skips it.

05 / Auth and securityMagic link plus row-level security

Auth is Supabase magic link. No passwords, no OAuth setup, no third-party identity to maintain. The user enters an email, gets a link, taps it, is in. For a single-user-per-account product where the data isn't catastrophically sensitive but is real personal info, magic link is the right balance of friction and security.

Row-level security on Postgres is the load-bearing piece. Every row in charges and target_overrides is queryable only by the user that owns it. The policy is mechanical:

-- charges table: user can only see and modify their own rows
create policy "users see own charges"
  on charges for select
  using (auth.uid() = user_id);

create policy "users insert own charges"
  on charges for insert
  with check (auth.uid() = user_id);

The reason this is worth highlighting is that every Supabase project has the option to skip RLS, and a non-trivial number of public Supabase projects ship with it disabled. v1 will not.

06 / Build status, honestlyWhat's done and what's next

Where the codebase actually is, as of this writing:

Done
Schema migrations. Tables, RLS policies, computed-column trigger for occurred_on.
Done
React shell. Vite + TypeScript, routing, Supabase client, magic-link auth flow.
Done
Today screen, read path. Live data from Supabase, today's spend and target rendering correctly across timezone changes.
Add-charge flow. Modal works, optimistic update is half-built, error rollback isn't.
Last-7-days strip. Query is fine, the visualization is unstyled and the day labels are wrong on week-rollover.
Not started
PWA manifest, service worker, install prompt. Last-mile work. Doesn't gate dogfood, does gate ship.
Not started
Onboarding. First-run target-setting flow. Currently a hardcoded value in the dev build.
Why I'm publishing the brief before the build

The most useful thing I can show for the kind of role I want next is the spec, the cuts, and the schema, not a screenshot of a finished UI. The brief reveals what I'd argue for in a planning meeting. The screenshot only reveals that I can ship.

07 / Caveats I want statedWhere this could be wrong

Honest about the risks
  • Manual entry might not have legs. The bet that users will actually log charges manually is the load-bearing one. If they won't, the whole product needs bank linking and v1 should have started there. v1 is also the cheapest test of that bet.
  • I am the first user. Building for yourself is a known PM trap. The discipline to avoid it is testing with non-me users early. That happens after v1 ships.
  • The "no categories" call. Could be wrong. The other apps have categories for a reason. The case for skipping them in v1 is testable; if early users beg for them, they go in v1.1.
  • Daily-only cadence. A weekly target might be the right primitive, not a daily one. v1 picks daily because it matches the "tonight not tomorrow" framing, but the model lets the cadence change without a schema migration.
  • Solo build, real product, real users. The risk isn't building the wrong thing once. It's building the wrong thing in a way that's hard to back out of. The schema is intentionally minimal so backing out is cheap.
Pre-registered open questions

What v1's launch will let me actually answer.

  1. Does manual entry survive past week three? The honest measurement is the seven-day rolling rate of any-entry-per-day, not total entries. Pre-registered fail condition: median user logs zero charges on more than half of days in week 3+.
  2. Does the "tonight check" actually change behavior? Hard to measure with a small sample, but worth a self-report question at week 4: "did seeing today's spend change a decision you made tonight?" Not a perfect signal, but better than waving hands.
  3. Is the daily cadence the right unit? If users consistently set their target to a weekly number divided by seven, the daily cadence is wrong and v1.1 should switch primitives.
  4. What's the friction floor on add-charge? Time-to-log from app open, in seconds, measured. The pitch is "twelve seconds." The bet is real only if the actual measured number is close to that.