A full-stack invoice management application built with React (Vite) + Tailwind CSS on the frontend and Node.js + Express on the backend.
Frontend (Netlify): your-site.netlify.app
Backend (Render): your-backend.onrender.com
Full CRUD — Create, Read, Update, Delete invoices
Draft & Payment Flow — Save as Draft, send as Pending, mark as Paid
Filter by Status — All, Draft, Pending, Paid with checkbox dropdown
Form Validation — Required fields, email format, positive quantities, at least one item
Delete Confirmation Modal — ESC-key dismissal, focus trap
Light / Dark Mode — Persisted via localStorage, system-aware default
Fully Responsive — Mobile (320px+), Tablet (768px+), Desktop (1024px+)
Hover States — All interactive elements have visible hover feedback
Data Persistence — JSON file-based backend (swap for SQLite/Postgres easily)
invoice-app/
├── frontend/ # Vite + React + Tailwind CSS
│ ├── src/
│ │ ├── components/
│ │ │ ├── Sidebar.jsx # Nav + theme toggle
│ │ │ ├── StatusBadge.jsx # Paid/Pending/Draft badge
│ │ │ ├── FilterDropdown.jsx # Status filter with checkboxes
│ │ │ ├── InvoiceForm.jsx # Create & Edit drawer form
│ │ │ └── DeleteModal.jsx # Confirmation modal
│ │ ├── context/
│ │ │ ├── ThemeContext.jsx # Dark/light mode state
│ │ │ └── InvoiceContext.jsx # Invoice API state + actions
│ │ └── pages/
│ │ ├── InvoiceList.jsx # List view with empty state
│ │ └── InvoiceDetail.jsx # Detail + action bar view
│ └── package.json
├── backend/ # Node.js + Express REST API
│ ├── src/
│ │ └── index.js # Express routes + JSON persistence
│ ├── data/
│ │ └── invoices.json # Auto-generated with seed data
│ └── package.json
└── netlify.toml # Netlify build + SPA redirect config
git clone https://github.com/yourusername/invoice-app.git
cd invoice-app
cd backend
npm install
npm run dev
# Runs on http://localhost:5000
cd frontend
npm install
cp .env.example .env.local
# Edit .env.local — for local dev, VITE_API_URL is not needed (Vite proxies /api automatically)
npm run dev
# Runs on http://localhost:5173
Backend → Render (free tier)
Push the backend/ folder to a GitHub repo (or monorepo)
Create a new Web Service on render.com
Set:
Build Command: npm install
Start Command: node src/index.js
Port: 5000
Note the deployed URL (e.g. https://invoice-api.onrender.com)
Push entire project to GitHub
Connect repo to netlify.com
Build settings (auto-detected from netlify.toml):
Base: frontend
Build command: npm run build
Publish: dist
Add environment variable:
VITE_API_URL = https://invoice-api.onrender.com/api
Deploy!
Method
Endpoint
Description
GET
/api/invoices
List all invoices (supports ?status=pending)
GET
/api/invoices/:id
Get single invoice
POST
/api/invoices
Create invoice
PUT
/api/invoices/:id
Update invoice
PATCH
/api/invoices/:id/status
Update status only
DELETE
/api/invoices/:id
Delete invoice
Decision
Rationale
JSON file store
Zero setup, portable, easy to inspect. Swap to SQLite/Postgres by replacing loadInvoices()/saveInvoices() in index.js
React Context
Sufficient for this app's scale; no need for Redux/Zustand overhead
Tailwind CSS
Rapid development, design system tokens in config, no runtime cost
No JWT auth
Out of scope for the brief; add middleware on Express routes + Clerk/Auth0 on frontend
Vite proxy for dev
Avoids CORS issues in development; production uses VITE_API_URL env var
Semantic HTML: <main>, <header>, <aside>, <section>, <ul>/<li> for invoice list
All form fields have associated <label> elements (via htmlFor)
All <button> elements use native button semantics
Delete modal:
role="dialog" + aria-modal="true" + aria-labelledby
Focus moves to confirm button on open
ESC key closes the modal
Status badges use role="status"
Loading indicators use role="status" + aria-label
Color contrast: WCAG AA compliant in both light and dark modes
Filter checkboxes have visible focus indicators
Improvements Beyond Requirements
Auto-generated invoice IDs in ##NNNN format (2 letters + 4 digits)
Auto-calculated paymentDue from invoiceDate + paymentTerms on backend
Seed data pre-loaded on first run (7 invoices from the Figma designs)
Responsive item list — collapses gracefully on mobile
Mobile action bar — sticky bottom bar on detail view for mobile UX
Smooth transitions — theme switching, hover states, drawer animation