Skip to content

FajarWG/tsuzuru

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

132 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

็ถดใ‚‹ โ€” Tsuzuru

Next.js TypeScript TailwindCSS Prisma PWA Vitest

Weave your money story โ€” ใŠ้‡‘ใฎ็‰ฉ่ชžใ‚’็ถดใ‚ใ†

Tsuzuru (็ถดใ‚‹) is a Zen-inspired, minimalist personal finance manager modeled after the traditional Japanese Kakeibo (ๅฎถ่จˆ็ฐฟ) budgeting method. Built with Next.js, Prisma, and Framer Motion, it offers a seamless, offline-first, and highly aesthetic interface to record, analyze, and master your monthly cash flows.


โœจ Features

  • ๐Ÿ““ Zen-inspired Minimalist Design: Premium aesthetics featuring clean layouts, smooth micro-animations, custom dark mode, and card structures that bring peace of mind to expense tracking.
  • ๐ŸŒ Offline-First PWA Capabilities: Fully installable as a Progressive Web App (PWA) on Mobile and Desktop. Log transactions on-the-go offline, which are automatically synced to the server once your connection is restored.
  • ๐Ÿ’ต Smart Multi-Currency Ledger: Native support for JPY (ยฅ) and IDR (Rp) tracking. View individual account balances (Cash, Banks, E-Wallets, Investments) and total assets separated clearly.
  • ๐Ÿ“Š Kakeibo Budget Limits: Define a monthly expected budget and custom spending categories (e.g. Pocket Money, Shopping). Instantly view remaining limits on the dashboard. Categories are fully customizable.
  • ๐Ÿ“ˆ Micro-animated Interactivity: Fluid number transitions using physics-based spring animations (Framer Motion) that roll up/down dynamically as account balances adjust.
  • ๐Ÿ—“๏ธ One-Click Recurring Bills: Create monthly template schedules (Rent, Utilities, SIM Cards) and record payments with a single tap.
  • โšก Advanced Analytics: Interactive bar charts and category donut charts powered by Recharts, complete with custom tooltips and pocket-money meal stats.
  • ๐Ÿค– AI Receipt Parsing: Scan or paste receipt text/images and have Gemini AI automatically extract items and prices โ€” with per-user rate limiting.
  • ๐Ÿ”— Split Bill Tracker: Track shared expenses with friends (who owes whom), and settle bills with automatic account balance adjustments.
  • ๐Ÿ”” Fixed Bill Reminders: Flag a recurring bill as fixed-amount with a known due day, and get a real Web Push browser notification the day before it's due (driven by a Vercel Cron job).
  • ๐Ÿšฆ Budget Threshold Alerts: Proactive toast warnings when a spending category crosses 80%/100% of its monthly limit, instead of a passive progress bar only.
  • โš–๏ธ Balance Reconciliation: Explicit "reconcile balance" flow to align an account's recorded balance with its real-world value, logged as an auditable adjustment transaction.
  • ๐Ÿ”‘ Email/Password Auth: Sign in with Google or a standalone email + password account (bcrypt-hashed), side by side via NextAuth Credentials provider.

๐Ÿ› ๏ธ Tech Stack

  • Framework: Next.js 16 (App Router, Server Actions, API Routes)
  • Database ORM: Prisma 7 with PostgreSQL (via @prisma/adapter-pg connection pool)
  • Authentication: NextAuth.js v5 (Google Sign-In + email/password via Credentials provider, JWT sessions)
  • Styles & Layout: TailwindCSS v4 & Shadcn UI
  • Animation: Framer Motion & Tabler Icons
  • Charts: Recharts
  • AI: Gemini API (receipt parsing, per-user rate limit stored in DB)
  • Testing: Vitest with mocked repositories (no DB required)
  • State & Sync: Service Workers (PWA) & LocalStorage Sync Queue

๐Ÿ—๏ธ Architecture

The project follows a strict layered architecture to enforce user data isolation:

Client Component / Page
    โ†“
Server Action  (lib/actions/*.ts)   โ† auth() check, userId from session only
    โ†“
Service        (services/*.ts)      โ† business logic, ownership verification
    โ†“
Repository     (repositories/*.ts)  โ† Prisma queries, always scoped by userId
    โ†“
PostgreSQL (Neon)

Key security invariants:

  • Every Server Action validates the session before doing anything.
  • userId is always taken from session.user.id โ€” never from client parameters.
  • Repository queries always include userId in the where clause.

๐Ÿš€ Getting Started

Prerequisites

Make sure you have Node.js (v18+) and Bun installed.

Installation

  1. Clone the Repository:

    git clone https://github.com/your-username/tsuzuru.git
    cd tsuzuru
  2. Install Dependencies:

    bun install
  3. Set Up Environment Variables: Create a .env file in the root directory:

    DATABASE_URL="postgresql://user:password@localhost:5432/tsuzuru"
    NEXTAUTH_SECRET="your-nextauth-secret-key"
    
    # Google OAuth Credentials
    GOOGLE_CLIENT_ID="your-google-client-id"
    GOOGLE_CLIENT_SECRET="your-google-client-secret"
    
    # Gemini AI (for receipt parsing)
    GEMINI_API_KEY="your-gemini-api-key"
    
    # Web Push (fixed recurring bill reminders)
    NEXT_PUBLIC_VAPID_PUBLIC_KEY="your-vapid-public-key"
    VAPID_PRIVATE_KEY="your-vapid-private-key"
    VAPID_SUBJECT="mailto:you@example.com"
    
    # Vercel Cron auth (protects /api/cron/bill-reminders)
    CRON_SECRET="your-cron-secret"
  4. Initialize the Database:

    # Generate Prisma client
    node node_modules/prisma/build/index.js generate
    
    # Push schema to database
    node node_modules/prisma/build/index.js db push
  5. Run the Development Server:

    bun dev

    Open http://localhost:3000 on your browser.


๐Ÿงช Testing

Tests are written with Vitest and run entirely with mocked repositories โ€” no real database connection needed.

bun run test           # run all tests once
bun run test:watch     # watch mode for development
bun run test:coverage  # generate coverage report

Test suites cover:

  • Service layer: ownership enforcement, business rule validation (e.g. cannot delete monthly budget, cannot settle another user's bill)
  • Server Actions: auth enforcement on every action, session userId always wins over client input

โš™๏ธ Offline Mode & PWA

Tsuzuru registers a custom Service Worker that intercepts network requests, caching static assets and API payloads to enable offline capabilities:

  • Offline Entry: If offline, transactions are pushed into a LocalStorage queue named tsuzuru_offline_transactions.
  • Auto-Sync: The PwaRegister component registers a window.addEventListener("online") event that flushes the queued transactions to the backend database server as soon as connection recovers.

๐Ÿ”” Bill Reminders (Web Push + Cron)

Recurring bill templates can be flagged as fixed-amount with a due day (isFixedAmount / fixedDueDay). A daily Vercel Cron job hits /api/cron/bill-reminders (configured in vercel.json, guarded by the CRON_SECRET bearer token), which finds bills due the next day that haven't been paid yet this cycle and sends a Web Push notification via web-push to every subscribed device. The service worker (public/sw.js) handles the push/notificationclick events, and users opt in from Settings โ†’ Profile โ†’ Fixed Bill Reminders.


๐Ÿ“„ License

This project is licensed under the MIT License.

About

Weave your money story. A Zen-inspired personal finance tracker (Kakeibo) built with Next.js, Prisma, and offline-first PWA support. ๐Ÿ““โœจ

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages