Skip to content
← All work

Invoice Generator

A private invoicing app for AED and INR tax invoices from several businesses, with five print designs and a PDF that matches the screen page for page.

Role
Design, Front end, Deploy
Year
2026
Region
UAE, India
Built with
Next.js, React, TypeScript, Tailwind CSS, Drizzle, Cloudflare Workers, D1, R2
Status
In private use
The form beside the exact printable page; discount and VAT update as you type

[01]

// THE PROBLEM

Why it exists

The
Problem

One owner sends tax invoices from several businesses: some in dirhams with UAE VAT, some in rupees with GST split into CGST and SGST. Each business has its own numbering, details and look.

The PDF a client receives has to match what was written, down to the last fil or paisa, and the invoices themselves are private: there's nothing to share publicly.

[02]

// WHAT I BUILT

The rules

What I
Built

  1. 01

    The page you edit is the page you print

    The form sits beside an A4 preview that updates as you type. The preview is the same page that gets printed, and the app does its own A4 page breaks, so the PDF has exactly the pages you saw.

  2. 02

    Five print designs

    Editorial, Minimal Swiss, Modern Accent, Classic and Corporate. Each business picks a default, and any invoice can switch.

  3. 03

    AED and INR, to the fil and the paisa

    Amounts are stored as whole fils and paise, multiplication goes through exact decimal maths, and each line is rounded once. Rupee invoices use Indian grouping (₹6,78,500.00) and lakh and crore in words.

  4. 04

    VAT or GST, single or split

    No tax, a single tax such as VAT 5% or IGST 18%, or two taxes such as CGST 9% + SGST 9%, each rounded on its own line.

    UAE

    • VAT 5%
    • AED
    • TRN

    India

    • CGST + SGST or IGST
    • INR
    • GSTIN
  5. 05

    Several businesses, one owner

    Each sending business has its own logo, signature, tax ID, payment details, number series and design. Every invoice keeps a snapshot of both parties as they were on the day it was written.

  6. 06

    Security without shortcuts

    Passwords are hashed with scrypt and a secret pepper. Accounts lock after repeated failures, per account and per network address. Sessions end after 12 hours idle or 7 days at most, and only a hash of the session is stored. Changing the password, exporting everything or restoring a backup asks for the password again.

[03]

UNDER THE HOOD

Request path

  1. BrowserNext.js 16 + React 19 + Tailwind 4
  2. Cloudflare WorkersThe app, with a rate limiter on sign-in
  3. D1SQLite through Drizzle: businesses, clients, invoices
  4. R2 + Browser RenderingPrivate uploads and backups; PDFs printed by headless Chrome, with no network access
From the code (27a3b25).

Decisions worth explaining

  • Money as whole numbers

    Amounts are integers of the smallest unit and totals are always recomputed on the server, so a rounding error can't creep in between the screen and the PDF.

  • Every page checks the login itself

    There's no shared middleware to forget. Each page, route and action checks the session, and a test plus a logged-out end-to-end run enforce it.

  • Backups that can't break the data

    A backup file is fully checked before import, totals are recomputed rather than trusted, and a copy of the current data is saved first. The last ten copies are kept.