payfrit-app/lib/services/order_polling_service.dart
John Mizerek 008b6d45b2 Add polling service for order status notifications
Client-side implementation:
- New service: order_polling_service.dart - Timer-based polling every 30s
- Updated AppState: track active order ID and current status
- Cart integration: start polling after order submission
- In-app notifications: color-coded SnackBar for status changes
- Notification widget: animated banner (created for future use)

Status notification colors:
- Blue = Submitted (1)
- Orange = Preparing (2)
- Green = Ready (3)
- Purple = Completed (4)

Polling workflow:
1. User submits order → polling starts with status=1
2. Every 30s: check backend for status changes
3. On update: show notification + update AppState
4. Continues until order completed or app closed

Simple polling approach (Option 2) with planned migration to self-hosted WebSocket push for instant updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 16:07:23 -08:00

119 lines
3.4 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'package:flutter/material.dart';
import 'api.dart';
/// Service that polls the backend API for order status updates
/// Uses a simple polling approach (Option 2) with 30-second intervals
class OrderPollingService {
static Timer? _pollingTimer;
static int? _currentOrderId;
static int? _lastKnownStatusId;
static Function(OrderStatusUpdate)? _onStatusUpdate;
/// Start polling for order status updates
static void startPolling({
required int orderId,
required int initialStatusId,
required Function(OrderStatusUpdate) onStatusUpdate,
}) {
print('[OrderPolling] 🔄 Starting polling for OrderID=$orderId, Status=$initialStatusId');
// Stop any existing polling
stopPolling();
_currentOrderId = orderId;
_lastKnownStatusId = initialStatusId;
_onStatusUpdate = onStatusUpdate;
// Poll every 30 seconds
_pollingTimer = Timer.periodic(const Duration(seconds: 30), (_) {
_checkForUpdate();
});
// Also check immediately
_checkForUpdate();
}
/// Stop polling
static void stopPolling() {
print('[OrderPolling] ⏹️ Stopping polling');
_pollingTimer?.cancel();
_pollingTimer = null;
_currentOrderId = null;
_lastKnownStatusId = null;
_onStatusUpdate = null;
}
/// Check if currently polling
static bool get isPolling => _pollingTimer != null && _pollingTimer!.isActive;
/// Get current order ID being polled
static int? get currentOrderId => _currentOrderId;
/// Internal method to check for updates
static Future<void> _checkForUpdate() async {
if (_currentOrderId == null || _lastKnownStatusId == null) {
print('[OrderPolling] ⚠️ No order to poll, stopping');
stopPolling();
return;
}
try {
print('[OrderPolling] 🔍 Checking status for OrderID=$_currentOrderId');
final response = await Api.post(
'/orders/checkStatusUpdate.cfm',
body: {
'OrderID': _currentOrderId,
'LastKnownStatusID': _lastKnownStatusId,
},
);
final data = response.data;
if (data['OK'] == true && data['HAS_UPDATE'] == true) {
final orderStatus = data['ORDER_STATUS'];
final newStatusId = orderStatus['StatusID'] as int;
final statusName = orderStatus['StatusName'] as String;
final message = orderStatus['Message'] as String;
print('[OrderPolling] ✅ Status update detected: $statusName (ID=$newStatusId)');
// Update last known status
_lastKnownStatusId = newStatusId;
// Notify listener
if (_onStatusUpdate != null) {
_onStatusUpdate!(OrderStatusUpdate(
orderId: _currentOrderId!,
statusId: newStatusId,
statusName: statusName,
message: message,
));
}
} else {
print('[OrderPolling] No status update');
}
} catch (e) {
print('[OrderPolling] ❌ Error checking status: $e');
}
}
}
/// Data class representing an order status update
class OrderStatusUpdate {
final int orderId;
final int statusId;
final String statusName;
final String message;
const OrderStatusUpdate({
required this.orderId,
required this.statusId,
required this.statusName,
required this.message,
});
@override
String toString() => 'OrderStatusUpdate(orderId: $orderId, statusId: $statusId, statusName: $statusName, message: $message)';
}