hoangsonww / hoangsonww/Boxed-Inventory-App
QR Labels + Scan-to-Open + Offline Cache (PWA-friendly)
- Dominant language
- TypeScript
- Stars
- 17
- Forks
- 10
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
Make it effortless to find a box in the real world: let users **print QR labels** for each box, then **scan** with the phone camera to jump straight to the box view—even **offline**. Add a small **offline cache** for box metadata & recent items so the scan works without a network, syncing once back online.
## Goals
* ✅ Generate a **shareable QR** (URL deep link) per box.
* ✅ **Print-ready** label sheet with box name/location + QR.
* ✅ In-app **camera scanner** that opens `Box Details` by ID.
* ✅ **Offline cache** of recent boxes & items so scanning works without data.
* ✅ Background **resync** + conflict-safe updates when online.
## High-Level Design
### 1) Deep Link & QR
* Canonical route: `/boxes/[boxId]` (already exists).
* Create a small helper to build an absolute URL (uses `NEXT_PUBLIC_SITE_URL` fallback to `window.location.origin`).
* Generate QR server-side or client-side (client is fine) using `qrcode` or `qrcode.react`.
### 2) Print Labels
* New page: `/boxes/[boxId]/label` with a **print-optimized** layout (Avery-like).
* Include: Box name, location, status pill, QR, tiny “boxed-app.vercel.app”.
* `@media print` CSS: remove chrome, margins, set exact size (e.g., 3×2in).
### 3) Camera Scanner
* New route `/scan` with a **camera scanner** component:
* Use `@zxing/browser` (or `jsQR` with canvas) to scan QR → parse URL → extract `boxId`.
* If app is offline, check cache first; otherwise navigate to `/boxes/[boxId]`.
### 4) Offline Cache (PWA)
* Storage layer: `localforage` (IndexedDB) or `idb-keyval`.
* Cache shapes:
```ts
type CachedBox = { id: string; name: string; location?: string; status: 'packed'|'unpacked'; updatedAt: string };
type CachedItem = { id: string; boxId: string; name: string; quantity: number; updatedAt: string };
```
* On visiting a box or listing, **hydrate cache** with the payload.
* On `/scan`, resolve locally:
* If `CachedBox` present → show minimal Box Details skeleton from cache.
* When online, **revalidate** via Supabase and refresh UI.
### 5) Sync & Freshness
* Keep a per-box `cacheVersion` (or last row `updated_at`) alongside:
* If newer from Supabase, overwrite cache.
* If offline edits are later added (future), we’ll extend to queue mutations; for now this is **read-only offline**.
## DB / API
*No schema changes required.* We reuse existing `boxes` & `items` reads.
**Optional**: expose a lean **box summary** RPC (`select id,name,location,status,updated_at from boxes where id = :id`) to keep payloads small for revalidation.
## UI/UX
* **Box Details**: “Print Label” button (visible on mobile & desktop).
* **Boxes List**: overflow menu → “Print Label” & “Show QR”.
* **Scanner page**: floating torch toggle (where supported), guidance text, haptic feedback on detection.
* **Offline states**: badge “Offline cache” when viewing cached data; toast “Synced” after revalidate.
## Security & Privacy
* Use authenticated, **non-guessable** IDs (existing UUIDs).
* QR points to authenticated route; if not logged in or no access → show normal auth/403 flow.
* Do **not** embed PII in the QR—only the URL.
## Performance
* Lazy-load scanner dependency on `/scan` only.
* Limit cache size (e.g., last **50 boxes** + items capped **1000**) with simple LRU.
* Precache scanner & label pages with Next PWA service worker (if present)—otherwise just rely on runtime cache in IndexedDB.
## Acceptance Criteria
* ✅ Scanning a printed QR opens the **correct box** within 1s online, 2s offline.
* ✅ Label prints on common sheet sizes without clipping.
* ✅ Offline device with previously viewed box shows cached name/location/status/items.
* ✅ Returning online refreshes content silently (toast confirmation).
* ✅ Access control respected (QR doesn’t bypass auth).
## Tasks
### Frontend
* [ ] `lib/qr.ts`: build absolute box URL; QR component (client).
* [ ] `/boxes/[boxId]/label`: print-optimized page + button in Box Details.
* [ ] `/scan`: scanner page using `@zxing/browser`; parse & route.
* [ ] `lib/cache.ts`: IndexedDB helpers (get/set, LRU, caps).
* [ ] Hook boxes/items fetch to write-through cache; read on fallback.
* [ ] Toasts, badges, empty/offline states.
### PWA (optional but recommended)
* [ ] Add/extend service worker to precache `/scan`, label CSS, and shell assets.
* [ ] Background sync: revalidate a scanned `boxId` when back online.
### QA
* [ ] E2E: scan → open correct box (desktop cam + mobile).
* [ ] Print snapshots across Chrome/Safari (A4/Letter).
* [ ] Offline mode: airplane mode after viewing a box → scan → cached open.
## Nice-to-Have (Future)
* Public **view-only** share links per box with expiring tokens.
* Bulk “Print all labels” for current household.
* **NFC tag** support (read tag → open box).
---
Contributor guide
Assessment
This issue has not been assessed yet.