Product design · Android tablet app
Cupsync
A point-of-sale app that runs a café counter on 1 Android tablet. It keeps selling offline and sends the owner's daily report to Google Sheets.
- Role
- Solo product designer and front-end engineer. Research, flows, UI, prototyping, build and field testing.
- Client
- Sidecup, a café in the Philippines
- Timeline
- Started September 2026 · in use at the counter
- Platform
- Android tablet (Capacitor) and web, mobile-first
- Tools
- Claude Design · Next.js · Tailwind CSS · Capacitor · Dexie (offline storage) · Google Sheets
- Status
- Live at the counter, still iterating from field feedback

Overview
At Sidecup, Cupsync replaced a notebook-and-calculator routine with a till that works offline, handles local tax and discount rules, and reports to the owner in Google Sheets.
The core design problem: make every common task take a few taps under rush-hour pressure, without hiding the edge cases the business can’t afford to get wrong: discounts, partner orders, stock, and cash at close.
Users and context
3 people use the same tablet for different jobs, and the cashier’s job sets the pace for everyone else.
| User | Main job | When they use it | What they need most |
|---|---|---|---|
| Cashier | Take orders and payment | All shift, often with a queue | Speed, clear totals, no maths |
| Barista | Make drinks in order | Glancing over from the bar | What to make next, and how long it’s waited |
| Owner | Run the business | End of day, often off-site | Trustworthy numbers without opening the POS |
Constraints that shaped the design:
- 1 shared tablet at the counter, used standing, often one-handed.
- Unreliable café Wi-Fi: the till must never stop because the internet did.
- Philippine rules: 12% VAT, and the legally required 20% discount for senior citizens and persons with disabilities (PWD).
- Payments by cash, card and InstaPay QR, the Philippines’ instant bank-transfer network, scanned from GCash, Maya or any bank app.
- Some orders come through partner establishments and must be reconciled monthly.
- Rotating staff with little training time, and a PIN login per person.
Design principles
5 rules settled most arguments before they started.
- The counter never waits. No step may block a sale: not the internet, not a stock count, not a sync.
- Ask only when it matters. Options appear only for items that have them; everything else is 1 tap.
- Warn, don’t stop. Problems are shown where the work happens, and staff decide what to do.
- Status must be honest. An indicator that is sometimes wrong is worse than none, because people stop checking.
- Meet each user where they already are. The owner lives in Google Sheets, so the report goes there.
Rush-hour order entry
A plain drink is 1 tap; a drink with add-ons is 2 or 3.
Scenario. 6 customers are queued. The cashier needs to ring up an Americano with an extra shot, an ube milk and a cocktail without slowing the line.


- Pick a category from the rail: Coffee, Non-coffee, Refreshers or Cocktails.
- Tap a tile. It flashes to confirm the item went in.
- Coffee and milk drinks open a short Add-ons sheet (espresso shot +₱30, syrup +₱10). Confirming with nothing picked is fine.
- Plain items, like refreshers and cocktails, go straight to the cart.
- On a phone, a review bar keeps the running total in view.
Design decisions
- Options only where they exist. An earlier version opened a size picker for most drinks. The real menu has 1 size, so that step was cut.
- Fixed-height tiles. Names wrap to at most 2 lines, so the grid stays aligned and scannable at a glance.
- Colour stripes per category. Staff find their place by colour before reading a word.
- Ingredients on cocktail tiles. Names like “Kariñosa” don’t say what’s in the glass, so the answer sits on the tile.
What to measure: seconds and taps per order, and items corrected after being added.
Taking payment
1 payment screen handles 4 situations that look alike but must be recorded differently.

| Situation | What the screen does | Why |
|---|---|---|
| Cash | Shows change due; warns when the amount given is short | Removes mental maths under pressure |
| QR / InstaPay | Shows the café’s QR full size, with “Check the transfer arrived before handing over the order” | A screenshot of a payment is not a payment |
| Senior / PWD | Applies the discount and VAT rules automatically | It’s the law, and hand-calculating it causes errors |
| Partner order | Requires the partner slip reference before completing | Month-end reconciliation fails without it |
The QR button first said “GCash”. Customers paid from GCash, Maya and bank apps, so it became “QR / InstaPay”, and past records were unaffected. The partner slip reference is the only required field, because a missing one costs the most at month end.


When the internet drops
The till keeps selling offline, and the Online badge now tells the truth about the connection.
Scenario. The café Wi-Fi is connected but has no internet. Orders keep coming.


What went wrong in the field. The header said Online while the tablet had no internet at all. Staff use that badge to judge whether syncing and the Google Sheets upload can work, so a wrong green light hides real problems.
Why it happened. The badge reported “connected to a network”, not “can reach the internet”. On the Android tablet it could never change at all.
What changed
- The badge now confirms a real connection: when the app opens, when it returns to the foreground, when the network changes, and every 30 seconds.
- It flips to Offline within seconds of losing the internet, and back once it returns.
- Sales still save on the tablet first, so nothing at the counter depends on the badge being green.
A status light earns trust slowly and loses it in one shift. If it can’t be right, it shouldn’t be there.
What to measure: uploads that fail while the badge says Online (target: zero).
Barista serve queue
Paid orders are listed oldest first, because the barista’s question is “what’s next?”, not “what just came in?”. Each order has a live wait timer that changes colour as the wait grows: the colour is read from across the bar, the minutes up close. Drinks can be ticked off one at a time, since big orders are often handed over in parts, and orders still waiting on payment are timed too, so none can quietly sit forever.
Running out of stock
Every sale deducts its recipe from stock. When an ingredient runs low, each drink that uses it is tinted amber and names the cause (“Low: Matcha powder”), then turns red at zero. The app warns but never blocks a sale: stock counts drift from reality, so staff make the final call with a 1-tap Sold out toggle.

