# Android Terminal App — Implementation Plan

## Overview

Native Android tablet POS app for loyalty terminal. 4 screens, landscape-only, kiosk mode. Calls the existing `/api/v1/store/loyalty/*` API — no backend changes.

**Already scaffolded:** Gradle project compiles, stub screens render on Pixel Tablet emulator. Retrofit API client, Room DB, Hilt DI, all dependencies wired.

---

## Screens & Workflows

### Screen 1: Setup (one-time)

```
┌──────────────────────────────────────────────────────┐
│                                                      │
│              RewardFlow Terminal                      │
│                                                      │
│         [Camera Preview — QR Scanner]                │
│                                                      │
│    Scan the setup QR code from your                  │
│    store settings page                               │
│                                                      │
│    ──── or enter manually ────                       │
│                                                      │
│    API URL:  [________________________]              │
│    Store Code: [____________________]                │
│    Auth Token: [____________________]                │
│                                                      │
│              [Connect]                               │
│                                                      │
└──────────────────────────────────────────────────────┘
```

**Flow:**
1. CameraX preview with ML Kit barcode scanning
2. QR code contains JSON: `{"api_url": "https://rewardflow.lu", "store_code": "FASHIONHUB", "auth_token": "jwt..."}`
3. Or manual entry (for dev/testing)
4. On scan/submit: call `GET /api/v1/store/loyalty/program` to verify connection
5. Download and cache: program config, branding (colors, logo), staff PINs, categories
6. Save to DataStore: api_url, auth_token, store_id, store_code
7. Navigate to PIN screen

**Files:**
- `ui/setup/SetupScreen.kt` — UI (exists, needs real implementation)
- `ui/setup/SetupViewModel.kt` — state (exists, needs expansion)
- `ui/scanner/QrScannerView.kt` — NEW, CameraX + ML Kit composable
- `data/repository/DeviceConfigRepository.kt` — NEW, DataStore read/write

**Web backend change needed:**
- New endpoint or page: "Generate tablet setup QR" button on store settings page (generates QR with JWT + store info)

---

### Screen 2: PIN / Staff Selection

```
┌──────────────────────────────────────────────────────┐
│  RewardFlow Terminal    Fashion Hub    [🌐 FR] [⚙️]  │
├──────────────────────────────────────────────────────┤
│                                                      │
│              Select your name                        │
│                                                      │
│    ┌─────────┐ ┌─────────┐ ┌─────────┐              │
│    │  Jane   │ │  Diana  │ │  Eric   │              │
│    │  Owner  │ │ Stylist │ │  Sales  │              │
│    └─────────┘ └─────────┘ └─────────┘              │
│                                                      │
│              Enter your PIN                          │
│                                                      │
│         [ • ]  [ • ]  [   ]  [   ]                   │
│                                                      │
│    ┌─────┐ ┌─────┐ ┌─────┐                          │
│    │  1  │ │  2  │ │  3  │                          │
│    ├─────┤ ├─────┤ ├─────┤                          │
│    │  4  │ │  5  │ │  6  │                          │
│    ├─────┤ ├─────┤ ├─────┤                          │
│    │  7  │ │  8  │ │  9  │                          │
│    ├─────┤ ├─────┤ ├─────┤                          │
│    │  C  │ │  0  │ │  ←  │                          │
│    └─────┘ └─────┘ └─────┘                          │
│                                                      │
│    3 pending transactions ⟳                          │
│                                                      │
└──────────────────────────────────────────────────────┘
```

**Flow:**
1. Show list of staff names (from cached PINs, refreshed from API periodically)
2. Seller taps their name → name highlighted, keypad appears
3. Enter 4-digit PIN → verified locally against bcrypt hash (cached from API)
4. If correct → navigate to Terminal with staffPinId + staffName
5. If wrong → shake animation, error message, clear digits
6. Language selector (globe icon) → switches app language, saved per-seller in DataStore
7. Settings gear → shows pending sync count, device info, "Reset device" option
8. If offline → show "Offline" badge, PIN still works (cached)

**Files:**
- `ui/pin/PinScreen.kt` — UI (exists, needs seller list + real PIN verification)
- `ui/pin/PinViewModel.kt` — NEW, loads cached PINs, verifies, manages language
- `data/repository/StaffPinRepository.kt` — NEW, caches PINs from API in DataStore

**Important:** PIN hashes are bcrypt — need to verify client-side. Add `implementation("at.favre.lib:bcrypt:0.10.2")` to gradle deps (pure Java bcrypt, no native deps).

---

### Screen 3: Terminal (main POS)

