All work

Sales Tracker & POS

Point-of-sale, inventory, and sales tracking for a real shop, where a wrong change amount matters more than a slow query.

Year
2025
Role
Full Stack Development
Status
In development
  • Next.js 15
  • React 19
  • React Native
  • Expo
  • Supabase
  • PostgreSQL
  • TypeScript
  • Tailwind CSS
Sales Tracker point-of-sale dashboard

Overview

A combined POS, inventory system, and sales tracker running a working cake and minimart business. Two independent clients share one Supabase backend: a Next.js admin back office with a web POS, and an Expo/React Native app for the cashier-facing counter. Built for one to three users first, with a data model already shaped for multi-tenancy.

The challenge

This handles real money for a real shop, and the person using it is not technical and cannot debug a failure. That reframes the whole problem: correctness matters more than scale, and silent failure is the worst possible outcome. A sale that quietly doesn't record is worse than one that loudly fails, because the second is recoverable. On top of that, the same domain has to work across two very different clients: a desktop back office and a phone at a counter.

The approach

I built it as two independent clients against one Supabase backend, with the domain rules that must not drift enforced server-side and the rest duplicated deliberately rather than abstracted into a shared package. The single most important rule: order creation goes through an Edge Function that receives only product IDs and quantities. Prices and profit are resolved server-side from the database and stamped with the seller's identity from the JWT. A tampered client cannot invent a sale price.

Features & details

Touch-first point of sale

A cashier-facing checkout on both web and mobile. Cart lines are keyed by product and variant, so scanning the same item twice increments the quantity rather than stacking duplicate rows, and the screen shows the change owed to the customer.

Products with variants

A product either carries its own price or has variants, never both. The POS shows a variant picker where they exist and a straight price-and-quantity stepper where they don't, and the admin table renders variants as indented child rows.

Server-calculated prices and profit

Order creation runs through a Supabase Edge Function that receives only product ids and quantities. Every price, line total, and profit figure is resolved server-side and stamped with the seller from the JWT, so a tampered client can't invent a sale price.

Sales dashboard with export

Revenue and profit rolled up by timeframe, with a full order list and CSV/PDF export for records the business actually has to keep.

A business day that ends at 3AM

The shop trades past midnight, so "today" means since the most recent 3AM rather than since midnight. Both clients implement the same cutoff, so the daily summary doesn't split one night's trading across two reporting days.

Archive and restore, never delete

Removing a product sets an archive flag instead of deleting the row, so the sales history that references it stays intact and the product can be brought back.

Email and Google sign-in

Both clients support email/password and Google OAuth through Supabase, with mobile using a native auth session and a deep-link callback. Accounts are created on the web; the mobile app is login-only.

Shipped to a real tester

The mobile POS is distributed as an EAS beta build and in use on an actual counter, not just running in a simulator.

Outcome

In use by the business it was built for, handling real customers and real receipts. Every row already carries a user_id, so moving to true multi-tenancy is a policy and permissions problem rather than a rewrite. Active roadmap: raw-material inventory that draws down ingredients per sale, offline POS with sync on reconnect, and thermal-printer and barcode-scanner support.