diff --git a/Makefile b/Makefile
index e444986..76f4a72 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: test, seed, assets
+.PHONY: test seed assets
clean:
flutter clean && flutter pub get
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index f4a79e3..a85c6c5 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -4,7 +4,7 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
-
+
-
+
+
+
@@ -84,7 +86,7 @@
-
+
diff --git a/lib/app/models/user.dart b/lib/app/models/user.dart
index a84897a..006c834 100644
--- a/lib/app/models/user.dart
+++ b/lib/app/models/user.dart
@@ -1,21 +1,97 @@
//User Model
-class UserModel {
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+class Zone2User {
final String uid;
final String email;
String name;
- Map fcmTokenMap;
-
- UserModel(
- {required this.uid, required this.email, required this.name, required this.fcmTokenMap});
+ final bool onboardingComplete;
+ ZoneSettings? zoneSettings;
+ Zone2User(
+ {required this.uid,
+ required this.email,
+ required this.name,
+ required this.onboardingComplete,
+ this.zoneSettings});
- factory UserModel.fromJson(Map data) {
- return UserModel(
+ factory Zone2User.fromJson(Map data) {
+ return Zone2User(
uid: data['uid'],
email: data['email'] ?? '',
name: data['name'] ?? '',
- fcmTokenMap: data['fcmTokenMap'] ?? {});
+ onboardingComplete: data['onboardingComplete'] ?? false,
+ zoneSettings: ZoneSettings.fromJson(data['zoneSettings'] ?? {}));
+ }
+
+ Map toJson() => {
+ "uid": uid,
+ "email": email,
+ "name": name,
+ "onboardingComplete": onboardingComplete,
+ "zoneSettings": zoneSettings?.toJson() ?? {}
+ };
+}
+
+class ZoneSettings {
+ final Timestamp journeyStartDate;
+ final int dailyWaterGoalInOz;
+ final int dailyZonePointsGoal;
+ final int dailyCalorieIntakeGoal;
+ final int dailyCaloriesBurnedGoal;
+ final int dailyStepsGoal;
+ final String reasonForStartingJourney;
+ final double initialWeightInLbs;
+ final double targetWeightInLbs;
+ final double heightInInches;
+ final int heightInFeet;
+ final String birthDate;
+ final String gender;
+
+ ZoneSettings(
+ {required this.journeyStartDate,
+ required this.dailyWaterGoalInOz,
+ required this.dailyZonePointsGoal,
+ required this.dailyCalorieIntakeGoal,
+ required this.dailyCaloriesBurnedGoal,
+ required this.dailyStepsGoal,
+ required this.reasonForStartingJourney,
+ required this.initialWeightInLbs,
+ required this.targetWeightInLbs,
+ required this.heightInInches,
+ required this.heightInFeet,
+ required this.birthDate,
+ required this.gender});
+
+ factory ZoneSettings.fromJson(Map data) {
+ return ZoneSettings(
+ journeyStartDate: data['journeyStartDate'] as Timestamp? ?? Timestamp.now(),
+ dailyWaterGoalInOz: (data['dailyWaterGoalInOz'] as num?)?.toInt() ?? 100,
+ dailyZonePointsGoal: (data['dailyZonePointsGoal'] as num?)?.toInt() ?? 100,
+ dailyCalorieIntakeGoal: (data['dailyCalorieIntakeGoal'] as num?)?.toInt() ?? 0,
+ dailyCaloriesBurnedGoal: (data['dailyCaloriesBurnedGoal'] as num?)?.toInt() ?? 0,
+ dailyStepsGoal: (data['dailyStepsGoal'] as num?)?.toInt() ?? 10000,
+ reasonForStartingJourney: data['reasonForStartingJourney'] as String? ?? '',
+ initialWeightInLbs: (data['initialWeightInLbs'] as num?)?.toDouble() ?? 0.0,
+ targetWeightInLbs: (data['targetWeightInLbs'] as num?)?.toDouble() ?? 0.0,
+ heightInInches: (data['heightInInches'] as num?)?.toDouble() ?? 0.0,
+ heightInFeet: (data['heightInFeet'] as num?)?.toInt() ?? 0,
+ birthDate: data['birthDate'] as String? ?? '',
+ gender: data['gender'] as String? ?? '');
}
- Map toJson() =>
- {"uid": uid, "email": email, "name": name, "fcmTokenMap": fcmTokenMap};
+ Map toJson() => {
+ "journeyStartDate": journeyStartDate,
+ "dailyWaterGoalInOz": dailyWaterGoalInOz,
+ "dailyZonePointsGoal": dailyZonePointsGoal,
+ "dailyCalorieIntakeGoal": dailyCalorieIntakeGoal,
+ "dailyCaloriesBurnedGoal": dailyCaloriesBurnedGoal,
+ "dailyStepsGoal": dailyStepsGoal,
+ "reasonForStartingJourney": reasonForStartingJourney,
+ "initialWeightInLbs": initialWeightInLbs,
+ "targetWeightInLbs": targetWeightInLbs,
+ "heightInInches": heightInInches,
+ "heightInFeet": heightInFeet,
+ "birthDate": birthDate,
+ "gender": gender
+ };
}
diff --git a/lib/app/modules/diary/controllers/diary_controller.dart b/lib/app/modules/diary/controllers/diary_controller.dart
index 5511613..9c37127 100644
--- a/lib/app/modules/diary/controllers/diary_controller.dart
+++ b/lib/app/modules/diary/controllers/diary_controller.dart
@@ -9,6 +9,7 @@ import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:speech_to_text/speech_recognition_error.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';
+import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:zone2/app/modules/diary/controllers/activity_manager.dart';
import 'package:zone2/app/models/food.dart';
import 'package:zone2/app/services/food_service.dart';
@@ -17,6 +18,64 @@ import 'package:intl/intl.dart'; // Added for date formatting
import 'package:zone2/app/services/notification_service.dart';
import 'package:zone2/app/services/openai_service.dart';
+class FoodVoiceResult {
+ final String label;
+ final String searchTerm;
+ final double quantity;
+ final String unit;
+ final MealType mealType;
+
+ FoodVoiceResult({
+ required this.label,
+ required this.searchTerm,
+ required this.quantity,
+ required this.unit,
+ required this.mealType,
+ });
+
+ // Factory method to create a FoodVoiceResult from JSON
+ factory FoodVoiceResult.fromJson(Map json) {
+ return FoodVoiceResult(
+ label: json['label'],
+ searchTerm: json['searchTerm'],
+ quantity: json['quantity'].toDouble(),
+ unit: json['unit'],
+ mealType: _parseMealType(json['mealType']),
+ );
+ }
+
+ // Factory method to create a list of FoodVoiceResult from OpenAI completion
+ static List fromOpenAiCompletion(List items) {
+ return items.map((item) {
+ // Handle nulls for food items
+ final food = item['food'];
+ return FoodVoiceResult(
+ label: food?['label'] as String? ?? 'Unknown Food', // Default value for label
+ searchTerm: food?['searchTerm'] as String? ?? '', // Default to empty string
+ quantity: (food?['quantity'] as double?) ?? 0.0, // Default to 0.0
+ unit: food?['unit'] as String? ?? 'units', // Default unit
+ mealType: _parseMealType(food?['mealType'] as String? ?? 'UNKNOWN'), // Default to UNKNOWN
+ );
+ }).toList();
+ }
+
+ // Helper method to parse meal type from string
+ static MealType _parseMealType(String type) {
+ switch (type.toUpperCase()) {
+ case 'BREAKFAST':
+ return MealType.BREAKFAST;
+ case 'LUNCH':
+ return MealType.LUNCH;
+ case 'DINNER':
+ return MealType.DINNER;
+ case 'SNACK':
+ return MealType.SNACK;
+ default:
+ return MealType.UNKNOWN;
+ }
+ }
+}
+
class DiaryController extends GetxController {
final logger = Get.find();
final healthService = Get.find();
@@ -68,6 +127,12 @@ class DiaryController extends GetxController {
final lastError = Rxn();
final recognizedWords = ''.obs;
final systemLocale = Rxn();
+ final isTestMode = false.obs; // Toggle this for testing
+
+ final voiceResults = RxList();
+
+ String selectedVoiceFood = '';
+ RxList searchResults = RxList();
// Barcode scanning
final Rxn barcode = Rxn();
@@ -75,6 +140,8 @@ class DiaryController extends GetxController {
late MobileScannerController scannerController;
StreamSubscription