## KoolTab Flutter App — Remote Backoffice Integration (Improved Pairing Flow) You are implementing the device-side integration for the KoolTab remote tab control system. The Flutter app (bar POS terminal) needs to register with a remote backend, sync open tabs, and process remote close-tab commands. ### Project Location `C:\Users\bram\StudioProjects\kooltab2` ### Architecture Overview - State management: `provider` (ChangeNotifier pattern) - Routing: `go_router` - Local DB: `drift` (SQLite ORM) - HTTP: `http` package - Auth tokens stored in `flutter_secure_storage` - MVVM pattern: views → viewmodels → services - Models in `lib/models/` - Services in `lib/services/` - ViewModels in `lib/viewmodels/` - Views in `lib/views/` - Config constants in `lib/utils/app_config.dart` - Main app bootstrap in `lib/app/app_bootstrap.dart` ### Pairing Flow (improved) Instead of typing a UUID venueId, the flow is: 1. Tablet calls `GET https://api.kooltab.com/api/v1/venues/` (public, no auth) 2. Gets list of `[{id, name}]` — admin picks venue from a dropdown 3. Admin enters device name (e.g. "Bar terminal 1") 4. Admin enters the 6-character registration code (generated from dashboard) 5. Tablet calls `POST /devices/register` with venueId + name + code 6. Stores deviceId + token + venueId + serverUrl in secure storage 7. Sync starts automatically ### Existing Sync Infrastructure (already built) Do NOT rewrite these files, just modify as needed: - **`lib/services/remote_sync_service.dart`** (410 lines) — `setCredentials()`, `startSync()`, `stopSync()`, sync loop, command processing, ACKs - **`lib/viewmodels/remote_sync_view_model.dart`** (90 lines) — ChangeNotifier wrapper - **`lib/models/remote_command.dart`** (135 lines) — DTOs: `RemoteCommand`, `SyncRequest`, `SyncResponse`, `SnapshotTab`, `CommandAck` - **`lib/utils/app_config.dart`** — URL constants - **`lib/models/payment_method.dart`** — payment enums ### What to Build #### 1. Update Server URL In `lib/utils/app_config.dart`: ```dart const kRemoteServerUrl = 'https://api.kooltab.com/api/v1/'; ``` #### 2. Add Venue Model Create `lib/models/venue.dart`: ```dart class Venue { final String id; final String name; Venue({required this.id, required this.name}); factory Venue.fromJson(Map json) => Venue(id: json['id'], name: json['name']); } ``` #### 3. Update RemoteSyncService — Add venueId Storage In `lib/services/remote_sync_service.dart`: - Add `venueId` parameter to `setCredentials()` signature - Store venueId in FlutterSecureStorage alongside deviceId/token/serverUrl - Add `String? get venueId` getter - Update `isConfigured` to also check venueId exists - Add `clearCredentials()` method (clears all stored credentials) #### 4. Registration ViewModel Create `lib/viewmodels/registration_view_model.dart`: - `Future> fetchVenues()` — calls `GET /api/v1/venues/` - `Future registerDevice(String venueId, String name, String code)` — calls `POST /devices/register`, then stores credentials via `RemoteSyncService.setCredentials()` - States: `initial`, `loading`, `venuesLoaded`, `error`, `registered` - Expose as ChangeNotifier #### 5. Registration UI Create `lib/views/registration_view.dart` — a full-screen registration flow with steps: **Step 1 — Select venue:** - Dropdown list of venues fetched from API - Show loading spinner while fetching - Show error + retry if fetch fails - "Next" button when venue selected **Step 2 — Device details:** - Device name text field (with default "Bar terminal 1") - Registration code text field (6 characters) - "Register" button - Loading state while registering - Error display if registration fails **Step 3 — Success screen:** - Success animation/message - "Start using KoolTab" button → navigates to main bar screen and triggers sync #### 6. Wire Registration into Navigation In `lib/app/app_bootstrap.dart`: - On app startup, check `remoteSyncService.isConfigured` - If NOT configured: show registration screen instead of main bar screen - If configured: proceed to main bar screen as normal In `lib/app/router.dart`: - Add route for registration screen as initial route when not configured #### 7. Disconnect Option In the **Settings screen** (`lib/views/settings_view.dart`) or **Admin screen**: - If configured, show "Connected to [venue name]" with device name - "Disconnect" button → calls `clearCredentials()`, navigates back to registration screen ### API Endpoints Summary | Method | Endpoint | Auth | Purpose | |--------|----------|------|---------| | GET | `/api/v1/venues/` | None | List venues (returns `[{id, name}]`) | | POST | `/api/v1/devices/register` | None | Register device (body: venueId, name, registrationCode) | | POST | `/api/v1/devices/:id/sync` | Bearer token | Sync tabs, receive commands | | POST | `/api/v1/devices/:id/commands/:cmdId/ack` | Bearer token | Acknowledge command | ### Registration API Details **Venues list:** ``` GET https://api.kooltab.com/api/v1/venues/ Response: [{"id": "uuid", "name": "KoolTab Demo Bar"}, ...] ``` **Device registration:** ``` POST https://api.kooltab.com/api/v1/devices/register { "venueId": "uuid-from-venue-list", "name": "Bar terminal 1", "registrationCode": "A8F3C2" } Response 201: { "deviceId": "uuid", "token": "dev_abc123...", "tokenExpiresAt": "2026-..." } ``` The registration code is generated from the dashboard (Devices → Add Device → generates 6-char code valid for 10 minutes). ### Files to Create - `lib/models/venue.dart` - `lib/viewmodels/registration_view_model.dart` - `lib/views/registration_view.dart` ### Files to Modify - `lib/utils/app_config.dart` — update URL - `lib/services/remote_sync_service.dart` — add venueId + clearCredentials - `lib/app/app_bootstrap.dart` — check isConfigured, route to registration if not - `lib/app/router.dart` — add registration route - `lib/views/admin_screen_view.dart` or `lib/views/settings_view.dart` — add disconnect option ### Important Notes - Do NOT break the existing sync loop — registration only configures credentials - The existing sync sends `SnapshotTab` and processes `close_tab` commands stays unchanged - Use `flutter_secure_storage` for persisting credentials - Handle loading/error states in the UI - After registration, call `remoteSyncViewModel.start()` to begin syncing - Tabs appear on the dashboard within ~90 seconds of first sync