feat: standard error handling, web bug fixes, and mock data setup

This commit is contained in:
2026-06-26 16:06:18 +05:30
parent 401cf600e8
commit 523609796e
18 changed files with 792 additions and 164 deletions

View File

@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:geolocator/geolocator.dart';
@@ -14,10 +15,13 @@ import 'package:doormile/core/theme/app_colors.dart';
/// Holds the in-progress booking the user is composing across the flow screens.
class BookingDraft {
String pickupAddress = '';
String pickupPincode = '';
double pickupLatitude = 0.0;
double pickupLongitude = 0.0;
String contactName = '';
String contactMobile = '';
String notes = '';
String destination = '';
String service = '';
String parcelType = '';
@@ -37,6 +41,9 @@ class BookingDraft {
void reset() {
pickupAddress = '';
pickupPincode = '';
pickupLatitude = 0.0;
pickupLongitude = 0.0;
contactName = '';
contactMobile = '';
notes = '';
@@ -71,6 +78,8 @@ class AppState extends ChangeNotifier {
String? pendingEmail;
String? authToken;
bool isResettingPin = false;
bool isLoginFlow = false;
String? _deviceToken;
AppState() {
_loadSession();
@@ -101,6 +110,18 @@ class AppState extends ChangeNotifier {
await prefs.setString('phone', phone);
}
Future<String?> _fetchDeviceToken() async {
try {
if (kIsWeb) return null;
await FirebaseMessaging.instance.requestPermission(alert: true, badge: true, sound: true);
_deviceToken = await FirebaseMessaging.instance.getToken();
return _deviceToken;
} catch (e) {
debugPrint('FCM token error: $e');
return null;
}
}
void setPhone(String value) {
phone = value;
notifyListeners();
@@ -195,7 +216,7 @@ class AppState extends ChangeNotifier {
double lat = 0.0;
double lng = 0.0;
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (serviceEnabled) {
@@ -212,7 +233,9 @@ class AppState extends ChangeNotifier {
} catch (e) {
debugPrint('Error getting location for registration: $e');
}
final token = await _fetchDeviceToken();
final res = await http.post(
Uri.parse(ApiConstants.register),
headers: {'Content-Type': 'application/json'},
@@ -224,7 +247,8 @@ class AppState extends ChangeNotifier {
"pin": pin,
"configid": 1001,
"defaultlatitude": lat,
"defaultlongitude": lng
"defaultlongitude": lng,
if (token != null) "device_token": token,
}),
);
if (res.statusCode >= 200 && res.statusCode < 300) {
@@ -277,10 +301,17 @@ class AppState extends ChangeNotifier {
return;
}
final token = await _fetchDeviceToken();
final verifyRes = await http.post(
Uri.parse(ApiConstants.verifyPin),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({"phone": rawPhone, "pin": pin, "configid": 1001}),
body: jsonEncode({
"phone": rawPhone,
"pin": pin,
"configid": 1001,
if (token != null) "device_token": token,
}),
);
if (verifyRes.statusCode >= 200 && verifyRes.statusCode < 300) {
@@ -316,8 +347,8 @@ class AppState extends ChangeNotifier {
void login() {
isLoggedIn = true;
// Wipe hardcoded mock data so real users only see their own actual data!
orders.clear();
isLoginFlow = false;
orders.clear();
savedAddresses.clear();
_saveSession();
notifyListeners();
@@ -326,13 +357,13 @@ class AppState extends ChangeNotifier {
Future<void> logout() async {
isLoggedIn = false;
authToken = null;
// Reset in-memory user details so the previous account does not linger.
firstName = 'User';
userName = 'User';
phone = '';
verificationId = null;
pendingFirstName = null;
pendingEmail = null;
isLoginFlow = false;
orders.clear();
savedAddresses.clear();
notifyListeners();
@@ -360,6 +391,27 @@ class AppState extends ChangeNotifier {
isLoadingOrders = true;
notifyListeners();
if (authToken == 'easter-egg-token') {
await Future.delayed(const Duration(milliseconds: 500));
orders = [
Order(
id: 'DM-TEST-001',
fromLabel: 'Home (Mock)',
fromSub: 'Pickup',
toLabel: 'Pincode: 110001',
toSub: 'Delivery',
date: 'Today',
amount: 65.0,
status: OrderStatus.active,
progress: 0.1,
eta: '10 min',
),
];
isLoadingOrders = false;
notifyListeners();
return;
}
try {
print('\n=== DOORMILE API FETCH ORDERS ===');
print('Endpoint: ${ApiConstants.getPickups}');
@@ -425,6 +477,7 @@ class AppState extends ChangeNotifier {
final payload = {
"zone": "Local",
"destinationpin": draft.dropPincode,
"pickuppincode": draft.pickupPincode,
"service_type": draft.service.isNotEmpty ? draft.service : "Normal",
"weight": 1.0, // Default since we removed it from UI
"category": backendCategory
@@ -435,6 +488,14 @@ class AppState extends ChangeNotifier {
print('Payload: ${jsonEncode(payload)}');
print('====================================\n');
if (authToken == 'easter-egg-token') {
await Future.delayed(const Duration(milliseconds: 500));
draft.minPrice = 50.0;
draft.maxPrice = 120.0;
notifyListeners();
return;
}
try {
final res = await http.post(
Uri.parse(ApiConstants.checkPrice),
@@ -480,8 +541,12 @@ class AppState extends ChangeNotifier {
final payload = {
"pickupaddress": draft.pickupAddress.isNotEmpty ? draft.pickupAddress : "Current Location (GPS)",
"pickuppincode": "560001", // Default pincode until we parse from GPS
"pickuppincode": draft.pickupPincode,
"pickuplatitude": draft.pickupLatitude,
"pickuplongitude": draft.pickupLongitude,
"deliverypincode": draft.dropPincode,
"deliveryaddress": draft.dropAddress,
"serviceoption": draft.service,
"parcels": [
{
"itemcategory": backendCategory,
@@ -495,6 +560,27 @@ class AppState extends ChangeNotifier {
print('Payload: ${jsonEncode(payload)}');
print('====================================\n');
if (authToken == 'easter-egg-token') {
await Future.delayed(const Duration(seconds: 1));
final newOrder = Order(
id: 'DM-PU-000${101 + orders.length}',
fromLabel: draft.pickupAddress,
fromSub: draft.contactName.isEmpty ? 'Pickup requested' : 'Contact: ${draft.contactName}',
toLabel: draft.dropPincode.isNotEmpty ? 'Pincode: ${draft.dropPincode}' : 'Awaiting Miler input',
toSub: 'Drop details pending',
date: 'Today',
amount: draft.minPrice,
status: OrderStatus.active,
progress: 0.1,
eta: 'Assigning Miler...',
);
orders.insert(0, newOrder);
draft.reset();
notifyListeners();
return newOrder;
}
final res = await http.post(
Uri.parse(ApiConstants.createPickup),
headers: {
@@ -542,6 +628,7 @@ class AppState extends ChangeNotifier {
icon: Icons.home_rounded,
iconBg: AppColors.primaryFixed,
iconColor: AppColors.primary,
pincode: '122003',
),
AddressEntry(
label: 'Work',
@@ -549,6 +636,7 @@ class AppState extends ChangeNotifier {
icon: Icons.work_rounded,
iconBg: AppColors.tertiaryFixed,
iconColor: AppColors.tertiary,
pincode: '122002',
),
AddressEntry(
label: 'Select City Walk Mall',
@@ -556,6 +644,7 @@ class AppState extends ChangeNotifier {
icon: Icons.history_rounded,
iconBg: AppColors.surfaceContainerHigh,
iconColor: AppColors.onSurfaceVariant,
pincode: '110017',
),
];