updated the sheet

This commit is contained in:
2026-08-28 18:16:28 +05:30
parent 69f4f3e909
commit 074cc0eccf
43 changed files with 2617 additions and 1201 deletions

View File

@@ -1,7 +1,6 @@
import 'package:get/get.dart';
import 'package:flutter/foundation.dart';
import 'dart:convert';
import 'package:miler/xpress/views/helpers/constants/apiconstants.dart';
import 'package:miler/xpress/Models/riders/riders_models.dart';
import 'package:miler/xpress/providers/Riderlog/riderlog_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -15,6 +14,7 @@ import 'package:miler/xpress/controllers/logcontroller.dart';
import 'package:miler/xpress/background/foreground_service.dart' as fg;
import 'package:miler/xpress/utils/kalman_filter.dart';
import 'package:miler/xpress/utils/mqtt_service.dart';
import 'package:miler/data/heartbeat.dart';
import 'package:miler/xpress/views/helpers/constants/mqtt_constants.dart';
import 'package:battery_plus/battery_plus.dart';
@@ -40,11 +40,9 @@ class RiderLogController extends GetxController {
Future<void> fetchRiderLogs({required String userId}) async {
isLoading.value = true;
try {
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.getRiderLogLive
: ApiConstants.getRiderLogDev;
final url = "$base?userid=$userId";
final response = await _getProvider.getRiderLog(url);
// v1 answers duty for the *token's* rider — `userid` is neither sent
// nor needed. See [GetRiderLogProvider].
final response = await _getProvider.getRiderLog();
// Handle either {details: {...}} or {data: [...]} shapes
if (response == null) {
@@ -122,11 +120,7 @@ class RiderLogController extends GetxController {
}
Future<void> fetchRiderCount({required String userId}) async {
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.getRiderCountLive
: ApiConstants.getRiderCountDev;
final url = "$base?userid=$userId";
final response = await _getProvider.getRiderCount(url);
final response = await _getProvider.getRiderCount();
final dynamic cnt = response != null ? response['count'] : null;
riderCount.value = cnt is num
? cnt.toInt()
@@ -134,46 +128,30 @@ class RiderLogController extends GetxController {
}
Future<bool> createLogin(RiderLogin login) async {
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.createRiderLogLive
: ApiConstants.createRiderLogDev;
final ok = await _createProvider.createRiderLog(
base,
_CreateRiderLogRequestCompat.fromRiderLogin(login),
);
return ok != null && ok.isNotEmpty;
}
Future<bool> updateLog(RiderUpdate update) async {
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.updateRiderLogLive
: ApiConstants.updateRiderLogDev;
final ok = await _updateProvider.updateRiderLog(
base,
_UpdateRiderLogRequestCompat.fromRiderUpdate(update),
);
return ok != null && ok.isNotEmpty;
}
Future<bool> createBreak(RiderBreak brk) async {
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.createBreakRiderLogLive
: ApiConstants.createBreakRiderLogDev;
final ok = await _breakProvider.createBreakRiderLog(
base,
_BreakLogRequestCompat.fromRiderBreak(brk),
);
return ok != null && ok.isNotEmpty;
}
Future<bool> updateBreak(RiderBreak brk) async {
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.updateBreakRiderLogLive
: ApiConstants.updateBreakRiderLogDev;
final ok = await _breakProvider.updateBreakRiderLog(
base,
_BreakLogRequestCompat.fromRiderBreak(brk),
);
// `PUT /miler/breaks/end` takes no body and closes whichever break is
// open, which the server knows and the app was only guessing at.
final ok = await _breakProvider.updateBreakRiderLog();
return ok != null && ok.isNotEmpty;
}
@@ -190,10 +168,6 @@ class RiderLogController extends GetxController {
required String latitude,
required String longitude,
}) async {
final url = ApiConstants.mainRoute == 'live'
? ApiConstants.updateBreakRiderLogLive
: ApiConstants.updateBreakRiderLogDev;
final payload = {
"breakid": breakid,
"logid": logid,
@@ -209,11 +183,9 @@ class RiderLogController extends GetxController {
};
try {
// Debug: show exact URL and JSON body
debugPrint('[BREAK][UPDATE] URL: $url');
debugPrint('[BREAK][UPDATE] Body: $payload');
final response = await _breakProvider.updateBreakRiderLog(url, payload);
final response = await _breakProvider.updateBreakRiderLog();
// Debug: show raw response
debugPrint(
@@ -375,13 +347,9 @@ class RiderLogController extends GetxController {
payload.remove('lastname');
}
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.createRiderLogLive
: ApiConstants.createRiderLogDev;
debugPrint('[RIDERLOG][CREATE LOGIN NOW] URL: $base');
debugPrint('[RIDERLOG][CREATE LOGIN NOW] Payload: $payload');
final resp = await _createProvider.createRiderLog(base, payload);
final resp = await _createProvider.createRiderLog(payload);
debugPrint(
'[RIDERLOG][CREATE LOGIN NOW] Response: ${resp == null ? 'null' : resp.toString()}',
);
@@ -391,7 +359,7 @@ class RiderLogController extends GetxController {
debugPrint(
'[RIDERLOG][CREATE LOGIN NOW] Failed, saving to offline queue',
);
await _saveToOfflineQueue(base, payload);
await _saveToOfflineQueue('/miler/logs', payload);
return false;
}
@@ -500,11 +468,7 @@ class RiderLogController extends GetxController {
payload.remove('lastname');
}
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.createRiderLogLive
: ApiConstants.createRiderLogDev;
await _saveToOfflineQueue(base, payload);
await _saveToOfflineQueue('/miler/logs', payload);
}
} catch (e) {
debugPrint('[RIDERLOG] Error saving offline log: $e');
@@ -532,10 +496,15 @@ class RiderLogController extends GetxController {
await prefs.setInt('onduty', on ? 1 : 0);
if (on) {
await createLoginNowV2();
final int interval = prefs.getInt('logseconds') ?? 0;
if (interval > 0) {
startAutoCreateLoginLoop(seconds: interval);
}
// ── Going on duty always starts the heartbeat ──
//
// Gated on `interval > 0`, this read a stored zero as a decision not
// to beat. The v1 login has no `logseconds` to store, so the gate was
// shut on every rider: duty went on, one log was posted by the call
// above, and nothing followed it. Mirrors the logistics controller —
// see [resolveLogSeconds].
final int interval = resolveLogSeconds(prefs.getInt('logseconds'));
startAutoCreateLoginLoop(seconds: interval);
// Ensure foreground logging notification is started when going on-duty
try {
@@ -604,16 +573,43 @@ class RiderLogController extends GetxController {
}
}
/// Whether the periodic heartbeat is currently running on the stream path.
///
/// ── Why a test needs to be able to see this ──
///
/// The `logseconds` bug was invisible from outside the class. Duty went on,
/// the API accepted it, one log was posted by the immediate call — and the
/// loop that was supposed to follow it simply never started. Nothing threw,
/// nothing logged an error, and the only symptom was a console panel that had
/// been blank for so long it read as a backend problem.
///
/// Android normally runs the heartbeat as a foreground service instead, and
/// leaves this null; the test host is never Android, so the stream path is
/// the one under test. See [startAutoCreateLoginLoop].
@visibleForTesting
bool get autoLoopRunning => _autoLoginSubscription != null;
// Start periodic createRiderLog calls based on seconds (or prefs 'logseconds')
// ✅ CRITICAL: When there are active deliveries, use 30 seconds (same as delivery logs)
// Otherwise, use the configured logseconds interval
void startAutoCreateLoginLoop({int? seconds}) async {
/// `Future<void>`, not `void`. It was fire-and-forget, which meant no caller
/// could wait for the loop to actually be up — and the one place that most
/// wanted to, [setOnDuty], had no way to tell whether the heartbeat it had
/// just asked for existed. Every existing call site ignores the future and is
/// unchanged by this.
Future<void> startAutoCreateLoginLoop({int? seconds}) async {
final prefs = await SharedPreferences.getInstance();
// ✅ Check if there are active deliveries - if yes, use 30 seconds (same as delivery logs)
final bool hasActiveDeliveries =
prefs.getBool('has_live_deliveries') ?? false;
final int baseInterval = seconds ?? (prefs.getInt('logseconds') ?? 0);
// The configured cadence, or the floor under it. `?? 0` here was the
// second half of the same bug as the one in [setOnDuty]: a caller passing
// no `seconds` fell through to a stored zero and returned at the
// `interval <= 0` guard below. See [resolveLogSeconds].
final int baseInterval = resolveLogSeconds(
seconds ?? prefs.getInt('logseconds'),
);
// When there are active deliveries, post rider logs every 30 seconds (matching delivery logs)
// Otherwise, use the configured interval
@@ -625,12 +621,18 @@ class RiderLogController extends GetxController {
);
}
await stopAutoCreateLoginLoop(); // Cancel existing subscription
// Stop before start, which is what keeps this method safe to call from
// every place that does. Whichever call arrives last wins, and there is
// never a second loop: the subscription is cancelled and the foreground
// service stopped before either is created again.
await stopAutoCreateLoginLoop();
// Attempt to flush offline logs on loop start
flushOfflineLogs();
if (interval <= 0) return;
// No `interval <= 0` guard. [resolveLogSeconds] cannot return one, and the
// guard that used to be here is what turned a missing `logseconds` into a
// rider who never reported.
final int onduty = prefs.getInt('onduty') ?? 0;
if (onduty != 1) {
debugPrint('[RIDERLOG][AUTO LOOP] Not starting - onduty=$onduty');
@@ -921,10 +923,6 @@ class RiderLogController extends GetxController {
final breakdate = _formatDateTimeFull(now); // e.g. 2025-10-16 16:36:16
final breakstart = _formatTime(now); // e.g. 16:36:16
final url = ApiConstants.mainRoute == 'live'
? ApiConstants.createBreakRiderLogLive
: ApiConstants.createBreakRiderLogDev;
// Build payload with all required fields in the exact format the API expects
final payload = <String, dynamic>{
"breakid": localBreakId,
@@ -941,13 +939,12 @@ class RiderLogController extends GetxController {
};
// Debug logs for terminal visibility
debugPrint('[BREAK][CREATE] URL: $url');
debugPrint('[BREAK][CREATE] Payload: $payload');
// Persist local break id immediately to ensure update can reference it
await prefs.setInt('breakId', localBreakId);
final resp = await _breakProvider.createBreakRiderLog(url, payload);
final resp = await _breakProvider.createBreakRiderLog(payload);
debugPrint(
'[BREAK][CREATE] Response: ${resp == null ? 'null' : resp.toString()}',
@@ -1045,9 +1042,6 @@ class RiderLogController extends GetxController {
final breakhoursDouble = duration.inSeconds / 3600.0;
// Debug logs for terminal visibility
debugPrint(
'[BREAK][UPDATE] URL: ${ApiConstants.mainRoute == 'live' ? ApiConstants.updateBreakRiderLogLive : ApiConstants.updateBreakRiderLogDev}',
);
debugPrint(
'[BREAK][UPDATE] Fields: breakid=$breakid, logid=$logid, userid=$userid, partnerid=$partnerid, shiftid=$shiftid, breakend=$breakend, breakhours=$breakhoursDouble, lat=$latitude, lng=$longitude',
);
@@ -1101,10 +1095,6 @@ class RiderLogController extends GetxController {
final iso = _formatDateTimeFull(now);
final loginTime = _formatTime(now);
final url = ApiConstants.mainRoute == 'live'
? ApiConstants.createRiderLogLive
: ApiConstants.createRiderLogDev;
// ✅ Check if there are active deliveries to set status
final bool hasActiveDeliveries =
prefs.getBool('has_live_deliveries') ?? false;
@@ -1141,10 +1131,9 @@ class RiderLogController extends GetxController {
userfcmtoken: userfcmtoken ?? '',
).toJson();
debugPrint('[RIDERLOG][CREATE LOGIN] URL: $url');
debugPrint('[RIDERLOG][CREATE LOGIN] Payload: $payload');
final resp = await _createProvider.createRiderLog(url, payload);
final resp = await _createProvider.createRiderLog(payload);
debugPrint(
'[RIDERLOG][CREATE LOGIN] Response: ${resp == null ? 'null' : resp.toString()}',
);
@@ -1205,7 +1194,7 @@ class RiderLogController extends GetxController {
// We can use _createProvider generic call
debugPrint('[RIDERLOG][OFFLINE] Retrying: $payload');
final resp = await _createProvider.createRiderLog(url, payload);
final resp = await _createProvider.createRiderLog(payload);
if (resp != null && resp.isNotEmpty) {
debugPrint('[RIDERLOG][OFFLINE] Success!');