Skip to content

Commit 8049bcf

Browse files
committed
support NutritionalPlan start & end dates
1 parent cad2054 commit 8049bcf

File tree

3 files changed

+122
-17
lines changed

3 files changed

+122
-17
lines changed

lib/l10n/app_en.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,14 @@
384384
"@date": {
385385
"description": "The date of a workout log or body weight entry"
386386
},
387+
"creationDate": "Start date",
388+
"@creationDate": {
389+
"description": "The Start date of a nutritional plan"
390+
},
391+
"endDate": "End date",
392+
"@endDate": {
393+
"description": "The End date of a nutritional plan"
394+
},
387395
"value": "Value",
388396
"@value": {
389397
"description": "The value of a measurement entry"

lib/models/nutrition/nutritional_plan.dart

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ class NutritionalPlan {
4141
@JsonKey(required: true, name: 'creation_date', toJson: dateToYYYYMMDD)
4242
late DateTime creationDate;
4343

44+
@JsonKey(required: true, name: 'start', toJson: dateToYYYYMMDD)
45+
late DateTime startDate;
46+
47+
@JsonKey(required: true, name: 'end', toJson: dateToYYYYMMDD)
48+
late DateTime? endDate;
49+
4450
@JsonKey(required: true, name: 'only_logging')
4551
late bool onlyLogging;
4652

@@ -84,6 +90,8 @@ class NutritionalPlan {
8490

8591
NutritionalPlan.empty() {
8692
creationDate = DateTime.now();
93+
startDate = DateTime.now();
94+
endDate = null;
8795
description = '';
8896
onlyLogging = false;
8997
goalEnergy = null;
@@ -94,12 +102,15 @@ class NutritionalPlan {
94102
}
95103

96104
// Boilerplate
97-
factory NutritionalPlan.fromJson(Map<String, dynamic> json) => _$NutritionalPlanFromJson(json);
105+
factory NutritionalPlan.fromJson(Map<String, dynamic> json) =>
106+
_$NutritionalPlanFromJson(json);
98107

99108
Map<String, dynamic> toJson() => _$NutritionalPlanToJson(this);
100109

101110
String getLabel(BuildContext context) {
102-
return description != '' ? description : AppLocalizations.of(context).nutritionalPlan;
111+
return description != ''
112+
? description
113+
: AppLocalizations.of(context).nutritionalPlan;
103114
}
104115

105116
bool get hasAnyGoals {
@@ -154,7 +165,9 @@ class NutritionalPlan {
154165
final now = DateTime.now();
155166
final today = DateTime(now.year, now.month, now.day);
156167

157-
return logEntriesValues.containsKey(today) ? logEntriesValues[today]! : NutritionalValues();
168+
return logEntriesValues.containsKey(today)
169+
? logEntriesValues[today]!
170+
: NutritionalValues();
158171
}
159172

160173
NutritionalValues get loggedNutritionalValues7DayAvg {
@@ -170,7 +183,8 @@ class NutritionalPlan {
170183
Map<DateTime, NutritionalValues> get logEntriesValues {
171184
final out = <DateTime, NutritionalValues>{};
172185
for (final log in diaryEntries) {
173-
final date = DateTime(log.datetime.year, log.datetime.month, log.datetime.day);
186+
final date =
187+
DateTime(log.datetime.year, log.datetime.month, log.datetime.day);
174188

175189
if (!out.containsKey(date)) {
176190
out[date] = NutritionalValues();
@@ -195,7 +209,8 @@ class NutritionalPlan {
195209
final List<Log> out = [];
196210
for (final log in diaryEntries) {
197211
final dateKey = DateTime(date.year, date.month, date.day);
198-
final logKey = DateTime(log.datetime.year, log.datetime.month, log.datetime.day);
212+
final logKey =
213+
DateTime(log.datetime.year, log.datetime.month, log.datetime.day);
199214

200215
if (dateKey == logKey) {
201216
out.add(log);
@@ -212,7 +227,9 @@ class NutritionalPlan {
212227
for (final meal in meals) {
213228
for (final mealItem in meal.mealItems) {
214229
final found = out.firstWhereOrNull(
215-
(e) => e.amount == mealItem.amount && e.ingredientId == mealItem.ingredientId,
230+
(e) =>
231+
e.amount == mealItem.amount &&
232+
e.ingredientId == mealItem.ingredientId,
216233
);
217234

218235
if (found == null) {

lib/widgets/nutrition/forms.dart

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class MealForm extends StatelessWidget {
4242
final _nameController = TextEditingController();
4343

4444
MealForm(this._planId, [meal]) {
45-
_meal = meal ?? Meal(plan: _planId, time: TimeOfDay.fromDateTime(DateTime.now()));
45+
_meal = meal ??
46+
Meal(plan: _planId, time: TimeOfDay.fromDateTime(DateTime.now()));
4647
_timeController.text = timeToString(_meal.time)!;
4748
_nameController.text = _meal.name;
4849
}
@@ -57,7 +58,8 @@ class MealForm extends StatelessWidget {
5758
children: [
5859
TextFormField(
5960
key: const Key('field-time'),
60-
decoration: InputDecoration(labelText: AppLocalizations.of(context).time),
61+
decoration:
62+
InputDecoration(labelText: AppLocalizations.of(context).time),
6163
controller: _timeController,
6264
onTap: () async {
6365
// Stop keyboard from appearing
@@ -79,7 +81,8 @@ class MealForm extends StatelessWidget {
7981
TextFormField(
8082
maxLength: 25,
8183
key: const Key('field-name'),
82-
decoration: InputDecoration(labelText: AppLocalizations.of(context).name),
84+
decoration:
85+
InputDecoration(labelText: AppLocalizations.of(context).name),
8386
controller: _nameController,
8487
onSaved: (newValue) {
8588
_meal.name = newValue as String;
@@ -125,7 +128,8 @@ Widget MealItemForm(
125128
recent: recent.map((e) => Log.fromMealItem(e, 0, e.mealId)).toList(),
126129
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
127130
mealItem.mealId = meal.id!;
128-
Provider.of<NutritionPlansProvider>(context, listen: false).addMealItem(mealItem, meal);
131+
Provider.of<NutritionPlansProvider>(context, listen: false)
132+
.addMealItem(mealItem, meal);
129133
},
130134
barcode: barcode ?? '',
131135
test: test ?? false,
@@ -235,9 +239,11 @@ class IngredientFormState extends State<IngredientForm> {
235239
Widget build(BuildContext context) {
236240
final String unit = AppLocalizations.of(context).g;
237241
final queryLower = _searchQuery.toLowerCase();
238-
final suggestions =
239-
widget.recent.where((e) => e.ingredient.name.toLowerCase().contains(queryLower)).toList();
240-
final numberFormat = NumberFormat.decimalPattern(Localizations.localeOf(context).toString());
242+
final suggestions = widget.recent
243+
.where((e) => e.ingredient.name.toLowerCase().contains(queryLower))
244+
.toList();
245+
final numberFormat =
246+
NumberFormat.decimalPattern(Localizations.localeOf(context).toString());
241247

242248
return Container(
243249
margin: const EdgeInsets.all(20),
@@ -344,7 +350,8 @@ class IngredientFormState extends State<IngredientForm> {
344350
),
345351
],
346352
),
347-
if (ingredientIdController.text.isNotEmpty && _amountController.text.isNotEmpty)
353+
if (ingredientIdController.text.isNotEmpty &&
354+
_amountController.text.isNotEmpty)
348355
Padding(
349356
padding: const EdgeInsets.all(8.0),
350357
child: Column(
@@ -395,7 +402,8 @@ class IngredientFormState extends State<IngredientForm> {
395402
return;
396403
}
397404
_form.currentState!.save();
398-
_mealItem.ingredientId = int.parse(_ingredientIdController.text);
405+
_mealItem.ingredientId =
406+
int.parse(_ingredientIdController.text);
399407

400408
var date = DateTime.parse(_dateController.text);
401409
final tod = stringToTime(_timeController.text);
@@ -508,6 +516,8 @@ class _PlanFormState extends State<PlanForm> {
508516
GoalType _goalType = GoalType.meals;
509517

510518
final _descriptionController = TextEditingController();
519+
final _startDateController = TextEditingController();
520+
final _endDateController = TextEditingController();
511521
final TextEditingController colorController = TextEditingController();
512522

513523
GoalType? selectedGoal;
@@ -518,6 +528,12 @@ class _PlanFormState extends State<PlanForm> {
518528

519529
_onlyLogging = widget._plan.onlyLogging;
520530
_descriptionController.text = widget._plan.description;
531+
_startDateController.text =
532+
'${widget._plan.startDate.year}-${widget._plan.startDate.month.toString().padLeft(2, '0')}-${widget._plan.startDate.day.toString().padLeft(2, '0')}';
533+
if (widget._plan.endDate != null) {
534+
_endDateController.text =
535+
'${widget._plan.endDate!.year}-${widget._plan.endDate!.month.toString().padLeft(2, '0')}-${widget._plan.endDate!.day.toString().padLeft(2, '0')}';
536+
}
521537
if (widget._plan.hasAnyAdvancedGoals) {
522538
_goalType = GoalType.advanced;
523539
} else if (widget._plan.hasAnyGoals) {
@@ -530,6 +546,8 @@ class _PlanFormState extends State<PlanForm> {
530546
@override
531547
void dispose() {
532548
_descriptionController.dispose();
549+
_startDateController.dispose();
550+
_endDateController.dispose();
533551
colorController.dispose();
534552
super.dispose();
535553
}
@@ -551,6 +569,66 @@ class _PlanFormState extends State<PlanForm> {
551569
widget._plan.description = newValue!;
552570
},
553571
),
572+
// Start Date
573+
TextFormField(
574+
key: const Key('field-start-date'),
575+
decoration: InputDecoration(
576+
labelText: AppLocalizations.of(context).start,
577+
hintText: 'YYYY-MM-DD',
578+
),
579+
controller: _startDateController,
580+
readOnly: true,
581+
onTap: () async {
582+
// Stop keyboard from appearing
583+
FocusScope.of(context).requestFocus(FocusNode());
584+
585+
// Open date picker
586+
final pickedDate = await showDatePicker(
587+
context: context,
588+
initialDate: widget._plan.startDate,
589+
firstDate: DateTime(2000),
590+
lastDate: DateTime(2100),
591+
);
592+
593+
if (pickedDate != null) {
594+
setState(() {
595+
_startDateController.text =
596+
'${pickedDate.year}-${pickedDate.month.toString().padLeft(2, '0')}-${pickedDate.day.toString().padLeft(2, '0')}';
597+
widget._plan.startDate = pickedDate;
598+
});
599+
}
600+
},
601+
),
602+
// End Date
603+
TextFormField(
604+
key: const Key('field-end-date'),
605+
decoration: InputDecoration(
606+
labelText: AppLocalizations.of(context).endDate,
607+
hintText: 'YYYY-MM-DD',
608+
),
609+
controller: _endDateController,
610+
readOnly: true,
611+
onTap: () async {
612+
// Stop keyboard from appearing
613+
FocusScope.of(context).requestFocus(FocusNode());
614+
615+
// Open date picker
616+
final pickedDate = await showDatePicker(
617+
context: context,
618+
initialDate: widget._plan.endDate,
619+
firstDate: DateTime(2000),
620+
lastDate: DateTime(2100),
621+
);
622+
623+
if (pickedDate != null) {
624+
setState(() {
625+
_endDateController.text =
626+
'${pickedDate.year}-${pickedDate.month.toString().padLeft(2, '0')}-${pickedDate.day.toString().padLeft(2, '0')}';
627+
widget._plan.endDate = pickedDate;
628+
});
629+
}
630+
},
631+
),
554632
SwitchListTile(
555633
title: Text(AppLocalizations.of(context).onlyLogging),
556634
subtitle: Text(AppLocalizations.of(context).onlyLoggingHelpText),
@@ -626,7 +704,8 @@ class _PlanFormState extends State<PlanForm> {
626704
val: widget._plan.goalCarbohydrates?.toString(),
627705
label: AppLocalizations.of(context).goalCarbohydrates,
628706
suffix: AppLocalizations.of(context).g,
629-
onSave: (double value) => widget._plan.goalCarbohydrates = value,
707+
onSave: (double value) =>
708+
widget._plan.goalCarbohydrates = value,
630709
key: const Key('field-goal-carbohydrates'),
631710
),
632711
GoalMacros(
@@ -706,7 +785,8 @@ class GoalMacros extends StatelessWidget {
706785

707786
@override
708787
Widget build(BuildContext context) {
709-
final numberFormat = NumberFormat.decimalPattern(Localizations.localeOf(context).toString());
788+
final numberFormat =
789+
NumberFormat.decimalPattern(Localizations.localeOf(context).toString());
710790

711791
return TextFormField(
712792
initialValue: val ?? '',

0 commit comments

Comments
 (0)