Features: - Save calculation runs with custom labels for later retrieval - View saved runs list with dominant factor summary - View detailed results of any saved run - View original inputs in read-only mode - Use any saved run as starting point for new calculation - Compare two saved runs side-by-side - About screen with app info, help links, privacy policy - Metric/Imperial unit toggle persisted across sessions - Info icon in app bar on all screens Technical: - New SavedRun model with full serialization - SQLite saved_runs table with CRUD operations - readOnly mode for all input screens - Unit preference stored in LocalStorage - Database schema upgraded to v2 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
80 lines
2.2 KiB
Dart
80 lines
2.2 KiB
Dart
import 'behavioral_inputs.dart';
|
|
import 'result.dart';
|
|
import 'user_profile.dart';
|
|
|
|
class SavedRun {
|
|
final String id;
|
|
final String label;
|
|
final CalculationResult result;
|
|
final UserProfile profile;
|
|
final BehavioralInputs behaviors;
|
|
final DateTime createdAt;
|
|
|
|
const SavedRun({
|
|
required this.id,
|
|
required this.label,
|
|
required this.result,
|
|
required this.profile,
|
|
required this.behaviors,
|
|
required this.createdAt,
|
|
});
|
|
|
|
String get displayDate {
|
|
final now = DateTime.now();
|
|
final diff = now.difference(createdAt);
|
|
|
|
if (diff.inDays == 0) {
|
|
return 'Today';
|
|
} else if (diff.inDays == 1) {
|
|
return 'Yesterday';
|
|
} else if (diff.inDays < 7) {
|
|
return '${diff.inDays} days ago';
|
|
} else {
|
|
return '${createdAt.month}/${createdAt.day}/${createdAt.year}';
|
|
}
|
|
}
|
|
|
|
String get dominantFactorSummary {
|
|
final factor = result.dominantFactor;
|
|
if (factor == null) return 'Optimal';
|
|
return '${factor.displayName}: ${factor.delta.rangeDisplay} mo';
|
|
}
|
|
|
|
Map<String, dynamic> toJson() => {
|
|
'id': id,
|
|
'label': label,
|
|
'result': result.toJson(),
|
|
'profile': profile.toJson(),
|
|
'behaviors': behaviors.toJson(),
|
|
'createdAt': createdAt.toIso8601String(),
|
|
};
|
|
|
|
factory SavedRun.fromJson(Map<String, dynamic> json) => SavedRun(
|
|
id: json['id'] as String,
|
|
label: json['label'] as String,
|
|
result:
|
|
CalculationResult.fromJson(json['result'] as Map<String, dynamic>),
|
|
profile:
|
|
UserProfile.fromJson(json['profile'] as Map<String, dynamic>),
|
|
behaviors: BehavioralInputs.fromJson(
|
|
json['behaviors'] as Map<String, dynamic>),
|
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
|
);
|
|
|
|
SavedRun copyWith({
|
|
String? id,
|
|
String? label,
|
|
CalculationResult? result,
|
|
UserProfile? profile,
|
|
BehavioralInputs? behaviors,
|
|
DateTime? createdAt,
|
|
}) =>
|
|
SavedRun(
|
|
id: id ?? this.id,
|
|
label: label ?? this.label,
|
|
result: result ?? this.result,
|
|
profile: profile ?? this.profile,
|
|
behaviors: behaviors ?? this.behaviors,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
);
|
|
}
|