434 lines
9.8 KiB
Dart
434 lines
9.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:hive/hive.dart';
|
|
|
|
import '../theme/app_theme.dart';
|
|
|
|
part 'cycle_entry.g.dart';
|
|
|
|
/// Mood levels for tracking
|
|
@HiveType(typeId: 3)
|
|
enum MoodLevel {
|
|
@HiveField(0)
|
|
verySad,
|
|
|
|
@HiveField(1)
|
|
sad,
|
|
|
|
@HiveField(2)
|
|
neutral,
|
|
|
|
@HiveField(3)
|
|
happy,
|
|
|
|
@HiveField(4)
|
|
veryHappy,
|
|
}
|
|
|
|
/// Flow intensity for period days
|
|
@HiveType(typeId: 4)
|
|
enum FlowIntensity {
|
|
@HiveField(0)
|
|
spotting,
|
|
|
|
@HiveField(1)
|
|
light,
|
|
|
|
@HiveField(2)
|
|
medium,
|
|
|
|
@HiveField(3)
|
|
heavy,
|
|
}
|
|
|
|
/// Cervical mucus type for NFP tracking
|
|
@HiveType(typeId: 5)
|
|
enum CervicalMucusType {
|
|
@HiveField(0)
|
|
dry,
|
|
|
|
@HiveField(1)
|
|
sticky,
|
|
|
|
@HiveField(2)
|
|
creamy,
|
|
|
|
@HiveField(3)
|
|
eggWhite,
|
|
|
|
@HiveField(4)
|
|
watery,
|
|
}
|
|
|
|
/// Cycle phase
|
|
@HiveType(typeId: 6)
|
|
enum CyclePhase {
|
|
@HiveField(0)
|
|
menstrual,
|
|
|
|
@HiveField(1)
|
|
follicular,
|
|
|
|
@HiveField(2)
|
|
ovulation,
|
|
|
|
@HiveField(3)
|
|
luteal,
|
|
}
|
|
|
|
/// Daily cycle entry
|
|
@HiveType(typeId: 7)
|
|
class CycleEntry extends HiveObject {
|
|
@HiveField(0)
|
|
String id;
|
|
|
|
@HiveField(1)
|
|
DateTime date;
|
|
|
|
@HiveField(2, defaultValue: false)
|
|
bool isPeriodDay;
|
|
|
|
@HiveField(3)
|
|
FlowIntensity? flowIntensity;
|
|
|
|
@HiveField(4)
|
|
MoodLevel? mood;
|
|
|
|
@HiveField(5)
|
|
int? energyLevel; // 1-5
|
|
|
|
@HiveField(6)
|
|
int? crampIntensity; // 1-5
|
|
|
|
@HiveField(7, defaultValue: false)
|
|
bool hasHeadache;
|
|
|
|
@HiveField(8, defaultValue: false)
|
|
bool hasBloating;
|
|
|
|
@HiveField(9, defaultValue: false)
|
|
bool hasBreastTenderness;
|
|
|
|
@HiveField(10, defaultValue: false)
|
|
bool hasFatigue;
|
|
|
|
@HiveField(11, defaultValue: false)
|
|
bool hasAcne;
|
|
|
|
@HiveField(22, defaultValue: false)
|
|
bool hasLowerBackPain;
|
|
|
|
@HiveField(23, defaultValue: false)
|
|
bool hasConstipation;
|
|
|
|
@HiveField(24, defaultValue: false)
|
|
bool hasDiarrhea;
|
|
|
|
@HiveField(25)
|
|
int? stressLevel; // 1-5
|
|
|
|
@HiveField(26, defaultValue: false)
|
|
bool hasInsomnia;
|
|
|
|
@HiveField(12)
|
|
double? basalBodyTemperature; // in Fahrenheit
|
|
|
|
@HiveField(13)
|
|
CervicalMucusType? cervicalMucus;
|
|
|
|
@HiveField(14)
|
|
bool? ovulationTestPositive;
|
|
|
|
@HiveField(15)
|
|
String? notes;
|
|
|
|
@HiveField(16)
|
|
int? sleepHours;
|
|
|
|
@HiveField(17)
|
|
int? waterIntake; // glasses
|
|
|
|
@HiveField(18, defaultValue: false)
|
|
bool hadExercise;
|
|
|
|
@HiveField(19, defaultValue: false)
|
|
bool hadIntimacy; // For married users only
|
|
|
|
@HiveField(20)
|
|
DateTime createdAt;
|
|
|
|
@HiveField(21)
|
|
DateTime updatedAt;
|
|
|
|
@HiveField(27)
|
|
List<String>? cravings;
|
|
|
|
@HiveField(28)
|
|
String? husbandNotes; // Separate notes for husband
|
|
|
|
@HiveField(29)
|
|
bool? intimacyProtected; // null = no selection, true = protected, false = unprotected
|
|
|
|
@HiveField(30, defaultValue: false)
|
|
bool usedPantyliner;
|
|
|
|
@HiveField(31, defaultValue: 0)
|
|
int pantylinerCount;
|
|
|
|
CycleEntry({
|
|
required this.id,
|
|
required this.date,
|
|
this.isPeriodDay = false,
|
|
this.flowIntensity,
|
|
this.mood,
|
|
this.energyLevel,
|
|
this.crampIntensity,
|
|
this.hasHeadache = false,
|
|
this.hasBloating = false,
|
|
this.hasBreastTenderness = false,
|
|
this.hasFatigue = false,
|
|
this.hasAcne = false,
|
|
this.hasLowerBackPain = false,
|
|
this.hasConstipation = false,
|
|
this.hasDiarrhea = false,
|
|
this.stressLevel,
|
|
this.hasInsomnia = false,
|
|
this.basalBodyTemperature,
|
|
this.cervicalMucus,
|
|
this.ovulationTestPositive,
|
|
this.notes,
|
|
this.cravings,
|
|
this.sleepHours,
|
|
this.waterIntake,
|
|
this.hadExercise = false,
|
|
this.hadIntimacy = false,
|
|
this.intimacyProtected,
|
|
required this.createdAt,
|
|
required this.updatedAt,
|
|
this.husbandNotes,
|
|
this.usedPantyliner = false,
|
|
this.pantylinerCount = 0,
|
|
});
|
|
|
|
List<bool> get _symptomsList => [
|
|
hasHeadache,
|
|
hasBloating,
|
|
hasBreastTenderness,
|
|
hasFatigue,
|
|
hasAcne,
|
|
hasLowerBackPain,
|
|
hasConstipation,
|
|
hasDiarrhea,
|
|
hasInsomnia,
|
|
(crampIntensity != null && crampIntensity! > 0),
|
|
(stressLevel != null && stressLevel! > 1),
|
|
];
|
|
|
|
/// Check if any symptoms are logged
|
|
bool get hasSymptoms => _symptomsList.contains(true);
|
|
|
|
/// Check if NFP data is logged
|
|
bool get hasNFPData =>
|
|
basalBodyTemperature != null ||
|
|
cervicalMucus != null ||
|
|
ovulationTestPositive != null;
|
|
|
|
/// Get symptom count
|
|
int get symptomCount => _symptomsList.where((s) => s).length;
|
|
|
|
// ... (omitted getters)
|
|
|
|
/// Copy with updated fields
|
|
CycleEntry copyWith({
|
|
String? id,
|
|
DateTime? date,
|
|
bool? isPeriodDay,
|
|
FlowIntensity? flowIntensity,
|
|
MoodLevel? mood,
|
|
int? energyLevel,
|
|
int? crampIntensity,
|
|
bool? hasHeadache,
|
|
bool? hasBloating,
|
|
bool? hasBreastTenderness,
|
|
bool? hasFatigue,
|
|
bool? hasAcne,
|
|
bool? hasLowerBackPain,
|
|
bool? hasConstipation,
|
|
bool? hasDiarrhea,
|
|
int? stressLevel,
|
|
bool? hasInsomnia,
|
|
double? basalBodyTemperature,
|
|
CervicalMucusType? cervicalMucus,
|
|
bool? ovulationTestPositive,
|
|
String? notes,
|
|
List<String>? cravings,
|
|
int? sleepHours,
|
|
int? waterIntake,
|
|
bool? hadExercise,
|
|
bool? hadIntimacy,
|
|
bool? intimacyProtected,
|
|
DateTime? createdAt,
|
|
DateTime? updatedAt,
|
|
String? husbandNotes,
|
|
bool? usedPantyliner,
|
|
int? pantylinerCount,
|
|
}) {
|
|
return CycleEntry(
|
|
id: id ?? this.id,
|
|
date: date ?? this.date,
|
|
isPeriodDay: isPeriodDay ?? this.isPeriodDay,
|
|
flowIntensity: flowIntensity ?? this.flowIntensity,
|
|
mood: mood ?? this.mood,
|
|
energyLevel: energyLevel ?? this.energyLevel,
|
|
crampIntensity: crampIntensity ?? this.crampIntensity,
|
|
hasHeadache: hasHeadache ?? this.hasHeadache,
|
|
hasBloating: hasBloating ?? this.hasBloating,
|
|
hasBreastTenderness: hasBreastTenderness ?? this.hasBreastTenderness,
|
|
hasFatigue: hasFatigue ?? this.hasFatigue,
|
|
hasAcne: hasAcne ?? this.hasAcne,
|
|
hasLowerBackPain: hasLowerBackPain ?? this.hasLowerBackPain,
|
|
hasConstipation: hasConstipation ?? this.hasConstipation,
|
|
hasDiarrhea: hasDiarrhea ?? this.hasDiarrhea,
|
|
stressLevel: stressLevel ?? this.stressLevel,
|
|
hasInsomnia: hasInsomnia ?? this.hasInsomnia,
|
|
basalBodyTemperature: basalBodyTemperature ?? this.basalBodyTemperature,
|
|
cervicalMucus: cervicalMucus ?? this.cervicalMucus,
|
|
ovulationTestPositive: ovulationTestPositive ?? this.ovulationTestPositive,
|
|
notes: notes ?? this.notes,
|
|
cravings: cravings ?? this.cravings,
|
|
sleepHours: sleepHours ?? this.sleepHours,
|
|
waterIntake: waterIntake ?? this.waterIntake,
|
|
hadExercise: hadExercise ?? this.hadExercise,
|
|
hadIntimacy: hadIntimacy ?? this.hadIntimacy,
|
|
intimacyProtected: intimacyProtected ?? this.intimacyProtected,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
updatedAt: updatedAt ?? DateTime.now(),
|
|
husbandNotes: husbandNotes ?? this.husbandNotes,
|
|
usedPantyliner: usedPantyliner ?? this.usedPantyliner,
|
|
pantylinerCount: pantylinerCount ?? this.pantylinerCount,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Extension to get display string for enums
|
|
extension MoodLevelExtension on MoodLevel {
|
|
String get emoji {
|
|
switch (this) {
|
|
case MoodLevel.verySad:
|
|
return '😢';
|
|
case MoodLevel.sad:
|
|
return '😕';
|
|
case MoodLevel.neutral:
|
|
return '😐';
|
|
case MoodLevel.happy:
|
|
return '🙂';
|
|
case MoodLevel.veryHappy:
|
|
return '😄';
|
|
}
|
|
}
|
|
|
|
String get label {
|
|
switch (this) {
|
|
case MoodLevel.verySad:
|
|
return 'Very Sad';
|
|
case MoodLevel.sad:
|
|
return 'Sad';
|
|
case MoodLevel.neutral:
|
|
return 'Neutral';
|
|
case MoodLevel.happy:
|
|
return 'Happy';
|
|
case MoodLevel.veryHappy:
|
|
return 'Very Happy';
|
|
}
|
|
}
|
|
}
|
|
|
|
extension FlowIntensityExtension on FlowIntensity {
|
|
String get label {
|
|
switch (this) {
|
|
case FlowIntensity.spotting:
|
|
return 'Spotting';
|
|
case FlowIntensity.light:
|
|
return 'Light';
|
|
case FlowIntensity.medium:
|
|
return 'Regular';
|
|
case FlowIntensity.heavy:
|
|
return 'Heavy';
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CyclePhaseExtension on CyclePhase {
|
|
String get label {
|
|
switch (this) {
|
|
case CyclePhase.menstrual:
|
|
return 'Menstrual';
|
|
case CyclePhase.follicular:
|
|
return 'Follicular';
|
|
case CyclePhase.ovulation:
|
|
return 'Ovulation';
|
|
case CyclePhase.luteal:
|
|
return 'Luteal';
|
|
}
|
|
}
|
|
|
|
String get emoji {
|
|
switch (this) {
|
|
case CyclePhase.menstrual:
|
|
return '🩸';
|
|
case CyclePhase.follicular:
|
|
return '🌱';
|
|
case CyclePhase.ovulation:
|
|
return '🌸';
|
|
case CyclePhase.luteal:
|
|
return '🌙';
|
|
}
|
|
}
|
|
|
|
Color get color {
|
|
switch (this) {
|
|
case CyclePhase.menstrual:
|
|
return AppColors.menstrualPhase;
|
|
case CyclePhase.follicular:
|
|
return AppColors.follicularPhase;
|
|
case CyclePhase.ovulation:
|
|
return AppColors.ovulationPhase;
|
|
case CyclePhase.luteal:
|
|
return AppColors.lutealPhase;
|
|
}
|
|
}
|
|
|
|
List<Color> get gradientColors {
|
|
switch (this) {
|
|
case CyclePhase.menstrual:
|
|
return [AppColors.rose, AppColors.menstrualPhase, AppColors.blushPink];
|
|
case CyclePhase.follicular:
|
|
return [
|
|
AppColors.sageGreen,
|
|
AppColors.follicularPhase,
|
|
AppColors.sageGreen.withOpacity(0.7)
|
|
];
|
|
case CyclePhase.ovulation:
|
|
return [AppColors.lavender, AppColors.ovulationPhase, AppColors.rose];
|
|
case CyclePhase.luteal:
|
|
return [
|
|
AppColors.lutealPhase,
|
|
AppColors.lavender,
|
|
AppColors.blushPink
|
|
];
|
|
}
|
|
}
|
|
|
|
String get description {
|
|
switch (this) {
|
|
case CyclePhase.menstrual:
|
|
return 'A time for rest and reflection';
|
|
case CyclePhase.follicular:
|
|
return 'A time of renewal and energy';
|
|
case CyclePhase.ovulation:
|
|
return 'Peak fertility window';
|
|
case CyclePhase.luteal:
|
|
return 'A time for patience and self-care';
|
|
}
|
|
}
|
|
}
|