Layout changes, fix bugs in new entry page

This commit is contained in:
2025-12-22 17:23:21 +00:00
parent 63b545c7a3
commit 45d543498f
20 changed files with 779 additions and 192 deletions

View File

@@ -84,6 +84,32 @@ extension _NewEntryDraftLogic on _NewEntryPageState {
}
}
Future<void> _saveDraftManually() async {
if (_savingDraft) return;
if (_formIsEmpty()) {
ScaffoldMessenger.maybeOf(context)?.showSnackBar(
const SnackBar(content: Text('Nothing to save yet.')),
);
return;
}
final hadDraft = _activeDraftId != null;
_setState(() => _savingDraft = true);
try {
await _saveDraftEntry(draftId: _activeDraftId);
if (!mounted) return;
ScaffoldMessenger.maybeOf(context)?.showSnackBar(
SnackBar(content: Text(hadDraft ? 'Draft updated' : 'Draft saved')),
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.maybeOf(context)?.showSnackBar(
SnackBar(content: Text('Failed to save draft: $e')),
);
} finally {
if (mounted) _setState(() => _savingDraft = false);
}
}
Future<void> _saveDraft() async {
if (_restoringDraft || !_draftPersistenceEnabled) return;
final prefs = await SharedPreferences.getInstance();
@@ -212,6 +238,7 @@ extension _NewEntryDraftLogic on _NewEntryPageState {
if (includeTimestamp) "saved_at": DateTime.now().toIso8601String(),
"mode": _useManualMileage ? 'manual' : 'auto',
"payload": payload,
"mileageText": _mileageController.text.trim(),
"routeResult": _routeResult == null
? null
: {

View File

@@ -27,6 +27,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
int? _selectedTripId;
bool _restoringDraft = false;
bool _loadingEdit = false;
bool _savingDraft = false;
String? _loadError;
Map<String, dynamic>? _lastSubmittedSnapshot;
Map<String, dynamic>? _loadedDraftSnapshot;
@@ -48,7 +49,7 @@ class _NewEntryPageState extends State<NewEntryPage> {
if (!mounted) return;
final data = context.read<DataService>();
data.fetchClassList();
data.fetchTrips();
data.fetchTripOptions();
if (_draftPersistenceEnabled) {
_loadDraft();
}
@@ -146,20 +147,31 @@ class _NewEntryPageState extends State<NewEntryPage> {
return;
}
if (result != null && result.isNotEmpty) {
final api = context.read<ApiService>();
final data = context.read<DataService>();
final messenger = ScaffoldMessenger.maybeOf(context);
try {
await api.put('/trips/new', {"trip_name": result});
await data.fetchTrips();
if (!context.mounted) return;
final trips = data.tripList;
final match = trips.firstWhere(
(t) => t.tripName == result,
orElse: () => trips.isNotEmpty
? trips.first
: TripSummary(tripId: 0, tripName: result, tripMileage: 0),
);
final api = context.read<ApiService>();
final data = context.read<DataService>();
final messenger = ScaffoldMessenger.maybeOf(context);
try {
final encoded = Uri.encodeComponent(result);
final res = await api.put('/trips/new?trip_name=$encoded', {});
await data.fetchTripOptions();
if (!context.mounted) return;
final trips = data.tripList;
final apiTripId = res is Map ? res['trip_id'] as int? : null;
TripSummary match;
try {
match = trips.firstWhere(
(t) =>
(apiTripId != null && t.tripId == apiTripId) ||
t.tripName == result,
);
} catch (_) {
match = TripSummary(
tripId: apiTripId ?? 0,
tripName: result,
tripMileage: 0,
);
data.upsertTripSummary(match);
}
setState(() => _selectedTripId = match.tripId);
_saveDraft();
} catch (e) {
@@ -176,9 +188,13 @@ class _NewEntryPageState extends State<NewEntryPage> {
}
Future<void> _openCalculator() async {
final initialStations = _routeResult?.inputRoute.isNotEmpty == true
? _routeResult!.inputRoute
: (_routeResult?.calculatedRoute ?? const []);
final result = await Navigator.of(context).push<RouteResult>(
MaterialPageRoute(
builder: (_) => _CalculatorPickerPage(
initialStations: initialStations.isEmpty ? null : initialStations,
onResult: (res) => Navigator.of(context).pop(res),
),
),
@@ -373,6 +389,25 @@ class _NewEntryPageState extends State<NewEntryPage> {
icon: const Icon(Icons.list_alt, size: 16),
label: const Text('Drafts'),
),
const SizedBox(width: 12),
TextButton.icon(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: const Size(0, 36),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
onPressed: _isEditing || _savingDraft || _submitting
? null
: _saveDraftManually,
icon: _savingDraft
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.save_alt, size: 16),
label: Text(_savingDraft ? 'Saving...' : 'Save to drafts'),
),
const Spacer(),
TextButton.icon(
style: TextButton.styleFrom(

View File

@@ -1,8 +1,12 @@
part of 'new_entry.dart';
class _CalculatorPickerPage extends StatelessWidget {
const _CalculatorPickerPage({required this.onResult});
const _CalculatorPickerPage({
required this.onResult,
this.initialStations,
});
final ValueChanged<RouteResult> onResult;
final List<String>? initialStations;
@override
Widget build(BuildContext context) {
@@ -14,8 +18,10 @@ class _CalculatorPickerPage extends StatelessWidget {
),
title: const Text('Mileage calculator'),
),
body: RouteCalculator(onApplyRoute: onResult),
body: RouteCalculator(
onApplyRoute: onResult,
initialStations: initialStations,
),
);
}
}

View File

@@ -27,7 +27,8 @@ extension _NewEntrySubmitLogic on _NewEntryPageState {
final fieldList = missing.join(', ');
await showDialog<void>(
context: context,
builder: (_) => AlertDialog(
useRootNavigator: false,
builder: (dialogCtx) => AlertDialog(
title: const Text('Required field missing'),
content: Text(
missing.length == 1
@@ -36,7 +37,7 @@ extension _NewEntrySubmitLogic on _NewEntryPageState {
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
onPressed: () => Navigator.of(dialogCtx).pop(),
child: const Text('OK'),
),
],
@@ -46,7 +47,9 @@ extension _NewEntrySubmitLogic on _NewEntryPageState {
}
Future<void> _submit() async {
if (!_formKey.currentState!.validate()) return;
final form = _formKey.currentState;
if (form == null) return;
if (!form.validate()) return;
if (!await _validateRequiredFields()) return;
final routeStations = _routeResult?.calculatedRoute ?? [];
final startVal = _useManualMileage
@@ -208,6 +211,8 @@ extension _NewEntrySubmitLogic on _NewEntryPageState {
_selectedTripId = null;
_submitting = false;
_activeDraftId = null;
_savingDraft = false;
_loadedDraftSnapshot = null;
});
if (clearDraft) {
await _clearDraft();