Case study · 2026
WorkBud
OJT Hours and Expense Tracker
A mobile-first, installable web app for tracking on-the-job-training hours and what a placement costs. You log each day's time in, time out, break and expenses, and it keeps a running hour total, an itemised view of spending, and an export to hand to a coordinator.

The problem
OJT hours are usually tracked on paper or in a notes app, and the out-of-pocket cost of commuting and meals often isn't tracked at all until the money is gone. Students need to know how many required hours are left, whether they will finish before the deadline, and what the placement is costing them.
What it does
- Daily log with time in, time out, unpaid break, a work note and itemised expenses by category.
- Dashboard with a progress ring, deadline countdown, required daily pace and an expected finish date.
- Daily and monthly budget meters, plus a catch-up figure that spreads an overspend across the days left in the month.
- Several jobs at once, each with its own target hours, deadline, budgets and logs.
- Milestones with due dates, and automatic hour badges at 25, 50, 75 and 100 percent of the target.
- Export of any date range as a printable time log or CSV.
- Email-and-password accounts verified by an emailed code, with password reset and profile pictures.
- Installable as a PWA; the app shell loads offline.
Engineering decisions
- Row Level Security stays on behind the API
- All app data goes through an Express API, but the server queries Postgres as the signed-in user rather than with an admin key. Even if a route had a bug, the database would still refuse to show or change another user's rows.
- A day and its expenses save in one transaction
- A database function saves a daily log together with its expense rows and computes the day's total from the rows it stored. Any total sent by the browser is ignored.
- Shift maths handles real shifts
- Hours are derived from the times entered, a shift in progress counts up live, and an overnight shift such as 10pm to 6am is 8 hours rather than a negative number.
- Stale data never comes from the service worker
- API and Supabase requests bypass the service worker (network-only), so the offline app shell can never serve an old login or old data.
My contribution
I built and deployed WorkBud as an installable web app with a React interface, an Express REST API and a PostgreSQL database on Supabase. I designed the five-table schema with Row Level Security to isolate each user's data, secured API requests with token authentication, server-side validation and rate limiting, and documented the setup, deployment and security controls for the Vercel release. The project was built with AI assistance, which is documented in the repository.
Technologies
- React 19
- Vite
- Tailwind CSS 4
- React Router
- Node.js
- Express 5
- Supabase (Postgres, Auth, Storage)
- Row Level Security
- vite-plugin-pwa
- Vercel
Screens

Dashboard — hours progress, pace, budget and milestones. 
Logging a day — shift times and itemised expenses. 
Export — a time log or full record for any date range. 
Onboarding — profile and first job. 
Settings — profile, currency and account.
What I'd do next
- Unit tests for the shift and date maths before adding new features.
- A save queue so a day logged offline syncs when the connection returns.
- Finish editing and deleting individual expense rows.