```
┌──────────────────────────────────────────────────────┐
│  Staff: Diana    Fashion Hub    [Offline ⚡] [🔒]    │
├──────────────────────┬───────────────────────────────┤
│                      │                               │
│  Search Customer     │   No customer selected        │
│  ┌────────────────┐  │                               │
│  │ 🔍 Card / Email│  │   Scan a QR code or search    │
│  └────────────────┘  │   by card number / email      │
│                      │                               │
│  [📷 Scan QR Code]   │                               │
│                      │                               │
│  ─── or ───          │                               │
│                      │                               │
│  [+ Enroll Customer] │                               │
│                      │                               │
├──────────────────────┤                               │
│  Recent Transactions │                               │
│  ┌────────────────┐  │                               │
│  │ 10:32 +50pts   │  │                               │
│  │ 10:15 +1 stamp │  │                               │
│  │ 09:45 Enrolled │  │                               │
│  └────────────────┘  │                               │
│                      │                               │
└──────────────────────┴───────────────────────────────┘
```

**After customer found:**

```
┌──────────────────────────────────────────────────────┐
│  Staff: Diana    Fashion Hub    [Online ✓]  [🔒]     │
├──────────────────────┬───────────────────────────────┤
│                      │  Jane Doe                     │
│  Search Customer     │  jane@example.com        [✕]  │
│  ┌────────────────┐  │  Card: 1234-5678-9012         │
│  │ 🔍 Card / Email│  │                               │
│  └────────────────┘  │  ┌───────────────────────┐    │
│                      │  │    500 Points          │    │
│  [📷 Scan QR Code]   │  │    3 / 10 Stamps       │    │
│                      │  └───────────────────────┘    │
│  ─── or ───          │                               │
│                      │  ┌──────────┐ ┌──────────┐    │
│  [+ Enroll Customer] │  │ + Stamp  │ │ Redeem   │    │
│                      │  │          │ │ Stamps   │    │
├──────────────────────┤  ├──────────┤ ├──────────┤    │
│  Recent Transactions │  │ + Points │ │ Redeem   │    │
│  ┌────────────────┐  │  │ EUR [__] │ │ Reward ▼ │    │
│  │ 10:32 +50pts   │  │  └──────────┘ └──────────┘    │
│  │ 10:15 +1 stamp │  │                               │
│  │ 09:45 Enrolled │  │                               │
│  └────────────────┘  │                               │
│                      │                               │
└──────────────────────┴───────────────────────────────┘
```

**Flow:**
1. **Search**: text field for card number or email, searches via API
2. **QR Scan**: opens camera overlay, scans loyalty card QR → lookup
3. **Enroll**: inline form (name, email, birthday) → creates customer + card
4. **Customer found**: right panel shows card details + action buttons
5. **Actions**: each opens a bottom sheet or dialog with:
   - Category selector (multi-select pills — same as web terminal)
   - Confirm button
   - No separate PIN entry (already authenticated on PIN screen)
6. **Earn points**: EUR amount input (digits only) + category → API call
7. **Add stamp**: category → API call
8. **Redeem stamps**: confirm dialog → API call
9. **Redeem reward**: reward picker dropdown → API call
10. **Offline**: stamp/earn queued to Room DB, redeem shows error dialog
11. **Auto-lock**: 2 min idle → navigate back to PIN screen
12. **Lock button** (top right): immediate lock to PIN screen

**Files:**
- `ui/terminal/TerminalScreen.kt` — UI (exists as stub, needs full implementation)
- `ui/terminal/TerminalViewModel.kt` — NEW, state management, API calls, offline queue
- `ui/terminal/components/CustomerCard.kt` — NEW, customer info + balance display
- `ui/terminal/components/ActionButtons.kt` — NEW, stamp/points/redeem buttons
- `ui/terminal/components/CategorySelector.kt` — NEW, multi-select pill group
- `ui/terminal/components/EnrollForm.kt` — NEW, inline enrollment
- `ui/terminal/components/TransactionList.kt` — NEW, recent transactions
- `ui/scanner/QrScannerOverlay.kt` — NEW, fullscreen camera overlay for QR scan

---

### Screen 4: Enrollment (inline or dialog)

```
┌──────────────────────────────────────┐
│         Enroll New Customer          │
│                                      │
│  Name *     [____________________]   │
│  Email *    [____________________]   │
│  Phone      [____________________]   │
│  Birthday   [____________________]   │
│                                      │
│       [Cancel]        [Enroll]       │
└──────────────────────────────────────┘
```

**Flow:**
1. Shown as a bottom sheet dialog from Terminal screen
2. Name + email required, phone + birthday optional
3. On submit: `POST /store/loyalty/cards/enroll`
4. On success: auto-selects the new card in the terminal
5. Offline: queued to Room DB, card shown with "Pending sync" badge

