A voice-based time tracking application for construction workers with AI-powered transcription and manager review dashboard.
Workers record time entries by speaking naturally into their mobile phone ("I worked 8 hours at Simons Property today"). The system uses OpenAI Whisper for transcription and GPT-4o-mini for structured data extraction. Managers review and approve entries through a web dashboard.
Key Benefits:
- No forms, no typing - just speak
- Works in any language (auto-detection)
- Progressive Web App (no app store required)
- Manager oversight with approval workflow
- Low cost (~$5-10/month total)
┌─────────────────┐
│ Worker Mobile │ Progressive Web App (React + Vite)
│ (iOS/Android) │ Voice recording → Display results
└────────┬────────┘
│ HTTPS
▼
┌─────────────────┐
│ Backend API │ Node.js + Express
│ (Railway) │ Audio upload → Transcribe → Extract → Store
└────────┬────────┘
│
┌────────┴────────┬────────────┬────────────┐
▼ ▼ ▼ ▼
┌──────────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐
│ Supabase │ │ Whisper │ │ GPT-4o │ │ Supabase │
│ Storage │ │ API │ │ mini │ │ DB │
│ (audio) │ │ voice to │ │ text to │ │(Postgres)│
│ │ │ text │ │ data │ │ │
└──────────┘ └───────────┘ └──────────┘ └──────────┘
Frontend: React 19, Vite 8, Tailwind CSS 4
Backend: Node.js 22, Express 5
Database: Supabase (PostgreSQL)
AI: OpenAI Whisper (transcription), GPT-4o-mini (extraction)
Hosting: Vercel (frontend), Railway (backend)
- Worker records voice (MediaRecorder API, 60s max)
- Upload to backend (multipart/form-data, audio file + worker ID)
- Transcribe audio (Whisper API → text)
- Extract structured data (GPT-4o-mini → JSON: worksite, hours, date, confidence)
- Validate & review (Worker reviews transcription and extracted data)
- Submit (Worker confirms → Save to database)
- Manager review (Future: Approve/Edit/Flag entries)
See Design Document for full architecture details.
The project is organized into three phases. Each phase has detailed documentation with technical decisions and implementation plans:
- Phase 1 - MVP Architecture & Testing
- Phase 2 - Voice Recording & AI Integration
- Phase 3 - Worker History & Manager Dashboard
Production deployment:
- Worker App: https://time-reporting-dun.vercel.app
- Backend API: https://time-reporting-production.up.railway.app
# Clone and install
git clone https://github.com/nduchastel/time-reporting.git
cd time-reporting
cd backend && npm install
cd ../frontend && npm installFull setup guide: See Development Guide for complete instructions including:
- Local Setup - Step-by-step environment configuration
- Development Modes - Real API vs Mock mode (with diagrams)
- Database setup and seed data
- OpenAI API configuration
- Troubleshooting common issues
Testing: see docs/testing.md. Run npm run test:all from the repo root.
Upload audio, transcribe, extract data (does NOT save - returns for review).
Request: multipart/form-data
audio- Audio file (webm/mp4/wav, max 10MB)workerId- UUIDactionType- IN/OUT/HOURS/OFF
Response (200):
{
"transcription": "I worked 8 hours at Simons Property",
"extractedData": {
"action_type": "HOURS",
"hours": 8,
"worksite": "Simons Property",
"date": "2026-05-24",
"confidence": "high"
},
"processedData": { /* ... data for submission ... */ }
}Save time card (after worker reviews and submits).
Request:
{
"workerId": "uuid",
"worksiteId": "uuid",
"actionType": "HOURS",
"date": "2026-05-24",
"hours": 8,
"transcription": "...",
"extractedData": { /* ... */ }
}Response (201): Created time card object.
Retrieve time cards with filters.
Query params: workerId, status, startDate, endDate
Response (200): Array of time cards with joined worker/worksite data.
All architectural decisions are documented in phase-specific files:
Phase 1 (details):
- ES Modules over CommonJS
- Vitest over Jest
- Express over Fastify
- Supabase PostgreSQL
- Mock transcription strategy for testing
Phase 2 (details):
- Browser default audio format (no conversion)
- 60-second recording limit
- Low-confidence rejection
- Auto-capture time for IN/OUT actions
- Review-before-submit workflow
Phase 3 (details):
- Supabase Storage for audio files
- Last 5 entries for worker history
- Username/password authentication for managers
- Manager dashboard in same React app
Three core tables:
| Table | Purpose |
|---|---|
workers |
Employee records with language preferences |
worksites |
Job site locations |
time_cards |
Time entries with approval workflow |
Key concepts:
- Worker speaks → Whisper transcribes → GPT extracts → Database stores
- Submission time (
created_at) vs work time (date,start_time,end_time) - Approval workflow: pending → approved/edited/flagged
- Audit trail: original transcription + extracted data preserved
Full documentation: See Database Schema Guide for complete details including:
- Schema diagram with relationships
- All fields and constraints
- Action types (IN/OUT/HOURS/OFF)
- Approval workflow
- Common queries and indexes
time-reporting/
├── backend/ # Node.js + Express API
│ ├── src/
│ │ ├── db/ # Database migrations, seed data
│ │ ├── routes/ # REST API endpoints
│ │ ├── services/ # Business logic (extraction, transcription)
│ │ └── server.js # Express app entry point
│ └── tests/ # Vitest unit and integration tests
├── frontend/ # React PWA
│ ├── src/
│ │ ├── components/ # UI components (WorkerUI, RecordButton)
│ │ └── App.jsx # Main app entry point
│ └── tests/ # React Testing Library tests
├── docs/ # Phase plans and technical decisions
├── Design/ # Original design specs and mockups
└── README.md # This file
Automated deployment on push to main:
- Frontend: Vercel (auto-deploy from GitHub)
- Backend: Railway (auto-deploy from GitHub)
- Database: Supabase (manually provisioned)
Environment variables:
Set in Railway (backend):
SUPABASE_URLSUPABASE_ANON_KEYOPENAI_API_KEYPORT(auto-set by Railway)
Set in Vercel (frontend):
VITE_API_URL(Railway backend URL)
No package-lock.json committed - Each platform generates its own to avoid npm proxy conflicts.
Monthly costs (production):
- Vercel: Free (Hobby plan)
- Railway: $5/month (Hobby plan)
- Supabase: Free (includes 500MB DB + 1GB storage)
- OpenAI API: ~$0.15-0.50/month (depends on usage)
- Whisper: $0.006/minute
- GPT-4o-mini: $0.15/$0.60 per 1M tokens (input/output)
Total: ~$5-10/month for small team usage.
See Design Doc for detailed breakdown.
Phases 1–3 — ✅ Delivered:
- Voice → transcription → extraction → review → submit
- Worker PIN auth & last-5 history; audio storage + playback
- Manager dashboard (review, approve, edit, flag), worker CRUD, reports + CSV
- Full automated test suite (unit + integration + smoke + Playwright)
Phase 4 — Beta hardening, admin & user management — ✅ Delivered:
- Security: worker-ownership auth on time-card reads, auth-endpoint rate limiting, CORS allowlist, role-based JWT TTLs (worker 7d / manager-admin 24h), Supabase RLS (defense-in-depth)
- Error monitoring (no-op without DSN, PII-scrubbed); PWA install (manifest + service worker + install prompt)
- Admin portal + full user management (gated
#/admin); temporary-credential + forced-first-login-change lifecycle; self-service PIN/password change;create-adminbootstrap script - Per-worker panel visibility; worksite management (add/edit/archive)
- Worker install guides (iOS/Android) + manager setup guide
- See Phase 4 Plan
Pre-Beta-Ship — QA gate before launch: automated coverage for all Phase 4 features + manual device-matrix QA. See Pre-Beta-Ship Plan.
🚀 Beta launch (real-customer beta) after the QA gate passes.
Phase 5 — Post-beta backlog: anomaly detection, reports charts/PDF, bulk approve, GPS, photos, crew entries, push notifications, payroll integration, offline sync, retention/monitoring/backup hardening, SSO. See Phase 5 Backlog.
This is currently a personal project. If you'd like to contribute, please open an issue first to discuss proposed changes.
TBD
Nicolas Duchastel de Montrouge (@nduchastel)
For questions or issues, please open a GitHub issue.