Files
doormile_milderapp/lib/xpress/providers/delivery/delivery_provider.dart
Thiru-tenext d7348e253f Miler rider app: surface system, visible design language, backend lifecycle
Design system
- MilerSurface ladder (canvas → working → raised → floating) with MilerPanel
  as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1.
- Visible vocabulary applied across Home, Deliveries, Activity, Account and
  the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x),
  canvas wells for anything that opens, small filled tags for shelf labels,
  demoted placeholders. Recorded in DESIGN_SYSTEM.md §6.
- One icon family: 222 Material glyphs migrated to Lucide; none left outside
  lib/xpress.
- Colour semantics corrected: amber only for what is genuinely owed, brand red
  reserved for the live stop, disabled primaries go neutral rather than pale.

Data and lifecycle
- lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart
  makes admin sequence the single ordering authority; service_day.dart, and
  stop_area.dart rewritten against live Coimbatore addresses (digit-token
  stripping, city stoplist, street suffixes, stammer collapse).
- countLabel states the load once, in bags.

Testing
- 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity,
  sheets and verify, with test/failures/ now gitignored (diff debris).
- New pins: home_gutter_test, stop_area_test, plus updated structural bounds.

Note: this commit also carries pre-existing working-tree deletions that were
present before this work (API_SPEC.md, README.md, demo test fixtures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 05:40:35 +05:30

144 lines
4.4 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:miler/xpress/views/helpers/constants/apiconstants.dart';
class DeliveryProvider {
final http.Client _client;
DeliveryProvider({http.Client? client}) : _client = client ?? http.Client();
Future<http.Response> _getWithRetry(Uri uri, {int maxAttempts = 3}) async {
int attempt = 0;
while (true) {
attempt++;
try {
return await _client.get(uri);
} on SocketException catch (_) {
if (attempt >= maxAttempts) rethrow;
} on http.ClientException catch (_) {
if (attempt >= maxAttempts) rethrow;
}
// Exponential backoff: 300ms, 600ms
final delayMs = 300 * attempt;
await Future.delayed(Duration(milliseconds: delayMs));
}
}
Future<List<dynamic>> getDeliveryQueues({
required bool live,
required int userid,
String? orderstatus,
}) async {
final base = live
? ApiConstants.deliveryQueueLive
: ApiConstants.deliveryQueueDev;
// Get current date in YYYY-MM-DD format
final now = DateTime.now();
final mm = now.month.toString().padLeft(2, '0');
final dd = now.day.toString().padLeft(2, '0');
final yyyy = now.year.toString();
final today = "$yyyy-$mm-$dd";
final qp = <String, String>{
'userid': userid.toString(),
'fromdate': today,
'todate': today,
't': DateTime.now().millisecondsSinceEpoch.toString(),
};
if (orderstatus != null && orderstatus.isNotEmpty) {
qp['orderstatus'] = orderstatus;
}
final uri = Uri.parse(base).replace(queryParameters: qp);
final res = await _getWithRetry(uri);
if (res.statusCode >= 200 && res.statusCode < 300) {
// Log URL and raw status
// ignore: avoid_print
print('[DELIVERIES][GET] URL: ${uri.toString()}');
final decoded = json.decode(res.body);
final data = decoded is Map<String, dynamic>
? (decoded['details'] ?? decoded['data'] ?? decoded)
: decoded;
if (data is List) {
try {
// Pretty log the list if deliveries are found
if (data.isNotEmpty) {
// ignore: avoid_print
// print('[DELIVERIES][GET] Data: ${json.encode(data)}'); // Heavy log, disabled for performance
} else {
// ignore: avoid_print
// print('[DELIVERIES][GET] Data: []');
}
} catch (_) {}
return data;
}
if (data is Map && data['items'] is List) return data['items'] as List;
return [];
}
throw Exception('Failed (${res.statusCode})');
}
Future<List<dynamic>> getDeliveryQueuesPicked({
required bool live,
required int userid,
}) async {
// Use v3 getdeliveries with fromdate/todate as today's date (dynamic)
final base = live
? ApiConstants.currentDeliveryV3Live
: ApiConstants.currentDeliveryV3Dev;
// Get current date in YYYY-MM-DD format (dynamically updated each day)
final now = DateTime.now();
final mm = now.month.toString().padLeft(2, '0');
final dd = now.day.toString().padLeft(2, '0');
final yyyy = now.year.toString();
final today = "$yyyy-$mm-$dd";
final qp = <String, String>{
'userid': userid.toString(),
'fromdate': today,
'todate': today,
't': DateTime.now().millisecondsSinceEpoch.toString(),
};
final uri = Uri.parse(base).replace(queryParameters: qp);
final res = await _getWithRetry(uri);
if (res.statusCode >= 200 && res.statusCode < 300) {
// ignore: avoid_print
print('[DELIVERIES][GET_PICKED] URL: ${uri.toString()}');
final decoded = json.decode(res.body);
// The API returns {"code":200,"details":[...],"message":"Success","status":true}
final data = decoded is Map<String, dynamic>
? (decoded['details'] ?? decoded['data'] ?? decoded)
: decoded;
if (data is List) {
try {
if (data.isNotEmpty) {
// ignore: avoid_print
// print('[DELIVERIES][GET_PICKED] Data: ${json.encode(data)}'); // Heavy log, disabled
} else {
// ignore: avoid_print
// print('[DELIVERIES][GET_PICKED] Data: []');
}
} catch (_) {}
return data;
}
if (data is Map && data['items'] is List) return data['items'] as List;
return [];
}
throw Exception('Failed (${res.statusCode})');
}
}