WFDASH-WEBAPP/docs/counter-pwa.md
Dave 18397bc9be feat(counter): add standalone counter order PWA
This commit adds a full unauthenticated PWA for in-store retail order taking:
- Dedicated `/new-order` route with complete order form, customer/company picker
- Public `/api/counter/*` endpoints for search, order creation, and photo uploads
- Photo capture and gallery selection with 10-photo per order limit
- Offline caching via dedicated service worker and app manifest
- Shortcut redirect at `/counter` for quick installation
- Comprehensive setup and usage documentation in `docs/counter-pwa.md`
- Minor cleanup to root layout metadata and service worker registration
- Small layout fix for collection pool row text truncation

Orders submitted via the PWA appear immediately in the main CRM's New tab, and the app is designed for trusted office kiosk phones without requiring user login.
2026-08-13 09:27:20 +02:00

4.8 KiB

Counter PWA — install + use

A standalone, installable web app on the office counter phones. One tap, no login, and the order lands on the New tab of the order board.

What it does

  • Pick an existing customer (search by name, email, or phone) — or tap Add new to create a customer inline (and a new company in the same sheet if needed).
  • Add any number of line items (description, qty, optional price).
  • Snap photos with the phone camera (up to 10 per order).
  • Tap Submit Order — the order appears on /orders under the New tab immediately (via PocketBase realtime).

Install on a counter phone

iPhone (Safari)

  1. Open https://<your-coolify-host>/new-order in Safari.
  2. Tap Share → Add to Home Screen.
  3. Confirm the name "Counter". The icon appears on the home screen.
  4. Tap the icon — it opens full-screen, no address bar.

Android (Chrome)

  1. Open the URL in Chrome.
  2. Tap the ⋮ menu → Install app (or "Add to Home Screen").
  3. Confirm. The icon appears in the launcher.
  4. Tap the icon — it opens full-screen.

The same shortcut URL works: https://<host>/counter redirects to /new-order.

What runs where

Layer What
Browser (phone) /new-order — single-screen form, no login
Next.js route handlers /api/counter/* — public, no Bearer token
src/lib/server/counter-service.ts Thin service layer; calls existing createOrder(), createCustomer(), createCompany()
PocketBase orders row + order_items rows with item_status="pending" (lands on New tab) + photos on orders.order_attachements

The counter routes intentionally do not go through the same Bearer-token gate as /api/v1/* — the counter phones are kiosk-mode and assumed to be on office Wi-Fi.

Smoke test after deploy

  1. npm run dev → open http://<host>:3000/new-order from the dev machine.
  2. Pick an existing customer, add one line item, hit Submit.
  3. Open /orders in the main app — confirm the new order sits on the New tab.
  4. Submit again, this time with a photo. Confirm the photo opens from the order detail page (/orders/<id>).
  5. From an iPhone on the same Wi-Fi, open the same URL → Add to Home Screen → confirm the standalone icon works.

Troubleshooting

  • Order doesn't appear on the board. Check Coolify logs for /api/counter/orders — PB admin SDK auth (PB_ADMIN_EMAIL / PB_ADMIN_PASSWORD) must be set in .env.local.
  • Photos rejected with 413. The phone uploaded a file > the cap. Default COUNTER_MAX_PHOTO_MB=8. Drop it lower if your network is slow, or compress on the client (not implemented yet).
  • Customer combobox empty. The /api/counter/customers endpoint failed on first load — see toast in the top-right and check the browser console.
  • "Photo upload failed" after submit. The order was saved (it appears on the board) but the photos didn't make it. Open the order in the main CRM and re-attach from there.
  • Sheet won't open on iPhone 16+. Some iOS versions need a user-tap to fire <input capture>. The form provides two buttons (camera + gallery) so the operator can fall back to gallery.

Files added / changed

src/app/(counter)/layout.tsx                              new
src/app/(counter)/shell.tsx                                new
src/app/(counter)/page.tsx                                 new (redirect)
src/app/(counter)/new-order/page.tsx                       new
src/app/(counter)/new-order/components/                    new dir
  header.tsx
  counter-select.tsx
  new-customer-sheet.tsx
  items-list.tsx
  photo-capture.tsx
  success-screen.tsx
src/lib/server/counter-service.ts                          new
src/app/api/counter/customers/route.ts                     new
src/app/api/counter/companies/route.ts                     new
src/app/api/counter/orders/route.ts                        new
src/app/api/counter/orders/[id]/attachments/route.ts       new
public/manifest-counter.json                               new
public/sw-counter.js                                       new
src/components/service-worker-registration.tsx             edited (dual SW)
src/app/layout.tsx                                         edited (cleanup)
next.config.ts                                             edited (/counter redirect)
.env.local                                                 edited (COUNTER_MAX_PHOTO_MB)

Out of scope (intentionally)

  • No login / PIN — phones are physically controlled by staff.
  • No offline-queue — a flaky network at submit time means the order is lost. The PWA caches the page chrome so navigation stays fast, but a pending-submit queue would be a separate feature.
  • No per-user audit (rep picker left blank — rep can be set later in the main CRM process page).
  • No quote linking — counter orders are standalone.