feat: make venues public
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
## 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<String, dynamic> 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<List<Venue>> fetchVenues()` — calls `GET /api/v1/venues/`
|
||||
- `Future<void> 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
|
||||
Reference in New Issue
Block a user