hoangsonww / hoangsonww/Boxed-Inventory-App

QR Labels + Scan-to-Open + Offline Cache (PWA-friendly)

Open
#8 0 comments 0 reactions 1 assignee Claimed by @hoangsonww View on GitHub
bug documentation enhancement good first issue help wanted question
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.