Add offline meal journal and secure sync
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
enum MealType { breakfast, lunch, dinner, snack }
|
||||
|
||||
enum MealSource { manual, photoAi }
|
||||
|
||||
enum MealSyncStatus { pending, synced, failed }
|
||||
|
||||
class MealEntry {
|
||||
MealEntry({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.mealType,
|
||||
required this.calories,
|
||||
required this.proteinGrams,
|
||||
required this.carbohydrateGrams,
|
||||
required this.fatGrams,
|
||||
required this.consumedAt,
|
||||
this.source = MealSource.manual,
|
||||
this.notes = '',
|
||||
this.syncStatus = MealSyncStatus.pending,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final MealType mealType;
|
||||
final double calories;
|
||||
final double proteinGrams;
|
||||
final double carbohydrateGrams;
|
||||
final double fatGrams;
|
||||
final DateTime consumedAt;
|
||||
final MealSource source;
|
||||
final String notes;
|
||||
MealSyncStatus syncStatus;
|
||||
|
||||
Map<String, dynamic> toMap() => {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'meal_type': mealType.name,
|
||||
'calories': calories,
|
||||
'protein_grams': proteinGrams,
|
||||
'carbohydrate_grams': carbohydrateGrams,
|
||||
'fat_grams': fatGrams,
|
||||
'consumed_at': consumedAt.toUtc().toIso8601String(),
|
||||
'source': source == MealSource.photoAi ? 'photo_ai' : 'manual',
|
||||
'notes': notes,
|
||||
'sync_status': syncStatus.name,
|
||||
};
|
||||
|
||||
Map<String, dynamic> toApiPayload() => {
|
||||
'client_meal_uuid': id,
|
||||
'name': name,
|
||||
'meal_type': mealType.name,
|
||||
'calories': calories,
|
||||
'protein_grams': proteinGrams,
|
||||
'carbohydrate_grams': carbohydrateGrams,
|
||||
'fat_grams': fatGrams,
|
||||
'consumed_at': consumedAt.toUtc().toIso8601String(),
|
||||
'source': source == MealSource.photoAi ? 'photo_ai' : 'manual',
|
||||
'notes': notes,
|
||||
};
|
||||
|
||||
factory MealEntry.fromMap(Map<String, dynamic> map) => MealEntry(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String,
|
||||
mealType: MealType.values.firstWhere(
|
||||
(value) => value.name == map['meal_type'],
|
||||
orElse: () => MealType.snack,
|
||||
),
|
||||
calories: _asDouble(map['calories']),
|
||||
proteinGrams: _asDouble(map['protein_grams']),
|
||||
carbohydrateGrams: _asDouble(map['carbohydrate_grams']),
|
||||
fatGrams: _asDouble(map['fat_grams']),
|
||||
consumedAt: DateTime.parse(map['consumed_at'] as String).toLocal(),
|
||||
source: map['source'] == 'photo_ai' || map['source'] == 'photoAi'
|
||||
? MealSource.photoAi
|
||||
: MealSource.manual,
|
||||
notes: map['notes'] as String? ?? '',
|
||||
syncStatus: MealSyncStatus.values.firstWhere(
|
||||
(value) => value.name == map['sync_status'],
|
||||
orElse: () => MealSyncStatus.pending,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
double _asDouble(dynamic value) => value is num ? value.toDouble() : 0;
|
||||
Reference in New Issue
Block a user