|
|
|
@@ -51,7 +51,8 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
String? _activeDraftId;
|
|
|
|
String? _activeDraftId;
|
|
|
|
|
|
|
|
|
|
|
|
bool get _isEditing => widget.editLegId != null;
|
|
|
|
bool get _isEditing => widget.editLegId != null;
|
|
|
|
bool get _draftPersistenceEnabled => false; // legacy single draft disabled in favor of draft list
|
|
|
|
bool get _draftPersistenceEnabled =>
|
|
|
|
|
|
|
|
false; // legacy single draft disabled in favor of draft list
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
void initState() {
|
|
|
|
@@ -266,8 +267,8 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
if (json is! Map<String, dynamic>) {
|
|
|
|
if (json is! Map<String, dynamic>) {
|
|
|
|
throw Exception('Unexpected response for leg $legId');
|
|
|
|
throw Exception('Unexpected response for leg $legId');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
final beginTime = DateTime.tryParse(json['leg_begin_time'] ?? '') ??
|
|
|
|
final beginTime =
|
|
|
|
_selectedDate;
|
|
|
|
DateTime.tryParse(json['leg_begin_time'] ?? '') ?? _selectedDate;
|
|
|
|
final routeStations = _parseRouteStations(json['leg_route']);
|
|
|
|
final routeStations = _parseRouteStations(json['leg_route']);
|
|
|
|
final mileageVal = (json['leg_mileage'] as num?)?.toDouble() ?? 0.0;
|
|
|
|
final mileageVal = (json['leg_mileage'] as num?)?.toDouble() ?? 0.0;
|
|
|
|
final useManual = routeStations.isEmpty;
|
|
|
|
final useManual = routeStations.isEmpty;
|
|
|
|
@@ -297,13 +298,14 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
_routeResult = routeResult;
|
|
|
|
_routeResult = routeResult;
|
|
|
|
_startController.text = json['leg_start'] ?? '';
|
|
|
|
_startController.text = json['leg_start'] ?? '';
|
|
|
|
_endController.text = json['leg_end'] ?? '';
|
|
|
|
_endController.text = json['leg_end'] ?? '';
|
|
|
|
_headcodeController.text =
|
|
|
|
_headcodeController.text = (json['leg_headcode'] as String? ?? '')
|
|
|
|
(json['leg_headcode'] as String? ?? '').toUpperCase();
|
|
|
|
.toUpperCase();
|
|
|
|
_notesController.text = json['leg_notes'] ?? '';
|
|
|
|
_notesController.text = json['leg_notes'] ?? '';
|
|
|
|
_networkController.text =
|
|
|
|
_networkController.text = (json['leg_network'] as String? ?? '')
|
|
|
|
(json['leg_network'] as String? ?? '').toUpperCase();
|
|
|
|
.toUpperCase();
|
|
|
|
_mileageController.text =
|
|
|
|
_mileageController.text = mileageVal == 0
|
|
|
|
mileageVal == 0 ? '' : mileageVal.toStringAsFixed(2);
|
|
|
|
? ''
|
|
|
|
|
|
|
|
: mileageVal.toStringAsFixed(2);
|
|
|
|
_tractionItems
|
|
|
|
_tractionItems
|
|
|
|
..clear()
|
|
|
|
..clear()
|
|
|
|
..addAll(tractionItems);
|
|
|
|
..addAll(tractionItems);
|
|
|
|
@@ -317,9 +319,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
setState(() {
|
|
|
|
setState(() {
|
|
|
|
_loadError = 'Failed to load entry: $e';
|
|
|
|
_loadError = 'Failed to load entry: $e';
|
|
|
|
});
|
|
|
|
});
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
ScaffoldMessenger.of(
|
|
|
|
SnackBar(content: Text('Failed to load entry: $e')),
|
|
|
|
context,
|
|
|
|
);
|
|
|
|
).showSnackBar(SnackBar(content: Text('Failed to load entry: $e')));
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
_restoringDraft = false;
|
|
|
|
_restoringDraft = false;
|
|
|
|
if (mounted) {
|
|
|
|
if (mounted) {
|
|
|
|
@@ -394,10 +396,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
_TractionItem _mapLocoToTractionItem(Map<String, dynamic> loco) {
|
|
|
|
_TractionItem _mapLocoToTractionItem(Map<String, dynamic> loco) {
|
|
|
|
final poweringRaw = loco['alloc_powering'];
|
|
|
|
final poweringRaw = loco['alloc_powering'];
|
|
|
|
final powering = poweringRaw == true || poweringRaw == 1;
|
|
|
|
final powering = poweringRaw == true || poweringRaw == 1;
|
|
|
|
return _TractionItem(
|
|
|
|
return _TractionItem(loco: LocoSummary.fromJson(loco), powering: powering);
|
|
|
|
loco: LocoSummary.fromJson(loco),
|
|
|
|
|
|
|
|
powering: powering,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DateTime get _legDateTime => DateTime(
|
|
|
|
DateTime get _legDateTime => DateTime(
|
|
|
|
@@ -452,20 +451,11 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
|
|
|
|
|
|
|
|
bool _draftChangedFromBaseline() {
|
|
|
|
bool _draftChangedFromBaseline() {
|
|
|
|
if (_loadedDraftSnapshot == null) return true;
|
|
|
|
if (_loadedDraftSnapshot == null) return true;
|
|
|
|
final current = _normalizeDraftSnapshot(
|
|
|
|
final current = _buildDraftSnapshot(
|
|
|
|
_buildDraftSnapshot(
|
|
|
|
|
|
|
|
id: _activeDraftId ?? 'temp',
|
|
|
|
id: _activeDraftId ?? 'temp',
|
|
|
|
includeTimestamp: false,
|
|
|
|
includeTimestamp: false,
|
|
|
|
),
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
final baseline = _normalizeDraftSnapshot(_loadedDraftSnapshot!);
|
|
|
|
return !_snapshotEquality.equals(_loadedDraftSnapshot, current);
|
|
|
|
return !_snapshotEquality.equals(baseline, current);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> _normalizeDraftSnapshot(Map<String, dynamic> snapshot) {
|
|
|
|
|
|
|
|
final normalized = Map<String, dynamic>.from(snapshot);
|
|
|
|
|
|
|
|
normalized.remove('saved_at');
|
|
|
|
|
|
|
|
return normalized;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool _formIsEmpty() {
|
|
|
|
bool _formIsEmpty() {
|
|
|
|
@@ -487,8 +477,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
useRootNavigator: false,
|
|
|
|
useRootNavigator: false,
|
|
|
|
builder: (_) => AlertDialog(
|
|
|
|
builder: (_) => AlertDialog(
|
|
|
|
title: const Text('Save draft?'),
|
|
|
|
title: const Text('Save draft?'),
|
|
|
|
content:
|
|
|
|
content: const Text(
|
|
|
|
const Text('Do you want to save this entry as a draft before leaving?'),
|
|
|
|
'Do you want to save this entry as a draft before leaving?',
|
|
|
|
|
|
|
|
),
|
|
|
|
actions: [
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
TextButton(
|
|
|
|
onPressed: () => Navigator.of(context).pop(_ExitChoice.discard),
|
|
|
|
onPressed: () => Navigator.of(context).pop(_ExitChoice.discard),
|
|
|
|
@@ -637,11 +628,11 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
if (!mounted) return;
|
|
|
|
if (!mounted) return;
|
|
|
|
context.read<DataService>().refreshLegs();
|
|
|
|
context.read<DataService>().refreshLegs();
|
|
|
|
if (!mounted) return;
|
|
|
|
if (!mounted) return;
|
|
|
|
ScaffoldMessenger.of(
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
context,
|
|
|
|
|
|
|
|
).showSnackBar(
|
|
|
|
|
|
|
|
SnackBar(
|
|
|
|
SnackBar(
|
|
|
|
content: Text(isEditingExisting ? 'Entry updated' : 'Entry submitted'),
|
|
|
|
content: Text(
|
|
|
|
|
|
|
|
isEditingExisting ? 'Entry updated' : 'Entry submitted',
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
_lastSubmittedSnapshot = snapshot;
|
|
|
|
_lastSubmittedSnapshot = snapshot;
|
|
|
|
@@ -816,8 +807,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
jsonEncode(drafts.map((e) => e.toJson()).toList()),
|
|
|
|
jsonEncode(drafts.map((e) => e.toJson()).toList()),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
_activeDraftId = id;
|
|
|
|
_activeDraftId = id;
|
|
|
|
_loadedDraftSnapshot =
|
|
|
|
_loadedDraftSnapshot = _buildDraftSnapshot(id: id, includeTimestamp: false);
|
|
|
|
_normalizeDraftSnapshot(_buildDraftSnapshot(id: id, includeTimestamp: false));
|
|
|
|
|
|
|
|
return id;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -882,11 +872,14 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
if (payloadRaw is! Map) return;
|
|
|
|
if (payloadRaw is! Map) return;
|
|
|
|
final payload = Map<String, dynamic>.from(payloadRaw);
|
|
|
|
final payload = Map<String, dynamic>.from(payloadRaw);
|
|
|
|
final mode = data['mode'] as String?;
|
|
|
|
final mode = data['mode'] as String?;
|
|
|
|
final useManual = mode == 'manual' ||
|
|
|
|
final useManual =
|
|
|
|
(payload.containsKey('leg_distance') && !payload.containsKey('leg_route'));
|
|
|
|
mode == 'manual' ||
|
|
|
|
|
|
|
|
(payload.containsKey('leg_distance') &&
|
|
|
|
|
|
|
|
!payload.containsKey('leg_route'));
|
|
|
|
final beginStr = payload['leg_begin_time'] as String?;
|
|
|
|
final beginStr = payload['leg_begin_time'] as String?;
|
|
|
|
final beginTime =
|
|
|
|
final beginTime = beginStr == null
|
|
|
|
beginStr == null ? DateTime.now() : DateTime.tryParse(beginStr) ?? DateTime.now();
|
|
|
|
? DateTime.now()
|
|
|
|
|
|
|
|
: DateTime.tryParse(beginStr) ?? DateTime.now();
|
|
|
|
final tripRaw = payload['leg_trip'];
|
|
|
|
final tripRaw = payload['leg_trip'];
|
|
|
|
final tripId = tripRaw is num ? tripRaw.toInt() : null;
|
|
|
|
final tripId = tripRaw is num ? tripRaw.toInt() : null;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -894,7 +887,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
RouteResult? restoredRouteResult;
|
|
|
|
RouteResult? restoredRouteResult;
|
|
|
|
if (!useManual) {
|
|
|
|
if (!useManual) {
|
|
|
|
if (payload['leg_route'] is List) {
|
|
|
|
if (payload['leg_route'] is List) {
|
|
|
|
routeStations = (payload['leg_route'] as List).map((e) => e.toString()).toList();
|
|
|
|
routeStations = (payload['leg_route'] as List)
|
|
|
|
|
|
|
|
.map((e) => e.toString())
|
|
|
|
|
|
|
|
.toList();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
final rr = data['routeResult'];
|
|
|
|
final rr = data['routeResult'];
|
|
|
|
if (rr is Map<String, dynamic>) {
|
|
|
|
if (rr is Map<String, dynamic>) {
|
|
|
|
@@ -903,10 +898,17 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
(rr['input_route'] as List?)?.map((e) => e.toString()).toList() ??
|
|
|
|
(rr['input_route'] as List?)?.map((e) => e.toString()).toList() ??
|
|
|
|
routeStations,
|
|
|
|
routeStations,
|
|
|
|
calculatedRoute:
|
|
|
|
calculatedRoute:
|
|
|
|
(rr['calculated_route'] as List?)?.map((e) => e.toString()).toList() ??
|
|
|
|
(rr['calculated_route'] as List?)
|
|
|
|
|
|
|
|
?.map((e) => e.toString())
|
|
|
|
|
|
|
|
.toList() ??
|
|
|
|
routeStations,
|
|
|
|
routeStations,
|
|
|
|
costs: (rr['costs'] as List?)?.map((e) => (e as num).toDouble()).toList() ?? [],
|
|
|
|
costs:
|
|
|
|
distance: (rr['distance'] as num?)?.toDouble() ??
|
|
|
|
(rr['costs'] as List?)
|
|
|
|
|
|
|
|
?.map((e) => (e as num).toDouble())
|
|
|
|
|
|
|
|
.toList() ??
|
|
|
|
|
|
|
|
[],
|
|
|
|
|
|
|
|
distance:
|
|
|
|
|
|
|
|
(rr['distance'] as num?)?.toDouble() ??
|
|
|
|
(payload['leg_mileage'] as num?)?.toDouble() ??
|
|
|
|
(payload['leg_mileage'] as num?)?.toDouble() ??
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
@@ -927,21 +929,26 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
_selectedTime = TimeOfDay.fromDateTime(beginTime);
|
|
|
|
_selectedTime = TimeOfDay.fromDateTime(beginTime);
|
|
|
|
_selectedTripId = tripId == null || tripId == 0 ? null : tripId;
|
|
|
|
_selectedTripId = tripId == null || tripId == 0 ? null : tripId;
|
|
|
|
_routeResult = restoredRouteResult;
|
|
|
|
_routeResult = restoredRouteResult;
|
|
|
|
_headcodeController.text =
|
|
|
|
_headcodeController.text = (payload['leg_headcode'] as String? ?? '')
|
|
|
|
(payload['leg_headcode'] as String? ?? '').toUpperCase();
|
|
|
|
.toUpperCase();
|
|
|
|
_networkController.text =
|
|
|
|
_networkController.text = (payload['leg_network'] as String? ?? '')
|
|
|
|
(payload['leg_network'] as String? ?? '').toUpperCase();
|
|
|
|
.toUpperCase();
|
|
|
|
_notesController.text = payload['leg_notes'] ?? '';
|
|
|
|
_notesController.text = payload['leg_notes'] ?? '';
|
|
|
|
|
|
|
|
|
|
|
|
if (useManual) {
|
|
|
|
if (useManual) {
|
|
|
|
_startController.text = payload['leg_start'] ?? '';
|
|
|
|
_startController.text = payload['leg_start'] ?? '';
|
|
|
|
_endController.text = payload['leg_end'] ?? '';
|
|
|
|
_endController.text = payload['leg_end'] ?? '';
|
|
|
|
final miles = (payload['leg_distance'] as num?)?.toDouble();
|
|
|
|
final miles = (payload['leg_distance'] as num?)?.toDouble();
|
|
|
|
_mileageController.text =
|
|
|
|
_mileageController.text = miles == null || miles == 0
|
|
|
|
miles == null || miles == 0 ? '' : miles.toStringAsFixed(2);
|
|
|
|
? ''
|
|
|
|
|
|
|
|
: miles.toStringAsFixed(2);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
_startController.text = routeStations.isNotEmpty ? routeStations.first : '';
|
|
|
|
_startController.text = routeStations.isNotEmpty
|
|
|
|
_endController.text = routeStations.isNotEmpty ? routeStations.last : '';
|
|
|
|
? routeStations.first
|
|
|
|
|
|
|
|
: '';
|
|
|
|
|
|
|
|
_endController.text = routeStations.isNotEmpty
|
|
|
|
|
|
|
|
? routeStations.last
|
|
|
|
|
|
|
|
: '';
|
|
|
|
final dist = _routeResult?.distance ?? 0;
|
|
|
|
final dist = _routeResult?.distance ?? 0;
|
|
|
|
_mileageController.text = dist == 0 ? '' : dist.toStringAsFixed(2);
|
|
|
|
_mileageController.text = dist == 0 ? '' : dist.toStringAsFixed(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -964,11 +971,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
final baselineId =
|
|
|
|
final baselineId =
|
|
|
|
_activeDraftId ?? data['id']?.toString() ?? DateTime.now().toString();
|
|
|
|
_activeDraftId ?? data['id']?.toString() ?? DateTime.now().toString();
|
|
|
|
_loadedDraftSnapshot = _normalizeDraftSnapshot(
|
|
|
|
_loadedDraftSnapshot = _buildDraftSnapshot(
|
|
|
|
_buildDraftSnapshot(
|
|
|
|
|
|
|
|
id: baselineId,
|
|
|
|
id: baselineId,
|
|
|
|
includeTimestamp: false,
|
|
|
|
includeTimestamp: false,
|
|
|
|
),
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
_restoringDraft = false;
|
|
|
|
_restoringDraft = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1072,8 +1077,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
|
|
|
|
minimumSize: const Size(0, 36),
|
|
|
|
minimumSize: const Size(0, 36),
|
|
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
onPressed:
|
|
|
|
onPressed: _submitting
|
|
|
|
_submitting ? null : () => _resetFormState(clearDraft: true),
|
|
|
|
? null
|
|
|
|
|
|
|
|
: () => _resetFormState(clearDraft: true),
|
|
|
|
icon: const Icon(Icons.clear, size: 16),
|
|
|
|
icon: const Icon(Icons.clear, size: 16),
|
|
|
|
label: const Text('Clear form'),
|
|
|
|
label: const Text('Clear form'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
@@ -1444,18 +1450,15 @@ class _StoredDraft {
|
|
|
|
final DateTime savedAt;
|
|
|
|
final DateTime savedAt;
|
|
|
|
final Map<String, dynamic> data;
|
|
|
|
final Map<String, dynamic> data;
|
|
|
|
|
|
|
|
|
|
|
|
_StoredDraft({
|
|
|
|
_StoredDraft({required this.id, required this.savedAt, required this.data});
|
|
|
|
required this.id,
|
|
|
|
|
|
|
|
required this.savedAt,
|
|
|
|
|
|
|
|
required this.data,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
factory _StoredDraft.fromJson(Map<String, dynamic> json) {
|
|
|
|
factory _StoredDraft.fromJson(Map<String, dynamic> json) {
|
|
|
|
final savedAt = DateTime.tryParse(json['saved_at'] ?? '') ?? DateTime.now();
|
|
|
|
final savedAt = DateTime.tryParse(json['saved_at'] ?? '') ?? DateTime.now();
|
|
|
|
final data = Map<String, dynamic>.from(json['data'] as Map? ?? {});
|
|
|
|
final data = Map<String, dynamic>.from(json['data'] as Map? ?? {});
|
|
|
|
final embeddedId = data['id']?.toString();
|
|
|
|
final embeddedId = data['id']?.toString();
|
|
|
|
return _StoredDraft(
|
|
|
|
return _StoredDraft(
|
|
|
|
id: json['id']?.toString() ??
|
|
|
|
id:
|
|
|
|
|
|
|
|
json['id']?.toString() ??
|
|
|
|
embeddedId ??
|
|
|
|
embeddedId ??
|
|
|
|
savedAt.microsecondsSinceEpoch.toString(),
|
|
|
|
savedAt.microsecondsSinceEpoch.toString(),
|
|
|
|
savedAt: savedAt,
|
|
|
|
savedAt: savedAt,
|
|
|
|
@@ -1464,11 +1467,7 @@ class _StoredDraft {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> toJson() {
|
|
|
|
Map<String, dynamic> toJson() {
|
|
|
|
return {
|
|
|
|
return {"id": id, "saved_at": savedAt.toIso8601String(), "data": data};
|
|
|
|
"id": id,
|
|
|
|
|
|
|
|
"saved_at": savedAt.toIso8601String(),
|
|
|
|
|
|
|
|
"data": data,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1491,10 +1490,7 @@ class _DraftListPage extends StatelessWidget {
|
|
|
|
if (drafts.isEmpty) {
|
|
|
|
if (drafts.isEmpty) {
|
|
|
|
return const Center(child: Text('No drafts saved yet.'));
|
|
|
|
return const Center(child: Text('No drafts saved yet.'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _DraftListBody(
|
|
|
|
return _DraftListBody(drafts: drafts, onDelete: onDeleteDraft);
|
|
|
|
drafts: drafts,
|
|
|
|
|
|
|
|
onDelete: onDeleteDraft,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
@@ -1551,16 +1547,16 @@ class _DraftListBodyState extends State<_DraftListBody> {
|
|
|
|
Future<void> _confirmDelete(BuildContext context, _StoredDraft draft) async {
|
|
|
|
Future<void> _confirmDelete(BuildContext context, _StoredDraft draft) async {
|
|
|
|
final confirmed = await showDialog<bool>(
|
|
|
|
final confirmed = await showDialog<bool>(
|
|
|
|
context: context,
|
|
|
|
context: context,
|
|
|
|
builder: (_) => AlertDialog(
|
|
|
|
builder: (dialogCtx) => AlertDialog(
|
|
|
|
title: const Text('Delete draft?'),
|
|
|
|
title: const Text('Delete draft?'),
|
|
|
|
content: const Text('This draft will be removed permanently.'),
|
|
|
|
content: const Text('This draft will be removed permanently.'),
|
|
|
|
actions: [
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
TextButton(
|
|
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
|
|
onPressed: () => Navigator.of(dialogCtx).pop(false),
|
|
|
|
child: const Text('Cancel'),
|
|
|
|
child: const Text('Cancel'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
TextButton(
|
|
|
|
TextButton(
|
|
|
|
onPressed: () => Navigator.of(context).pop(true),
|
|
|
|
onPressed: () => Navigator.of(dialogCtx).pop(true),
|
|
|
|
child: const Text('Delete'),
|
|
|
|
child: const Text('Delete'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
@@ -1607,11 +1603,13 @@ class _DraftListBodyState extends State<_DraftListBody> {
|
|
|
|
if (network.isNotEmpty) parts.add('Network $network');
|
|
|
|
if (network.isNotEmpty) parts.add('Network $network');
|
|
|
|
final notes = (map['leg_notes'] as String? ?? '').trim();
|
|
|
|
final notes = (map['leg_notes'] as String? ?? '').trim();
|
|
|
|
if (notes.isNotEmpty) parts.add('Notes');
|
|
|
|
if (notes.isNotEmpty) parts.add('Notes');
|
|
|
|
final mileage = (map['leg_distance'] as num?)?.toDouble() ??
|
|
|
|
final mileage =
|
|
|
|
|
|
|
|
(map['leg_distance'] as num?)?.toDouble() ??
|
|
|
|
(map['leg_mileage'] as num?)?.toDouble();
|
|
|
|
(map['leg_mileage'] as num?)?.toDouble();
|
|
|
|
if (mileage != null && mileage > 0) {
|
|
|
|
if (mileage != null && mileage > 0) {
|
|
|
|
parts.add('${mileage.toStringAsFixed(1)} mi');
|
|
|
|
parts.add('${mileage.toStringAsFixed(1)} mi');
|
|
|
|
} else if (map['leg_route'] is List && (map['leg_route'] as List).isNotEmpty) {
|
|
|
|
} else if (map['leg_route'] is List &&
|
|
|
|
|
|
|
|
(map['leg_route'] as List).isNotEmpty) {
|
|
|
|
parts.add('Route ${(map['leg_route'] as List).length} stops');
|
|
|
|
parts.add('Route ${(map['leg_route'] as List).length} stops');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
final locos = map['locos'];
|
|
|
|
final locos = map['locos'];
|
|
|
|
|