---

## Data Layer

### DataStore (device config — persists across app restarts)
- `api_url: String`
- `auth_token: String`
- `store_id: Int`
- `store_code: String`
- `store_name: String`
- `program_json: String` (cached program config)
- `staff_pins_json: String` (cached PINs list with bcrypt hashes)
- `categories_json: String` (cached categories)
- `is_device_set_up: Boolean`
- `seller_language_{pin_id}: String` (per-seller language preference)

### Room DB (offline transaction queue)
Already created: `PendingTransaction` entity + DAO.
- type: "stamp", "points_earn", "enroll"
- requestJson: full API request body
- status: pending → syncing → synced / failed

### WorkManager (background sync)
- `SyncWorker.kt` — NEW, runs when connectivity returns
- Processes pending transactions in order (FIFO)
- Retries failed ones with backoff
- Registered as `OneTimeWorkRequest` with network constraint

---

## Implementation Phases

### Phase A: Core infrastructure (~1 day)
1. `DeviceConfigRepository` — DataStore read/write for all config
2. `StaffPinRepository` — cache + refresh PINs from API
3. `CategoryRepository` — cache + refresh categories
4. `NetworkMonitor` — observe connectivity state (ConnectivityManager)
5. `AuthInterceptor` — OkHttp interceptor that adds Bearer token from DataStore
6. Add bcrypt dependency for client-side PIN verification
7. String resources for EN/FR/DE/LB (all 4 locales)

### Phase B: Setup screen (~0.5 day)
1. QR scanner composable (CameraX + ML Kit)
2. Manual entry form (for dev)
3. Connection verification (call program API)
4. Download + cache initial config
5. Navigate to PIN on success

### Phase C: PIN screen (~0.5 day)
1. Staff name list (from cached PINs)
2. PIN keypad with verification (bcrypt compare)
3. Per-seller language preference
4. Language selector (globe icon)
5. Pending sync count indicator
6. Error handling (wrong PIN, locked PIN)

### Phase D: Terminal screen (~2 days)
1. Two-pane landscape layout
2. Search field + API lookup
3. QR scanner overlay (reuse from setup)
4. Customer card display (balance, stamps, rewards)
5. Action buttons (stamp, earn, redeem stamps, redeem reward)
6. Category multi-select pills
7. EUR input (digits only, same validation as web)
8. Reward dropdown picker
9. Recent transactions list
10. Offline indicator in header
11. Auto-lock timer (2 min idle)

### Phase E: Offline queue + sync (~0.5 day)
1. Queue stamp/earn/enroll to Room when offline
2. Show "Pending sync" badges
3. SyncWorker with network constraint
4. Toast on sync completion
5. Redeem shows "offline" error dialog

### Phase F: Polish + kiosk (~0.5 day)
1. Lock Task Mode setup
2. App icon + splash screen
3. Error handling (network errors, API errors, toast messages)
4. Loading states on all API calls
5. Landscape-only enforcement
6. Hide navigation/status bars (immersive mode)

**Total: ~5 days**

---

## Web Backend — One Change Needed

**Generate setup QR code** — new button on the store settings page that creates a QR containing `{"api_url": "...", "store_code": "...", "auth_token": "..."}`. The auth_token should be a long-lived store API JWT (or a device-specific token).

This could be a simple endpoint: `POST /api/v1/store/loyalty/device-setup-token` that returns a JWT with a 1-year expiry, then the settings page renders it as a QR code.

---

## API Models to Add

The existing `ApiModels.kt` is missing:
- `category_ids: List<Int>?` on `StampRequest` and `PointsEarnRequest`
- `CategoryResponse` model for cached categories
- `GET /api/v1/store/loyalty/categories` endpoint in `LoyaltyApi.kt`

---

## String Resources (multi-language)

4 files:
- `res/values/strings.xml` (EN — default)
- `res/values-fr/strings.xml`
- `res/values-de/strings.xml`
- `res/values-lb/strings.xml`

Key strings (~50):
- Setup: "Scan QR code", "Connect", "Connecting..."
- PIN: "Select your name", "Enter your PIN", "Wrong PIN", "PIN locked"
- Terminal: "Search customer", "Scan QR", "Enroll customer", "No customer selected"
- Actions: "Add stamp", "Earn points", "Redeem stamps", "Redeem reward", "Purchase amount"
- Categories: "Select category"
- Status: "Online", "Offline", "Syncing", "X pending"
- Errors: "Connection failed", "No internet", "Try again"
