207 lines
6.3 KiB
Dart
207 lines
6.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../models/stop.dart';
|
|
import '../models/delivery_history.dart';
|
|
import '../services/database.dart';
|
|
|
|
class DeliveryProvider extends ChangeNotifier {
|
|
final DatabaseService _db = DatabaseService();
|
|
|
|
List<Stop> _stops = [];
|
|
Set<int> _todayDeliveredIds = {};
|
|
bool _isLoading = false;
|
|
String? _error;
|
|
int? _currentRouteId;
|
|
int _navigateIndex = 0;
|
|
bool _navigateMode = false;
|
|
|
|
List<Stop> get stops => _stops;
|
|
bool get isLoading => _isLoading;
|
|
String? get error => _error;
|
|
bool get navigateMode => _navigateMode;
|
|
Set<int> get todayDeliveredIds => _todayDeliveredIds;
|
|
int get navigateIndex => _navigateIndex;
|
|
|
|
/// Stops filtered to show delivered status based on TODAY's history.
|
|
List<Stop> get stopsWithDeliveryStatus {
|
|
return _stops.map((s) {
|
|
final isDeliveredToday = _todayDeliveredIds.contains(s.id);
|
|
return s.copyWith(delivered: isDeliveredToday);
|
|
}).toList();
|
|
}
|
|
|
|
int get deliveredCount => _todayDeliveredIds.length;
|
|
int get totalStops => _stops.length;
|
|
double get progressPercent =>
|
|
_stops.isEmpty ? 0.0 : _todayDeliveredIds.length / _stops.length;
|
|
|
|
/// Newspaper counts breakdown
|
|
Map<String, int> get newspaperCounts {
|
|
final counts = <String, int>{};
|
|
for (final stop in _stops) {
|
|
for (final np in stop.newspapers) {
|
|
counts[np] = (counts[np] ?? 0) + 1;
|
|
}
|
|
}
|
|
return counts;
|
|
}
|
|
|
|
int get totalNewspapers {
|
|
int total = 0;
|
|
for (final stop in _stops) {
|
|
total += stop.newspapers.length;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
/// Current stop in navigation mode
|
|
Stop? get currentNavigateStop {
|
|
if (!_navigateMode || _stops.isEmpty) return null;
|
|
if (_navigateIndex >= _stops.length) return null;
|
|
return stopsWithDeliveryStatus[_navigateIndex];
|
|
}
|
|
|
|
/// Get the next undelivered stop index for navigation
|
|
int get nextUndeliveredIndex {
|
|
final orderedStops = stopsWithDeliveryStatus;
|
|
for (var i = 0; i < orderedStops.length; i++) {
|
|
if (!orderedStops[i].delivered) return i;
|
|
}
|
|
return -1; // all delivered
|
|
}
|
|
|
|
// ── Load ────────────────────────────────────────────────
|
|
|
|
Future<void> loadStops(int routeId) async {
|
|
_currentRouteId = routeId;
|
|
_isLoading = true;
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
try {
|
|
_stops = await _db.getStops(routeId);
|
|
_todayDeliveredIds = await _db.getTodayDeliveredStopIds(routeId);
|
|
_isLoading = false;
|
|
_error = null;
|
|
} catch (e) {
|
|
_error = 'Failed to load stops: $e';
|
|
_isLoading = false;
|
|
}
|
|
notifyListeners();
|
|
}
|
|
|
|
// ── Toggle Delivery ─────────────────────────────────────
|
|
|
|
Future<void> toggleDelivered(int stopId) async {
|
|
if (_currentRouteId == null) return;
|
|
|
|
final isDelivered = _todayDeliveredIds.contains(stopId);
|
|
if (isDelivered) {
|
|
await _db.removeDeliveryRecord(_currentRouteId!, stopId);
|
|
_todayDeliveredIds.remove(stopId);
|
|
} else {
|
|
await _db.recordDelivery(_currentRouteId!, stopId);
|
|
_todayDeliveredIds.add(stopId);
|
|
}
|
|
notifyListeners();
|
|
}
|
|
|
|
// ── Reset Today ─────────────────────────────────────────
|
|
|
|
Future<void> resetToday() async {
|
|
if (_currentRouteId == null) return;
|
|
await _db.resetToday(_currentRouteId!);
|
|
_todayDeliveredIds.clear();
|
|
notifyListeners();
|
|
}
|
|
|
|
// ── Navigation Mode ─────────────────────────────────────
|
|
|
|
void startNavigation() {
|
|
_navigateMode = true;
|
|
_navigateIndex = nextUndeliveredIndex >= 0 ? nextUndeliveredIndex : 0;
|
|
notifyListeners();
|
|
}
|
|
|
|
void stopNavigation() {
|
|
_navigateMode = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
void nextStop() {
|
|
if (_navigateIndex < _stops.length - 1) {
|
|
_navigateIndex++;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
void previousStop() {
|
|
if (_navigateIndex > 0) {
|
|
_navigateIndex--;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
void goToStop(int index) {
|
|
if (index >= 0 && index < _stops.length) {
|
|
_navigateIndex = index;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
// ── Statistics ──────────────────────────────────────────
|
|
|
|
Future<List<DailyStats>> getStats({int days = 30}) async {
|
|
if (_currentRouteId == null) return [];
|
|
return _db.getDeliveryStats(_currentRouteId!, days: days);
|
|
}
|
|
|
|
Future<int> get totalDeliveries async {
|
|
if (_currentRouteId == null) return 0;
|
|
return _db.getTotalDeliveries(_currentRouteId!);
|
|
}
|
|
|
|
Future<int> get weeklyDeliveries async {
|
|
if (_currentRouteId == null) return 0;
|
|
return _db.getWeeklyDeliveryCount(_currentRouteId!);
|
|
}
|
|
|
|
// ── Notes ───────────────────────────────────────────────
|
|
|
|
Future<void> saveNotes(int stopId, String notes) async {
|
|
await _db.updateStopNotes(stopId, notes);
|
|
final idx = _stops.indexWhere((s) => s.id == stopId);
|
|
if (idx >= 0) {
|
|
_stops[idx] = _stops[idx].copyWith(notes: notes);
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
// ── Delete Stop ─────────────────────────────────────────
|
|
|
|
Future<void> deleteStop(int stopId) async {
|
|
await _db.deleteStop(stopId);
|
|
_stops.removeWhere((s) => s.id == stopId);
|
|
_todayDeliveredIds.remove(stopId);
|
|
notifyListeners();
|
|
}
|
|
|
|
// ── Reorder ─────────────────────────────────────────────
|
|
|
|
Future<void> reorderStops(List<Stop> newOrder) async {
|
|
await _db.reorderStops(newOrder);
|
|
_stops = newOrder;
|
|
notifyListeners();
|
|
}
|
|
|
|
// ── Update Coordinates ──────────────────────────────────
|
|
|
|
Future<void> updateCoords(int stopId, double lat, double lng) async {
|
|
await _db.updateStopCoords(stopId, lat, lng);
|
|
final idx = _stops.indexWhere((s) => s.id == stopId);
|
|
if (idx >= 0) {
|
|
_stops[idx] = _stops[idx].copyWith(lat: lat, lng: lng);
|
|
notifyListeners();
|
|
}
|
|
}
|
|
}
|