delivery_app/lib/providers/delivery_provider.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();
}
}
}