Back to Projects
Professional2026

TimeKo - Offline-First POS & Operations Platform

Solo founder / full-stack developer

TimeKo - Offline-First POS & Operations Platform screenshot

Overview

A multi-app point-of-sale and operations ecosystem for fast-food restaurants in the Philippines. I designed and built the entire stack as a Turborepo monorepo: a PWA POS terminal that keeps selling through internet outages, an admin dashboard for managing products, stores, schedules, payroll, and inventory, an employee self-service portal for time-in/out and shift viewing, and a Next.js marketing site. Every piece shares types, UI, and utilities through internal packages, and talks to the same Supabase backend with row-level security keyed to per-organization, per-store, and per-role boundaries. The system is in production at timeko.app - used to run real shifts, real sales, real payroll across the POS (pos.timeko.app), admin (admin.timeko.app), and staff portal (staff.timeko.app).

Tech Stack

React 18 + TypeScriptVite 5Next.js 15 (landing)Turborepo + pnpm workspacesTailwind CSSZustand (with localStorage persistence)TanStack Query v5React Router v7Supabase (PostgreSQL, Auth, Storage, Edge Functions, Realtime)vite-plugin-pwa (Workbox)Web Push (VAPID + send-push edge function)Recharts@dnd-kit (drag-and-drop scheduling)Lucide Reactreact-hot-toastPapaParse (CSV import/export)Vercel (per-app deploys + vercel.json headers)

Screenshots

TimeKo - Offline-First POS & Operations Platform screenshot
TimeKo - Offline-First POS & Operations Platform screenshot
TimeKo - Offline-First POS & Operations Platform screenshot
TimeKo - Offline-First POS & Operations Platform screenshot

The Problem

Small fast-food chains in the Philippines run on cash, intermittent connectivity, and pen-and-paper scheduling. Off-the-shelf POS systems assume stable Wi-Fi, fixed terminals, and single-store operations - none of which match how a 2–5-location turo-turo or burger stand actually works. I needed a system that runs from a tablet at the counter, keeps selling when the internet drops, lets the owner manage products, schedules, and payroll across every store from one dashboard, and gives staff a self-service portal to view their shifts and time in/out - all without paying for per-terminal hardware locks or monthly SaaS per-seat fees.

The Solution

Shipped the product as four focused apps in a Turborepo monorepo, each independently deployable to its own Vercel project: a PWA POS terminal for cashiers at the counter, an admin dashboard for owners, a staff self-service portal, and a Next.js marketing site. The POS keeps selling through internet outages by persisting cart and shift state to localStorage and replaying queued sales through a single transaction when connectivity returns, so a cashier never sees a failed checkout. The admin app handles the full operations back office - products, multi-store inventory with per-store thresholds, drag-and-drop scheduling, payroll composition, and sales analytics - all behind role-gated Supabase RLS policies that key off a small set of SECURITY DEFINER helper functions. A single Supabase backend ties the four apps together with PostgreSQL, Auth, Storage, Realtime, and six privileged-write edge functions (employee invite, delete, email change, resend invite, push). Each edge function verifies the caller's JWT, checks their role against the target organization, and only then uses the service-role admin API - keeping the service-role key out of the client. Web Push subscriptions are VAPID-keyed through a send-push edge function, with a same-origin check in the service worker to prevent deep-link injection. Shared internal packages keep the apps visually and behaviorally consistent without coupling their builds: a single Tailwind preset, a design system of Button/Modal/Input/Table/Toaster, shared utilities for Philippine peso formatting and store color helpers, and PWA glue for install prompts and icons. The result is a product that runs real shifts, real sales, and real payroll across multiple stores today - at timeko.app, pos.timeko.app, admin.timeko.app, and staff.timeko.app.

Challenges

  • Designing an offline story that survives real outages: the POS keeps selling with a Zustand-backed local DB, queues every sale in an offline outbox, and replays them through a single transaction when connectivity returns - without ever double-charging or losing a shift
  • Getting Supabase RLS right across 60+ tables: per-store, per-org, per-role policies that all key off a small set of SECURITY DEFINER helper functions (get_user_store_ids, is_org_admin, is_superadmin, get_org_admin_org_id, get_user_store_role), with a recent tightening pass that sealed nine blanket FOR SELECT USING (true) policies that were leaking inventory and product-recipe data to anonymous callers
  • Payroll is per-employee with policy stored as JSONB on employee_profiles.pay_rate (keyed by CATEGORY): break deduction, schedule clamping, overtime, manual deductions, and hourly-leave-unpaid all compose into one RPC - and pieces derive ONLY from POS sales (no manual override path)
  • A drag-and-drop weekly schedule that overlays approved leave as violet blocks, auto-fills from each employee's default schedule, and gates write actions by role - the staff portal sees a read-only view of their own schedule plus a leave-filing flow that lands in admin for approval
  • Shipped a marketing site, an admin app, a staff app, and a PWA POS - each with its own Vercel project, custom domain, CSP + Permissions-Policy headers, and isolated build - by sharing a single Tailwind preset, shared UI components, shared utils, and Turborepo's parallel pipeline
  • Auth across four apps: Supabase Auth with per-app redirect allowlists, an invite-employee edge function for admin onboarding, and the recent hardening pass that removed a dead JWT-from-URL handoff in admin and staff (CSRF surface), added a same-origin check to the SW notificationclick (deep-link injection), and tightened the prod RLS posture

Key Learnings

  • RLS is the real auth layer. The recent tightening pass that sealed nine blanket FOR SELECT USING (true) policies on inventory, product recipes, and store mappings was the single highest-impact security change - and it required zero app code because the helper functions (get_user_store_ids, is_org_admin) already existed; the policies were just wrong
  • A monorepo pays off the moment the second app shows up. Sharing the Tailwind preset, the Button/Modal/Table design system, the formatCurrency helper, and the SW-glue package across POS/admin/staff kept every app visually consistent and let each one stay independently deployable
  • Offline-first is a state-management discipline, not a feature. Persisting every cart, shift, and pending sale to localStorage + queuing writes to an outbox + replaying on reconnect means the POS works the same with or without Wi-Fi - and the cashier never has to think about it
  • Edge functions are the seam where auth checks matter most. Every privileged write (invite, delete, email-change, push) goes through an edge function that verifies the caller's JWT, checks their role against the target org, and only then uses the service-role admin API - a clean pattern that keeps the service-role key out of the client
  • PWA install + Web Push needs real glue. The installPromptDismissed() helper, the SW-side same-origin URL check on notificationclick, the VAPID-keyed push subscription flow through send-push, and the per-app manifest+icons all had to ship as one feature - half of it is a UX problem, half is a permissions problem, and the seams between them are where bugs live