Full-stack work hours and earnings tracker with local-first storage and native iOS app. – Case Study
Full-stack work hours and earnings tracker with local-first storage and native iOS app.
Overview
Key features
- Job management — Create and manage jobs with hourly rates or fixed day wages; multi-currency (USD, GBP, EUR).
- Work entry tracking — Log hours and minutes per job, add notes, mark entries as received with payment method and date.
- Live work timer — Start/stop timer per job with real-time earnings display.
- Expenses — Track expenses by job and category with dates.
- Goals — Set daily, weekly, monthly, or yearly earnings goals and track progress.
- Recurring entries — Auto-create work entries on a daily, weekly, or monthly schedule.
- Invoices — Build invoices from selected work entries; export to PDF.
- Reminders — Task reminders with due dates; filter by active/completed.
- Analytics — Total earnings, wages received, wages left, total hours; charts (earnings by month, by job; hours by month); average hourly rate.
- Reports — Summary and detailed reports with PDF export.
- Calendar view — See work entries and events by date.
- Export / import — CSV export for jobs, entries, expenses; PDF report export; data import.
- Web + iOS — Static Next.js export for web; Capacitor for native iOS (safe area insets, PDF via Web Share API).
About this project
Tech stack
- Framework: Next.js 16 (App Router), React 19, TypeScript
- Storage: IndexedDB (via
lib/storage.ts) - Styling: Tailwind CSS 4
- Charts: Recharts
- PDF: jsPDF, jspdf-autotable
- Utilities: date-fns, PapaParse (CSV)
- Native: Capacitor (iOS)
- Optional: Google AdSense, PayPal donation buttons
Notable technical decisions
- Local-first — Migrated from Firebase to IndexedDB so all data stays on the device; no backend or sign-in required; works offline.
- Single codebase for web and iOS — Next.js static export (
output: 'export') plus Capacitor; one build serves the web app and the iOS app. - PDF on iOS — Use Web Share API with a PDF
Fileso users can “Save to Files” or share; fallback to blob download on desktop. - iOS safe area — Viewport
viewport-fit=coverandenv(safe-area-inset-*)so the header and bottom nav don’t sit under the Dynamic Island, notch, or home indicator.
Technology stack
- Next .js
- React
- Typescript
Categories
Productivity, Management System
Completed: 2025-09-01
Links
This post was generated from our portfolio. View all projects or get in touch for a similar project.