
Problem
Bills recur on different schedules, income clusters unevenly, and due dates do not care when a paycheck lands. The hard part was making that understandable without building a spreadsheet clone or a brittle rules engine.
Context / users
The app is built around a simple question: given a month of bills and a month of paychecks, what gets covered when? It models timing, not just categories.
My role
I designed and built the application end to end: schema design, route structure, server actions, validation, authentication, paycheck-window assignment logic, month propagation flow, and planning UI.
Solution
I built the app with Next.js App Router, TypeScript, Neon Postgres, and Drizzle. Forms use React Hook Form with Zod. Server actions revalidate affected pages and recompute assignments when income changes.
- Create months from bill templates or by propagating a previous month forward
- Review and edit bill and income drafts before saving a new month
- Track expected vs received income and scheduled vs paid bills
- Auto-assign bills into paycheck windows based on due dates
- Support manual bill-to-paycheck overrides when the automatic assignment is not what the user wants
- Filter bills by status, including due, paid, overdue, and unassigned
- Manage reusable bill templates with default due rules and payment links
- Show month-level metrics such as expected income, paid expenses, overdue counts, and unassigned bills
Architecture
The app uses a small relational core: ledgers, months, bill templates, income events, and bill instances. App routes handle top-level navigation. Feature folders own forms and actions. Shared helpers in `src/lib` handle metrics, paycheck-window generation, propagation, auth helpers, and validation schemas.
Engineering Details
- • Feature-based folder structure keeps domain logic separated from page composition
- • Server actions enforce ledger ownership before reads and writes instead of trusting client state
- • Zod schemas validate login, month creation, bills, income entries, and templates
- • Paycheck-window logic groups same-day income events, merges adjacent paydays, creates a pre-income window, and assigns bills by date boundaries
- • Income edits trigger bill reassignment so the month view stays consistent after planning changes
- • Month propagation clamps carried-forward dates to valid month boundaries instead of assuming all months have the same length
- • Optional Postgres RLS SQL is included, even though the current app scope stays intentionally simple
- • External payment links are opened with safe link attributes and auth cookies are configured as `httpOnly`
Outcome
- Produced a real full-stack planning app rather than a static UI concept
- Encoded a household planning problem into reusable domain logic instead of burying it in components
- Created a practical workflow for planning a month before bills start landing, not just logging transactions after the fact
- Established a solid MVP foundation for future additions like richer reporting, stronger multi-user support, or automated tests
Tradeoffs / Limits
- • Authentication is MVP-grade credentials auth backed by environment configuration, not a full account system
- • The app is scoped around a default household ledger, which simplifies ownership and avoids more complex collaboration rules
- • No automated tests or visible CI workflow are present in the repo yet
- • The current build is strongest at monthly planning and assignment, not long-horizon forecasting or advanced budgeting analytics
- • Some schema capacity exists ahead of the surfaced UI, which suggests the model is slightly ahead of the product polish in a few places
Why It Matters
It shows domain modeling, workflow design, and scheduling logic applied to a real planning problem.
Like what you see?
Feel free to reach out if you have questions about this project or want to chat about working together.