Inspiration

Most small shops and restaurants in Cambodia still run on a notebook and a calculator. Stock counts live in someone's head, debts get written on scraps of paper, and closing out the day means adding up receipts by hand. We looked at the POS tools available and none of them really fit: they're built for other markets, priced for bigger businesses, or assume you've got internet all day. We wanted something Khmer-first, something that keeps working when the wifi drops, and something that actually understands paying in both riel and dollars. So we built Haang.

What it does

Haang is a POS and inventory app built for Cambodian SMEs, whether you're running a retail shop or a restaurant. On the retail side you get checkout with barcode scanning, KHR/USD pricing with live exchange rates, stock tracking with low-stock alerts, a customer CRM that handles buy-now-pay-later debt, and a dashboard for profit and VAT. Restaurants get table management, a kitchen display that moves orders from pending to served, bookings, and a way for customers to call a waiter or ask for the bill from their table. There's also a public QR menu customers can order from directly, KHQR payments, and a handful of Gemini-powered features — business insights pulled from your actual sales data, AI-written product descriptions and images, and the ability to snap a photo of a handwritten order or a payment screenshot and have it read automatically. The whole thing installs as a PWA and works offline.

How we built it

React and TypeScript on the frontend, built with Vite and styled in Tailwind. Charts are recharts, icons are lucide-react. Supabase handles the backend — Postgres, Auth, Storage, and Edge Functions. Every call to Gemini goes through an Edge Function rather than straight from the browser, so the API key stays off the client. For hardware we're using Web Bluetooth to talk to ESC/POS thermal printers and html5-qrcode for scanning barcodes with the phone camera. Staff log in with a 6-digit PIN and get access based on their role — admin, manager, cashier, waiter, kitchen — and Supabase's Row-Level Security enforces that at the database level, not just in the UI. Offline support comes from a service worker caching reads plus a write queue that holds changes locally and syncs once you're back online. We deploy to Cloudflare Pages with Wrangler.

Challenges we ran into

Offline-first was by far the hardest part. It's not enough to just cache data — every sale or stock change has to sit in a queue and sync back up without stepping on other changes made on a different device in the meantime. Running two currencies side by side turned out to touch almost every screen in the app, not just the checkout. And getting Gemini to read messy real-world input reliably — a blurry photo of a handwritten order, a screenshot from four different Cambodian banking apps that all format things differently — took a lot of trial and error on the prompts before it actually held up.

Accomplishments that we're proud of

Getting the offline sync working properly without data conflicts felt like a real win. We're also proud that the Khmer UI isn't just a translated version of an English app bolted on afterward — it was built Khmer-first from the start. The payment screenshot reader and the handwritten order OCR both solve annoyances we'd actually seen shop owners deal with, not made-up problems. And having one app that handles both a corner store and a full restaurant, with proper role-based access underneath, is more than we expected to get working in the time we had.

What we learned

We learned that "offline-first" is a much bigger design decision than it sounds like — it changes how you think about almost every action in the app, not just how you store data. We also got a lot better at prompting Gemini for structured extraction tasks like reading receipts, as opposed to open-ended chat, and saw firsthand how much a proxy layer like Supabase Edge Functions simplifies keeping things secure without slowing down the AI features.

What's next for HAANG

Better demand forecasting so shops know what to reorder before they run out, more payment providers beyond KHQR/ABA/ACLEDA, filling out the language support we've already laid groundwork for (Chinese, Japanese, Korean), and deeper restaurant analytics like table turnover and busy-hour staffing.

Built With

  • cloudflare-pages
  • edge-functions
  • gemini-api
  • google-gemini
  • html5-qrcode
  • khqr
  • lucide-react
  • postgresql
  • pwa
  • react
  • recharts
  • row-level-security
  • supabase
  • tailwindcss
  • typescript
  • vite
  • vite-plugin-pwa
  • web-bluetooth
  • wrangler
Share this project:

Updates