# Plan: Groups & Batches Feature ## API Surface (from Postman) ### Groups - `GET /api/public/recipient-groups?userId={userId}` — list groups - `GET /api/public/recipient-groups/members?recipientGroupId={id}` — list members - `POST /api/public/recipient-groups` — create group - Body: `{ name, description, userId, recipients: [{name, phoneNumber, latestProviderLabel, account}] }` ### Batches - `GET /api/public/group-batches?recipientGroupId={id}&sort=createdAt,desc` — list batches - `GET /api/public/group-batches/items?groupBatchId={id}&sort=createdAt,desc` — list batch items - `POST /api/public/group-batches` — create batch (recipientGroupId, userId, currency, amount, paymentProcessorLabel, paymentProcessorName, paymentProcessorImage, transactionTemplate) - `POST /api/public/group-batches/{batchId}/confirm` — confirm batch - `POST /api/public/group-batches/{batchId}/request` — request batch (payment) - `POST /api/public/group-batches/{batchId}/poll` — poll batch status ## Files To Create ### Phase 1: Models - `lib/models/recipient_group_model.dart` — RecipientGroup, RecipientMember, CreateGroupRequest - `lib/models/group_batch_model.dart` — GroupBatch, GroupBatchItem, CreateBatchRequest (transactionTemplate typed as TransactionModel from lib/screens/transactions/transaction_model.dart), BatchActionRequest. No separate TransactionTemplate class needed. ### Phase 2: Controllers - `lib/screens/groups/group_controller.dart` — ChangeNotifier, list/create groups, list members - `lib/screens/groups/batch_controller.dart` — ChangeNotifier, list/create batches, list items, confirm/request/poll ### Phase 3: Screens - `lib/screens/groups/groups_screen.dart` — list all groups - `lib/screens/groups/group_create_screen.dart` — create group form (name, description, multiline textarea: one recipient per line as `name,account`; phoneNumber defaults to account value) - `lib/screens/groups/group_detail_screen.dart` — tabbed view: Batches first (default), Members second; receives RecipientGroup via extra; FAB on Batches tab to create new batch - `lib/screens/groups/batch_create_screen.dart` — create batch form (provider, processor, amount, currency); on success navigate to BatchDetailScreen passing created batch via extra - `lib/screens/groups/batch_detail_screen.dart` — batch items list + Confirm/Request/Poll action buttons - `lib/screens/groups/batch_item_screen.dart` — single batch item detail view ### Phase 4: Integration - `lib/navbar.dart` — add Groups as 3rd nav item (index 2) - `lib/main.dart` — add routes + GroupController + BatchController providers ## Routes - `/groups` → GroupsScreen - `/groups/create` → GroupCreateScreen - `/groups/:groupId` → GroupDetailScreen - `/groups/:groupId/batches/create` → BatchCreateScreen - `/groups/:groupId/batches/:batchId` → BatchDetailScreen - `/groups/:groupId/batches/:batchId/items/:itemId` → BatchItemScreen ## Action Return Types - `createBatch` → `ApiResponse` - `confirmBatch` → `ApiResponse` - `requestBatch` → `ApiResponse` (reuses TransactionModel) - `pollBatch` → `ApiResponse` - List endpoints → `ApiResponse>` ## BatchDetailScreen Auto-Poll Logic After **Request** action: 1. `requestBatch()` returns `TransactionModel` 2. Check `transaction.pollingStatus` — if successful (e.g. `SUCCESS`, `COMPLETE`) → no poll needed, refresh batch 3. Otherwise → auto-trigger `pollBatch()` immediately without user input 4. If auto-poll fails (network error / non-terminal status) → show **Poll** button for manual retry 5. Batch status from poll response drives UI state (show/hide action buttons) ## Key Patterns - Controllers extend ChangeNotifier - Models use `@JsonSerializable` with generated `.g.dart` files - All network calls use `ApiResponse` wrapping pattern (try/catch in controller) - Use `ListenableBuilder` in screens - Read `userId`/`token` from SharedPreferences - Pass extra data via GoRouter `extra:` parameter - `idempotencyKey` for batch actions generated as `"batch-{batchId}-{action}-{timestamp}"` - Navbar: add Groups at index 2, update `_handleTap` + `_calculateSelectedIndex` ## Member Input Parsing (group_create_screen) - One recipient per line: `name,account` - `phoneNumber` defaults to `account` value - `latestProviderLabel` defaults to `""` ## Decisions - `TransactionModel` reused as-is for `transactionTemplate` field — no wrapper class - No pagination on groups/batches lists in this iteration - No edit/delete for groups or batches in scope - Poll button only shown as manual fallback after auto-poll failure