Closing the shift
1 End Shift button produces the day’s report, checks the cash drawer, and sends the numbers to the owner’s spreadsheet.
Scenario. It’s 9 pm. The admin on shift wants to go home; the owner wants to know how the day went without driving to the café.


- An admin taps End Shift; other staff don’t see the button. A confirm dialog shows the date, says whether the report will go to Google Sheets or stay on the tablet, and flags anything to deal with first: unpaid held tabs, ingredients low on stock, or a report already made today.
- Confirming builds the end-of-day report (the Z-reading, a till’s standard end-of-day report) from today’s orders: gross and net sales, discounts, VAT, sales by payment type and by in-house vs. partner, and voided orders with their total.
- Check the cash drawer: starting float + cash sales − cash expenses = what should be in the drawer. QR and card sales are shown beside it, not counted in it, because they never touch the drawer.
- Unpaid held tabs go on the report as money still owed, not as sales, including tabs opened on an earlier day.
- The report saves on the tablet first, then uploads to Google Sheets if an account is connected. If the upload fails, the report is still saved and can be uploaded later from the menu. Nothing is lost.

Design decisions
- The owner never has to open the POS. They already work in Google Sheets, so that’s where the report lands.
- Separate cash from everything else. If QR payments counted as drawer cash, the drawer would look short every single day.
- Closing never fails because of the internet. The upload is a bonus on top of a report that always saves.
- Warn, don’t block. Unpaid tabs and low stock are flagged, but closing still goes ahead, and the report states how unpaid tabs were counted.
- The date is read when you confirm, not when the dialog opened, so a dialog left open past midnight still closes out the right day.
- Running it twice adds a report instead of replacing one, so an accidental second close can’t overwrite the first.
What to measure: minutes to close, and days where the drawer and the report disagree.
Lessons from field testing
The most useful findings came from watching the app on the real tablet, not from the desk.
| Found | What staff saw | Root cause | Fix |
|---|---|---|---|
| Oct 2026 | Tapping “Connect Google account” did nothing | The error message only showed once an account was already connected | Errors now show in every state, with Google’s reason |
| Oct 2026 | The account picker opened twice in a row | The second sign-in step didn’t know which account was already chosen | Pass the chosen account through; 1 picker |
| Oct 2026 | “Online” shown with no internet | The badge checked for a network, not for the internet | Confirm a real connection every 30 s and on every change |
| Oct 2026 | Payment button said “GCash” for every QR payment | The label matched 1 app, not how people pay | Renamed to “QR / InstaPay” |
The pattern: every one was an empty or wrong state, not a missing feature. Designing the failure path deserves as much care as the happy path.
Working with the client
JB, the owner, used the app at the counter and passed on whatever came up during shifts. 2 examples show how that turned into changes.
A forgotten End Shift left a gap in the owner’s sheet. If the person on shift didn’t tap End Shift, that day’s report was never made or uploaded, so the owner’s numbers for the day were missing. JB reported it, and I shipped a fix the same day: a System Settings screen where the owner sets the café’s business day hours and can export any date or range to Google Sheets. A day nobody closed can still reach the owner’s sheet, and if the café stays open past midnight, late sales count toward the right day. Each upload now goes into a new dated file by default, so past exports are kept rather than overwritten.

“How long has this tab been waiting?” JB wanted to see how long held tabs had been sitting unpaid. A bare number doesn’t tell staff when to act, so each held tab got a running timer in 3 states: green under 5 minutes, amber from 5, and red from 10. It’s the same scale the barista’s serve queue uses, so staff learn 1 rule for both.

Looking for friction before it’s reported. In 2 weeks of field testing, the End Shift gap was the only bug the owner reported, so the check-ins now focus on small frictions instead of breakages. One result: resuming a held tab now opens the order straight away, 1 tap fewer at the moment a customer is waiting to pay.
Outcomes and measurement
Sidecup has been running the app at the counter for about 2 weeks of field testing, with the café’s real menu and payment QR. The next step is putting numbers on its impact.
- Android tablet app and web version from 1 codebase, working fully offline.
- Real menu: 14 drinks in 4 categories, with add-ons.
- Cash, card, QR / InstaPay, senior / PWD discount and partner-order flows.
- Serve queue, stock warnings, End Shift report and Google Sheets export.
One of our biggest worries when introducing new tech was the learning curve for our staff. To our relief, the interface is amazingly clean and intuitive. New baristas and team members pick it up in a matter of minutes. No complex manuals or lengthy training sessions required!
Measurement plan. Baselines to be captured from the notebook routine and the first weeks of use.
| Metric | How it’s measured | Target |
|---|---|---|
| Time per order | Timed sample of 20 orders at peak | Faster than the notebook routine |
| Payment errors | Drawer vs. report mismatches per week | Zero |
| Longest wait | Serve-queue timestamps | Under 10 min at peak |
| Partner orders missing a reference | Sheets export check, monthly | Zero |
| Status trust | Uploads failing while badge shows Online | Zero |
| Staff onboarding | Time until a new hire rings up orders alone | 1 shift or less |
Reflection and next steps
Building the product myself made every design decision testable within a day, and showed me how much of good UX lives in states nobody draws in a mock-up.
What I’d do differently
- Interview the owner and staff with a script before building, instead of learning mostly from field bugs.
- Capture baselines on day one; numbers are much harder to recover later.
- Prototype the failure states (offline, sign-in errors, empty queue) as deliberately as the main screens.
Next
- Let staff manage the menu and prices themselves; today a menu change needs an update to the app.
- Add a “changes not uploaded yet” badge so the owner’s sheet is never silently out of date.
- Run a usability session with a new hire and record the first shift.
- Publish the app to production with Google, so sign-in no longer expires every 7 days.